root / drupal7 / sites / all / themes / bootstrap / includes / cdn / JsDelivr.php @ b6b9edaa
1 |
<?php
|
---|---|
2 |
|
3 |
namespace Drupal\bootstrap\Backport\Plugin\Provider; |
4 |
|
5 |
/**
|
6 |
* The "jsdelivr" CDN provider plugin.
|
7 |
*
|
8 |
* Note: this class is a backport from the 8.x-3.x code base.
|
9 |
*
|
10 |
* @see https://drupal-bootstrap.org/api/bootstrap/namespace/Drupal%21bootstrap%21Plugin%21Provider/8
|
11 |
*
|
12 |
* @ingroup plugins_provider
|
13 |
*/
|
14 |
class JsDelivr extends ProviderBase { |
15 |
|
16 |
protected $pluginId = 'jsdelivr'; |
17 |
|
18 |
/**
|
19 |
* The base API URL.
|
20 |
*
|
21 |
* @var string
|
22 |
*/
|
23 |
const BASE_API_URL = 'https://data.jsdelivr.com/v1/package/npm'; |
24 |
|
25 |
/**
|
26 |
* The base CDN URL.
|
27 |
*
|
28 |
* @var string
|
29 |
*/
|
30 |
const BASE_CDN_URL = 'https://cdn.jsdelivr.net/npm'; |
31 |
|
32 |
/**
|
33 |
* A list of latest versions, keyed by NPM package name.
|
34 |
*
|
35 |
* @var string[]
|
36 |
*/
|
37 |
protected $latestVersion = array(); |
38 |
|
39 |
/**
|
40 |
* A list of themes, keyed by NPM package name.
|
41 |
*
|
42 |
* @var array[]
|
43 |
*/
|
44 |
protected $themes = array(); |
45 |
|
46 |
/**
|
47 |
* A list of versions, keyed by NPM package name.
|
48 |
*
|
49 |
* @var array[]
|
50 |
*/
|
51 |
protected $versions = array(); |
52 |
|
53 |
/**
|
54 |
* {@inheritdoc}
|
55 |
*/
|
56 |
public function getDescription() { |
57 |
return t('<p><a href="!jsdelivr" target="_blank">jsDelivr</a> is a free multi-CDN infrastructure that uses <a href="!maxcdn" target="_blank">MaxCDN</a>, <a href="!cloudflare" target="_blank">Cloudflare</a> and many others to combine their powers for the good of the open source community... <a href="!jsdelivr_about" target="_blank">read more</a></p>', array( |
58 |
'!jsdelivr' => 'https://www.jsdelivr.com', |
59 |
'!jsdelivr_about' => 'https://www.jsdelivr.com/about', |
60 |
'!maxcdn' => 'https://www.maxcdn.com', |
61 |
'!cloudflare' => 'https://www.cloudflare.com', |
62 |
)); |
63 |
} |
64 |
|
65 |
/**
|
66 |
* {@inheritdoc}
|
67 |
*/
|
68 |
public function getLabel() { |
69 |
return t('jsDelivr'); |
70 |
} |
71 |
|
72 |
/**
|
73 |
* {@inheritdoc}
|
74 |
*/
|
75 |
protected function discoverCdnAssets($version, $theme = 'bootstrap') { |
76 |
$themes = $this->getCdnThemes($version); |
77 |
return isset($themes[$theme]) ? $themes[$theme] : array(); |
78 |
} |
79 |
|
80 |
/**
|
81 |
* {@inheritdoc}
|
82 |
*/
|
83 |
public function getCdnThemes($version = NULL) { |
84 |
if (!isset($version)) { |
85 |
$version = $this->getCdnVersion(); |
86 |
} |
87 |
if (!isset($this->themes[$version])) { |
88 |
$instance = $this; |
89 |
$this->themes[$version] = $this->cacheGet('themes.' . static::escapeDelimiter($version), array(), function ($themes) use ($version, $instance) { |
90 |
foreach (array('bootstrap', 'bootswatch') as $package) { |
91 |
$mappedVersion = $instance->mapVersion($version, $package); |
92 |
$files = $instance->requestApiV1($package, $mappedVersion); |
93 |
$themes = $instance->parseThemes($files, $package, $mappedVersion, $themes); |
94 |
} |
95 |
return $themes; |
96 |
}); |
97 |
} |
98 |
return $this->themes[$version]; |
99 |
} |
100 |
|
101 |
/**
|
102 |
* {@inheritdoc}
|
103 |
*/
|
104 |
public function getCdnVersions($package = 'bootstrap') { |
105 |
if (!isset($this->versions[$package])) { |
106 |
$instance = $this; |
107 |
$this->versions[$package] = $this->cacheGet("versions.$package", array(), function ($versions) use ($package, $instance) { |
108 |
$json = $instance->requestApiV1($package) + array('versions' => array()); |
109 |
foreach ($json['versions'] as $version) { |
110 |
// Skip irrelevant versions.
|
111 |
if (!preg_match('/^' . substr(BOOTSTRAP_VERSION, 0, 1) . '\.\d+\.\d+$/', $version)) { |
112 |
continue;
|
113 |
} |
114 |
$versions[$version] = $version; |
115 |
} |
116 |
return $versions; |
117 |
}); |
118 |
} |
119 |
return $this->versions[$package]; |
120 |
} |
121 |
|
122 |
/**
|
123 |
* {@inheritdoc}
|
124 |
*/
|
125 |
protected function mapVersion($version, $package = NULL) { |
126 |
// While the Bootswatch project attempts to maintain version parity with
|
127 |
// Bootstrap, it doesn't always happen. This causes issues when the system
|
128 |
// expects a 1:1 version match between Bootstrap and Bootswatch.
|
129 |
// @see https://github.com/thomaspark/bootswatch/issues/892#ref-issue-410070082
|
130 |
if ($package === 'bootswatch') { |
131 |
switch ($version) { |
132 |
// This version is "broken" because of jsDelivr's API limit.
|
133 |
case '3.4.1': |
134 |
$version = '3.4.0'; |
135 |
break;
|
136 |
|
137 |
// This version doesn't exist.
|
138 |
case '3.1.1': |
139 |
$version = '3.2.0'; |
140 |
break;
|
141 |
} |
142 |
} |
143 |
return $version; |
144 |
} |
145 |
|
146 |
/**
|
147 |
* Parses JSON from the API and retrieves valid files.
|
148 |
*
|
149 |
* @param array $json
|
150 |
* The JSON data to parse.
|
151 |
*
|
152 |
* @return array
|
153 |
* An array of files parsed from provided JSON data.
|
154 |
*/
|
155 |
protected function parseFiles(array $json) { |
156 |
// Immediately return if malformed.
|
157 |
if (!isset($json['files']) || !is_array($json['files'])) { |
158 |
return array(); |
159 |
} |
160 |
|
161 |
$files = array(); |
162 |
foreach ($json['files'] as $file) { |
163 |
// Skip old bootswatch file structure.
|
164 |
if (preg_match('`^/2|/bower_components`', $file['name'], $matches)) { |
165 |
continue;
|
166 |
} |
167 |
preg_match('`([^/]*)/bootstrap(-theme)?(\.min)?\.(js|css)$`', $file['name'], $matches); |
168 |
if (!empty($matches[1]) && !empty($matches[4])) { |
169 |
$files[] = $file['name']; |
170 |
} |
171 |
} |
172 |
return $files; |
173 |
} |
174 |
|
175 |
/**
|
176 |
* Extracts assets from files provided by the jsDelivr API.
|
177 |
*
|
178 |
* This will place the raw files into proper "css", "js" and "min" arrays
|
179 |
* (if they exist) and prepends them with a base URL provided.
|
180 |
*
|
181 |
* @param array $files
|
182 |
* An array of files to process.
|
183 |
* @param string $package
|
184 |
* The base URL each one of the $files are relative to, this usually
|
185 |
* should also include the version path prefix as well.
|
186 |
* @param string $version
|
187 |
* A specific version to use.
|
188 |
* @param array $themes
|
189 |
* An existing array of themes. This is primarily used when building a
|
190 |
* complete list of themes.
|
191 |
*
|
192 |
* @return array
|
193 |
* An associative array containing the following keys, if there were
|
194 |
* matching files found:
|
195 |
* - css
|
196 |
* - js
|
197 |
* - min:
|
198 |
* - css
|
199 |
* - js
|
200 |
*/
|
201 |
protected function parseThemes(array $files, $package, $version, array $themes = array()) { |
202 |
$baseUrl = static::BASE_CDN_URL . "/$package@$version"; |
203 |
foreach ($files as $file) { |
204 |
preg_match('`([^/]*)/bootstrap(-theme)?(\.min)?\.(js|css)$`', $file, $matches); |
205 |
if (!empty($matches[1]) && !empty($matches[4])) { |
206 |
$path = $matches[1]; |
207 |
$min = $matches[3]; |
208 |
$filetype = $matches[4]; |
209 |
|
210 |
// Determine the "theme" name.
|
211 |
if ($path === 'css' || $path === 'js') { |
212 |
$theme = 'bootstrap'; |
213 |
$title = (string) t('Bootstrap'); |
214 |
} |
215 |
else {
|
216 |
$theme = $path; |
217 |
$title = ucfirst($path); |
218 |
} |
219 |
if ($matches[2]) { |
220 |
$theme = 'bootstrap_theme'; |
221 |
$title = (string) t('Bootstrap Theme'); |
222 |
} |
223 |
|
224 |
$themes[$theme]['title'] = $title; |
225 |
if ($min) { |
226 |
$themes[$theme]['min'][$filetype][] = "$baseUrl/" . ltrim($file, '/'); |
227 |
} |
228 |
else {
|
229 |
$themes[$theme][$filetype][] = "$baseUrl/" . ltrim($file, '/'); |
230 |
} |
231 |
} |
232 |
} |
233 |
|
234 |
// Post process the themes to fill in any missing assets.
|
235 |
foreach (array_keys($themes) as $theme) { |
236 |
// Some themes do not have a non-minified version, clone them to the
|
237 |
// "normal" css/js arrays to ensure that the theme still loads if
|
238 |
// aggregation (minification) is disabled.
|
239 |
foreach (array('css', 'js') as $type) { |
240 |
if (!isset($themes[$theme][$type]) && isset($themes[$theme]['min'][$type])) { |
241 |
$themes[$theme][$type] = $themes[$theme]['min'][$type]; |
242 |
} |
243 |
} |
244 |
|
245 |
// Prepend the main Bootstrap styles before the Bootstrap theme.
|
246 |
if ($theme === 'bootstrap_theme') { |
247 |
if (isset($themes['bootstrap']['css'])) { |
248 |
$themes[$theme]['css'] = array_unique(array_merge($themes['bootstrap']['css'], isset($themes[$theme]['css']) ? $themes[$theme]['css'] : array())); |
249 |
} |
250 |
if (isset($themes['bootstrap']['min']['css'])) { |
251 |
$themes[$theme]['min']['css'] = array_unique(array_merge($themes['bootstrap']['min']['css'], isset($themes[$theme]['min']['css']) ? $themes[$theme]['min']['css'] : array())); |
252 |
} |
253 |
} |
254 |
|
255 |
// Populate missing JavaScript.
|
256 |
if (!isset($themes[$theme]['js']) && isset($themes['bootstrap']['js'])) { |
257 |
$themes[$theme]['js'] = $themes['bootstrap']['js']; |
258 |
} |
259 |
if (!isset($themes[$theme]['min']['js']) && isset($themes['bootstrap']['min']['js'])) { |
260 |
$themes[$theme]['min']['js'] = $themes['bootstrap']['min']['js']; |
261 |
} |
262 |
} |
263 |
|
264 |
return $themes; |
265 |
} |
266 |
|
267 |
/**
|
268 |
* Requests JSON from jsDelivr's API V1.
|
269 |
*
|
270 |
* @param string $package
|
271 |
* The NPM package being requested.
|
272 |
* @param string $version
|
273 |
* A specific version of $package to request. If not provided, a list of
|
274 |
* available versions will be returned.
|
275 |
*
|
276 |
* @return array
|
277 |
* The JSON data from the API.
|
278 |
*/
|
279 |
protected function requestApiV1($package, $version = NULL) { |
280 |
$url = static::BASE_API_URL . "/$package"; |
281 |
|
282 |
// If no version was passed, then all versions are returned.
|
283 |
if (!$version) { |
284 |
return $this->requestJson($url); |
285 |
} |
286 |
|
287 |
$json = $this->requestJson("$url@$version/flat"); |
288 |
|
289 |
// If bootstrap JSON could not be returned, provide defaults.
|
290 |
if (!$json && $package === 'bootstrap') { |
291 |
$version = BOOTSTRAP_VERSION; |
292 |
return array( |
293 |
'css' => array(static::BASE_CDN_URL . "/$package@$version/dist/css/bootstrap.css"), |
294 |
'js' => array(static::BASE_CDN_URL . "/$package@$version/dist/js/bootstrap.js"), |
295 |
'min' => array( |
296 |
'css' => array(static::BASE_CDN_URL . "/$package@$version/dist/css/bootstrap.min.css"), |
297 |
'js' => array(static::BASE_CDN_URL . "/$package@$version/dist/js/bootstrap.min.js"), |
298 |
), |
299 |
); |
300 |
} |
301 |
|
302 |
// Parse the files from JSON.
|
303 |
return $this->parseFiles($json); |
304 |
} |
305 |
|
306 |
} |