From 3ab78505303ca5ecf1de1324893918f3b541f2d6 Mon Sep 17 00:00:00 2001 From: yuxiaoy Date: Wed, 19 May 2021 20:55:16 +0800 Subject: [PATCH] Replace JQuery with vanillaJS --- README.md | 89 +++++++++++++++++++++++++--------- example/templates/index.html | 19 ++++---- flask_moment.py | 75 ++++++++++------------------- tests/test_flask_moment.py | 92 +++++------------------------------- tox.ini | 6 +-- 5 files changed, 117 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index d0ba4c1..be21af0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ Flask-Moment [![Build Status](https://travis-ci.org/miguelgrinberg/Flask-Moment.svg?branch=master)](https://travis-ci.org/miguelgrinberg/Flask-Moment) -This extension enhances Jinja2 templates with formatting of dates and times using [moment.js](http://momentjs.com/). +This extension enhances Jinja2 templates with formatting of dates and times +using [moment.js](http://momentjs.com/). Quick Start ----------- @@ -16,11 +17,12 @@ Step 1: Initialize the extension: Step 2: In your `` section of your base template add the following code: - {{ moment.include_jquery() }} {{ moment.include_moment() }} -This extension also supports the [Flask application factory pattern](http://flask.pocoo.org/docs/latest/patterns/appfactories/) by allowing you to create a Moment object and then separately initialize it for an app: +This extension also supports the [Flask application factory pattern](http://flask.pocoo.org/docs/latest/patterns/appfactories/) +by allowing you to create a Moment object and then separately initialize it +for an app: moment = Moment() @@ -32,9 +34,17 @@ This extension also supports the [Flask application factory pattern](http://flas app = create_app(prod_config) -Note that jQuery is required. If you are already including it on your own then you can remove the `include_jquery()` line. Secure HTTP is always used to request the external js files.. +Note that older versions of this extension required jQuery, but this library +isn't needed anymore. The `include_jquery()` function that existed in older +releases has now been removed. -The `include_jquery()` and `include_moment()` methods take some optional arguments. If you pass a `version` argument to any of these two calls, then the requested version will be loaded from the default CDN. If you pass `local_js`, then the given local path will be used to load the library. The `include_moment()` argument takes a third argument `no_js` that when set to `True` will assume that the Moment JavaScript library is already loaded and will only add the JavaScript code that supports this extension. +The `include_moment()` methods take some optional arguments. If you pass a +`version` argument to any of these two calls, then the requested version will +be loaded from the default CDN. If you pass `local_js`, then the given local +path will be used to load the library. The `include_moment()` argument takes a +third argument `no_js` that when set to `True` will assume that the Moment +JavaScript library is already loaded and will only add the JavaScript code +that supports this extension. Step 3: Render timestamps in your template. For example: @@ -42,13 +52,21 @@ Step 3: Render timestamps in your template. For example:

Something happened {{ moment(then).fromTime(now) }}.

{{ moment(then).calendar() }}.

-In the second and third examples template variables `then` and `now` are used. These must be instances of Python's `datetime` class, and must be "naive" objects. See the [documentation](http://docs.python.org/2/library/datetime.html) for a discussion of naive date and time objects. As an example, `now` can be set as follows: +In the second and third examples template variables `then` and `now` are used. +These must be instances of Python's `datetime` class, and must be "naive" +objects. See the [documentation](http://docs.python.org/2/library/datetime.html) +for a discussion of naive date and time objects. As an example, `now` can be +set as follows: now = datetime.utcnow() -By default the timestamps will be converted from UTC to the local time in each client's machine before rendering. To disable the conversion to local time pass `local=True`. +By default the timestamps will be converted from UTC to the local time in each +client's machine before rendering. To disable the conversion to local time +pass `local=True`. -Note that even though the timestamps are provided in UTC the rendered dates and times will be in the local time of the client computer, so each users will always see their local time regardless of where they are located. +Note that even though the timestamps are provided in UTC the rendered dates +and times will be in the local time of the client computer, so each users will +always see their local time regardless of where they are located. Function Reference ------------------ @@ -65,26 +83,38 @@ The supported list of display functions is shown below: - `moment(timestamp=None, local=False).unix()` - `moment(timestamp=None, local=False).diff(another_timesatmp, units='days')` -Consult the [moment.js documentation](http://momentjs.com/) for details on these functions. +Consult the [moment.js documentation](http://momentjs.com/) for details on +these functions. Auto-Refresh ------------ -All the display functions take an optional `refresh` argument that when set to `True` will re-render timestamps every minute. This can be useful for relative time formats such as the one returned by the `fromNow()` or `fromTime()` functions. By default refreshing is disabled. +All the display functions take an optional `refresh` argument that when set to +`True` will re-render timestamps every minute. This can be useful for relative +time formats such as the one returned by the `fromNow()` or `fromTime()` +functions. By default refreshing is disabled. Default Format -------------- -The `format()` function can be invoked without arguments, in which case a default format of ISO8601 defined by the moment.js library is used. If you want to set a different default, you can set the `MOMENT_DEFAULT_FORMAT` variable in the Flask configuration. Consult the [moment.js format documentation](http://momentjs.com/docs/#/displaying/format/) for a list of accepted tokens. +The `format()` function can be invoked without arguments, in which case a +default format of ISO8601 defined by the moment.js library is used. If you +want to set a different default, you can set the `MOMENT_DEFAULT_FORMAT` +variable in the Flask configuration. Consult the [moment.js format documentation](http://momentjs.com/docs/#/displaying/format/) +for a list of accepted tokens. Internationalization -------------------- -By default dates and times are rendered in English. To change to a different language add the following line in the `` section after the `include_moment()` line: +By default dates and times are rendered in English. To change to a different +language add the following line in the `` section after the +`include_moment()` line: {{ moment.locale("es") }} -The above example sets the language to Spanish. Moment.js supports a large number of languages, consult the documentation for the list of languages and their two letter codes. +The above example sets the language to Spanish. Moment.js supports a large +number of languages, consult the documentation for the list of languages and +their two letter codes. The extension also supports auto-detection of the client's browser language: @@ -94,31 +124,46 @@ Custom locales can also be included as a dictionary: {{ moment.locale(customizations={ ... }) }} -See the [Moment.js locale customizations](https://momentjs.com/docs/#/i18n/changing-locale/) documentation for details on how to define a custom locale. +See the [Moment.js locale customizations](https://momentjs.com/docs/#/i18n/changing-locale/) +documentation for details on how to define a custom locale. Ajax Support ------------ -It is also possible to create Flask-Moment timestamps in Python code, for cases where a template is not used. This is the syntax: +It is also possible to create Flask-Moment timestamps in Python code, for +cases where a template is not used. This is the syntax: timestamp = moment.create(datetime.utcnow()).calendar() -The `moment` variable is the `Moment` instance that was created at initialization time. +The `moment` variable is the `Moment` instance that was created at +initialization time. -A timestamp created in this way is an HTML string that can be returned as part of a response. For example, here is how a timestamp can be returned in a JSON object: +A timestamp created in this way is an HTML string that can be returned as part +of a response. For example, here is how a timestamp can be returned in a JSON +object: return jsonify({ 'timestamp': moment.create(datetime.utcnow()).format('L') }) -The Ajax callback in the browser needs to call `flask_moment_render_all()` each time an element containing a timestamp is added to the DOM. The included application demonstrates how this is done. +The Ajax callback in the browser needs to call `flask_moment_render_all()` +each time an element containing a timestamp is added to the DOM. The included +application demonstrates how this is done. Subresource Integrity(SRI) ----------- -[SRI ](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) is a security feature that enables browsers to verify that resources they fetch are not maliciously manipulated. To do so a cryptographic hash is provided that proves integrity. +[SRI ](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) +is a security feature that enables browsers to verify that resources they +fetch are not maliciously manipulated. To do so a cryptographic hash is +provided that proves integrity. -SRI is enabled by default. If you wish to use another version or want to host your own javascript, a [separate hash ](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#Tools_for_generating_SRI_hashes) can be provided. -Just add `sri=` when calling either `moment.include_moment()` or `moment.include_jquery()`. If no sri hash is provided and you choose to use a non default version of javascript, no sri hash will be added. +SRI is enabled by default. If you wish to use another version or want to host +your own javascript, a [separate hash ](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#Tools_for_generating_SRI_hashes) +can be provided. -You can always choose to disable sri. To do so just set `sri=False`. +Just add `sri=` when calling `moment.include_moment()`. If no SRI +hash is provided and you choose to use a non default version of javascript, no +SRI hash will be added. + +You can always choose to disable SRI. To do so just set `sri=False`. Development diff --git a/example/templates/index.html b/example/templates/index.html index 7add632..1a58369 100644 --- a/example/templates/index.html +++ b/example/templates/index.html @@ -1,17 +1,20 @@ Flask-Moment example app - {{ moment.include_jquery() }} {{ moment.include_moment() }} diff --git a/flask_moment.py b/flask_moment.py index d150fea..c6ec921 100644 --- a/flask_moment.py +++ b/flask_moment.py @@ -3,11 +3,6 @@ from markupsafe import Markup from flask import current_app -# //code.jquery.com/jquery-3.5.1.min.js -default_jquery_version = '3.5.1' -default_jquery_sri = ('sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn' - '/6Z/hRTt8+pR6L4N2') - # //cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment-with-locales.min.js default_moment_version = '2.29.1' default_moment_sri = ('sha512-LGXaggshOkD/at6PFNcp2V2unf9LzFq6LE+sChH7ceMTDP0' @@ -39,12 +34,12 @@ def include_moment(version=default_moment_version, local_js=None, js_filename = 'moment.min.js' if not sri: - js = ('\n').format( + js = ('\n').format( version, js_filename) else: - js = ('\n').format( version, js_filename, sri) @@ -55,13 +50,13 @@ def include_moment(version=default_moment_version, local_js=None, return Markup('''{}'''.format(js, default_format)) # noqa: E501 - @staticmethod - def include_jquery(version=default_jquery_version, local_js=None, - sri=None): - js = '' - if sri is None and version == default_jquery_version and \ - local_js is None: - sri = default_jquery_sri - if local_js is not None: - if not sri: - js = '\n'.format(local_js) - else: - js = ('\n').format( - local_js, sri) - - else: - if not sri: - js = ('').format(version) - else: - js = ('').format(version, sri) - return Markup(js) - @staticmethod def locale(language='en', auto_detect=False, customization=None): if auto_detect: diff --git a/tests/test_flask_moment.py b/tests/test_flask_moment.py index cab2f56..7c6ce82 100644 --- a/tests/test_flask_moment.py +++ b/tests/test_flask_moment.py @@ -1,8 +1,8 @@ from datetime import datetime from flask import render_template_string from markupsafe import Markup -from flask_moment import _moment, Moment, default_jquery_version, \ - default_moment_version, default_moment_sri +from flask_moment import _moment, Moment, default_moment_version, \ + default_moment_sri # Mock Objects @@ -95,19 +95,6 @@ def test_include_moment_with_default(self, app, moment): assert default_moment_version + '/moment-with-locales.min.js' in ts assert 'moment.defaultFormat = "foo";' in ts - def test_include_jquery_default(self): - include_jquery = _moment.include_jquery() - - assert isinstance(include_jquery, Markup) - assert all([each in str(include_jquery) for each in [ - 'code.jquery.com', default_jquery_version]]) - - def test_include_jquery_local(self): - include_jquery = _moment.include_jquery(local_js=True) - - assert all([each in str(include_jquery) for each in [ - '']]) - class TestPrivateMomentClass(object): """Tests for the _moment class""" @@ -268,9 +255,10 @@ def test_diff_days(self): ts = datetime(2020, 1, 15, 22, 47, 6, 479898) rts = mom.diff(ts, 'days') - assert rts.find('data-function="diff" data-timestamp2="{}" ' - 'data-units="days" data-refresh="0"'.format( - mom._timestamp_as_iso_8601(ts))) > 0 + assert rts.find( + 'data-function="diff" data-timestamp2="{}" data-units="days" ' + 'data-refresh="0"'.format( + mom._timestamp_as_iso_8601(ts))) > 0 assert rts.find(mom._timestamp_as_iso_8601( timestamp=mom.timestamp)) > 0 @@ -279,9 +267,10 @@ def test_diff_hours(self): ts = datetime(2020, 1, 15, 22, 47, 6, 479898) rts = mom.diff(ts, 'hours') - assert rts.find('data-function="diff" data-timestamp2="{}" ' - 'data-units="hours" data-refresh="0"'.format( - mom._timestamp_as_iso_8601(ts))) > 0 + assert rts.find( + 'data-function="diff" data-timestamp2="{}" data-units="hours" ' + 'data-refresh="0"'.format( + mom._timestamp_as_iso_8601(ts))) > 0 assert rts.find(mom._timestamp_as_iso_8601( timestamp=mom.timestamp)) > 0 @@ -305,63 +294,6 @@ def test_create_default_with_timestamp(self, app): class TestSubresourceIntegrity(object): - def test_jquery_with_non_default_version(self): - include_jquery = _moment.include_jquery(version='2.0.9') - - assert 'src=\"' in include_jquery - assert 'integrity=\"' not in include_jquery - assert 'crossorigin=\"' not in include_jquery - - def test_jquery_with_default_version(self): - include_jquery = _moment.include_jquery() - - assert 'src=\"' in include_jquery - assert 'integrity=\"sha' in include_jquery - assert 'crossorigin=\"anonymous\"' in include_jquery - - def test_jquery_from_cdn_without_custom_sri_hash(self): - include_jquery = _moment.include_jquery(version='2.1.1', - sri='sha384-12345678') - - assert ('') == include_jquery - - def test_jquery_local_has_no_sri_as_default(self): - include_jquery = _moment.include_jquery(local_js=True) - - assert 'src=\"' in include_jquery - assert 'integrity=\"' not in include_jquery - assert 'crossorigin\"' not in include_jquery - - def test_jquery_local_with_sri(self): - include_jquery = _moment.include_jquery(local_js=True, - sri='sha384-12345678') - - assert ('\n') == include_jquery - - def test_disabling_sri_jquery_default(self): - include_jquery = _moment.include_jquery(sri=False) - - assert 'src=\"' in include_jquery - assert 'integrity=\"' not in include_jquery - assert 'crossorigin\"' not in include_jquery - - def test_disabling_sri_jquery_custom_js(self): - include_jquery = _moment.include_jquery(local_js=True, sri=False) - - assert 'src=\"' in include_jquery - assert 'integrity=\"' not in include_jquery - assert 'crossorigin\"' not in include_jquery - - def test_disabling_sri_jquery_custom_version(self): - include_jquery = _moment.include_jquery(version='2.1.1', sri=False) - - assert 'src=\"' in include_jquery - assert 'integrity=\"' not in include_jquery - assert 'crossorigin\"' not in include_jquery - def test_moment_with_non_default_versions(self): include_moment = None @@ -400,8 +332,8 @@ def test_moment_from_cdn_with_custom_sri_hash(self): sri='sha384-12345678') assert include_moment.startswith( - '') def test_moment_local(self): diff --git a/tox.ini b/tox.ini index 9803cd8..fb47f2f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=flake8,py27,py35,py36,py37,py38,pypy,pypy3 +envlist=flake8,py36,py37,py38,py39,pypy3 skip_missing_interpreters=true [testenv] @@ -9,12 +9,10 @@ deps= pytest-cov mock basepython= - py27: python2.7 - py35: python3.5 py36: python3.6 py37: python3.7 py38: python3.8 - pypy: pypy + py39: python3.9 pypy3: pypy3 [testenv:flake8]