Skip to content

Commit

Permalink
Return the raw JavaScript code to enable unsupported use cases (Fixes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Jan 16, 2022
1 parent b745b69 commit c79961d
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 59 deletions.
144 changes: 86 additions & 58 deletions src/flask_moment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,42 @@
default_moment_sri = ('sha512-LGXaggshOkD/at6PFNcp2V2unf9LzFq6LE+sChH7ceMTDP0'
'g2kn6Vxwgg7wkPP7AAtX+lmPqPdxB47A0Nz0cMQ==')

js_code = '''function flask_moment_render(elem) {{
const timestamp = moment(elem.dataset.timestamp);
const func = elem.dataset.function;
const format = elem.dataset.format;
const timestamp2 = elem.dataset.timestamp2;
const no_suffix = elem.dataset.nosuffix;
const units = elem.dataset.units;
let args = [];
if (format)
args.push(format);
if (timestamp2)
args.push(moment(timestamp2));
if (no_suffix)
args.push(no_suffix);
if (units)
args.push(units);
elem.textContent = timestamp[func].apply(timestamp, args);
elem.classList.remove('flask-moment');
elem.style.display = "";
}}
function flask_moment_render_all() {{
const moments = document.querySelectorAll('.flask-moment');
moments.forEach(function(moment) {{
flask_moment_render(moment);
const refresh = moment.dataset.refresh;
if (refresh && refresh > 0) {{
(function(elem, interval) {{
setInterval(function() {{
flask_moment_render(elem);
}}, interval);
}})(moment, refresh);
}}
}})
}}
document.addEventListener("DOMContentLoaded", flask_moment_render_all);'''


class moment(object):
"""Create a moment object.
Expand All @@ -18,8 +54,8 @@ class moment(object):
to ``False`` and all the timestamps managed by the server
will be in the UTC timezone.
"""
@staticmethod
def include_moment(version=default_moment_version, local_js=None,
@classmethod
def include_moment(cls, version=default_moment_version, local_js=None,
no_js=None, sri=None, with_locales=True):
"""Include the moment.js library and the supporting JavaScript code
used by this extension.
Expand All @@ -41,18 +77,18 @@ def include_moment(version=default_moment_version, local_js=None,
:param with_locales: If ``True``, include the version of moment.js that
has all the locales.
"""
js = ''
mjs = ''
if version == default_moment_version and local_js is None and \
sri is None:
with_locales is True and sri is None:
sri = default_moment_sri
if not no_js:
if local_js is not None:
if not sri:
js = '<script src="{}"></script>\n'.format(local_js)
mjs = '<script src="{}"></script>\n'.format(local_js)
else:
js = ('<script src="{}" integrity="{}" '
'crossorigin="anonymous"></script>\n').format(
local_js, sri)
mjs = ('<script src="{}" integrity="{}" '
'crossorigin="anonymous"></script>\n').format(
local_js, sri)
elif version is not None:
if with_locales:
js_filename = 'moment-with-locales.min.js' \
Expand All @@ -62,57 +98,16 @@ def include_moment(version=default_moment_version, local_js=None,
js_filename = 'moment.min.js'

if not sri:
js = ('<script src="https://cdnjs.cloudflare.com/ajax/'
'libs/moment.js/{}/{}"></script>\n').format(
version, js_filename)
mjs = ('<script src="https://cdnjs.cloudflare.com/ajax/'
'libs/moment.js/{}/{}"></script>\n').format(
version, js_filename)
else:
js = ('<script src="https://cdnjs.cloudflare.com/ajax/'
'libs/moment.js/{}/{}" integrity="{}" '
'crossorigin="anonymous"></script>\n').format(
version, js_filename, sri)

default_format = ''
if 'MOMENT_DEFAULT_FORMAT' in current_app.config:
default_format = '\nmoment.defaultFormat = "{}";'.format(
current_app.config['MOMENT_DEFAULT_FORMAT'])
return Markup('''{}<script>
moment.locale("en");{}
function flask_moment_render(elem) {{
const timestamp = moment(elem.dataset.timestamp);
const func = elem.dataset.function;
const format = elem.dataset.format;
const timestamp2 = elem.dataset.timestamp2;
const no_suffix = elem.dataset.nosuffix;
const units = elem.dataset.units;
let args = [];
if (format)
args.push(format);
if (timestamp2)
args.push(moment(timestamp2));
if (no_suffix)
args.push(no_suffix);
if (units)
args.push(units);
elem.textContent = timestamp[func].apply(timestamp, args);
elem.classList.remove('flask-moment');
elem.style.display = "";
}}
function flask_moment_render_all() {{
const moments = document.querySelectorAll('.flask-moment');
moments.forEach(function(moment) {{
flask_moment_render(moment);
const refresh = moment.dataset.refresh;
if (refresh && refresh > 0) {{
(function(elem, interval) {{
setInterval(function() {{
flask_moment_render(elem);
}}, interval);
}})(moment, refresh);
}}
}})
}}
document.addEventListener("DOMContentLoaded", flask_moment_render_all);
</script>'''.format(js, default_format)) # noqa: E501
mjs = ('<script src="https://cdnjs.cloudflare.com/ajax/'
'libs/moment.js/{}/{}" integrity="{}" '
'crossorigin="anonymous"></script>\n').format(
version, js_filename, sri)
return Markup('{}\n<script>\n{}\n</script>\n'''.format(
mjs, cls.flask_moment_js()))

@staticmethod
def locale(language='en', auto_detect=False, customization=None):
Expand All @@ -135,6 +130,36 @@ def locale(language='en', auto_detect=False, customization=None):
return Markup(
'<script>\nmoment.locale("{}");\n</script>'.format(language))

@staticmethod
def flask_moment_js():
"""Return the JavaScript supporting code for this extension.
This method is provided to enable custom configurations that are not
supported by ``include_moment``. The return value of this method is
a string with raw JavaScript code. This code can be added to your own
``<script>`` tag in a template file::
<script>
{{ moment.flask_moment_js() }}
</script>
Alternatively, the code can be returned in a JavaScript endpoint that
can be loaded from the HTML file as an external resource::
@app.route('/flask-moment.js')
def flask_moment_js():
return (moment.flask_moment_js(), 200,
{'Content-Type': 'application/javascript'})
Note: only the code specific to Flask-Moment is included. When using
this method, you must include the moment.js library separately.
"""
default_format = ''
if 'MOMENT_DEFAULT_FORMAT' in current_app.config:
default_format = '\nmoment.defaultFormat = "{}";'.format(
current_app.config['MOMENT_DEFAULT_FORMAT'])
return '''moment.locale("en");{}\n{}'''.format(default_format, js_code)

@staticmethod
def lang(language):
"""Set the language. This is a simpler version of the :func:`locale`
Expand Down Expand Up @@ -326,5 +351,8 @@ def context_processor():
'moment': current_app.extensions['moment']
}

def flask_moment_js(self):
return current_app.extensions['moment'].flask_moment_js()

def create(self, timestamp=None):
return current_app.extensions['moment'](timestamp)
7 changes: 6 additions & 1 deletion tests/test_flask_moment.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class TestMoment(unittest.TestCase):
def setUp(self):
self.app = Flask(__name__)
self.moment = Moment(self.app)
self.moment_app = Moment(self.app)
self.appctx = self.app.app_context()
self.moment = self.app.extensions['moment']
self.appctx.push()
Expand Down Expand Up @@ -91,6 +91,11 @@ def test_lang(self):
assert isinstance(lang, Markup)
assert 'moment.locale("en")' in str(lang)

def test_flask_moment_js(self):
js = self.moment_app.flask_moment_js()
assert isinstance(js, str)
assert 'function flask_moment_render(elem) {' in js

def test__moment_timestamp_passed(self):
ts = datetime(2017, 1, 15, 22, 47, 6, 479898)
m = self.moment(timestamp=ts)
Expand Down

0 comments on commit c79961d

Please sign in to comment.