Source code for vial.jsglue

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# Use of this source is governed by the MIT license, see LICENSE file.
#-------------------------------------------------------------------------------

#
# This file is part of official Flask-JSGlue project (https://github.com/stewartpark/Flask-JSGlue).
# However at this point (June 2019) the latest version available from PyPI (0.3.1)
# lacks elegant support for applications that are not running at webserver root.
# When the next version is released this file can be removed again.
#
# No copyright infrigement intended.
#


import re, json

from jinja2 import Markup
from flask import current_app, make_response, url_for


JSGLUE_JS_PATH = '/jsglue.js'
JSGLUE_NAMESPACE = 'Flask'
rule_parser = re.compile(r'<(.+?)>')
splitter = re.compile(r'<.+?>')


[docs]def get_routes(app): output = [] for r in app.url_map.iter_rules(): endpoint = r.endpoint if app.config['APPLICATION_ROOT'] == '/' or\ not app.config['APPLICATION_ROOT']: rule = r.rule else: rule = '{root}{rule}'.format( root=app.config['APPLICATION_ROOT'], rule=r.rule ) rule_args = [x.split(':')[-1] for x in rule_parser.findall(rule)] rule_tr = splitter.split(rule) output.append((endpoint, rule_tr, rule_args)) return sorted(output, key=lambda x: len(x[1]), reverse=True)
[docs]class JSGlue(object): def __init__(self, app=None, **kwargs): self.app = app if app is not None: self.init_app(app)
[docs] def init_app(self, app): self.app = app @app.route(JSGLUE_JS_PATH) def serve_js(): return make_response( (self.generate_js(), 200, {'Content-Type': 'text/javascript'}) ) @app.context_processor def context_processor(): return {'JSGlue': JSGlue}
[docs] def generate_js(self): rules = get_routes(self.app) return """ var %s = new (function(){ 'use strict'; return { '_endpoints': %s, 'url_for': function(endpoint, rule) { if(typeof rule === "undefined") rule = {}; var has_everything = false, url = ""; var is_absolute = false, has_anchor = false, has_scheme; var anchor = "", scheme = ""; if(rule['_external'] === true) { is_absolute = true; scheme = location.protocol.split(':')[0]; delete rule['_external']; } if('_scheme' in rule) { if(is_absolute) { scheme = rule['_scheme']; delete rule['_scheme']; } else { throw {name:"ValueError", message:"_scheme is set without _external."}; } } if('_anchor' in rule) { has_anchor = true; anchor = rule['_anchor']; delete rule['_anchor']; } for(var i in this._endpoints) { if(endpoint == this._endpoints[i][0]) { var url = ''; var j = 0; var has_everything = true; var used = {}; for(var j = 0; j < this._endpoints[i][2].length; j++) { var t = rule[this._endpoints[i][2][j]]; if(typeof t === "undefined") { has_everything = false; break; } url += this._endpoints[i][1][j] + t; used[this._endpoints[i][2][j]] = true; } if(has_everything) { if(this._endpoints[i][2].length != this._endpoints[i][1].length) url += this._endpoints[i][1][j]; var first = true; for(var r in rule) { if(r[0] != '_' && !(r in used)) { if(first) { url += '?'; first = false; } else { url += '&'; } url += r + '=' + rule[r]; } } if(has_anchor) { url += "#" + anchor; } if(is_absolute) { return scheme + "://" + location.host + url; } else { return url; } } } } throw {name: 'BuildError', message: "Couldn't find the matching endpoint."}; } }; });""" % (JSGLUE_NAMESPACE, json.dumps(rules))
[docs] @staticmethod def include(): js_path = url_for('serve_js') return Markup('<script src="%s" type="text/javascript"></script>') % (js_path,)