mirror of
https://github.com/HorlogeSkynet/CDNUpdates
synced 2025-05-12 04:00:19 +02:00
Compare commits
7 Commits
e446fcb479
...
8f3facd076
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8f3facd076 | ||
![]() |
a81ff7ebe6 | ||
![]() |
8cc7d976b6 | ||
![]() |
42ffbed4f6 | ||
![]() |
84065165af | ||
![]() |
5f9f446e41 | ||
![]() |
c6655a2d5f |
@ -2,8 +2,8 @@
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from .CDNConstants import CDN_PROVIDERS
|
||||
from .CDNContent import CDNContent
|
||||
from .CDNConstants import CDN_PROVIDERS
|
||||
from .CDNUtils import log_message
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from sublime import IGNORECASE
|
||||
|
||||
from .CDNConstants import LINK_REGEX
|
||||
from .CDNConstants import LINK_REGEXP_PATTERN
|
||||
|
||||
|
||||
class CheckForLinks: # pylint: disable=too-few-public-methods
|
||||
@ -15,6 +15,6 @@ class CheckForLinks: # pylint: disable=too-few-public-methods
|
||||
self.view = view
|
||||
self.region_list = region_list
|
||||
|
||||
for region in self.view.find_all(LINK_REGEX, IGNORECASE):
|
||||
for region in self.view.find_all(LINK_REGEXP_PATTERN, IGNORECASE):
|
||||
# We have to fill the list directly (passed by reference)
|
||||
self.region_list.append(region)
|
||||
|
@ -8,9 +8,10 @@ from sublime import (
|
||||
DRAW_EMPTY_AS_OVERWRITE,
|
||||
DRAW_NO_FILL,
|
||||
DRAW_NO_OUTLINE,
|
||||
DRAW_SOLID_UNDERLINE
|
||||
DRAW_SOLID_UNDERLINE,
|
||||
LAYOUT_BLOCK,
|
||||
message_dialog
|
||||
)
|
||||
from sublime import LAYOUT_BLOCK, message_dialog
|
||||
|
||||
from .CDNUtils import log_message
|
||||
|
||||
@ -111,7 +112,7 @@ class CheckForUpdates(Thread):
|
||||
}}
|
||||
</style>
|
||||
<div class="https_warning">
|
||||
You should load <b>{0}</b> over HTTPS !
|
||||
You should explicitly load <b>{0}</b> over HTTPS !
|
||||
</div>
|
||||
</body>
|
||||
""".format(
|
||||
@ -123,7 +124,7 @@ class CheckForUpdates(Thread):
|
||||
|
||||
# Let's add some regions for the user with specific icons.
|
||||
# This is done afterwards to reduce the number of regions drawn.
|
||||
for status in ['up_to_date', 'to_update', 'not_found']:
|
||||
for status in ('up_to_date', 'to_update', 'not_found'):
|
||||
self.view.add_regions(
|
||||
status,
|
||||
[cdn_content.sublime_region for cdn_content in self.cdn_content_list
|
||||
|
@ -1,14 +1,21 @@
|
||||
"""CDNUpdates' constants module"""
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
# This regex has been written by @sindresorhus for Semver.
|
||||
# <https://github.com/sindresorhus/semver-regex> (v3.1.1)
|
||||
SEMVER_REGEX = r"(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(?=$|\s)"
|
||||
import re
|
||||
|
||||
# This is a regex written by @diegoperini, ported for Python by @adamrofer.
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
# This Semver regular expression has been written by @sindresorhus for NodeJS.
|
||||
# It has been adapted to remove the starting non-fixed width look-behind (incompatible) and trailing positive look-ahead (useless here).
|
||||
# <https://github.com/sindresorhus/semver-regex> (v3.1.1)
|
||||
SEMVER_REGEXP_OBJECT = re.compile(
|
||||
r"v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:0|[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?",
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
# This is a regular expression written by @diegoperini, and ported for Python by @adamrofer.
|
||||
# <https://gist.github.com/dperini/729294>
|
||||
# It has been tweaked to work with network path references and HTML tags.
|
||||
LINK_REGEX = r"(?:(https?:)?//)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S[^\"\s]*)?"
|
||||
LINK_REGEXP_PATTERN = r"(?:(https?:)?//)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S[^\"\s]*)?"
|
||||
# pylint: enable=line-too-long
|
||||
|
||||
# This list stores the API links of handled CDN providers.
|
||||
|
@ -13,7 +13,7 @@ from .CDNConstants import (
|
||||
CDN_STATIC_FILE_CORRESPONDENCES,
|
||||
MAXCDN_BOOTSTRAP_CORRESPONDENCES,
|
||||
OPENSOURCE_KEYCDN_CORRESPONDENCES,
|
||||
SEMVER_REGEX
|
||||
SEMVER_REGEXP_OBJECT
|
||||
)
|
||||
from .CDNUtils import log_message
|
||||
|
||||
@ -54,7 +54,7 @@ class CDNContent:
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
self.name = tmp[3]
|
||||
|
||||
self.compare_with_latest_cdnjs_version(self.name, tmp[4])
|
||||
self._compare_with_latest_cdnjs_version(self.name, tmp[4])
|
||||
|
||||
elif self.parsed_result.netloc == 'maxcdn.bootstrapcdn.com':
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
@ -64,7 +64,7 @@ class CDNContent:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
MAXCDN_BOOTSTRAP_CORRESPONDENCES.get(self.name)['owner'],
|
||||
MAXCDN_BOOTSTRAP_CORRESPONDENCES.get(self.name)['name'],
|
||||
tmp[2]
|
||||
@ -76,25 +76,26 @@ class CDNContent:
|
||||
|
||||
if tmp[1].startswith('jquery'):
|
||||
self.name = 'jquery'
|
||||
version = re.search(SEMVER_REGEX, tmp[1]).group(0)
|
||||
|
||||
elif tmp[1] in ['ui', 'mobile', 'color']:
|
||||
version = SEMVER_REGEXP_OBJECT.search(tmp[1])
|
||||
version = version and version.group(0)
|
||||
elif tmp[1] in ('ui', 'mobile', 'color'):
|
||||
self.name = 'jquery-' + tmp[1]
|
||||
version = tmp[2]
|
||||
|
||||
elif tmp[1] == 'qunit':
|
||||
self.name = 'qunit'
|
||||
version = re.search(SEMVER_REGEX, tmp[2]).group(0)
|
||||
|
||||
version = SEMVER_REGEXP_OBJECT.search(tmp[2])
|
||||
version = version and version.group(0)
|
||||
elif tmp[1] == 'pep':
|
||||
self.name = 'PEP'
|
||||
version = tmp[2]
|
||||
|
||||
else:
|
||||
version = None
|
||||
|
||||
if not version:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
# Only `QUnit` belongs to another organization.
|
||||
('qunitjs' if self.name == 'qunit' else 'jquery'),
|
||||
self.name,
|
||||
@ -109,7 +110,7 @@ class CDNContent:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
AJAX_GOOGLE_APIS_CORRESPONDENCES.get(self.name)['owner'],
|
||||
AJAX_GOOGLE_APIS_CORRESPONDENCES.get(self.name)['name'],
|
||||
tmp[4]
|
||||
@ -117,33 +118,31 @@ class CDNContent:
|
||||
|
||||
# CDN from CDN.JSDLIVR.NET will be handled here.
|
||||
elif self.parsed_result.netloc == 'cdn.jsdelivr.net':
|
||||
"""
|
||||
The API from JSDLIVR is powerful.
|
||||
It implies we compute a "fuzzy" version checking.
|
||||
For instance : "jquery@3" is OK for '3.2.1'.
|
||||
"""
|
||||
# The API from JSDLIVR is powerful.
|
||||
# It implies we compute a "fuzzy" version checking.
|
||||
# For instance : "jquery@3" is OK for '3.2.1'.
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
|
||||
try:
|
||||
if tmp[1] == 'npm':
|
||||
self.name, version = tmp[2].split('@')
|
||||
self.compare_with_npmjs_version(self.name, version)
|
||||
self._compare_with_npmjs_version(self.name, version)
|
||||
|
||||
elif tmp[1] == 'gh':
|
||||
self.name, version = tmp[3].split('@')
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
tmp[2], self.name, version,
|
||||
fuzzy_check=True
|
||||
)
|
||||
|
||||
elif tmp[1] == 'wp':
|
||||
# This how we'll handle the latest version references, as :
|
||||
# (https://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js)
|
||||
# <https://cdn.jsdelivr.net/wp/wp-slimstat/trunk/wp-slimstat.js>
|
||||
if len(tmp) < 6:
|
||||
raise IndexError
|
||||
|
||||
self.name = tmp[2]
|
||||
self.compare_with_latest_wpsvn_tag(self.name, tmp[4])
|
||||
self._compare_with_latest_wpsvn_tag(self.name, tmp[4])
|
||||
|
||||
else:
|
||||
self.status = 'not_found'
|
||||
@ -161,17 +160,16 @@ class CDNContent:
|
||||
# If no semantic version is specified in the URL, we assume either:
|
||||
# * The developer uses the latest version available (`master`) [OR]
|
||||
# * The developer knows what he is doing (commit hash specified)
|
||||
if re.search(SEMVER_REGEX, tmp[3]) is None:
|
||||
if not SEMVER_REGEXP_OBJECT.search(tmp[3]):
|
||||
self.status = 'up_to_date'
|
||||
|
||||
else:
|
||||
# If not, we compare this version with the latest tag !
|
||||
self.compare_with_latest_github_tag(tmp[1], self.name, tmp[3])
|
||||
self._compare_with_latest_github_tag(tmp[1], self.name, tmp[3])
|
||||
|
||||
elif self.parsed_result.netloc == 'code.ionicframework.com':
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
self.name = tmp[1]
|
||||
self.compare_with_latest_github_release('ionic-team', self.name, tmp[2])
|
||||
self._compare_with_latest_github_release('ionic-team', self.name, tmp[2])
|
||||
|
||||
elif self.parsed_result.netloc == 'use.fontawesome.com':
|
||||
self.name = 'Font Awesome'
|
||||
@ -187,7 +185,7 @@ class CDNContent:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
OPENSOURCE_KEYCDN_CORRESPONDENCES.get(self.name)['owner'],
|
||||
OPENSOURCE_KEYCDN_CORRESPONDENCES.get(self.name)['name'],
|
||||
tmp[2]
|
||||
@ -201,26 +199,32 @@ class CDNContent:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
CDN_STATIC_FILE_CORRESPONDENCES.get(self.name)['owner'],
|
||||
CDN_STATIC_FILE_CORRESPONDENCES.get(self.name)['name'],
|
||||
tmp[2]
|
||||
)
|
||||
|
||||
elif self.parsed_result.netloc in ['ajax.microsoft.com', 'ajax.aspnetcdn.com']:
|
||||
elif self.parsed_result.netloc in ('ajax.microsoft.com', 'ajax.aspnetcdn.com'):
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
if tmp[2] not in AJAX_MICROSOFT_CORRESPONDENCES.keys():
|
||||
|
||||
# Sometimes the version is in the path...
|
||||
if len(tmp) == 5:
|
||||
version = tmp[3]
|
||||
# ... and some other times contained within the name.
|
||||
else:
|
||||
version = SEMVER_REGEXP_OBJECT.search(tmp[3])
|
||||
version = version and version.group(0)
|
||||
|
||||
if tmp[2] not in AJAX_MICROSOFT_CORRESPONDENCES.keys() or not version:
|
||||
self.status = 'not_found'
|
||||
return
|
||||
|
||||
self.name = tmp[2]
|
||||
self.compare_with_latest_github_tag(
|
||||
self._compare_with_latest_github_tag(
|
||||
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2])['owner'],
|
||||
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2])['name'],
|
||||
# Sometimes the version is in the path...
|
||||
tmp[3] if len(tmp) == 5
|
||||
# ... and some other times contained within the name.
|
||||
else re.search(SEMVER_REGEX, tmp[3]).group(0),
|
||||
version,
|
||||
# Microsoft has tagged some libraries very badly...
|
||||
# Check `CDNConstants.AJAX_MICROSOFT_CORRESPONDENCES` for this entry.
|
||||
AJAX_MICROSOFT_CORRESPONDENCES.get(tmp[2]).get('fuzzy_check', False)
|
||||
@ -230,9 +234,9 @@ class CDNContent:
|
||||
tmp = self.parsed_result.path.split('/')
|
||||
|
||||
if tmp[1] == 'ckeditor5' and \
|
||||
tmp[3] in ['classic', 'inline', 'balloon']:
|
||||
tmp[3] in ('classic', 'inline', 'balloon'):
|
||||
self.name = "{0} ({1})".format(tmp[1], tmp[3])
|
||||
self.compare_with_latest_github_release('ckeditor', tmp[1], tmp[2])
|
||||
self._compare_with_latest_github_release('ckeditor', tmp[1], tmp[2])
|
||||
|
||||
else:
|
||||
self.status = 'not_found'
|
||||
@ -243,7 +247,7 @@ class CDNContent:
|
||||
else:
|
||||
log_message("This statement should not be reached.")
|
||||
|
||||
def compare_with_latest_cdnjs_version(self, name, version):
|
||||
def _compare_with_latest_cdnjs_version(self, name, version):
|
||||
"""This method handles call and result comparison with the CDNJS' API"""
|
||||
|
||||
# We ask CDNJS API to retrieve information about this library.
|
||||
@ -285,7 +289,7 @@ class CDNContent:
|
||||
)
|
||||
)
|
||||
|
||||
def compare_with_latest_github_tag(
|
||||
def _compare_with_latest_github_tag(
|
||||
self,
|
||||
owner, name, version,
|
||||
fuzzy_check=False):
|
||||
@ -327,7 +331,7 @@ class CDNContent:
|
||||
)
|
||||
)
|
||||
|
||||
def compare_with_latest_github_release(
|
||||
def _compare_with_latest_github_release(
|
||||
self,
|
||||
owner, name, version,
|
||||
fuzzy_check=False):
|
||||
@ -367,7 +371,7 @@ class CDNContent:
|
||||
)
|
||||
)
|
||||
|
||||
def compare_with_npmjs_version(self, name, version):
|
||||
def _compare_with_npmjs_version(self, name, version):
|
||||
"""This method handles call and result comparison with the NPMJS' API"""
|
||||
request = urlopen(Request(
|
||||
"https://api.npms.io/v2/search?q={name}".format(name=quote(name)),
|
||||
@ -402,7 +406,7 @@ class CDNContent:
|
||||
)
|
||||
)
|
||||
|
||||
def compare_with_latest_wpsvn_tag(self, name, version):
|
||||
def _compare_with_latest_wpsvn_tag(self, name, version):
|
||||
"""This method parses HTML from WordPress' SVN plugin page to retrieve the latest tag"""
|
||||
request = urlopen(
|
||||
"https://plugins.svn.wordpress.org/{name}/tags/".format(
|
||||
|
@ -1,7 +1,6 @@
|
||||
"""CDNUpdates main class"""
|
||||
|
||||
from sublime import error_message
|
||||
|
||||
from sublime_plugin import EventListener, TextCommand
|
||||
|
||||
from .CDNCheckForCDNProviders import CheckForCDNProviders
|
||||
@ -27,10 +26,10 @@ class CDNUpdatesCommand(TextCommand): # pylint: disable=too-few-public-methods
|
||||
"""
|
||||
# First we check if the current sheet is not still being loaded.
|
||||
if self.view.is_loading():
|
||||
error_message("This file is not fully loaded yet.")
|
||||
error_message("This view is not fully loaded yet.")
|
||||
return
|
||||
|
||||
# If it's OK, we clear the view from the elements added previously.
|
||||
# If it's OK, we clear the view from the previously added elements.
|
||||
clear_view(self.view)
|
||||
|
||||
self.view.set_status(
|
||||
|
@ -48,7 +48,7 @@ Package Control dedicated page [here](https://packagecontrol.io/packages/CDNUpda
|
||||
Most of the CDN providers don't provide any API for their service, so it would be very tricky to retrieve latest version available directly from them.
|
||||
Unless for <https://cdnjs.com/>, this plugin is actually based on the GitHub API to fetch the latest existing Git tag directly from the repositories. Its `name` is compared afterwards with the CDN version present in your sources.
|
||||
If you have many many CDNs in your sheets (or if you want to contribute to this project 😜), you'll surely need to set a GitHub API token to avoid being blocked by the rate limit.
|
||||
You can generate one [here](https://github.com/settings/tokens), and paste in under the plugin preferences (accessible from `CDNUpdates`'s Sublime menu).
|
||||
You can generate one [here](https://github.com/settings/tokens) (`public_repo` scope), and paste in under the plugin preferences (accessible from `CDNUpdates`'s Sublime menu).
|
||||
|
||||
## CDN Providers currently handled
|
||||
|
||||
@ -71,7 +71,7 @@ You can generate one [here](https://github.com/settings/tokens), and paste in un
|
||||
|
||||
> It'll be done automatically next time you'll save your sheet :wink:
|
||||
|
||||
### I've updated my links, but the _Phantom_ objects don't want to leave...
|
||||
### I've updated my links, how can I get rid of these _Phantom_ objects ?
|
||||
|
||||
> Same as above :smile:
|
||||
|
||||
|
@ -23,23 +23,23 @@ https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.1/css/bootstrap.min.css
|
||||
https://maxcdn.bootstrapcdn.com/does_not_exist/1.2.3/js/test.min.js
|
||||
|
||||
<!-- JQUERY with JQuery -->
|
||||
https://code.jquery.com/jquery-3.4.1.slim.min.js
|
||||
https://code.jquery.com/jquery-3.5.1.slim.min.js
|
||||
https://code.jquery.com/ui/1.11.4/jquery-ui.js
|
||||
https://code.jquery.com/does_not_exist/1.2.3/pep.js
|
||||
|
||||
<!-- GOOGLEAPIS -->
|
||||
https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
|
||||
https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
|
||||
https://ajax.googleapis.com/ajax/libs/shaka-player/2.2.8/shaka-player.compiled.js
|
||||
https://ajax.googleapis.com/ajax/libs/does_not_exist/1.2.3/swfobject.js
|
||||
|
||||
<!-- JSDELIVR... -->
|
||||
<!-- ...with GitHub -->
|
||||
https://cdn.jsdelivr.net/gh/jquery/jquery@3.4.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/gh/jquery/jquery@3.5.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/gh/jquery/jquery@3.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/gh/jquery/jquery/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/gh/user/does_not_exist@version/file
|
||||
<!-- ...with NPMJS -->
|
||||
https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/npm/jquery@3.1/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js
|
||||
https://cdn.jsdelivr.net/npm/does_not_exist@version/file
|
||||
@ -51,7 +51,7 @@ https://cdn.jsdelivr.net/wp/does_not_exist/tags/version/file
|
||||
|
||||
<!-- GITHUB AS CDN... -->
|
||||
<!-- ... with RAWGIT -->
|
||||
https://rawgit.com/jquery/jquery/3.4.1/src/jquery.js
|
||||
https://rawgit.com/jquery/jquery/3.5.1/src/jquery.js
|
||||
https://cdn.rawgit.com/jquery/jquery/3.2.0/src/jquery.js
|
||||
https://cdn.rawgit.com/does_not_exist/does_not_exist/1.2.2/src/test.js
|
||||
|
||||
@ -65,23 +65,23 @@ https://use.fontawesome.com/0123456789.js
|
||||
|
||||
<!-- KEYCDN... -->
|
||||
<!-- ... with Pure -->
|
||||
https://opensource.keycdn.com/pure/1.0.0/pure-min.css
|
||||
https://opensource.keycdn.com/pure/2.0.3/pure-min.css
|
||||
<!-- ... with FontAwesome -->
|
||||
https://opensource.keycdn.com/fontawesome/4.4.1/font-awesome.min.css
|
||||
<!-- ... with AngularJS -->
|
||||
https://opensource.keycdn.com/angularjs/1.6.8/angular.min.js
|
||||
|
||||
<!-- STATICFILE -->
|
||||
https://cdn.staticfile.org/jquery/3.4.1/core.js
|
||||
https://cdn.staticfile.org/jquery/3.5.1/core.js
|
||||
https://cdn.staticfile.org/vue/2.2.6/vue.js
|
||||
https://cdn.staticfile.org/does_not_exist/1.2.3/jquery.min.js
|
||||
|
||||
<!-- ASPNETCDN -->
|
||||
https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.1/jquery.validate.js
|
||||
https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.2/jquery.validate.js
|
||||
https://ajax.aspnetcdn.com/ajax/jquery.cycle/2.99/jquery.cycle.all.js
|
||||
https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js
|
||||
|
||||
<!-- CKEDITOR -->
|
||||
https://cdn.ckeditor.com/ckeditor5/17.0.0/classic/ckeditor.js
|
||||
https://cdn.ckeditor.com/ckeditor5/19.1.1/classic/ckeditor.js
|
||||
https://cdn.ckeditor.com/ckeditor5/1.0.0-alpha.1/inline/ckeditor.js
|
||||
https://cdn.ckeditor.com/ckeditor4/1.0.0-alpha.2/balloon/ckeditor.js
|
||||
|
Loading…
x
Reference in New Issue
Block a user