├── CHANGELOG.md
├── .gitignore
├── README.md
├── resources
├── logback.xml
├── bgg-schema.edn
└── graphiql
│ ├── index.html
│ └── graphiql.css
├── src
└── bgg_graphql_proxy
│ ├── main.clj
│ ├── cache.clj
│ ├── schema.clj
│ ├── server.clj
│ └── client.clj
├── project.clj
└── LICENSE
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## vNEXT - UNRELEASED
2 |
3 | Initial version.
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /checkouts
4 | pom.xml
5 | pom.xml.asc
6 | *.jar
7 | *.class
8 | /.lein-*
9 | /.nrepl-port
10 | .hgignore
11 | .hg/
12 | .idea/
13 | *.iml
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bgg-graphql-proxy
2 |
3 | A GraphQL frontend to the BoardGameGeek XML web api.
4 |
5 | ## Usage
6 |
7 | Coming soon!
8 |
9 | ## License
10 |
11 | Copyright © 2017 Howard M. Lewis Ship
12 |
13 |
--------------------------------------------------------------------------------
/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/bgg_graphql_proxy/main.clj:
--------------------------------------------------------------------------------
1 | (ns bgg-graphql-proxy.main
2 | (:require
3 | [io.pedestal.http :as http]
4 | [com.walmartlabs.lacinia :refer [execute]]
5 | [bgg-graphql-proxy.schema :refer [bgg-schema]]
6 | [bgg-graphql-proxy.server :refer [pedestal-server]]))
7 |
8 | (defn stop-server
9 | [server]
10 | (http/stop server)
11 | nil)
12 |
13 | (defn start-server
14 | "Creates and starts Pedestal server, ready to handle Graphql (and Graphiql) requests."
15 | []
16 | (-> (bgg-schema)
17 | pedestal-server
18 | http/start))
19 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject com.howardlewisship/bgg-graphql-proxy "0.0.1"
2 | :description "GraphQL interface to BoardGameGeek"
3 | :dependencies [[org.clojure/clojure "1.8.0"]
4 | [io.aviso/logging "0.2.0"]
5 | [com.walmartlabs/lacinia "0.13.0"]
6 | [clj-http "2.3.0"]
7 | [org.clojure/data.json "0.2.6"]
8 | [org.clojure/data.xml "0.0.8"]
9 | [io.pedestal/pedestal.service "0.5.2"]
10 | [io.pedestal/pedestal.jetty "0.5.2"]]
11 | :codox {:source-uri "https://github.com/hlship/boardgamegeek-graphql-proxy/blob/master/{filepath}#L{line}"
12 | :metadata {:doc/format :markdown}})
13 |
--------------------------------------------------------------------------------
/src/bgg_graphql_proxy/cache.clj:
--------------------------------------------------------------------------------
1 | (ns bgg-graphql-proxy.cache
2 | "A little bit of in-memory caching so we don't overload BGG.
3 |
4 | THe cache is simply a standard atom."
5 | (:require [clojure.set :as set]))
6 |
7 | (defn resolve-by-id
8 | [cache category id]
9 | (get-in @cache [category id]))
10 |
11 | (defn resolve-by-ids
12 | [cache category ids]
13 | (let [category-cache (get @cache category {})
14 | cached-values (keep category-cache ids)
15 | cached-ids (->> cached-values
16 | (map :id)
17 | set)
18 | uncached-ids (set/difference (set ids) cached-ids)]
19 | [cached-values (seq uncached-ids)]))
20 |
21 | (defn ^:private map-by-id
22 | [values]
23 | (persistent! (reduce (fn [m v]
24 | (assoc! m (:id v) v))
25 | (transient {})
26 | values)))
27 |
28 | (defn fill
29 | [cache category values]
30 | (swap! cache update category merge (map-by-id values)))
31 |
--------------------------------------------------------------------------------
/src/bgg_graphql_proxy/schema.clj:
--------------------------------------------------------------------------------
1 | (ns bgg-graphql-proxy.schema
2 | (:require
3 | [clojure.java.io :as io]
4 | [clojure.edn :as edn]
5 | [com.walmartlabs.lacinia.schema :as schema]
6 | [com.walmartlabs.lacinia.util :refer [attach-resolvers]]
7 | [bgg-graphql-proxy.client :as client]))
8 |
9 | (defn ^:private resolve-board-game
10 | [context args _value]
11 | ;; TODO: Error handling, including not found
12 | (client/get-board-game (:cache context) (:id args)))
13 |
14 | (defn ^:private resolve-search
15 | [context args _value]
16 | (client/search (:cache context) (:term args)))
17 |
18 | (defn ^:private extract-ids
19 | [board-game key args]
20 | (let [{:keys [limit]} args]
21 | (cond->> (get board-game key)
22 | limit (take limit))))
23 |
24 | (defn ^:private resolve-game-publishers
25 | [context args board-game]
26 | (client/publishers (:cache context) (extract-ids board-game :publisher-ids args)))
27 |
28 | (defn ^:private resolve-game-designers
29 | [context args board-game]
30 | (client/designers (:cache context) (extract-ids board-game :designer-ids args)))
31 |
32 | (defn bgg-schema
33 | []
34 | (-> (io/resource "bgg-schema.edn")
35 | slurp
36 | edn/read-string
37 | (attach-resolvers {:resolve-game resolve-board-game
38 | :resolve-search resolve-search
39 | :resolve-game-publishers resolve-game-publishers
40 | :resolve-game-designers resolve-game-designers})
41 | schema/compile))
42 |
--------------------------------------------------------------------------------
/src/bgg_graphql_proxy/server.clj:
--------------------------------------------------------------------------------
1 | (ns bgg-graphql-proxy.server
2 | (:require
3 | [io.pedestal.http :as http]
4 | [io.pedestal.http.route :as route]
5 | [io.pedestal.interceptor :refer [interceptor]]
6 | [clojure.java.io :as io]
7 | [clojure.data.json :as json]
8 | [com.walmartlabs.lacinia :refer [execute]]
9 | [ring.util.response :as response]
10 | [clojure.string :as str]))
11 |
12 |
13 | (defn ^:private index-handler
14 | "Handles the index request as if it were /graphiql/index.html."
15 | [request]
16 | (response/redirect "/index.html"))
17 |
18 | (defn variable-map
19 | "Reads the `variables` query parameter, which contains a JSON string
20 | for any and all GraphQL variables to be associated with this request.
21 |
22 | Returns a map of the variables (using keyword keys)."
23 | [request]
24 | (let [vars (get-in request [:query-params :variables])]
25 | (if-not (str/blank? vars)
26 | (json/read-str vars :key-fn keyword)
27 | {})))
28 |
29 | (defn extract-query
30 | [request]
31 | (case (:request-method request)
32 | :get (get-in request [:query-params :query])
33 | :post (slurp (:body request))
34 | :else ""))
35 |
36 | (defn ^:private graphql-handler
37 | "Accepts a GraphQL query via GET or POST, and executes the query.
38 | Returns the result as text/json."
39 | [compiled-schema]
40 | (let [context {:cache (atom {})}]
41 | (fn [request]
42 | (let [vars (variable-map request)
43 | query (extract-query request)
44 | result (execute compiled-schema query vars context)
45 | status (if (-> result :errors seq)
46 | 400
47 | 200)]
48 | {:status status
49 | :headers {"Content-Type" "application/json"}
50 | :body (json/write-str result)}))))
51 |
52 | (defn ^:private routes
53 | [compiled-schema]
54 | (let [query-handler (graphql-handler compiled-schema)]
55 | (route/expand-routes
56 | #{["/" :get index-handler :route-name :graphiql-ide-index]
57 | ["/graphql" :post query-handler :route-name :graphql-post]
58 | ["/graphql" :get query-handler :route-name :graphql-get]})))
59 |
60 | (defn pedestal-server
61 | "Creates and returns server instance, ready to be started."
62 | [compiled-schema]
63 | (http/create-server {:env :dev
64 | ::http/routes (routes compiled-schema)
65 | ::http/resource-path "graphiql"
66 | ::http/port 8888
67 | ::http/type :jetty
68 | ::http/join? false}))
69 |
--------------------------------------------------------------------------------
/resources/bgg-schema.edn:
--------------------------------------------------------------------------------
1 | {:interfaces {
2 | :BGGEntity {:description "A Standard BGG entity with id, name, and optional description."
3 | :fields {:id {:type (non-null ID)}
4 | :name {:type (non-null String)}
5 | :description {:type String}}}}
6 | :objects {
7 | :Company {:description "A company that may publish a Board Game."
8 | :implements [:BGGEntity]
9 | :fields {:id {:type (non-null ID)}
10 | :name {:type (non-null String)}
11 | :description {:type String}}}
12 |
13 | :Designer {:description "Person who designs a game."
14 | :implements [:BGGEntity]
15 | :fields {:id {:type (non-null ID)}
16 | :name {:type (non-null String)}
17 | :description {:type String}}}
18 |
19 | :BoardGame {:description "Details about a Board Game including description, number of players, creators, and publishers."
20 | :implements [:BGGEntity]
21 | :fields {:id {:type (non-null ID)}
22 | :name {:type (non-null String)
23 | :description "Common name or title for the game."}
24 | :publish_year {:type Int}
25 | :min_players {:type Int}
26 | :max_players {:type Int}
27 | :playing_time {:type Int
28 | :description "Approximiate playing time, in minutes."}
29 | :min_player_age {:type Int
30 | :description "Minimum age, in years, for a player. A rough guide to complexity of the game."}
31 | :description {:type String
32 | :description "Short text description of the game."}
33 | :thumbnail {:type String
34 | :description "URL for a small image representing the game, used in various lists."}
35 | :image {:type String
36 | :description "URL for a larger image for the game."}
37 | :publishers {:type (list :Company)
38 | :args {:limit {:type Int
39 | :description "Maximum number of results to include.
40 | Default is no limit."}}
41 | :description "Company that has published the game."
42 | :resolve :resolve-game-publishers}
43 | :designers {:type (list :Designer)
44 | :args {:limit {:type Int
45 | :description "Maximum number of results to include. Default is no limit."}}
46 | :description "Person who contributed to the design of the game."
47 | :resolve :resolve-game-designers}}}}
48 |
49 | :queries {
50 | :search {:type (list :BoardGame)
51 | :description "Searches for board games matching a search term."
52 | :args {:term {:type (non-null String)
53 | :description "Search term used to locate Games."}}
54 | :resolve :resolve-search}
55 | :game {:type :BoardGame
56 | :description "Retrieve a single BoardGame by its unique id."
57 | :args {:id {:type (non-null ID)
58 | :description "Unique identifier for the game."}}
59 | :resolve :resolve-game}}}
60 |
--------------------------------------------------------------------------------
/resources/graphiql/index.html:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Loading...
28 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/bgg_graphql_proxy/client.clj:
--------------------------------------------------------------------------------
1 | (ns bgg-graphql-proxy.client
2 | "Client code for accessing BoardGameGeek, and converting XML responses into Clojure data."
3 | (:require
4 | [bgg-graphql-proxy.cache :as cache]
5 | [clj-http.client :as client]
6 | [clojure.data.xml :as xml]
7 | [clojure.string :as str]
8 | [clojure.tools.logging :as log])
9 | (:import (java.io StringReader)))
10 |
11 | (def ^:private base-url "https://www.boardgamegeek.com/xmlapi")
12 |
13 | (defn ^:private expect-tag [tag element]
14 | (when-not (= tag (:tag element))
15 | (throw (ex-info (format "Wrong tag. Expected `%s', not `%s'."
16 | (name tag)
17 | (-> element :tag name))
18 | {:expected-tag tag
19 | :actual-tag (:tag element)
20 | :element element})))
21 | element)
22 |
23 | (defn ^:private parse-int [s] (Integer/parseInt s))
24 |
25 | (defn ^:private prefix-with-https [s] (str "https:" s))
26 |
27 | (def ^:private boardgame-content-renames
28 | {:yearpublished [:publish-year parse-int]
29 | :minplayers [:min-players parse-int]
30 | :maxplayers [:max-players parse-int]
31 | :playingtime [:playing-time parse-int]
32 | :age [:min-player-age parse-int]
33 | :description [:description str]
34 | :thumbnail [:thumbnail prefix-with-https]
35 | :image [:image prefix-with-https]})
36 |
37 | (defmulti process-bg-content
38 | (fn [_bg element]
39 | (:tag element)))
40 |
41 | (defmethod process-bg-content :default
42 | [bg element]
43 | (let [tag (:tag element)
44 | [output-key parser] (get boardgame-content-renames tag)]
45 | (if output-key
46 | (assoc bg output-key (-> element :content first parser))
47 | bg)))
48 |
49 | (defmethod process-bg-content :name
50 | [bg element]
51 | ;; non-primary names are usually translations to other languages
52 | (if (-> element :attrs :primary)
53 | ;; TODO: Trim/reformat loose HTML
54 | (assoc bg :name (-> element :content first))
55 | bg))
56 |
57 | (defmethod process-bg-content :boardgamepublisher
58 | [bg element]
59 | (update bg :publisher-ids conj (-> element :attrs :objectid)))
60 |
61 | (defmethod process-bg-content :boardgamedesigner
62 | [bg element]
63 | (update bg :designer-ids conj (-> element :attrs :objectid)))
64 |
65 | (defn ^:private xml->board-game
66 | [element]
67 | (reduce process-bg-content
68 | {:id (-> element :attrs :objectid)
69 | :publisher-ids []
70 | :designer-ids []}
71 | (:content element)))
72 |
73 | (defn ^:private get-xml
74 | [url query-params]
75 | (log/info (str "BGG Query: " url (when query-params
76 | (str " " (pr-str query-params)))))
77 | (->> (client/get url
78 | {:accept "text/xml"
79 | :query-params query-params
80 | :throw-exceptions false})
81 | :body
82 | StringReader.
83 | xml/parse))
84 |
85 | (defn get-board-game
86 | [cache id]
87 | (or (cache/resolve-by-id cache :games id)
88 | (let [game (->> (get-xml (str base-url "/boardgame/" id) nil)
89 | (expect-tag :boardgames)
90 | :content
91 | first
92 | xml->board-game)]
93 | (cache/fill cache :games [game])
94 | game)))
95 |
96 | (defn search
97 | "Performs a search of matching games by name."
98 | [cache text]
99 | (let [game-ids (->> (get-xml (str base-url "/search") {:search text})
100 | (expect-tag :boardgames)
101 | :content
102 | (map (comp :objectid :attrs)))
103 | [cached more-ids] (cache/resolve-by-ids cache :games game-ids)
104 | games (when more-ids
105 | (->> (get-xml (str base-url "/boardgame/" (str/join "," more-ids)) nil)
106 | (expect-tag :boardgames)
107 | :content
108 | (map xml->board-game)))]
109 | (cache/fill cache :games games)
110 | (concat cached games)))
111 |
112 | (defn ^:private xml->map
113 | [element keys]
114 | (-> (into {}
115 | (map #(vector (:tag %)
116 | ;; Assumes :content is a single element, a string containing the value.
117 | (-> % :content first)))
118 | (:content element))
119 | (select-keys keys)))
120 |
121 | (defn ^:private xml->company
122 | [id company-element]
123 | (expect-tag :company company-element)
124 | (-> company-element
125 | (xml->map [:name :description])
126 | (assoc :id (str id))))
127 |
128 | (defn ^:private xml->person
129 | [id person-element]
130 | (expect-tag :person person-element)
131 | (-> person-element
132 | (xml->map [:name :description])
133 | (assoc :id (str id))))
134 |
135 | (defn publishers
136 | [cache ids]
137 | (let [[cached more-ids] (cache/resolve-by-ids cache :publishers ids)
138 | publishers (when more-ids
139 | (->> (get-xml (str base-url "/boardgamepublisher/" (str/join "," more-ids)) nil)
140 | (expect-tag :companies)
141 | :content
142 | ;; BGG doesn't return the company id in the XML, so we have to
143 | ;; hope it all lines up. Demoware.
144 | (map xml->company more-ids)))]
145 | (cache/fill cache :publishers publishers)
146 | (concat cached publishers)))
147 |
148 | (defn designers
149 | [cache ids]
150 | (let [[cached more-ids] (cache/resolve-by-ids cache :designers ids)
151 | designers (when more-ids
152 | (->> (get-xml (str base-url "/boardgamedesigner/" (str/join "," more-ids)) nil)
153 | (expect-tag :people)
154 | :content
155 | (map xml->person more-ids)))]
156 | (cache/fill cache :designers designers)
157 | (concat cached designers)))
158 |
--------------------------------------------------------------------------------
/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.
202 |
--------------------------------------------------------------------------------
/resources/graphiql/graphiql.css:
--------------------------------------------------------------------------------
1 | .graphiql-container {
2 | color: #141823;
3 | display: -webkit-box;
4 | display: -ms-flexbox;
5 | display: flex;
6 | -webkit-box-orient: horizontal;
7 | -webkit-box-direction: normal;
8 | -ms-flex-direction: row;
9 | flex-direction: row;
10 | font-family:
11 | system,
12 | -apple-system,
13 | 'San Francisco',
14 | '.SFNSDisplay-Regular',
15 | 'Segoe UI',
16 | Segoe,
17 | 'Segoe WP',
18 | 'Helvetica Neue',
19 | helvetica,
20 | 'Lucida Grande',
21 | arial,
22 | sans-serif;
23 | font-size: 14px;
24 | height: 100%;
25 | margin: 0;
26 | overflow: hidden;
27 | width: 100%;
28 | }
29 |
30 | .graphiql-container .editorWrap {
31 | display: -webkit-box;
32 | display: -ms-flexbox;
33 | display: flex;
34 | -webkit-box-orient: vertical;
35 | -webkit-box-direction: normal;
36 | -ms-flex-direction: column;
37 | flex-direction: column;
38 | -webkit-box-flex: 1;
39 | -ms-flex: 1;
40 | flex: 1;
41 | }
42 |
43 | .graphiql-container .title {
44 | font-size: 18px;
45 | }
46 |
47 | .graphiql-container .title em {
48 | font-family: georgia;
49 | font-size: 19px;
50 | }
51 |
52 | .graphiql-container .topBarWrap {
53 | display: -webkit-box;
54 | display: -ms-flexbox;
55 | display: flex;
56 | -webkit-box-orient: horizontal;
57 | -webkit-box-direction: normal;
58 | -ms-flex-direction: row;
59 | flex-direction: row;
60 | }
61 |
62 | .graphiql-container .topBar {
63 | -webkit-box-align: center;
64 | -ms-flex-align: center;
65 | align-items: center;
66 | background: -webkit-linear-gradient(#f7f7f7, #e2e2e2);
67 | background: linear-gradient(#f7f7f7, #e2e2e2);
68 | border-bottom: 1px solid #d0d0d0;
69 | cursor: default;
70 | display: -webkit-box;
71 | display: -ms-flexbox;
72 | display: flex;
73 | -webkit-box-orient: horizontal;
74 | -webkit-box-direction: normal;
75 | -ms-flex-direction: row;
76 | flex-direction: row;
77 | -webkit-box-flex: 1;
78 | -ms-flex: 1;
79 | flex: 1;
80 | height: 34px;
81 | padding: 7px 14px 6px;
82 | -webkit-user-select: none;
83 | -moz-user-select: none;
84 | -ms-user-select: none;
85 | user-select: none;
86 | }
87 |
88 | .graphiql-container .toolbar {
89 | overflow-x: auto;
90 | }
91 |
92 | .graphiql-container .docExplorerShow {
93 | background: -webkit-linear-gradient(#f7f7f7, #e2e2e2);
94 | background: linear-gradient(#f7f7f7, #e2e2e2);
95 | border-bottom: 1px solid #d0d0d0;
96 | border-left: 1px solid rgba(0, 0, 0, 0.2);
97 | border-right: none;
98 | border-top: none;
99 | color: #3B5998;
100 | cursor: pointer;
101 | font-size: 14px;
102 | margin: 0;
103 | outline: 0;
104 | padding: 2px 20px 0 18px;
105 | }
106 |
107 | .graphiql-container .docExplorerShow:before {
108 | border-left: 2px solid #3B5998;
109 | border-top: 2px solid #3B5998;
110 | content: '';
111 | display: inline-block;
112 | height: 9px;
113 | margin: 0 3px -1px 0;
114 | position: relative;
115 | -webkit-transform: rotate(-45deg);
116 | transform: rotate(-45deg);
117 | width: 9px;
118 | }
119 |
120 | .graphiql-container .editorBar {
121 | display: -webkit-box;
122 | display: -ms-flexbox;
123 | display: flex;
124 | -webkit-box-orient: horizontal;
125 | -webkit-box-direction: normal;
126 | -ms-flex-direction: row;
127 | flex-direction: row;
128 | -webkit-box-flex: 1;
129 | -ms-flex: 1;
130 | flex: 1;
131 | }
132 |
133 | .graphiql-container .queryWrap {
134 | display: -webkit-box;
135 | display: -ms-flexbox;
136 | display: flex;
137 | -webkit-box-orient: vertical;
138 | -webkit-box-direction: normal;
139 | -ms-flex-direction: column;
140 | flex-direction: column;
141 | -webkit-box-flex: 1;
142 | -ms-flex: 1;
143 | flex: 1;
144 | }
145 |
146 | .graphiql-container .resultWrap {
147 | border-left: solid 1px #e0e0e0;
148 | display: -webkit-box;
149 | display: -ms-flexbox;
150 | display: flex;
151 | -webkit-box-orient: vertical;
152 | -webkit-box-direction: normal;
153 | -ms-flex-direction: column;
154 | flex-direction: column;
155 | -webkit-box-flex: 1;
156 | -ms-flex: 1;
157 | flex: 1;
158 | position: relative;
159 | }
160 |
161 | .graphiql-container .docExplorerWrap {
162 | background: white;
163 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.15);
164 | position: relative;
165 | z-index: 3;
166 | }
167 |
168 | .graphiql-container .docExplorerResizer {
169 | cursor: col-resize;
170 | height: 100%;
171 | left: -5px;
172 | position: absolute;
173 | top: 0;
174 | width: 10px;
175 | z-index: 10;
176 | }
177 |
178 | .graphiql-container .docExplorerHide {
179 | cursor: pointer;
180 | font-size: 18px;
181 | margin: -7px -8px -6px 0;
182 | padding: 18px 16px 15px 12px;
183 | }
184 |
185 | .graphiql-container .query-editor {
186 | -webkit-box-flex: 1;
187 | -ms-flex: 1;
188 | flex: 1;
189 | position: relative;
190 | }
191 |
192 | .graphiql-container .variable-editor {
193 | display: -webkit-box;
194 | display: -ms-flexbox;
195 | display: flex;
196 | -webkit-box-orient: vertical;
197 | -webkit-box-direction: normal;
198 | -ms-flex-direction: column;
199 | flex-direction: column;
200 | height: 29px;
201 | position: relative;
202 | }
203 |
204 | .graphiql-container .variable-editor-title {
205 | background: #eeeeee;
206 | border-bottom: 1px solid #d6d6d6;
207 | border-top: 1px solid #e0e0e0;
208 | color: #777;
209 | font-variant: small-caps;
210 | font-weight: bold;
211 | letter-spacing: 1px;
212 | line-height: 14px;
213 | padding: 6px 0 8px 43px;
214 | text-transform: lowercase;
215 | -webkit-user-select: none;
216 | -moz-user-select: none;
217 | -ms-user-select: none;
218 | user-select: none;
219 | }
220 |
221 | .graphiql-container .codemirrorWrap {
222 | -webkit-box-flex: 1;
223 | -ms-flex: 1;
224 | flex: 1;
225 | height: 100%;
226 | position: relative;
227 | }
228 |
229 | .graphiql-container .result-window {
230 | -webkit-box-flex: 1;
231 | -ms-flex: 1;
232 | flex: 1;
233 | height: 100%;
234 | position: relative;
235 | }
236 |
237 | .graphiql-container .footer {
238 | background: #f6f7f8;
239 | border-left: 1px solid #e0e0e0;
240 | border-top: 1px solid #e0e0e0;
241 | margin-left: 12px;
242 | position: relative;
243 | }
244 |
245 | .graphiql-container .footer:before {
246 | background: #eeeeee;
247 | bottom: 0;
248 | content: " ";
249 | left: -13px;
250 | position: absolute;
251 | top: -1px;
252 | width: 12px;
253 | }
254 |
255 | .graphiql-container .result-window .CodeMirror {
256 | background: #f6f7f8;
257 | }
258 |
259 | .graphiql-container .result-window .CodeMirror-gutters {
260 | background-color: #eeeeee;
261 | border-color: #e0e0e0;
262 | cursor: col-resize;
263 | }
264 |
265 | .graphiql-container .result-window .CodeMirror-foldgutter,
266 | .graphiql-container .result-window .CodeMirror-foldgutter-open:after,
267 | .graphiql-container .result-window .CodeMirror-foldgutter-folded:after {
268 | padding-left: 3px;
269 | }
270 |
271 | .graphiql-container .toolbar-button {
272 | background: #fdfdfd;
273 | background: -webkit-linear-gradient(#fbfbfb, #f8f8f8);
274 | background: linear-gradient(#fbfbfb, #f8f8f8);
275 | border-color: #d3d3d3 #d0d0d0 #bababa;
276 | border-radius: 4px;
277 | border-style: solid;
278 | border-width: 0.5px;
279 | box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.13), inset 0 1px #fff;
280 | color: #444;
281 | cursor: pointer;
282 | display: inline-block;
283 | margin: 0 5px 0;
284 | padding: 2px 8px 4px;
285 | text-decoration: none;
286 | }
287 |
288 | .graphiql-container .toolbar-button:active {
289 | background: -webkit-linear-gradient(#ececec, #d8d8d8);
290 | background: linear-gradient(#ececec, #d8d8d8);
291 | border-color: #cacaca #c9c9c9 #b0b0b0;
292 | box-shadow:
293 | 0 1px 0 #fff,
294 | inset 0 1px rgba(255, 255, 255, 0.2),
295 | inset 0 1px 1px rgba(0, 0, 0, 0.08);
296 | }
297 |
298 | .graphiql-container .toolbar-button.error {
299 | background: -webkit-linear-gradient(#fdf3f3, #e6d6d7);
300 | background: linear-gradient(#fdf3f3, #e6d6d7);
301 | color: #b00;
302 | }
303 |
304 | .graphiql-container .execute-button-wrap {
305 | height: 34px;
306 | margin: 0 14px 0 28px;
307 | position: relative;
308 | }
309 |
310 | .graphiql-container .execute-button {
311 | background: -webkit-linear-gradient(#fdfdfd, #d2d3d6);
312 | background: linear-gradient(#fdfdfd, #d2d3d6);
313 | border-radius: 17px;
314 | border: 1px solid rgba(0,0,0,0.25);
315 | box-shadow: 0 1px 0 #fff;
316 | cursor: pointer;
317 | fill: #444;
318 | height: 34px;
319 | margin: 0;
320 | padding: 0;
321 | width: 34px;
322 | }
323 |
324 | .graphiql-container .execute-button svg {
325 | pointer-events: none;
326 | }
327 |
328 | .graphiql-container .execute-button:active {
329 | background: -webkit-linear-gradient(#e6e6e6, #c0c0c0);
330 | background: linear-gradient(#e6e6e6, #c0c0c0);
331 | box-shadow:
332 | 0 1px 0 #fff,
333 | inset 0 0 2px rgba(0, 0, 0, 0.3),
334 | inset 0 0 6px rgba(0, 0, 0, 0.2);
335 | }
336 |
337 | .graphiql-container .execute-button:focus {
338 | outline: 0;
339 | }
340 |
341 | .graphiql-container .execute-options {
342 | background: #fff;
343 | box-shadow:
344 | 0 0 0 1px rgba(0,0,0,0.1),
345 | 0 2px 4px rgba(0,0,0,0.25);
346 | left: -1px;
347 | margin: 0;
348 | padding: 8px 0;
349 | position: absolute;
350 | top: 37px;
351 | z-index: 100;
352 | }
353 |
354 | .graphiql-container .execute-options li {
355 | cursor: pointer;
356 | list-style: none;
357 | min-width: 100px;
358 | padding: 2px 30px 4px 10px;
359 | }
360 |
361 | .graphiql-container .execute-options li.selected {
362 | background: #e10098;
363 | color: white;
364 | }
365 |
366 | .graphiql-container .CodeMirror-scroll {
367 | overflow-scrolling: touch;
368 | }
369 |
370 | .graphiql-container .CodeMirror {
371 | color: #141823;
372 | font-family:
373 | 'Consolas',
374 | 'Inconsolata',
375 | 'Droid Sans Mono',
376 | 'Monaco',
377 | monospace;
378 | font-size: 13px;
379 | height: 100%;
380 | left: 0;
381 | position: absolute;
382 | top: 0;
383 | width: 100%;
384 | }
385 |
386 | .graphiql-container .CodeMirror-lines {
387 | padding: 20px 0;
388 | }
389 |
390 | .CodeMirror-hint-information .content {
391 | box-orient: vertical;
392 | color: #141823;
393 | display: -webkit-box;
394 | display: -ms-flexbox;
395 | display: flex;
396 | font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif;
397 | font-size: 13px;
398 | line-clamp: 3;
399 | line-height: 16px;
400 | max-height: 48px;
401 | overflow: hidden;
402 | text-overflow: -o-ellipsis-lastline;
403 | }
404 |
405 | .CodeMirror-hint-information .content p:first-child {
406 | margin-top: 0;
407 | }
408 |
409 | .CodeMirror-hint-information .content p:last-child {
410 | margin-bottom: 0;
411 | }
412 |
413 | .CodeMirror-hint-information .infoType {
414 | color: #30a;
415 | cursor: pointer;
416 | display: inline;
417 | margin-right: 0.5em;
418 | }
419 |
420 | .autoInsertedLeaf.cm-property {
421 | -webkit-animation-duration: 6s;
422 | animation-duration: 6s;
423 | -webkit-animation-name: insertionFade;
424 | animation-name: insertionFade;
425 | border-bottom: 2px solid rgba(255, 255, 255, 0);
426 | border-radius: 2px;
427 | margin: -2px -4px -1px;
428 | padding: 2px 4px 1px;
429 | }
430 |
431 | @-webkit-keyframes insertionFade {
432 | from, to {
433 | background: rgba(255, 255, 255, 0);
434 | border-color: rgba(255, 255, 255, 0);
435 | }
436 |
437 | 15%, 85% {
438 | background: #fbffc9;
439 | border-color: #f0f3c0;
440 | }
441 | }
442 |
443 | @keyframes insertionFade {
444 | from, to {
445 | background: rgba(255, 255, 255, 0);
446 | border-color: rgba(255, 255, 255, 0);
447 | }
448 |
449 | 15%, 85% {
450 | background: #fbffc9;
451 | border-color: #f0f3c0;
452 | }
453 | }
454 |
455 | div.CodeMirror-lint-tooltip {
456 | background-color: white;
457 | border-radius: 2px;
458 | border: 0;
459 | color: #141823;
460 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
461 | font-family:
462 | system,
463 | -apple-system,
464 | 'San Francisco',
465 | '.SFNSDisplay-Regular',
466 | 'Segoe UI',
467 | Segoe,
468 | 'Segoe WP',
469 | 'Helvetica Neue',
470 | helvetica,
471 | 'Lucida Grande',
472 | arial,
473 | sans-serif;
474 | font-size: 13px;
475 | line-height: 16px;
476 | opacity: 0;
477 | padding: 6px 10px;
478 | -webkit-transition: opacity 0.15s;
479 | transition: opacity 0.15s;
480 | }
481 |
482 | div.CodeMirror-lint-message-error, div.CodeMirror-lint-message-warning {
483 | padding-left: 23px;
484 | }
485 |
486 | /* COLORS */
487 |
488 | .graphiql-container .CodeMirror-foldmarker {
489 | border-radius: 4px;
490 | background: #08f;
491 | background: -webkit-linear-gradient(#43A8FF, #0F83E8);
492 | background: linear-gradient(#43A8FF, #0F83E8);
493 | box-shadow:
494 | 0 1px 1px rgba(0, 0, 0, 0.2),
495 | inset 0 0 0 1px rgba(0, 0, 0, 0.1);
496 | color: white;
497 | font-family: arial;
498 | font-size: 12px;
499 | line-height: 0;
500 | margin: 0 3px;
501 | padding: 0px 4px 1px;
502 | text-shadow: 0 -1px rgba(0, 0, 0, 0.1);
503 | }
504 |
505 | .graphiql-container div.CodeMirror span.CodeMirror-matchingbracket {
506 | color: #555;
507 | text-decoration: underline;
508 | }
509 |
510 | .graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket {
511 | color: #f00;
512 | }
513 |
514 | /* Comment */
515 | .cm-comment {
516 | color: #999;
517 | }
518 |
519 | /* Punctuation */
520 | .cm-punctuation {
521 | color: #555;
522 | }
523 |
524 | /* Keyword */
525 | .cm-keyword {
526 | color: #B11A04;
527 | }
528 |
529 | /* OperationName, FragmentName */
530 | .cm-def {
531 | color: #D2054E;
532 | }
533 |
534 | /* FieldName */
535 | .cm-property {
536 | color: #1F61A0;
537 | }
538 |
539 | /* FieldAlias */
540 | .cm-qualifier {
541 | color: #1C92A9;
542 | }
543 |
544 | /* ArgumentName and ObjectFieldName */
545 | .cm-attribute {
546 | color: #8B2BB9;
547 | }
548 |
549 | /* Number */
550 | .cm-number {
551 | color: #2882F9;
552 | }
553 |
554 | /* String */
555 | .cm-string {
556 | color: #D64292;
557 | }
558 |
559 | /* Boolean */
560 | .cm-builtin {
561 | color: #D47509;
562 | }
563 |
564 | /* EnumValue */
565 | .cm-string-2 {
566 | color: #0B7FC7;
567 | }
568 |
569 | /* Variable */
570 | .cm-variable {
571 | color: #397D13;
572 | }
573 |
574 | /* Directive */
575 | .cm-meta {
576 | color: #B33086;
577 | }
578 |
579 | /* Type */
580 | .cm-atom {
581 | color: #CA9800;
582 | }
583 | /* BASICS */
584 |
585 | .CodeMirror {
586 | /* Set height, width, borders, and global font properties here */
587 | color: black;
588 | font-family: monospace;
589 | height: 300px;
590 | }
591 |
592 | /* PADDING */
593 |
594 | .CodeMirror-lines {
595 | padding: 4px 0; /* Vertical padding around content */
596 | }
597 | .CodeMirror pre {
598 | padding: 0 4px; /* Horizontal padding of content */
599 | }
600 |
601 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
602 | background-color: white; /* The little square between H and V scrollbars */
603 | }
604 |
605 | /* GUTTER */
606 |
607 | .CodeMirror-gutters {
608 | border-right: 1px solid #ddd;
609 | background-color: #f7f7f7;
610 | white-space: nowrap;
611 | }
612 | .CodeMirror-linenumbers {}
613 | .CodeMirror-linenumber {
614 | color: #999;
615 | min-width: 20px;
616 | padding: 0 3px 0 5px;
617 | text-align: right;
618 | white-space: nowrap;
619 | }
620 |
621 | .CodeMirror-guttermarker { color: black; }
622 | .CodeMirror-guttermarker-subtle { color: #999; }
623 |
624 | /* CURSOR */
625 |
626 | .CodeMirror div.CodeMirror-cursor {
627 | border-left: 1px solid black;
628 | }
629 | /* Shown when moving in bi-directional text */
630 | .CodeMirror div.CodeMirror-secondarycursor {
631 | border-left: 1px solid silver;
632 | }
633 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
634 | background: #7e7;
635 | border: 0;
636 | width: auto;
637 | }
638 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
639 | z-index: 1;
640 | }
641 |
642 | .cm-animate-fat-cursor {
643 | -webkit-animation: blink 1.06s steps(1) infinite;
644 | animation: blink 1.06s steps(1) infinite;
645 | border: 0;
646 | width: auto;
647 | }
648 | @-webkit-keyframes blink {
649 | 0% { background: #7e7; }
650 | 50% { background: none; }
651 | 100% { background: #7e7; }
652 | }
653 | @keyframes blink {
654 | 0% { background: #7e7; }
655 | 50% { background: none; }
656 | 100% { background: #7e7; }
657 | }
658 |
659 | /* Can style cursor different in overwrite (non-insert) mode */
660 | div.CodeMirror-overwrite div.CodeMirror-cursor {}
661 |
662 | .cm-tab { display: inline-block; text-decoration: inherit; }
663 |
664 | .CodeMirror-ruler {
665 | border-left: 1px solid #ccc;
666 | position: absolute;
667 | }
668 |
669 | /* DEFAULT THEME */
670 |
671 | .cm-s-default .cm-keyword {color: #708;}
672 | .cm-s-default .cm-atom {color: #219;}
673 | .cm-s-default .cm-number {color: #164;}
674 | .cm-s-default .cm-def {color: #00f;}
675 | .cm-s-default .cm-variable,
676 | .cm-s-default .cm-punctuation,
677 | .cm-s-default .cm-property,
678 | .cm-s-default .cm-operator {}
679 | .cm-s-default .cm-variable-2 {color: #05a;}
680 | .cm-s-default .cm-variable-3 {color: #085;}
681 | .cm-s-default .cm-comment {color: #a50;}
682 | .cm-s-default .cm-string {color: #a11;}
683 | .cm-s-default .cm-string-2 {color: #f50;}
684 | .cm-s-default .cm-meta {color: #555;}
685 | .cm-s-default .cm-qualifier {color: #555;}
686 | .cm-s-default .cm-builtin {color: #30a;}
687 | .cm-s-default .cm-bracket {color: #997;}
688 | .cm-s-default .cm-tag {color: #170;}
689 | .cm-s-default .cm-attribute {color: #00c;}
690 | .cm-s-default .cm-header {color: blue;}
691 | .cm-s-default .cm-quote {color: #090;}
692 | .cm-s-default .cm-hr {color: #999;}
693 | .cm-s-default .cm-link {color: #00c;}
694 |
695 | .cm-negative {color: #d44;}
696 | .cm-positive {color: #292;}
697 | .cm-header, .cm-strong {font-weight: bold;}
698 | .cm-em {font-style: italic;}
699 | .cm-link {text-decoration: underline;}
700 | .cm-strikethrough {text-decoration: line-through;}
701 |
702 | .cm-s-default .cm-error {color: #f00;}
703 | .cm-invalidchar {color: #f00;}
704 |
705 | .CodeMirror-composing { border-bottom: 2px solid; }
706 |
707 | /* Default styles for common addons */
708 |
709 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
710 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
711 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
712 | .CodeMirror-activeline-background {background: #e8f2ff;}
713 |
714 | /* STOP */
715 |
716 | /* The rest of this file contains styles related to the mechanics of
717 | the editor. You probably shouldn't touch them. */
718 |
719 | .CodeMirror {
720 | background: white;
721 | overflow: hidden;
722 | position: relative;
723 | }
724 |
725 | .CodeMirror-scroll {
726 | height: 100%;
727 | /* 30px is the magic margin used to hide the element's real scrollbars */
728 | /* See overflow: hidden in .CodeMirror */
729 | margin-bottom: -30px; margin-right: -30px;
730 | outline: none; /* Prevent dragging from highlighting the element */
731 | overflow: scroll !important; /* Things will break if this is overridden */
732 | padding-bottom: 30px;
733 | position: relative;
734 | }
735 | .CodeMirror-sizer {
736 | border-right: 30px solid transparent;
737 | position: relative;
738 | }
739 |
740 | /* The fake, visible scrollbars. Used to force redraw during scrolling
741 | before actual scrolling happens, thus preventing shaking and
742 | flickering artifacts. */
743 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
744 | display: none;
745 | position: absolute;
746 | z-index: 6;
747 | }
748 | .CodeMirror-vscrollbar {
749 | overflow-x: hidden;
750 | overflow-y: scroll;
751 | right: 0; top: 0;
752 | }
753 | .CodeMirror-hscrollbar {
754 | bottom: 0; left: 0;
755 | overflow-x: scroll;
756 | overflow-y: hidden;
757 | }
758 | .CodeMirror-scrollbar-filler {
759 | right: 0; bottom: 0;
760 | }
761 | .CodeMirror-gutter-filler {
762 | left: 0; bottom: 0;
763 | }
764 |
765 | .CodeMirror-gutters {
766 | min-height: 100%;
767 | position: absolute; left: 0; top: 0;
768 | z-index: 3;
769 | }
770 | .CodeMirror-gutter {
771 | display: inline-block;
772 | height: 100%;
773 | margin-bottom: -30px;
774 | vertical-align: top;
775 | white-space: normal;
776 | /* Hack to make IE7 behave */
777 | *zoom:1;
778 | *display:inline;
779 | }
780 | .CodeMirror-gutter-wrapper {
781 | background: none !important;
782 | border: none !important;
783 | position: absolute;
784 | z-index: 4;
785 | }
786 | .CodeMirror-gutter-background {
787 | position: absolute;
788 | top: 0; bottom: 0;
789 | z-index: 4;
790 | }
791 | .CodeMirror-gutter-elt {
792 | cursor: default;
793 | position: absolute;
794 | z-index: 4;
795 | }
796 | .CodeMirror-gutter-wrapper {
797 | -webkit-user-select: none;
798 | -moz-user-select: none;
799 | -ms-user-select: none;
800 | user-select: none;
801 | }
802 |
803 | .CodeMirror-lines {
804 | cursor: text;
805 | min-height: 1px; /* prevents collapsing before first draw */
806 | }
807 | .CodeMirror pre {
808 | -webkit-tap-highlight-color: transparent;
809 | /* Reset some styles that the rest of the page might have set */
810 | background: transparent;
811 | border-radius: 0;
812 | border-width: 0;
813 | color: inherit;
814 | font-family: inherit;
815 | font-size: inherit;
816 | -webkit-font-variant-ligatures: none;
817 | font-variant-ligatures: none;
818 | line-height: inherit;
819 | margin: 0;
820 | overflow: visible;
821 | position: relative;
822 | white-space: pre;
823 | word-wrap: normal;
824 | z-index: 2;
825 | }
826 | .CodeMirror-wrap pre {
827 | word-wrap: break-word;
828 | white-space: pre-wrap;
829 | word-break: normal;
830 | }
831 |
832 | .CodeMirror-linebackground {
833 | position: absolute;
834 | left: 0; right: 0; top: 0; bottom: 0;
835 | z-index: 0;
836 | }
837 |
838 | .CodeMirror-linewidget {
839 | overflow: auto;
840 | position: relative;
841 | z-index: 2;
842 | }
843 |
844 | .CodeMirror-widget {}
845 |
846 | .CodeMirror-code {
847 | outline: none;
848 | }
849 |
850 | /* Force content-box sizing for the elements where we expect it */
851 | .CodeMirror-scroll,
852 | .CodeMirror-sizer,
853 | .CodeMirror-gutter,
854 | .CodeMirror-gutters,
855 | .CodeMirror-linenumber {
856 | box-sizing: content-box;
857 | }
858 |
859 | .CodeMirror-measure {
860 | height: 0;
861 | overflow: hidden;
862 | position: absolute;
863 | visibility: hidden;
864 | width: 100%;
865 | }
866 |
867 | .CodeMirror-cursor { position: absolute; }
868 | .CodeMirror-measure pre { position: static; }
869 |
870 | div.CodeMirror-cursors {
871 | position: relative;
872 | visibility: hidden;
873 | z-index: 3;
874 | }
875 | div.CodeMirror-dragcursors {
876 | visibility: visible;
877 | }
878 |
879 | .CodeMirror-focused div.CodeMirror-cursors {
880 | visibility: visible;
881 | }
882 |
883 | .CodeMirror-selected { background: #d9d9d9; }
884 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
885 | .CodeMirror-crosshair { cursor: crosshair; }
886 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
887 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
888 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
889 |
890 | .cm-searching {
891 | background: #ffa;
892 | background: rgba(255, 255, 0, .4);
893 | }
894 |
895 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
896 | .CodeMirror span { *vertical-align: text-bottom; }
897 |
898 | /* Used to force a border model for a node */
899 | .cm-force-border { padding-right: .1px; }
900 |
901 | @media print {
902 | /* Hide the cursor when printing */
903 | .CodeMirror div.CodeMirror-cursors {
904 | visibility: hidden;
905 | }
906 | }
907 |
908 | /* See issue #2901 */
909 | .cm-tab-wrap-hack:after { content: ''; }
910 |
911 | /* Help users use markselection to safely style text background */
912 | span.CodeMirror-selectedtext { background: none; }
913 |
914 | .CodeMirror-dialog {
915 | background: inherit;
916 | color: inherit;
917 | left: 0; right: 0;
918 | overflow: hidden;
919 | padding: .1em .8em;
920 | position: absolute;
921 | z-index: 15;
922 | }
923 |
924 | .CodeMirror-dialog-top {
925 | border-bottom: 1px solid #eee;
926 | top: 0;
927 | }
928 |
929 | .CodeMirror-dialog-bottom {
930 | border-top: 1px solid #eee;
931 | bottom: 0;
932 | }
933 |
934 | .CodeMirror-dialog input {
935 | background: transparent;
936 | border: 1px solid #d3d6db;
937 | color: inherit;
938 | font-family: monospace;
939 | outline: none;
940 | width: 20em;
941 | }
942 |
943 | .CodeMirror-dialog button {
944 | font-size: 70%;
945 | }
946 | .graphiql-container .doc-explorer {
947 | background: white;
948 | }
949 |
950 | .graphiql-container .doc-explorer-title-bar {
951 | cursor: default;
952 | display: -webkit-box;
953 | display: -ms-flexbox;
954 | display: flex;
955 | height: 34px;
956 | line-height: 14px;
957 | padding: 8px 8px 5px;
958 | position: relative;
959 | -webkit-user-select: none;
960 | -moz-user-select: none;
961 | -ms-user-select: none;
962 | user-select: none;
963 | }
964 |
965 | .graphiql-container .doc-explorer-title {
966 | -webkit-box-flex: 1;
967 | -ms-flex: 1;
968 | flex: 1;
969 | font-weight: bold;
970 | overflow-x: hidden;
971 | padding: 10px 0 10px 10px;
972 | text-align: center;
973 | text-overflow: ellipsis;
974 | white-space: nowrap;
975 | }
976 |
977 | .graphiql-container .doc-explorer-back {
978 | color: #3B5998;
979 | cursor: pointer;
980 | margin: -7px 0 -6px -8px;
981 | overflow-x: hidden;
982 | padding: 17px 12px 16px 16px;
983 | text-overflow: ellipsis;
984 | white-space: nowrap;
985 | }
986 |
987 | .doc-explorer-narrow .doc-explorer-back {
988 | width: 0;
989 | }
990 |
991 | .graphiql-container .doc-explorer-back:before {
992 | border-left: 2px solid #3B5998;
993 | border-top: 2px solid #3B5998;
994 | content: '';
995 | display: inline-block;
996 | height: 9px;
997 | margin: 0 3px -1px 0;
998 | position: relative;
999 | -webkit-transform: rotate(-45deg);
1000 | transform: rotate(-45deg);
1001 | width: 9px;
1002 | }
1003 |
1004 | .graphiql-container .doc-explorer-rhs {
1005 | position: relative;
1006 | }
1007 |
1008 | .graphiql-container .doc-explorer-contents {
1009 | background-color: #ffffff;
1010 | border-top: 1px solid #d6d6d6;
1011 | bottom: 0;
1012 | left: 0;
1013 | min-width: 300px;
1014 | overflow-y: auto;
1015 | padding: 20px 15px;
1016 | position: absolute;
1017 | right: 0;
1018 | top: 47px;
1019 | }
1020 |
1021 | .graphiql-container .doc-type-description p:first-child ,
1022 | .graphiql-container .doc-type-description blockquote:first-child {
1023 | margin-top: 0;
1024 | }
1025 |
1026 | .graphiql-container .doc-explorer-contents a {
1027 | cursor: pointer;
1028 | text-decoration: none;
1029 | }
1030 |
1031 | .graphiql-container .doc-explorer-contents a:hover {
1032 | text-decoration: underline;
1033 | }
1034 |
1035 | .graphiql-container .doc-value-description {
1036 | padding: 4px 0 8px 12px;
1037 | }
1038 |
1039 | .graphiql-container .doc-category {
1040 | margin: 20px 0;
1041 | }
1042 |
1043 | .graphiql-container .doc-category-title {
1044 | border-bottom: 1px solid #e0e0e0;
1045 | color: #777;
1046 | cursor: default;
1047 | font-size: 14px;
1048 | font-variant: small-caps;
1049 | font-weight: bold;
1050 | letter-spacing: 1px;
1051 | margin: 0 -15px 10px 0;
1052 | padding: 10px 0;
1053 | -webkit-user-select: none;
1054 | -moz-user-select: none;
1055 | -ms-user-select: none;
1056 | user-select: none;
1057 | }
1058 |
1059 | .graphiql-container .doc-category-item {
1060 | margin: 12px 0;
1061 | color: #555;
1062 | }
1063 |
1064 | .graphiql-container .keyword {
1065 | color: #B11A04;
1066 | }
1067 |
1068 | .graphiql-container .type-name {
1069 | color: #CA9800;
1070 | }
1071 |
1072 | .graphiql-container .field-name {
1073 | color: #1F61A0;
1074 | }
1075 |
1076 | .graphiql-container .value-name {
1077 | color: #0B7FC7;
1078 | }
1079 |
1080 | .graphiql-container .arg-name {
1081 | color: #8B2BB9;
1082 | }
1083 |
1084 | .graphiql-container .arg:after {
1085 | content: ', ';
1086 | }
1087 |
1088 | .graphiql-container .arg:last-child:after {
1089 | content: '';
1090 | }
1091 |
1092 | .graphiql-container .doc-alert-text {
1093 | color: #F00F00;
1094 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
1095 | font-size: 13px;
1096 | }
1097 |
1098 | .graphiql-container .search-box-outer {
1099 | border: 1px solid #d3d6db;
1100 | box-sizing: border-box;
1101 | display: inline-block;
1102 | font-size: 12px;
1103 | height: 24px;
1104 | margin-bottom: 12px;
1105 | padding: 3px 8px 5px;
1106 | vertical-align: middle;
1107 | width: 100%;
1108 | }
1109 |
1110 | .graphiql-container .search-box-input {
1111 | border: 0;
1112 | font-size: 12px;
1113 | margin: 0;
1114 | outline: 0;
1115 | padding: 0;
1116 | width: 100%;
1117 | }
1118 | .CodeMirror-foldmarker {
1119 | color: blue;
1120 | cursor: pointer;
1121 | font-family: arial;
1122 | line-height: .3;
1123 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
1124 | }
1125 | .CodeMirror-foldgutter {
1126 | width: .7em;
1127 | }
1128 | .CodeMirror-foldgutter-open,
1129 | .CodeMirror-foldgutter-folded {
1130 | cursor: pointer;
1131 | }
1132 | .CodeMirror-foldgutter-open:after {
1133 | content: "\25BE";
1134 | }
1135 | .CodeMirror-foldgutter-folded:after {
1136 | content: "\25B8";
1137 | }
1138 | /* The lint marker gutter */
1139 | .CodeMirror-lint-markers {
1140 | width: 16px;
1141 | }
1142 |
1143 | .CodeMirror-lint-tooltip {
1144 | background-color: infobackground;
1145 | border-radius: 4px 4px 4px 4px;
1146 | border: 1px solid black;
1147 | color: infotext;
1148 | font-family: monospace;
1149 | font-size: 10pt;
1150 | max-width: 600px;
1151 | opacity: 0;
1152 | overflow: hidden;
1153 | padding: 2px 5px;
1154 | position: fixed;
1155 | -webkit-transition: opacity .4s;
1156 | transition: opacity .4s;
1157 | white-space: pre-wrap;
1158 | white-space: pre;
1159 | z-index: 100;
1160 | }
1161 |
1162 | .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
1163 | background-position: left bottom;
1164 | background-repeat: repeat-x;
1165 | }
1166 |
1167 | .CodeMirror-lint-mark-error {
1168 | background-image:
1169 | url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
1170 | ;
1171 | }
1172 |
1173 | .CodeMirror-lint-mark-warning {
1174 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
1175 | }
1176 |
1177 | .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
1178 | background-position: center center;
1179 | background-repeat: no-repeat;
1180 | cursor: pointer;
1181 | display: inline-block;
1182 | height: 16px;
1183 | position: relative;
1184 | vertical-align: middle;
1185 | width: 16px;
1186 | }
1187 |
1188 | .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
1189 | background-position: top left;
1190 | background-repeat: no-repeat;
1191 | padding-left: 18px;
1192 | }
1193 |
1194 | .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
1195 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
1196 | }
1197 |
1198 | .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
1199 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
1200 | }
1201 |
1202 | .CodeMirror-lint-marker-multiple {
1203 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
1204 | background-position: right bottom;
1205 | background-repeat: no-repeat;
1206 | width: 100%; height: 100%;
1207 | }
1208 | .graphiql-container .spinner-container {
1209 | height: 36px;
1210 | left: 50%;
1211 | position: absolute;
1212 | top: 50%;
1213 | -webkit-transform: translate(-50%, -50%);
1214 | transform: translate(-50%, -50%);
1215 | width: 36px;
1216 | z-index: 10;
1217 | }
1218 |
1219 | .graphiql-container .spinner {
1220 | -webkit-animation: rotation .6s infinite linear;
1221 | animation: rotation .6s infinite linear;
1222 | border-bottom: 6px solid rgba(150, 150, 150, .15);
1223 | border-left: 6px solid rgba(150, 150, 150, .15);
1224 | border-radius: 100%;
1225 | border-right: 6px solid rgba(150, 150, 150, .15);
1226 | border-top: 6px solid rgba(150, 150, 150, .8);
1227 | display: inline-block;
1228 | height: 24px;
1229 | position: absolute;
1230 | vertical-align: middle;
1231 | width: 24px;
1232 | }
1233 |
1234 | @-webkit-keyframes rotation {
1235 | from { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
1236 | to { -webkit-transform: rotate(359deg); transform: rotate(359deg); }
1237 | }
1238 |
1239 | @keyframes rotation {
1240 | from { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
1241 | to { -webkit-transform: rotate(359deg); transform: rotate(359deg); }
1242 | }
1243 | .CodeMirror-hints {
1244 | background: white;
1245 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1246 | font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
1247 | font-size: 13px;
1248 | list-style: none;
1249 | margin-left: -6px;
1250 | margin: 0;
1251 | max-height: 14.5em;
1252 | overflow-y: auto;
1253 | overflow: hidden;
1254 | padding: 0;
1255 | position: absolute;
1256 | z-index: 10;
1257 | }
1258 |
1259 | .CodeMirror-hints-wrapper {
1260 | background: white;
1261 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
1262 | margin-left: -6px;
1263 | position: absolute;
1264 | z-index: 10;
1265 | }
1266 |
1267 | .CodeMirror-hints-wrapper .CodeMirror-hints {
1268 | box-shadow: none;
1269 | margin-left: 0;
1270 | position: relative;
1271 | z-index: 0;
1272 | }
1273 |
1274 | .CodeMirror-hint {
1275 | border-top: solid 1px #f7f7f7;
1276 | color: #141823;
1277 | cursor: pointer;
1278 | margin: 0;
1279 | max-width: 300px;
1280 | overflow: hidden;
1281 | padding: 2px 6px;
1282 | white-space: pre;
1283 | }
1284 |
1285 | li.CodeMirror-hint-active {
1286 | background-color: #08f;
1287 | border-top-color: white;
1288 | color: white;
1289 | }
1290 |
1291 | .CodeMirror-hint-information {
1292 | border-top: solid 1px #c0c0c0;
1293 | max-width: 300px;
1294 | padding: 4px 6px;
1295 | position: relative;
1296 | z-index: 1;
1297 | }
1298 |
1299 | .CodeMirror-hint-information:first-child {
1300 | border-bottom: solid 1px #c0c0c0;
1301 | border-top: none;
1302 | margin-bottom: -1px;
1303 | }
1304 |
--------------------------------------------------------------------------------