#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# This file is part of Mentat system (https://mentat.cesnet.cz/).
#
# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/)
# 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 infringement intended.
#
__author__ = "Jan Mach <jan.mach@cesnet.cz>"
__credits__ = "Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
import re
import json
from markupsafe import Markup
from flask import 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():
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,)