├── LICENSE
├── README.md
├── channels
├── cmapi.acknowledgements.js
├── cmapi.acronyms.js
├── cmapi.appendix.a.js
├── cmapi.appendix.b.js
├── cmapi.appendix.c.js
├── cmapi.js
├── cmapi.overview.js
├── cmapi.references.js
├── cmapi.widget-intents.overview.js
├── cmapi.widget-intents.pick.overview.js
├── cmapi.widget-intents.plot.overview.js
├── cmapi.widget-intents.view.overview.js
├── map.drag-drop.examples.js
├── map.drag-drop.js
├── map.error.examples.js
├── map.error.js
├── map.feature.deselected.examples.js
├── map.feature.deselected.js
├── map.feature.hide.examples.js
├── map.feature.hide.js
├── map.feature.plot.examples.js
├── map.feature.plot.js
├── map.feature.plot.url.examples.js
├── map.feature.plot.url.js
├── map.feature.selected.examples.js
├── map.feature.selected.js
├── map.feature.show.examples.js
├── map.feature.show.js
├── map.feature.unplot.examples.js
├── map.feature.unplot.js
├── map.feature.update.examples.js
├── map.feature.update.js
├── map.overlay.cluster.activate.examples.js
├── map.overlay.cluster.activate.js
├── map.overlay.cluster.deactivate.examples.js
├── map.overlay.cluster.deactivate.js
├── map.overlay.cluster.overview.js
├── map.overlay.cluster.references.js
├── map.overlay.cluster.remove.examples.js
├── map.overlay.cluster.remove.js
├── map.overlay.cluster.replaceableParameters.js
├── map.overlay.cluster.set.examples.js
├── map.overlay.cluster.set.js
├── map.overlay.create.examples.js
├── map.overlay.create.js
├── map.overlay.hide.examples.js
├── map.overlay.hide.js
├── map.overlay.remove.examples.js
├── map.overlay.remove.js
├── map.overlay.show.examples.js
├── map.overlay.show.js
├── map.overlay.update.examples.js
├── map.overlay.update.js
├── map.status.about.examples.js
├── map.status.about.js
├── map.status.format.examples.js
├── map.status.format.js
├── map.status.request.examples.js
├── map.status.request.js
├── map.status.selected.examples.js
├── map.status.selected.js
├── map.status.view.examples.js
├── map.status.view.js
├── map.view.center.bounds.examples.js
├── map.view.center.bounds.js
├── map.view.center.feature.examples.js
├── map.view.center.feature.js
├── map.view.center.location.examples.js
├── map.view.center.location.js
├── map.view.center.overlay.examples.js
├── map.view.center.overlay.js
├── map.view.clicked.examples.js
├── map.view.clicked.js
├── map.view.zoom.examples.js
└── map.view.zoom.js
├── cmapi-toc-1.2.0.json
├── css
├── first.css
├── icons.gif
├── layout.css
├── schema.css
├── second.css
└── ui.fancytree.css
├── img
├── cmapi.clustering.figure1.jpg
├── github-ribbon.png
├── kml.diagram.figureA1.jpg
└── loading.gif
├── index.html
├── js
├── libs
│ ├── jquery-1.11.1.min.js
│ ├── jquery-ui.custom.js
│ ├── jquery.fancytree.js
│ └── tv4.js
├── main.js
└── renderer.js
└── test
├── channel-validation-descriptor.html
├── channel-validation.html
├── channel-validation.js
└── rpc_relay.uncompressed.html
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | cmapi-1.2.0
2 | ===========
3 |
4 | Schemas, decriptions, and examples for the CMAPI 1.2.0 specification
5 |
6 |
--------------------------------------------------------------------------------
/channels/cmapi.acknowledgements.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.acknowledgements"] = {
2 | "sections": [{
3 | "title": "CMAPI Editors",
4 | "paragraphs": [
5 | "Chris Bashioum - cbashioum@mitre.org",
6 | "Eric Briscoe - eric.j.briscoe@gmail.com",
7 | "Scott Hammonds - "
8 | ]
9 | },
10 | {
11 | "title": "CMAPI Contributors",
12 | "paragraphs": [
13 | "Collecting list of contributors..."
14 | ]
15 | }]
16 | }
--------------------------------------------------------------------------------
/channels/cmapi.acronyms.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.acronyms"] = {
2 | "sections": [{
3 | "title": "Acronyms",
4 | "paragraphs": [
5 | "API - Application Program Interface",
6 | "GeoJSON - Geospatial JavaScript Object Notation",
7 | "GUI - Graphical User Interface",
8 | "KML - Keyhole Markup Language",
9 | "Lat - Latitude",
10 | "Lon - Longitude",
11 | "OWF - OZONE Widget Framework ",
12 | "URL - Uniform Resource Locator",
13 | "WMS - Web Map Service",
14 | "OGC - Open Geospatial Consortium",
15 | "RFC - Request For Comment"
16 | ]
17 | }]
18 | }
--------------------------------------------------------------------------------
/channels/cmapi.appendix.a.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.appendix.a"] = {
2 | "title" : "Appendix A: KML Support",
3 | "sections": [{
4 | "title": "KML Support",
5 | "paragraphs": [
6 | "Implementers of the Common Map Widget API MUST support at least as much of the official KML spec (http://www.opengeospatial.org/standards/kml ) as is highlighted in Figure A.1. The diagram in Figure A.1 is a subset of the diagram showing all of the various elements of the KML specification at https://developers.google.com/kml/documentation/kmlreference . ",
7 | " Figure A.1 - Minimum support for KML. "
8 | ]
9 | }]
10 | };
--------------------------------------------------------------------------------
/channels/cmapi.appendix.b.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["cmapi.appendix.b"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "id": "http://json-schema.org/geojson/geojson.json#",
5 | "title": "Appendix B - GeoJSON Support",
6 | "description": "The GeoJSON specification can be found at http://geojson.org/geojson-spec.html . The Common Map Widget API specification extends the GeoJSON specification by adding the “style”, “name”, “id”, “description“, and “timePrimitive“ objects to the “Properties” object of the GeoJSON specification. These extended objects ONLY apply to the GeoJSON Feature object.",
7 | "type": "object",
8 | "properties": {
9 | "name": {
10 | "type": "string",
11 | "description": "name of the specific GeoJSON feature. Generally used when the GeoJSON parent object is a featureCollection or feature objects."
12 | },
13 | "id": {
14 | "type": "string",
15 | "description": "a unique identifier for the feature object. Though not required, it is RECOMMENDED. If the id of the GeoJSON Feature.properties.id is omitted, and part of a FeatureCollection, selection may not work for these features as they cannot be uniquely identified. "
16 | },
17 | "description": {
18 | "type": "string",
19 | "description": "user supplied content that appears in a pop-up balloon associated with the feature. Can be plain text, or HTML formatted."
20 | },
21 | "timePrimitive": {
22 | "type": "object",
23 | "description": "",
24 | "properties": {
25 | "timeSpan": {
26 | "type": "object",
27 | "description": "",
28 | "properties": {
29 | "begin": {
30 | "type": "string",
31 | "format": "date-time",
32 | "description": "time stamp identifying the beginning of the time span (see timeStamp definition for format info). "
33 | },
34 | "end": {
35 | "type": "string",
36 | "format": "date-time",
37 | "description": "time stamp identifying the end of the time span (see timeStamp definition for format info)."
38 | }
39 | },
40 | "required": ["begin", "end"]
41 | },
42 | "timeStamp": {
43 | "type": "string",
44 | "format": "date-time",
45 | "description": "time stamp value expressed as yyyy-mm-ddThh:mm:ss.ssszzzzzz, where T is the separator between the date and the time, and the time zone is either Z (for UTC) or zzzzzz, which represents ±hh:mm in relation to UTC. Additionally, the value can be expressed as a date only."
46 | }
47 | }
48 | },
49 | "style": {
50 | "type": "object",
51 | "description": "",
52 | "properties": {
53 | "lineStyle": {
54 | "type": "object",
55 | "description": "",
56 | "properties": {
57 | "color": {
58 | "description": "Object representing CSS3 RGBA. See Note 1 below for more info",
59 | "type": "object",
60 | "default": "No value sent results in default settings on the map.",
61 | "properties": {
62 | "r": {
63 | "description": "Integer value between 0 and 255 for red.",
64 | "type": "integer",
65 | "minimum": 0,
66 | "maximum": 255
67 | },
68 | "g": {
69 | "description": "Integer value between 0 and 255 for green.",
70 | "type": "integer",
71 | "minimum": 0,
72 | "maximum": 255
73 | },
74 | "b": {
75 | "description": "Integer value between 0 and 255 for blue.",
76 | "type": "integer",
77 | "minimum": 0,
78 | "maximum": 255
79 | },
80 | "a": {
81 | "description": "Number value between 0.0 (fully transparent) to 1.0 (fully opaque).",
82 | "type": "number",
83 | "minimum": 0,
84 | "maximum": 1
85 | }
86 | },
87 | "required": [
88 | "r",
89 | "g",
90 | "b",
91 | "a"
92 | ]
93 | }
94 | },
95 | "required": ["color"]
96 | },
97 | "polyStyle": {
98 | "type": "object",
99 | "description": "",
100 | "properties": {
101 | "color": {
102 | "description": "Object representing CSS3 RGBA. See Note 1 below for more info. ",
103 | "type": "object",
104 | "default": "No value sent results in default settings on the map.",
105 | "properties": {
106 | "r": {
107 | "description": "Integer value between 0 and 255 for red.",
108 | "type": "integer",
109 | "minimum": 0,
110 | "maximum": 255
111 | },
112 | "g": {
113 | "description": "Integer value between 0 and 255 for green.",
114 | "type": "integer",
115 | "minimum": 0,
116 | "maximum": 255
117 | },
118 | "b": {
119 | "description": "Integer value between 0 and 255 for blue.",
120 | "type": "integer",
121 | "minimum": 0,
122 | "maximum": 255
123 | },
124 | "a": {
125 | "description": "Number value between 0.0 (fully transparent) to 1.0 (fully opaque).",
126 | "type": "number",
127 | "minimum": 0,
128 | "maximum": 1
129 | }
130 | },
131 | "required": [
132 | "r",
133 | "g",
134 | "b",
135 | "a"
136 | ]
137 | }
138 | },
139 | "required": ["color"]
140 | },
141 | "iconStyle": {
142 | "type": "object",
143 | "description": "",
144 | "properties": {
145 | "url": {
146 | "type": "uri",
147 | "description": "URL to an image file that will be used for the icon for a point.",
148 | "default": "If no URL is provided, result will be map’s default icon."
149 | }
150 | },
151 | "required": ["url"]
152 | }
153 | }
154 | }
155 | }
156 | },
157 | notes: [
158 | 'See http://www.w3.org/wiki/CSS3/Color/RGBA for more info on RGBA.'
159 | ]
160 | };
161 |
--------------------------------------------------------------------------------
/channels/cmapi.appendix.c.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["cmapi.appendix.c"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "id": "http://json-schema.org/geojson/geojson.json#",
5 | "title": "Appendix C - Area of Interest (AOI)",
6 | "description": "The Common Map Widget API supports Areas of Interest (AOIs) by extending the GeoJSON specification by adding the “aoi” object to the “Properties” object of the GeoJSON specification. This extended object ONLY applies to the GeoJSON Feature object. Note that when passing AOIs, the base GeoJSON object MUST be a single feature object, and MUST NOT be a Feature Collection object.",
7 | "type": "object",
8 | "required": ["aoi"],
9 | "properties": {
10 | "aoi": {
11 | "$schema": "http://json-schema.org/draft-04/schema#",
12 | "title": "aoi",
13 | "description": "If included in a GeoJSON feature, signifies the feature is an Area of Interest and is to be treated accordingly. Note that only GeoJSON features can be used to convey Areas of Interest.",
14 | "type": "object",
15 | "properties": {
16 | "buffer": {
17 | "type": "number",
18 | "description": "*CONDITIONAL Distance in meters from the points identified in the “feature” data. MUST be included and greater than 0 if the feature is a point-radius or line, MAY be included for polygon, and SHALL be ignored for bbox. Specific interpretation of buffer based on type is spelled out in definition of “type” below. ",
19 | "minimum": 1
20 | },
21 | "type": {
22 | "enum": ["bbox", "polygon", "line", "point-radius"],
23 | "description": "Defines how to interpret the passed in AOI geometry. Valid values are “bbox”, “polygon”, “line”, and “point-radius”.
If type = “bbox”:AOI is interpreted as a geospatial rectangle. The bbox MUST NOT be allowed to be manipulated into a different geometric shape (e.g. a trapezoid). geoJSON feature geometry type MUST be a polygon. “buffer” SHALL be ignored. If type = “line”:geoJSON feature geometry type MUST be a line. “buffer” MUST be greater than 0. “buffer” is interpreted as distance in meters perpendicular to both sides of the line. Buffer does not extend past the start and end points of the actual line (i.e., no “end-caps” are supported). If type = “point-radius”:geoJSON feature geometry type MUST be a point. “buffer” MUST be greater than 0. “buffer” is interpreted as the radius in meters from the point. If type = “polygon”:geoJSON feature geometry type MUST be a polygon. “buffer” MAY be included. “buffer” is interpreted as the distance in meters outside of the polygon boundaries that the AOI is to extend. "
24 | }
25 |
26 | },
27 | "required": ["type"]
28 | }
29 | }
30 | },
31 | notes: [
32 | "If sending an AOI, the GeoJSON object MUST be a single GeoJSON Feature object with either a Line, Point, or Polygon geometry type. MulitLineStrings, MultiPolygons, and GeometryCollections are NOT supported for AOIs.",
33 | "For example of AOI, see map.feature.plot Example 3."
34 | ]
35 | };
--------------------------------------------------------------------------------
/channels/cmapi.js:
--------------------------------------------------------------------------------
1 | var cmapi = cmapi || {};
2 | cmapi.channel = cmapi.channel || {};
3 | cmapi.overview = cmapi.overview || {};
--------------------------------------------------------------------------------
/channels/cmapi.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.overview"] = {
2 | "title": "CMAPI Overview",
3 | "sections": [{
4 | "title": "Background",
5 | "paragraphs": [
6 | "Many programs and projects create widgets that search for or manipulate data then present the results on a map. The desire is to be able to combine data search/manipulation widgets from any provider with map widgets from other providers. In order to accomplish this, a standard way for the data search/manipulation widgets to be able to communicate with the map widget is necessary. This Application Program Interface (API) is the codification of that standard. "
7 | ]
8 | }, {
9 | "title": "Overview",
10 | "paragraphs": [
11 | "Using this API allows developers to focus on the problem domain rather than implementing a map widget themselves. It also allows the actual map implementation used to be chosen dynamically by the user at runtime rather than being chosen by the developer. Any map implementation that applies this API can be used. Currently, implementations using Google Earth, Google Maps V2, Google Maps V3, and OpenLayers APIs are available, and others can be written as needed.",
12 | "Another benefit of this API is that it allows multiple widgets to collaboratively display data on a single map widget rather than forcing the user to have a separate map for each widget so the user does not have to learn a different map user interface for each widget.",
13 | "The API uses the OZONE Widget Framework (OWF) inter-widget communication mechanism to allow client widgets to interact with the map. Messages are sent to the appropriate channels (defined below), and the map updates its state accordingly. Other widgets interested in knowing the current map state can subscribe to these messages as well.",
14 | "It is worth noting that the map itself may publish data to these channels on occasion. For example, a map.feature.selected message may originate from a widget asking that a particular feature be selected or because a user has selected the feature on the map.",
15 | "While in most instances the map will not echo back another message to confirm that it has performed an operation, the map will send a view status message whenever the map view (zoom/pan) has been changed, either directly by the user or by another widget that sent a view change message. This allows non-map widgets to be aware of the current viewport without having to process all the various messages that can change the non-map widget's state.",
16 | "In addition, it is expected that subsequently opened widgets may wish to query the map for the current state. The API, therefore, supports widgets requesting the current status so they can configure themselves appropriately."
17 | ]
18 | }, {
19 | "title": "How to use the channels and messages",
20 | "paragraphs": [
21 | "There are two ways that the map can be manipulated. The first is by other widgets via the Common Map Widget API. The second is by a user directly manipulating the map via the map Graphical User Interface (GUI) (for example, using a drawing tool on the map).",
22 | "To manipulate the map, widgets send messages in the map channels, and the map widget responds by modifying its current state. The current map state can be modified by a data source widget requesting that a Keyhole Markup Language (KML) or GeoJSON file be loaded or that the view change to some other location. The map and any listening widgets react to these change requests and modify their state accordingly.",
23 | "When the user manipulates the map, the map will post messages to the map channels, and any widget listening to the map channels can respond the changes.",
24 | "In other words, if a feature is selected by a widget request or by a user, a map.feature.selected message is sent out to any widget configured to receive the message. "
25 | ]
26 | }, {
27 | "title": "Structure of this Document",
28 | "paragraphs": [
29 | "This document is organized into the following four major sections: Introduction, Core Specification, User Manipulation Core, and Optional Extensions. The Introduction provides basic background information and an overview of the channels and messages, as well as provides general requirements that apply across the board for Common Map Widget API conformance. The Core Specification is the minimum required channels and messages that a widget MUST implement in order to be considered Common Map Widget API conformant. The User Manipulation Core defines those channels and messages that a widget MAY implement if it is to support an end-user map oriented workflow. However, IF a widget chooses to implement the User Manipulation Core it MUST implement the whole section in order to be Common Map Widget API – User Manipulation Core conformant. The Optional Extensions define those messages and channels that a widget MAY implement if it wishes to support the identified functionality (i.e., widget intents or clustering). Each particular extension stands on its own, so that a widget may choose which particular extensions it wants to implement on an extension by extension basis. "
30 | ]
31 | }, {
32 | "title": "General Requirements",
33 | "paragraphs": [
34 | "The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL in this document, when in all capital letters, are to be interpreted as described in RFC 2119 (http://tools.ietf.org/html/rfc2119)."
35 | ]
36 | }, {
37 | "title": "Overlays",
38 | "paragraphs": [
39 | "By default, all data added by an individual widget is placed into a single overlay unique to that widget, which means one overlay per widget. However, the API supports specifying into which overlay data are inserted, so a widget can insert data in multiple overlays, and multiple widgets can insert data into the same overlay. To prevent unintended merging of data due to multiple widgets unintentionally using the same overlay ID, it is suggested that if a widget needs to use multiple overlays, and no sharing of those overlays with other widgets is intended, developers include the widget ID as part of the overlay ID. If a widget needs to share overlays with other widgets, developers SHOULD follow the guidelines in the OWF documentation regarding preference namespaces for shared overlays to avoid unintended collisions."
40 | ]
41 | }, {
42 | "title": "Features and Feature IDs",
43 | "paragraphs": [
44 | "Features in the context of this document refer to the discrete pieces of data passed to the API. A feature may be a single marker, polygon, or a complex feature (for example, a KML or GeoJSON Document containing many sub-features). The feature ID used by the API (featureId) refers to the feature ID given when plotting the entire feature. Sub-features also have IDs but their IDs are only used in the map.feature.selected message, which contains the ID of the lowest level feature selected (if available). Since feature IDs are required to be unique within an overlay, it is recommended to use an approach similar to the OWF channels: use a hierarchical naming pattern with the levels of the hierarchy separated by a dot (.). To form a unique feature ID, begin with the ID of the widget creating the feature ID. Then, the widget can generate a unique number to complete the feature ID. For example, if generating a feature ID from a widget with the name of “army.bccs.targeter”, the feature ID’s would begin with army.bccs.targeter. "
45 | ]
46 | }, {
47 | "title": "Payloads",
48 | "paragraphs": [
49 | "All Payloads can be either a single object or an array of like objects."
50 | ]
51 | }, {
52 | "title": "Latitude and Longitude",
53 | "paragraphs": [
54 | "All latitudes and longitudes are in decimal degrees. "
55 | ]
56 | }, {
57 | "title": "Errors",
58 | "paragraphs": [
59 | "Any message sent that is missing a required attribute SHOULD result in the map widget publishing an error message on the error channel. An error is also published if the map widget is unable to find an object based on the given identifier. In general, any time the map is unable to complete a requested operation, an error will be published (if possible)."
60 | ]
61 | }, {
62 | "title": "Case Sensitivity",
63 | "paragraphs": [
64 | "All keywords (i.e., those specifically identified in this API) in a message payload are case sensitive unless otherwise noted"
65 | ]
66 | }, {
67 | "title": "Coordinate Reference System",
68 | "paragraphs": [
69 | "All latitudes and longitudes MUST be in the WGS-84 Geocentric coordinate reference system (CRS) as defined in the Open Geospatial Consortium (OGC) KML Specification Section 6.2, Coordinate Reference System and Annex B, KML Coordinate Reference System Definition. The specification can be found at http://www.opengeospatial.org/standards/kml."
70 | ]
71 | }, {
72 | "title": "Core API",
73 | "paragraphs": [
74 | "The channels associated with the Common Map Widget API core specification are grouped according to the following:",
75 | "map.overlay channels – Messages associated with creating and manipulating overlays.",
76 | "map.feature channels – Messages associated with loading feature data onto the map.",
77 | "map.view channels – Messages associated with manipulating the map view.",
78 | "map.status channels – Messages associated with obtaining the current map state.",
79 | "map.error channels – Error messages."
80 | ]
81 |
82 | }]
83 | };
--------------------------------------------------------------------------------
/channels/cmapi.references.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.references"] = {
2 | "sections": [{
3 | "title": "References",
4 | "paragraphs": [
5 | "GeoJSON specification - http://www.geojson.org/geojson-spec.html ",
6 | "Open Geospatial Consortium KML specification - http://www.opengeospatial.org/standards/kml ",
7 | "Google KML resources - https://developers.google.com/kml/documentation/ ",
8 | "Open Geospatial Consortium WMS specification - - http://www.opengeospatial.org/standards/wms "
9 | ]
10 | }]
11 | }
--------------------------------------------------------------------------------
/channels/cmapi.widget-intents.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.widget-intents.overview"] = {
2 | "title" : "Widget Intents",
3 | "sections": [{
4 | "title": "Background",
5 | "paragraphs": [
6 | "The goal of the Common Map Widget API is to enable a uniform interface for map widget implementations to make core map capabilities available to everyone. To that end, the initial specification supports the OWF Eventing and Drag & Drop APIs as two means of communicating geospatial data to a map.",
7 | "With the introduction of the Widget Intents API to the OWF platform, there is a powerful new way to perform inter-widget communication in a manner that gives the user more choice and power. The Widget Intents extension to the Common Map Widget API addresses some fundamental restrictions of the Eventing-based core API. ",
8 | "Also, since widget intents is modeled after Android intents, it was determined by the Common Map Widget API Technical Committee that following a similar pattern of viewing and plotting appropriate mime types would be in order. Although the “picking a common map widget” intent is unique to the Common Map Widget API, the view and plot of KML, KML URLs, GeoJSON, and GeoJSON URLs is more generic in nature and not specific to the Common Map Widget API – and follows the already existing pattern in Android intents. The Common Map Widget API unique addition to that is the optional common_map metadata object."
9 | ]
10 | }, {
11 | "title": "Intents Payloads",
12 | "paragraphs": [
13 | "In keeping with best practices regarding OWF message payloads, Intents payloads should always be an Object, with a property called data that contains the literal data of the type described by the dataType property of the intent. For example:",
14 | "OWF.Intents.startActivity({ action: \"view\", dataType: \"application/vnd.google-earth.kml+xml\" }, { data: <\"?xml version=\"1.0\"?><kml>…<\kml>\" }, function(dest) { … });
",
15 | "This approach also allows for the distribution of always optional metadata. For the Common Map Widget API, the property containing this metadata is common_map . This property is used to group metadata that is specifically related to the Common Map Widget API, reduce possible conflicts, and avoid polluting the top-level payload object (which is not specific to the Common Map Widget API). For example:",
16 | "OWF.Intents.startActivity({ action: 'view', dataType: 'application/vnd.google-earth.kml+xml' }, { data: \"<?xml version=\"1.0 \"?><kml>…</kml>\", common_map: { overlayId: 'abc123', featureId: 'def456', name: 'My KML', zoom: true }, function(dest) { … });
",
17 | "For each intent defined below, the properties identified in the metadata section are intended to go into the data.common_map object. The identified properties are defined in the Common Map Widget API core specification, and a reference to the payload they are defined in is provided in the text. Note that there is a potential inconsistency here between the fact that the metadata object data.common_map is optional, but some of the properties within the data.common_map object are defined as “required” in the Common Map Widget API core spec. In these cases, when the data.common_map object is not sent with the given intent, the features being viewed or plotted may not be accessible to the other Common Map Widget API functions (e.g., via a map.feature.selected event)."
18 | ]
19 | }]
20 | };
--------------------------------------------------------------------------------
/channels/cmapi.widget-intents.pick.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.widget-intents.pick.overview"] = {
2 | "title": "Picking a Map widget",
3 | "sections": [{
4 | "title": "Purpose",
5 | "paragraphs": [
6 | "Pick a widget that supports a specific version of the Common Map Widget API. This intent is useful for callers to obtain the GUID of an API compatible widget, for further Common Map Widget API related interaction via directed messaging (i.e., between two specific widgets vice broadcast to all)."
7 | ]
8 | }, {
9 | "title": "Action",
10 | "paragraphs": [
11 | "pick"
12 | ]
13 | }, {
14 | "title": "DataType",
15 | "paragraphs": [
16 | "application/vnd.owf.common-map-1.0 application/vnd.owf.common-map-1.1 application/vnd.owf.common-map-1.2 ",
17 | "When doing a “pick”, a widget should ask for the lowest possible version of the Common Map Widget API that is needed to accomplish its desired functionality in order to ensure the widest possible selection of widgets to pick from."
18 | ]
19 | }, {
20 | "title": "Metadata",
21 | "paragraphs": [
22 | "none"
23 | ]
24 | }, {
25 | "title": "Example - Receiving Widget",
26 | "paragraphs": [
27 | "OWF.Intents.receive( { action: 'pick', dataType: 'application/vnd.owf.common-map-1.0' }, function(sender, intent, data) { // no-op } );
",
28 | "In the OWF.Intents.recieve, the receiver of the intent SHOULD NOT take any action as a result of the intent. This is to maintain a common user experience when implementing the pick intent"
29 | ]
30 | }, {
31 | "title": "Example - Sending Widget",
32 | "paragraphs": [
33 | "OWF.Intents.startActivity( { action: 'pick', dataType: 'application/vnd.owf.common-map-1.0’ }, { }, function(dest) { … } );
",
34 | "In OWF.Intents.startActivity, the payload SHOULD be empty (i.e., no actual data is conveyed in the map widget pick intent). An example use case that this supports is for an end user to connect a specific data widget with a specific map widget for further directed communications via the Common Map Widget API. "
35 | ]
36 | }]
37 | };
--------------------------------------------------------------------------------
/channels/cmapi.widget-intents.plot.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.widget-intents.plot.overview"] = {
2 | "title": "Plotting Data",
3 | "sections": [{
4 | "title": "Purpose",
5 | "paragraphs": [
6 | "Plot KML or GeoJSON data, meaning visualize specifically on a geospatial display, not just any widget that is can accept KML or GeoJSON."
7 | ]
8 | }, {
9 | "title": "Action",
10 | "paragraphs": [
11 | "plot"
12 | ]
13 | }, {
14 | "title": "DataType",
15 | "paragraphs": [
16 | "application/vnd.google-earth.kml+xml application/geo+json uri-list/vnd.google-earth.kml+xml uri-list/geo+json "
17 | ]
18 | }, {
19 | "title": "Metadata",
20 | "paragraphs": [
21 | "The following properties from the map.feature.plot payload: overlayId featureId name zoom "
22 | ]
23 | }, {
24 | "title": "Notes",
25 | "paragraphs": [
26 | "When the optional metadata is included in this intent, the receiving widget’s behavior SHOULD be the same as if a map.feature.plot message was sent (see Common Map Widget API Version 1.2 core specification for more details on the map.feature.plot message). Although the map.feature.plot states featureId is required, featureId is optional for the plot intents. If the featureId is not provided, the receiving map SHALL plot the feature and use a locally generated id if needed for internal tracking purposes. If the featureId is omitted, the map SHALL NOT be required to support any of the other common map API commands such as map.feature.selected or map.feature.update that require a featureId.",
27 | "When the optional metadata IS NOT included in this intent, the receiving widget’s behavior SHOULD be the same as if a map.feature.plot message was sent with the exception that the feature may not be selectable or otherwise manipulate-able via the Common Map Widget API at a later time due to the fact that there is not a featureId associated with the data being passed in."
28 | ]
29 | }, {
30 | "title": "Example - Receiving Widget",
31 | "paragraphs": [
32 | " { action: 'plot', dataType: 'application/vnd.google-earth.kml+xml' }, function(sender, intent, data) { // check for common_map metadata if(data.common_map) { if(data.common_map.overlayId){…} if(data.common_map.featureId){…} if(data.common_map.name){…} if(data.common_map.zoom){…} } // visualize KML in data.data } );
"
33 | ]
34 | }, {
35 | "title": "Example - Sending Widget",
36 | "paragraphs": [
37 | "OWF.Intents.startActivity( { action: 'plot', dataType: 'application/vnd.google-earth.kml+xml' }, { data: '<?xml version=\"1.0\"?><kml>…</kml>', common_map: { overlayId: 'abc123', featureId: 'def456', name: 'My Geo Data', zoom: true } } function(dest) { … } );
"
38 | ]
39 | }]
40 | };
--------------------------------------------------------------------------------
/channels/cmapi.widget-intents.view.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["cmapi.widget-intents.view.overview"] = {
2 | "title" : "Viewing Data",
3 | "sections": [{
4 | "title": "Purpose",
5 | "paragraphs": [
6 | "Visualize KML or GeoJSON feature data directly, or via URLs."
7 | ]
8 | },{
9 | "title": "Action",
10 | "paragraphs": [
11 | "view"
12 | ]
13 | }, {
14 | "title": "DataType",
15 | "paragraphs": [
16 | "application/vnd.google-earth.kml+xml application/geo+json uri-list/vnd.google-earth.kml+xml uri-list/geo+json "
17 | ]
18 | },{
19 | "title": "Metadata",
20 | "paragraphs": [
21 | "The following properties from the map.feature.plot payload: overlayId featureId name zoom readOnly "
22 | ]
23 | },
24 | {
25 | "title": "Notes",
26 | "paragraphs": [
27 | "When the optional metadata is included in this intent, the receiving widget’s behavior SHOULD be the same as if a map.feature.plot message was sent (see Common Map Widget API Version 1.2 core specification for more details on the map.feature.plot message). Although the map.feature.plot states featureId is required, featureId is optional for the plot intents. If the featureId is not provided, the receiving map SHALL plot the feature and use a locally generated id if needed for internal tracking purposes. If the featureId is omitted, the map SHALL NOT be required to support any of the other common map API commands such as map.feature.selected or map.feature.update that require a featureId.When the optional metadata IS NOT included in this intent, the receiving widget’s behavior SHOULD be the same as if a map.feature.plot message was sent with the exception that the feature may not be selectable or otherwise manipulate-able via the Common Map Widget API at a later time due to the fact that there is not a featureId associated with the data being passed in."
28 | ]
29 | },{
30 | "title": "Example - Receiving Widget",
31 | "paragraphs": [
32 | "
OWF.Intents.receive( { action: 'view', dataType: 'application/vnd.google-earth.kml+xml' }, function(sender, intent, data) { // check for common_map metadata if(data.common_map) { if (data.common_map.overlayId){…} if (data.common_map.featureId){…} if (data.common_map.name){…} if (data.common_map.zoom){…} } // visualize KML in data.data };
"
33 | ]
34 | }, {
35 | "title": "Example - Sending Widget",
36 | "paragraphs": [
37 | "OWF.Intents.startActivity( { action: 'view', dataType: 'application/vnd.google-earth.kml+xml' }, { data: '<?xml version=\"1.0\"?><kml>…</kml>', common_map: { overlayId: 'abc123', featureId: 'def456', name: 'My Geo Data', zoom: true } }, function(dest) { … } );
"
38 | ]
39 | }]
40 | };
--------------------------------------------------------------------------------
/channels/map.drag-drop.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.drag-drop"].examples = [{
2 | "title": "Example Drag Drop Data",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "dragDropData": {
7 | "overlayId": "Spot Reports",
8 | "featureId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
9 | "name": "A Spot Report",
10 | "zoom": true,
11 | "marker": {
12 | "details": "Spot report details here",
13 | "iconUrl": "http://localhost/widgets/mapWidget/images/spotReport.gif"
14 | }
15 | }
16 | }
17 | }];
--------------------------------------------------------------------------------
/channels/map.drag-drop.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.drag-drop"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "OWF Drag and Drop",
5 | "description": "Drop an item on the map. See OWF drag drop API for details.",
6 | "type": "object",
7 | "properties": {
8 | "dragDropData": {
9 | "description": "Container for the drap drop data",
10 | "type": "object",
11 | "required": ["featureId"],
12 | "properties": {
13 | "overlayId": {
14 | "type": "string",
15 | "description": "The ID of the overlay the dropped item should be loaded into. If overlay with this ID already exists, new item is merged into existing overlay; otherwise, new overlay will be created. If no overlayId is included, sending widget’s ID is used."
16 | },
17 | "featureId": {
18 | "type": "string",
19 | "description": "Unique identifier for the dropped feature. Note that feature IDs MUST be unique within a given overlay. Reusing a feature ID will be considered a reload, with the original feature data being removed and replaced by the new feature data."
20 | },
21 | "name": {
22 | "type": "string",
23 | "description": "Name for the given feature data. Note that feature names do not have to be unique and are intended for display purposes only."
24 | },
25 | "zoom": {
26 | "type": "boolean",
27 | "description": "true if map should zoom to newly loaded feature or marker data, false if not. Default is false.",
28 | "default": false
29 | },
30 | "marker": {
31 | "type": "object",
32 | "description": "A JSON object containing information with which to create a new marker on the map.",
33 | "properties": {
34 | "details": {
35 | "type": "string",
36 | "description": "Detail text associated with the item that will appear in an info window."
37 | },
38 | "iconUrl": {
39 | "type": "string",
40 | "description": "URL to an icon that represents this dropped item on the map. If no URL is included, a default icon is used."
41 | }
42 | }
43 | },
44 | "featureUrl": {
45 | "type": "object",
46 | "description": "A JSON object containing feature data to be plotted on the map.",
47 | "properties": {
48 | "format": {
49 | "type": "string",
50 | "default": "kml",
51 | "description": "Data format (http header content type) of the given feature. If no format is specified, the format defaults to “kml.” A list of formats supported by a particular map implementation can be obtained by querying the map using the map.status channel (see map.status). Note that for this version of the Common Map Widget API, all map implementations MUST support KML and GeoJSON."
52 | },
53 | "url": {
54 | "type": "string",
55 | "description": "URL from which to retrieve the feature data to load onto the map."
56 | },
57 | "params": {
58 | "type": "object",
59 | "description": "A JSON object containing a list of parameters to be passed to the server along with the URL when loading WMS data. Params object is ignored unless “format” is set to ”wms.” Note that request, exceptions, SRS/CRS, width, height, and bbox params SHOULD not be passed in as they are determined by the map as needed and will be ignored if passed. Params as passed will be concatenated to the URL and are expected to follow the WMS specification. All parameters passed in MUST NOT be URL encoded (the map widget implementation will URL encode all passed in params)."
60 | }
61 | }
62 | },
63 | "feature": {
64 | "type": "object",
65 | "description": "A JSON object containing feature data to be plotted on the map.",
66 | "properties": {
67 | "format": {
68 | "type": "string",
69 | "default": "kml",
70 | "description": "Data format (http header content type) of the given feature. If no format is specified, the format defaults to “kml.” A list of formats supported by a particular map implementation can be obtained by querying the map using the map.status channel (see map.status). Note that for this version of the Common Map Widget API, all map implementations MUST support KML and GeoJSON."
71 | },
72 | "featureData": {
73 | "type": "object",
74 | "description": "Feature data to be loaded into the map (see Appendices A and B for additional information on KML and GeoJSON data)."
75 | }
76 | },
77 | "required": ["format", "featureData"]
78 | }
79 | }
80 | }
81 | }
82 | },
83 | notes: [
84 | 'Although marker, feature, and featureUrl are optional, one MUST be present. If marker is included, the marker will be placed at the location of the drop. Feature and featureUrl data will be placed at their natural location (equivalent to being loaded using map.feature.plot.* message).'
85 | ]
86 | };
--------------------------------------------------------------------------------
/channels/map.error.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.error"].examples = [{
2 | "title": "Example map error",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "sender": "29ff882d-4ef3-4ea2-852e-de799dd0699d",
7 | "type": "map.feature.hide",
8 | "msg": {
9 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
10 | "featureId": "example.mapWidget.doesntExist"
11 | },
12 | "error": "No feature with id example.mapWidget.doesntExist found to hide."
13 | }
14 | }];
--------------------------------------------------------------------------------
/channels/map.error.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.error"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.error",
5 | "description": "Map Widget reports errors occurred when attempting to process any message.",
6 | "type": "object",
7 | "properties": {
8 | "sender": {
9 | "type": "string",
10 | "default": "N/A",
11 | "description": "Sender ID of message that caused error."
12 | },
13 | "type": {
14 | "type": "string",
15 | "default": "N/A",
16 | "description": "Type of message that caused error."
17 | },
18 | "msg": {
19 | "type": "object",
20 | "default": "N/A",
21 | "description": "The message that caused error."
22 | },
23 | "error": {
24 | "type": "string",
25 | "default": "N/A",
26 | "description": "A description of the error."
27 | }
28 | },
29 | "required": ["sender", "type", "msg", "error"]
30 | },
31 | "notes": []
32 | };
--------------------------------------------------------------------------------
/channels/map.feature.deselected.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.deselected"].examples = [
2 | {
3 | "title": "Feature De-selected Message",
4 | "description" : "De-select a feature",
5 | "valid": true,
6 | "payload": {
7 | "deSelectedId": "example.mapWidget.1.1",
8 | "deSelectedName": "World Trade Center",
9 | "featureId": "example.mapWidget.1",
10 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
11 | }
12 | }
13 | ]
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/channels/map.feature.deselected.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.deselected"] = {
2 | "schema": {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.deselected",
5 | "description": "De-Select, or report that object was de-selected.",
6 | "properties": {
7 | "deSelectedId": {
8 | "description": "The ID of the object to be de-selected (may be a sub-feature contained within the aggregate feature data with the given featureId).",
9 | "type": "string",
10 | "default": "N/A"
11 | },
12 | "deSelectedName": {
13 | "description": "The name of the de-selected object.",
14 | "type": "string",
15 | "default": "N/A"
16 | },
17 | "featureId": {
18 | "description": "The ID of the feature that contains the de-selected object.",
19 | "type": "string",
20 | "default": "N/A"
21 | },
22 | "overlayId": {
23 | "description": "The ID of the overlay which contains the de-selected object. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
24 | "type": "string",
25 | "default": "sending widget's ID"
26 | }
27 | },
28 | "required" : ["featureId"]
29 | },
30 | "notes" : [
31 | "Although both deSelectedId and deSelectedName are optional, one MUST be passed in if a sub-feature is to be identified. Generally, deSelectedId is preferred and deSelectedName is used when no deSelectedId is available.",
32 | "The expected behavior resulting from this message is that this feature will be removed from the list of currently selected features (see map.status.selected below). If the identified feature is not currently selected when the message is received, then the map widget SHOULD ignore this message (i.e., this message is idempotent)."
33 | ]
34 | }
--------------------------------------------------------------------------------
/channels/map.feature.hide.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.hide"].examples = [
2 | {
3 | "title": "Hide a feature",
4 | "description" : "Hide an existing feature on the map",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.2"
9 | }
10 | }
11 | ]
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/channels/map.feature.hide.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.hide"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.hide",
5 | "description": "Hide existing feature on the map.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay where the feature to be hidden is found. If no overlayId is included, default overlay with ID equal to sending widget's ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "The ID of the feature to be hidden.",
15 | "type": "string",
16 | "default": "N/A"
17 | }
18 | },
19 | "required": ["featureId"]
20 | },
21 | notes: []
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/channels/map.feature.plot.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.plot"].examples = [
2 | {
3 | "title": "Plot KML",
4 | "description": "",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.1",
9 | "feature": "World Trade Center -74.01324033737183,40.71149172571141,0 ",
10 | "name": "World Trade Center",
11 | "zoom": true
12 | }
13 | },
14 | {
15 | "title": "Plot KML",
16 | "valid": false,
17 | "payload": {
18 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
19 | "feature": "World Trade Center -74.01324033737183,40.71149172571141,0 ",
20 | "name": "World Trade Center",
21 | "zoom": true
22 | }
23 | },
24 | {
25 | "title": "Plot GeoJSON",
26 | "valid": true,
27 | "payload": {
28 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
29 | "featureId": "example.geojson.1",
30 | "format": "geojson",
31 | "feature": {
32 | "type": "FeatureCollection",
33 | "features": [
34 | {
35 | "type": "Feature",
36 | "geometry": {
37 | "type": "Polygon",
38 | "coordinates": [
39 | [
40 | 100,
41 | 0
42 | ],
43 | [
44 | 101,
45 | 0
46 | ],
47 | [
48 | 101,
49 | 1
50 | ],
51 | [
52 | 100,
53 | 1
54 | ],
55 | [
56 | 100,
57 | 0
58 | ]
59 | ]
60 | },
61 | "properties": {
62 | "style": {
63 | "lineStyle": {
64 | "color": {
65 | "r": 255,
66 | "g": 0,
67 | "b": 255,
68 | "a": 0.5
69 | }
70 | },
71 | "polyStyle": {
72 | "color": {
73 | "r": 0,
74 | "g": 255,
75 | "b": 0,
76 | "a": 0.25
77 | }
78 | },
79 | "name": "test polygon",
80 | "id": "tp13456",
81 | "description": "polygon pop-up text"
82 | }
83 | }
84 | },
85 | {
86 | "type": "Feature",
87 | "geometry": {
88 | "type": "Line",
89 | "coordinates": [
90 | [
91 | 80,
92 | 3
93 | ],
94 | [
95 | 81,
96 | 3
97 | ],
98 | [
99 | 81,
100 | 5
101 | ],
102 | [
103 | 82,
104 | 2
105 | ]
106 | ]
107 | },
108 | "properties": {
109 | "style": {
110 | "lineStyle": {
111 | "color": {
112 | "r": 0,
113 | "g": 255,
114 | "b": 255,
115 | "a": 0.5
116 | }
117 | }
118 | }
119 | },
120 | "name": "crossingLine",
121 | "id": "0x45632",
122 | "description": "this is a line you don’t want to cross"
123 | }
124 | ]
125 | },
126 | "name": "Sample GeoJSON Feature Collection",
127 | "zoom": true,
128 | "readOnly": false
129 | }
130 | },
131 | {
132 | "title": "Plot Area of Interest (AOI)",
133 | "valid": true,
134 | "payload": {
135 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
136 | "featureId": "example.aoi.1",
137 | "format": "geojson",
138 | "feature": {
139 | "type": "Feature",
140 | "geometry": {
141 | "type": "point",
142 | "coordinates": [
143 | 100,
144 | 0
145 | ]
146 | }
147 | },
148 | "properties": {
149 | "name": "SampleAOIBufferedpoint(i.e., Point/Radius)",
150 | "aoi": {
151 | "type": "point-radius",
152 | "buffer": 1000
153 | },
154 | "style": {
155 | "lineStyle": {
156 | "color": {
157 | "r": 255,
158 | "g": 0,
159 | "b": 255,
160 | "a": 1
161 | }
162 | },
163 | "polyStyle": {
164 | "color": {
165 | "r": 0,
166 | "g": 255,
167 | "b": 25,
168 | "a": 0.5
169 | },
170 | "iconStyle": {
171 | "url": "http: //www.coolicons.org/icon"
172 | }
173 | },
174 | "id": "0x75023443",
175 | "description": "ThisisimportanttextfortheAOIpopup"
176 | },
177 | "zoom": true,
178 | "readOnly": "false"
179 | }
180 | }
181 | }
182 | ]
183 |
184 |
185 |
186 |
--------------------------------------------------------------------------------
/channels/map.feature.plot.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.plot"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.plot",
5 | "description": "Plots feature data on the map.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay this feature should be loaded into. If an overlay with this ID already exists, the new feature is merged into existing overlay; otherwise, a new overlay is created. If no overlayId is included, default overlay with ID equal to sending widget’s ID is used. If an overlay exists, it will retain its status (whether visible or hidden). If an overlay is created, it will be made visible.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "Unique identifier for the given feature data. Note that feature IDs MUST be unique within a given overlay. Reusing a feature ID will be considered a reload, with the original feature data being removed and replaced by the new feature data.",
15 | "type": "string",
16 | "default": "N/A"
17 | },
18 | "name": {
19 | "description": "Name for the given feature data. Note that feature names do not have to be unique and are intended for display purposes only.",
20 | "type": "string",
21 | "default": "N/A"
22 | },
23 | "format": {
24 | "description": "Data format of the given feature. All map implementations MUST support “kml” and “geojson”. If no format is specified, the format defaults to “kml.” A list of formats supported by a particular map implementation may be obtained by querying the map using the map.status channel (see map.status).",
25 | "type": "string",
26 | "default": "kml"
27 | },
28 | "feature": {
29 | "description": "Feature data to be loaded into the map. See Appendix A for additional information on required KML support, Appendix B for information on required GeoJSON, and Appendix C for information on Area of Interest (AOI) support.",
30 | "type": [
31 | "object",
32 | "string"
33 | ],
34 | "default": "N/A"
35 | },
36 | "zoom": {
37 | "description": "true if map should zoom to newly loaded feature data, false if not. Default is false.",
38 | "type": "boolean",
39 | "default": false
40 | },
41 | "readOnly": {
42 | "description": "Valid values are “true” or “false”. If “true”, then the end user MUST NOT be able to edit the feature from the map’s user interface, if “false” the end user MAY edit the feature from the map’s user interface. Default value is “true”. If an edit takes place, the map SHOULD dispatch a map.feature.plot with the updated feature to ensure other widgets are aware that a change took place.",
43 | "type": "boolean",
44 | "default": true
45 | }
46 | },
47 | "required": ["featureId", "feature"]
48 | },
49 | notes: [
50 | 'If using the channel shouter to send a feature, embedded quotes in KML MUST be escaped with a backward slash (that is, use \\" instead of ").',
51 | 'If sending GeoJSON follow the guidance in Appendix B for style information.',
52 | 'When plotting an Area of Interest, the format MUST be “geojson” AND the aoi object defined in Appendix B MUST be included as part of the GeoJSON parameters object.'
53 | ]
54 | };
--------------------------------------------------------------------------------
/channels/map.feature.plot.url.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.plot.url"].examples = [
2 | {
3 | "title": "Plot KML From URL",
4 | "description" : "",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.2",
9 | "url": "https://developers.google.com/kml/documentation/KML_Samples.kml",
10 | "name": "Samples",
11 | "zoom": true
12 | }
13 | },
14 | {
15 | "title": "Plot WMS",
16 | "valid": true,
17 | "payload": {
18 | "overlayId":"xyz",
19 | "featureId":"def",
20 | "name": "Bodies of Water",
21 | "format":"wms",
22 | "url": "http://demo.opengeo.org/geoserver/wms",
23 | "params": {
24 | "layers": "topp:tasmania_water_bodies",
25 | "transparent": true,
26 | "format": "image/gif"
27 | }
28 | }
29 | }
30 | ]
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/channels/map.feature.plot.url.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.plot.url"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.plot.url",
5 | "description": "Have the map plot feature data from a Uniform Resource Locator (URL).",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay this feature should be loaded into. If an overlay with this ID already exists, new feature is merged into existing overlay; otherwise, a new overlay will be created. If no overlayId is included, default overlay with ID equal to sending widget's ID is used. If overlay exists, it will retain its status (whether visible or hidden). If overlay is created, it will be made visible.",
10 | "required": false,
11 | "type": "string",
12 | "default": "sending widget's ID"
13 | },
14 | "featureId": {
15 | "description": "Unique identifier for the given feature data. Note that feature ids must be unique within a given overlay. Reusing a feature id will be considered a reload with the original feature data being removed and replaced by the new feature data.",
16 | "required": true,
17 | "type": "string",
18 | "default": "N/A"
19 | },
20 | "name": {
21 | "description": "Name for the given feature data. Note that feature names do not have to be unique and are intended for display purposes only.",
22 | "required": false,
23 | "type": "string",
24 | "default": "N/A"
25 | },
26 | "format": {
27 | "description": "Data format of the given feature. If no format is specified, the format defaults to “kml.” A list of formats supported by a particular map implementation can be obtained by querying the map using the map.status channel (see map.status). Note that for this version of the Common Map Widget API, all map implementations MUST support KML, GeoJSON, and WMS (GetMap only).",
28 | "required": false,
29 | "type": "string",
30 | "default": "kml"
31 | },
32 | "url": {
33 | "description": "URL from which to retrieve the feature data to load onto the map",
34 | "required": true,
35 | "type": "string",
36 | "default": "N/A"
37 | },
38 | "params": {
39 | "description": "A JSON object containing a list of parameters to be passed to the server along with the URL when loading WMS data. Params object is ignored unless “format” is set to “wms”. Note that request, exceptions, SRS/CRS, width, height, and bbox params should not be passed in as they are determined by the map as needed and will be ignored if passed. Params as passed will be concatenated to the URL and are expected to follow the WMS specification. All parameters passed in must not be URL encoded (the map widget implementation will URL encode all passed in params).",
40 | "type": "object",
41 | "required": [],
42 | "default": "N/A"
43 | },
44 | "zoom": {
45 | "description": "true if map should zoom to newly loaded feature data, false if not. Default is false. Ignored when loading WMS data.",
46 | "required": false,
47 | "type": "boolean",
48 | "default": false
49 | }
50 | },
51 | "required": ["featureId", "url"]
52 | },
53 | notes:[
54 | "For version 1.1.0 of the API, “featureName” was changed to “name” for consistency with feature.plot. In order to maintain backwards compatibility with version 1.0.* of the API, it is suggested that developers on the receiving side of these messages should look for the old “featureName” if “name” is not found in the message."
55 | ]
56 | }
--------------------------------------------------------------------------------
/channels/map.feature.selected.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.selected"].examples = [
2 | {
3 | "title": "Feature Selected Message",
4 | "description" : "Select a feature",
5 | "valid": true,
6 | "payload": {
7 | "selectedId": "example.mapWidget.1.1",
8 | "selectedName": "World Trade Center",
9 | "featureId": "example.mapWidget.1",
10 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
11 | }
12 | }
13 | ]
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/channels/map.feature.selected.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.selected"] = {
2 | "schema": {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.selected",
5 | "description": "Select, or report that object was selected.",
6 | "properties": {
7 | "selectedId": {
8 | "description": "The ID of the object to be selected (may be a sub-feature contained within the aggregate feature data with the given featureId).",
9 | "type": "string",
10 | "default": "N/A"
11 | },
12 | "selectedName": {
13 | "description": "The name of the selected object.",
14 | "type": "string",
15 | "default": "N/A"
16 | },
17 | "featureId": {
18 | "description": "The ID of the feature that contains the selected object.",
19 | "type": "string",
20 | "default": "N/A"
21 | },
22 | "overlayId": {
23 | "description": "The ID of the overlay which contains the selected object. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
24 | "type": "string",
25 | "default": "sending widget's ID"
26 | }
27 | },
28 | "required" : ["featureId"]
29 | },
30 | "notes" : [
31 | "Although both selectedId and selectedName are optional, one MUST be passed in if a sub-feature is to be identified. Generally, selectedId is preferred and selectedName is used when no selectedId is available.",
32 | "The expected behavior resulting from this message is that this feature will be added to the list of currently selected features (see map.status.selected below). If the identified feature is currently selected when the message is received, then the map widget SHOULD ignore this message (i.e., this message is idempotent)."
33 | ]
34 | }
--------------------------------------------------------------------------------
/channels/map.feature.show.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.show"].examples = [
2 | {
3 | "title": "Show a feature",
4 | "description" : "Show an existing feature on the map",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.2",
9 | "zoom" : true
10 | }
11 | }
12 | ]
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/channels/map.feature.show.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.show"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.show",
5 | "description": "Have the map show previously hidden feature data.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay where the feature to be shown is found. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "The ID of the feature to be shown.",
15 | "type": "string",
16 | "default": "N/A"
17 | },
18 | "zoom": {
19 | "description": "true if map should zoom to the shown feature, false if not. Default is false.",
20 | "type": "boolean",
21 | "default": false
22 | }
23 | },
24 | "required": ["featureId"]
25 | },
26 | notes: []
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/channels/map.feature.unplot.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.unplot"].examples = [
2 | {
3 | "title": "Unplot a feature",
4 | "description" : "Remove an existing feature from the map",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.2"
9 | }
10 | }
11 | ]
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/channels/map.feature.unplot.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.unplot"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.unplot",
5 | "description": "Removes feature data from the map.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay where the feature to be removed is found. If no overlayId is included, default overlay with an ID equal to sending widget's ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "The ID of the feature to be removed.",
15 | "type": "string",
16 | "default": "N/A"
17 | }
18 | },
19 | "required": ["featureId"]
20 | },
21 | notes: []
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/channels/map.feature.update.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.update"].examples = [
2 | {
3 | "title": "Update a feature",
4 | "description": "",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.2",
9 | "name": "New Name"
10 | }
11 | }
12 | ]
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/channels/map.feature.update.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.feature.update"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.feature.update",
5 | "description": "Update an existing feature on the map.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay where the feature to be updated is currently found. If no overlayId is included, default overlay with ID equal to sending widget's ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "The ID of the feature to be updated",
15 | "type": "string",
16 | "default": "N/A"
17 | },
18 | "name": {
19 | "description": "If the name provided differs from the feature's current name, the display value will be changed to show the new value. If no value is provided, the feature name will remain unchanged.",
20 | "type": "string",
21 | "default": "N/A"
22 | },
23 | "newOverlayId": {
24 | "description": "This represents the ID of an overlay to move the feature to. If this attribute is provided, the feature MUST be removed from its current overlay and added to the overlay with this ID. If an overlay with an ID of newOverlayId does not exist, a new overlay will be created with an ID of newOverlayId, and the feature to be updated will be moved to the overlay identified by newOverlayId.",
25 | "type": "string",
26 | "default": "N/A"
27 | }
28 | },
29 | "required": ["featureId"]
30 | },
31 | notes: [
32 | 'In order to update the feature data itself, use the map.feature.plot or map.feature.plot.url message and use the existing featureId'
33 | ]
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.activate.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.activate"].examples = [{
2 | "title": "Activate overlay cluster rule example",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "overlayId": "BattlePlan2"
7 | }
8 | }];
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.activate.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.activate"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.cluster.activate",
5 | "description": "Activate the clustering rule for the specified overlay.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay where the clustering rule is to be activated. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed. ",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required": []
14 | },
15 | notes: []
16 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.deactivate.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.deactivate"].examples = [{
2 | "title": "Deactivate overlay cluster rule example",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "overlayId": "BattlePlan2"
7 | }
8 | }];
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.deactivate.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.deactivate"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.cluster.deactivate",
5 | "description": "Deactivate the clustering rule for a specified overlay.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay where the clustering rule is to be deactivated. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required": []
14 | },
15 | notes: []
16 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.overview.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["map.overlay.cluster.overview"] = {
2 | "title" : "Feature Clustering",
3 | "sections": [{
4 | "title": "Background",
5 | "paragraphs": [
6 | "The goal of the Common Map Widget API (CMAPI) is to enable a uniform interface for map widget implementations to make core map capabilities available to everyone. To that end, the initial specification supports the OWF Eventing and Drag & Drop APIs as two means of communicating geospatial data to a map.",
7 | "However, some widgets plot thousands of features onto the map, which can degrade the user experience and lead poor map performance. In order to remedy these issues, maps can cluster results into a single feature on the map as shown in Figure 1 below.",
8 | " Figure 1 - A screenshot of a map using clustering. ",
9 | "The CMAPI Technical Committee has decided to support clustering of results onto the map via an optional extension to the core CMAPI specification. This document defines the optional clustering extension to the CMAPI core specification. Currently, this extension supports clustering multiple features into a single point feature styled using CSS color values or an icon URL. Support for additional clustering features will be added in future versions of the extension to CMAPI.",
10 | "Note that clustering rules are applied at the overlay level. If it is desired to separate items into different clustering rules, then the elements should be separated into different overlays and the appropriate clustering rule set for each specific overlay."
11 | ]
12 | }]
13 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.references.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["map.overlay.cluster.references"] = {
2 | "title" : "Clustering: References",
3 | "sections": [{
4 | "title": " ",
5 | "paragraphs": [
6 | "Too Many Markers by Luke Mahe and Chris Broadfoot - https://developers.google.com/maps/articles/toomanymarkers "
7 | ]
8 | }]
9 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.remove.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.remove"].examples = [{
2 | "title": "Remove overlay cluster rule example",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "overlayId": "BattlePlan2"
7 | }
8 | }];
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.remove.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.remove"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.cluster.remove",
5 | "description": "Remove clustering rule from the specified overlay.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay where the clustering rule is to be removed from. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed. ",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required": []
14 | },
15 | notes: []
16 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.replaceableParameters.js:
--------------------------------------------------------------------------------
1 | cmapi.overview["map.overlay.cluster.replaceableParameters"] = {
2 | "title" : "Clustering: Replaceable Parameters",
3 | "sections": [{
4 | "title": " ",
5 | "paragraphs": [
6 | "When clustering multiple features into a single feature, it is important to allow the clustered feature to contain style and information from the features in the cluster. As requirements evolve, additional replaceable values and functionality will be added to this document to cover all supported features.",
7 | "Replacement currently requires the value sent in the map.overlay.cluster.set channel to be expressed as a string so it can be evaluated by the map. These replaceable parameters are represented using the following syntax, ${param} where param represents the name of the replaceable parameter from the following list:",
8 | "ParameterName = count ; Parameter Description = The number of features in the clustered feature."
9 |
10 | ]
11 | }]
12 | };
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.set.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.set"].examples = [{
2 | "title": "Point example",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "threshold": 3,
7 | "distance": 40,
8 | "clusterStyle": {
9 | "label": "Clustered Point 1",
10 | "title": "Clustered Point 1",
11 | "summary": "A sample summary",
12 | "pointStyle": {
13 | "color": {
14 | "r": 0,
15 | "g": 255,
16 | "b": 0,
17 | "a": 0.25
18 | },
19 | "radius": 6
20 | }
21 | },
22 | "overlayId": "BattlePlan2"
23 | }
24 | }, {
25 | "title": "Icon example",
26 | "description": "",
27 | "valid": true,
28 | "payload": {
29 | "threshold": 3,
30 | "distance": 40,
31 | "clusterStyle": {
32 | "label": "Clustered Point 1",
33 | "title": "Clustered Point 1",
34 | "summary": "A sample summary",
35 | "iconStyle": {
36 | "url": "http://www.cmapi.org/test.png"
37 | }
38 | },
39 | "overlayId": "BattlePlan2"
40 | }
41 | }, {
42 | "title": "Replaceable Radius",
43 | "description": "",
44 | "valid": true,
45 | "payload": {
46 | "threshold": 3,
47 | "distance": 40,
48 | "clusterStyle": {
49 | "label": "Clustered Point 1",
50 | "title": "Clustered Point 1",
51 | "summary": "A sample summary",
52 | "pointStyle": {
53 | "color": {
54 | "r": 0,
55 | "g": 255,
56 | "b": 0,
57 | "a": 0.25
58 | },
59 | "radius": "Math.min(${count}, 7) + 3"
60 | }
61 | },
62 | "overlayId": "BattlePlan2"
63 | }
64 | }, {
65 | "title": "Replaceable Radius",
66 | "description": "",
67 | "valid": true,
68 | "payload": {
69 | "threshold": 3,
70 | "distance": 40,
71 | "clusterStyle": {
72 | "label": "Clustered Point 1",
73 | "title": "Clustered Point 1",
74 | "summary": "A sample summary",
75 | "iconStyle": {
76 | "url": "http://www.cmapi.org/${count}.png"
77 | }
78 | },
79 | "overlayId": "BattlePlan2"
80 | }
81 | }];
--------------------------------------------------------------------------------
/channels/map.overlay.cluster.set.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.cluster.set"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.cluster.set",
5 | "description": "Sets the clustering rule for a specified overlay.",
6 | "properties": {
7 | "threshold": {
8 | "description": "The minimum number of features to form a cluster. Default value is 2.",
9 | "type": "integer",
10 | "minimum": 2,
11 | "default": 2
12 | },
13 | "distance": {
14 | "description": "Pixel distance between features that should be considered a single cluster. Default is 50.",
15 | "type": "integer",
16 | "minimum": 1,
17 | "default": 50
18 | },
19 | "clusterStyle": {
20 | "description": "Styling information to be applied to cluster objects. All string based values (i.e., label, title, description, iconStyle.url) MUST support replacement as detailed in Appendix A.",
21 | "type": "object",
22 | "default": "N/A",
23 | "properties": {
24 | "label": {
25 | "description": "The label to be placed on the clustered item. This SHOULD be rendered adjacent to an icon when using iconStyle or inside the point when using pointStyle. If no value is specified, then the map MUST NOT display a label.",
26 | "type": "string"
27 | },
28 | "title": {
29 | "description": "The title used on the info bubble when the clustered item is clicked. If no value is specified, then the info bubble MUST NOT contain a title. If a value is specified, then the title MUST be located at the top of the info bubble.",
30 | "type": "string"
31 | },
32 | "summary": {
33 | "description": "The summary is specific to the cluster as a whole, and is used on the info bubble when the clustered item is clicked. If no value is specified, then the info bubble MUST NOT contain a summary. If a value is specified, the summary MUST be located below the title and above the description in the info bubble.",
34 | "type": "string"
35 | },
36 | "description": {
37 | "description": "The description used on the info bubble when the clustered item is clicked. If no value is specified, then the pop-up bubble SHOULD contain a roll-up description of all clustered items based on default roll-up behavior of the map implementation. Whether specified or not, the description MUST be located below the summary in the info bubble. \n\r Examples of default roll-up behavior: the default roll-up description could be a list of items in the cluster by element name (e.g., element name in KML). When a user clicks the title of one of the items, the info bubble could display additional details of the item. In addition, the bubble could simply contain a table or listing of all items in the cluster.",
38 | "type": "string"
39 | },
40 | "pointStyle": {
41 | "type": "object",
42 | "properties": {
43 | "color": {
44 | "description": "Object representing CSS3 RGBA. No value sent results in default settings on the map. See http://www.w3.org/wiki/CSS3/Color/RGBA for more info on RGBA. ",
45 | "type": "object",
46 | "properties": {
47 | "r": {
48 | "description": "Integer value between 0 and 255 for red.",
49 | "type": "integer",
50 | "minimum": 0,
51 | "maximum": 255
52 | },
53 | "g": {
54 | "description": "Integer value between 0 and 255 for green.",
55 | "type": "integer",
56 | "minimum": 0,
57 | "maximum": 255
58 | },
59 | "b": {
60 | "description": "Integer value between 0 and 255 for blue.",
61 | "type": "integer",
62 | "minimum": 0,
63 | "maximum": 255
64 | },
65 | "a": {
66 | "description": "Numeric value between 0.0 and 1.0 for alpha.",
67 | "type": "number",
68 | "minimum": 0,
69 | "maximum": 1
70 | }
71 | },
72 | "required": ["r", "g", "b", "a"]
73 | },
74 | "radius": {
75 | "description": "Integer value representing the radius of the clustered point in pixels. Default value is 6.",
76 | "type": ["integer","string"],
77 | "default": 6
78 | }
79 |
80 | },
81 | "required": []
82 | },
83 | "iconStyle": {
84 | "decription": "",
85 | "type": "object",
86 | "properties": {
87 | "url": {
88 | "description": "URL to an image file that will be used for the icon for a point. If no URL is provided, result will be map’s default icon.",
89 | "type": "string"
90 | }
91 | },
92 | "required": ["url"]
93 | }
94 | }
95 | },
96 | "overlayId": {
97 | "description": "The ID of the overlay where the clustering rule is to be applied. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
98 | "type": "string",
99 | "default": "sending widget's ID"
100 | }
101 | },
102 | "required": []
103 | },
104 | notes: []
105 | };
--------------------------------------------------------------------------------
/channels/map.overlay.create.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.create"].examples = [
2 | {
3 | "title": "Create Overlay",
4 | "description" : "",
5 | "valid": true,
6 | "payload": {
7 | "name": "Test",
8 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
9 | }
10 | },
11 | ]
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/channels/map.overlay.create.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.create"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.create",
5 | "description": "Create an overlay into which data can be aggregated.",
6 | "properties": {
7 | "name": {
8 | "description": "The name of the overlay. If not included, the ID is used as the name. Note that overlay names do not have to be unique and are intended for display purposes only.",
9 | "type": "string",
10 | "default": "N/A"
11 | },
12 | "overlayId": {
13 | "description": "The unique ID of the new overlay. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed. If an overlay with the given ID already exists, this message will have no effect. Note that overlay IDs must be unique even across multiple parent overlays.",
14 | "type": "string",
15 | "default": "sending widget's ID"
16 | },
17 | "parentId": {
18 | "description": "The ID of the parent overlay in which to create this overlay. If an overlay with an ID of parentId does not exist, a new overlay will be created with an ID of parentId, and the parentage of the overlay identified by overlayId will be set to the newly created parent overlay.",
19 | "type": "string",
20 | "default": "N/A"
21 | }
22 | },
23 | "required" : []
24 | },
25 | notes : []
26 | }
--------------------------------------------------------------------------------
/channels/map.overlay.hide.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.hide"].examples = [
2 | {
3 | "title": "Hide Overlay",
4 | "description" : "Hide existing overlay on the map",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
8 | }
9 | }
10 | ]
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/channels/map.overlay.hide.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.hide"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.hide",
5 | "description": "Hide existing overlay on the map.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay to be hidden. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed. ",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required" : []
14 | },
15 | notes : []
16 | }
--------------------------------------------------------------------------------
/channels/map.overlay.remove.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.remove"].examples = [
2 | {
3 | "title": "Remove Overlay",
4 | "description" : "Remove entire overlay from the map.",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
8 | }
9 | }
10 | ]
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/channels/map.overlay.remove.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.remove"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.remove",
5 | "description": "Remove entire overlay from the map.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay to be removed. If no overlayId is included, default overlay with ID equal to sending widget's ID is assumed. ",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required" : []
14 | },
15 | notes : []
16 | }
--------------------------------------------------------------------------------
/channels/map.overlay.show.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.show"].examples = [
2 | {
3 | "title": "Show Overlay",
4 | "description" : "Show existing overlay on the map.",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
8 | }
9 | }
10 | ]
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/channels/map.overlay.show.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.show"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.show",
5 | "description": "Show existing overlay on the map.",
6 | "properties": {
7 | "overlayId": {
8 | "description": "The ID of the overlay to be shown. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
9 | "type": "string",
10 | "default": "sending widget's ID"
11 | }
12 | },
13 | "required" : []
14 | },
15 | notes : []
16 | }
--------------------------------------------------------------------------------
/channels/map.overlay.update.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.update"].examples = [
2 | {
3 | "title": "Update Overlay",
4 | "description" : "",
5 | "valid": true,
6 | "payload": {
7 | "name": "New Name",
8 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1"
9 | }
10 | }
11 | ]
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/channels/map.overlay.update.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.overlay.update"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.overlay.update",
5 | "description": "Update an existing overlay.",
6 | "properties": {
7 | "name": {
8 | "description": "The new name of the overlay. Note that overlay names do not have to be unique and are intended for display purposes only.",
9 | "type": "string",
10 | "default": "N/A"
11 | },
12 | "overlayId": {
13 | "description": "The unique ID of the overlay being updated. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed. If an overlay with the given ID already exists, this message will update that overlay. If an overlay with the given ID does not exist, an error is generated. Note that overlay IDs MUST be unique even across multiple parent overlays.",
14 | "type": "string",
15 | "default": "sending widget's ID"
16 | },
17 | "parentId": {
18 | "description": "The ID of the parent overlay that is associated with this overlay. If no ID is provided, the overlay will keep its existing parentage. If a parentId is provided, the parentage of the overlay will be changed to the new parentId. If an overlay with an ID of parentId does not exist, a new overlay will be created and the parentage of the overlay identified by overlayId will be changed to the newly created parent overlay.",
19 | "type": "string",
20 | "default": "N/A"
21 | }
22 | },
23 | "required" : []
24 | },
25 | notes : []
26 | }
--------------------------------------------------------------------------------
/channels/map.status.about.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.about"].examples = [{
2 | "title": "Send out static information about this map implementation",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "version": "1.0.0",
7 | "type": "2-D",
8 | "widgetName": "Common Map Widget",
9 | "extensions": ["clustering", "userManipulation"]
10 | }
11 | }]
--------------------------------------------------------------------------------
/channels/map.status.about.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.about"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.status.about",
5 | "description": "Send out static information about the map implementation",
6 | "type": "object",
7 | "properties": {
8 | "version": {
9 | "type": "string",
10 | "default": "N/A",
11 | "description": "The version numbers of the Common Map Widget API that this map widget supports."
12 | },
13 | "type": {
14 | "enum": ["2-D", "3-D", "other"],
15 | "default": "N/A",
16 | "description": "The type of map in the map widget. Allowable values are “2-D,” “3-D,” or “other.”"
17 | },
18 | "widgetName": {
19 | "type": "string",
20 | "default": "N/A",
21 | "description": "The registered name of the map widget which is consistent across all version of OWF and cannot change during a user’s session. "
22 | },
23 | "instanceName": {
24 | "type": "string",
25 | "default": "N/A",
26 | "description": "The name of the widget on the users dashboard. This name can be changed by the user and in code so this name may change during a user’s session. "
27 | },
28 | "universalName": {
29 | "type": "string",
30 | "default": "N/A",
31 | "description": "The universal name of the map widget set in the widget registration. This is not available in all versions of OWF."
32 | },
33 | "extensions": {
34 | "description": "An array of optional extensions that the widget supports. Allowable values are “intents”, “clustering”, and “userManipulation”. If no extensions are supported, then an empty array SHALL be sent.",
35 | "type": ["array", "enum"],
36 | "default": "N/A",
37 | "uniqueItems": true,
38 | "items": {
39 | "anyOf": ["intents", "clustering", "userManipulation", "selected", null]
40 | }
41 | }
42 | },
43 | "required": ["version", "type", "widgetName", "extensions"]
44 | },
45 | "notes": []
46 | };
--------------------------------------------------------------------------------
/channels/map.status.format.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.format"].examples = [{
2 | "title": "Send view status with all properties",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "formats": [
7 | "kml",
8 | "geojson",
9 | "wms"
10 | ]
11 | }
12 | },
13 | {
14 | "title": "Send view status with all properties",
15 | "description": "",
16 | "valid": true,
17 | "payload": {
18 | "formats": [
19 | "kml",
20 | "geojson",
21 | "wms",
22 | "czml"
23 | ]
24 | }
25 | },
26 | {
27 | "title": "Send view status with all properties",
28 | "description": "",
29 | "valid": false,
30 | "payload": {
31 | }
32 | }]
--------------------------------------------------------------------------------
/channels/map.status.format.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.format"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.status.format",
5 | "description": "Send out the list of data formats that the map widget supports; in other words, this map implementation supports the following feature formats.",
6 | "type": "object",
7 | "properties": {
8 | "formats": {
9 | "description": "An array of the formats that this map supports. Note that for this version of the Common Map Widget API, all map implementations MUST support KML, GeoJSON and WMS. Additional map formats MAY be supported.",
10 | "type": "array",
11 | "uniqueItems": true,
12 | "default": ["kml"],
13 | "items": {
14 | "anyOf": ["kml", "geojson", "wms"]
15 | }
16 | }
17 | },
18 | "required": ["formats"]
19 | },
20 | "notes": []
21 | };
--------------------------------------------------------------------------------
/channels/map.status.request.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.request"].examples = [{
2 | "title": "Send a status request",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "types": [
7 | "view",
8 | "about",
9 | "format",
10 | "selected"
11 | ]
12 | }
13 | }, {
14 | "title": "Send a status request with a typo for types",
15 | "description": "",
16 | "valid": false,
17 | "payload": {
18 | "typo": [
19 | "view",
20 | "about",
21 | "format",
22 | "selected"
23 | ]
24 | }
25 | }, {
26 | "title": "Send a status request with a wrong type",
27 | "description": "",
28 | "valid": false,
29 | "payload": {
30 | "types": [
31 | "all",
32 | "about",
33 | "format",
34 | "selected"
35 | ]
36 | }
37 | }];
--------------------------------------------------------------------------------
/channels/map.status.request.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.request"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.status.request",
5 | "description": "Request current status from the map. Map will send out requested map.status messages in response.",
6 | "type": "object",
7 | "properties": {
8 | "types": {
9 | "description": "An array of status types being requested. Currently only status types of “about,” “format,”, “selected”, and “view” are supported (future versions are expected to support a larger family of statuses, perhaps including “overlay,” “feature,” or “selected”). If types attribute is not included, all status types will be generated.",
10 | "type": ["array", "enum"],
11 | "default": "N/A",
12 | "uniqueItems": true,
13 | "items": {
14 | "anyOf": ["view", "about", "format", "selected"]
15 | }
16 | }
17 | },
18 | "required": []
19 | },
20 | "notes": []
21 | };
--------------------------------------------------------------------------------
/channels/map.status.selected.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.selected"].examples = [{
2 | "title": "Send out the list of currently selected features",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
7 | "selectedFeatures": [{
8 | "featureId": "example.mapWidget.1",
9 | "selectedId": "example.mapWidget.1.1"
10 | }, {
11 | "featureId": "example.mapWidget.1",
12 | "selectedId": "example.mapWidget.1.2"
13 | }, {
14 | "featureId": "example.mapWidget.2",
15 | "selectedId": "example.mapWidget.2.1"
16 | }]
17 | }
18 | },
19 | {
20 | "title": "Currently selected features missing featureId",
21 | "description": "",
22 | "valid": false,
23 | "payload": {
24 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
25 | "selectedFeatures": [{
26 | "featureId": "example.mapWidget.1",
27 | "selectedId": "example.mapWidget.1.1"
28 | }, {
29 | "selectedId": "example.mapWidget.1.2"
30 | }, {
31 | "featureId": "example.mapWidget.2",
32 | "selectedId": "example.mapWidget.2.1"
33 | }]
34 | }
35 | }];
--------------------------------------------------------------------------------
/channels/map.status.selected.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.selected"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.status.selected",
5 | "description": "Send out the list of currently selected features.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "type": "string",
10 | "default": "N/A",
11 | "description": "The ID of the overlay which contains the selected objects. If the list of selected objects spans multiple overlays, then the payload will be an array of messages – one message for each overlay that contains selected objects."
12 | },
13 | "selectedFeatures": {
14 | "description": "An array of features from the identified overlay that are currently selected.",
15 | "type": "array",
16 | "default": "N/A",
17 | "items": {
18 | "type": "object",
19 | "properties": {
20 | "featureId": {
21 | "type": "string",
22 | "description": "The ID of the feature that contains the clicked object."
23 | },
24 | "selectedId": {
25 | "type": "string",
26 | "description": "The ID of the actual clicked object (may be a sub-feature contained within the aggregate feature data with the given featureId)."
27 | },
28 | "selectedName": {
29 | "type": "string",
30 | "description": "The name of the selected object."
31 | }
32 | },
33 | "required": ["featureId"]
34 | }
35 | }
36 | },
37 | "required": ["overlayId", "selectedFeatures"]
38 | },
39 | "notes": [
40 | "Within a given selectedFeature, although both selectedId and selectedName are optional, one MUST be passed in if a sub-feature is to be identified. Generally, selectedId is preferred and selectedName is used when no selectedId is available. The implication of this is that if sub-features cannot be identified, they cannot be selected.",
41 | "The payload sent out in map.selected.status is intended to be itself an array."
42 | ]
43 | };
--------------------------------------------------------------------------------
/channels/map.status.view.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.view"].examples = [{
2 | "title": "Send view status with all properties",
3 | "description": "",
4 | "valid": true,
5 | "payload": {
6 | "requester": "217c086f-719f-928f-5e75-972530cf0db6",
7 | "bounds": {
8 | "southWest": {
9 | "lat": 39.46164364205549,
10 | "lon": -75.6134033203125
11 | },
12 | "northEast": {
13 | "lat": 40.97575093157534,
14 | "lon": -73.10302734375
15 | }
16 | },
17 | "center": {
18 | "lat": 40.2205,
19 | "lon": -74.3579
20 | },
21 | "range": 137500
22 | }
23 | },
24 | {
25 | "title": "Send view status with no requestor",
26 | "description": "Valid with no requestor",
27 | "valid": true,
28 | "payload": {
29 | "bounds": {
30 | "southWest": {
31 | "lat": 39.46164364205549,
32 | "lon": -75.6134033203125
33 | },
34 | "northEast": {
35 | "lat": 40.97575093157534,
36 | "lon": -73.10302734375
37 | }
38 | },
39 | "center": {
40 | "lat": 40.2205,
41 | "lon": -74.3579
42 | },
43 | "range": 137500
44 | }
45 | },
46 | {
47 | "title": "Send view status with no bounds",
48 | "description": "",
49 | "valid": false,
50 | "payload": {
51 | "requester": "217c086f-719f-928f-5e75-972530cf0db6",
52 | "center": {
53 | "lat": 40.2205,
54 | "lon": -74.3579
55 | },
56 | "range": 137500
57 | }
58 | },
59 | {
60 | "title": "Send view status with no center",
61 | "description": "invalid with no center",
62 | "valid": false,
63 | "payload": {
64 | "bounds": {
65 | "southWest": {
66 | "lat": 39.46164364205549,
67 | "lon": -75.6134033203125
68 | },
69 | "northEast": {
70 | "lat": 40.97575093157534,
71 | "lon": -73.10302734375
72 | }
73 | },
74 | "range": 137500
75 | }
76 | },
77 | {
78 | "title": "Send view status with no range",
79 | "description": "invalid with no nage",
80 | "valid": false,
81 | "payload": {
82 | "bounds": {
83 | "southWest": {
84 | "lat": 39.46164364205549,
85 | "lon": -75.6134033203125
86 | },
87 | "northEast": {
88 | "lat": 40.97575093157534,
89 | "lon": -73.10302734375
90 | }
91 | },
92 | "center": {
93 | "lat": 40.2205,
94 | "lon": -74.3579
95 | }
96 | }
97 | }]
--------------------------------------------------------------------------------
/channels/map.status.view.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.status.view"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.status.view",
5 | "description": "Send out the current status of the map view",
6 | "type": "object",
7 | "properties": {
8 | "requestor": {
9 | "type": "string",
10 | "default": "N/A",
11 | "description": "Client that requested this status message be sent (if any). If no requester, message is being sent due to a map view change."
12 | },
13 | "bounds": {
14 | "description": "Bounding box of area visible on map.",
15 | "type": "object",
16 | "default": "N/A",
17 | "properties": {
18 | "southWest": {
19 | "description": "Bottom right of the bounds",
20 | "type": "object",
21 | "properties": {
22 | "lat": {
23 | "type": "number",
24 | "description": "The latitude value of the point",
25 | "minimum": "-90",
26 | "maximum": "90"
27 | },
28 | "lon": {
29 | "type": "number",
30 | "description": "The longitude value of the point",
31 | "minimum": "-180",
32 | "maximum": "180"
33 | }
34 | },
35 | "required": ["lat", "lon"]
36 | },
37 | "northEast": {
38 | "description": "Top left of the bounds",
39 | "type": "object",
40 | "properties": {
41 | "lat": {
42 | "type": "number",
43 | "description": "The latitude value of the point",
44 | "minimum": "-90",
45 | "maximum": "90"
46 | },
47 | "lon": {
48 | "type": "number",
49 | "description": "The longitude value of the point",
50 | "minimum": "-180",
51 | "maximum": "180"
52 | }
53 | }
54 | },
55 | "required": ["lat", "lon"]
56 | },
57 | "required": ["southWest", "northEast"]
58 | },
59 | "center": {
60 | "type": "object",
61 | "default": "N/A",
62 | "description": "The current center of the map",
63 | "properties": {
64 | "lat": {
65 | "type": "number",
66 | "description": "The latitude of the location that was clicked",
67 | "minimum": "-90",
68 | "maximum": "90"
69 | },
70 | "lon": {
71 | "type": "number",
72 | "description": "The longitude of the location that was clicked",
73 | "minimum": "-180",
74 | "maximum": "180"
75 | }
76 | },
77 | "required": ["lat", "lon"]
78 | },
79 | "range": {
80 | "description": "The current distance, in meters, map is zoomed out",
81 | "type": ["number"],
82 | "default": "N/A"
83 | }
84 | },
85 | "required": ["bounds", "center", "range"]
86 | },
87 | "notes": []
88 | };
--------------------------------------------------------------------------------
/channels/map.view.center.bounds.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.bounds"].examples = [
2 | {
3 | "title": "Center on bounds",
4 | "description": "",
5 | "valid": true,
6 | "payload": {
7 | "bounds": {
8 | "southWest": {
9 | "lat": 24.5,
10 | "lon": -124
11 | },
12 | "northEast": {
13 | "lat": 50.5,
14 | "lon": -79
15 | }
16 | },
17 | "zoom": 3000000
18 | }
19 | },
20 | {
21 | "title": "Invalid center on bounds",
22 | "description": "",
23 | "valid": false,
24 | "payload": {
25 | "zoom": 3000000
26 | }
27 | },
28 | {
29 | "title": "Invalid center on bounds",
30 | "description": "",
31 | "valid": false,
32 | "payload": {
33 | "bounds": {
34 | "southWest": {
35 | "lat": 24.5,
36 | "lon": -8124
37 | },
38 | "northEast": {
39 | "lat": 50.5,
40 | "lon": -79
41 | }
42 | },
43 | "zoom": 3000000
44 | }
45 | }
46 | ];
--------------------------------------------------------------------------------
/channels/map.view.center.bounds.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.bounds"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.center.bounds",
5 | "description": "Center the map on a particular bounding box. The map may also be zoomed to show the entire bounds (if possible) or to show a given range.",
6 | "type": "object",
7 | "properties": {
8 | "bounds": {
9 | "description": "Bounding box of area to be centered in map.",
10 | "type": "object",
11 | "default": "N/A",
12 | "properties": {
13 | "southWest": {
14 | "description": "Bottom right of the bounds",
15 | "type": "object",
16 | "properties": {
17 | "lat": {
18 | "type": "number",
19 | "description": "The latitude value of the point",
20 | "minimum": "-90",
21 | "maximum": "90"
22 | },
23 | "lon": {
24 | "type": "number",
25 | "description": "The longitude value of the point",
26 | "minimum": "-180",
27 | "maximum": "180"
28 | }
29 | },
30 | "required": ["lat", "lon"]
31 | },
32 | "northEast": {
33 | "description": "Top left of the bounds",
34 | "type": "object",
35 | "properties": {
36 | "lat": {
37 | "type": "number",
38 | "description": "The latitude value of the point",
39 | "minimum": "-90",
40 | "maximum": "90"
41 | },
42 | "lon": {
43 | "type": "number",
44 | "description": "The longitude value of the point",
45 | "minimum": "-180",
46 | "maximum": "180"
47 | }
48 | }
49 | },
50 | "required": ["lat", "lon"]
51 | },
52 | "required": ["southWest", "northEast"]
53 | },
54 | "zoom": {
55 | "description": "If “auto,” map will adjust to zoom as close as possible to the given location in the user's viewable area. If a number, map will zoom to specified range in meters. If no zoom attribute is included, no zoom is performed.",
56 | "type": ["string", "number"],
57 | "default": "N/A"
58 | }
59 | },
60 | "required": ["bounds"]
61 | },
62 | "notes": []
63 | };
--------------------------------------------------------------------------------
/channels/map.view.center.feature.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.feature"].examples = [
2 | {
3 | "title": "Center on a feature",
4 | "description": "Center the map on a particular feature",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2d882141-0d9e-59d4-20bb-58e6d0460699.1",
8 | "featureId": "example.mapWidget.1"
9 | }
10 | }
11 | ]
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/channels/map.view.center.feature.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.feature"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.center.feature",
5 | "description": "Center the map on a particular feature. The map may also be zoomed to show the entire feature (if possible) or to show a given range.",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay overlay where the feature to be centered on is found. If no overlayId is included, default overlay with ID equal to sending widget ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "featureId": {
14 | "description": "The ID of the feature to center on",
15 | "type": "string",
16 | "default": "N/A"
17 | },
18 | "zoom": {
19 | "description": "If auto, map will adjust to best fit the feature in the user's viewable area. If a number, map will zoom to specified range in meters. If no zoom attribute is included, no zoom is performed.",
20 | "type": ["string", "number"],
21 | "default": "N/A"
22 | }
23 | },
24 | "required": ["featureId"]
25 | },
26 | notes: []
27 | };
--------------------------------------------------------------------------------
/channels/map.view.center.location.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.location"].examples = [
2 | {
3 | "title": "Center on a location and zoom",
4 | "description": "Center the map on a particular location and zoom to range of 1000",
5 | "valid": true,
6 | "payload": {
7 | "location": {
8 | "lat": 38.8708,
9 | "lon": -77.0558
10 | },
11 | "zoom": 1000
12 | }
13 | },
14 | {
15 | "title": "Center on a location and do not zoom",
16 | "description": "Center the map on a particular location and do not zoom",
17 | "valid": true,
18 | "payload": {
19 | "location": {
20 | "lat": 38.8708,
21 | "lon": -77.0558
22 | }
23 | }
24 | },
25 | {
26 | "title": "Center on an invalid location",
27 | "description": "Center the map on a particular overlay",
28 | "valid": false,
29 | "payload": {
30 | "location": {
31 | "lat": 38.8708,
32 | "lon": -777.0558
33 | },
34 | "zoom": false
35 | }
36 | }
37 | ]
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/channels/map.view.center.location.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.location"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.center.location",
5 | "description": "Center the map on a particular location. The map may also be zoomed as close as possible to the location or to a given range",
6 | "type": "object",
7 | "properties": {
8 | "location": {
9 | "description": "Location to be centered in map.",
10 | "default": "N/A",
11 | "type": "object",
12 | "properties": {
13 | "lat": {
14 | "type": "number",
15 | "description": "The latitude value of the point",
16 | "minimum": "-90",
17 | "maximum": "90"
18 | },
19 | "lon": {
20 | "type": "number",
21 | "description": "The longitude value of the point",
22 | "minimum": "-180",
23 | "maximum": "180"
24 | }
25 | },
26 | "required": ["lat", "lon"]
27 | },
28 | "zoom": {
29 | "description": "If “auto,” map will adjust to zoom as close as possible to the given location in the user's viewable area. If a number, map will zoom to specified range in meters. If no zoom attribute is included, no zoom is performed.",
30 | "type": ["string", "number"],
31 | "default": "N/A"
32 | }
33 | },
34 | "required": ["location"]
35 | },
36 | "notes": []
37 | };
--------------------------------------------------------------------------------
/channels/map.view.center.overlay.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.overlay"].examples = [
2 | {
3 | "title": "Center on an Overlay",
4 | "description": "Center the map on a particular overlay",
5 | "valid": true,
6 | "payload": {
7 | "overlayId": "2tyjhp-23idk38-rml389k6kd-29-flsow2c"
8 | }
9 | }
10 | ]
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/channels/map.view.center.overlay.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.center.overlay"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.center.overlay",
5 | "description": "Center the map on a particular overlay. The map may also be zoomed to show the entire overlay (if possible) or to show a given range",
6 | "type": "object",
7 | "properties": {
8 | "overlayId": {
9 | "description": "The ID of the overlay to center on. If no overlayId is included, default overlay with ID equal to sending widget’s ID is assumed.",
10 | "type": "string",
11 | "default": "sending widget's ID"
12 | },
13 | "zoom": {
14 | "description": "If “auto,” zoom will adjust to best fit the overlay in the user’s viewable area. If a number, map will zoom to specified range in meters. If no zoom attribute is included, no zoom is performed.",
15 | "type": "string",
16 | "default": "N/A"
17 | }
18 | },
19 | "required": []
20 | },
21 | notes: []
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/channels/map.view.clicked.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.clicked"].examples = [
2 | {
3 | "title": "Map Clicked",
4 | "description": "Report that the map was clicked at a location",
5 | "valid": true,
6 | "payload": {
7 | "lat": 40.195659093364654,
8 | "lon": -74.28955078125,
9 | "button": "right",
10 | "type": "single",
11 | "keys": ["shift", "ctrl"]
12 | }
13 | },
14 | {
15 | "title": "Map Clicked",
16 | "description": "Report that the map was clicked at a location",
17 | "valid": true,
18 | "payload": {
19 | "lat": 40.195659093364654,
20 | "lon": -74.28955078125,
21 | "button": "middle",
22 | "type": "double",
23 | "keys": ["none"]
24 | }
25 | },
26 | {
27 | "title": "Map Clicked",
28 | "description": "Report that the map was clicked at a location",
29 | "valid": false,
30 | "payload": {
31 | "lat": 40.195659093364654,
32 | "lon": -74.28955078125,
33 | "button": "taco",
34 | "type": "single",
35 | "keys": ["shift", "ctrl"]
36 | }
37 | },
38 | {
39 | "title": "Map Clicked",
40 | "description": "Report that the map was clicked at a location",
41 | "valid": false,
42 | "payload": {
43 | "lat": 40.195659093364654,
44 | "lon": -74.28955078125,
45 | "type": "single",
46 | "keys": ["shift", "ctrl"]
47 | }
48 | }
49 | ];
--------------------------------------------------------------------------------
/channels/map.view.clicked.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.clicked"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.clicked",
5 | "description": "'Click', or report that map was clicked",
6 | "type": "object",
7 | "properties": {
8 | "lat": {
9 | "type": "number",
10 | "description": "The latitude of the location that was clicked",
11 | "default": "N/A",
12 | "minimum": "-90",
13 | "maximum": "90"
14 | },
15 | "lon": {
16 | "type": "number",
17 | "description": "The longitude of the location that was clicked",
18 | "default": "N/A",
19 | "minimum": "-180",
20 | "maximum": "180"
21 | },
22 | "button": {
23 | "description": "Which mouse button was clicked. Allowable values are “right,” “left,” and “middle.” For backwards compatibility, if this attribute is not populated it MUST be treated as a left mouse click the same as if it were populated with “left.”",
24 | "type": ["string", "enum"],
25 | "enum": ["left", "middle", "right"],
26 | "default": "left"
27 | },
28 | "type": {
29 | "description": "The type of click event. Allowable values are “single” and “double.” For backwards compatibility, if this attribute is not populated it MUST be assumed to be a single mouse click and treated the same as if it were populated with “single.”",
30 | "type": ["string", "enum"],
31 | "enum": ["single", "double"],
32 | "default": "single"
33 | },
34 | "keys": {
35 | "description": "An array of keys pressed during the click event. Allowable values are “alt,” “ctrl,” “shift,” and “none.” For backwards compatibility, if this attribute is not populated it MUST be assumed that no additional keys were pressed and behave the same way as if it were populated with “none.”",
36 | "type": ["array", "enum"],
37 | "uniqueItems": true,
38 | "default": ["none"],
39 | "items": {
40 | "anyOf": ["shift", "alt", "ctrl", "none"]
41 | }
42 | }
43 | },
44 | "required": ["lat", "lon", "button", "keys", "type"]
45 | },
46 | "notes": []
47 | };
--------------------------------------------------------------------------------
/channels/map.view.zoom.examples.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.zoom"].examples = [
2 | {
3 | "title": "Zoomto a range",
4 | "description": "Zoom the map to a particular range.",
5 | "valid": true,
6 | "payload": {
7 | "range": 100000
8 | }
9 | }
10 | ]
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/channels/map.view.zoom.js:
--------------------------------------------------------------------------------
1 | cmapi.channel["map.view.zoom"] = {
2 | schema: {
3 | "$schema": "http://json-schema.org/draft-04/schema#",
4 | "title": "map.view.zoom",
5 | "description": "Zoom the map to a particular range",
6 | "type": "object",
7 | "properties": {
8 | "range": {
9 | "description": "The distance in meters from the map (note that most 2-D map renderers do not support infinite zoom and the range will be translated into the nearest supported zoom level).",
10 | "type": "number",
11 | "default": "N/A"
12 | }
13 | },
14 | "required": ["range"]
15 | },
16 | notes: []
17 | }
18 |
19 |
20 |
--------------------------------------------------------------------------------
/cmapi-toc-1.2.0.json:
--------------------------------------------------------------------------------
1 | [{
2 | "key": "cmapi.overview",
3 | "type": "overview",
4 | "title": "Introduction"
5 | }, {
6 | "key": "",
7 | "title": "API CORE",
8 | "folder": true,
9 | "expanded": true,
10 | "children": [{
11 | "key": "",
12 | "title": "Overlay Channels",
13 | "children": [{
14 | "key": "map.overlay.create",
15 | "title": "Create Overlay"
16 | }, {
17 | "key": "map.overlay.remove",
18 | "title": "Remove Overlay"
19 | }, {
20 | "key": "map.overlay.hide",
21 | "title": "Hide Overlay"
22 | }, {
23 | "key": "map.overlay.show",
24 | "title": "Show Overlay"
25 | }, {
26 | "key": "map.overlay.update",
27 | "title": "Update Overlay"
28 | }]
29 | }, {
30 | "key": "",
31 | "title": "Feature Channels",
32 | "children": [{
33 | "key": "map.feature.plot",
34 | "title": "Plot Feature"
35 | }, {
36 | "key": "map.feature.plot.url",
37 | "title": "Plot URL"
38 | }, {
39 | "key": "map.feature.unplot",
40 | "title": "Unplot Feature"
41 | }, {
42 | "key": "map.feature.hide",
43 | "title": "Hide Feature"
44 | }, {
45 | "key": "map.feature.show",
46 | "title": "Show Feature"
47 | }, {
48 | "key": "map.feature.selected",
49 | "title": "Feature Selected"
50 | }, {
51 | "key": "map.feature.deselected",
52 | "title": "Feature De-Selected"
53 | }, {
54 | "key": "map.feature.update",
55 | "title": "Update Feature Data"
56 | }]
57 | }, {
58 | "key": "",
59 | "title": "Map View Channels",
60 | "children": [{
61 | "key": "map.view.zoom",
62 | "title": "Zoom"
63 | }, {
64 | "key": "map.view.center.overlay",
65 | "title": "Center on Overlay"
66 | }, {
67 | "key": "map.view.center.feature",
68 | "title": "Center on Feature"
69 | }, {
70 | "key": "map.view.center.location",
71 | "title": "Center on Location"
72 | }, {
73 | "key": "map.view.center.bounds",
74 | "title": "Center on Bounds"
75 | }, {
76 | "key": "map.view.clicked",
77 | "title": "Map View Clicked"
78 | }]
79 | }, {
80 | "key": "",
81 | "title": "Map Status Channels",
82 | "children": [{
83 | "key": "map.status.request",
84 | "title": "Request Map Status"
85 | },
86 |
87 | {
88 | "key": "map.status.view",
89 | "title": "Map View Status"
90 | }, {
91 | "key": "map.status.format",
92 | "title": "Map Format Status"
93 | }, {
94 | "key": "map.status.about",
95 | "title": "Map About Status"
96 | }, {
97 | "key": "map.status.selected",
98 | "title": "Map Selected Status"
99 | }
100 | ]
101 | }, {
102 | "key": "map.error",
103 | "title": "Map Errors"
104 | }, {
105 | "key": "map.drag-drop",
106 | "title": "Drag and Drop"
107 | }]
108 | },
109 |
110 | {
111 | "key": "",
112 | "title": "API Extensions",
113 | "folder": true,
114 | "expanded": true,
115 | "children": [{
116 |
117 | "key": "",
118 | "title": "Widget Intents",
119 | "type" : "overview",
120 | "children": [{
121 | "key": "cmapi.widget-intents.overview",
122 | "type": "overview",
123 | "title": "Background"
124 | }, {
125 | "key": "cmapi.widget-intents.pick.overview",
126 | "title": "Picking a Common Map Widget",
127 | "type": "overview"
128 | }, {
129 | "key": "cmapi.widget-intents.view.overview",
130 | "title": "Viewing Data ",
131 | "type": "overview"
132 | }, {
133 | "key": "cmapi.widget-intents.plot.overview",
134 | "title": "Plotting Data ",
135 | "type": "overview"
136 | }]
137 | }, {
138 | "key": "",
139 | "type": "overview",
140 | "title": "Clustering",
141 | "children": [{
142 | "key": "map.overlay.cluster.overview",
143 | "type": "overview",
144 | "title": "Background"
145 | }, {
146 | "key": "map.overlay.cluster.set",
147 | "title": "Set Overlay Level Clustering Rule"
148 | }, {
149 | "key": "map.overlay.cluster.remove",
150 | "title": "Remove Overlay Level Clustering Rule"
151 | }, {
152 | "key": "map.overlay.cluster.activate",
153 | "title": "Activate Overlay Level Clustering Rule"
154 | }, {
155 | "key": "map.overlay.cluster.deactivate",
156 | "title": "De-Activate Overlay Level Clustering Rule"
157 | }, {
158 | "key": "map.overlay.cluster.references",
159 | "type": "overview",
160 | "title": "Clustering: References"
161 | }, {
162 | "key": "map.overlay.cluster.replaceableParameters",
163 | "type": "overview",
164 | "title": "Clustering: Replaceable Parameters"
165 | }]
166 | }]
167 | }, {
168 | "key": "",
169 | "title": "Appendices",
170 | "expanded": true,
171 | "folder": true,
172 | "children": [{
173 | "key": "cmapi.appendix.a",
174 | "type": "overview",
175 | "title": "A. KML Support"
176 | }, {
177 | "key": "cmapi.appendix.b",
178 | "title": "B. GeoJSON Support"
179 | }, {
180 | "key": "cmapi.appendix.c",
181 | "title": "C. Area of Interest Support"
182 | }]
183 | }, {
184 | "key": "cmapi.acronyms",
185 | "type": "overview",
186 | "title": "Acronyms"
187 | }, {
188 | "key": "cmapi.references",
189 | "type": "overview",
190 | "title": "References"
191 | }, {
192 | "key": "cmapi.acknowledgements",
193 | "type": "overview",
194 | "title": "Acknowledgements"
195 | }
196 |
197 | ]
--------------------------------------------------------------------------------
/css/first.css:
--------------------------------------------------------------------------------
1 | html,body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | body {
7 | padding: 20px;
8 | }
9 |
10 | h1,h2,h3,h3,h3,h4,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td {
11 | margin: 0;
12 | padding: 0;
13 | border: 0;
14 | font-weight: normal;
15 | font-style: normal;
16 | font-size: 100%;
17 | line-height: 1;
18 | font-family: inherit;
19 | }
20 |
21 | table {
22 | border-collapse: collapse;
23 | border-spacing: 0;
24 | }
25 |
26 | ol,ul {
27 | list-style: none;
28 | }
29 |
30 | q:before,q:after,blockquote:before,blockquote:after {
31 | content: "";
32 | }
33 |
34 | html {
35 | overflow-y: scroll;
36 | font-size: 100%;
37 | -webkit-text-size-adjust: 100%;
38 | -ms-text-size-adjust: 100%;
39 | }
40 |
41 | a:focus {
42 | outline: thin dotted;
43 | }
44 |
45 | a:hover,a:active {
46 | outline: 0;
47 | }
48 |
49 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section {
50 | display: block;
51 | }
52 |
53 | audio,canvas,video {
54 | display: inline-block;
55 | *display: inline;
56 | *zoom: 1;
57 | }
58 |
59 | audio:not([controls]) {
60 | display: none;
61 | }
62 |
63 | sub,sup {
64 | font-size: 75%;
65 | line-height: 0;
66 | position: relative;
67 | vertical-align: baseline;
68 | }
69 |
70 | sup {
71 | top: -0.5em;
72 | }
73 |
74 | sub {
75 | bottom: -0.25em;
76 | }
77 |
78 | img {
79 | border: 0;
80 | -ms-interpolation-mode: bicubic;
81 | }
82 |
83 | button,input,select,textarea {
84 | font-size: 100%;
85 | margin: 0;
86 | vertical-align: baseline;
87 | *vertical-align: middle;
88 | }
89 |
90 | button,input {
91 | line-height: normal;
92 | *overflow: visible;
93 | }
94 |
95 | button::-moz-focus-inner,input::-moz-focus-inner {
96 | border: 0;
97 | padding: 0;
98 | }
99 |
100 | button,input[type="button"],input[type="reset"],input[type="submit"] {
101 | cursor: pointer;
102 | -webkit-appearance: button;
103 | }
104 |
105 | input[type="search"] {
106 | -webkit-appearance: textfield;
107 | -webkit-box-sizing: content-box;
108 | -moz-box-sizing: content-box;
109 | box-sizing: content-box;
110 | }
111 |
112 | input[type="search"]::-webkit-search-decoration {
113 | -webkit-appearance: none;
114 | }
115 |
116 | textarea {
117 | overflow: auto;
118 | vertical-align: top;
119 | }
120 |
121 | html,body {
122 | background-color: #ffffff;
123 | }
124 |
125 | body {
126 | margin: 0;
127 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
128 | font-size: 13px;
129 | font-weight: normal;
130 | line-height: 18px;
131 | color: #404040;
132 | }
133 |
134 | .container {
135 | width: 940px;
136 | margin-left: auto;
137 | margin-right: auto;
138 | zoom: 1;
139 | }
140 |
141 | .container:before,.container:after {
142 | display: table;
143 | content: "";
144 | zoom: 1;
145 | *display: inline;
146 | }
147 |
148 | .container:after {
149 | clear: both;
150 | }
151 |
152 | .container-fluid {
153 | position: relative;
154 | min-width: 940px;
155 | padding-left: 20px;
156 | padding-right: 20px;
157 | zoom: 1;
158 | }
159 |
160 | .container-fluid:before,.container-fluid:after {
161 | display: table;
162 | content: "";
163 | zoom: 1;
164 | *display: inline;
165 | }
166 |
167 | .container-fluid:after {
168 | clear: both;
169 | }
170 |
171 | .container-fluid>.sidebar {
172 | float: left;
173 | width: 220px;
174 | }
175 |
176 | .container-fluid>.content {
177 | margin-left: 240px;
178 | }
179 |
180 | a {
181 | color: #0069d6;
182 | text-decoration: none;
183 | line-height: inherit;
184 | font-weight: inherit;
185 | }
186 |
187 | a:hover {
188 | color: #00438a;
189 | text-decoration: underline;
190 | }
191 |
192 | .pull-right {
193 | float: right;
194 | }
195 |
196 | .pull-left {
197 | float: left;
198 | }
199 |
200 | .hide {
201 | display: none;
202 | }
203 |
204 | .show {
205 | display: block;
206 | }
207 |
208 | .row {
209 | zoom: 1;
210 | margin-left: -20px;
211 | }
212 |
213 | .row:before,.row:after {
214 | display: table;
215 | content: "";
216 | zoom: 1;
217 | *display: inline;
218 | }
219 |
220 | .row:after {
221 | clear: both;
222 | }
223 |
224 | p {
225 | font-size: 13px;
226 | font-weight: normal;
227 | line-height: 18px;
228 | margin-bottom: 9px;
229 | }
230 |
231 | p small {
232 | font-size: 11px;
233 | color: #bfbfbf;
234 | }
235 |
236 | h1,h2,h3,h3,h3,h4 {
237 | font-weight: bold;
238 | color: #404040;
239 | }
240 |
241 | h1 small,h2 small,h3 small,h3 small,h3 small,h4 small {
242 | color: #bfbfbf;
243 | }
244 |
245 | h1 {
246 | margin-bottom: 18px;
247 | font-size: 30px;
248 | line-height: 36px;
249 | }
250 |
251 | h1 small {
252 | font-size: 18px;
253 | }
254 |
255 | h2 {
256 | font-size: 24px;
257 | line-height: 36px;
258 | }
259 |
260 | h2 small {
261 | font-size: 14px;
262 | }
263 |
264 | h3,h3,h3,h4 {
265 | line-height: 36px;
266 | }
267 |
268 | h3 {
269 | font-size: 18px;
270 | }
271 |
272 | h3 small {
273 | font-size: 14px;
274 | }
275 |
276 | h3 {
277 | font-size: 16px;
278 | }
279 |
280 | h3 small {
281 | font-size: 12px;
282 | }
283 |
284 | h3 {
285 | font-size: 14px;
286 | }
287 |
288 | h4 {
289 | font-size: 13px;
290 | color: #bfbfbf;
291 | text-transform: uppercase;
292 | }
293 |
294 | ul,ol {
295 | margin: 0 0 18px 25px;
296 | }
297 |
298 | ul ul,ul ol,ol ol,ol ul {
299 | margin-bottom: 0;
300 | }
301 |
302 | ul {
303 | list-style: disc;
304 | }
305 |
306 | ol {
307 | list-style: decimal;
308 | }
309 |
310 | li {
311 | line-height: 18px;
312 | color: #808080;
313 | }
314 |
315 | ul.unstyled {
316 | list-style: none;
317 | margin-left: 0;
318 | }
319 |
320 | dl {
321 | margin-bottom: 18px;
322 | }
323 |
324 | dl dt,dl dd {
325 | line-height: 18px;
326 | }
327 |
328 | dl dt {
329 | font-weight: bold;
330 | }
331 |
332 | dl dd {
333 | margin-left: 9px;
334 | }
335 |
336 | hr {
337 | margin: 20px 0 19px;
338 | border: 0;
339 | border-bottom: 1px solid #eee;
340 | }
341 |
342 | strong {
343 | font-style: inherit;
344 | font-weight: bold;
345 | }
346 |
347 | em {
348 | font-style: italic;
349 | font-weight: inherit;
350 | line-height: inherit;
351 | }
352 |
353 | .muted {
354 | color: #bfbfbf;
355 | }
356 |
357 | blockquote {
358 | margin-bottom: 18px;
359 | border-left: 5px solid #eee;
360 | padding-left: 15px;
361 | }
362 |
363 | blockquote p {
364 | font-size: 14px;
365 | font-weight: 300;
366 | line-height: 18px;
367 | margin-bottom: 0;
368 | }
369 |
370 | blockquote small {
371 | display: block;
372 | font-size: 12px;
373 | font-weight: 300;
374 | line-height: 18px;
375 | color: #bfbfbf;
376 | }
377 |
378 | blockquote small:before {
379 | content: '\2014 \00A0';
380 | }
381 |
382 | address {
383 | display: block;
384 | line-height: 18px;
385 | margin-bottom: 18px;
386 | }
387 |
388 | code,pre {
389 | padding: 0 3px 2px;
390 | font-family: Monaco, Andale Mono, Courier New, monospace;
391 | font-size: 12px;
392 | -webkit-border-radius: 3px;
393 | -moz-border-radius: 3px;
394 | border-radius: 3px;
395 | }
396 |
397 | code {
398 | padding: 1px 3px;
399 | }
400 |
401 | pre {
402 | background-color: #f5f5f5;
403 | display: block;
404 | padding: 8.5px;
405 | margin: 0 0 18px;
406 | line-height: 18px;
407 | font-size: 12px;
408 | border: 1px solid #ccc;
409 | border: 1px solid rgba(0, 0, 0, 0.15);
410 | -webkit-border-radius: 3px;
411 | -moz-border-radius: 3px;
412 | border-radius: 3px;
413 | white-space: pre;
414 | white-space: pre-wrap;
415 | word-wrap: break-word;
416 | }
417 |
418 | form {
419 | margin-bottom: 18px;
420 | }
421 |
422 | fieldset {
423 | margin-bottom: 18px;
424 | padding-top: 18px;
425 | }
426 |
427 | fieldset legend {
428 | display: block;
429 | padding-left: 150px;
430 | font-size: 19.5px;
431 | line-height: 1;
432 | color: #404040;
433 | *padding: 0 0 5px 145px;
434 | *line-height: 1.5;
435 | }
436 |
437 | form .clearfix {
438 | margin-bottom: 18px;
439 | zoom: 1;
440 | }
441 |
442 | form .clearfix:before,form .clearfix:after {
443 | display: table;
444 | content: "";
445 | zoom: 1;
446 | *display: inline;
447 | }
448 |
449 | form .clearfix:after {
450 | clear: both;
451 | }
452 |
453 | label,input,select,textarea {
454 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
455 | font-size: 13px;
456 | font-weight: normal;
457 | line-height: normal;
458 | }
459 |
460 | label {
461 | padding-top: 6px;
462 | font-size: 13px;
463 | line-height: 18px;
464 | float: left;
465 | width: 130px;
466 | text-align: right;
467 | color: #404040;
468 | }
469 |
470 | form .input {
471 | margin-left: 150px;
472 | }
473 |
474 | input[type=checkbox],input[type=radio] {
475 | cursor: pointer;
476 | }
477 |
478 | input,textarea,select,.uneditable-input {
479 | display: inline-block;
480 | width: 210px;
481 | height: 18px;
482 | padding: 4px;
483 | font-size: 13px;
484 | line-height: 18px;
485 | color: #808080;
486 | border: 1px solid #ccc;
487 | -webkit-border-radius: 3px;
488 | -moz-border-radius: 3px;
489 | border-radius: 3px;
490 | }
491 |
492 | input[type=checkbox],input[type=radio] {
493 | width: auto;
494 | height: auto;
495 | padding: 0;
496 | margin: 3px 0;
497 | *margin-top: 0;
498 | line-height: normal;
499 | border: none;
500 | }
501 |
502 | input[type=file] {
503 | background-color: #ffffff;
504 | padding: initial;
505 | border: initial;
506 | line-height: initial;
507 | -webkit-box-shadow: none;
508 | -moz-box-shadow: none;
509 | box-shadow: none;
510 | }
511 |
512 | input[type=button],input[type=reset],input[type=submit] {
513 | width: auto;
514 | height: auto;
515 | }
516 |
517 | select,input[type=file] {
518 | height: 27px;
519 | line-height: 27px;
520 | *margin-top: 4px;
521 | }
522 |
523 | select[multiple] {
524 | height: inherit;
525 | }
526 |
527 | textarea {
528 | height: auto;
529 | }
530 |
531 | .uneditable-input {
532 | background-color: #ffffff;
533 | display: block;
534 | border-color: #eee;
535 | -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
536 | -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
537 | box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
538 | cursor: not-allowed;
539 | }
540 |
541 | :-moz-placeholder {
542 | color: #bfbfbf;
543 | }
544 |
545 | ::-webkit-input-placeholder {
546 | color: #bfbfbf;
547 | }
548 |
549 | input,textarea {
550 | -webkit-transition: border linear 0.2s,box-shadow linear 0.2s;
551 | -moz-transition: border linear 0.2s,box-shadow linear 0.2s;
552 | -ms-transition: border linear 0.2s,box-shadow linear 0.2s;
553 | -o-transition: border linear 0.2s,box-shadow linear 0.2s;
554 | transition: border linear 0.2s,box-shadow linear 0.2s;
555 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
556 | -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
557 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
558 | }
559 |
560 | input:focus,textarea:focus {
561 | outline: 0;
562 | border-color: rgba(82, 168, 236, 0.8);
563 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);
564 | -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);
565 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);
566 | }
567 |
568 | input[type=file]:focus,input[type=checkbox]:focus,select:focus {
569 | -webkit-box-shadow: none;
570 | -moz-box-shadow: none;
571 | box-shadow: none;
572 | outline: 1px dotted #666;
573 | }
574 |
575 | form div.clearfix.error {
576 | background: #fae5e3;
577 | padding: 10px 0;
578 | margin: -10px 0 10px;
579 | -webkit-border-radius: 4px;
580 | -moz-border-radius: 4px;
581 | border-radius: 4px;
582 | }
583 |
584 | form div.clearfix.error>label,form div.clearfix.error span.help-inline,form div.clearfix.error span.help-block {
585 | color: #9d261d;
586 | }
587 |
588 | form div.clearfix.error input,form div.clearfix.error textarea {
589 | border-color: #c87872;
590 | -webkit-box-shadow: 0 0 3px rgba(171, 41, 32, 0.25);
591 | -moz-box-shadow: 0 0 3px rgba(171, 41, 32, 0.25);
592 | box-shadow: 0 0 3px rgba(171, 41, 32, 0.25);
593 | }
594 |
595 | form div.clearfix.error input:focus,form div.clearfix.error textarea:focus {
596 | border-color: #b9554d;
597 | -webkit-box-shadow: 0 0 6px rgba(171, 41, 32, 0.5);
598 | -moz-box-shadow: 0 0 6px rgba(171, 41, 32, 0.5);
599 | box-shadow: 0 0 6px rgba(171, 41, 32, 0.5);
600 | }
601 |
602 | form div.clearfix.error .input-prepend span.add-on,form div.clearfix.error .input-append span.add-on {
603 | background: #f4c8c5;
604 | border-color: #c87872;
605 | color: #b9554d;
606 | }
607 |
608 | table {
609 | width: 100%;
610 | margin-bottom: 18px;
611 | padding: 0;
612 | border-collapse: separate;
613 | *border-collapse: collapse;
614 | font-size: 13px;
615 | border: 1px solid #ddd;
616 | -webkit-border-radius: 4px;
617 | -moz-border-radius: 4px;
618 | border-radius: 4px;
619 | }
620 |
621 | table th,table td {
622 | padding: 10px 10px 9px;
623 | line-height: 18px;
624 | text-align: left;
625 | }
626 |
627 | table th {
628 | padding-top: 9px;
629 | font-weight: bold;
630 | vertical-align: middle;
631 | border-bottom: 1px solid #ddd;
632 | }
633 |
634 | table td {
635 | vertical-align: top;
636 | }
637 |
638 | table th+th,table td+td {
639 | border-left: 1px solid #ddd;
640 | }
641 |
642 | table tr+tr td {
643 | border-top: 1px solid #ddd;
644 | }
645 |
646 | table tbody tr:first-child td:first-child {
647 | -webkit-border-radius: 4px 0 0 0;
648 | -moz-border-radius: 4px 0 0 0;
649 | border-radius: 4px 0 0 0;
650 | }
651 |
652 | table tbody tr:first-child td:last-child {
653 | -webkit-border-radius: 0 4px 0 0;
654 | -moz-border-radius: 0 4px 0 0;
655 | border-radius: 0 4px 0 0;
656 | }
657 |
658 | table tbody tr:last-child td:first-child {
659 | -webkit-border-radius: 0 0 0 4px;
660 | -moz-border-radius: 0 0 0 4px;
661 | border-radius: 0 0 0 4px;
662 | }
663 |
664 | table tbody tr:last-child td:last-child {
665 | -webkit-border-radius: 0 0 4px 0;
666 | -moz-border-radius: 0 0 4px 0;
667 | border-radius: 0 0 4px 0;
668 | }
669 |
670 | .zebra-striped tbody tr:nth-child(odd) td {
671 | background-color: #f9f9f9;
672 | }
673 |
674 | .zebra-striped tbody tr:hover td {
675 | background-color: #f5f5f5;
676 | }
677 |
678 | .zebra-striped .header {
679 | cursor: pointer;
680 | }
681 |
682 | .zebra-striped .header:after {
683 | content: "";
684 | float: right;
685 | margin-top: 7px;
686 | border-width: 0 4px 4px;
687 | border-style: solid;
688 | border-color: #000 transparent;
689 | visibility: hidden;
690 | }
691 |
692 | .zebra-striped .header:hover:after {
693 | visibility: visible;
694 | }
695 |
696 | footer {
697 | margin-top: 17px;
698 | padding-top: 17px;
699 | border-top: 1px solid #eee;
700 | }
701 |
702 | .page-header {
703 | margin-bottom: 17px;
704 | border-bottom: 1px solid #ddd;
705 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
706 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
707 | box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
708 | }
709 |
710 | .page-header h1 {
711 | margin-bottom: 8px;
712 | }
713 |
714 | .close {
715 | float: right;
716 | color: #000000;
717 | font-size: 20px;
718 | font-weight: bold;
719 | line-height: 13.5px;
720 | text-shadow: 0 1px 0 #ffffff;
721 | filter: alpha(opacity=20);
722 | -khtml-opacity: 0.2;
723 | -moz-opacity: 0.2;
724 | opacity: 0.2;
725 | }
726 |
727 | .close:hover {
728 | color: #000000;
729 | text-decoration: none;
730 | filter: alpha(opacity=40);
731 | -khtml-opacity: 0.4;
732 | -moz-opacity: 0.4;
733 | opacity: 0.4;
734 | }
735 |
736 | pre {
737 | padding: 0;
738 | margin: 10px 0px 10px;
739 | overflow: auto;
740 | /*--If the Code exceeds the width, a scrolling is available--*/
741 | overflow-Y: hidden;
742 | /*--Hides vertical scroll created by IE--*/;
743 | }
744 |
745 | pre code {
746 | margin: 5px;
747 | /*--Left Margin--*/
748 | padding: 0px;
749 | display: block;
750 | line-height: 18px;
751 | }
752 |
753 | .center {
754 | text-align: center;
755 | }
756 |
757 | .left {
758 | text-align: left;
759 | }
760 |
761 | .right {
762 | text-align: right;
763 | }
--------------------------------------------------------------------------------
/css/icons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CMAPI/cmapi/ce58db64086603435eb0c03403fccb86a1eb111e/css/icons.gif
--------------------------------------------------------------------------------
/css/layout.css:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | margin:0;
4 | padding:0;
5 |
6 | }
7 | #wrap {
8 | max-width:1024px;
9 | margin:0 auto;
10 | }
11 | #header {
12 | padding:5px 10px;
13 |
14 | }
15 | h1 {
16 | margin:0;
17 | }
18 | #nav {
19 | padding:5px 10px;
20 |
21 | }
22 | #nav ul {
23 | margin:0;
24 | padding:0;
25 | list-style:none;
26 | }
27 | #nav li {
28 | display:inline;
29 | margin:0;
30 | padding:0;
31 | }
32 | #main {
33 | float:right;
34 | width:60%;
35 | padding:10px;
36 |
37 | }
38 | h2 {
39 | margin:0 0 1em;
40 | }
41 | #sidebar {
42 | float:left;
43 | width:30%;
44 | padding:10px;
45 |
46 | }
47 | #footer {
48 | clear:both;
49 | padding:5px 10px;
50 |
51 | }
52 | #footer p {
53 | margin:0;
54 | }
55 | * html #footer {
56 | height:1px;
57 | }
--------------------------------------------------------------------------------
/css/schema.css:
--------------------------------------------------------------------------------
1 | {
2 | html,body{margin:0;padding:0;}
3 | body {padding: 20px}
4 | h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;}
5 | table{border-collapse:collapse;border-spacing:0;}
6 | ol,ul{list-style:none;}
7 | q:before,q:after,blockquote:before,blockquote:after{content:"";}
8 | html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
9 | a:focus{outline:thin dotted;}
10 | a:hover,a:active{outline:0;}
11 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
12 | audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
13 | audio:not([controls]){display:none;}
14 | sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
15 | sup{top:-0.5em;}
16 | sub{bottom:-0.25em;}
17 | img{border:0;-ms-interpolation-mode:bicubic;}
18 | button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}
19 | button,input{line-height:normal;*overflow:visible;}
20 | button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
21 | button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
22 | input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
23 | input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
24 | textarea{overflow:auto;vertical-align:top;}
25 | html,body{background-color:#ffffff;}
26 | body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;}
27 | .container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;*display:inline;}
28 | .container:after{clear:both;}
29 | .container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;*display:inline;}
30 | .container-fluid:after{clear:both;}
31 | .container-fluid>.sidebar{float:left;width:220px;}
32 | .container-fluid>.content{margin-left:240px;}
33 | a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;}
34 | .pull-right{float:right;}
35 | .pull-left{float:left;}
36 | .hide{display:none;}
37 | .show{display:block;}
38 | .row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;*display:inline;}
39 | .row:after{clear:both;}
40 | p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;}
41 | h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;}
42 | h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;}
43 | h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;}
44 | h3,h4,h5,h6{line-height:36px;}
45 | h3{font-size:18px;}h3 small{font-size:14px;}
46 | h4{font-size:16px;}h4 small{font-size:12px;}
47 | h5{font-size:14px;}
48 | h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;}
49 | ul,ol{margin:0 0 18px 25px;}
50 | ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
51 | ul{list-style:disc;}
52 | ol{list-style:decimal;}
53 | li{line-height:18px;color:#808080;}
54 | ul.unstyled{list-style:none;margin-left:0;}
55 | dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;}
56 | dl dt{font-weight:bold;}
57 | dl dd{margin-left:9px;}
58 | hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;}
59 | strong{font-style:inherit;font-weight:bold;}
60 | em{font-style:italic;font-weight:inherit;line-height:inherit;}
61 | .muted{color:#bfbfbf;}
62 | blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;}
63 | blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';}
64 | address{display:block;line-height:18px;margin-bottom:18px;}
65 | code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
66 | code{padding:1px 3px;}
67 | pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;}
68 | form{margin-bottom:18px;}
69 | fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;}
70 | form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;*display:inline;}
71 | form .clearfix:after{clear:both;}
72 | label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;}
73 | label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;}
74 | form .input{margin-left:150px;}
75 | input[type=checkbox],input[type=radio]{cursor:pointer;}
76 | input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
77 | input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;}
78 | input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
79 | input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;}
80 | select,input[type=file]{height:27px;line-height:27px;*margin-top:4px;}
81 | select[multiple]{height:inherit;}
82 | textarea{height:auto;}
83 | .uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
84 | :-moz-placeholder{color:#bfbfbf;}
85 | ::-webkit-input-placeholder{color:#bfbfbf;}
86 | input,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);}
87 | input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);}
88 | input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;}
89 | form div.clearfix.error{background:#fae5e3;padding:10px 0;margin:-10px 0 10px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}form div.clearfix.error>label,form div.clearfix.error span.help-inline,form div.clearfix.error span.help-block{color:#9d261d;}
90 | form div.clearfix.error input,form div.clearfix.error textarea{border-color:#c87872;-webkit-box-shadow:0 0 3px rgba(171, 41, 32, 0.25);-moz-box-shadow:0 0 3px rgba(171, 41, 32, 0.25);box-shadow:0 0 3px rgba(171, 41, 32, 0.25);}form div.clearfix.error input:focus,form div.clearfix.error textarea:focus{border-color:#b9554d;-webkit-box-shadow:0 0 6px rgba(171, 41, 32, 0.5);-moz-box-shadow:0 0 6px rgba(171, 41, 32, 0.5);box-shadow:0 0 6px rgba(171, 41, 32, 0.5);}
91 | form div.clearfix.error .input-prepend span.add-on,form div.clearfix.error .input-append span.add-on{background:#f4c8c5;border-color:#c87872;color:#b9554d;}
92 | table{width:100%;margin-bottom:18px;padding:0;border-collapse:separate;*border-collapse:collapse;font-size:13px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;}
93 | table th{padding-top:9px;font-weight:bold;vertical-align:middle;border-bottom:1px solid #ddd;}
94 | table td{vertical-align:top;}
95 | table th+th,table td+td{border-left:1px solid #ddd;}
96 | table tr+tr td{border-top:1px solid #ddd;}
97 | table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
98 | table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
99 | table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
100 | table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
101 | .zebra-striped tbody tr:nth-child(odd) td{background-color:#f9f9f9;}
102 | .zebra-striped tbody tr:hover td{background-color:#f5f5f5;}
103 | .zebra-striped .header{cursor:pointer;}.zebra-striped .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;}
104 | .zebra-striped .header:hover:after{visibility:visible;}
105 | footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;}
106 | .page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;}
107 | .close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);-khtml-opacity:0.2;-moz-opacity:0.2;opacity:0.2;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;}
108 |
109 | pre {
110 | padding: 0;
111 | margin: 10px 0px 10px;
112 | overflow: auto; /*--If the Code exceeds the width, a scrolling is available--*/
113 | overflow-Y: hidden; /*--Hides vertical scroll created by IE--*/
114 | }
115 | pre code {
116 | margin: 5px; /*--Left Margin--*/
117 | padding: 0px;
118 | display: block;
119 | line-height: 18px;
120 | }
121 | .center { text-align:center}
122 | .left {text-align:left}
123 | .right {text-align:right}
124 |
125 |
126 | body{font-size:14px;line-height:1.6;font-family: Helvetica, arial, freesans, clean, sans-serif;padding: 20px;}
127 | body>*:first-child{margin-top:0!important;}
128 | body>*:last-child{margin-bottom:0!important;}
129 | body a:active {outline: none;}
130 | body a:hover {text-decoration: underline;}
131 | body a {color: #4183C4;text-decoration: none;}
132 | body a.absent{color:#c00;}
133 | body h1, body h2, h3, h4, h5, h6{margin:20px 0 10px;padding:0;font-weight:bold;-webkit-font-smoothing:antialiased;}
134 | body h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code{font-size:inherit;}
135 | body h1{font-size:28px;color:#000;}
136 | body h2{font-size:24px;border-bottom:1px solid #ccc;color:#000;}
137 | body h3{font-size:18px;}
138 | body h4{font-size:16px;}
139 | body h5{font-size:14px;}
140 | body h6{color:#777;font-size:14px;}
141 | body p, blockquote, ul, ol, dl, li, table, pre{margin:15px 0;}
142 | body hr{background:transparent url('../../images/modules/pulls/dirty-shade.png') repeat-x 0 0;border:0 none;color:#ccc;height:4px;padding:0;}
143 | body>h2:first-child,>h1:first-child,>h1:first-child+h2,>h3:first-child,>h4:first-child,>h5:first-child,>h6:first-child{margin-top:0;padding-top:0;}
144 | body h1+p, h2+p, h3+p, h4+p, h5+p, h6+p{margin-top:0;}
145 | body li p.first{display:inline-block;}
146 | body ul, ol{padding-left:30px;}
147 | body ul li>:first-child, ol li>:first-child{margin-top:0;}
148 | body ul li>:last-child, ol li>:last-child{margin-bottom:0;}
149 | body dl{padding:0;}
150 | body dl dt{font-size:14px;font-weight:bold;font-style:italic;padding:0;margin:15px 0 5px;}
151 | body dl dt:first-child{padding:0;}
152 | body dl dt>:first-child{margin-top:0;}
153 | body dl dt>:last-child{margin-bottom:0;}
154 | body dl dd{margin:0 0 15px;padding:0 15px;}
155 | body dl dd>:first-child{margin-top:0;}
156 | body dl dd>:last-child{margin-bottom:0;}
157 | body blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777;}
158 | body blockquote>:first-child{margin-top:0;}
159 | body blockquote>:last-child{margin-bottom:0;}
160 | body table{padding:0;}
161 | body table tr{border-top:1px solid #ccc;background-color:#fff;margin:0;padding:0;}
162 | body table tr:nth-child(2n){background-color:#f8f8f8;}
163 | body table tr th{font-weight:bold;}
164 | body table tr th, table tr td{border:1px solid #ccc;text-align:left;margin:0;padding:6px 13px;}
165 | body table tr th>:first-child, table tr td>:first-child{margin-top:0;}
166 | body table tr th>:last-child, table tr td>:last-child{margin-bottom:0;}
167 | body img{max-width:100%;}
168 | body span.frame{display:block;overflow:hidden;}
169 | body span.frame>span{border:1px solid #ddd;display:block;float:left;overflow:hidden;margin:13px 0 0;padding:7px;width:auto;}
170 | body span.frame span img{display:block;float:left;}
171 | body span.frame span span{clear:both;color:#333;display:block;padding:5px 0 0;}
172 | body span.align-center{display:block;overflow:hidden;clear:both;}
173 | body span.align-center>span{display:block;overflow:hidden;margin:13px auto 0;text-align:center;}
174 | body span.align-center span img{margin:0 auto;text-align:center;}
175 | body span.align-right{display:block;overflow:hidden;clear:both;}
176 | body span.align-right>span{display:block;overflow:hidden;margin:13px 0 0;text-align:right;}
177 | body span.align-right span img{margin:0;text-align:right;}
178 | body span.float-left{display:block;margin-right:13px;overflow:hidden;float:left;}
179 | body span.float-left span{margin:13px 0 0;}
180 | body span.float-right{display:block;margin-left:13px;overflow:hidden;float:right;}
181 | body span.float-right>span{display:block;overflow:hidden;margin:13px auto 0;text-align:right;}
182 | body code, tt{margin:0 2px;padding:0 5px;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px;}
183 | body pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent;}
184 | body .highlight pre, pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px;}
185 | body pre code, pre tt{background-color:transparent;border:none;}
186 | }
--------------------------------------------------------------------------------
/css/second.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-size: 14px;
3 | line-height: 1.6;
4 | font-family: Helvetica, arial, freesans, clean, sans-serif;
5 | padding: 20px;
6 | }
7 |
8 | body>*:first-child {
9 | margin-top: 0!important;
10 | }
11 |
12 | body>*:last-child {
13 | margin-bottom: 0!important;
14 | }
15 |
16 | body a:active {
17 | outline: none;
18 | }
19 |
20 | body a:hover {
21 | text-decoration: underline;
22 | }
23 |
24 | body a {
25 | color: #4183C4;
26 | text-decoration: none;
27 | }
28 |
29 | body a.absent {
30 | color: #c00;
31 | }
32 |
33 | body h1, body h2, h3, h3, h3, h4 {
34 | margin: 20px 0 10px;
35 | padding: 0;
36 | font-weight: bold;
37 | -webkit-font-smoothing: antialiased;
38 | }
39 |
40 | body h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h3 tt, h3 code, h3 tt, h3 code, h4 tt, h4 code {
41 | font-size: inherit;
42 | }
43 |
44 | body h1 {
45 | font-size: 28px;
46 | color: #000;
47 | }
48 |
49 | body h2 {
50 | font-size: 24px;
51 | border-bottom: 1px solid #ccc;
52 | color: #000;
53 | }
54 |
55 | body h3 {
56 | font-size: 18px;
57 | }
58 |
59 | body h3 {
60 | font-size: 16px;
61 | }
62 |
63 | body h3 {
64 | font-size: 14px;
65 | }
66 |
67 | body h4 {
68 | color: #777;
69 | font-size: 14px;
70 | }
71 |
72 | body p, blockquote, ul, ol, dl, li, table, pre {
73 | margin: 15px 0;
74 | }
75 |
76 | body hr {
77 | background: transparent url('../../images/modules/pulls/dirty-shade.png') repeat-x 0 0;
78 | border: 0 none;
79 | color: #ccc;
80 | height: 4px;
81 | padding: 0;
82 | }
83 |
84 | body>h2:first-child,>h1:first-child,>h1:first-child+h2,>h3:first-child,>h3:first-child,>h3:first-child,>h4:first-child {
85 | margin-top: 0;
86 | padding-top: 0;
87 | }
88 |
89 | body h1+p, h2+p, h3+p, h3+p, h3+p, h4+p {
90 | margin-top: 0;
91 | }
92 |
93 | body li p.first {
94 | display: inline-block;
95 | }
96 |
97 | body ul, ol {
98 | padding-left: 30px;
99 | }
100 |
101 | body ul li>:first-child, ol li>:first-child {
102 | margin-top: 0;
103 | }
104 |
105 | body ul li>:last-child, ol li>:last-child {
106 | margin-bottom: 0;
107 | }
108 |
109 | body dl {
110 | padding: 0;
111 | }
112 |
113 | body dl dt {
114 | font-size: 14px;
115 | font-weight: bold;
116 | font-style: italic;
117 | padding: 0;
118 | margin: 15px 0 5px;
119 | }
120 |
121 | body dl dt:first-child {
122 | padding: 0;
123 | }
124 |
125 | body dl dt>:first-child {
126 | margin-top: 0;
127 | }
128 |
129 | body dl dt>:last-child {
130 | margin-bottom: 0;
131 | }
132 |
133 | body dl dd {
134 | margin: 0 0 15px;
135 | padding: 0 15px;
136 | }
137 |
138 | body dl dd>:first-child {
139 | margin-top: 0;
140 | }
141 |
142 | body dl dd>:last-child {
143 | margin-bottom: 0;
144 | }
145 |
146 | body blockquote {
147 | border-left: 4px solid #DDD;
148 | padding: 0 15px;
149 | color: #777;
150 | }
151 |
152 | body blockquote>:first-child {
153 | margin-top: 0;
154 | }
155 |
156 | body blockquote>:last-child {
157 | margin-bottom: 0;
158 | }
159 |
160 | body table {
161 | padding: 0;
162 | }
163 |
164 | body table tr {
165 | border-top: 1px solid #ccc;
166 | background-color: #fff;
167 | margin: 0;
168 | padding: 0;
169 | }
170 |
171 | body table tr:nth-child(2n) {
172 | background-color: #f8f8f8;
173 | }
174 |
175 | body table tr th {
176 | font-weight: bold;
177 | }
178 |
179 | body table tr th, table tr td {
180 | border: 1px solid #ccc;
181 | text-align: left;
182 | margin: 0;
183 | padding: 6px 13px;
184 | }
185 |
186 | body table tr th>:first-child, table tr td>:first-child {
187 | margin-top: 0;
188 | }
189 |
190 | body table tr th>:last-child, table tr td>:last-child {
191 | margin-bottom: 0;
192 | }
193 |
194 | body img {
195 | max-width: 100%;
196 | }
197 |
198 | body span.frame {
199 | display: block;
200 | overflow: hidden;
201 | }
202 |
203 | body span.frame>span {
204 | border: 1px solid #ddd;
205 | display: block;
206 | float: left;
207 | overflow: hidden;
208 | margin: 13px 0 0;
209 | padding: 7px;
210 | width: auto;
211 | }
212 |
213 | body span.frame span img {
214 | display: block;
215 | float: left;
216 | }
217 |
218 | body span.frame span span {
219 | clear: both;
220 | color: #333;
221 | display: block;
222 | padding: 5px 0 0;
223 | }
224 |
225 | body span.align-center {
226 | display: block;
227 | overflow: hidden;
228 | clear: both;
229 | }
230 |
231 | body span.align-center>span {
232 | display: block;
233 | overflow: hidden;
234 | margin: 13px auto 0;
235 | text-align: center;
236 | }
237 |
238 | body span.align-center span img {
239 | margin: 0 auto;
240 | text-align: center;
241 | }
242 |
243 | body span.align-right {
244 | display: block;
245 | overflow: hidden;
246 | clear: both;
247 | }
248 |
249 | body span.align-right>span {
250 | display: block;
251 | overflow: hidden;
252 | margin: 13px 0 0;
253 | text-align: right;
254 | }
255 |
256 | body span.align-right span img {
257 | margin: 0;
258 | text-align: right;
259 | }
260 |
261 | body span.float-left {
262 | display: block;
263 | margin-right: 13px;
264 | overflow: hidden;
265 | float: left;
266 | }
267 |
268 | body span.float-left span {
269 | margin: 13px 0 0;
270 | }
271 |
272 | body span.float-right {
273 | display: block;
274 | margin-left: 13px;
275 | overflow: hidden;
276 | float: right;
277 | }
278 |
279 | body span.float-right>span {
280 | display: block;
281 | overflow: hidden;
282 | margin: 13px auto 0;
283 | text-align: right;
284 | }
285 |
286 | body code, tt {
287 | margin: 0 2px;
288 | padding: 0 5px;
289 | white-space: nowrap;
290 | border: 1px solid #eaeaea;
291 | background-color: #f8f8f8;
292 | border-radius: 3px;
293 | }
294 |
295 | body pre>code {
296 | margin: 0;
297 | padding: 0;
298 | white-space: pre;
299 | border: none;
300 | background: transparent;
301 | }
302 |
303 | body .highlight pre, pre {
304 | background-color: #f8f8f8;
305 | border: 1px solid #ccc;
306 | font-size: 13px;
307 | line-height: 19px;
308 | overflow: auto;
309 | padding: 6px 10px;
310 | border-radius: 3px;
311 | }
312 |
313 | body pre code, pre tt {
314 | background-color: transparent;
315 | border: none;
316 | }
--------------------------------------------------------------------------------
/img/cmapi.clustering.figure1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CMAPI/cmapi/ce58db64086603435eb0c03403fccb86a1eb111e/img/cmapi.clustering.figure1.jpg
--------------------------------------------------------------------------------
/img/github-ribbon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CMAPI/cmapi/ce58db64086603435eb0c03403fccb86a1eb111e/img/github-ribbon.png
--------------------------------------------------------------------------------
/img/kml.diagram.figureA1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CMAPI/cmapi/ce58db64086603435eb0c03403fccb86a1eb111e/img/kml.diagram.figureA1.jpg
--------------------------------------------------------------------------------
/img/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CMAPI/cmapi/ce58db64086603435eb0c03403fccb86a1eb111e/img/loading.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Common Map API v1.2.0
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | var DT = $.ui.fancytree;
3 | $.ui.fancytree.debug("Using fancytree " + $.ui.fancytree.version);
4 |
5 | // attach to all instances
6 | $("#tree")
7 | .fancytree({
8 | source: {
9 | url: "cmapi-toc-1.2.0.json"
10 | },
11 | checkbox: false,
12 | selectMode: 1,
13 | icons: false,
14 | minExpandLevel: 1,
15 | activate: function (event, data) {
16 | if(data.node.key !== ""){
17 | cmapi.channel.renderer.loadContent(data.node);
18 | }
19 | },
20 | dblclick: function (event, data) {
21 |
22 | cmapi.channel.renderer.loadContent(data.node);
23 | }
24 | });
25 |
26 | cmapi.channel.renderer.loadContent({data: {type: "overview"}, key:"cmapi.overview"});
27 |
28 | });
--------------------------------------------------------------------------------
/js/renderer.js:
--------------------------------------------------------------------------------
1 | /*global window, cmapi, tv4, alert */
2 | var cmapi = cmapi || {};
3 | cmapi.channel = cmapi.channel || {};
4 |
5 | cmapi.channel.renderer = (function () {
6 | var publicInterface,
7 | baseUrl = "channels/",
8 | currentSchema,
9 | currentChannel,
10 | currentOverview;
11 |
12 | function loadScript(args) {
13 | var script = document.createElement("script");
14 |
15 | script.type = "text/javascript";
16 |
17 | if (script.readyState) { //IE
18 | script.onreadystatechange = function () {
19 | if (script.readyState === "loaded" || script.readyState === "complete") {
20 | script.onreadystatechange = null;
21 | args.callback(args);
22 | }
23 | };
24 | } else { //Others
25 | script.onload = function () {
26 | args.callback(args);
27 | };
28 | }
29 |
30 | script.src = args.url;
31 | document.getElementsByTagName("head")[0].appendChild(script);
32 | }
33 |
34 | function checkRequired(prop, schema) {
35 | var i, len, returnValue = "optional";
36 | if (schema.required) {
37 | len = schema.required.length;
38 | for (i = 0; i < len; i++) {
39 | if (prop === schema.required[i]) {
40 | returnValue = 'required';
41 | break;
42 | }
43 | }
44 | }
45 | return returnValue;
46 | }
47 |
48 | function getValidationErrorString(valError) {
49 | var message = "The example has failed to validate: ",
50 | i;
51 | for (i = 0; i < valError.errors.length; i = 1 + i) {
52 | message += "\n " + valError.errors[i].message + " " + valError.errors[i].dataPath;
53 | }
54 |
55 | return message;
56 | }
57 |
58 | function getObjectString(obj) {
59 | var raw = JSON.stringify(obj),
60 | objChars = raw.split(""),
61 | tab = " ",
62 | indentation = "",
63 | indent = 0,
64 | len = objChars.length,
65 | i,
66 | txt,
67 | k,
68 | bypass = false;
69 |
70 | for (i = 0; i < len; i++) {
71 | txt = objChars[i];
72 | if (bypass === false) {
73 | switch (txt) {
74 | case '"':
75 | bypass = true;
76 | break;
77 | case "{":
78 | indent++;
79 | indentation = "";
80 | for (k = 0; k < indent; k++) {
81 | indentation += tab;
82 | }
83 | objChars[i] = "{\n" + indentation;
84 |
85 | break;
86 | case "[":
87 | indent++;
88 | indentation = "";
89 | for (k = 0; k < indent; k++) {
90 | indentation += tab;
91 | }
92 | objChars[i] = "[\n" + indentation;
93 |
94 | break;
95 | case ",":
96 | objChars[i] = ",\n" + indentation;
97 | break;
98 | case "}":
99 | indent--;
100 | indentation = "";
101 | for (k = 0; k < indent; k++) {
102 | indentation += tab;
103 | }
104 | if (objChars[i + 1] === ",") {
105 | objChars[i] = "\n" + indentation + "}";
106 | objChars[i + 1] = ",\n" + indentation;
107 | } else {
108 | objChars[i] = "\n" + indentation + "}" + indentation;
109 | }
110 | break;
111 | case "]":
112 | indent--;
113 | indentation = "";
114 | for (k = 0; k < indent; k++) {
115 | indentation += tab;
116 | }
117 | if (objChars[i + 1] === ",") {
118 | objChars[i] = "\n" + indentation + "]";
119 | objChars[i + 1] = ",\n" + indentation;
120 | } else {
121 | objChars[i] = "\n" + indentation + "]" + indentation;
122 | }
123 | break;
124 | }
125 | } else {
126 | if (txt === '"') {
127 | bypass = false;
128 | }
129 | }
130 | }
131 | return objChars.join("");
132 | }
133 |
134 | function validate(payload, schema) {
135 | var response = {
136 | valid: true,
137 | message: "Valid CMAPI Payload"
138 | },
139 | valid = tv4.validateMultiple(payload, schema);
140 |
141 | if (!valid.valid) {
142 | response.message = getValidationErrorString(valid);
143 | response.valid = false;
144 | }
145 | return response;
146 | }
147 |
148 | function getObjectTable(obj, output, parent) {
149 | var prop, propVal, optional, type, defaultVal, len, i, subProp, j, options, opLen, opt, enums;
150 | output.push('Property Required Type Default Description ');
151 |
152 | for (prop in obj) {
153 | propVal = obj[prop];
154 |
155 | optional = checkRequired(prop, parent);
156 | if (propVal.hasOwnProperty("type")) {
157 | type = propVal.type;
158 | } else if (propVal.hasOwnProperty("enum")) {
159 | enums = JSON.stringify(propVal.enum).split(",").join(", ");
160 | type = "enumeration " + enums;
161 | }
162 | defaultVal = "";
163 | if (propVal.hasOwnProperty("default")) {
164 | defaultVal = propVal["default"];
165 | }
166 | if ($.isArray(propVal)) {
167 | output.push('');
168 | output.push('' + prop + ' ');
169 | len = propVal.length;
170 | for (i = 0; i < len; i++) {
171 | output.push(' ');
172 | output.push('');
173 | output.push(getObjectTable(propVal[i], output, obj));
174 | output.push(' ');
175 | output.push(' ');
176 | }
177 | output.push('');
178 | output.push('');
179 | } else {
180 | output.push('');
181 | output.push('' + prop + ' ');
182 | output.push('' + optional + ' ');
183 | output.push('' + type + ' ');
184 | output.push('' + defaultVal + ' ');
185 | output.push('' + propVal.description + ' ');
186 | output.push(' ');
187 | if (propVal.hasOwnProperty("properties")) {
188 | subProp = propVal.properties;
189 |
190 | output.push('');
191 | output.push('');
192 | output.push(getObjectTable(subProp, output, propVal));
193 | output.push(' ');
194 | output.push(' ');
195 |
196 | /* } else if (type === "array" && propVal.hasOwnProperty("items")){
197 | subProp = propVal.items;
198 | output.push('');
199 | output.push('');
200 | len = subProp.length;
201 | for(i=0;i');
205 | output.push(' ');
206 | */
207 | } else if (propVal.hasOwnProperty("oneOf")) {
208 | options = propVal.oneOf;
209 | opLen = options.length;
210 | output.push('');
211 | output.push('');
212 | output.push('One Of: ');
213 | for (j = 0; j < opLen; j++) {
214 | opt = options[j].properties;
215 | output.push(options[j].title + ' ');
216 | output.push(getObjectTable(opt, output, options[j]));
217 |
218 | }
219 | output.push(' ');
220 | output.push(' ');
221 |
222 | }
223 | }
224 |
225 | }
226 | output.push('
');
227 | }
228 |
229 | function render(link, channelDef) {
230 |
231 | var output = [],
232 | i = 0,
233 | prop,
234 | propVal,
235 | optional,
236 | schema = channelDef.schema,
237 | noteLen = channelDef.notes.length,
238 | type,
239 | defaultVal;
240 |
241 | try {
242 | output.push('' + schema.title + ' ');
243 |
244 | output.push('Purpose: ');
245 |
246 | output.push('' + schema.description + '
');
247 |
248 | output.push('Channel: ');
249 |
250 | output.push('' + schema.title + '
');
251 |
252 | output.push('Payload: ');
253 |
254 | output.push('');
255 | output.push('{');
256 | for (prop in schema.properties) {
257 | if (i > 0) {
258 | output.push(", ");
259 | }
260 | output.push(' ');
261 | optional = checkRequired(prop, schema);
262 | output.push(prop + ": " + schema.properties[prop].type + " (" + optional + ")");
263 | i++;
264 | }
265 | i = 0;
266 | output.push(' }');
267 |
268 | output.push('
');
269 | output.push('Properties:');
270 | getObjectTable(schema.properties, output, schema);
271 | /*
272 | output.push(' Property Required Type Default Description ');
273 |
274 | for (prop in schema.properties) {
275 | propVal = schema.properties[prop];
276 | optional = checkRequired(prop, schema);
277 | if (propVal.hasOwnProperty("type")) {
278 | type = propVal.type;
279 | } else if (propVal.hasOwnProperty("enum")) {
280 | type = "enumeration " + JSON.stringify(propVal.enum);
281 | }
282 | defaultVal = "";
283 | if (propVal.hasOwnProperty("default")) {
284 | defaultVal = propVal["default"];
285 | }
286 |
287 | output.push('');
288 | output.push('' + prop + ' ');
289 | output.push('' + optional + ' ');
290 | output.push('' + type + ' ');
291 | output.push('' + defaultVal + ' ');
292 | output.push('' + propVal.description + ' ');
293 | output.push(' ');
294 |
295 | }
296 | output.push('
');
297 | */
298 | output.push('Notes ');
299 | if (noteLen > 0) {
300 | for (i = 0; i < noteLen; i++) {
301 |
302 | output.push('' + (i + 1) + ': ' + channelDef.notes[i] + '
');
303 | }
304 | } else {
305 | output.push('There are no notes for this channel
');
306 | }
307 | output.push('Schema ');
308 | output.push('Link ');
309 | output.push('' + link + '
');
310 | // output.push('' + JSON.stringify(channelDef.schema) + '
');
311 | output.push('');
312 | output.push(getObjectString(channelDef.schema));
313 | output.push('
');
314 | } catch (err) {
315 | output = ["An error occured when parsing the schema: " + err.message];
316 | }
317 | return output.join("");
318 | }
319 |
320 | function appendExamples(exampleLink, examples, channelDef) {
321 | var output = [],
322 | exampleLen,
323 | exampleValidation,
324 | validationIntent,
325 | i;
326 | try {
327 | if (examples !== undefined && examples !== null) {
328 | exampleLen = examples.length;
329 | if (exampleLen > 0) {
330 | output.push('Examples ');
331 | output.push('Link ');
332 | output.push('' + exampleLink + '
');
333 | for (i = 0; i < exampleLen; i++) {
334 | if (examples[i].valid === true) {
335 | validationIntent = "Pass Validation";
336 | } else {
337 | validationIntent = "Fail Validation";
338 | }
339 |
340 | output.push('' + examples[i].title + ' - ' + validationIntent + ' ');
341 |
342 | exampleValidation = validate(examples[i].payload, channelDef.schema);
343 | if (exampleValidation.valid === true) {
344 | output.push('' + exampleValidation.message + '
');
345 | } else {
346 | output.push('' + exampleValidation.message + '
');
347 | }
348 | if (exampleValidation.valid === examples[i].valid) {
349 | output.push('This message validated as expected
');
350 | } else {
351 | output.push('This example DID NOT validate as expected. This example was expected to validate as ' + examples[i].valid.toString() + '
');
352 | }
353 |
354 | output.push('');
355 |
356 | }
357 | }
358 | }
359 | output.push('Try it yourself...
');
360 | output.push('');
361 | output.push('Validate ');
362 | output.push('Clear ');
363 | } catch (err) {
364 | output = ["An error occured while parsing the example: " + err.message];
365 | }
366 | return output.join("");
367 | }
368 |
369 | function validateUserPayload() {
370 | var exampleValidation,
371 | message = "";
372 | try {
373 | var target = $("#userPayloadValid");
374 | exampleValidation = validate(JSON.parse($("#userPayloadInput").val()), currentSchema);
375 | message = exampleValidation.message;
376 | if (exampleValidation.valid === true) {
377 | target.css("color", "green");
378 | } else {
379 | target.css("color", "red");
380 | }
381 |
382 | } catch (err) {
383 | message = "An error occured while attempting to validate your payload: " + err.message;
384 | }
385 | alert(message);
386 | }
387 |
388 | function renderOverview(overview) {
389 | var len,
390 | sLen,
391 | i,
392 | k,
393 | section,
394 | sections,
395 | paragraphs,
396 | output = [];
397 | if (overview.hasOwnProperty("title")) {
398 | output.push('' + overview.title + ' ');
399 | }
400 | sections = overview.sections;
401 | len = sections.length;
402 | for (i = 0; i < len; i++) {
403 | section = sections[i];
404 | output.push('' + section.title + ' ');
405 | paragraphs = section.paragraphs;
406 | sLen = paragraphs.length;
407 | for (k = 0; k < sLen; k++) {
408 | output.push('' + paragraphs[k] + '
');
409 | }
410 | }
411 | return output.join("");
412 | }
413 |
414 | function exampleLoaded(args) {
415 | $('#main').html($('#main').html() + appendExamples(args.url, cmapi.channel[args.channel].examples, cmapi.channel[args.channel]));
416 | }
417 |
418 | function channelLoaded(args) {
419 | var channelDef = cmapi.channel[args.channel];
420 | currentChannel = args.channel;
421 | currentSchema = channelDef.schema;
422 | $('#main').html(render(args.url, cmapi.channel[args.channel]));
423 | var url = baseUrl + args.channel + ".examples.js";
424 | loadScript({
425 | url: url,
426 | channel: args.channel,
427 | callback: exampleLoaded
428 | });
429 |
430 | }
431 |
432 | function overviewLoaded() {
433 | var overview = cmapi.overview[currentOverview];
434 | $('#main').html(renderOverview(overview));
435 | }
436 |
437 | function loadChannelDef(channel) {
438 | var url = baseUrl + channel + ".js";
439 | loadScript({
440 | url: url,
441 | channel: channel,
442 | callback: channelLoaded
443 | });
444 | }
445 |
446 | function loadOverview(target) {
447 | var url = "channels/" + target + ".js";
448 | currentOverview = target;
449 | loadScript({
450 | url: url,
451 | channel: "",
452 | callback: overviewLoaded
453 | });
454 | }
455 |
456 | publicInterface = {
457 | loadContent: function (target) {
458 | $('#main').html(' ');
459 | if (!target.data.hasOwnProperty("type")) {
460 | target.data.type = "";
461 | }
462 | switch (target.data.type) {
463 | case "overview":
464 | loadOverview(target.key);
465 | break;
466 | default:
467 | loadChannelDef(target.key);
468 | break;
469 | }
470 | },
471 | validateInput: function () {
472 | validateUserPayload();
473 | },
474 | clearInput: function () {
475 | $("#userPayloadInput").val("");
476 | }
477 | };
478 | return publicInterface;
479 | }());
--------------------------------------------------------------------------------
/test/channel-validation-descriptor.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
51 |
52 | window.name Transport
53 | Value in window.name is
54 | HTTP Status Code is 200
55 |
56 |
57 |
--------------------------------------------------------------------------------
/test/channel-validation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CMAPI Channel Validation
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
48 | CMAPI allows extended properties on all payloads. If you want to validate using only properties defined in the schema, set to "Restrict"
49 |
50 | Allow
51 | Restrict
52 |
53 |
54 |
55 | Clear Errors
56 |
57 |
58 |
59 |
60 |
61 | Date
62 | Channel
63 | Error
64 | View
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/test/channel-validation.js:
--------------------------------------------------------------------------------
1 | /*global window, owf, OWF, tv4, Ozone, alert */
2 | var cmapi = window.cmapi || {};
3 |
4 | cmapi.channelList = [
5 | "map.overlay.create",
6 | "map.overlay.remove",
7 | "map.overlay.hide",
8 | "map.overlay.show",
9 | "map.overlay.style",
10 | "map.overlay.update",
11 | "map.overlay.cluster.activate",
12 | "map.overlay.cluster.deactivate",
13 | "map.overlay.cluster.remove",
14 | "map.overlay.cluster.set",
15 | "map.feature.plot",
16 | "map.feature.plot.url",
17 | "map.feature.unplot",
18 | "map.feature.hide",
19 | "map.feature.show",
20 | "map.feature.update",
21 | "map.feature.selected",
22 | "map.feature.deselected",
23 | "map.view.zoom",
24 | "map.view.center.overlay",
25 | "map.view.center.feature",
26 | "map.view.center.location",
27 | "map.view.center.bounds",
28 | "map.view.clicked",
29 | "map.status.request",
30 | "map.status.selected",
31 | "map.status.view",
32 | "map.status.format",
33 | "map.status.about",
34 | "map.status.selected",
35 | "map.error"
36 | ];
37 |
38 | cmapi.channelValidation = (function () {
39 | // interface returned from singleton function stance to enfors public / private member access
40 | var publicInterface,
41 | // Private array of messages that fail validation and the error
42 | failures = [],
43 | failureLookup = {},
44 | environment = {
45 | pubSub: {}
46 | },
47 | msgTotal = 0,
48 | errorTotal = 0;
49 |
50 | function getValidationErrorString(valError, channel, index, total) {
51 | var message,
52 | i;
53 | if (total <= 1) {
54 | message = "The " + channel + " message is not valid: ";
55 | } else {
56 | message = "The " + channel + " message payload " + index + " of " + total + " is not valid: ";
57 | }
58 | for (i = 0; i < valError.errors.length; i++) {
59 | message += "\n " + valError.errors[i].message + " " + valError.errors[i].schemaPath + " " + valError.errors[i].dataPath;
60 | }
61 | /*
62 | for (i = 0; i < valError.missing.length; i++) {
63 | message += "\n " + valError.missing[i].message + "\n Schema Path: " + valError.errors[i].schemaPath;
64 | }
65 | */
66 | return message;
67 | }
68 |
69 | publicInterface = {
70 | subscribe: function (channels) {
71 | environment.pubSub = new Ozone.eventing.Widget('rpc_relay.uncompressed.html');
72 | var len, i;
73 | len = channels.length;
74 | for (i = 0; i < len; i++) {
75 | environment.pubSub.subscribe(channels[i], cmapi.channelValidation.validateMessage);
76 | }
77 | },
78 | view: function (id) {
79 | var msgError = failureLookup[id];
80 | if (msgError !== undefined && msgError !== null) {
81 | alert(JSON.stringify(msgError));
82 | }
83 | },
84 | clear: function () {
85 | $('#outputDiv').html('');
86 | },
87 | validateMessage: function (sender, msg, channel) {
88 | var response = {
89 | valid: true,
90 | message: "Valid CMAPI Payload"
91 | },
92 | schema,
93 | valid,
94 | len,
95 | i,
96 | html = [],
97 | output,
98 | failure,
99 | id,
100 | additionalProperties = parseInt($("#allow-additional-select").val(),10);
101 |
102 | msgTotal++;
103 |
104 | if (typeof msg === "string") {
105 | msg = JSON.parse(msg);
106 | }
107 | schema = cmapi.channel[channel].schema;
108 | if(additionalProperties === 1){
109 | schema.additionalProperties = false;
110 | } else {
111 | schema.additionalProperties = true;
112 | }
113 | if (!$.isArray(msg)) {
114 | msg = [msg];
115 | }
116 | len = msg.length;
117 | for (i = 0; i < len; i++) {
118 | valid = tv4.validateMultiple(msg[i], schema);
119 |
120 | if (!valid.valid) {
121 | errorTotal++;
122 | response.message = getValidationErrorString(valid, channel, i, len);
123 | response.valid = false;
124 | id = "error_" + failures.length;
125 | failure = {
126 | msg: msg[i],
127 | sender: sender,
128 | channel: channel,
129 | error: response.message,
130 | date: new Date(),
131 | id: id
132 | };
133 | failures.push(failure);
134 | failureLookup[failure.id] = failure;
135 | //$("#outputTA").val($("#outputTA").val() + response.message + "<\n>");
136 | html.push("");
137 | html.push("" + failure.date.toUTCString() + " ");
138 | html.push("" + failure.channel + " ");
139 | html.push("" + failure.error + " ");
140 | html.push('View ');
141 | html.push(" ");
142 | output = html.join("");
143 | $('#errorTable tr:last').after(output);
144 | }
145 |
146 | $('#totalsSpan').text(errorTotal + " of " + msgTotal + " message payloads have errors. See errors below.");
147 | }
148 | return response;
149 | }
150 |
151 | };
152 | return publicInterface;
153 | }());
154 |
155 | $(function () {
156 | OWF.ready(function () {
157 | cmapi.channelValidation.subscribe(cmapi.channelList);
158 |
159 | });
160 | });
--------------------------------------------------------------------------------
/test/rpc_relay.uncompressed.html:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------