UNPKG

5.86 kBJavaScriptView Raw
1(function () {
2 if (!self.Prism || !self.document || !document.querySelectorAll || ![].filter) return;
3
4 /**
5 * @callback Adapter
6 * @param {any} response
7 * @param {HTMLPreElement} [pre]
8 * @returns {string}
9 */
10
11 /**
12 * The list of adapter which will be used if `data-adapter` is not specified.
13 *
14 * @type {Array.<{adapter: Adapter, name: string}>}
15 */
16 var adapters = [];
17
18 /**
19 * Adds a new function to the list of adapters.
20 *
21 * If the given adapter is already registered or not a function or there is an adapter with the given name already,
22 * nothing will happen.
23 *
24 * @param {Adapter} adapter The adapter to be registered.
25 * @param {string} [name] The name of the adapter. Defaults to the function name of `adapter`.
26 */
27 function registerAdapter(adapter, name) {
28 name = name || adapter.name;
29 if (typeof adapter === "function" && !getAdapter(adapter) && !getAdapter(name)) {
30 adapters.push({ adapter: adapter, name: name });
31 }
32 }
33 /**
34 * Returns the given adapter itself, if registered, or a registered adapter with the given name.
35 *
36 * If no fitting adapter is registered, `null` will be returned.
37 *
38 * @param {string|Function} adapter The adapter itself or the name of an adapter.
39 * @returns {Adapter} A registered adapter or `null`.
40 */
41 function getAdapter(adapter) {
42 if (typeof adapter === "function") {
43 for (var i = 0, item; item = adapters[i++];) {
44 if (item.adapter.valueOf() === adapter.valueOf()) {
45 return item.adapter;
46 }
47 }
48 }
49 else if (typeof adapter === "string") {
50 for (var i = 0, item; item = adapters[i++];) {
51 if (item.name === adapter) {
52 return item.adapter;
53 }
54 }
55 }
56 return null;
57 }
58 /**
59 * Remove the given adapter or the first registered adapter with the given name from the list of
60 * registered adapters.
61 *
62 * @param {string|Function} adapter The adapter itself or the name of an adapter.
63 */
64 function removeAdapter(adapter) {
65 if (typeof adapter === "string") {
66 adapter = getAdapter(adapter);
67 }
68 if (typeof adapter === "function") {
69 var index = adapters.map(function (item) { return item.adapter; }).indexOf(adapter);
70 if (index >= 0) {
71 adapters.splice(index, 1);
72 }
73 }
74 }
75
76 registerAdapter(function github(rsp, el) {
77 if (rsp && rsp.meta && rsp.data) {
78 if (rsp.meta.status && rsp.meta.status >= 400) {
79 return "Error: " + (rsp.data.message || rsp.meta.status);
80 }
81 else if (typeof (rsp.data.content) === "string") {
82 return typeof (atob) === "function"
83 ? atob(rsp.data.content.replace(/\s/g, ""))
84 : "Your browser cannot decode base64";
85 }
86 }
87 return null;
88 }, 'github');
89 registerAdapter(function gist(rsp, el) {
90 if (rsp && rsp.meta && rsp.data && rsp.data.files) {
91 if (rsp.meta.status && rsp.meta.status >= 400) {
92 return "Error: " + (rsp.data.message || rsp.meta.status);
93 }
94
95 var files = rsp.data.files;
96 var filename = el.getAttribute("data-filename");
97 if (filename == null) {
98 // Maybe in the future we can somehow render all files
99 // But the standard <script> include for gists does that nicely already,
100 // so that might be getting beyond the scope of this plugin
101 for (var key in files) {
102 if (files.hasOwnProperty(key)) {
103 filename = key;
104 break;
105 }
106 }
107 }
108
109 if (files[filename] !== undefined) {
110 return files[filename].content;
111 }
112 return "Error: unknown or missing gist file " + filename;
113 }
114 return null;
115 }, 'gist');
116 registerAdapter(function bitbucket(rsp, el) {
117 if (rsp && rsp.node && typeof (rsp.data) === "string") {
118 return rsp.data;
119 }
120 return null;
121 }, 'bitbucket');
122
123 var jsonpcb = 0,
124 loadMsg = "Loading\u2026";
125
126 /**
127 * Highlights all `pre` elements with an `data-jsonp` by requesting the specified JSON and using the specified adapter
128 * or a registered adapter to extract the code to highlight from the response. The highlighted code will be inserted
129 * into the `pre` element.
130 */
131 function highlight() {
132 Array.prototype.slice.call(document.querySelectorAll("pre[data-jsonp]")).forEach(function (pre) {
133 pre.textContent = "";
134
135 var code = document.createElement("code");
136 code.textContent = loadMsg;
137 pre.appendChild(code);
138
139 var adapterName = pre.getAttribute("data-adapter");
140 var adapter = null;
141 if (adapterName) {
142 if (typeof window[adapterName] === "function") {
143 adapter = window[adapterName];
144 }
145 else {
146 code.textContent = "JSONP adapter function '" + adapterName + "' doesn't exist";
147 return;
148 }
149 }
150
151 var cb = "prismjsonp" + jsonpcb++;
152
153 var uri = document.createElement("a");
154 var src = uri.href = pre.getAttribute("data-jsonp");
155 uri.href += (uri.search ? "&" : "?") + (pre.getAttribute("data-callback") || "callback") + "=" + cb;
156
157 var timeout = setTimeout(function () {
158 // we could clean up window[cb], but if the request finally succeeds, keeping it around is a good thing
159 if (code.textContent === loadMsg) {
160 code.textContent = "Timeout loading '" + src + "'";
161 }
162 }, 5000);
163
164 var script = document.createElement("script");
165 script.src = uri.href;
166
167 window[cb] = function (rsp) {
168 document.head.removeChild(script);
169 clearTimeout(timeout);
170 delete window[cb];
171
172 var data = "";
173
174 if (adapter) {
175 data = adapter(rsp, pre);
176 }
177 else {
178 for (var p in adapters) {
179 data = adapters[p].adapter(rsp, pre);
180 if (data !== null) {
181 break;
182 }
183 }
184 }
185
186 if (data === null) {
187 code.textContent = "Cannot parse response (perhaps you need an adapter function?)";
188 }
189 else {
190 code.textContent = data;
191 Prism.highlightElement(code);
192 }
193 };
194
195 document.head.appendChild(script);
196 });
197 }
198
199 Prism.plugins.jsonphighlight = {
200 registerAdapter: registerAdapter,
201 removeAdapter: removeAdapter,
202 highlight: highlight
203 };
204
205 highlight();
206})();