forked from stewartpark/Flask-JSGlue
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflask_jsglue.py
129 lines (110 loc) · 4.21 KB
/
flask_jsglue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from flask import current_app, make_response, url_for
from jinja2 import Markup
import re, json
JSGLUE_JS_PATH = '/jsglue.js'
JSGLUE_NAMESPACE = 'Flask'
rule_parser = re.compile(r'<(.+?)>')
splitter = re.compile(r'<.+?>')
def get_routes(app):
output = []
for r in app.url_map.iter_rules():
endpoint = r.endpoint
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)
class JSGlue(object):
def __init__(self, app=None, **kwargs):
self.app = app
if app is not None:
self.init_app(app)
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}
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))
@staticmethod
def include():
js_path = url_for('serve_js')
return Markup('<script src="%s" type="text/javascript"></script>') % (js_path,)