summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Bounds <jesse@rebounds.net>2016-07-07 15:40:07 -0700
committerJesse Bounds <jesse@rebounds.net>2016-07-07 15:40:07 -0700
commit23caa9f7aa9a128896afc0743482e015d2c60373 (patch)
tree0d927ad1f66351c52ad53fe295fe4bed855963a3
parent2e10c0a8660af9cdf6ff897aaa39e15fe62c6582 (diff)
parentbb057409c728968cf37c9faca2c1eb4c589c5716 (diff)
downloadqtlocation-mapboxgl-23caa9f7aa9a128896afc0743482e015d2c60373.tar.gz
Merge branch 'release-ios-v3.3.0' into master
-rw-r--r--Makefile10
-rw-r--r--include/mbgl/util/compression.hpp12
-rw-r--r--mbgl.gypi1
-rw-r--r--platform/darwin/docs/theme/assets/css/highlight.css.scss88
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss600
-rwxr-xr-xplatform/darwin/docs/theme/assets/fonts/opensans-bold.eotbin0 -> 24122 bytes
-rwxr-xr-xplatform/darwin/docs/theme/assets/fonts/opensans-bold.woffbin0 -> 28584 bytes
-rwxr-xr-xplatform/darwin/docs/theme/assets/fonts/opensans-regular.eotbin0 -> 23625 bytes
-rwxr-xr-xplatform/darwin/docs/theme/assets/fonts/opensans-regular.woffbin0 -> 27964 bytes
-rwxr-xr-xplatform/darwin/docs/theme/assets/img/carat.pngbin0 -> 274 bytes
-rwxr-xr-xplatform/darwin/docs/theme/assets/img/dash.pngbin0 -> 1338 bytes
-rw-r--r--platform/darwin/docs/theme/assets/img/github.svg4
-rw-r--r--platform/darwin/docs/theme/assets/img/mapbox.svg21
-rwxr-xr-xplatform/darwin/docs/theme/assets/js/jazzy.js40
-rwxr-xr-xplatform/darwin/docs/theme/assets/js/jquery.min.js4
-rw-r--r--platform/darwin/docs/theme/templates/doc.mustache53
-rw-r--r--platform/darwin/docs/theme/templates/footer.mustache3
-rw-r--r--platform/darwin/docs/theme/templates/header.mustache28
-rw-r--r--platform/darwin/docs/theme/templates/nav.mustache16
-rw-r--r--platform/darwin/docs/theme/templates/parameter.mustache12
-rw-r--r--platform/darwin/docs/theme/templates/task.mustache95
-rw-r--r--platform/darwin/docs/theme/templates/tasks.mustache9
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm122
-rw-r--r--platform/darwin/src/NSData+MGLAdditions.h15
-rw-r--r--platform/darwin/src/NSData+MGLAdditions.mm23
-rw-r--r--platform/darwin/src/async_task.cpp2
-rw-r--r--platform/darwin/test/MGLOfflineStorageTests.m11
-rw-r--r--platform/ios/CHANGELOG.md62
-rw-r--r--platform/ios/Mapbox-iOS-SDK-symbols.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec2
-rw-r--r--platform/ios/Mapbox.playground/Contents.swift1
-rw-r--r--platform/ios/app/MBXAnnotationView.h3
-rw-r--r--platform/ios/app/MBXAnnotationView.m33
-rw-r--r--platform/ios/app/MBXViewController.m18
-rw-r--r--platform/ios/docs/doc-README.md2
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj26
-rw-r--r--platform/ios/jazzy.yml5
-rw-r--r--platform/ios/originals/screenshots.sketchbin0 -> 2875392 bytes
-rw-r--r--platform/ios/platform.gyp8
-rw-r--r--platform/ios/screenshot.pngbin302191 -> 1239986 bytes
-rwxr-xr-xplatform/ios/scripts/document.sh5
-rwxr-xr-xplatform/ios/scripts/package.sh13
-rw-r--r--platform/ios/src/MGLAPIClient.m18
-rw-r--r--platform/ios/src/MGLAnnotationView.h209
-rw-r--r--platform/ios/src/MGLAnnotationView.mm88
-rw-r--r--platform/ios/src/MGLAnnotationView_Private.h2
-rw-r--r--platform/ios/src/MGLMapView.h20
-rw-r--r--platform/ios/src/MGLMapView.mm66
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h406
-rw-r--r--platform/ios/src/MGLMapView_Internal.h3
-rw-r--r--platform/ios/test/MGLAnnotationViewTests.m45
-rw-r--r--platform/ios/test/MGLNSDataAdditionsTests.m52
-rw-r--r--platform/macos/CHANGELOG.md1
-rw-r--r--platform/macos/docs/doc-README.md2
-rwxr-xr-xplatform/macos/scripts/document.sh3
-rw-r--r--platform/macos/src/MGLMapViewDelegate.h54
-rw-r--r--src/mbgl/util/compression.cpp2
-rw-r--r--src/mbgl/util/compression.hpp12
58 files changed, 1949 insertions, 383 deletions
diff --git a/Makefile b/Makefile
index 7cf6fcc1c0..165dbb8cce 100644
--- a/Makefile
+++ b/Makefile
@@ -140,23 +140,23 @@ test-ios: ios
-workspace $(IOS_WORK_PATH) -scheme CI test $(XCPRETTY)
ipackage: $(IOS_PROJ_PATH)
- BITCODE=$(BITCODE) FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
+ FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
./platform/ios/scripts/package.sh
ipackage-strip: $(IOS_PROJ_PATH)
- BITCODE=$(BITCODE) FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=NO \
+ FORMAT=$(FORMAT) BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=NO \
./platform/ios/scripts/package.sh
ipackage-sim: $(IOS_PROJ_PATH)
- BUILDTYPE=Debug BITCODE=$(BITCODE) FORMAT=dynamic BUILD_DEVICE=false SYMBOLS=$(SYMBOLS) \
+ BUILDTYPE=Debug FORMAT=dynamic BUILD_DEVICE=false SYMBOLS=$(SYMBOLS) \
./platform/ios/scripts/package.sh
iframework: $(IOS_PROJ_PATH)
- BITCODE=$(BITCODE) FORMAT=dynamic BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
+ FORMAT=dynamic BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=$(SYMBOLS) \
./platform/ios/scripts/package.sh
ifabric: $(IOS_PROJ_PATH)
- BITCODE=$(BITCODE) FORMAT=static BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=NO SELF_CONTAINED=YES \
+ FORMAT=static BUILD_DEVICE=$(BUILD_DEVICE) SYMBOLS=NO SELF_CONTAINED=YES \
./platform/ios/scripts/package.sh
idocument:
diff --git a/include/mbgl/util/compression.hpp b/include/mbgl/util/compression.hpp
new file mode 100644
index 0000000000..5e232187c3
--- /dev/null
+++ b/include/mbgl/util/compression.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <string>
+
+namespace mbgl {
+namespace util {
+
+std::string compress(const std::string& raw);
+std::string decompress(const std::string& raw);
+
+} // namespace util
+} // namespace mbgl
diff --git a/mbgl.gypi b/mbgl.gypi
index 5eea61fd5a..26e32ac07e 100644
--- a/mbgl.gypi
+++ b/mbgl.gypi
@@ -217,6 +217,7 @@
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
'OTHER_CFLAGS': [ '<@(cflags)' ],
+ 'BITCODE_GENERATION_MODE': 'bitcode',
},
}, {
'cflags_cc': [ '<@(cflags_cc)' ],
diff --git a/platform/darwin/docs/theme/assets/css/highlight.css.scss b/platform/darwin/docs/theme/assets/css/highlight.css.scss
new file mode 100644
index 0000000000..231e0c3168
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/css/highlight.css.scss
@@ -0,0 +1,88 @@
+/* Rouge Syntax Highlighting, Mapbox Base style
+------------------------------------------------------- */
+.highlight .hll { background-color:#ffffcc }
+
+/* No Styling, Just Default:
+ .highlight .nx, Normal Text
+ .highlight .ni Name.Entity
+ .highlight .nf Name.Entity
+ .highlight .no Name.Constant
+*/
+
+/* Comments */
+.highlight .o, /* Operator */
+.highlight .c,
+.highlight .c1,
+.highlight .cp,
+.highlight .cm { color:#999; font-style:italic; }
+.highlight .err { color:#F00000; background-color:#F0A0A0 } /* Error */
+
+.highlight .k { color:#404040; font-weight:bold; } /* Keyword */
+.highlight .css .k { font-weight:normal; }
+
+.highlight .cs { color:#404040; font-style:italic; } /* Comment.Special */
+.highlight .gd { color:#A00000; } /* Generic.Deleted */
+.highlight .ge { font-style:italic } /* Generic.Emph */
+.highlight .gs { font-weight:bold; } /* Generic.Strong */
+.highlight .gr { color:#FF0000; } /* Generic.Error */
+.highlight .gh { color:#000080; } /* Generic.Heading */
+.highlight .gi { color:#00A000; } /* Generic.Inserted */
+.highlight .go { color:#808080; } /* Generic.Output */
+.highlight .gp { color:#c65d09; } /* Generic.Prompt */
+.highlight .gu { color:#800080; } /* Generic.Subheading */
+.highlight .gt { color:#0040D0; } /* Generic.Traceback */
+.highlight .kc { color:#D24400; } /* Keyword.Constant */
+
+/* Keyword.Declaration
+ * Keyword.Namespace
+ * Keyword.Reserved */
+.highlight .kd,
+.highlight .kn,
+.highlight .kr,
+.highlight .nt { color:#0B5A91; } /* Name.Tag */
+
+/* Literal.Number */
+.highlight .mh,
+.highlight .mo,
+.highlight .il,
+.highlight .mi,
+.highlight .kt,
+.highlight .mf,
+.highlight .nl, /* Name.Label */
+.highlight .na, /* Name.Attribute */
+.highlight .m { color:#0C9DC2; } /* Keyword.Type */
+.highlight .kp { color:#0080f0; } /* Keyword.Pseudo */
+
+.highlight .nc { color:#DF6637; } /* Name.Class */
+.highlight .css .nc { color:#75A21C; }
+
+.highlight .nd { color:#505050; } /* Name.Decorator */
+.highlight .ne { color:#F00000; } /* Name.Exception */
+
+.highlight .nn { color:#0e84b5; } /* Name.Namespace */
+
+.highlight .nf, /* Name.Function */
+.highlight .nv { color:#003060; } /* Name.Variable */
+.highlight .ow { color:#404040; } /* Operator.Word */
+.highlight .w { color:#bbbbbb; } /* Text.Whitespace */
+.highlight .sc { color:#8080F0; } /* Literal.String.Char */
+.highlight .sd { color:#D04020; } /* Literal.String.Doc */
+
+/* Name.Builtin / Name.Builtin.Pseudo */
+.highlight .bp,
+.highlight .nb { color:#007020; }
+
+/* Literal.String */
+.highlight .s,
+.highlight .sh,
+.highlight .sb,
+.highlight .s1,
+.highlight .sr,
+.highlight .se { color:#75A21C; }
+
+.highlight .si { background-color:#eee; } /* Literal.String.Interpol */
+.highlight .p { color:#444444; } /* Normal Text */
+.highlight .ss { color:#f0c080; } /* Literal.String.Symbol */
+.highlight .vc { color:#c0c0f0; } /* Name.Variable.Class */
+.highlight .vg { color:#f08040; } /* Name.Variable.Global */
+.highlight .vi { color:#a0a0f0; } /* Name.Variable.Instance */
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
new file mode 100644
index 0000000000..99eebd0572
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -0,0 +1,600 @@
+// ===========================================================================
+//
+// Variables
+//
+// ===========================================================================
+
+$body_background: #fff;
+$body_font: 15px/25px 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+$text_color: #333;
+$gray_border: 1px solid #ddd;
+
+$quote_color: #858585;
+$quote_border: 4px solid #e5e5e5;
+
+$link_color: #3887BE;
+$link_hover_color: #63b6e5;
+
+$table_alt_row_color: #fbfbfb;
+$table_border_color: #ddd;
+
+$code_bg_color: #f6f6f6;
+$code_font: Menlo, Bitstream Vera Sans Mono, Monaco, Consolas, monospacee;
+
+
+// ----- Layout
+
+$gutter: 16px;
+$navigation_min_width: 175px;
+$navigation_max_width: 300px;
+
+
+// ----- Header
+
+$header_weight: normal;
+$header_color: #555;
+$header_font: 'Open Sans Bold', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+$header_bg_color: #3887be;
+$header_link_color: #fff;
+$doc_coverage_color: #999;
+
+
+// ----- Breadcrumbs
+
+$breadcrumbs_bg_color: #fbfbfb;
+$breadcrumbs_border_color: #ddd;
+
+
+// ----- Navigation
+
+$navigation_max_width: 300px;
+$navigation_bg_color: #fbfbfb;
+$navigation_border_color: #ddd;
+$navigation_title_color: rgba(0, 0, 0, 0.5);
+$navigation_task_color: $link_color;
+
+$section_name_color: $navigation_title_color;
+
+// ----- Content
+
+$declaration_title_language_color: #4183c4;
+$declaration_language_border: 5px solid #cde9f4;
+$declaration_bg_color: #fdfeff;
+$declaration_border_color: #ddd;
+
+$aside_color: #aaa;
+$aside_border: 5px solid lighten($aside_color, 20%);
+$aside_warning_color: #ff0000;
+$aside_warning_border: 5px solid lighten($aside_warning_color, 20%);
+
+// ----- Footer
+
+$footer_text_color: #888;
+$footer_link_color: #555;
+
+
+// ===========================================================================
+//
+// Base
+//
+// ===========================================================================
+
+*, *:before, *:after {
+ box-sizing: inherit;
+}
+
+body {
+ margin: 0;
+ background: $body_background;
+ color: $text_color;
+ font: $body_font;
+ letter-spacing: .2px;
+ -webkit-font-smoothing: antialiased;
+ box-sizing: border-box;
+}
+
+// ----- Block elements
+
+@mixin heading($font-size: 1rem, $margin: 1.275em 0 0.85em) {
+ font-family: $header_font;
+ font-size: $font-size;
+ font-weight: $header_weight;
+ color: $header_color;
+ margin: $margin;
+}
+
+h1 {
+ @include heading(2.0rem, 0.5em 0 0.6em);
+}
+
+h1 a {
+ color: $header_color;
+}
+
+h2 {
+ @include heading(1.5rem, 1em 0 0.6em);
+}
+
+h3 {
+ @include heading(1rem, 1em 0 0.3em);
+}
+
+h4 {
+ @include heading(1rem);
+}
+
+h5, h6 {
+ @include heading;
+}
+
+p {
+ margin: 0 0 1em;
+}
+
+ul, ol {
+ padding: 0 0 0 2em;
+ margin: 0 0 0.85em;
+}
+
+blockquote {
+ margin: 0 0 0.85em;
+ padding: 0 15px;
+ color: $quote_color;
+ border-left: $quote_border;
+}
+
+
+// ----- Inline elements
+
+img {
+ max-width: 100%;
+}
+
+a {
+ color: $link_color;
+ text-decoration: none;
+
+ &:hover, &:focus {
+ color: $link_hover_color;
+ }
+
+ &:focus {
+ box-shadow:inset 0 0 0 1px rgba(0,0,0,0.05);
+ }
+}
+
+// ----- Tables
+
+table {
+ background: $body_background;
+ width: 100%;
+ border-collapse: collapse;
+ border-spacing: 0;
+ overflow: auto;
+ margin: 0 0 0.85em;
+}
+
+tr {
+ &:nth-child(2n) {
+ background-color: $table_alt_row_color;
+ }
+}
+
+th, td {
+ padding: 6px 13px;
+ border: 1px solid $table_border_color;
+}
+
+
+// ----- Code
+
+pre {
+ margin: 0 0 1.275em;
+ padding: .85em 1em;
+ overflow: auto;
+ background: $code_bg_color;
+ font-family: $code_font;
+ border-radius: 4px;
+}
+
+code {
+ font-family: $code_font;
+ font-size: .85em;
+ line-height: .85em;
+}
+
+p, li {
+ > code {
+ background: $code_bg_color;
+ padding: .2em;
+ border-radius: 4px;
+ &:before, &:after {
+ letter-spacing: -.35em;
+ content: "\00a0";
+ }
+ }
+}
+
+pre code {
+ padding: 0;
+ white-space: pre;
+}
+
+
+// ===========================================================================
+//
+// Layout
+//
+// ===========================================================================
+
+.content-wrapper {
+ display: flex;
+ flex-direction: column;
+ @media (min-width: 768px) {
+ flex-direction: row;
+ }
+}
+
+
+// ===========================================================================
+//
+// Header
+//
+// ===========================================================================
+
+.header {
+ display: flex;
+ padding: $gutter/2;
+ font-size: 0.875em;
+ background: $header_bg_color;
+ color: $doc_coverage_color;
+}
+
+.header-col {
+ margin: 0;
+ padding: 0 $gutter/2
+}
+
+.header-col--primary {
+ flex: 1;
+}
+
+.header-link {
+ color: $header_link_color;
+}
+
+.header-image, .header-icon {
+ padding-right: 6px;
+ vertical-align: -4px;
+ height: 16px;
+}
+
+// ===========================================================================
+//
+// Breadcrumbs
+//
+// ===========================================================================
+
+.breadcrumbs {
+ font-size: 0.875em;
+ padding: $gutter / 2 $gutter;
+ margin: 0;
+ background: $breadcrumbs_bg_color;
+ border-bottom: 1px solid $breadcrumbs_border_color;
+}
+
+.carat {
+ height: 10px;
+ margin: 0 5px;
+}
+
+
+// ===========================================================================
+//
+// Navigation
+//
+// ===========================================================================
+
+.navigation {
+ order: 2;
+
+ @media (min-width: 768px) {
+ order: 1;
+ width: 25%;
+ min-width: $navigation_min_width;
+ max-width: $navigation_max_width;
+ padding-bottom: $gutter*4;
+ font-size: 12px;
+ line-height: 20px;
+ overflow: hidden;
+ background: $navigation_bg_color;
+ border-right: 1px solid $navigation_border_color;
+ }
+}
+
+.nav-groups {
+ list-style-type: none;
+ padding-left: 0;
+}
+
+.nav-group-name {
+ border-bottom: 1px solid $navigation_border_color;
+ padding: $gutter/2 0 $gutter/2 $gutter;
+}
+
+.nav-group-name-link {
+ font-family: $header_font;
+ color: $navigation_title_color;
+}
+
+.nav-group-tasks {
+ margin: $gutter/2 0;
+ padding: 0 0 0 $gutter/2;
+}
+
+.nav-group-task {
+ font-size: 1em;
+ list-style-type: none;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ color: $navigation_task_color;
+}
+
+.nav-group-task-link {
+ color: $navigation_task_color;
+}
+
+// ===========================================================================
+//
+// Content
+//
+// ===========================================================================
+
+.main-content {
+ order: 1;
+ @media (min-width: 768px) {
+ order: 2;
+ flex: 1;
+ padding-bottom: 60px;
+ max-width: 80%;
+ }
+}
+
+.section {
+ padding: 0 $gutter * 2;
+ border-bottom: 1px solid $navigation_border_color;
+}
+
+.section-content {
+ margin: 0 auto;
+ padding: $gutter 0;
+}
+
+.section-content img {
+ margin: 0 auto;
+ display: block;
+}
+
+.section-name {
+ color: $section_name_color;
+ display: block;
+}
+
+.declaration .highlight {
+ overflow-x: initial; // This allows the scrollbar to show up inside declarations
+ padding: $gutter/2 0;
+ margin: 0;
+ background-color: transparent;
+ border: none;
+}
+
+.task-group-section {
+ border-top: $gray_border;
+}
+
+.task-group {
+ padding-top: 0px;
+}
+
+.task-name-container {
+ a[name] {
+ &:before {
+ content: "";
+ display: block;
+ }
+ }
+}
+
+.item-container { }
+
+.item {
+ padding-top: 8px;
+ width: 100%;
+ list-style-type: none;
+
+ a[name] {
+ &:before {
+ content: "";
+ display: block;
+ }
+ }
+
+ .token {
+ padding-left: 3px;
+ margin-left: 0px;
+ font-size: 1em;
+ word-break: break-all;
+ }
+
+ .declaration-note {
+ font-size: .85em;
+ color: #808080;
+ font-style: italic;
+ }
+}
+
+.pointer-container {
+ border-bottom: $gray_border;
+ left: -23px;
+ padding-bottom: 13px;
+ position: relative;
+ width: 110%;
+}
+
+.pointer {
+ left: 21px;
+ top: 7px;
+ display: block;
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ border-left: 1px solid $declaration_border_color;
+ border-top: 1px solid $declaration_border_color;
+ background: $declaration_bg_color;
+ transform: rotate(45deg);
+}
+
+.height-container {
+ display: none;
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+ .section {
+ background: $declaration_bg_color;
+ border: $gray_border;
+ border-top-width: 0;
+ padding: $gutter / 1.5 $gutter;
+ }
+}
+
+.aside, .language {
+ padding: 6px 12px;
+ margin: 12px 0;
+ border-left: $aside_border;
+ overflow-y: hidden;
+ .aside-title {
+ font-size: 9px;
+ letter-spacing: 2px;
+ text-transform: uppercase;
+ padding-bottom: 0;
+ margin: 0;
+ color: $aside_color;
+ -webkit-user-select: none;
+ }
+ p:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.language {
+ border-left: $declaration_language_border;
+ .aside-title {
+ color: $declaration_title_language_color;
+ }
+}
+
+.aside-warning {
+ border-left: $aside_warning_border;
+ .aside-title {
+ color: $aside_warning_color;
+ }
+}
+
+.graybox {
+ border-collapse: collapse;
+ width: 100%;
+ p {
+ margin: 0;
+ word-break: break-word;
+ min-width: 50px;
+ }
+ td {
+ border: $gray_border;
+ padding: 5px 25px 5px 10px;
+ vertical-align: middle;
+ }
+ tr td:first-of-type {
+ text-align: right;
+ padding: 7px;
+ vertical-align: top;
+ word-break: normal;
+ width: 40px;
+ }
+}
+
+.slightly-smaller {
+ font-size: 0.9em;
+}
+
+.show-on-github {
+ font-variant: small-caps;
+}
+
+.show-on-github-icon {
+ width: 16px;
+ display: inline-block !important;
+ vertical-align: -3px;
+ padding-left: 2px;
+}
+
+
+// ===========================================================================
+//
+// Footer
+//
+// ===========================================================================
+
+.footer {
+ border-top: 1px $navigation_border_color solid;
+ padding: $gutter/2 $gutter;
+ color: $footer_text_color;
+ font-size: 0.8em;
+
+ p {
+ display: inline;
+ }
+
+ a {
+ color: $footer_link_color;
+ }
+}
+
+
+// ===========================================================================
+//
+// Dash
+//
+// ===========================================================================
+
+html.dash {
+
+ .header, .breadcrumbs, .navigation {
+ display: none;
+ }
+
+ .height-container {
+ display: block;
+ }
+}
+
+
+// ===========================================================================
+//
+// Fonts
+//
+// ===========================================================================
+
+@font-face {
+ font-family:'Open Sans';
+ src:url('../fonts/opensans-regular.eot');
+ src:url('../fonts/opensans-regular.eot#iefix') format('embedded-opentype'),
+ url('../fonts/opensans-regular.woff') format('woff');
+ }
+
+@font-face {
+ font-family:'Open Sans Bold';
+ src:url('../fonts/opensans-bold.eot');
+ src:url('../fonts/opensans-bold.eot#iefix') format('embedded-opentype'),
+ url('../fonts/opensans-bold.woff') format('woff');
+ }
diff --git a/platform/darwin/docs/theme/assets/fonts/opensans-bold.eot b/platform/darwin/docs/theme/assets/fonts/opensans-bold.eot
new file mode 100755
index 0000000000..7d99f603d7
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/fonts/opensans-bold.eot
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/fonts/opensans-bold.woff b/platform/darwin/docs/theme/assets/fonts/opensans-bold.woff
new file mode 100755
index 0000000000..005fadcd02
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/fonts/opensans-bold.woff
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/fonts/opensans-regular.eot b/platform/darwin/docs/theme/assets/fonts/opensans-regular.eot
new file mode 100755
index 0000000000..73abfc002d
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/fonts/opensans-regular.eot
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/fonts/opensans-regular.woff b/platform/darwin/docs/theme/assets/fonts/opensans-regular.woff
new file mode 100755
index 0000000000..af925a255a
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/fonts/opensans-regular.woff
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/img/carat.png b/platform/darwin/docs/theme/assets/img/carat.png
new file mode 100755
index 0000000000..29d2f7fd49
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/img/carat.png
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/img/dash.png b/platform/darwin/docs/theme/assets/img/dash.png
new file mode 100755
index 0000000000..6f694c7a01
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/img/dash.png
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/img/github.svg b/platform/darwin/docs/theme/assets/img/github.svg
new file mode 100644
index 0000000000..b59e7f82f1
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/img/github.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg viewBox="0 0 33 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <path stroke="none" fill="#555" fill-rule="evenodd" d="M16.6,0.4 C7.6,0.4 0.3,7.7 0.3,16.7 C0.3,23.9 5,30 11.4,32.2 C12.2,32.3 12.5,31.8 12.5,31.4 L12.5,28.6 C8,29.6 7,26.4 7,26.4 C6.3,24.5 5.2,24 5.2,24 C3.7,23 5.3,23 5.3,23 C6.9,23.1 7.8,24.7 7.8,24.7 C9.3,27.2 11.6,26.5 12.5,26.1 C12.6,25 13.1,24.3 13.5,23.9 C9.9,23.5 6.1,22.1 6.1,15.8 C6.1,14 6.7,12.6 7.8,11.4 C7.6,11 7.1,9.3 8,7.1 C8,7.1 9.4,6.7 12.5,8.8 C13.8,8.4 15.2,8.3 16.6,8.3 C18,8.3 19.4,8.5 20.7,8.8 C23.8,6.7 25.2,7.1 25.2,7.1 C26.1,9.3 25.5,11 25.4,11.4 C26.4,12.5 27.1,14 27.1,15.8 C27.1,22.1 23.3,23.4 19.7,23.8 C20.3,24.3 20.8,25.3 20.8,26.8 L20.8,31.3 C20.8,31.7 21.1,32.2 21.9,32.1 C28.4,29.9 33,23.8 33,16.6 C32.9,7.7 25.6,0.4 16.6,0.4 L16.6,0.4 Z"></path>
+</svg>
diff --git a/platform/darwin/docs/theme/assets/img/mapbox.svg b/platform/darwin/docs/theme/assets/img/mapbox.svg
new file mode 100644
index 0000000000..a0c1ee44a4
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/img/mapbox.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="580px" height="156px" viewBox="0 0 580 156" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
+ <title>layer1</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="mapbox" transform="translate(-1.000000, -1.000000)" fill="#FFFFFF">
+ <g id="layer1" transform="translate(0.428470, 0.781010)">
+ <g id="g3024" transform="translate(0.542850, 0.401720)">
+ <path d="M0.028592,0.417258 L0.028592,122.817276 L18.028592,122.817276 L18.028592,22.017258 L54.028592,83.217258 L90.028592,22.017258 L90.028592,122.817276 L108.028592,122.817276 L108.028592,0.417258 L93.628592,0.417258 C86.428592,0.394218 80.77382,2.217258 77.597342,7.617258 L54.028592,47.667258 L30.459842,7.617258 C27.283364,2.217258 21.628592,0.394218 14.428592,0.417258 L0.028592,0.417258 L0.028592,0.417258 Z" id="path4479"></path>
+ <path d="M315.028592,0.417258 L315.028592,124.617276 L349.228592,124.617276 C374.428592,124.617276 394.228592,104.81724 394.228592,77.817258 C394.228592,50.81724 374.428592,31.01724 349.228592,31.017258 C343.529972,31.041018 338.884892,30.824568 333.028592,30.848598 L333.028592,0.417348 L315.028592,0.417348 L315.028592,0.417258 Z M333.028592,49.017258 L349.228592,49.017258 C364.381892,49.017258 376.228592,62.677008 376.228592,77.817258 C376.228592,92.95632 365.621732,106.617258 349.228592,106.617258 L333.028592,106.617258 L333.028592,49.017258 L333.028592,49.017258 Z" id="path4477"></path>
+ <path d="M165.628592,31.017258 C140.428592,31.017258 122.428592,50.817258 122.428592,77.817258 C122.428592,104.817258 140.428574,124.617276 165.628592,124.617276 L208.828592,124.617276 L208.828592,77.817258 C208.828592,50.817258 190.82861,31.017258 165.628592,31.017258 L165.628592,31.017258 Z M165.628592,49.017258 C180.028592,49.017258 190.828592,61.424226 190.828592,77.817258 L190.828592,106.617258 L165.628592,106.617258 C151.228574,106.617258 140.428628,93.994254 140.428592,77.817258 C140.428574,61.617258 151.228592,49.017258 165.628592,49.017258 L165.628592,49.017258 Z" id="path4475"></path>
+ <path d="M223.228592,31.017258 L223.228592,155.217276 L241.228592,155.217276 L241.228592,124.729776 C247.084856,124.753536 251.729954,124.593516 257.428592,124.617276 C282.628592,124.617276 302.428592,104.817258 302.428592,77.817258 C302.428592,50.817258 282.628592,31.017258 257.428592,31.017258 L223.228592,31.017258 L223.228592,31.017258 Z M241.228592,49.017258 L257.428592,49.017258 C273.821552,49.017258 284.428592,62.678196 284.428592,77.817258 C284.428592,92.957508 272.581892,106.617258 257.428592,106.617258 L241.228592,106.617258 L241.228592,49.017258 L241.228592,49.017258 Z" id="path4473"></path>
+ <path d="M450.028592,31.017258 C425.175812,31.017258 405.028592,51.970374 405.028592,77.817258 C405.028592,103.664142 425.175812,124.617276 450.028592,124.617276 C474.881372,124.617276 495.028592,103.664142 495.028592,77.817258 C495.028592,51.970374 474.881372,31.017258 450.028592,31.017258 L450.028592,31.017258 Z M450.028592,49.017258 C464.940332,49.017258 477.028592,61.911522 477.028592,77.817258 C477.028592,93.722994 464.940332,106.617258 450.028592,106.617258 C435.116852,106.617258 423.028592,93.722994 423.028592,77.817258 C423.028592,61.911522 435.116852,49.017258 450.028592,49.017258 L450.028592,49.017258 Z" id="path4471"></path>
+ <polygon id="path4469" points="493.116092 32.794182 524.447342 76.444182 491.428592 122.7942 513.478592 122.7942 535.472342 91.856682 557.578592 122.7942 579.628592 122.7942 546.553592 76.444182 577.828592 32.794182 555.666092 32.794182 535.472342 60.975432 515.278592 32.794218"></polygon>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/platform/darwin/docs/theme/assets/js/jazzy.js b/platform/darwin/docs/theme/assets/js/jazzy.js
new file mode 100755
index 0000000000..70d90890c7
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/js/jazzy.js
@@ -0,0 +1,40 @@
+window.jazzy = {'docset': false}
+if (typeof window.dash != 'undefined') {
+ document.documentElement.className += ' dash'
+ window.jazzy.docset = true
+}
+if (navigator.userAgent.match(/xcode/i)) {
+ document.documentElement.className += ' xcode'
+ window.jazzy.docset = true
+}
+
+// On doc load, toggle the URL hash discussion if present
+$(document).ready(function() {
+ if (!window.jazzy.docset) {
+ var linkToHash = $('a[href="' + window.location.hash +'"]');
+ linkToHash.trigger("click");
+ }
+});
+
+// On token click, toggle its discussion and animate token.marginLeft
+$(".token").click(function(event) {
+ if (window.jazzy.docset) {
+ return;
+ }
+ var link = $(this);
+ var animationDuration = 300;
+ var tokenOffset = "-15px";
+ var original = link.css('marginLeft') == tokenOffset;
+ link.animate({'margin-left':original ? "0px" : tokenOffset}, animationDuration);
+ $content = link.parent().parent().next();
+ $content.slideToggle(animationDuration);
+
+ // Keeps the document from jumping to the hash.
+ var href = $(this).attr('href');
+ if (history.pushState) {
+ history.pushState({}, '', href);
+ } else {
+ location.hash = href;
+ }
+ event.preventDefault();
+});
diff --git a/platform/darwin/docs/theme/assets/js/jquery.min.js b/platform/darwin/docs/theme/assets/js/jquery.min.js
new file mode 100755
index 0000000000..ab28a24729
--- /dev/null
+++ b/platform/darwin/docs/theme/assets/js/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
+!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;
+if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[m.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=Z.test(e)?this.mouseHooks:Y.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new m.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=f.srcElement||y),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,g.filter?g.filter(a,f):a},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(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button,g=b.fromElement;return null==a.pageX&&null!=b.clientX&&(d=a.target.ownerDocument||y,e=d.documentElement,c=d.body,a.pageX=b.clientX+(e&&e.scrollLeft||c&&c.scrollLeft||0)-(e&&e.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(e&&e.scrollTop||c&&c.scrollTop||0)-(e&&e.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&g&&(a.relatedTarget=g===a.target?b.toElement:g),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==cb()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===cb()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return m.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return m.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=m.extend(new m.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?m.event.trigger(e,null,b):m.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},m.removeEvent=y.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){var d="on"+b;a.detachEvent&&(typeof a[d]===K&&(a[d]=null),a.detachEvent(d,c))},m.Event=function(a,b){return this instanceof m.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ab:bb):this.type=a,b&&m.extend(this,b),this.timeStamp=a&&a.timeStamp||m.now(),void(this[m.expando]=!0)):new m.Event(a,b)},m.Event.prototype={isDefaultPrevented:bb,isPropagationStopped:bb,isImmediatePropagationStopped:bb,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ab,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ab,a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ab,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},m.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){m.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!m.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.submitBubbles||(m.event.special.submit={setup:function(){return m.nodeName(this,"form")?!1:void m.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=m.nodeName(b,"input")||m.nodeName(b,"button")?b.form:void 0;c&&!m._data(c,"submitBubbles")&&(m.event.add(c,"submit._submit",function(a){a._submit_bubble=!0}),m._data(c,"submitBubbles",!0))})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&m.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){return m.nodeName(this,"form")?!1:void m.event.remove(this,"._submit")}}),k.changeBubbles||(m.event.special.change={setup:function(){return X.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(m.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._just_changed=!0)}),m.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1),m.event.simulate("change",this,a,!0)})),!1):void m.event.add(this,"beforeactivate._change",function(a){var b=a.target;X.test(b.nodeName)&&!m._data(b,"changeBubbles")&&(m.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||m.event.simulate("change",this.parentNode,a,!0)}),m._data(b,"changeBubbles",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return m.event.remove(this,"._change"),!X.test(this.nodeName)}}),k.focusinBubbles||m.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){m.event.simulate(b,a.target,m.event.fix(a),!0)};m.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=m._data(d,b);e||d.addEventListener(a,c,!0),m._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=m._data(d,b)-1;e?m._data(d,b,e):(d.removeEventListener(a,c,!0),m._removeData(d,b))}}}),m.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(f in a)this.on(f,b,c,a[f],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=bb;else if(!d)return this;return 1===e&&(g=d,d=function(a){return m().off(a),g.apply(this,arguments)},d.guid=g.guid||(g.guid=m.guid++)),this.each(function(){m.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,m(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=bb),this.each(function(){m.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){m.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?m.event.trigger(a,b,c,!0):void 0}});function db(a){var b=eb.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}var eb="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",fb=/ jQuery\d+="(?:null|\d+)"/g,gb=new RegExp("<(?:"+eb+")[\\s/>]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/<tbody/i,lb=/<|&#?\w+;/,mb=/<(?:script|style|link)/i,nb=/checked\s*(?:[^=]|=\s*.checked.)/i,ob=/^$|\/(?:java|ecma)script/i,pb=/^true\/(.*)/,qb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,rb={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:k.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1></$2>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?"<table>"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="<table><tr><td></td><td>t</td></tr></table>",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
+},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||m.fx.stop(),$b=void 0},m.fx.timer=function(a){m.timers.push(a),a()?m.fx.start():m.timers.pop()},m.fx.interval=13,m.fx.start=function(){_b||(_b=setInterval(m.fx.tick,m.fx.interval))},m.fx.stop=function(){clearInterval(_b),_b=null},m.fx.speeds={slow:600,fast:200,_default:400},m.fn.delay=function(a,b){return a=m.fx?m.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a,b,c,d,e;b=y.createElement("div"),b.setAttribute("className","t"),b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.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(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,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",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("<div>").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/platform/darwin/docs/theme/templates/doc.mustache b/platform/darwin/docs/theme/templates/doc.mustache
new file mode 100644
index 0000000000..b16521a431
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/doc.mustache
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <title>{{name}} {{kind}} Reference</title>
+ <link rel="stylesheet" type="text/css" href="{{path_to_root}}css/jazzy.css" />
+ <link rel="stylesheet" type="text/css" href="{{path_to_root}}css/highlight.css" />
+ <meta charset="utf-8">
+ <script src="{{path_to_root}}js/jquery.min.js" defer></script>
+ <script src="{{path_to_root}}js/jazzy.js" defer></script>
+ {{{custom_head}}}
+ </head>
+ <body>
+
+ {{#dash_type}}
+ <a name="//apple_ref/{{language_stub}}/{{dash_type}}/{{name}}" class="dashAnchor"></a>
+ {{/dash_type}}
+
+ <a title="{{name}} {{kind}} Reference"></a>
+
+ {{> header}}
+
+ <p class="breadcrumbs">
+ <a class="breadcrumb" href="{{path_to_root}}index.html">{{module_name}} Reference</a>
+ <img class="carat" src="{{path_to_root}}img/carat.png" />
+ {{name}} {{kind}} Reference
+ </p>
+
+ <div class="content-wrapper">
+ {{> nav}}
+ <article class="main-content">
+
+ <section class="section">
+ <div class="section-content">
+ {{^hide_name}}<h1>{{name}}</h1>{{/hide_name}}
+ {{#declaration}}
+ <div class="declaration">
+ <div class="language">
+ {{{declaration}}}
+ </div>
+ </div>
+ {{/declaration}}
+ {{{overview}}}
+ </div>
+ </section>
+
+ {{> tasks}}
+
+ </article>
+ </div>
+ {{> footer}}
+ </body>
+</div>
+</html>
diff --git a/platform/darwin/docs/theme/templates/footer.mustache b/platform/darwin/docs/theme/templates/footer.mustache
new file mode 100644
index 0000000000..e72ede6126
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/footer.mustache
@@ -0,0 +1,3 @@
+<section class="footer">
+ {{{copyright}}} Generated by <a class="link" href="https://github.com/realm/jazzy" target="_blank" rel="external">jazzy ♪♫ v{{jazzy_version}}</a>, a <a class="link" href="http://realm.io" target="_blank" rel="external">Realm</a> project.
+</section>
diff --git a/platform/darwin/docs/theme/templates/header.mustache b/platform/darwin/docs/theme/templates/header.mustache
new file mode 100644
index 0000000000..15e35b9a81
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/header.mustache
@@ -0,0 +1,28 @@
+<header class="header">
+ <p class="header-col header-col--primary">
+ <a class="header-link" href="{{path_to_root}}index.html">
+ <img class="header-icon" src="{{path_to_root}}img/mapbox.svg" alt="{{module_name}} Docs"/>
+ </a>
+ {{#doc_coverage}} ({{doc_coverage}}% documented){{/doc_coverage}}
+ </p>
+
+ {{#github_url}}
+ <p class="header-col header-col--secondary">
+ <a class="header-link" href="{{github_url}}">
+ <svg class="header-icon" viewBox="0 0 33 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <path stroke="none" stroke-width="1" fill="white" fill-rule="evenodd" d="M16.6,0.4 C7.6,0.4 0.3,7.7 0.3,16.7 C0.3,23.9 5,30 11.4,32.2 C12.2,32.3 12.5,31.8 12.5,31.4 L12.5,28.6 C8,29.6 7,26.4 7,26.4 C6.3,24.5 5.2,24 5.2,24 C3.7,23 5.3,23 5.3,23 C6.9,23.1 7.8,24.7 7.8,24.7 C9.3,27.2 11.6,26.5 12.5,26.1 C12.6,25 13.1,24.3 13.5,23.9 C9.9,23.5 6.1,22.1 6.1,15.8 C6.1,14 6.7,12.6 7.8,11.4 C7.6,11 7.1,9.3 8,7.1 C8,7.1 9.4,6.7 12.5,8.8 C13.8,8.4 15.2,8.3 16.6,8.3 C18,8.3 19.4,8.5 20.7,8.8 C23.8,6.7 25.2,7.1 25.2,7.1 C26.1,9.3 25.5,11 25.4,11.4 C26.4,12.5 27.1,14 27.1,15.8 C27.1,22.1 23.3,23.4 19.7,23.8 C20.3,24.3 20.8,25.3 20.8,26.8 L20.8,31.3 C20.8,31.7 21.1,32.2 21.9,32.1 C28.4,29.9 33,23.8 33,16.6 C32.9,7.7 25.6,0.4 16.6,0.4 L16.6,0.4 Z"></path>
+ </svg>
+ View on GitHub
+ </a>
+ </p>
+ {{/github_url}}
+
+ {{#dash_url}}
+ <p class="header-col header-col--secondary">
+ <a class="header-link" href="{{dash_url}}">
+ <img class="header-icon" src="{{path_to_root}}img/dash.png"/>
+ Install in Dash
+ </a>
+ </p>
+ {{/dash_url}}
+</header>
diff --git a/platform/darwin/docs/theme/templates/nav.mustache b/platform/darwin/docs/theme/templates/nav.mustache
new file mode 100644
index 0000000000..f7de866712
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/nav.mustache
@@ -0,0 +1,16 @@
+<nav class="navigation">
+ <ul class="nav-groups">
+ {{#structure}}
+ <li class="nav-group-name">
+ <a class="nav-group-name-link" href="{{path_to_root}}{{section}}.html">{{section}}</a>
+ <ul class="nav-group-tasks">
+ {{#children}}
+ <li class="nav-group-task">
+ <a class="nav-group-task-link" href="{{path_to_root}}{{url}}">{{name}}</a>
+ </li>
+ {{/children}}
+ </ul>
+ </li>
+ {{/structure}}
+ </ul>
+</nav>
diff --git a/platform/darwin/docs/theme/templates/parameter.mustache b/platform/darwin/docs/theme/templates/parameter.mustache
new file mode 100644
index 0000000000..6fa329d72e
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/parameter.mustache
@@ -0,0 +1,12 @@
+<tr>
+ <td>
+ <code>
+ <em>{{name}}</em>
+ </code>
+ </td>
+ <td>
+ <div>
+ {{{discussion}}}
+ </div>
+ </td>
+</tr>
diff --git a/platform/darwin/docs/theme/templates/task.mustache b/platform/darwin/docs/theme/templates/task.mustache
new file mode 100644
index 0000000000..dd30c670eb
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/task.mustache
@@ -0,0 +1,95 @@
+<div class="task-group">
+ {{#name}}
+ <div class="task-name-container">
+ <a name="/{{uid}}"></a>
+ <a name="//apple_ref/{{language_stub}}/Section/{{name}}" class="dashAnchor"></a>
+ <a href="#/{{uid}}">
+ <h3 class="section-name">{{name}}</h3>
+ </a>
+ </div>
+ {{/name}}
+ <ul class="item-container">
+ {{#items}}
+ <li class="item">
+ <div>
+ <code>
+ <a name="/{{usr}}"></a>
+ <a name="//apple_ref/{{language_stub}}/{{dash_type}}/{{name}}" class="dashAnchor"></a>
+ <a class="token" href="#/{{usr}}">{{name}}</a>
+ </code>
+ {{#default_impl_abstract}}
+ <span class="declaration-note">
+ Default implementation
+ </span>
+ {{/default_impl_abstract}}
+ {{#from_protocol_extension}}
+ <span class="declaration-note">
+ Extension method
+ </span>
+ {{/from_protocol_extension}}
+ </div>
+ <div class="height-container">
+ <div class="pointer-container"></div>
+ <section class="section">
+ <div class="pointer"></div>
+ {{#abstract}}
+ <div class="abstract">
+ {{{abstract}}}
+ {{#url}}
+ <a href="{{{path_to_root}}}{{{url}}}" class="slightly-smaller">See more</a>
+ {{/url}}
+ </div>
+ {{/abstract}}
+ {{#default_impl_abstract}}
+ <h4>Default Implementation</h4>
+ <div class="default_impl abstract">
+ {{{default_impl_abstract}}}
+ </div>
+ {{/default_impl_abstract}}
+ {{#declaration}}
+ <div class="declaration">
+ <h4>Declaration</h4>
+ <div class="language">
+ <p class="aside-title">{{language}}</p>
+ {{{declaration}}}
+ </div>
+ {{#other_language_declaration}}
+ <div class="language">
+ <p class="aside-title">Swift</p>
+ {{{other_language_declaration}}}
+ </div>
+ {{/other_language_declaration}}
+ </div>
+ {{/declaration}}
+ {{#parameters.count}}
+ <div>
+ <h4>Parameters</h4>
+ <table class="graybox">
+ <tbody>
+ {{#parameters}}
+ {{> parameter}}
+ {{/parameters}}
+ </tbody>
+ </table>
+ </div>
+ {{/parameters.count}}
+ {{#return}}
+ <div>
+ <h4>Return Value</h4>
+ {{{return}}}
+ </div>
+ {{/return}}
+ {{#github_token_url}}
+ <div class="slightly-smaller show-on-github">
+ <a href="{{{github_token_url}}}">
+ show on github
+ <img class="show-on-github-icon" src="{{{path_to_root}}}img/github.svg"/>
+ </a>
+ </div>
+ {{/github_token_url}}
+ </section>
+ </div>
+ </li>
+ {{/items}}
+ </ul>
+</div>
diff --git a/platform/darwin/docs/theme/templates/tasks.mustache b/platform/darwin/docs/theme/templates/tasks.mustache
new file mode 100644
index 0000000000..16f65e096b
--- /dev/null
+++ b/platform/darwin/docs/theme/templates/tasks.mustache
@@ -0,0 +1,9 @@
+{{#tasks.count}}
+<section class="section">
+ <div class="section-content">
+ {{#tasks}}
+ {{> task}}
+ {{/tasks}}
+ </div>
+</section>
+{{/tasks.count}}
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index a59fb69943..dd15920eab 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -40,58 +40,92 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = @"MaximumCount";
return sharedOfflineStorage;
}
+/**
+ Returns the file URL to the offline cache, with the option to omit the private
+ subdirectory for legacy (v3.2.0 - v3.2.3) migration purposes.
+
+ The cache is located in a directory specific to the application, so that packs
+ downloaded by other applications don’t count toward this application’s limits.
+
+ The cache is located at:
+ ~/Library/Application Support/tld.app.bundle.id/.mapbox/cache.db
+
+ The subdirectory-less cache was located at:
+ ~/Library/Application Support/tld.app.bundle.id/cache.db
+ */
++ (NSURL *)cacheURLIncludingSubdirectory:(BOOL)useSubdirectory {
+ NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:nil
+ create:YES
+ error:nil];
+ NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
+ if (!bundleIdentifier) {
+ // There’s no main bundle identifier when running in a unit test bundle.
+ bundleIdentifier = [NSBundle bundleForClass:self].bundleIdentifier;
+ }
+ cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
+ if (useSubdirectory) {
+ cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
+ }
+ [[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil];
+ if (useSubdirectory) {
+ // Avoid backing up the offline cache onto iCloud, because it can be
+ // redownloaded. Ideally, we’d even put the ambient cache in Caches, so
+ // it can be reclaimed by the system when disk space runs low. But
+ // unfortunately it has to live in the same file as offline resources.
+ [cacheDirectoryURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
+ }
+ return [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
+}
+
+/**
+ Returns the absolute path to the location where v3.2.0-beta.1 placed the
+ offline cache.
+ */
++ (NSString *)legacyCachePath {
+#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
+ // ~/Documents/offline.db
+ NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
+#elif TARGET_OS_MAC
+ // ~/Library/Caches/tld.app.bundle.id/offline.db
+ NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
+ NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
+ inDomain:NSUserDomainMask
+ appropriateForURL:nil
+ create:NO
+ error:nil];
+ legacyCacheDirectoryURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
+ NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
+ NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
+#endif
+ return legacyCachePath;
+}
+
- (instancetype)init {
if (self = [super init]) {
- // Place the cache in a location specific to the application, so that
- // packs downloaded by other applications don’t count toward this
- // application’s limits.
- // ~/Library/Application Support/tld.app.bundle.id/cache.db
- NSURL *cacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
- inDomain:NSUserDomainMask
- appropriateForURL:nil
- create:YES
- error:nil];
- NSString *bundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
- if (!bundleIdentifier) {
- // There’s no main bundle identifier when running in a unit test bundle.
- bundleIdentifier = [NSBundle bundleForClass:[self class]].bundleIdentifier;
- }
- cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
- [[NSFileManager defaultManager] createDirectoryAtURL:cacheDirectoryURL
- withIntermediateDirectories:YES
- attributes:nil
- error:nil];
- NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName];
- NSString *cachePath = cacheURL ? cacheURL.path : @"";
-
+ NSURL *cacheURL = [[self class] cacheURLIncludingSubdirectory:YES];
+ NSString *cachePath = cacheURL.path ?: @"";
+
// Move the offline cache from v3.2.0-beta.1 to a location that can also
// be used for ambient caching.
-#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- // ~/Documents/offline.db
- NSArray *legacyPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *legacyCachePath = [legacyPaths.firstObject stringByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
-#elif TARGET_OS_MAC
- // ~/Library/Caches/tld.app.bundle.id/offline.db
- NSURL *legacyCacheDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory
- inDomain:NSUserDomainMask
- appropriateForURL:nil
- create:NO
- error:nil];
- legacyCacheDirectoryURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
- NSURL *legacyCacheURL = [legacyCacheDirectoryURL URLByAppendingPathComponent:MGLOfflineStorageFileName3_2_0_beta_1];
- NSString *legacyCachePath = legacyCacheURL ? legacyCacheURL.path : @"";
-#endif
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
+ NSString *legacyCachePath = [[self class] legacyCachePath];
[[NSFileManager defaultManager] moveItemAtPath:legacyCachePath toPath:cachePath error:NULL];
}
-
- _mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [NSBundle mainBundle].resourceURL.path.UTF8String);
- // Avoid backing up the offline cache onto iCloud, because it can be
- // redownloaded. Ideally, we’d even put the ambient cache in Caches, so
- // it can be reclaimed by the system when disk space runs low. But
- // unfortunately it has to live in the same file as offline resources.
- [cacheURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:NULL];
+ // Move the offline file cache from v3.2.x path to a subdirectory that
+ // can be reliably excluded from backups.
+ if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
+ NSURL *subdirectorylessCacheURL = [[self class] cacheURLIncludingSubdirectory:NO];
+ [[NSFileManager defaultManager] moveItemAtPath:subdirectorylessCacheURL.path toPath:cachePath error:NULL];
+ }
+
+ _mbglFileSource = new mbgl::DefaultFileSource(cachePath.UTF8String, [NSBundle mainBundle].resourceURL.path.UTF8String);
// Observe for changes to the global access token (and find out the current one).
[[MGLAccountManager sharedManager] addObserver:self
diff --git a/platform/darwin/src/NSData+MGLAdditions.h b/platform/darwin/src/NSData+MGLAdditions.h
new file mode 100644
index 0000000000..0c68c81f45
--- /dev/null
+++ b/platform/darwin/src/NSData+MGLAdditions.h
@@ -0,0 +1,15 @@
+#import <Foundation/Foundation.h>
+
+#import "MGLTypes.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface NSData (MGLAdditions)
+
+- (NSData *)mgl_compressedData;
+
+- (NSData *)mgl_decompressedData;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/NSData+MGLAdditions.mm b/platform/darwin/src/NSData+MGLAdditions.mm
new file mode 100644
index 0000000000..ef171c5e1e
--- /dev/null
+++ b/platform/darwin/src/NSData+MGLAdditions.mm
@@ -0,0 +1,23 @@
+#import "NSData+MGLAdditions.h"
+
+#include <mbgl/util/compression.hpp>
+
+@implementation NSData (MGLAdditions)
+
+- (NSData *)mgl_compressedData
+{
+ std::string string(static_cast<const char*>(self.bytes), self.length);
+ std::string compressed_string = mbgl::util::compress(string);
+
+ return [NSData dataWithBytes:&compressed_string[0] length:compressed_string.length()];
+}
+
+- (NSData *)mgl_decompressedData
+{
+ std::string string(static_cast<const char*>(self.bytes), self.length);
+ std::string decompressed_string = mbgl::util::decompress(string);
+
+ return [NSData dataWithBytes:&decompressed_string[0] length:decompressed_string.length()];
+}
+
+@end
diff --git a/platform/darwin/src/async_task.cpp b/platform/darwin/src/async_task.cpp
index 513629726b..48457d24a8 100644
--- a/platform/darwin/src/async_task.cpp
+++ b/platform/darwin/src/async_task.cpp
@@ -25,7 +25,7 @@ public:
perform
};
source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
- CFRunLoopAddSource(loop, source, kCFRunLoopDefaultMode);
+ CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes);
}
~Impl() {
diff --git a/platform/darwin/test/MGLOfflineStorageTests.m b/platform/darwin/test/MGLOfflineStorageTests.m
index 415039c527..e2346c5f61 100644
--- a/platform/darwin/test/MGLOfflineStorageTests.m
+++ b/platform/darwin/test/MGLOfflineStorageTests.m
@@ -106,17 +106,18 @@
// Unit tests don't use the main bundle; use com.mapbox.ios.sdk instead.
NSString *bundleIdentifier = [NSBundle bundleForClass:[MGLMapView class]].bundleIdentifier;
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:bundleIdentifier];
- XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache directory should exist.");
+ cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:@".mapbox"];
+ XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheDirectoryURL.path], @"Cache subdirectory should exist.");
NSURL *cacheURL = [cacheDirectoryURL URLByAppendingPathComponent:@"cache.db"];
XCTAssertTrue([[NSFileManager defaultManager] fileExistsAtPath:cacheURL.path], @"Cache database should exist.");
NSError *error = nil;
NSNumber *exclusionFlag = nil;
- [cacheURL getResourceValue:&exclusionFlag
- forKey:NSURLIsExcludedFromBackupKey
- error:&error];
- XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for cache database.");
+ [cacheDirectoryURL getResourceValue:&exclusionFlag
+ forKey:NSURLIsExcludedFromBackupKey
+ error:&error];
+ XCTAssertTrue(exclusionFlag && [exclusionFlag boolValue], @"Backup exclusion flag should be set for the directory containing the cache database.");
XCTAssertNil(error, @"No errors should be returned when checking backup exclusion flag.");
}
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 4c753b0d31..f8c5f51616 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -6,36 +6,58 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON
## 3.3.0
-- Applications linking against the SDK static framework no longer need to add `-ObjC` to the Other Linker Flags (`OTHER_LDFLAGS`) build setting. If you previously added this flag solely for this SDK, removing the flag may potentially reduce the overall size of your application. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
-- Removed the `armv7s` slice from the SDK to reduce its size. iPhone 5 and iPhone 5c automatically use the `armv7` slice instead. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
-- MGLPointAnnotation and custom MGLAnnotation implementations (but not MGLMultiPoint) can be backed by an MGLAnnotationView instead of an MGLAnnotationImage. MGLAnnotationView is a subclass of UIView, so you can use Core Animation and other familiar technologies with it. To associate an MGLAnnotation with an MGLAnnotationView, implement `-mapView:viewForAnnotation:` in your MGLMapViewDelegate class. ([#4801](https://github.com/mapbox/mapbox-gl-native/pull/4801))
-- The user dot now moves smoothly between user location updates while user location tracking is disabled. ([#1582](https://github.com/mapbox/mapbox-gl-native/pull/1582))
-- An MGLAnnotation can be relocated by changing its `coordinate` property in a KVO-compliant way. An MGLMultiPoint cannot be relocated. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
-- Setting the `image` property of an MGLAnnotationImage to `nil` resets it to the default red pin image and reclaims resources that can be used to customize additional annotations. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
+### Styles and data
+
- Added methods to MGLMapView for obtaining the underlying map data rendered by the current style, along with additional classes to represent complex geometry in that data. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
-- An MGLPolygon can now have interior polygons, representing holes knocked out of the overall shape. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
-- `MGLOfflinePackProgress` now indicates how many tiles have been downloaded and how much space they take up. ([#4874](https://github.com/mapbox/mapbox-gl-native/pull/4874))
-- The compass, user dot, and visible annotations are now accessible to VoiceOver users. ([#1496](https://github.com/mapbox/mapbox-gl-native/pull/1496))
-- Added a method to MGLMapView, `-anchorPointForGesture:`, that you can override to anchor gestures at a point other than the user location. ([#5302](https://github.com/mapbox/mapbox-gl-native/pull/5302))
-- Fixed an issue (speculatively) where the tile cache could be included in iCloud backups. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124))
- Improved performance viewing regions with large landcover polygons when viewing a style that uses the Mapbox Streets source. ([#2444](https://github.com/mapbox/mapbox-gl-native/pull/2444))
- Fixed a memory leak when using raster resources. ([#5141](https://github.com/mapbox/mapbox-gl-native/pull/5141))
-- The SDK is now localizable. No localizations are currently provided, other than English, but if you need a particular localization, you can install the SDK manually and drop a .lproj folder into the framework. ([#4783](https://github.com/mapbox/mapbox-gl-native/pull/4783))
-- Fixed an issue preventing KVO change notifications from being generated on MGLMapView’s `userTrackingMode` key path when `-setUserTrackingMode:animated:` is called. ([#4724](https://github.com/mapbox/mapbox-gl-native/pull/4724))
- Rendering now occurs on the main thread, fixing a hang when calling `-[MGLMapView styleURL]` before the map view has fully loaded or while the application is in the background. ([#2909](https://github.com/mapbox/mapbox-gl-native/pull/2909))
-- Improved responsiveness when zooming in then immediately panning around. ([#4595](https://github.com/mapbox/mapbox-gl-native/pull/4595))
-- Fixed a crash setting MGLMapView’s `userLocationVerticalAlignment` property before a user location update has occurred. ([#5274](https://github.com/mapbox/mapbox-gl-native/issues/5274))
-- Added category methods on NSValue for converting to and from the structure types defined in MGLGeometry.h. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
-- Added NSFormatter subclasses for converting geographic coordinates and directions into display strings. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
-- Added a new method, `-[MGLMapView cameraThatFitsCoordinateBounds:]`, to get a camera that you can pass into `-setCamera:` that fits the given coordinate bounds. ([#4790](https://github.com/mapbox/mapbox-gl-native/pull/4790))
- Added a `-reloadStyle:` action to MGLMapView to force a reload of the current style. ([#4728](https://github.com/mapbox/mapbox-gl-native/pull/4728))
- A more specific user agent string is now sent with style and tile requests. ([#4012](https://github.com/mapbox/mapbox-gl-native/pull/4012))
+- Added a new option to `MGLMapDebugMaskOptions`, `MGLMapDebugOverdrawVisualizationMask`, that highlights overlapping drawing operations instead of the usual rendered output. ([#5403](https://github.com/mapbox/mapbox-gl-native/pull/5403))
+
+### Interactivity
+
+- The compass, user dot, and visible annotations are now accessible to VoiceOver users. ([#1496](https://github.com/mapbox/mapbox-gl-native/pull/1496))
+- Added a method to MGLMapView, `-anchorPointForGesture:`, that you can override to anchor gestures at a point other than the user location. ([#5302](https://github.com/mapbox/mapbox-gl-native/pull/5302))
+- Added a property to MGLMapView, `decelerationRate`, that allows you to speed up or slow down the drift animation at the end of a user gesture. You can also use this property to disable the drift animation entirely. ([#5504](https://github.com/mapbox/mapbox-gl-native/pull/5504))
+- Improved responsiveness when zooming in then immediately panning around. ([#4595](https://github.com/mapbox/mapbox-gl-native/pull/4595))
+- Added a new method, `-[MGLMapView cameraThatFitsCoordinateBounds:]`, to get a camera that you can pass into `-setCamera:` that fits the given coordinate bounds. ([#4790](https://github.com/mapbox/mapbox-gl-native/pull/4790))
+
+### Annotations
+
+- MGLPointAnnotation and custom MGLAnnotation implementations (but not MGLMultiPoint) can be backed by an MGLAnnotationView instead of an MGLAnnotationImage. MGLAnnotationView is a subclass of UIView, so you can use Core Animation and other familiar technologies with it. To associate an MGLAnnotation with an MGLAnnotationView, implement `-mapView:viewForAnnotation:` in your MGLMapViewDelegate class. ([#4801](https://github.com/mapbox/mapbox-gl-native/pull/4801))
+- An MGLAnnotation can be relocated by changing its `coordinate` property in a KVO-compliant way. An MGLMultiPoint cannot be relocated. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
+- Setting the `image` property of an MGLAnnotationImage to `nil` resets it to the default red pin image and reclaims resources that can be used to customize additional annotations. ([#3835](https://github.com/mapbox/mapbox-gl-native/pull/3835))
+- An MGLPolygon can now have interior polygons, representing holes knocked out of the overall shape. ([#5110](https://github.com/mapbox/mapbox-gl-native/pull/5110))
+
+### User location
+
+- The user dot now moves smoothly between user location updates while user location tracking is disabled. ([#1582](https://github.com/mapbox/mapbox-gl-native/pull/1582))
+- Fixed an issue preventing KVO change notifications from being generated on MGLMapView’s `userTrackingMode` key path when `-setUserTrackingMode:animated:` is called. ([#4724](https://github.com/mapbox/mapbox-gl-native/pull/4724))
+- Fixed a crash setting MGLMapView’s `userLocationVerticalAlignment` property before a user location update has occurred. ([#5274](https://github.com/mapbox/mapbox-gl-native/issues/5274))
- Mapbox Telemetry is automatically disabled while the host application is running in the iOS Simulator. ([#4726](https://github.com/mapbox/mapbox-gl-native/pull/4726))
+
+### Offline maps
+
+- `MGLOfflinePackProgress` now indicates how many tiles have been downloaded and how much space they take up. ([#4874](https://github.com/mapbox/mapbox-gl-native/pull/4874))
+- Fixed an issue where the tile cache could be included in iCloud backups on the first launch. ([#5124](https://github.com/mapbox/mapbox-gl-native/pull/5124), [#5601](https://github.com/mapbox/mapbox-gl-native/pull/5601))
- Suppressed “Unable to make space for entry” console spew. ([#4708](https://github.com/mapbox/mapbox-gl-native/pull/4708))
-- Removed unused SVG files from the SDK’s resource bundle. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
- Deprecated `-[MGLMapView emptyMemoryCache]`. ([#4725](https://github.com/mapbox/mapbox-gl-native/pull/4725))
+
+### Packaging
+
+- Improved the design of the generated API documentation. ([#5306](https://github.com/mapbox/mapbox-gl-native/pull/5306))
+- Applications linking against the SDK static framework no longer need to add `-ObjC` to the Other Linker Flags (`OTHER_LDFLAGS`) build setting. If you previously added this flag solely for this SDK, removing the flag may potentially reduce the overall size of your application. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+- Removed the `armv7s` slice from the SDK to reduce its size. iPhone 5 and iPhone 5c automatically use the `armv7` slice instead. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+- The SDK is now localizable. No localizations are currently provided, other than English, but if you need a particular localization, you can install the SDK manually and drop a .lproj folder into the framework. ([#4783](https://github.com/mapbox/mapbox-gl-native/pull/4783))
+- Removed unused SVG files from the SDK’s resource bundle. ([#4641](https://github.com/mapbox/mapbox-gl-native/pull/4641))
+
+### Other changes
+
+- Added category methods on NSValue for converting to and from the structure types defined in MGLGeometry.h. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
+- Added NSFormatter subclasses for converting geographic coordinates and directions into display strings. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802))
- Added `MGLCoordinateInCoordinateBounds()`, a function that tests whether or not a coordinate is in a given bounds. ([#5053](https://github.com/mapbox/mapbox-gl-native/pull/5053))
-- Added a new option to `MGLMapDebugMaskOptions`, `MGLMapDebugOverdrawVisualizationMask`, that highlights overlapping drawing operations instead of the usual rendered output. ([#5403](https://github.com/mapbox/mapbox-gl-native/pull/5403))
## 3.2.3
diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
index 022902a3fd..73a505a256 100644
--- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'Mapbox-iOS-SDK'
- m.version = '3.3.0-beta.2-symbols'
+ m.version = '3.3.0-rc.1-symbols'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.'
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index df9020671b..391a3a073b 100644
--- a/platform/ios/Mapbox-iOS-SDK.podspec
+++ b/platform/ios/Mapbox-iOS-SDK.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |m|
m.name = 'Mapbox-iOS-SDK'
- m.version = '3.3.0-beta.2'
+ m.version = '3.3.0-rc.1'
m.summary = 'Open source vector map solution for iOS with full styling capabilities.'
m.description = 'Open source, OpenGL-based vector map solution for iOS with full styling capabilities and Cocoa Touch APIs.'
diff --git a/platform/ios/Mapbox.playground/Contents.swift b/platform/ios/Mapbox.playground/Contents.swift
index ed48116da9..b1b11e5e34 100644
--- a/platform/ios/Mapbox.playground/Contents.swift
+++ b/platform/ios/Mapbox.playground/Contents.swift
@@ -75,7 +75,6 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
let av = PlaygroundAnnotationView(reuseIdentifier: "annotation")
av.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
av.centerOffset = CGVector(dx: -15, dy: -15)
- av.flat = true
let centerView = UIView(frame: CGRectInset(av.bounds, 3, 3))
centerView.backgroundColor = UIColor.whiteColor()
av.addSubview(centerView)
diff --git a/platform/ios/app/MBXAnnotationView.h b/platform/ios/app/MBXAnnotationView.h
index 78dfe17699..5337ffae57 100644
--- a/platform/ios/app/MBXAnnotationView.h
+++ b/platform/ios/app/MBXAnnotationView.h
@@ -1,7 +1,4 @@
#import <Mapbox/Mapbox.h>
@interface MBXAnnotationView : MGLAnnotationView
-
-@property (nonatomic) UIColor *centerColor;
-
@end
diff --git a/platform/ios/app/MBXAnnotationView.m b/platform/ios/app/MBXAnnotationView.m
index c181211431..61f9b1c047 100644
--- a/platform/ios/app/MBXAnnotationView.m
+++ b/platform/ios/app/MBXAnnotationView.m
@@ -1,28 +1,16 @@
#import "MBXAnnotationView.h"
@interface MBXAnnotationView ()
-
-@property (nonatomic) UIView *centerView;
-
@end
@implementation MBXAnnotationView
- (void)layoutSubviews {
[super layoutSubviews];
- if (!self.centerView) {
- self.backgroundColor = [UIColor blueColor];
- self.centerView = [[UIView alloc] initWithFrame:CGRectInset(self.bounds, 1.0, 1.0)];
- self.centerView.backgroundColor = self.centerColor;
- [self addSubview:self.centerView];
- }
-}
-
-- (void)setCenterColor:(UIColor *)centerColor {
- if (![_centerColor isEqual:centerColor]) {
- _centerColor = centerColor;
- self.centerView.backgroundColor = centerColor;
- }
+
+ self.layer.borderColor = [UIColor blueColor].CGColor;
+ self.layer.borderWidth = 1;
+ self.layer.cornerRadius = 2;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
@@ -51,6 +39,7 @@
case MGLAnnotationViewDragStateCanceling:
break;
case MGLAnnotationViewDragStateEnding: {
+ self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2, 2);
[UIView animateWithDuration:.4 delay:0 usingSpringWithDamping:.4 initialSpringVelocity:.5 options:UIViewAnimationOptionCurveLinear animations:^{
self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, 1);
} completion:nil];
@@ -60,5 +49,17 @@
}
+- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
+{
+ if (([event isEqualToString:@"transform"] || [event isEqualToString:@"position"])
+ && self.dragState == MGLAnnotationViewDragStateNone)
+ {
+ CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:event];
+ animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ animation.speed = 0.1;
+ return animation;
+ }
+ return [super actionForLayer:layer forKey:event];
+}
@end
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index cd5694d835..6158a9a3de 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -587,36 +587,24 @@ static NSString * const MBXViewControllerAnnotationViewReuseIdentifer = @"MBXVie
{
annotationView = [[MBXAnnotationView alloc] initWithReuseIdentifier:MBXViewControllerAnnotationViewReuseIdentifer];
annotationView.frame = CGRectMake(0, 0, 10, 10);
- annotationView.centerColor = [UIColor whiteColor];
-
- // uncomment to flatten the annotation view against the map when the map is tilted
- // this currently causes severe performance issues when more than 2k annotations are visible
- // annotationView.flat = YES;
+ annotationView.backgroundColor = [UIColor whiteColor];
// uncomment to make the annotation view draggable
// also note that having two long press gesture recognizers on overlapping views (`self.view` & `annotationView`) will cause weird behaviour
// comment out the pin dropping functionality in the handleLongPress: method in this class to make draggable annotation views play nice
annotationView.draggable = YES;
-
-
+
// uncomment to force annotation view to maintain a constant size when the map is tilted
// by default, annotation views will shrink and grow as the move towards and away from the
// horizon. Relatedly, annotations backed by GL sprites ONLY scale with viewing distance currently.
// annotationView.scalesWithViewingDistance = NO;
-
} else {
// orange indicates that the annotation view was reused
- annotationView.centerColor = [UIColor orangeColor];
+ annotationView.backgroundColor = [UIColor orangeColor];
}
return annotationView;
}
-- (void)mapView:(MGLMapView *)mapView didDragAnnotationView:(nonnull MGLAnnotationView *)annotationView toCoordinate:(CLLocationCoordinate2D)coordinate
-{
- MGLPointAnnotation *annotation = (MGLPointAnnotation *)annotationView.annotation;
- annotation.coordinate = coordinate;
-}
-
- (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unused id <MGLAnnotation>)annotation
{
return YES;
diff --git a/platform/ios/docs/doc-README.md b/platform/ios/docs/doc-README.md
index 3e7c9e0a27..22493b1502 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -2,7 +2,7 @@
The Mapbox iOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 7.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
-[![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/ios/screenshot.png)]()
+![Mapbox iOS SDK screenshots](screenshot.png)
For setup information, check out the [Mapbox iOS SDK homepage](https://www.mapbox.com/ios-sdk/). For detailed usage instructions, read “[First steps with the Mapbox iOS SDK](https://www.mapbox.com/help/first-steps-ios-sdk/)” and consult the [online examples](https://www.mapbox.com/ios-sdk/examples/). A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) is also available.
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index c6f14dfa94..294b4f7b55 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -7,7 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
+ 35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35305D471D22AA450007D005 /* NSData+MGLAdditions.mm */; };
+ 35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35305D471D22AA450007D005 /* NSData+MGLAdditions.mm */; };
+ 35305D4A1D22AA6A0007D005 /* NSData+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 353794D01D22B3BD002C281C /* NSData+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */; settings = {ATTRIBUTES = (Private, ); }; };
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */; };
+ 35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */; };
4018B1C71CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */; };
4018B1C91CDC288A00F666AF /* MGLAnnotationView_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */; };
@@ -327,7 +332,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSData+MGLAdditions.h"; sourceTree = "<group>"; };
+ 35305D471D22AA450007D005 /* NSData+MGLAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+MGLAdditions.mm"; sourceTree = "<group>"; };
353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationViewTests.m; sourceTree = "<group>"; };
+ 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLNSDataAdditionsTests.m; sourceTree = "<group>"; };
4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView_Private.h; sourceTree = "<group>"; };
4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAnnotationView.mm; sourceTree = "<group>"; };
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationView.h; sourceTree = "<group>"; };
@@ -651,17 +659,18 @@
DA2E88521CC036F400F24E7B /* SDK Tests */ = {
isa = PBXGroup;
children = (
+ DA2E88551CC036F400F24E7B /* Info.plist */,
+ 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */,
DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */,
DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */,
DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */,
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
+ 35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */,
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */,
DA2E885F1CC0382C00F24E7B /* MGLOfflineStorageTests.m */,
- 353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */,
DA2E88601CC0382C00F24E7B /* MGLStyleTests.mm */,
- DA2E88551CC036F400F24E7B /* Info.plist */,
);
name = "SDK Tests";
path = test;
@@ -899,6 +908,8 @@
children = (
DA8848121CBAFA6200AB86E3 /* NSBundle+MGLAdditions.h */,
DA8848131CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m */,
+ 35305D461D22AA450007D005 /* NSData+MGLAdditions.h */,
+ 35305D471D22AA450007D005 /* NSData+MGLAdditions.mm */,
DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */,
DA8848151CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h */,
DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */,
@@ -915,8 +926,8 @@
children = (
40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */,
40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */,
- 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */,
4018B1C51CDC277F00F666AF /* MGLAnnotationView.h */,
+ 4018B1C31CDC277F00F666AF /* MGLAnnotationView_Private.h */,
4018B1C41CDC277F00F666AF /* MGLAnnotationView.mm */,
DA8848341CBAFB8500AB86E3 /* MGLAnnotationImage.h */,
DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */,
@@ -1004,6 +1015,7 @@
DA88481B1CBAFA6200AB86E3 /* MGLGeometry_Private.h in Headers */,
DA88485C1CBAFB9800AB86E3 /* MGLUserLocationAnnotationView.h in Headers */,
DA8848871CBB033F00AB86E3 /* Fabric.h in Headers */,
+ 35305D4A1D22AA6A0007D005 /* NSData+MGLAdditions.h in Headers */,
DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */,
DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */,
@@ -1049,6 +1061,7 @@
DABFB86E1CBE9A0F00D62B32 /* MGLCalloutView.h in Headers */,
DABFB8601CBE99E500D62B32 /* MGLMapCamera.h in Headers */,
DA737EE21D056A4E005BDA16 /* MGLMapViewDelegate.h in Headers */,
+ 353794D01D22B3BD002C281C /* NSData+MGLAdditions.h in Headers */,
DABFB86A1CBE99E500D62B32 /* MGLStyle.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1359,6 +1372,7 @@
DA2E88621CC0382C00F24E7B /* MGLOfflinePackTests.m in Sources */,
DA35A2AA1CCA058D00E826B2 /* MGLCoordinateFormatterTests.m in Sources */,
353D23961D0B0DFE002BE09D /* MGLAnnotationViewTests.m in Sources */,
+ 35E208A71D24210F00EC9A46 /* MGLNSDataAdditionsTests.m in Sources */,
DA0CD5901CF56F6A00A5F5A5 /* MGLFeatureTests.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1386,6 +1400,7 @@
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */,
DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ 35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */,
DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */,
DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */,
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
@@ -1427,6 +1442,7 @@
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */,
DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ 35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */,
DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */,
DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
@@ -1717,6 +1733,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
buildSettings = {
+ BITCODE_GENERATION_MODE = bitcode;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1757,6 +1774,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
buildSettings = {
+ BITCODE_GENERATION_MODE = bitcode;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1819,6 +1837,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
buildSettings = {
+ BITCODE_GENERATION_MODE = bitcode;
HEADER_SEARCH_PATHS = (
../default,
../../include,
@@ -1849,6 +1868,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = DAC07C961CBB2CD6000CB309 /* mbgl.xcconfig */;
buildSettings = {
+ BITCODE_GENERATION_MODE = bitcode;
HEADER_SEARCH_PATHS = (
../default,
../../include,
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
index 67d877cda3..aec0d5fe9f 100644
--- a/platform/ios/jazzy.yml
+++ b/platform/ios/jazzy.yml
@@ -21,6 +21,9 @@ custom_categories:
- MGLMapCamera
- MGLMapDebugMaskOptions
- MGLMapView
+ - MGLMapViewDecelerationRateFast
+ - MGLMapViewDecelerationRateImmediate
+ - MGLMapViewDecelerationRateNormal
- MGLMapViewDelegate
- MGLStyle
- MGLStyleDefaultVersion
@@ -31,6 +34,7 @@ custom_categories:
- MGLAnnotationImage
- MGLAnnotationVerticalAlignment
- MGLAnnotationView
+ - MGLAnnotationViewDragState
- MGLCalloutView
- MGLCalloutViewDelegate
- MGLMultiPoint
@@ -81,6 +85,7 @@ custom_categories:
- MGLCoordinateBoundsMake
- MGLCoordinateBoundsOffset
- MGLCoordinateFormatter
+ - MGLCoordinateInCoordinateBounds
- MGLCoordinateSpan
- MGLCoordinateSpanEqualToCoordinateSpan
- MGLCoordinateSpanMake
diff --git a/platform/ios/originals/screenshots.sketch b/platform/ios/originals/screenshots.sketch
new file mode 100644
index 0000000000..9098b3db60
--- /dev/null
+++ b/platform/ios/originals/screenshots.sketch
Binary files differ
diff --git a/platform/ios/platform.gyp b/platform/ios/platform.gyp
index e4abff27e3..4cb26091d4 100644
--- a/platform/ios/platform.gyp
+++ b/platform/ios/platform.gyp
@@ -129,6 +129,14 @@
'CLANG_ENABLE_OBJC_ARC': 'YES',
'CLANG_ENABLE_MODULES': 'YES',
},
+
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'BITCODE_GENERATION_MODE': 'bitcode',
+ },
+ },],
+ ],
'link_settings': {
'libraries': [ '<@(libraries)' ],
diff --git a/platform/ios/screenshot.png b/platform/ios/screenshot.png
index 5bea3095a6..6f5f7bb7d8 100644
--- a/platform/ios/screenshot.png
+++ b/platform/ios/screenshot.png
Binary files differ
diff --git a/platform/ios/scripts/document.sh b/platform/ios/scripts/document.sh
index 3059550ba0..a4fd5b98b8 100755
--- a/platform/ios/scripts/document.sh
+++ b/platform/ios/scripts/document.sh
@@ -27,11 +27,13 @@ README=/tmp/mbgl/README.md
cp platform/ios/docs/doc-README.md "${README}"
# http://stackoverflow.com/a/4858011/4585461
echo "## Changes in version ${RELEASE_VERSION}" >> "${README}"
-sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^##/q' -e 'p' -e 'ba' -e '}' platform/ios/CHANGELOG.md >> "${README}"
+sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^## /q' -e 'p' -e 'ba' -e '}' platform/ios/CHANGELOG.md >> "${README}"
rm -rf ${OUTPUT}
mkdir -p ${OUTPUT}
+cp platform/ios/screenshot.png "${OUTPUT}"
+
jazzy \
--config platform/ios/jazzy.yml \
--sdk iphonesimulator \
@@ -40,6 +42,7 @@ jazzy \
--module-version ${SHORT_VERSION} \
--readme ${README} \
--root-url https://www.mapbox.com/ios-sdk/api/${RELEASE_VERSION}/ \
+ --theme platform/darwin/docs/theme \
--output ${OUTPUT}
# https://github.com/realm/jazzy/issues/411
find ${OUTPUT} -name *.html -exec \
diff --git a/platform/ios/scripts/package.sh b/platform/ios/scripts/package.sh
index 8bc2a3fd63..5650dc144b 100755
--- a/platform/ios/scripts/package.sh
+++ b/platform/ios/scripts/package.sh
@@ -178,11 +178,24 @@ if [[ "${GCC_GENERATE_DEBUGGING_SYMBOLS}" == false ]]; then
fi
fi
+function create_local_podspec {
+ step "Creating local podspec"
+ POD_SOURCE_PATH=' :path => ".",'
+ POD_FRAMEWORKS=" m.vendored_frameworks = '"${NAME}".framework'"
+ [[ $SYMBOLS = YES ]] && POD_SUFFIX="-symbols" || POD_SUFFIX=""
+ POD_LOCALSPEC=${OUTPUT}/$1/${NAME}-iOS-SDK${POD_SUFFIX}.podspec
+ sed "s/.*:http.*/${POD_SOURCE_PATH}/" platform/ios/${NAME}-iOS-SDK${POD_SUFFIX}.podspec > ${POD_LOCALSPEC}
+ sed -i.bak "s/.*vendored_frameworks.*/${POD_FRAMEWORKS}/" ${POD_LOCALSPEC}
+ rm -rf ${POD_LOCALSPEC}.bak
+ cp -pv LICENSE.md ${OUTPUT}/$1/
+}
+
if [[ ${BUILD_STATIC} == true ]]; then
stat "${OUTPUT}/static/${NAME}.framework"
fi
if [[ ${BUILD_DYNAMIC} == true ]]; then
stat "${OUTPUT}/dynamic/${NAME}.framework"
+ create_local_podspec "dynamic"
fi
if [[ ${BUILD_STATIC} == true ]]; then
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
index 31fd39c83d..fb13113c81 100644
--- a/platform/ios/src/MGLAPIClient.m
+++ b/platform/ios/src/MGLAPIClient.m
@@ -1,5 +1,6 @@
#import "MGLAPIClient.h"
#import "NSBundle+MGLAdditions.h"
+#import "NSData+MGLAdditions.h"
#import "MGLAccountManager.h"
static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS";
@@ -9,6 +10,7 @@ static NSString * const MGLAPIClientEventsPath = @"events/v2";
static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent";
static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type";
static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json";
+static NSString * const MGLAPIClientHeaderFieldContentEncodingKey = @"Content-Encoding";
static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
@interface MGLAPIClient ()
@@ -82,8 +84,22 @@ static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
[request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
[request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
[request setHTTPMethod:MGLAPIClientHTTPMethodPost];
+
NSData *jsonData = [self serializedDataForEvents:events];
- [request setHTTPBody:jsonData];
+
+ // Compressing less than 3 events can have a negative impact on the size.
+ if (events.count > 2) {
+ NSData *compressedData = [jsonData mgl_compressedData];
+ [request setValue:@"deflate" forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
+ [request setHTTPBody:compressedData];
+ }
+
+ // Set JSON data if events.count were less than 3 or something went wrong with compressing HTTP body data.
+ if (!request.HTTPBody) {
+ [request setValue:nil forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
+ [request setHTTPBody:jsonData];
+ }
+
return [request copy];
}
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 18e4985884..622c31c7b3 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -6,112 +6,223 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLAnnotation;
+/** These constants indicate the current drag state of an annotation view. **/
typedef NS_ENUM(NSUInteger, MGLAnnotationViewDragState) {
- MGLAnnotationViewDragStateNone = 0, // View is sitting on the map.
- MGLAnnotationViewDragStateStarting, // View is beginning to drag.
- MGLAnnotationViewDragStateDragging, // View is being dragged.
- MGLAnnotationViewDragStateCanceling, // View dragging was cancelled and will be returned to its starting positon.
- MGLAnnotationViewDragStateEnding // View was dragged.
+ /**
+ The view is not involved in a drag operation.
+ */
+ MGLAnnotationViewDragStateNone = 0,
+ /**
+ An action occurred that indicated the view should begin dragging.
+
+ The map view automatically moves draggable annotation views to this state
+ in response to the dragging the view after pressing and holding on it.
+ */
+ MGLAnnotationViewDragStateStarting,
+ /**
+ The view is in the midst of a drag operation and is actively tracking the
+ user’s gesture.
+ */
+ MGLAnnotationViewDragStateDragging,
+ /**
+ An action occurred that indicated the view should cancel the drag
+ operation.
+ */
+ MGLAnnotationViewDragStateCanceling,
+ /**
+ An action occurred that indicated the view was dropped by the user.
+
+ The map view automatically moves annotation views to this state in response
+ to the user lifting their finger at the end of a drag gesture.
+ */
+ MGLAnnotationViewDragStateEnding,
};
-/** The MGLAnnotationView class is responsible for representing point-based annotation markers as a view. Annotation views represent an annotation object, which is an object that corresponds to the MGLAnnotation protocol. When an annotation’s coordinate point is visible on the map view, the map view delegate is asked to provide a corresponding annotation view. If an annotation view is created with a reuse identifier, the map view may recycle the view when it goes offscreen. */
+/**
+ The `MGLAnnotationView` class is responsible for marking a point annotation
+ with a view. Annotation views represent an annotation object, which is an
+ object that corresponds to the `MGLAnnotation` protocol. When an annotation’s
+ geographic coordinate is visible in the map view, the map view asks its
+ delegate to a corresponding annotation view. If an annotation view is created
+ with a reuse identifier, the map view may recycle the view when it goes
+ offscreen.
+
+ Annotation views are compatible with UIKit, Core Animation, and other Cocoa
+ Touch frameworks. On the other hand, if you do not need animation or
+ interactivity such as dragging, you can use an `MGLAnnotationImage` instead to
+ conserve memory and optimize drawing performance.
+ */
@interface MGLAnnotationView : UIView
+#pragma mark Initializing and Preparing the View
+
/**
Initializes and returns a new annotation view object.
- @param reuseIdentifier The string that identifies that this annotation view is reusable.
+ The reuse identifier provides a way for you to improve performance by recycling
+ annotation views as they enter and leave the map’s viewport. As an annotation
+ leaves the viewport, the map view moves its associated view to a reuse queue.
+ When a new annotation becomes visible, you can request a view for that
+ annotation by passing the appropriate reuse identifier string to the
+ `-[MGLMapView dequeueReusableAnnotationViewWithIdentifier:` method.
+
+ @param reuseIdentifier A unique string identifier for this view that allows you
+ to reuse this view with multiple similar annotations. You can set this
+ parameter to `nil` if you don’t intend to reuse the view, but it is a good
+ idea in general to specify a reuse identifier to avoid creating redundant
+ views.
@return The initialized annotation view object.
*/
- (instancetype)initWithReuseIdentifier:(nullable NSString *)reuseIdentifier;
/**
- This property will be set to the associated annotation when the view is visible.
+ Called when the view is removed from the reuse queue.
+
+ The default implementation of this method does nothing. You can override it in
+ your custom annotation view implementation to put the view in a known state
+ before it is returned to your map view delegate.
+ */
+- (void)prepareForReuse;
+
+/**
+ The annotation object currently associated with the view.
- When the view is queued and waiting to be reused, the value will be set to nil.
+ You should not change the value of this property directly. This property
+ contains a non-`nil` value while the annotation view is visible on the map. If
+ the view is queued, waiting to be reused, the value is `nil`.
*/
@property (nonatomic, readonly, nullable) id <MGLAnnotation> annotation;
/**
- The string that identifies that this annotation view is reusable. (read-only)
+ The string that identifies that this annotation view is reusable.
- You specify the reuse identifier when you create the view. You use the identifier later to retrieve an annotation view that was
- created previously but which is currently unused because its annotation is not on screen.
+ You specify the reuse identifier when you create the view. You use the
+ identifier later to retrieve an annotation view that was created previously but
+ which is currently unused because its annotation is not on-screen.
- If you define distinctly different types of annotations (with distinctly different annotation views to go with them), you can
- differentiate between the annotation types by specifying different reuse identifiers for each one.
+ If you define distinctly different types of annotations (with distinctly
+ different annotation views to go with them), you can differentiate between the
+ annotation types by specifying different reuse identifiers for each one.
*/
@property (nonatomic, readonly, nullable) NSString *reuseIdentifier;
+#pragma mark Configuring the Appearance
+
/**
- Annotation view is centered at the coordinate point of the associated annotation.
+ The offset, measured in points, at which to place the center of the view.
- By changing this property you can reposition the view as needed. The offset is measured in points.
- Positive offset moves the annotation view towards the bottom right, while negative offset moves it towards the top left.
+ By default, the center point of an annotation view is placed at the geographic
+ coordinate point of the associated annotation. If you do not want the view to
+ be centered, you can use this property to reposition the view. The offset’s
+ `dx` and `dy` values are measured in points. Positive offset values move the
+ annotation view down and to the right, while negative values move it up and to
+ the left.
+
+ Set the offset if the annotation view’s visual center point is somewhere other
+ than the logical center of the view. For example, the view may contain an image
+ that depicts a downward-pointing pushpin or thumbtack, with the tip positioned
+ at the center-bottom of the view. In that case, you would set the offset’s `dx`
+ to zero and its `dy` to half the height of the view.
*/
@property (nonatomic) CGVector centerOffset;
-
/**
- Setting this property to YES will force the annotation view to tilt according to the associated map view.
+ A Boolean value that determines whether the annotation view grows and shrinks
+ as the distance between the viewpoint and the annotation view changes on a
+ tilted map.
+
+ When the value of this property is `YES` and the map is tilted, the annotation
+ view appears smaller if it is towards the top of the view (closer to the
+ horizon) and larger if it is towards the bottom of the view (closer to the
+ viewpoint). This is also the behavior of `MGLAnnotationImage` objects. When the
+ value of this property is `NO` or the map’s pitch is zero, the annotation view
+ remains the same size regardless of its position on-screen.
+
+ The default value of this property is `YES`. Set this property to `NO` if the
+ view’s legibility is important.
*/
-@property (nonatomic, assign, getter=isFlat) BOOL flat;
+@property (nonatomic, assign) BOOL scalesWithViewingDistance;
+
+#pragma mark Managing the Selection State
/**
- Defaults to NO and becomes YES when the view is tapped on.
+ A Boolean value indicating whether the annotation view is currently selected.
+
+ You should not set the value of this property directly. If the property is set
+ to `YES`, the annotation view is displaying a callout.
- Selecting another view will first deselect the currently selected view.
- This property should not be changed directly.
+ By default, this property is set to `NO` and becomes `YES` when the user taps
+ the view. Selecting another annotation, whether it is associated with an
+ `MGLAnnotationView` or `MGLAnnotationImage` object, deselects any currently
+ selected view.
+
+ Setting this property changes the view’s appearance to reflect the new value
+ immediately. If you want the change to be animated, use the
+ `-setSelected:animated:` method instead.
*/
@property (nonatomic, assign, getter=isSelected) BOOL selected;
/**
- Subclasses may override this method in order to customize appearance.
- This method should not be called directly.
+ Sets the selection state of the annotation view with an optional animation.
+
+ You should not call this method directly. A map view calls this method in
+ response to user interactions with the annotation. Subclasses may override this
+ method in order to customize the appearance of the view depending on its
+ selection state.
+
+ @param selected `YES` if the view should display itself as selected; `NO`
+ if it should display itself as unselected.
+ @param animated `YES` if the change in selection state is animated; `NO` if the
+ change is immediate.
*/
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;
/*
- This property defaults to YES. Setting it to NO will cause the annotation view to ignore all touch events.
- Subclasses may use this property to customize the appearance.
+ A Boolean value indicating whether the annotation is enabled.
+
+ The default value of this property is `YES`. If the value of this property is
+ `NO`, the annotation view ignores touch events and cannot be selected.
+ Subclasses may also customize the appearance of the view depending on its
+ enabled state.
*/
@property (nonatomic, assign, getter=isEnabled) BOOL enabled;
+#pragma mark Supporting Drag Operations
+
/**
- Setting this property to YES will make the view draggable. Long-press followed by a pan gesture will start to move the
- view around the map. `-mapView:didDragAnnotationView:toCoordinate:` will be called when a view is dropped.
+ A Boolean value indicating whether the annotation view is draggable.
+
+ If this property is set to `YES`, the user can drag the annotation after
+ pressing and holding the view, and the associated annotation object must also
+ implement the `-setCoordinate:` method. The default value of this property is
+ `NO`.
+
+ Setting this property to `YES` lets the map view know that the annotation is
+ always draggable. In other words, you cannot conditionalize drag operations by
+ attempting to stop an operation that has already been initiated; doing so can
+ lead to undefined behavior. Once begun, the drag operation should always
+ continue to completion.
*/
@property (nonatomic, assign, getter=isDraggable) BOOL draggable;
/**
- All states are handled automatically when `draggable` is set to YES.
- Custom animations can be achieved by overriding setDragState:animated:
+ The current drag state of the annotation view.
+
+ All states are handled automatically when the `draggable` property is set to
+ `YES`. To perform a custom animation in response to a change to this property,
+ override the `-setDragState:animated:` method.
*/
@property (nonatomic, readonly) MGLAnnotationViewDragState dragState;
/**
- Called when the `dragState` changes.
+ Sets the current drag state for the annotation view.
- Implementer may override this method in order to customize animations in subclasses.
+ You can override this method to animate a custom annotation view as the user
+ drags it. As the system detects user actions that would indicate a drag, it
+ calls this method to update the drag state.
*/
- (void)setDragState:(MGLAnnotationViewDragState)dragState animated:(BOOL)animated NS_REQUIRES_SUPER;
-/**
- Setting this property to YES will cause the annotation view to shrink as it approaches the horizon and grow as it moves away from the
- horizon when the associated map view is tilted. Conversely, setting this property to NO will ensure that the annotation view maintains
- a constant size even when the map view is tilted. To maintain consistency with annotation representations that are not backed by an
- MGLAnnotationView object, the default value of this property is YES.
- */
-@property (nonatomic, assign, getter=isScaledWithViewingDistance) BOOL scalesWithViewingDistance;
-
-/**
- Called when the view is removed from the reuse queue.
-
- The default implementation of this method does nothing. You can override it in your custom annotation views and use it to put the view
- in a known state before it is returned to your map view delegate.
- */
-- (void)prepareForReuse;
-
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAnnotationView.mm b/platform/ios/src/MGLAnnotationView.mm
index e086e3bde5..f8591f036b 100644
--- a/platform/ios/src/MGLAnnotationView.mm
+++ b/platform/ios/src/MGLAnnotationView.mm
@@ -54,42 +54,46 @@
[self didChangeValueForKey:@"selected"];
}
-- (void)setCenter:(CGPoint)center
+- (CGPoint)center
{
- [self setCenter:center pitch:0];
+ CGPoint center = super.center;
+ center.x -= _centerOffset.dx;
+ center.y -= _centerOffset.dy;
+ return center;
}
-- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch
+- (void)setCenter:(CGPoint)center
{
center.x += _centerOffset.dx;
center.y += _centerOffset.dy;
- [super setCenter:center];
-
- // Omit applying a new transformation while the view is being dragged.
- if (self.dragState == MGLAnnotationViewDragStateDragging) {
- return;
- }
-
- if (self.flat)
- {
- [self updatePitch:pitch];
- }
-
- if (self.scalesWithViewingDistance)
- {
- [self updateScaleForPitch:pitch];
- }
+ super.center = center;
+ [self updateTransform];
}
-- (void)updatePitch:(CGFloat)pitch
+- (void)setScalesWithViewingDistance:(BOOL)scalesWithViewingDistance
{
- CATransform3D t = CATransform3DRotate(CATransform3DIdentity, MGLRadiansFromDegrees(pitch), 1.0, 0, 0);
- self.layer.transform = t;
+ if (_scalesWithViewingDistance != scalesWithViewingDistance)
+ {
+ _scalesWithViewingDistance = scalesWithViewingDistance;
+ [self updateTransform];
+ }
}
-- (void)updateScaleForPitch:(CGFloat)pitch
+- (void)updateTransform
{
+ // Omit applying a new transformation while the view is being dragged.
+ if (self.dragState == MGLAnnotationViewDragStateDragging)
+ {
+ return;
+ }
+
+ self.layer.transform = CATransform3DIdentity;
+ if ( ! self.scalesWithViewingDistance)
+ {
+ return;
+ }
+
CGFloat superviewHeight = CGRectGetHeight(self.superview.frame);
if (superviewHeight > 0.0) {
// Find the maximum amount of scale reduction to apply as the view's center moves from the top
@@ -104,7 +108,7 @@
// as the map view will allow). The map view's maximum pitch is defined in `mbgl::util::PITCH_MAX`.
// Since it is possible for the map view to report a pitch less than 0 due to the nature of
// how the gesture information is captured, the value is guarded with MAX.
- CGFloat pitchIntensity = MAX(pitch, 0) / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
+ CGFloat pitchIntensity = MAX(self.mapView.camera.pitch, 0) / MGLDegreesFromRadians(mbgl::util::PITCH_MAX);
// The pitch adjusted scale is the inverse proportion of the maximum possible scale reduction
// multiplied by the pitch intensity. For example, if the maximum scale reduction is 75% and the
@@ -112,7 +116,7 @@
// reduction is then normalized for a scale of 1.0.
CGFloat pitchAdjustedScale = 1.0 - maxScaleReduction * pitchIntensity;
- CATransform3D transform = self.flat ? self.layer.transform : CATransform3DIdentity;
+ CATransform3D transform = CATransform3DIdentity;
self.layer.transform = CATransform3DScale(transform, pitchAdjustedScale, pitchAdjustedScale, 1);
}
}
@@ -185,9 +189,8 @@
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
- CGPoint center = [sender locationInView:sender.view.superview];
- [self setCenter:center pitch:self.mapView.camera.pitch];
-
+ self.center = [sender locationInView:sender.view.superview];
+
if (sender.state == UIGestureRecognizerStateEnded) {
self.dragState = MGLAnnotationViewDragStateNone;
}
@@ -206,19 +209,24 @@
if (dragState == MGLAnnotationViewDragStateStarting)
{
+ [self.mapView.calloutViewForSelectedAnnotation dismissCalloutAnimated:animated];
[self.superview bringSubviewToFront:self];
}
-
- if (dragState == MGLAnnotationViewDragStateEnding)
+ else if (dragState == MGLAnnotationViewDragStateCanceling)
+ {
+ self.panGestureRecognizer.enabled = NO;
+ self.longPressRecognizer.enabled = NO;
+ self.center = [self.mapView convertCoordinate:self.annotation.coordinate toPointToView:self.mapView];
+ self.panGestureRecognizer.enabled = YES;
+ self.longPressRecognizer.enabled = YES;
+ self.dragState = MGLAnnotationViewDragStateNone;
+ }
+ else if (dragState == MGLAnnotationViewDragStateEnding)
{
- if ([self.mapView.delegate respondsToSelector:@selector(mapView:didDragAnnotationView:toCoordinate:)])
+ if ([self.annotation respondsToSelector:@selector(setCoordinate:)])
{
- CGPoint offsetAdjustedCenter = self.center;
- offsetAdjustedCenter.x -= self.centerOffset.dx;
- offsetAdjustedCenter.y -= self.centerOffset.dy;
-
- CLLocationCoordinate2D coordinate = [self.mapView convertPoint:offsetAdjustedCenter toCoordinateFromView:self.mapView];
- [self.mapView.delegate mapView:self.mapView didDragAnnotationView:self toCoordinate:coordinate];
+ CLLocationCoordinate2D coordinate = [self.mapView convertPoint:self.center toCoordinateFromView:self.mapView];
+ [(NSObject *)self.annotation setValue:[NSValue valueWithMGLCoordinate:coordinate] forKey:@"coordinate"];
}
}
}
@@ -231,19 +239,19 @@
{
return NO;
}
-
+
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
- return YES;
+ return otherGestureRecognizer == _longPressRecognizer || otherGestureRecognizer == _panGestureRecognizer;
}
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
// Allow mbgl to drive animation of this view’s bounds.
- if ([event isEqualToString:@"bounds"])
+ if ([event isEqualToString:@"bounds"] || [event isEqualToString:@"position"])
{
return [NSNull null];
}
diff --git a/platform/ios/src/MGLAnnotationView_Private.h b/platform/ios/src/MGLAnnotationView_Private.h
index 8f4f4fc17a..8a0af3565c 100644
--- a/platform/ios/src/MGLAnnotationView_Private.h
+++ b/platform/ios/src/MGLAnnotationView_Private.h
@@ -11,8 +11,6 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readwrite, nullable) id <MGLAnnotation> annotation;
@property (nonatomic, weak) MGLMapView *mapView;
-- (void)setCenter:(CGPoint)center pitch:(CGFloat)pitch;
-
@end
NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 5512264a35..4cd4a58131 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -21,6 +21,15 @@ NS_ASSUME_NONNULL_BEGIN
@protocol MGLCalloutView;
@protocol MGLFeature;
+/** The default deceleration rate for a map view. */
+extern const CGFloat MGLMapViewDecelerationRateNormal;
+
+/** A fast deceleration rate for a map view. */
+extern const CGFloat MGLMapViewDecelerationRateFast;
+
+/** Disables decleration in a map view. */
+extern const CGFloat MGLMapViewDecelerationRateImmediate;
+
/** The vertical alignment of an annotation within a map view. */
typedef NS_ENUM(NSUInteger, MGLAnnotationVerticalAlignment) {
/** Aligns the annotation vertically in the center of the map view. */
@@ -404,6 +413,17 @@ IB_DESIGNABLE
*/
@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
+/**
+ A floating-point value that determines the rate of deceleration after the user
+ lifts their finger.
+
+ Your application can use the `MGLMapViewDecelerationRateNormal` and
+ `MGLMapViewDecelerationRateFast` constants as reference points for reasonable
+ deceleration rates. `MGLMapViewDecelerationRateImmediate` can be used to
+ disable deceleration entirely.
+ */
+@property(nonatomic) CGFloat decelerationRate;
+
#pragma mark Manipulating the Viewpoint
/**
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index a036e6d812..28b030b7dd 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -51,6 +51,10 @@
class MBGLView;
class MGLAnnotationContext;
+const CGFloat MGLMapViewDecelerationRateNormal = UIScrollViewDecelerationRateNormal;
+const CGFloat MGLMapViewDecelerationRateFast = UIScrollViewDecelerationRateFast;
+const CGFloat MGLMapViewDecelerationRateImmediate = 0.0;
+
/// Indicates the manner in which the map view is tracking the user location.
typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {
/// The map view is not yet tracking the user location.
@@ -500,6 +504,8 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self addGestureRecognizer:_twoFingerDrag];
_pitchEnabled = YES;
+ _decelerationRate = MGLMapViewDecelerationRateNormal;
+
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
_quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
@@ -579,8 +585,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale);
[scaleImage drawInRect:{ CGPointZero, scaleImage.size }];
+ CGFloat weight = &UIFontWeightUltraLight ? UIFontWeightUltraLight : -0.8;
NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{
- NSFontAttributeName: [UIFont systemFontOfSize:9 weight:UIFontWeightUltraLight],
+ NSFontAttributeName: [UIFont systemFontOfSize:9 weight:weight],
NSForegroundColorAttributeName: [UIColor whiteColor],
}];
CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2,
@@ -1201,18 +1208,17 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled)
{
CGPoint velocity = [pan velocityInView:pan.view];
- if (sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) < 100)
+ if (self.decelerationRate == MGLMapViewDecelerationRateImmediate || sqrtf(velocity.x * velocity.x + velocity.y * velocity.y) < 100)
{
// Not enough velocity to overcome friction
velocity = CGPointZero;
}
- NSTimeInterval duration = UIScrollViewDecelerationRateNormal;
BOOL drift = ! CGPointEqualToPoint(velocity, CGPointZero);
if (drift)
{
- CGPoint offset = CGPointMake(velocity.x * duration / 4, velocity.y * duration / 4);
- _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationInSeconds(duration));
+ CGPoint offset = CGPointMake(velocity.x * self.decelerationRate / 4, velocity.y * self.decelerationRate / 4);
+ _mbglMap->moveBy({ offset.x, offset.y }, MGLDurationInSeconds(self.decelerationRate));
}
[self notifyGestureDidEndWithDrift:drift];
@@ -1282,7 +1288,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
velocity = 0;
}
- NSTimeInterval duration = velocity > 0 ? 1 : 0.25;
+ NSTimeInterval duration = (velocity > 0 ? 1 : 0.25) * self.decelerationRate;
CGFloat scale = self.scale * pinch.scale;
CGFloat newScale = scale;
@@ -1300,12 +1306,12 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
velocity = 0;
}
- if (velocity)
+ if (velocity && duration)
{
_mbglMap->setScale(newScale, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
}
- [self notifyGestureDidEndWithDrift:velocity];
+ [self notifyGestureDidEndWithDrift:velocity && duration];
[self unrotateIfNeededForGesture];
}
@@ -1354,21 +1360,20 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
{
CGFloat velocity = rotate.velocity;
-
- if (fabs(velocity) > 3)
+ CGFloat decelerationRate = self.decelerationRate;
+ if (decelerationRate != MGLMapViewDecelerationRateImmediate && fabs(velocity) > 3)
{
CGFloat radians = self.angle + rotate.rotation;
- NSTimeInterval duration = UIScrollViewDecelerationRateNormal;
- CGFloat newRadians = radians + velocity * duration * 0.1;
+ CGFloat newRadians = radians + velocity * decelerationRate * 0.1;
CGFloat newDegrees = MGLDegreesFromRadians(newRadians) * -1;
- _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(duration));
+ _mbglMap->setBearing(newDegrees, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y }, MGLDurationInSeconds(decelerationRate));
[self notifyGestureDidEndWithDrift:YES];
__weak MGLMapView *weakSelf = self;
- [self animateWithDelay:duration animations:^
+ [self animateWithDelay:decelerationRate animations:^
{
[weakSelf unrotateIfNeededForGesture];
}];
@@ -1767,16 +1772,19 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (annotation == [self annotationWithTag:annotationTag])
{
const mbgl::Point<double> point = MGLPointFromLocationCoordinate2D(annotation.coordinate);
-
+
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
- NSString *symbolName;
- if (!annotationContext.annotationView)
+ if (annotationContext.annotationView)
{
- MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
- symbolName = annotationImage.styleIconIdentifier;
+ // Redundantly move the associated annotation view outside the scope of the animation-less transaction block in -updateAnnotationViews.
+ annotationContext.annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
}
- _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String ?: "" });
+ MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
+ NSString *symbolName = annotationImage.styleIconIdentifier;
+
+ // Update the annotation’s backing geometry to match the annotation model object. Any associated annotation view is also moved by side effect. However, -updateAnnotationViews disables the view’s animation actions, because it can’t distinguish between moves due to the viewport changing and moves due to the annotation’s coordinate changing.
+ _mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, symbolName.UTF8String });
if (annotationTag == _selectedAnnotationTag)
{
[self deselectAnnotation:annotation animated:YES];
@@ -2934,6 +2942,12 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self updateAnnotationContainerViewWithAnnotationViews:newAnnotationViews];
[self didChangeValueForKey:@"annotations"];
+
+ if ([self.delegate respondsToSelector:@selector(mapView:didAddAnnotationViews:)])
+ {
+ [self.delegate mapView:self didAddAnnotationViews:newAnnotationViews];
+ }
+
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
@@ -2997,6 +3011,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
if (annotationView)
{
annotationView.annotation = annotation;
+ annotationView.mapView = self;
CGRect bounds = UIEdgeInsetsInsetRect({ CGPointZero, annotationView.frame.size }, annotationView.alignmentRectInsets);
_largestAnnotationViewSize = CGSizeMake(MAX(_largestAnnotationViewSize.width, CGRectGetWidth(bounds)),
@@ -4548,6 +4563,9 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
return;
}
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+
for (auto &pair : _annotationContextsByAnnotationTag)
{
CGRect viewPort = CGRectInset(self.bounds,
@@ -4568,8 +4586,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
[self.glView addSubview:annotationView];
}
- CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
- [annotationView setCenter:center pitch:self.camera.pitch];
+ annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
annotationView.mapView = self;
annotationContext.annotationView = annotationView;
}
@@ -4582,10 +4599,11 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
}
else
{
- CGPoint center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
- [annotationView setCenter:center pitch:self.camera.pitch];
+ annotationView.center = [self convertCoordinate:annotationContext.annotation.coordinate toPointToView:self];
}
}
+
+ [CATransaction commit];
}
- (void)enqueueAnnotationViewForAnnotationContext:(MGLAnnotationContext &)annotationContext
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 173b40f93f..12a0658a51 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -6,7 +6,15 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLMapView;
-/** The MGLMapViewDelegate protocol defines a set of optional methods that you can use to receive map-related update messages. Because many map operations require the `MGLMapView` class to load data asynchronously, the map view calls these methods to notify your application when specific operations complete. The map view also uses these methods to request annotation marker symbology and to manage interactions with those markers. */
+/**
+ The `MGLMapViewDelegate` protocol defines a set of optional methods that you
+ can use to receive map-related update messages. Because many map operations
+ require the `MGLMapView` class to load data asynchronously, the map view calls
+ these methods to notify your application when specific operations complete. The
+ map view also uses these methods to request information about annotations
+ displayed on the map, such as the styles and interaction modes to apply to
+ individual annotations.
+ */
@protocol MGLMapViewDelegate <NSObject>
@optional
@@ -14,30 +22,39 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark Responding to Map Position Changes
/**
- Tells the delegate that the region displayed by the map view is about to change.
+ Tells the delegate that the viewpoint depicted by the map view is about to
+ change.
- This method is called whenever the currently displayed map region will start changing.
+ This method is called whenever the currently displayed map camera will start
+ changing for any reason.
- @param mapView The map view whose visible region will change.
+ @param mapView The map view whose viewpoint will change.
@param animated Whether the change will cause an animated effect on the map.
*/
- (void)mapView:(MGLMapView *)mapView regionWillChangeAnimated:(BOOL)animated;
/**
- Tells the delegate that the region displayed by the map view is changing.
+ Tells the delegate that the viewpoint depicted by the map view is changing.
- This method is called whenever the currently displayed map region changes. During movement, this method may be called many times to report updates to the map position. Therefore, your implementation of this method should be as lightweight as possible to avoid affecting performance.
+ This method is called as the currently displayed map camera changes as part of
+ an animation, whether due to a user gesture or due to a call to a method such
+ as `-[MGLMapView setCamera:animated:]`. During the animation, this method may
+ be called many times to report updates to the viewpoint. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
- @param mapView The map view whose visible region is changing.
+ @param mapView The map view whose viewpoint is changing.
*/
- (void)mapViewRegionIsChanging:(MGLMapView *)mapView;
/**
- Tells the delegate that the region displayed by the map view just changed.
+ Tells the delegate that the viewpoint depicted by the map view has finished
+ changing.
- This method is called whenever the currently displayed map region has finished changing.
+ This method is called whenever the currently displayed map camera has finished
+ changing, after any calls to `-mapViewRegionIsChanging:` due to animation.
- @param mapView The map view whose visible region changed.
+ @param mapView The map view whose viewpoint has changed.
@param animated Whether the change caused an animated effect on the map.
*/
- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated;
@@ -47,7 +64,8 @@ NS_ASSUME_NONNULL_BEGIN
/**
Tells the delegate that the map view will begin to load.
- This method is called whenever the map view starts loading, including when a new style has been set and the map must reload.
+ This method is called whenever the map view starts loading, including when a
+ new style has been set and the map must reload.
@param mapView The map view that is starting to load.
*/
@@ -56,7 +74,8 @@ NS_ASSUME_NONNULL_BEGIN
/**
Tells the delegate that the map view has finished loading.
- This method is called whenever the map view finishes loading, either after the initial load or after a style change has forced a reload.
+ This method is called whenever the map view finishes loading, either after the
+ initial load or after a style change has forced a reload.
@param mapView The map view that has finished loading.
*/
@@ -71,56 +90,87 @@ NS_ASSUME_NONNULL_BEGIN
// TODO
- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-// TODO
+/**
+ Tells the delegate that the map view is about to redraw.
+
+ This method is called any time the map view needs to redraw due to a change in
+ the viewpoint or style property transition. This method may be called very
+ frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
+
+ @param mapView The map view that is about to redraw.
+ */
- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
-// TODO
+/**
+ Tells the delegate that the map view has just redrawn.
+
+ This method is called any time the map view needs to redraw due to a change in
+ the viewpoint or style property transition. This method may be called very
+ frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
+
+ @param mapView The map view that has just redrawn.
+ */
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
#pragma mark Tracking User Location
/**
- Tells the delegate that the map view will begin tracking the user's location.
+ Tells the delegate that the map view will begin tracking the user’s location.
- This method is called when the value of the `showsUserLocation` property changes to `YES`.
+ This method is called when the value of the `showsUserLocation` property
+ changes to `YES`.
- @param mapView The map view that is tracking the user's location.
+ @param mapView The map view that is tracking the user’s location.
*/
- (void)mapViewWillStartLocatingUser:(MGLMapView *)mapView;
/**
- Tells the delegate that the map view has stopped tracking the user's location.
+ Tells the delegate that the map view has stopped tracking the user’s location.
- This method is called when the value of the `showsUserLocation` property changes to `NO`.
+ This method is called when the value of the `showsUserLocation` property
+ changes to `NO`.
- @param mapView The map view that is tracking the user's location.
+ @param mapView The map view that is tracking the user’s location.
*/
- (void)mapViewDidStopLocatingUser:(MGLMapView *)mapView;
/**
Tells the delegate that the location of the user was updated.
- While the `showsUserLocation` property is set to `YES`, this method is called whenever a new location update is received by the map view. This method is also called if the map view's user tracking mode is set to `MGLUserTrackingModeFollowWithHeading` and the heading changes, or if it is set to `MGLUserTrackingModeFollowWithCourse` and the course changes.
+ While the `showsUserLocation` property is set to `YES`, this method is called
+ whenever a new location update is received by the map view. This method is also
+ called if the map view’s user tracking mode is set to
+ `MGLUserTrackingModeFollowWithHeading` and the heading changes, or if it is set
+ to `MGLUserTrackingModeFollowWithCourse` and the course changes.
- This method is not called if the application is currently running in the background. If you want to receive location updates while running in the background, you must use the Core Location framework.
+ This method is not called if the application is currently running in the
+ background. If you want to receive location updates while running in the
+ background, you must use the Core Location framework.
- @param mapView The map view that is tracking the user's location.
- @param userLocation The location object representing the user's latest location. This property may be `nil`.
+ @param mapView The map view that is tracking the user’s location.
+ @param userLocation The location object representing the user’s latest
+ location. This property may be `nil`.
*/
- (void)mapView:(MGLMapView *)mapView didUpdateUserLocation:(nullable MGLUserLocation *)userLocation;
/**
- Tells the delegate that an attempt to locate the user's position failed.
+ Tells the delegate that an attempt to locate the user’s position failed.
- @param mapView The map view that is tracking the user's location.
- @param error An error object containing the reason why location tracking failed.
+ @param mapView The map view that is tracking the user’s location.
+ @param error An error object containing the reason why location tracking
+ failed.
*/
- (void)mapView:(MGLMapView *)mapView didFailToLocateUserWithError:(NSError *)error;
/**
- Tells the delegate that the map view's user tracking mode has changed.
+ Tells the delegate that the map view’s user tracking mode has changed.
- This method is called after the map view asynchronously changes to reflect the new user tracking mode, for example by beginning to zoom or rotate.
+ This method is called after the map view asynchronously changes to reflect the
+ new user tracking mode, for example by beginning to zoom or rotate.
@param mapView The map view that changed its tracking mode.
@param mode The new tracking mode.
@@ -128,37 +178,46 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)mapView:(MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(BOOL)animated;
-#pragma mark Managing the Display of Annotations
+#pragma mark Managing the Appearance of Annotations
/**
- Returns a view object to use for the marker for the specified point annotation object.
+ Returns an annotation image object to mark the given point annotation object on
+ the map.
- @param mapView The map view that requested the annotation view.
- @param annotation The object representing the annotation that is about to be displayed.
- @return The view object to display for the specified annotation or `nil` if you want to display the default marker image.
- */
-- (nullable MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id <MGLAnnotation>)annotation;
-
-/**
- Returns an image object to use for the marker for the specified point annotation object.
+ Implement this method to mark a point annotation with a static image. If you
+ want to mark a particular point annotation with an annotation view instead,
+ omit this method or have it return `nil` for that annotation, then implement
+ `-mapView:viewForAnnotation:`.
+
+ Static annotation images use less memory and draw more quickly than annotation
+ views. On the other hand, annotation views are compatible with UIKit, Core
+ Animation, and other Cocoa Touch frameworks.
@param mapView The map view that requested the annotation image.
- @param annotation The object representing the annotation that is about to be displayed.
- @return The image object to display for the specified annotation or `nil` if you want to display the default marker image.
+ @param annotation The object representing the annotation that is about to be
+ displayed.
+ @return The annotation image object to display for the given annotation or
+ `nil` if you want to display the default marker image or an annotation view.
*/
- (nullable MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation;
/**
- Returns the alpha value to use when rendering a shape annotation. Defaults to `1.0`.
+ Returns the alpha value to use when rendering a shape annotation.
+
+ A value of 0.0 results in a completely transparent shape. A value of 1.0, the
+ default, results in a completely opaque shape.
@param mapView The map view rendering the shape annotation.
@param annotation The annotation being rendered.
- @return An alpha value between `0` and `1.0`.
+ @return An alpha value between 0 and 1.0.
*/
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation;
/**
- Returns the stroke color to use when rendering a shape annotation. Defaults to the map view’s tint color.
+ Returns the color to use when rendering the outline of a shape annotation.
+
+ The default stroke color is the map view’s tint color. If a pattern color is
+ specified, the result is undefined.
@param mapView The map view rendering the shape annotation.
@param annotation The annotation being rendered.
@@ -167,150 +226,265 @@ NS_ASSUME_NONNULL_BEGIN
- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
/**
- Returns the fill color to use when rendering a polygon annotation. Defaults to the map view’s tint color.
+ Returns the color to use when rendering the fill of a polygon annotation.
+
+ The default fill color is the map view’s tint color. If a pattern color is
+ specified, the result is undefined.
@param mapView The map view rendering the polygon annotation.
@param annotation The annotation being rendered.
- @return A color to use for the polygon interior.
+ @return The polygon’s interior fill color.
*/
- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
/**
- Returns the line width to use when rendering a polyline annotation. Defaults to `3.0`.
+ Returns the line width in points to use when rendering the outline of a
+ polyline annotation.
+
+ By default, the polyline is outlined with a line 3.0 points wide.
@param mapView The map view rendering the polygon annotation.
@param annotation The annotation being rendered.
- @return A line width for the polyline.
+ @return A line width for the polyline, measured in points.
*/
- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation;
+#pragma mark Managing Annotation Views
+
/**
- Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble.
+ Returns a view object to mark the given point annotation object on the map.
- If the value returned is `YES`, a standard callout bubble is shown when the user taps a selected annotation. The callout uses the title and subtitle text from the associated annotation object. If there is no title text, though, the annotation will not show a callout. The callout also displays any custom callout views returned by the delegate for the left and right callout accessory views.
+ Implement this method to mark a point annotation with a view object. If you
+ want to mark a particular point annotation with a static image instead, omit
+ this method or have it return `nil` for that annotation, then implement
+ `-mapView:imageForAnnotation:` instead.
- If the value returned is `NO`, the value of the title and subtitle strings are ignored.
+ Annotation views are compatible with UIKit, Core Animation, and other Cocoa
+ Touch frameworks. On the other hand, static annotation images use less memory
+ and draw more quickly than annotation views.
- @param mapView The map view that requested the annotation callout ability.
- @param annotation The object representing the annotation.
- @return A Boolean indicating whether the annotation should show a callout.
+ @param mapView The map view that requested the annotation view.
+ @param annotation The object representing the annotation that is about to be
+ displayed.
+ @return The view object to display for the given annotation or `nil` if you
+ want to display an annotation image instead.
*/
-- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
+- (nullable MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id <MGLAnnotation>)annotation;
/**
- Returns a callout view to display for the specified annotation.
+ Tells the delegate that one or more annotation views have been added and
+ positioned on the map.
- If this method is present in the delegate, it must return a new instance of a view dedicated to display the callout bubble. It will be configured by the map view. If this method is not present, or if it returns `nil`, a standard, two-line, bubble-like callout view is displayed by default.
+ This method is called just after the views are added to the map. You can
+ implement this method to animate the addition of the annotation views.
- @param mapView The map view that requested the callout view.
- @param annotation The object representing the annotation.
- @return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the default callout view.
+ @param mapView The map view to which the annotation views were added.
+ @param annotationViews An array of `MGLAnnotationView` objects representing the
+ views that were added.
*/
-- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;
+- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews;
+
+#pragma mark Selecting Annotations
/**
- Returns the view to display on the left side of the standard callout bubble.
+ Tells the delegate that one of its annotations was selected.
- The default value is treated as if `nil`. The left callout view is typically used to display information about the annotation or to link to custom information provided by your application.
+ You can use this method to track changes in the selection state of annotations.
- If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
+ If the annotation is associated with an annotation view, you can also implement
+ `-mapView:didSelectAnnotationView:`, which is called immediately after this
+ method is called.
- @param mapView The map view presenting the annotation callout.
- @param annotation The object representing the annotation with the callout.
- @return The accessory view to display.
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was selected.
*/
-- (nullable UIView *)mapView:(MGLMapView *)mapView leftCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
/**
- Returns the view to display on the right side of the standard callout bubble.
+ Tells the delegate that one of its annotations was deselected.
- The default value is treated is if `nil`. The right callout view is typically used to link to more detailed information about the annotation. A common view to specify for this property is `UIButton` object whose type is set to `UIButtonTypeDetailDisclosure`.
+ You can use this method to track changes in the selection state of annotations.
- If the view you specify is also a descendant of the `UIControl` class, you can use the map view's delegate to receive notifications when your control is tapped. If it does not descend from `UIControl`, your view is responsible for handling any touch events within its bounds.
+ If the annotation is associated with an annotation view, you can also implement
+ `-mapView:didDeselectAnnotationView:`, which is called immediately after this
+ method is called.
- @param mapView The map view presenting the annotation callout.
- @param annotation The object representing the annotation with the callout.
- @return The accessory view to display.
+ @param mapView The map view containing the annotation.
+ @param annotation The annotation that was deselected.
*/
-- (nullable UIView *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
-
-#pragma mark Managing Annotations
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
/**
- Tells the delegate that the user tapped one of the annotation's accessory buttons.
+ Tells the delegate that one of its annotation views was selected.
- Accessory views contain custom content and are positioned on either side of the annotation title text. If a view you specify is a descendant of the `UIControl` class, the map view calls this method as a convenience whenever the user taps your view. You can use this method to respond to taps and perform any actions associated with that control. For example, if your control displayed additional information about the annotation, you could use this method to present a modal panel with that information.
+ You can use this method to track changes in the selection state of annotation
+ views.
- If your custom accessory views are not descendants of the `UIControl` class, the map view does not call this method.
+ This method is only called for annotation views. To track changes in the
+ selection state of all annotations, including those associated with static
+ annotation images, implement `-mapView:didSelectAnnotation:`, which is called
+ immediately before this method is called.
- @param mapView The map view containing the specified annotation.
- @param annotation The annotation whose button was tapped.
- @param control The control that was tapped.
+ @param mapView The map view containing the annotation.
+ @param annotationView The annotation view that was selected.
*/
-- (void)mapView:(MGLMapView *)mapView annotation:(id <MGLAnnotation>)annotation calloutAccessoryControlTapped:(UIControl *)control;
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotationView:(MGLAnnotationView *)annotationView;
/**
- Tells the delegate that the user tapped on an annotation's callout view.
+ Tells the delegate that one of its annotation views was deselected.
- @param mapView The map view containing the specified annotation.
- @param annotation The annotation whose callout was tapped.
+ You can use this method to track changes in the selection state of annotation
+ views.
+
+ This method is only called for annotation views. To track changes in the
+ selection state of all annotations, including those associated with static
+ annotation images, implement `-mapView:didDeselectAnnotation:`, which is called
+ immediately before this method is called.
+
+ @param mapView The map view containing the annotation.
+ @param annotationView The annotation view that was deselected.
*/
-- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotation>)annotation;
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotationView:(MGLAnnotationView *)annotationView;
-#pragma mark Selecting Annotations
+#pragma mark Managing Callout Views
/**
- Tells the delegate that one of its annotations was selected.
+ Returns a Boolean value indicating whether the annotation is able to display
+ extra information in a callout bubble.
- You can use this method to track changes in the selection state of annotations.
+ This method is called after an annotation is selected, before any callout is
+ displayed for the annotation.
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was selected.
+ If the return value is `YES`, a callout view is shown when the user taps on an
+ annotation, selecting it. The default callout displays the annotation’s title
+ and subtitle. You can add accessory views to either end of the callout by
+ implementing the `-mapView:leftCalloutAccessoryViewForAnnotation:` and
+ `-mapView:rightCalloutAccessoryViewForAnnotation:` methods. You can further
+ customize the callout’s contents by implementing the
+ `-mapView:calloutViewForAnnotation:` method.
+
+ If the return value is `NO`, or if this method is absent from the delegate, or
+ if the annotation lacks a title, the annotation will not show a callout even
+ when selected.
+
+ @param mapView The map view that has selected the annotation.
+ @param annotation The object representing the annotation.
+ @return A Boolean value indicating whether the annotation should show a
+ callout.
*/
-- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation;
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation;
/**
- Tells the delegate that one of its annotations was deselected.
+ Returns a callout view to display for the given annotation.
- You can use this method to track changes in the selection state of annotations.
+ If this method is present in the delegate, it must return a new instance of a
+ view dedicated to display the callout. The returned view will be configured by
+ the map view.
- @param mapView The map view containing the annotation.
- @param annotation The annotation that was deselected.
+ If this method is absent from the delegate, or if it returns `nil`, a standard,
+ two-line, bubble-like callout view is displayed by default.
+
+ @param mapView The map view that requested the callout view.
+ @param annotation The object representing the annotation.
+ @return A view conforming to the `MGLCalloutView` protocol, or `nil` to use the
+ default callout view.
*/
-- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
-
+- (nullable UIView <MGLCalloutView> *)mapView:(MGLMapView *)mapView calloutViewForAnnotation:(id <MGLAnnotation>)annotation;
/**
- Tells the delegate that one of its annotation views was selected.
+ Returns the view to display on the left side of the standard callout bubble.
- You can use this method to track changes in the selection state of annotation views.
+ The left callout view is typically used to convey information about the
+ annotation or to link to custom information provided by your application.
- @param mapView The map view containing the annotation.
- @param annotationView The annotation view that was selected.
+ If the view you specify is a descendant of the `UIControl` class, you can use
+ the map view’s delegate to receive notifications when your control is tapped,
+ by implementing the `-mapView:annotation:calloutAccessoryControlTapped:`
+ method. If the view you specify does not descend from `UIControl`, your view is
+ responsible for handling any touch events within its bounds.
+
+ If this method is absent from the delegate, or if it returns `nil`, the
+ standard callout view has no accessory view on its left side. The return value
+ of this method is ignored if `-mapView:calloutViewForAnnotation:` is present in
+ the delegate.
+
+ To display a view on the callout’s right side, implement the
+ `-mapView:rightCalloutAccessoryViewForAnnotation:` method.
+
+ @param mapView The map view presenting the annotation callout.
+ @param annotation The object representing the annotation with the callout.
+ @return The accessory view to display.
*/
-- (void)mapView:(MGLMapView *)mapView didSelectAnnotationView:(MGLAnnotationView *)annotationView;
+- (nullable UIView *)mapView:(MGLMapView *)mapView leftCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
/**
- Tells the delegate that one of its annotation views was deselected.
+ Returns the view to display on the right side of the standard callout bubble.
- You can use this method to track changes in the selection state of annotation views.
+ The right callout view is typically used to convey information about the
+ annotation or to link to custom information provided by your application.
- @param mapView The map view containing the annotation.
- @param annotationView The annotation view that was deselected.
+ If the view you specify is a descendant of the `UIControl` class, you can use
+ the map view’s delegate to receive notifications when your control is tapped,
+ by implementing the `-mapView:annotation:calloutAccessoryControlTapped:`
+ method. If the view you specify does not descend from `UIControl`, your view is
+ responsible for handling any touch events within its bounds.
+
+ If this method is absent from the delegate, or if it returns `nil`, the
+ standard callout view has no accessory view on its right side. The return value
+ of this method is ignored if `-mapView:calloutViewForAnnotation:` is present in
+ the delegate.
+
+ To display a view on the callout’s left side, implement the
+ `-mapView:leftCalloutAccessoryViewForAnnotation:` method.
+
+ @param mapView The map view presenting the annotation callout.
+ @param annotation The object representing the annotation with the callout.
+ @return The accessory view to display.
*/
-- (void)mapView:(MGLMapView *)mapView didDeselectAnnotationView:(MGLAnnotationView *)annotationView;
+- (nullable UIView *)mapView:(MGLMapView *)mapView rightCalloutAccessoryViewForAnnotation:(id <MGLAnnotation>)annotation;
/**
- Tells the delegate that one if its annotation views was dragged to a new coordinate.
+ Tells the delegate that the user tapped one of the accessory controls in the
+ annotation’s callout view.
+
+ In a standard callout view, accessory views contain custom content and are
+ positioned on either side of the annotation title text. If an accessory view
+ you specify is a descendant of the `UIControl` class, the map view calls this
+ method as a convenience whenever the user taps your view. You can use this
+ method to respond to taps and perform any actions associated with that control.
+ For example, if your control displays additional information about the
+ annotation, you could use this method to present a modal panel with that
+ information.
+
+ If your custom accessory views are not descendants of the `UIControl` class,
+ the map view does not call this method. If the annotation has a custom callout
+ view via the `-mapView:calloutViewForAnnotation:` method, you can specify the
+ custom accessory views using the `MGLCalloutView` protocol’s
+ `leftAccessoryView` and `rightAccessoryView` properties.
- In order to make the new location persistent, you have to update the `coordinate` property of the associated annotation.
+ @param mapView The map view containing the specified annotation.
+ @param annotation The annotation whose accessory view was tapped.
+ @param control The control that was tapped.
+ */
+- (void)mapView:(MGLMapView *)mapView annotation:(id <MGLAnnotation>)annotation calloutAccessoryControlTapped:(UIControl *)control;
+
+/**
+ Tells the delegate that the user tapped on an annotation’s callout view.
+
+ This method is called when the user taps on the body of the callout view, as
+ opposed to the callout’s left or right accessory view. If the annotation has a
+ custom callout view via the `-mapView:calloutViewForAnnotation:` method, this
+ method is only called whenever the callout view calls its delegate’s
+ `-[MGLCalloutViewDelegate calloutViewTapped:]` method.
- @param mapView The map view containing the annotation view.
- @param annotationView The annotation view that was dragged.
- @param coordinate The coordinate that the annotation view was dropped on.
+ If this method is present on the delegate, the standard callout view’s body
+ momentarily highlights when the user taps it, whether or not this method does
+ anything in response to the tap.
+ @param mapView The map view containing the specified annotation.
+ @param annotation The annotation whose callout was tapped.
*/
-- (void)mapView:(MGLMapView *)mapView didDragAnnotationView:(MGLAnnotationView *)annotationView toCoordinate:(CLLocationCoordinate2D)coordinate;
+- (void)mapView:(MGLMapView *)mapView tapOnCalloutForAnnotation:(id <MGLAnnotation>)annotation;
@end
diff --git a/platform/ios/src/MGLMapView_Internal.h b/platform/ios/src/MGLMapView_Internal.h
index 6225e11749..9133aca857 100644
--- a/platform/ios/src/MGLMapView_Internal.h
+++ b/platform/ios/src/MGLMapView_Internal.h
@@ -5,6 +5,9 @@ extern const CGSize MGLAnnotationAccessibilityElementMinimumSize;
@interface MGLMapView (Internal)
+/// Currently shown popover representing the selected annotation.
+@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
+
/** Triggers another render pass even when it is not necessary. */
- (void)setNeedsGLDisplay;
diff --git a/platform/ios/test/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m
index 541c43b5a1..65d7b3d35e 100644
--- a/platform/ios/test/MGLAnnotationViewTests.m
+++ b/platform/ios/test/MGLAnnotationViewTests.m
@@ -3,6 +3,17 @@
static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReuseIdentifer";
+@interface MGLAnnotationView (Test)
+@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic, readwrite) MGLAnnotationViewDragState dragState;
+
+- (void)setDragState:(MGLAnnotationViewDragState)dragState;
+@end
+
+@interface MGLMapView (Test)
+@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
+@end
+
@interface MGLTestAnnotation : NSObject <MGLAnnotation>
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@end
@@ -10,6 +21,19 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
@implementation MGLTestAnnotation
@end
+@interface MGLTestCalloutView: UIView<MGLCalloutView>
+@property (nonatomic) BOOL didCallDismissCalloutAnimated;
+@end
+
+@implementation MGLTestCalloutView
+
+- (void)dismissCalloutAnimated:(BOOL)animated
+{
+ _didCallDismissCalloutAnimated = YES;
+}
+
+@end
+
@interface MGLAnnotationViewTests : XCTestCase <MGLMapViewDelegate>
@property (nonatomic) XCTestExpectation *expectation;
@property (nonatomic) MGLMapView *mapView;
@@ -28,15 +52,21 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
- (void)testAnnotationView
{
_expectation = [self expectationWithDescription:@"annotation property"];
-
+
MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init];
[_mapView addAnnotation:annotation];
-
+
[self waitForExpectationsWithTimeout:1 handler:nil];
-
+
XCTAssert(_mapView.annotations.count == 1, @"number of annotations should be 1");
XCTAssertNotNil(_annotationView.annotation, @"annotation property should not be nil");
-
+ XCTAssertNotNil(_annotationView.mapView, @"mapView property should not be nil");
+
+ MGLTestCalloutView *testCalloutView = [[MGLTestCalloutView alloc] init];
+ _mapView.calloutViewForSelectedAnnotation = testCalloutView;
+ _annotationView.dragState = MGLAnnotationViewDragStateStarting;
+ XCTAssertTrue(testCalloutView.didCallDismissCalloutAnimated, @"callout view was not dismissed");
+
[_mapView removeAnnotation:_annotationView.annotation];
XCTAssert(_mapView.annotations.count == 0, @"number of annotations should be 0");
@@ -54,9 +84,12 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
_annotationView = annotationView;
- [_expectation fulfill];
-
return annotationView;
}
+- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray<MGLAnnotationView *> *)annotationViews
+{
+ [_expectation fulfill];
+}
+
@end
diff --git a/platform/ios/test/MGLNSDataAdditionsTests.m b/platform/ios/test/MGLNSDataAdditionsTests.m
new file mode 100644
index 0000000000..38f19a9703
--- /dev/null
+++ b/platform/ios/test/MGLNSDataAdditionsTests.m
@@ -0,0 +1,52 @@
+#import <XCTest/XCTest.h>
+
+#import "../../darwin/src/NSData+MGLAdditions.h"
+
+@interface MGLNSDataAdditionsTests : XCTestCase
+@end
+
+@implementation MGLNSDataAdditionsTests
+
+- (void)setUp {
+ [super setUp];
+}
+
+- (void)testCompressDecompress
+{
+ NSArray *originalArray = [self mockDataWithCount:180];
+
+ NSData *originalData = [NSJSONSerialization dataWithJSONObject:originalArray options:0 error:nil];
+
+ NSData *compressedData = [originalData mgl_compressedData];
+ NSData *decompressedData = [compressedData mgl_decompressedData];
+
+ NSArray *decompressedArray = [NSJSONSerialization JSONObjectWithData:decompressedData options:0 error:nil];
+
+ XCTAssertTrue([originalArray isEqualToArray:decompressedArray], @"originalArray and decompressedArray should be equal");
+}
+
+- (NSArray *)mockDataWithCount:(NSUInteger)count
+{
+ NSMutableArray *array = [NSMutableArray array];
+
+ for (NSUInteger i=0;i<count;i++)
+ {
+ [array addObject:@{@"lat": @([self safeValueBetween:-90 and:90]),
+ @"lng": @([self safeValueBetween:-180 and:180]),
+ @"timestamp": @((floor([NSDate date].timeIntervalSince1970) * 100) / 100)}];
+ }
+
+ return array;
+}
+
+- (double)safeValueBetween:(double)lowerBound and:(double)upperBound
+{
+ return floor([self randomBetween:lowerBound and:upperBound] * 100 ) / 100;
+}
+
+- (double)randomBetween:(double)lowerBound and:(double)upperBound
+{
+ return lowerBound * drand48() + upperBound * drand48();
+}
+
+@end
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 675cdd885a..c30dbe064b 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -4,6 +4,7 @@
* Right-clicking to open MGLMapView’s context menu no longer prevents the user from subsequently panning the map by clicking and dragging. ([#5593](https://github.com/mapbox/mapbox-gl-native/pull/5593))
* Replaced the wireframe debug mask with an overdraw visualization debug mask to match Mapbox GL JS’s overdraw inspector. ([#5403](https://github.com/mapbox/mapbox-gl-native/pull/5403))
+* Improved the design of the generated API documentation. ([#5306](https://github.com/mapbox/mapbox-gl-native/pull/5306))
## 0.2.0
diff --git a/platform/macos/docs/doc-README.md b/platform/macos/docs/doc-README.md
index e8f7029bd6..4aba26afd9 100644
--- a/platform/macos/docs/doc-README.md
+++ b/platform/macos/docs/doc-README.md
@@ -2,7 +2,7 @@
The Mapbox macOS SDK is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
-<img alt="" src="https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/screenshot.png" width="645">
+<img alt="Mapbox macOS SDK screenshot" src="screenshot.png" width="645">
For setup information, consult the README.md that comes with this documentation. The [Mapbox iOS SDK](https://www.mapbox.com/ios-sdk/)’s [API documentation](https://www.mapbox.com/ios-sdk/api/) and [online examples](https://www.mapbox.com/ios-sdk/examples/) apply to the Mapbox macOS SDK with few differences, mostly around unimplemented features like user location tracking. A [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/macos/CHANGELOG.md) is also available.
diff --git a/platform/macos/scripts/document.sh b/platform/macos/scripts/document.sh
index e62a1e1668..526aecb7d1 100755
--- a/platform/macos/scripts/document.sh
+++ b/platform/macos/scripts/document.sh
@@ -32,6 +32,8 @@ sed -n -e '/^## /{' -e ':a' -e 'n' -e '/^##/q' -e 'p' -e 'ba' -e '}' platform/ma
rm -rf ${OUTPUT}
mkdir -p ${OUTPUT}
+cp platform/macos/screenshot.png "${OUTPUT}"
+
jazzy \
--config platform/macos/jazzy.yml \
--sdk macosx \
@@ -39,6 +41,7 @@ jazzy \
--github-file-prefix https://github.com/mapbox/mapbox-gl-native/tree/${BRANCH} \
--module-version ${SHORT_VERSION} \
--readme ${README} \
+ --theme platform/darwin/docs/theme \
--output ${OUTPUT}
# https://github.com/realm/jazzy/issues/411
find ${OUTPUT} -name *.html -exec \
diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h
index 0b7eec84ac..6ea69f845a 100644
--- a/platform/macos/src/MGLMapViewDelegate.h
+++ b/platform/macos/src/MGLMapViewDelegate.h
@@ -40,10 +40,12 @@ NS_ASSUME_NONNULL_BEGIN
/**
Tells the delegate that the viewpoint depicted by the map view is changing.
- This method is called as the currently displayed map camera changes due to
- animation. During movement, this method may be called many times to report
- updates to the viewpoint. Therefore, your implementation of this method should
- be as lightweight as possible to avoid affecting performance.
+ This method is called as the currently displayed map camera changes as part of
+ an animation, whether due to a user gesture or due to a call to a method such
+ as `-[MGLMapView setCamera:animated:]`. During the animation, this method may
+ be called many times to report updates to the viewpoint. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
@param mapView The map view whose viewpoint is changing.
*/
@@ -54,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
changing.
This method is called whenever the currently displayed map camera has finished
- changing.
+ changing, after any calls to `-mapViewRegionIsChanging:` due to animation.
@param mapView The map view whose viewpoint has changed.
@param animated Whether the change caused an animated effect on the map.
@@ -85,10 +87,34 @@ NS_ASSUME_NONNULL_BEGIN
- (void)mapViewWillStartRenderingMap:(MGLMapView *)mapView;
- (void)mapViewDidFinishRenderingMap:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
+
+/**
+ Tells the delegate that the map view is about to redraw.
+
+ This method is called any time the map view needs to redraw due to a change in
+ the viewpoint or style property transition. This method may be called very
+ frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
+
+ @param mapView The map view that is about to redraw.
+ */
- (void)mapViewWillStartRenderingFrame:(MGLMapView *)mapView;
+
+/**
+ Tells the delegate that the map view has just redrawn.
+
+ This method is called any time the map view needs to redraw due to a change in
+ the viewpoint or style property transition. This method may be called very
+ frequently, even moreso than `-mapViewRegionIsChanging:`. Therefore, your
+ implementation of this method should be as lightweight as possible to avoid
+ affecting performance.
+
+ @param mapView The map view that has just redrawn.
+ */
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
-#pragma mark Managing the Display of Annotations
+#pragma mark Managing the Appearance of Annotations
/**
Returns an annotation image object to mark the given point annotation object on
@@ -129,7 +155,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
Returns the color to use when rendering the fill of a polygon annotation.
- The default fill color is selected menu item color. If a pattern color is
+ The default fill color is the selected menu item color. If a pattern color is
specified, the result is undefined.
@param mapView The map view rendering the polygon annotation.
@@ -172,7 +198,7 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation;
-#pragma mark Displaying Information About Annotations
+#pragma mark Managing Callout Popovers
/**
Returns a Boolean value indicating whether the annotation is able to display
@@ -182,13 +208,13 @@ NS_ASSUME_NONNULL_BEGIN
displayed for the annotation.
If the return value is `YES`, a callout popover is shown when the user clicks
- on a selected annotation. The default callout displays the annotation’s title
- and subtitle. You can customize the popover’s contents by implementing the
- `-mapView:calloutViewControllerForAnnotation:` method.
+ on an annotation, selecting it. The default callout displays the annotation’s
+ title and subtitle. You can customize the popover’s contents by implementing
+ the `-mapView:calloutViewControllerForAnnotation:` method.
- If the return value is `NO`, or if this method is unimplemented, or if the
- annotation lacks a title, the annotation will not show a callout even when
- selected.
+ If the return value is `NO`, or if this method is absent from the delegate, or
+ if the annotation lacks a title, the annotation will not show a callout even
+ when selected.
@param mapView The map view that has selected the annotation.
@param annotation The object representing the annotation.
diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp
index e189991b55..18cb189d02 100644
--- a/src/mbgl/util/compression.cpp
+++ b/src/mbgl/util/compression.cpp
@@ -1,4 +1,4 @@
-#include "compression.hpp"
+#include <mbgl/util/compression.hpp>
#include <zlib.h>
diff --git a/src/mbgl/util/compression.hpp b/src/mbgl/util/compression.hpp
deleted file mode 100644
index 8f89a3ac03..0000000000
--- a/src/mbgl/util/compression.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-#include <string>
-
-namespace mbgl {
-namespace util {
-
-std::string compress(const std::string &raw);
-std::string decompress(const std::string &raw);
-
-} // namespace util
-} // namespace mbgl