rbbt-rest 1.7.19 → 1.7.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rbbt/rest/common/cache.rb +13 -1
- data/lib/rbbt/rest/common/misc.rb +4 -2
- data/lib/rbbt/rest/common/users.rb +5 -0
- data/lib/rbbt/rest/entity.rb +2 -1
- data/lib/rbbt/rest/knowledge_base.rb +5 -2
- data/lib/rbbt/rest/knowledge_base/helpers.rb +9 -0
- data/lib/rbbt/rest/main.rb +2 -0
- data/share/views/compass/app.sass +6 -0
- data/share/views/layout/header.haml +8 -4
- data/share/views/public/js/helpers/defer.js +11 -9
- data/share/views/public/js/helpers/helpers.js +23 -5
- data/share/views/public/js/rbbt.basic.js +32 -1
- data/share/views/public/js/rbbt.entity.basic.js +41 -17
- data/share/views/public/js/rbbt.knowledge_base.js +60 -9
- data/share/views/public/js/rbbt.page.js +0 -1
- data/share/views/public/js/rbbt.plots/rbbt.plots.aes.js +195 -0
- data/share/views/public/js/{rbbt.aesthetics.js → rbbt.plots/rbbt.plots.aesthetics.js} +0 -0
- data/share/views/public/js/rbbt.plots/rbbt.plots.basic.js +2 -0
- data/share/views/public/js/rbbt.plots/rbbt.plots.graph.adapters.js +216 -0
- data/share/views/public/js/rbbt.plots/rbbt.plots.graph.js +138 -0
- data/share/views/public/js/rbbt.plots/rbbt.plots.graph.kb.js +72 -0
- data/share/views/public/js/{rbbt.plots.js → rbbt.plots/rbbt.plots.list.js} +1 -4
- data/share/views/public/js/rbbt/actions.js +0 -1
- data/share/views/public/plugins/js-cookie/js/js.cookie.js +145 -0
- data/share/views/public/plugins/mithril/js/mithril.js +1819 -833
- metadata +10 -5
- data/share/views/public/js/rbbt.aes_plots.js +0 -381
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
rbbt.plots.graph.prepare_associations = function(db, associations, info){
|
3
|
+
var fields = ['source', 'target']
|
4
|
+
|
5
|
+
forArray(info.fields, function(f,i){
|
6
|
+
if (f == 'source') f = 'Source'
|
7
|
+
if (f == 'target') f = 'Target'
|
8
|
+
fields.push(f)
|
9
|
+
})
|
10
|
+
|
11
|
+
var edges = {}
|
12
|
+
edges.codes = []
|
13
|
+
edges.database = db
|
14
|
+
edges.source_type = info.source
|
15
|
+
edges.target_type = info.target
|
16
|
+
edges.source_info = info.source_entity_options
|
17
|
+
edges.target_info = info.target_entity_options
|
18
|
+
edges.info = {}
|
19
|
+
edges.info.database = db
|
20
|
+
edges.info.undirected = info.undirected
|
21
|
+
edges.properties = {}
|
22
|
+
edges.aes = {}
|
23
|
+
|
24
|
+
for (i in fields)
|
25
|
+
edges.info[fields[i]] = []
|
26
|
+
|
27
|
+
forHash(associations, function(code, values){
|
28
|
+
edges.codes.push(code)
|
29
|
+
for(i in values){
|
30
|
+
var field = fields[i]
|
31
|
+
var value = values[i]
|
32
|
+
edges.info[field].push(value)
|
33
|
+
}
|
34
|
+
})
|
35
|
+
|
36
|
+
edges.info['source'] = edges.info['source'].map(function(e){return e.replace(new RegExp('-\\.\\.-', 'g'), '~')})
|
37
|
+
edges.info['target'] = edges.info['target'].map(function(e){return e.replace(new RegExp('-\\.\\.-', 'g'), '~')})
|
38
|
+
edges.aes['source'] = edges.info['source']
|
39
|
+
edges.aes['target'] = edges.info['target']
|
40
|
+
edges.aes['database'] = db
|
41
|
+
|
42
|
+
return edges
|
43
|
+
}
|
44
|
+
rbbt.plots.graph.subset = function(db,source,target){
|
45
|
+
var promise = rbbt.entity_array.subset(db,source,target)
|
46
|
+
var db_info = KB.database_info(db)
|
47
|
+
return m.sync([promise,db_info]).then(function(d){
|
48
|
+
var associations = d[0]
|
49
|
+
var info = d[1]
|
50
|
+
return rbbt.plots.graph.prepare_associations(db, associations, info)
|
51
|
+
})
|
52
|
+
}
|
53
|
+
|
54
|
+
rbbt.plots.graph.children = function(db, source, type){
|
55
|
+
var promise = rbbt.entity_array.children(source,type,db)
|
56
|
+
var db_info = KB.database_info(db)
|
57
|
+
return m.sync([promise,db_info]).then(function(d){
|
58
|
+
var associations = d[0]
|
59
|
+
var info = d[1]
|
60
|
+
return rbbt.plots.graph.prepare_associations(db, associations, info)
|
61
|
+
})
|
62
|
+
}
|
63
|
+
|
64
|
+
rbbt.plots.graph.parents = function(db, source, type){
|
65
|
+
var promise = rbbt.entity_array.parents(source,type,db)
|
66
|
+
var db_info = KB.database_info(db)
|
67
|
+
return m.sync([promise,db_info]).then(function(d){
|
68
|
+
var associations = d[0]
|
69
|
+
var info = d[1]
|
70
|
+
return rbbt.plots.graph.prepare_associations(db, associations, info)
|
71
|
+
})
|
72
|
+
}
|
@@ -1,5 +1,3 @@
|
|
1
|
-
rbbt.plots = {}
|
2
|
-
|
3
1
|
rbbt.plots.list_plot = function(list, rules, create_obj){
|
4
2
|
var component = {}
|
5
3
|
component.create_obj = create_obj
|
@@ -248,6 +246,7 @@ rbbt.plots.d3js_graph = function(graph, object, node_obj){
|
|
248
246
|
.links(graph.links)
|
249
247
|
.start()
|
250
248
|
|
249
|
+
|
251
250
|
force.on("tick", function() {
|
252
251
|
link.attr("x1", function(d) { return d.source.x + 0*xsize/2; })
|
253
252
|
.attr("y1", function(d) { return d.source.y + 0*ysize/2; })
|
@@ -369,8 +368,6 @@ rbbt.plots.d3js_group_graph = function(graph, object, node_obj){
|
|
369
368
|
forArray(graph.nodes, function(node){ node.height = ysize + 2*pad; node.width=xsize + 2*pad})
|
370
369
|
var color = d3.scale.category20();
|
371
370
|
|
372
|
-
console.log(graph)
|
373
|
-
|
374
371
|
var svg = d3.select(object)
|
375
372
|
.attr("width", "100%")
|
376
373
|
.attr("height", height)
|
@@ -213,7 +213,6 @@ $.widget("rbbt.action_controller", {
|
|
213
213
|
|
214
214
|
_reload_action: function(e){
|
215
215
|
if($(e).hasClass('disabled')){ return false}
|
216
|
-
console.log(1)
|
217
216
|
var action_list_item = $(e);
|
218
217
|
var action_list = action_list_item.parent('.controls');
|
219
218
|
var action_controller = action_list.parent('.action_controller');
|
@@ -0,0 +1,145 @@
|
|
1
|
+
/*!
|
2
|
+
* JavaScript Cookie v2.1.0
|
3
|
+
* https://github.com/js-cookie/js-cookie
|
4
|
+
*
|
5
|
+
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
|
6
|
+
* Released under the MIT license
|
7
|
+
*/
|
8
|
+
(function (factory) {
|
9
|
+
if (typeof define === 'function' && define.amd) {
|
10
|
+
define(factory);
|
11
|
+
} else if (typeof exports === 'object') {
|
12
|
+
module.exports = factory();
|
13
|
+
} else {
|
14
|
+
var _OldCookies = window.Cookies;
|
15
|
+
var api = window.Cookies = factory();
|
16
|
+
api.noConflict = function () {
|
17
|
+
window.Cookies = _OldCookies;
|
18
|
+
return api;
|
19
|
+
};
|
20
|
+
}
|
21
|
+
}(function () {
|
22
|
+
function extend () {
|
23
|
+
var i = 0;
|
24
|
+
var result = {};
|
25
|
+
for (; i < arguments.length; i++) {
|
26
|
+
var attributes = arguments[ i ];
|
27
|
+
for (var key in attributes) {
|
28
|
+
result[key] = attributes[key];
|
29
|
+
}
|
30
|
+
}
|
31
|
+
return result;
|
32
|
+
}
|
33
|
+
|
34
|
+
function init (converter) {
|
35
|
+
function api (key, value, attributes) {
|
36
|
+
var result;
|
37
|
+
|
38
|
+
// Write
|
39
|
+
|
40
|
+
if (arguments.length > 1) {
|
41
|
+
attributes = extend({
|
42
|
+
path: '/'
|
43
|
+
}, api.defaults, attributes);
|
44
|
+
|
45
|
+
if (typeof attributes.expires === 'number') {
|
46
|
+
var expires = new Date();
|
47
|
+
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
|
48
|
+
attributes.expires = expires;
|
49
|
+
}
|
50
|
+
|
51
|
+
try {
|
52
|
+
result = JSON.stringify(value);
|
53
|
+
if (/^[\{\[]/.test(result)) {
|
54
|
+
value = result;
|
55
|
+
}
|
56
|
+
} catch (e) {}
|
57
|
+
|
58
|
+
if (!converter.write) {
|
59
|
+
value = encodeURIComponent(String(value))
|
60
|
+
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
|
61
|
+
} else {
|
62
|
+
value = converter.write(value, key);
|
63
|
+
}
|
64
|
+
|
65
|
+
key = encodeURIComponent(String(key));
|
66
|
+
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
|
67
|
+
key = key.replace(/[\(\)]/g, escape);
|
68
|
+
|
69
|
+
return (document.cookie = [
|
70
|
+
key, '=', value,
|
71
|
+
attributes.expires && '; expires=' + attributes.expires.toUTCString(), // use expires attribute, max-age is not supported by IE
|
72
|
+
attributes.path && '; path=' + attributes.path,
|
73
|
+
attributes.domain && '; domain=' + attributes.domain,
|
74
|
+
attributes.secure ? '; secure' : ''
|
75
|
+
].join(''));
|
76
|
+
}
|
77
|
+
|
78
|
+
// Read
|
79
|
+
|
80
|
+
if (!key) {
|
81
|
+
result = {};
|
82
|
+
}
|
83
|
+
|
84
|
+
// To prevent the for loop in the first place assign an empty array
|
85
|
+
// in case there are no cookies at all. Also prevents odd result when
|
86
|
+
// calling "get()"
|
87
|
+
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
88
|
+
var rdecode = /(%[0-9A-Z]{2})+/g;
|
89
|
+
var i = 0;
|
90
|
+
|
91
|
+
for (; i < cookies.length; i++) {
|
92
|
+
var parts = cookies[i].split('=');
|
93
|
+
var name = parts[0].replace(rdecode, decodeURIComponent);
|
94
|
+
var cookie = parts.slice(1).join('=');
|
95
|
+
|
96
|
+
if (cookie.charAt(0) === '"') {
|
97
|
+
cookie = cookie.slice(1, -1);
|
98
|
+
}
|
99
|
+
|
100
|
+
try {
|
101
|
+
cookie = converter.read ?
|
102
|
+
converter.read(cookie, name) : converter(cookie, name) ||
|
103
|
+
cookie.replace(rdecode, decodeURIComponent);
|
104
|
+
|
105
|
+
if (this.json) {
|
106
|
+
try {
|
107
|
+
cookie = JSON.parse(cookie);
|
108
|
+
} catch (e) {}
|
109
|
+
}
|
110
|
+
|
111
|
+
if (key === name) {
|
112
|
+
result = cookie;
|
113
|
+
break;
|
114
|
+
}
|
115
|
+
|
116
|
+
if (!key) {
|
117
|
+
result[name] = cookie;
|
118
|
+
}
|
119
|
+
} catch (e) {}
|
120
|
+
}
|
121
|
+
|
122
|
+
return result;
|
123
|
+
}
|
124
|
+
|
125
|
+
api.get = api.set = api;
|
126
|
+
api.getJSON = function () {
|
127
|
+
return api.apply({
|
128
|
+
json: true
|
129
|
+
}, [].slice.call(arguments));
|
130
|
+
};
|
131
|
+
api.defaults = {};
|
132
|
+
|
133
|
+
api.remove = function (key, attributes) {
|
134
|
+
api(key, '', extend(attributes, {
|
135
|
+
expires: -1
|
136
|
+
}));
|
137
|
+
};
|
138
|
+
|
139
|
+
api.withConverter = init;
|
140
|
+
|
141
|
+
return api;
|
142
|
+
}
|
143
|
+
|
144
|
+
return init(function () {});
|
145
|
+
}));
|
@@ -1,23 +1,80 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
var
|
5
|
-
|
6
|
-
|
1
|
+
;(function (global, factory) { // eslint-disable-line
|
2
|
+
"use strict"
|
3
|
+
/* eslint-disable no-undef */
|
4
|
+
var m = factory(global)
|
5
|
+
if (typeof module === "object" && module != null && module.exports) {
|
6
|
+
module.exports = m
|
7
|
+
} else if (typeof define === "function" && define.amd) {
|
8
|
+
define(function () { return m })
|
9
|
+
} else {
|
10
|
+
global.m = m
|
11
|
+
}
|
12
|
+
/* eslint-enable no-undef */
|
13
|
+
})(typeof window !== "undefined" ? window : {}, function (global, undefined) { // eslint-disable-line
|
14
|
+
"use strict"
|
15
|
+
|
16
|
+
m.version = function () {
|
17
|
+
return "v0.2.3"
|
18
|
+
}
|
19
|
+
|
20
|
+
var hasOwn = {}.hasOwnProperty
|
21
|
+
var type = {}.toString
|
22
|
+
|
23
|
+
function isFunction(object) {
|
24
|
+
return typeof object === "function"
|
25
|
+
}
|
26
|
+
|
27
|
+
function isObject(object) {
|
28
|
+
return type.call(object) === "[object Object]"
|
29
|
+
}
|
30
|
+
|
31
|
+
function isString(object) {
|
32
|
+
return type.call(object) === "[object String]"
|
33
|
+
}
|
34
|
+
|
35
|
+
var isArray = Array.isArray || function (object) {
|
36
|
+
return type.call(object) === "[object Array]"
|
37
|
+
}
|
38
|
+
|
39
|
+
function noop() {}
|
40
|
+
|
41
|
+
var voidElements = {
|
42
|
+
AREA: 1,
|
43
|
+
BASE: 1,
|
44
|
+
BR: 1,
|
45
|
+
COL: 1,
|
46
|
+
COMMAND: 1,
|
47
|
+
EMBED: 1,
|
48
|
+
HR: 1,
|
49
|
+
IMG: 1,
|
50
|
+
INPUT: 1,
|
51
|
+
KEYGEN: 1,
|
52
|
+
LINK: 1,
|
53
|
+
META: 1,
|
54
|
+
PARAM: 1,
|
55
|
+
SOURCE: 1,
|
56
|
+
TRACK: 1,
|
57
|
+
WBR: 1
|
58
|
+
}
|
7
59
|
|
8
60
|
// caching commonly used variables
|
9
|
-
var $document, $location, $requestAnimationFrame, $cancelAnimationFrame
|
61
|
+
var $document, $location, $requestAnimationFrame, $cancelAnimationFrame
|
10
62
|
|
11
63
|
// self invoking function needed because of the way mocks work
|
12
|
-
function initialize(
|
13
|
-
$document =
|
14
|
-
$location =
|
15
|
-
$cancelAnimationFrame =
|
16
|
-
$requestAnimationFrame =
|
64
|
+
function initialize(mock) {
|
65
|
+
$document = mock.document
|
66
|
+
$location = mock.location
|
67
|
+
$cancelAnimationFrame = mock.cancelAnimationFrame || mock.clearTimeout
|
68
|
+
$requestAnimationFrame = mock.requestAnimationFrame || mock.setTimeout
|
17
69
|
}
|
18
70
|
|
19
|
-
|
71
|
+
// testing API
|
72
|
+
m.deps = function (mock) {
|
73
|
+
initialize(global = mock || window)
|
74
|
+
return global
|
75
|
+
}
|
20
76
|
|
77
|
+
m.deps(global)
|
21
78
|
|
22
79
|
/**
|
23
80
|
* @typedef {String} Tag
|
@@ -25,957 +82,1825 @@ var m = (function app(window, undefined) {
|
|
25
82
|
* Which describes a DOM node
|
26
83
|
*/
|
27
84
|
|
85
|
+
function parseTagAttrs(cell, tag) {
|
86
|
+
var classes = []
|
87
|
+
var parser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g
|
88
|
+
var match
|
89
|
+
|
90
|
+
while ((match = parser.exec(tag))) {
|
91
|
+
if (match[1] === "" && match[2]) {
|
92
|
+
cell.tag = match[2]
|
93
|
+
} else if (match[1] === "#") {
|
94
|
+
cell.attrs.id = match[2]
|
95
|
+
} else if (match[1] === ".") {
|
96
|
+
classes.push(match[2])
|
97
|
+
} else if (match[3][0] === "[") {
|
98
|
+
var pair = /\[(.+?)(?:=("|'|)(.*?)\2)?\]/.exec(match[3])
|
99
|
+
cell.attrs[pair[1]] = pair[3] || (pair[2] ? "" : true)
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
return classes
|
104
|
+
}
|
105
|
+
|
106
|
+
function getVirtualChildren(args, hasAttrs) {
|
107
|
+
var children = hasAttrs ? args.slice(1) : args
|
108
|
+
|
109
|
+
if (children.length === 1 && isArray(children[0])) {
|
110
|
+
return children[0]
|
111
|
+
} else {
|
112
|
+
return children
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
function assignAttrs(target, attrs, classes) {
|
117
|
+
var classAttr = "class" in attrs ? "class" : "className"
|
118
|
+
|
119
|
+
for (var attrName in attrs) {
|
120
|
+
if (hasOwn.call(attrs, attrName)) {
|
121
|
+
if (attrName === classAttr &&
|
122
|
+
attrs[attrName] != null &&
|
123
|
+
attrs[attrName] !== "") {
|
124
|
+
classes.push(attrs[attrName])
|
125
|
+
// create key in correct iteration order
|
126
|
+
target[attrName] = ""
|
127
|
+
} else {
|
128
|
+
target[attrName] = attrs[attrName]
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
if (classes.length) target[classAttr] = classes.join(" ")
|
134
|
+
}
|
135
|
+
|
28
136
|
/**
|
29
137
|
*
|
30
138
|
* @param {Tag} The DOM node tag
|
31
139
|
* @param {Object=[]} optional key-value pairs to be mapped to DOM attrs
|
32
|
-
* @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array,
|
33
|
-
*
|
140
|
+
* @param {...mNode=[]} Zero or more Mithril child nodes. Can be an array,
|
141
|
+
* or splat (optional)
|
34
142
|
*/
|
35
|
-
function m() {
|
36
|
-
var args = [].slice.call(arguments)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
while (match = parser.exec(args[0])) {
|
44
|
-
if (match[1] === "" && match[2]) cell.tag = match[2];
|
45
|
-
else if (match[1] === "#") cell.attrs.id = match[2];
|
46
|
-
else if (match[1] === ".") classes.push(match[2]);
|
47
|
-
else if (match[3][0] === "[") {
|
48
|
-
var pair = attrParser.exec(match[3]);
|
49
|
-
cell.attrs[pair[1]] = pair[3] || (pair[2] ? "" :true)
|
50
|
-
}
|
143
|
+
function m(tag, pairs) {
|
144
|
+
var args = [].slice.call(arguments, 1)
|
145
|
+
|
146
|
+
if (isObject(tag)) return parameterize(tag, args)
|
147
|
+
|
148
|
+
if (!isString(tag)) {
|
149
|
+
throw new Error("selector in m(selector, attrs, children) should " +
|
150
|
+
"be a string")
|
51
151
|
}
|
52
152
|
|
53
|
-
var
|
54
|
-
|
55
|
-
|
153
|
+
var hasAttrs = pairs != null && isObject(pairs) &&
|
154
|
+
!("tag" in pairs || "view" in pairs || "subtree" in pairs)
|
155
|
+
|
156
|
+
var attrs = hasAttrs ? pairs : {}
|
157
|
+
var cell = {
|
158
|
+
tag: "div",
|
159
|
+
attrs: {},
|
160
|
+
children: getVirtualChildren(args, hasAttrs)
|
161
|
+
}
|
162
|
+
|
163
|
+
assignAttrs(cell.attrs, attrs, parseTagAttrs(cell, tag))
|
164
|
+
return cell
|
165
|
+
}
|
166
|
+
|
167
|
+
function forEach(list, f) {
|
168
|
+
for (var i = 0; i < list.length && !f(list[i], i++);) {
|
169
|
+
// function called in condition
|
56
170
|
}
|
57
|
-
|
58
|
-
|
171
|
+
}
|
172
|
+
|
173
|
+
function forKeys(list, f) {
|
174
|
+
forEach(list, function (attrs, i) {
|
175
|
+
return (attrs = attrs && attrs.attrs) &&
|
176
|
+
attrs.key != null &&
|
177
|
+
f(attrs, i)
|
178
|
+
})
|
179
|
+
}
|
180
|
+
// This function was causing deopts in Chrome.
|
181
|
+
function dataToString(data) {
|
182
|
+
// data.toString() might throw or return null if data is the return
|
183
|
+
// value of Console.log in some versions of Firefox (behavior depends on
|
184
|
+
// version)
|
185
|
+
try {
|
186
|
+
if (data != null && data.toString() != null) return data
|
187
|
+
} catch (e) {
|
188
|
+
// silently ignore errors
|
59
189
|
}
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
190
|
+
return ""
|
191
|
+
}
|
192
|
+
|
193
|
+
// This function was causing deopts in Chrome.
|
194
|
+
function injectTextNode(parentElement, first, index, data) {
|
195
|
+
try {
|
196
|
+
insertNode(parentElement, first, index)
|
197
|
+
first.nodeValue = data
|
198
|
+
} catch (e) {
|
199
|
+
// IE erroneously throws error when appending an empty text node
|
200
|
+
// after a null
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
function flatten(list) {
|
205
|
+
// recursively flatten array
|
206
|
+
for (var i = 0; i < list.length; i++) {
|
207
|
+
if (isArray(list[i])) {
|
208
|
+
list = list.concat.apply([], list)
|
209
|
+
// check current index again and flatten until there are no more
|
210
|
+
// nested arrays at that index
|
211
|
+
i--
|
68
212
|
}
|
69
213
|
}
|
70
|
-
|
71
|
-
|
72
|
-
return cell
|
214
|
+
return list
|
73
215
|
}
|
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
|
-
else if (cached.nodes) clear(cached.nodes, cached)
|
216
|
+
|
217
|
+
function insertNode(parentElement, node, index) {
|
218
|
+
parentElement.insertBefore(node,
|
219
|
+
parentElement.childNodes[index] || null)
|
220
|
+
}
|
221
|
+
|
222
|
+
var DELETION = 1
|
223
|
+
var INSERTION = 2
|
224
|
+
var MOVE = 3
|
225
|
+
|
226
|
+
function handleKeysDiffer(data, existing, cached, parentElement) {
|
227
|
+
forKeys(data, function (key, i) {
|
228
|
+
existing[key = key.key] = existing[key] ? {
|
229
|
+
action: MOVE,
|
230
|
+
index: i,
|
231
|
+
from: existing[key].index,
|
232
|
+
element: cached.nodes[existing[key].index] ||
|
233
|
+
$document.createElement("div")
|
234
|
+
} : {action: INSERTION, index: i}
|
235
|
+
})
|
236
|
+
|
237
|
+
var actions = []
|
238
|
+
for (var prop in existing) if (hasOwn.call(existing, prop)) {
|
239
|
+
actions.push(existing[prop])
|
240
|
+
}
|
241
|
+
|
242
|
+
var changes = actions.sort(sortChanges)
|
243
|
+
var newCached = new Array(cached.length)
|
244
|
+
|
245
|
+
newCached.nodes = cached.nodes.slice()
|
246
|
+
|
247
|
+
forEach(changes, function (change) {
|
248
|
+
var index = change.index
|
249
|
+
if (change.action === DELETION) {
|
250
|
+
clear(cached[index].nodes, cached[index])
|
251
|
+
newCached.splice(index, 1)
|
112
252
|
}
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
data = data.concat.apply([], data);
|
123
|
-
i-- //check current index again and flatten until there are no more nested arrays at that index
|
124
|
-
len = data.length
|
125
|
-
}
|
253
|
+
if (change.action === INSERTION) {
|
254
|
+
var dummy = $document.createElement("div")
|
255
|
+
dummy.key = data[index].attrs.key
|
256
|
+
insertNode(parentElement, dummy, index)
|
257
|
+
newCached.splice(index, 0, {
|
258
|
+
attrs: {key: data[index].attrs.key},
|
259
|
+
nodes: [dummy]
|
260
|
+
})
|
261
|
+
newCached.nodes[index] = dummy
|
126
262
|
}
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
//4) for each key, handle its corresponding action as marked in previous steps
|
135
|
-
var DELETION = 1, INSERTION = 2 , MOVE = 3;
|
136
|
-
var existing = {}, shouldMaintainIdentities = false;
|
137
|
-
for (var i = 0; i < cached.length; i++) {
|
138
|
-
if (cached[i] && cached[i].attrs && cached[i].attrs.key != null) {
|
139
|
-
shouldMaintainIdentities = true;
|
140
|
-
existing[cached[i].attrs.key] = {action: DELETION, index: i}
|
263
|
+
|
264
|
+
if (change.action === MOVE) {
|
265
|
+
var changeElement = change.element
|
266
|
+
var maybeChanged = parentElement.childNodes[index]
|
267
|
+
if (maybeChanged !== changeElement && changeElement !== null) {
|
268
|
+
parentElement.insertBefore(changeElement,
|
269
|
+
maybeChanged || null)
|
141
270
|
}
|
271
|
+
newCached[index] = cached[change.from]
|
272
|
+
newCached.nodes[index] = changeElement
|
142
273
|
}
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
274
|
+
})
|
275
|
+
|
276
|
+
return newCached
|
277
|
+
}
|
278
|
+
|
279
|
+
function diffKeys(data, cached, existing, parentElement) {
|
280
|
+
var keysDiffer = data.length !== cached.length
|
281
|
+
|
282
|
+
if (!keysDiffer) {
|
283
|
+
forKeys(data, function (attrs, i) {
|
284
|
+
var cachedCell = cached[i]
|
285
|
+
return keysDiffer = cachedCell &&
|
286
|
+
cachedCell.attrs &&
|
287
|
+
cachedCell.attrs.key !== attrs.key
|
288
|
+
})
|
289
|
+
}
|
290
|
+
|
291
|
+
if (keysDiffer) {
|
292
|
+
return handleKeysDiffer(data, existing, cached, parentElement)
|
293
|
+
} else {
|
294
|
+
return cached
|
295
|
+
}
|
296
|
+
}
|
297
|
+
|
298
|
+
function diffArray(data, cached, nodes) {
|
299
|
+
// diff the array itself
|
300
|
+
|
301
|
+
// update the list of DOM nodes by collecting the nodes from each item
|
302
|
+
forEach(data, function (_, i) {
|
303
|
+
if (cached[i] != null) nodes.push.apply(nodes, cached[i].nodes)
|
304
|
+
})
|
305
|
+
// remove items from the end of the array if the new array is shorter
|
306
|
+
// than the old one. if errors ever happen here, the issue is most
|
307
|
+
// likely a bug in the construction of the `cached` data structure
|
308
|
+
// somewhere earlier in the program
|
309
|
+
forEach(cached.nodes, function (node, i) {
|
310
|
+
if (node.parentNode != null && nodes.indexOf(node) < 0) {
|
311
|
+
clear([node], [cached[i]])
|
152
312
|
}
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
for (var i = 0, len = data.length; i < len; i++) {
|
166
|
-
if (data[i] && data[i].attrs) {
|
167
|
-
if (data[i].attrs.key != null) {
|
168
|
-
var key = data[i].attrs.key;
|
169
|
-
if (!existing[key]) existing[key] = {action: INSERTION, index: i};
|
170
|
-
else existing[key] = {
|
171
|
-
action: MOVE,
|
172
|
-
index: i,
|
173
|
-
from: existing[key].index,
|
174
|
-
element: cached.nodes[existing[key].index] || $document.createElement("div")
|
175
|
-
}
|
176
|
-
}
|
177
|
-
}
|
178
|
-
}
|
179
|
-
var actions = []
|
180
|
-
for (var prop in existing) actions.push(existing[prop])
|
181
|
-
var changes = actions.sort(sortChanges);
|
182
|
-
var newCached = new Array(cached.length)
|
183
|
-
newCached.nodes = cached.nodes.slice()
|
184
|
-
|
185
|
-
for (var i = 0, change; change = changes[i]; i++) {
|
186
|
-
if (change.action === DELETION) {
|
187
|
-
clear(cached[change.index].nodes, cached[change.index]);
|
188
|
-
newCached.splice(change.index, 1)
|
189
|
-
}
|
190
|
-
if (change.action === INSERTION) {
|
191
|
-
var dummy = $document.createElement("div");
|
192
|
-
dummy.key = data[change.index].attrs.key;
|
193
|
-
parentElement.insertBefore(dummy, parentElement.childNodes[change.index] || null);
|
194
|
-
newCached.splice(change.index, 0, {attrs: {key: data[change.index].attrs.key}, nodes: [dummy]})
|
195
|
-
newCached.nodes[change.index] = dummy
|
196
|
-
}
|
197
|
-
|
198
|
-
if (change.action === MOVE) {
|
199
|
-
if (parentElement.childNodes[change.index] !== change.element && change.element !== null) {
|
200
|
-
parentElement.insertBefore(change.element, parentElement.childNodes[change.index] || null)
|
201
|
-
}
|
202
|
-
newCached[change.index] = cached[change.from]
|
203
|
-
newCached.nodes[change.index] = change.element
|
204
|
-
}
|
205
|
-
}
|
206
|
-
cached = newCached;
|
313
|
+
})
|
314
|
+
|
315
|
+
if (data.length < cached.length) cached.length = data.length
|
316
|
+
cached.nodes = nodes
|
317
|
+
}
|
318
|
+
|
319
|
+
function buildArrayKeys(data) {
|
320
|
+
var guid = 0
|
321
|
+
forKeys(data, function () {
|
322
|
+
forEach(data, function (attrs) {
|
323
|
+
if ((attrs = attrs && attrs.attrs) && attrs.key == null) {
|
324
|
+
attrs.key = "__mithril__" + guid++
|
207
325
|
}
|
326
|
+
})
|
327
|
+
return 1
|
328
|
+
})
|
329
|
+
}
|
330
|
+
|
331
|
+
function isDifferentEnough(data, cached, dataAttrKeys) {
|
332
|
+
if (data.tag !== cached.tag) return true
|
333
|
+
|
334
|
+
if (dataAttrKeys.sort().join() !==
|
335
|
+
Object.keys(cached.attrs).sort().join()) {
|
336
|
+
return true
|
337
|
+
}
|
338
|
+
|
339
|
+
if (data.attrs.id !== cached.attrs.id) {
|
340
|
+
return true
|
341
|
+
}
|
342
|
+
|
343
|
+
if (data.attrs.key !== cached.attrs.key) {
|
344
|
+
return true
|
345
|
+
}
|
346
|
+
|
347
|
+
if (m.redraw.strategy() === "all") {
|
348
|
+
return !cached.configContext || cached.configContext.retain !== true
|
349
|
+
}
|
350
|
+
|
351
|
+
if (m.redraw.strategy() === "diff") {
|
352
|
+
return cached.configContext && cached.configContext.retain === false
|
353
|
+
}
|
354
|
+
|
355
|
+
return false
|
356
|
+
}
|
357
|
+
|
358
|
+
function maybeRecreateObject(data, cached, dataAttrKeys) {
|
359
|
+
// if an element is different enough from the one in cache, recreate it
|
360
|
+
if (isDifferentEnough(data, cached, dataAttrKeys)) {
|
361
|
+
if (cached.nodes.length) clear(cached.nodes)
|
362
|
+
|
363
|
+
if (cached.configContext &&
|
364
|
+
isFunction(cached.configContext.onunload)) {
|
365
|
+
cached.configContext.onunload()
|
208
366
|
}
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
if (item === undefined) continue;
|
215
|
-
if (!item.nodes.intact) intact = false;
|
216
|
-
if (item.$trusted) {
|
217
|
-
//fix offset of next element if item was a trusted string w/ more than one html element
|
218
|
-
//the first clause in the regexp matches elements
|
219
|
-
//the second clause (after the pipe) matches text nodes
|
220
|
-
subArrayCount += (item.match(/<[^\/]|\>\s*[^<]/g) || [0]).length
|
221
|
-
}
|
222
|
-
else subArrayCount += type.call(item) === ARRAY ? item.length : 1;
|
223
|
-
cached[cacheCount++] = item
|
367
|
+
|
368
|
+
if (cached.controllers) {
|
369
|
+
forEach(cached.controllers, function (controller) {
|
370
|
+
if (controller.onunload) controller.onunload({preventDefault: noop});
|
371
|
+
});
|
224
372
|
}
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
373
|
+
}
|
374
|
+
}
|
375
|
+
|
376
|
+
function getObjectNamespace(data, namespace) {
|
377
|
+
if (data.attrs.xmlns) return data.attrs.xmlns
|
378
|
+
if (data.tag === "svg") return "http://www.w3.org/2000/svg"
|
379
|
+
if (data.tag === "math") return "http://www.w3.org/1998/Math/MathML"
|
380
|
+
return namespace
|
381
|
+
}
|
382
|
+
|
383
|
+
var pendingRequests = 0
|
384
|
+
m.startComputation = function () { pendingRequests++ }
|
385
|
+
m.endComputation = function () {
|
386
|
+
if (pendingRequests > 1) {
|
387
|
+
pendingRequests--
|
388
|
+
} else {
|
389
|
+
pendingRequests = 0
|
390
|
+
m.redraw()
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
function unloadCachedControllers(cached, views, controllers) {
|
395
|
+
if (controllers.length) {
|
396
|
+
cached.views = views
|
397
|
+
cached.controllers = controllers
|
398
|
+
forEach(controllers, function (controller) {
|
399
|
+
if (controller.onunload && controller.onunload.$old) {
|
400
|
+
controller.onunload = controller.onunload.$old
|
231
401
|
}
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
402
|
+
|
403
|
+
if (pendingRequests && controller.onunload) {
|
404
|
+
var onunload = controller.onunload
|
405
|
+
controller.onunload = noop
|
406
|
+
controller.onunload.$old = onunload
|
236
407
|
}
|
237
|
-
|
238
|
-
|
408
|
+
})
|
409
|
+
}
|
410
|
+
}
|
411
|
+
|
412
|
+
function scheduleConfigsToBeCalled(configs, data, node, isNew, cached) {
|
413
|
+
// schedule configs to be called. They are called after `build` finishes
|
414
|
+
// running
|
415
|
+
if (isFunction(data.attrs.config)) {
|
416
|
+
var context = cached.configContext = cached.configContext || {}
|
417
|
+
|
418
|
+
// bind
|
419
|
+
configs.push(function () {
|
420
|
+
return data.attrs.config.call(data, node, !isNew, context,
|
421
|
+
cached)
|
422
|
+
})
|
423
|
+
}
|
424
|
+
}
|
425
|
+
|
426
|
+
function buildUpdatedNode(
|
427
|
+
cached,
|
428
|
+
data,
|
429
|
+
editable,
|
430
|
+
hasKeys,
|
431
|
+
namespace,
|
432
|
+
views,
|
433
|
+
configs,
|
434
|
+
controllers
|
435
|
+
) {
|
436
|
+
var node = cached.nodes[0]
|
437
|
+
|
438
|
+
if (hasKeys) {
|
439
|
+
setAttributes(node, data.tag, data.attrs, cached.attrs, namespace)
|
440
|
+
}
|
441
|
+
|
442
|
+
cached.children = build(
|
443
|
+
node,
|
444
|
+
data.tag,
|
445
|
+
undefined,
|
446
|
+
undefined,
|
447
|
+
data.children,
|
448
|
+
cached.children,
|
449
|
+
false,
|
450
|
+
0,
|
451
|
+
data.attrs.contenteditable ? node : editable,
|
452
|
+
namespace,
|
453
|
+
configs
|
454
|
+
)
|
455
|
+
|
456
|
+
cached.nodes.intact = true
|
457
|
+
|
458
|
+
if (controllers.length) {
|
459
|
+
cached.views = views
|
460
|
+
cached.controllers = controllers
|
461
|
+
}
|
462
|
+
|
463
|
+
return node
|
464
|
+
}
|
465
|
+
|
466
|
+
function handleNonexistentNodes(data, parentElement, index) {
|
467
|
+
var nodes
|
468
|
+
if (data.$trusted) {
|
469
|
+
nodes = injectHTML(parentElement, index, data)
|
470
|
+
} else {
|
471
|
+
nodes = [$document.createTextNode(data)]
|
472
|
+
if (!(parentElement.nodeName in voidElements)) {
|
473
|
+
insertNode(parentElement, nodes[0], index)
|
239
474
|
}
|
240
475
|
}
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
476
|
+
|
477
|
+
var cached
|
478
|
+
|
479
|
+
if (typeof data === "string" ||
|
480
|
+
typeof data === "number" ||
|
481
|
+
typeof data === "boolean") {
|
482
|
+
cached = new data.constructor(data)
|
483
|
+
} else {
|
484
|
+
cached = data
|
485
|
+
}
|
486
|
+
|
487
|
+
cached.nodes = nodes
|
488
|
+
return cached
|
489
|
+
}
|
490
|
+
|
491
|
+
function reattachNodes(
|
492
|
+
data,
|
493
|
+
cached,
|
494
|
+
parentElement,
|
495
|
+
editable,
|
496
|
+
index,
|
497
|
+
parentTag
|
498
|
+
) {
|
499
|
+
var nodes = cached.nodes
|
500
|
+
if (!editable || editable !== $document.activeElement) {
|
501
|
+
if (data.$trusted) {
|
502
|
+
clear(nodes, cached)
|
503
|
+
nodes = injectHTML(parentElement, index, data)
|
504
|
+
} else if (parentTag === "textarea") {
|
505
|
+
// <textarea> uses `value` instead of `nodeValue`.
|
506
|
+
parentElement.value = data
|
507
|
+
} else if (editable) {
|
508
|
+
// contenteditable nodes use `innerHTML` instead of `nodeValue`.
|
509
|
+
editable.innerHTML = data
|
510
|
+
} else {
|
511
|
+
// was a trusted string
|
512
|
+
if (nodes[0].nodeType === 1 || nodes.length > 1 ||
|
513
|
+
(nodes[0].nodeValue.trim &&
|
514
|
+
!nodes[0].nodeValue.trim())) {
|
515
|
+
clear(cached.nodes, cached)
|
516
|
+
nodes = [$document.createTextNode(data)]
|
253
517
|
}
|
254
|
-
|
255
|
-
|
256
|
-
controllers.push(controller)
|
518
|
+
|
519
|
+
injectTextNode(parentElement, nodes[0], index, data)
|
257
520
|
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
521
|
+
}
|
522
|
+
cached = new data.constructor(data)
|
523
|
+
cached.nodes = nodes
|
524
|
+
return cached
|
525
|
+
}
|
526
|
+
|
527
|
+
function handleTextNode(
|
528
|
+
cached,
|
529
|
+
data,
|
530
|
+
index,
|
531
|
+
parentElement,
|
532
|
+
shouldReattach,
|
533
|
+
editable,
|
534
|
+
parentTag
|
535
|
+
) {
|
536
|
+
if (!cached.nodes.length) {
|
537
|
+
return handleNonexistentNodes(data, parentElement, index)
|
538
|
+
} else if (cached.valueOf() !== data.valueOf() || shouldReattach) {
|
539
|
+
return reattachNodes(data, cached, parentElement, editable, index,
|
540
|
+
parentTag)
|
541
|
+
} else {
|
542
|
+
return (cached.nodes.intact = true, cached)
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
function getSubArrayCount(item) {
|
547
|
+
if (item.$trusted) {
|
548
|
+
// fix offset of next element if item was a trusted string w/ more
|
549
|
+
// than one html element
|
550
|
+
// the first clause in the regexp matches elements
|
551
|
+
// the second clause (after the pipe) matches text nodes
|
552
|
+
var match = item.match(/<[^\/]|\>\s*[^<]/g)
|
553
|
+
if (match != null) return match.length
|
554
|
+
} else if (isArray(item)) {
|
555
|
+
return item.length
|
556
|
+
}
|
557
|
+
return 1
|
558
|
+
}
|
559
|
+
|
560
|
+
function buildArray(
|
561
|
+
data,
|
562
|
+
cached,
|
563
|
+
parentElement,
|
564
|
+
index,
|
565
|
+
parentTag,
|
566
|
+
shouldReattach,
|
567
|
+
editable,
|
568
|
+
namespace,
|
569
|
+
configs
|
570
|
+
) {
|
571
|
+
data = flatten(data)
|
572
|
+
var nodes = []
|
573
|
+
var intact = cached.length === data.length
|
574
|
+
var subArrayCount = 0
|
575
|
+
|
576
|
+
// keys algorithm: sort elements without recreating them if keys are
|
577
|
+
// present
|
578
|
+
//
|
579
|
+
// 1) create a map of all existing keys, and mark all for deletion
|
580
|
+
// 2) add new keys to map and mark them for addition
|
581
|
+
// 3) if key exists in new list, change action from deletion to a move
|
582
|
+
// 4) for each key, handle its corresponding action as marked in
|
583
|
+
// previous steps
|
584
|
+
|
585
|
+
var existing = {}
|
586
|
+
var shouldMaintainIdentities = false
|
587
|
+
|
588
|
+
forKeys(cached, function (attrs, i) {
|
589
|
+
shouldMaintainIdentities = true
|
590
|
+
existing[cached[i].attrs.key] = {action: DELETION, index: i}
|
591
|
+
})
|
592
|
+
|
593
|
+
buildArrayKeys(data)
|
594
|
+
if (shouldMaintainIdentities) {
|
595
|
+
cached = diffKeys(data, cached, existing, parentElement)
|
596
|
+
}
|
597
|
+
// end key algorithm
|
598
|
+
|
599
|
+
var cacheCount = 0
|
600
|
+
// faster explicitly written
|
601
|
+
for (var i = 0, len = data.length; i < len; i++) {
|
602
|
+
// diff each item in the array
|
603
|
+
var item = build(
|
604
|
+
parentElement,
|
605
|
+
parentTag,
|
606
|
+
cached,
|
607
|
+
index,
|
608
|
+
data[i],
|
609
|
+
cached[cacheCount],
|
610
|
+
shouldReattach,
|
611
|
+
index + subArrayCount || subArrayCount,
|
612
|
+
editable,
|
613
|
+
namespace,
|
614
|
+
configs)
|
615
|
+
|
616
|
+
if (item !== undefined) {
|
617
|
+
intact = intact && item.nodes.intact
|
618
|
+
subArrayCount += getSubArrayCount(item)
|
619
|
+
cached[cacheCount++] = item
|
273
620
|
}
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
};
|
293
|
-
if (controllers.length) {
|
294
|
-
cached.views = views
|
295
|
-
cached.controllers = controllers
|
296
|
-
for (var i = 0, controller; controller = controllers[i]; i++) {
|
297
|
-
if (controller.onunload && controller.onunload.$old) controller.onunload = controller.onunload.$old
|
298
|
-
if (pendingRequests && controller.onunload) {
|
299
|
-
var onunload = controller.onunload
|
300
|
-
controller.onunload = noop
|
301
|
-
controller.onunload.$old = onunload
|
302
|
-
}
|
303
|
-
}
|
304
|
-
}
|
305
|
-
|
306
|
-
if (cached.children && !cached.children.nodes) cached.children.nodes = [];
|
307
|
-
//edge case: setting value on <select> doesn't work before children exist, so set it again after children have been created
|
308
|
-
if (data.tag === "select" && "value" in data.attrs) setAttributes(node, data.tag, {value: data.attrs.value}, {}, namespace);
|
309
|
-
parentElement.insertBefore(node, parentElement.childNodes[index] || null)
|
621
|
+
}
|
622
|
+
|
623
|
+
if (!intact) diffArray(data, cached, nodes)
|
624
|
+
return cached
|
625
|
+
}
|
626
|
+
|
627
|
+
function makeCache(data, cached, index, parentIndex, parentCache) {
|
628
|
+
if (cached != null) {
|
629
|
+
if (type.call(cached) === type.call(data)) return cached
|
630
|
+
|
631
|
+
if (parentCache && parentCache.nodes) {
|
632
|
+
var offset = index - parentIndex
|
633
|
+
var end = offset + (isArray(data) ? data : cached.nodes).length
|
634
|
+
clear(
|
635
|
+
parentCache.nodes.slice(offset, end),
|
636
|
+
parentCache.slice(offset, end))
|
637
|
+
} else if (cached.nodes) {
|
638
|
+
clear(cached.nodes, cached)
|
310
639
|
}
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
640
|
+
}
|
641
|
+
|
642
|
+
cached = new data.constructor()
|
643
|
+
// if constructor creates a virtual dom element, use a blank object as
|
644
|
+
// the base cached node instead of copying the virtual el (#277)
|
645
|
+
if (cached.tag) cached = {}
|
646
|
+
cached.nodes = []
|
647
|
+
return cached
|
648
|
+
}
|
649
|
+
|
650
|
+
function constructNode(data, namespace) {
|
651
|
+
if (data.attrs.is) {
|
652
|
+
if (namespace == null) {
|
653
|
+
return $document.createElement(data.tag, data.attrs.is)
|
654
|
+
} else {
|
655
|
+
return $document.createElementNS(namespace, data.tag,
|
656
|
+
data.attrs.is)
|
321
657
|
}
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
658
|
+
} else if (namespace == null) {
|
659
|
+
return $document.createElement(data.tag)
|
660
|
+
} else {
|
661
|
+
return $document.createElementNS(namespace, data.tag)
|
662
|
+
}
|
663
|
+
}
|
664
|
+
|
665
|
+
function constructAttrs(data, node, namespace, hasKeys) {
|
666
|
+
if (hasKeys) {
|
667
|
+
return setAttributes(node, data.tag, data.attrs, {}, namespace)
|
668
|
+
} else {
|
669
|
+
return data.attrs
|
670
|
+
}
|
671
|
+
}
|
672
|
+
|
673
|
+
function constructChildren(
|
674
|
+
data,
|
675
|
+
node,
|
676
|
+
cached,
|
677
|
+
editable,
|
678
|
+
namespace,
|
679
|
+
configs
|
680
|
+
) {
|
681
|
+
if (data.children != null && data.children.length > 0) {
|
682
|
+
return build(
|
683
|
+
node,
|
684
|
+
data.tag,
|
685
|
+
undefined,
|
686
|
+
undefined,
|
687
|
+
data.children,
|
688
|
+
cached.children,
|
689
|
+
true,
|
690
|
+
0,
|
691
|
+
data.attrs.contenteditable ? node : editable,
|
692
|
+
namespace,
|
693
|
+
configs)
|
694
|
+
} else {
|
695
|
+
return data.children
|
696
|
+
}
|
697
|
+
}
|
698
|
+
|
699
|
+
function reconstructCached(
|
700
|
+
data,
|
701
|
+
attrs,
|
702
|
+
children,
|
703
|
+
node,
|
704
|
+
namespace,
|
705
|
+
views,
|
706
|
+
controllers
|
707
|
+
) {
|
708
|
+
var cached = {
|
709
|
+
tag: data.tag,
|
710
|
+
attrs: attrs,
|
711
|
+
children: children,
|
712
|
+
nodes: [node]
|
713
|
+
}
|
714
|
+
|
715
|
+
unloadCachedControllers(cached, views, controllers)
|
716
|
+
|
717
|
+
if (cached.children && !cached.children.nodes) {
|
718
|
+
cached.children.nodes = []
|
719
|
+
}
|
720
|
+
|
721
|
+
// edge case: setting value on <select> doesn't work before children
|
722
|
+
// exist, so set it again after children have been created
|
723
|
+
if (data.tag === "select" && "value" in data.attrs) {
|
724
|
+
setAttributes(node, data.tag, {value: data.attrs.value}, {},
|
725
|
+
namespace)
|
726
|
+
}
|
727
|
+
|
728
|
+
return cached
|
729
|
+
}
|
730
|
+
|
731
|
+
function getController(views, view, cachedControllers, controller) {
|
732
|
+
var controllerIndex
|
733
|
+
|
734
|
+
if (m.redraw.strategy() === "diff" && views) {
|
735
|
+
controllerIndex = views.indexOf(view)
|
736
|
+
} else {
|
737
|
+
controllerIndex = -1
|
738
|
+
}
|
739
|
+
|
740
|
+
if (controllerIndex > -1) {
|
741
|
+
return cachedControllers[controllerIndex]
|
742
|
+
} else if (isFunction(controller)) {
|
743
|
+
return new controller()
|
744
|
+
} else {
|
745
|
+
return {}
|
746
|
+
}
|
747
|
+
}
|
748
|
+
|
749
|
+
var unloaders = []
|
750
|
+
|
751
|
+
function updateLists(views, controllers, view, controller) {
|
752
|
+
if (controller.onunload != null && unloaders.map(function(u) {return u.handler}).indexOf(controller.onunload) < 0) {
|
753
|
+
unloaders.push({
|
754
|
+
controller: controller,
|
755
|
+
handler: controller.onunload
|
756
|
+
})
|
757
|
+
}
|
758
|
+
|
759
|
+
views.push(view)
|
760
|
+
controllers.push(controller)
|
761
|
+
}
|
762
|
+
|
763
|
+
var forcing = false
|
764
|
+
function checkView(data, view, cached, cachedControllers, controllers, views) {
|
765
|
+
var controller = getController(cached.views, view, cachedControllers, data.controller)
|
766
|
+
var key = data && data.attrs && data.attrs.key
|
767
|
+
data = pendingRequests === 0 || forcing || cachedControllers && cachedControllers.indexOf(controller) > -1 ? data.view(controller) : {tag: "placeholder"}
|
768
|
+
if (data.subtree === "retain") return data;
|
769
|
+
data.attrs = data.attrs || {}
|
770
|
+
data.attrs.key = key
|
771
|
+
updateLists(views, controllers, view, controller)
|
772
|
+
return data
|
773
|
+
}
|
774
|
+
|
775
|
+
function markViews(data, cached, views, controllers) {
|
776
|
+
var cachedControllers = cached && cached.controllers
|
777
|
+
|
778
|
+
while (data.view != null) {
|
779
|
+
data = checkView(
|
780
|
+
data,
|
781
|
+
data.view.$original || data.view,
|
782
|
+
cached,
|
783
|
+
cachedControllers,
|
784
|
+
controllers,
|
785
|
+
views)
|
786
|
+
}
|
787
|
+
|
788
|
+
return data
|
789
|
+
}
|
790
|
+
|
791
|
+
function buildObject( // eslint-disable-line max-statements
|
792
|
+
data,
|
793
|
+
cached,
|
794
|
+
editable,
|
795
|
+
parentElement,
|
796
|
+
index,
|
797
|
+
shouldReattach,
|
798
|
+
namespace,
|
799
|
+
configs
|
800
|
+
) {
|
801
|
+
var views = []
|
802
|
+
var controllers = []
|
803
|
+
|
804
|
+
data = markViews(data, cached, views, controllers)
|
805
|
+
|
806
|
+
if (data.subtree === "retain") return cached
|
807
|
+
|
808
|
+
if (!data.tag && controllers.length) {
|
809
|
+
throw new Error("Component template must return a virtual " +
|
810
|
+
"element, not an array, string, etc.")
|
811
|
+
}
|
812
|
+
|
813
|
+
data.attrs = data.attrs || {}
|
814
|
+
cached.attrs = cached.attrs || {}
|
815
|
+
|
816
|
+
var dataAttrKeys = Object.keys(data.attrs)
|
817
|
+
var hasKeys = dataAttrKeys.length > ("key" in data.attrs ? 1 : 0)
|
818
|
+
|
819
|
+
maybeRecreateObject(data, cached, dataAttrKeys)
|
820
|
+
|
821
|
+
if (!isString(data.tag)) return
|
822
|
+
|
823
|
+
var isNew = cached.nodes.length === 0
|
824
|
+
|
825
|
+
namespace = getObjectNamespace(data, namespace)
|
826
|
+
|
827
|
+
var node
|
828
|
+
if (isNew) {
|
829
|
+
node = constructNode(data, namespace)
|
830
|
+
// set attributes first, then create children
|
831
|
+
var attrs = constructAttrs(data, node, namespace, hasKeys)
|
832
|
+
|
833
|
+
var children = constructChildren(data, node, cached, editable,
|
834
|
+
namespace, configs)
|
835
|
+
|
836
|
+
cached = reconstructCached(
|
837
|
+
data,
|
838
|
+
attrs,
|
839
|
+
children,
|
840
|
+
node,
|
841
|
+
namespace,
|
842
|
+
views,
|
843
|
+
controllers)
|
844
|
+
} else {
|
845
|
+
node = buildUpdatedNode(
|
846
|
+
cached,
|
847
|
+
data,
|
848
|
+
editable,
|
849
|
+
hasKeys,
|
850
|
+
namespace,
|
851
|
+
views,
|
852
|
+
configs,
|
853
|
+
controllers)
|
854
|
+
}
|
855
|
+
|
856
|
+
if (isNew || shouldReattach === true && node != null) {
|
857
|
+
insertNode(parentElement, node, index)
|
858
|
+
}
|
859
|
+
|
860
|
+
// The configs are called after `build` finishes running
|
861
|
+
scheduleConfigsToBeCalled(configs, data, node, isNew, cached)
|
862
|
+
|
863
|
+
return cached
|
864
|
+
}
|
865
|
+
|
866
|
+
function build(
|
867
|
+
parentElement,
|
868
|
+
parentTag,
|
869
|
+
parentCache,
|
870
|
+
parentIndex,
|
871
|
+
data,
|
872
|
+
cached,
|
873
|
+
shouldReattach,
|
874
|
+
index,
|
875
|
+
editable,
|
876
|
+
namespace,
|
877
|
+
configs
|
878
|
+
) {
|
879
|
+
/*
|
880
|
+
* `build` is a recursive function that manages creation/diffing/removal
|
881
|
+
* of DOM elements based on comparison between `data` and `cached` the
|
882
|
+
* diff algorithm can be summarized as this:
|
883
|
+
*
|
884
|
+
* 1 - compare `data` and `cached`
|
885
|
+
* 2 - if they are different, copy `data` to `cached` and update the DOM
|
886
|
+
* based on what the difference is
|
887
|
+
* 3 - recursively apply this algorithm for every array and for the
|
888
|
+
* children of every virtual element
|
889
|
+
*
|
890
|
+
* The `cached` data structure is essentially the same as the previous
|
891
|
+
* redraw's `data` data structure, with a few additions:
|
892
|
+
* - `cached` always has a property called `nodes`, which is a list of
|
893
|
+
* DOM elements that correspond to the data represented by the
|
894
|
+
* respective virtual element
|
895
|
+
* - in order to support attaching `nodes` as a property of `cached`,
|
896
|
+
* `cached` is *always* a non-primitive object, i.e. if the data was
|
897
|
+
* a string, then cached is a String instance. If data was `null` or
|
898
|
+
* `undefined`, cached is `new String("")`
|
899
|
+
* - `cached also has a `configContext` property, which is the state
|
900
|
+
* storage object exposed by config(element, isInitialized, context)
|
901
|
+
* - when `cached` is an Object, it represents a virtual element; when
|
902
|
+
* it's an Array, it represents a list of elements; when it's a
|
903
|
+
* String, Number or Boolean, it represents a text node
|
904
|
+
*
|
905
|
+
* `parentElement` is a DOM element used for W3C DOM API calls
|
906
|
+
* `parentTag` is only used for handling a corner case for textarea
|
907
|
+
* values
|
908
|
+
* `parentCache` is used to remove nodes in some multi-node cases
|
909
|
+
* `parentIndex` and `index` are used to figure out the offset of nodes.
|
910
|
+
* They're artifacts from before arrays started being flattened and are
|
911
|
+
* likely refactorable
|
912
|
+
* `data` and `cached` are, respectively, the new and old nodes being
|
913
|
+
* diffed
|
914
|
+
* `shouldReattach` is a flag indicating whether a parent node was
|
915
|
+
* recreated (if so, and if this node is reused, then this node must
|
916
|
+
* reattach itself to the new parent)
|
917
|
+
* `editable` is a flag that indicates whether an ancestor is
|
918
|
+
* contenteditable
|
919
|
+
* `namespace` indicates the closest HTML namespace as it cascades down
|
920
|
+
* from an ancestor
|
921
|
+
* `configs` is a list of config functions to run after the topmost
|
922
|
+
* `build` call finishes running
|
923
|
+
*
|
924
|
+
* there's logic that relies on the assumption that null and undefined
|
925
|
+
* data are equivalent to empty strings
|
926
|
+
* - this prevents lifecycle surprises from procedural helpers that mix
|
927
|
+
* implicit and explicit return statements (e.g.
|
928
|
+
* function foo() {if (cond) return m("div")}
|
929
|
+
* - it simplifies diffing code
|
930
|
+
*/
|
931
|
+
data = dataToString(data)
|
932
|
+
if (data.subtree === "retain") return cached
|
933
|
+
cached = makeCache(data, cached, index, parentIndex, parentCache)
|
934
|
+
|
935
|
+
if (isArray(data)) {
|
936
|
+
return buildArray(
|
937
|
+
data,
|
938
|
+
cached,
|
939
|
+
parentElement,
|
940
|
+
index,
|
941
|
+
parentTag,
|
942
|
+
shouldReattach,
|
943
|
+
editable,
|
944
|
+
namespace,
|
945
|
+
configs)
|
946
|
+
} else if (data != null && isObject(data)) {
|
947
|
+
return buildObject(
|
948
|
+
data,
|
949
|
+
cached,
|
950
|
+
editable,
|
951
|
+
parentElement,
|
952
|
+
index,
|
953
|
+
shouldReattach,
|
954
|
+
namespace,
|
955
|
+
configs)
|
956
|
+
} else if (!isFunction(data)) {
|
957
|
+
return handleTextNode(
|
958
|
+
cached,
|
959
|
+
data,
|
960
|
+
index,
|
961
|
+
parentElement,
|
962
|
+
shouldReattach,
|
963
|
+
editable,
|
964
|
+
parentTag)
|
965
|
+
} else {
|
966
|
+
return cached
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
970
|
+
function sortChanges(a, b) {
|
971
|
+
return a.action - b.action || a.index - b.index
|
972
|
+
}
|
973
|
+
|
974
|
+
function copyStyleAttrs(node, dataAttr, cachedAttr) {
|
975
|
+
for (var rule in dataAttr) if (hasOwn.call(dataAttr, rule)) {
|
976
|
+
if (cachedAttr == null || cachedAttr[rule] !== dataAttr[rule]) {
|
977
|
+
node.style[rule] = dataAttr[rule]
|
333
978
|
}
|
334
979
|
}
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
980
|
+
|
981
|
+
for (rule in cachedAttr) if (hasOwn.call(cachedAttr, rule)) {
|
982
|
+
if (!hasOwn.call(dataAttr, rule)) node.style[rule] = ""
|
983
|
+
}
|
984
|
+
}
|
985
|
+
|
986
|
+
var shouldUseSetAttribute = {
|
987
|
+
list: 1,
|
988
|
+
style: 1,
|
989
|
+
form: 1,
|
990
|
+
type: 1,
|
991
|
+
width: 1,
|
992
|
+
height: 1
|
993
|
+
}
|
994
|
+
|
995
|
+
function setSingleAttr(
|
996
|
+
node,
|
997
|
+
attrName,
|
998
|
+
dataAttr,
|
999
|
+
cachedAttr,
|
1000
|
+
tag,
|
1001
|
+
namespace
|
1002
|
+
) {
|
1003
|
+
if (attrName === "config" || attrName === "key") {
|
1004
|
+
// `config` isn't a real attribute, so ignore it
|
1005
|
+
return true
|
1006
|
+
} else if (isFunction(dataAttr) && attrName.slice(0, 2) === "on") {
|
1007
|
+
// hook event handlers to the auto-redrawing system
|
1008
|
+
node[attrName] = autoredraw(dataAttr, node)
|
1009
|
+
} else if (attrName === "style" && dataAttr != null &&
|
1010
|
+
isObject(dataAttr)) {
|
1011
|
+
// handle `style: {...}`
|
1012
|
+
copyStyleAttrs(node, dataAttr, cachedAttr)
|
1013
|
+
} else if (namespace != null) {
|
1014
|
+
// handle SVG
|
1015
|
+
if (attrName === "href") {
|
1016
|
+
node.setAttributeNS("http://www.w3.org/1999/xlink",
|
1017
|
+
"href", dataAttr)
|
1018
|
+
} else {
|
1019
|
+
node.setAttribute(
|
1020
|
+
attrName === "className" ? "class" : attrName,
|
1021
|
+
dataAttr)
|
348
1022
|
}
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
nodes = [$document.createTextNode(data)]
|
365
|
-
}
|
366
|
-
parentElement.insertBefore(nodes[0], parentElement.childNodes[index] || null);
|
367
|
-
nodes[0].nodeValue = data
|
368
|
-
}
|
369
|
-
}
|
1023
|
+
} else if (attrName in node && !shouldUseSetAttribute[attrName]) {
|
1024
|
+
// handle cases that are properties (but ignore cases where we
|
1025
|
+
// should use setAttribute instead)
|
1026
|
+
//
|
1027
|
+
// - list and form are typically used as strings, but are DOM
|
1028
|
+
// element references in js
|
1029
|
+
//
|
1030
|
+
// - when using CSS selectors (e.g. `m("[style='']")`), style is
|
1031
|
+
// used as a string, but it's an object in js
|
1032
|
+
//
|
1033
|
+
// #348 don't set the value if not needed - otherwise, cursor
|
1034
|
+
// placement breaks in Chrome
|
1035
|
+
try {
|
1036
|
+
if (tag !== "input" || node[attrName] !== dataAttr) {
|
1037
|
+
node[attrName] = dataAttr
|
370
1038
|
}
|
371
|
-
|
372
|
-
|
1039
|
+
} catch (e) {
|
1040
|
+
node.setAttribute(attrName, dataAttr)
|
373
1041
|
}
|
374
|
-
else cached.nodes.intact = true
|
375
1042
|
}
|
1043
|
+
else node.setAttribute(attrName, dataAttr)
|
1044
|
+
}
|
376
1045
|
|
377
|
-
|
1046
|
+
function trySetAttr(
|
1047
|
+
node,
|
1048
|
+
attrName,
|
1049
|
+
dataAttr,
|
1050
|
+
cachedAttr,
|
1051
|
+
cachedAttrs,
|
1052
|
+
tag,
|
1053
|
+
namespace
|
1054
|
+
) {
|
1055
|
+
if (!(attrName in cachedAttrs) || (cachedAttr !== dataAttr)) {
|
1056
|
+
cachedAttrs[attrName] = dataAttr
|
1057
|
+
try {
|
1058
|
+
return setSingleAttr(
|
1059
|
+
node,
|
1060
|
+
attrName,
|
1061
|
+
dataAttr,
|
1062
|
+
cachedAttr,
|
1063
|
+
tag,
|
1064
|
+
namespace)
|
1065
|
+
} catch (e) {
|
1066
|
+
// swallow IE's invalid argument errors to mimic HTML's
|
1067
|
+
// fallback-to-doing-nothing-on-invalid-attributes behavior
|
1068
|
+
if (e.message.indexOf("Invalid argument") < 0) throw e
|
1069
|
+
}
|
1070
|
+
} else if (attrName === "value" && tag === "input" &&
|
1071
|
+
node.value !== dataAttr) {
|
1072
|
+
// #348 dataAttr may not be a string, so use loose comparison
|
1073
|
+
node.value = dataAttr
|
1074
|
+
}
|
378
1075
|
}
|
379
|
-
|
1076
|
+
|
380
1077
|
function setAttributes(node, tag, dataAttrs, cachedAttrs, namespace) {
|
381
|
-
for (var attrName in dataAttrs) {
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
node[attrName] = autoredraw(dataAttr, node)
|
392
|
-
}
|
393
|
-
//handle `style: {...}`
|
394
|
-
else if (attrName === "style" && dataAttr != null && type.call(dataAttr) === OBJECT) {
|
395
|
-
for (var rule in dataAttr) {
|
396
|
-
if (cachedAttr == null || cachedAttr[rule] !== dataAttr[rule]) node.style[rule] = dataAttr[rule]
|
397
|
-
}
|
398
|
-
for (var rule in cachedAttr) {
|
399
|
-
if (!(rule in dataAttr)) node.style[rule] = ""
|
400
|
-
}
|
401
|
-
}
|
402
|
-
//handle SVG
|
403
|
-
else if (namespace != null) {
|
404
|
-
if (attrName === "href") node.setAttributeNS("http://www.w3.org/1999/xlink", "href", dataAttr);
|
405
|
-
else if (attrName === "className") node.setAttribute("class", dataAttr);
|
406
|
-
else node.setAttribute(attrName, dataAttr)
|
407
|
-
}
|
408
|
-
//handle cases that are properties (but ignore cases where we should use setAttribute instead)
|
409
|
-
//- list and form are typically used as strings, but are DOM element references in js
|
410
|
-
//- when using CSS selectors (e.g. `m("[style='']")`), style is used as a string, but it's an object in js
|
411
|
-
else if (attrName in node && !(attrName === "list" || attrName === "style" || attrName === "form" || attrName === "type" || attrName === "width" || attrName === "height")) {
|
412
|
-
//#348 don't set the value if not needed otherwise cursor placement breaks in Chrome
|
413
|
-
if (tag !== "input" || node[attrName] !== dataAttr) node[attrName] = dataAttr
|
414
|
-
}
|
415
|
-
else node.setAttribute(attrName, dataAttr)
|
416
|
-
}
|
417
|
-
catch (e) {
|
418
|
-
//swallow IE's invalid argument errors to mimic HTML's fallback-to-doing-nothing-on-invalid-attributes behavior
|
419
|
-
if (e.message.indexOf("Invalid argument") < 0) throw e
|
420
|
-
}
|
421
|
-
}
|
422
|
-
//#348 dataAttr may not be a string, so use loose comparison (double equal) instead of strict (triple equal)
|
423
|
-
else if (attrName === "value" && tag === "input" && node.value != dataAttr) {
|
424
|
-
node.value = dataAttr
|
1078
|
+
for (var attrName in dataAttrs) if (hasOwn.call(dataAttrs, attrName)) {
|
1079
|
+
if (trySetAttr(
|
1080
|
+
node,
|
1081
|
+
attrName,
|
1082
|
+
dataAttrs[attrName],
|
1083
|
+
cachedAttrs[attrName],
|
1084
|
+
cachedAttrs,
|
1085
|
+
tag,
|
1086
|
+
namespace)) {
|
1087
|
+
continue
|
425
1088
|
}
|
426
1089
|
}
|
427
1090
|
return cachedAttrs
|
428
1091
|
}
|
1092
|
+
|
429
1093
|
function clear(nodes, cached) {
|
430
1094
|
for (var i = nodes.length - 1; i > -1; i--) {
|
431
1095
|
if (nodes[i] && nodes[i].parentNode) {
|
432
|
-
try {
|
433
|
-
|
434
|
-
|
1096
|
+
try {
|
1097
|
+
nodes[i].parentNode.removeChild(nodes[i])
|
1098
|
+
} catch (e) {
|
1099
|
+
/* eslint-disable max-len */
|
1100
|
+
// ignore if this fails due to order of events (see
|
1101
|
+
// http://stackoverflow.com/questions/21926083/failed-to-execute-removechild-on-node)
|
1102
|
+
/* eslint-enable max-len */
|
1103
|
+
}
|
1104
|
+
cached = [].concat(cached)
|
435
1105
|
if (cached[i]) unload(cached[i])
|
436
1106
|
}
|
437
1107
|
}
|
438
|
-
if
|
1108
|
+
// release memory if nodes is an array. This check should fail if nodes
|
1109
|
+
// is a NodeList (see loop above)
|
1110
|
+
if (nodes.length) {
|
1111
|
+
nodes.length = 0
|
1112
|
+
}
|
439
1113
|
}
|
1114
|
+
|
440
1115
|
function unload(cached) {
|
441
|
-
if (cached.configContext &&
|
442
|
-
cached.configContext.onunload()
|
1116
|
+
if (cached.configContext && isFunction(cached.configContext.onunload)) {
|
1117
|
+
cached.configContext.onunload()
|
443
1118
|
cached.configContext.onunload = null
|
444
1119
|
}
|
445
1120
|
if (cached.controllers) {
|
446
|
-
|
447
|
-
if (
|
448
|
-
|
1121
|
+
forEach(cached.controllers, function (controller) {
|
1122
|
+
if (isFunction(controller.onunload)) {
|
1123
|
+
controller.onunload({preventDefault: noop})
|
1124
|
+
}
|
1125
|
+
})
|
449
1126
|
}
|
450
1127
|
if (cached.children) {
|
451
|
-
if (
|
452
|
-
for (var i = 0, child; child = cached.children[i]; i++) unload(child)
|
453
|
-
}
|
1128
|
+
if (isArray(cached.children)) forEach(cached.children, unload)
|
454
1129
|
else if (cached.children.tag) unload(cached.children)
|
455
1130
|
}
|
456
1131
|
}
|
1132
|
+
|
1133
|
+
function appendTextFragment(parentElement, data) {
|
1134
|
+
try {
|
1135
|
+
parentElement.appendChild(
|
1136
|
+
$document.createRange().createContextualFragment(data))
|
1137
|
+
} catch (e) {
|
1138
|
+
parentElement.insertAdjacentHTML("beforeend", data)
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
|
457
1142
|
function injectHTML(parentElement, index, data) {
|
458
|
-
var nextSibling = parentElement.childNodes[index]
|
1143
|
+
var nextSibling = parentElement.childNodes[index]
|
459
1144
|
if (nextSibling) {
|
460
|
-
var isElement = nextSibling.nodeType
|
461
|
-
var placeholder = $document.createElement("span")
|
1145
|
+
var isElement = nextSibling.nodeType !== 1
|
1146
|
+
var placeholder = $document.createElement("span")
|
462
1147
|
if (isElement) {
|
463
|
-
parentElement.insertBefore(placeholder, nextSibling || null)
|
464
|
-
placeholder.insertAdjacentHTML("beforebegin", data)
|
1148
|
+
parentElement.insertBefore(placeholder, nextSibling || null)
|
1149
|
+
placeholder.insertAdjacentHTML("beforebegin", data)
|
465
1150
|
parentElement.removeChild(placeholder)
|
1151
|
+
} else {
|
1152
|
+
nextSibling.insertAdjacentHTML("beforebegin", data)
|
466
1153
|
}
|
467
|
-
|
1154
|
+
} else {
|
1155
|
+
appendTextFragment(parentElement, data)
|
468
1156
|
}
|
469
|
-
|
470
|
-
var nodes = []
|
1157
|
+
|
1158
|
+
var nodes = []
|
1159
|
+
|
471
1160
|
while (parentElement.childNodes[index] !== nextSibling) {
|
472
|
-
nodes.push(parentElement.childNodes[index])
|
1161
|
+
nodes.push(parentElement.childNodes[index])
|
473
1162
|
index++
|
474
1163
|
}
|
1164
|
+
|
475
1165
|
return nodes
|
476
1166
|
}
|
1167
|
+
|
477
1168
|
function autoredraw(callback, object) {
|
478
|
-
return function(e) {
|
479
|
-
e = e || event
|
480
|
-
m.redraw.strategy("diff")
|
481
|
-
m.startComputation()
|
482
|
-
try {
|
483
|
-
|
1169
|
+
return function (e) {
|
1170
|
+
e = e || event
|
1171
|
+
m.redraw.strategy("diff")
|
1172
|
+
m.startComputation()
|
1173
|
+
try {
|
1174
|
+
return callback.call(object, e)
|
1175
|
+
} finally {
|
484
1176
|
endFirstComputation()
|
485
1177
|
}
|
486
1178
|
}
|
487
1179
|
}
|
488
1180
|
|
489
|
-
var html
|
1181
|
+
var html
|
490
1182
|
var documentNode = {
|
491
|
-
appendChild: function(node) {
|
492
|
-
if (html === undefined) html = $document.createElement("html")
|
493
|
-
if ($document.documentElement &&
|
1183
|
+
appendChild: function (node) {
|
1184
|
+
if (html === undefined) html = $document.createElement("html")
|
1185
|
+
if ($document.documentElement &&
|
1186
|
+
$document.documentElement !== node) {
|
494
1187
|
$document.replaceChild(node, $document.documentElement)
|
1188
|
+
} else {
|
1189
|
+
$document.appendChild(node)
|
495
1190
|
}
|
496
|
-
|
1191
|
+
|
497
1192
|
this.childNodes = $document.childNodes
|
498
1193
|
},
|
499
|
-
|
1194
|
+
|
1195
|
+
insertBefore: function (node) {
|
500
1196
|
this.appendChild(node)
|
501
1197
|
},
|
1198
|
+
|
502
1199
|
childNodes: []
|
503
|
-
}
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
var nodeCache = []
|
1203
|
+
var cellCache = {}
|
1204
|
+
|
1205
|
+
m.render = function (root, cell, forceRecreation) {
|
1206
|
+
if (!root) {
|
1207
|
+
throw new Error("Ensure the DOM element being passed to " +
|
1208
|
+
"m.route/m.mount/m.render is not undefined.")
|
1209
|
+
}
|
1210
|
+
var configs = []
|
1211
|
+
var id = getCellCacheKey(root)
|
1212
|
+
var isDocumentRoot = root === $document
|
1213
|
+
var node
|
1214
|
+
|
1215
|
+
if (isDocumentRoot || root === $document.documentElement) {
|
1216
|
+
node = documentNode
|
1217
|
+
} else {
|
1218
|
+
node = root
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
if (isDocumentRoot && cell.tag !== "html") {
|
1222
|
+
cell = {tag: "html", attrs: {}, children: cell}
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
if (cellCache[id] === undefined) clear(node.childNodes)
|
1226
|
+
if (forceRecreation === true) reset(root)
|
1227
|
+
|
1228
|
+
cellCache[id] = build(
|
1229
|
+
node,
|
1230
|
+
null,
|
1231
|
+
undefined,
|
1232
|
+
undefined,
|
1233
|
+
cell,
|
1234
|
+
cellCache[id],
|
1235
|
+
false,
|
1236
|
+
0,
|
1237
|
+
null,
|
1238
|
+
undefined,
|
1239
|
+
configs)
|
1240
|
+
|
1241
|
+
forEach(configs, function (config) { config() })
|
1242
|
+
}
|
1243
|
+
|
517
1244
|
function getCellCacheKey(element) {
|
518
|
-
var index = nodeCache.indexOf(element)
|
1245
|
+
var index = nodeCache.indexOf(element)
|
519
1246
|
return index < 0 ? nodeCache.push(element) - 1 : index
|
520
1247
|
}
|
521
1248
|
|
522
|
-
m.trust = function(value) {
|
523
|
-
value = new String(value)
|
524
|
-
value.$trusted = true
|
1249
|
+
m.trust = function (value) {
|
1250
|
+
value = new String(value) // eslint-disable-line no-new-wrappers
|
1251
|
+
value.$trusted = true
|
525
1252
|
return value
|
526
|
-
}
|
1253
|
+
}
|
527
1254
|
|
528
1255
|
function gettersetter(store) {
|
529
|
-
|
530
|
-
if (arguments.length) store = arguments[0]
|
1256
|
+
function prop() {
|
1257
|
+
if (arguments.length) store = arguments[0]
|
531
1258
|
return store
|
532
|
-
}
|
1259
|
+
}
|
533
1260
|
|
534
|
-
prop.toJSON = function() {
|
1261
|
+
prop.toJSON = function () {
|
535
1262
|
return store
|
536
|
-
}
|
1263
|
+
}
|
537
1264
|
|
538
1265
|
return prop
|
539
1266
|
}
|
540
1267
|
|
541
1268
|
m.prop = function (store) {
|
542
|
-
|
543
|
-
|
1269
|
+
if ((store != null && isObject(store) || isFunction(store)) &&
|
1270
|
+
isFunction(store.then)) {
|
544
1271
|
return propify(store)
|
545
1272
|
}
|
546
1273
|
|
547
1274
|
return gettersetter(store)
|
548
|
-
}
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
var roots = []
|
1278
|
+
var components = []
|
1279
|
+
var controllers = []
|
1280
|
+
var lastRedrawId = null
|
1281
|
+
var lastRedrawCallTime = 0
|
1282
|
+
var computePreRedrawHook = null
|
1283
|
+
var computePostRedrawHook = null
|
1284
|
+
var topComponent
|
1285
|
+
var FRAME_BUDGET = 16 // 60 frames per second = 1 call per 16 ms
|
549
1286
|
|
550
|
-
var roots = [], components = [], controllers = [], lastRedrawId = null, lastRedrawCallTime = 0, computePreRedrawHook = null, computePostRedrawHook = null, prevented = false, topComponent, unloaders = [];
|
551
|
-
var FRAME_BUDGET = 16; //60 frames per second = 1 call per 16 ms
|
552
1287
|
function parameterize(component, args) {
|
553
|
-
|
1288
|
+
function controller() {
|
1289
|
+
/* eslint-disable no-invalid-this */
|
554
1290
|
return (component.controller || noop).apply(this, args) || this
|
1291
|
+
/* eslint-enable no-invalid-this */
|
1292
|
+
}
|
1293
|
+
|
1294
|
+
if (component.controller) {
|
1295
|
+
controller.prototype = component.controller.prototype
|
555
1296
|
}
|
556
|
-
|
557
|
-
|
558
|
-
|
1297
|
+
|
1298
|
+
function view(ctrl) {
|
1299
|
+
var currentArgs = [ctrl].concat(args)
|
1300
|
+
for (var i = 1; i < arguments.length; i++) {
|
1301
|
+
currentArgs.push(arguments[i])
|
1302
|
+
}
|
1303
|
+
|
1304
|
+
return component.view.apply(component, currentArgs)
|
559
1305
|
}
|
1306
|
+
|
560
1307
|
view.$original = component.view
|
561
1308
|
var output = {controller: controller, view: view}
|
562
1309
|
if (args[0] && args[0].key != null) output.attrs = {key: args[0].key}
|
563
1310
|
return output
|
564
1311
|
}
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
var isPrevented = false;
|
574
|
-
var event = {preventDefault: function() {
|
575
|
-
isPrevented = true;
|
576
|
-
computePreRedrawHook = computePostRedrawHook = null;
|
577
|
-
}};
|
578
|
-
for (var i = 0, unloader; unloader = unloaders[i]; i++) {
|
579
|
-
unloader.handler.call(unloader.controller, event)
|
580
|
-
unloader.controller.onunload = null
|
581
|
-
}
|
582
|
-
if (isPrevented) {
|
583
|
-
for (var i = 0, unloader; unloader = unloaders[i]; i++) unloader.controller.onunload = unloader.handler
|
584
|
-
}
|
585
|
-
else unloaders = []
|
586
|
-
|
587
|
-
if (controllers[index] && typeof controllers[index].onunload === FUNCTION) {
|
588
|
-
controllers[index].onunload(event)
|
589
|
-
}
|
590
|
-
|
1312
|
+
|
1313
|
+
m.component = function (component) {
|
1314
|
+
var args = [].slice.call(arguments, 1)
|
1315
|
+
|
1316
|
+
return parameterize(component, args)
|
1317
|
+
}
|
1318
|
+
|
1319
|
+
function checkPrevented(component, root, index, isPrevented) {
|
591
1320
|
if (!isPrevented) {
|
592
|
-
m.redraw.strategy("all")
|
593
|
-
m.startComputation()
|
594
|
-
roots[index] = root
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
1321
|
+
m.redraw.strategy("all")
|
1322
|
+
m.startComputation()
|
1323
|
+
roots[index] = root
|
1324
|
+
var currentComponent
|
1325
|
+
|
1326
|
+
if (component) {
|
1327
|
+
currentComponent = topComponent = component
|
1328
|
+
} else {
|
1329
|
+
currentComponent = topComponent = component = {controller: noop}
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
var controller = new (component.controller || noop)()
|
1333
|
+
|
1334
|
+
// controllers may call m.mount recursively (via m.route redirects,
|
1335
|
+
// for example)
|
1336
|
+
// this conditional ensures only the last recursive m.mount call is
|
1337
|
+
// applied
|
601
1338
|
if (currentComponent === topComponent) {
|
602
|
-
controllers[index] = controller
|
1339
|
+
controllers[index] = controller
|
603
1340
|
components[index] = component
|
604
1341
|
}
|
605
|
-
endFirstComputation()
|
1342
|
+
endFirstComputation()
|
1343
|
+
if (component === null) {
|
1344
|
+
removeRootElement(root, index)
|
1345
|
+
}
|
606
1346
|
return controllers[index]
|
1347
|
+
} else if (component == null) {
|
1348
|
+
removeRootElement(root, index)
|
1349
|
+
}
|
1350
|
+
}
|
1351
|
+
|
1352
|
+
m.mount = m.module = function (root, component) {
|
1353
|
+
if (!root) {
|
1354
|
+
throw new Error("Please ensure the DOM element exists before " +
|
1355
|
+
"rendering a template into it.")
|
1356
|
+
}
|
1357
|
+
|
1358
|
+
var index = roots.indexOf(root)
|
1359
|
+
if (index < 0) index = roots.length
|
1360
|
+
|
1361
|
+
var isPrevented = false
|
1362
|
+
var event = {
|
1363
|
+
preventDefault: function () {
|
1364
|
+
isPrevented = true
|
1365
|
+
computePreRedrawHook = computePostRedrawHook = null
|
1366
|
+
}
|
1367
|
+
}
|
1368
|
+
|
1369
|
+
forEach(unloaders, function (unloader) {
|
1370
|
+
unloader.handler.call(unloader.controller, event)
|
1371
|
+
unloader.controller.onunload = null
|
1372
|
+
})
|
1373
|
+
|
1374
|
+
if (isPrevented) {
|
1375
|
+
forEach(unloaders, function (unloader) {
|
1376
|
+
unloader.controller.onunload = unloader.handler
|
1377
|
+
})
|
1378
|
+
} else {
|
1379
|
+
unloaders = []
|
607
1380
|
}
|
608
|
-
|
1381
|
+
|
1382
|
+
if (controllers[index] && isFunction(controllers[index].onunload)) {
|
1383
|
+
controllers[index].onunload(event)
|
1384
|
+
}
|
1385
|
+
|
1386
|
+
return checkPrevented(component, root, index, isPrevented)
|
1387
|
+
}
|
1388
|
+
|
1389
|
+
function removeRootElement(root, index) {
|
1390
|
+
roots.splice(index, 1)
|
1391
|
+
controllers.splice(index, 1)
|
1392
|
+
components.splice(index, 1)
|
1393
|
+
reset(root)
|
1394
|
+
nodeCache.splice(getCellCacheKey(root), 1)
|
1395
|
+
}
|
1396
|
+
|
609
1397
|
var redrawing = false
|
610
|
-
m.redraw = function(force) {
|
1398
|
+
m.redraw = function (force) {
|
611
1399
|
if (redrawing) return
|
612
1400
|
redrawing = true
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
//
|
617
|
-
//
|
618
|
-
|
619
|
-
|
620
|
-
|
1401
|
+
if (force) forcing = true
|
1402
|
+
|
1403
|
+
try {
|
1404
|
+
// lastRedrawId is a positive number if a second redraw is requested
|
1405
|
+
// before the next animation frame
|
1406
|
+
// lastRedrawID is null if it's the first redraw and not an event
|
1407
|
+
// handler
|
1408
|
+
if (lastRedrawId && !force) {
|
1409
|
+
// when setTimeout: only reschedule redraw if time between now
|
1410
|
+
// and previous redraw is bigger than a frame, otherwise keep
|
1411
|
+
// currently scheduled timeout
|
1412
|
+
// when rAF: always reschedule redraw
|
1413
|
+
if ($requestAnimationFrame === global.requestAnimationFrame ||
|
1414
|
+
new Date() - lastRedrawCallTime > FRAME_BUDGET) {
|
1415
|
+
if (lastRedrawId > 0) $cancelAnimationFrame(lastRedrawId)
|
1416
|
+
lastRedrawId = $requestAnimationFrame(redraw, FRAME_BUDGET)
|
1417
|
+
}
|
1418
|
+
} else {
|
1419
|
+
redraw()
|
1420
|
+
lastRedrawId = $requestAnimationFrame(function () {
|
1421
|
+
lastRedrawId = null
|
1422
|
+
}, FRAME_BUDGET)
|
621
1423
|
}
|
1424
|
+
} finally {
|
1425
|
+
redrawing = forcing = false
|
622
1426
|
}
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
}
|
627
|
-
redrawing = false
|
628
|
-
};
|
629
|
-
m.redraw.strategy = m.prop();
|
1427
|
+
}
|
1428
|
+
|
1429
|
+
m.redraw.strategy = m.prop()
|
630
1430
|
function redraw() {
|
631
1431
|
if (computePreRedrawHook) {
|
632
1432
|
computePreRedrawHook()
|
633
1433
|
computePreRedrawHook = null
|
634
1434
|
}
|
635
|
-
|
1435
|
+
forEach(roots, function (root, i) {
|
1436
|
+
var component = components[i]
|
636
1437
|
if (controllers[i]) {
|
637
|
-
var args =
|
638
|
-
m.render(root,
|
1438
|
+
var args = [controllers[i]]
|
1439
|
+
m.render(root,
|
1440
|
+
component.view ? component.view(controllers[i], args) : "")
|
639
1441
|
}
|
640
|
-
}
|
641
|
-
//after rendering within a routed context, we need to scroll back to
|
1442
|
+
})
|
1443
|
+
// after rendering within a routed context, we need to scroll back to
|
1444
|
+
// the top, and fetch the document title for history.pushState
|
642
1445
|
if (computePostRedrawHook) {
|
643
|
-
computePostRedrawHook()
|
1446
|
+
computePostRedrawHook()
|
644
1447
|
computePostRedrawHook = null
|
645
1448
|
}
|
646
|
-
lastRedrawId = null
|
647
|
-
lastRedrawCallTime = new Date
|
1449
|
+
lastRedrawId = null
|
1450
|
+
lastRedrawCallTime = new Date()
|
648
1451
|
m.redraw.strategy("diff")
|
649
1452
|
}
|
650
1453
|
|
651
|
-
|
652
|
-
|
653
|
-
m.endComputation = function() {
|
654
|
-
pendingRequests = Math.max(pendingRequests - 1, 0);
|
655
|
-
if (pendingRequests === 0) m.redraw()
|
656
|
-
};
|
657
|
-
var endFirstComputation = function() {
|
658
|
-
if (m.redraw.strategy() == "none") {
|
1454
|
+
function endFirstComputation() {
|
1455
|
+
if (m.redraw.strategy() === "none") {
|
659
1456
|
pendingRequests--
|
660
1457
|
m.redraw.strategy("diff")
|
1458
|
+
} else {
|
1459
|
+
m.endComputation()
|
1460
|
+
}
|
1461
|
+
}
|
1462
|
+
|
1463
|
+
m.withAttr = function (prop, withAttrCallback, callbackThis) {
|
1464
|
+
return function (e) {
|
1465
|
+
e = e || event
|
1466
|
+
/* eslint-disable no-invalid-this */
|
1467
|
+
var currentTarget = e.currentTarget || this
|
1468
|
+
var _this = callbackThis || this
|
1469
|
+
/* eslint-enable no-invalid-this */
|
1470
|
+
var target = prop in currentTarget ?
|
1471
|
+
currentTarget[prop] :
|
1472
|
+
currentTarget.getAttribute(prop)
|
1473
|
+
withAttrCallback.call(_this, target)
|
661
1474
|
}
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
var path = currentRoute = normalizeRoute(source);
|
684
|
-
if (!routeByValue(root, router, path)) {
|
685
|
-
if (isDefaultRoute) throw new Error("Ensure the default route matches one of the routes defined in m.route")
|
1475
|
+
}
|
1476
|
+
|
1477
|
+
// routing
|
1478
|
+
var modes = {pathname: "", hash: "#", search: "?"}
|
1479
|
+
var redirect = noop
|
1480
|
+
var isDefaultRoute = false
|
1481
|
+
var routeParams, currentRoute
|
1482
|
+
|
1483
|
+
m.route = function (root, arg1, arg2, vdom) { // eslint-disable-line
|
1484
|
+
// m.route()
|
1485
|
+
if (arguments.length === 0) return currentRoute
|
1486
|
+
// m.route(el, defaultRoute, routes)
|
1487
|
+
if (arguments.length === 3 && isString(arg1)) {
|
1488
|
+
redirect = function (source) {
|
1489
|
+
var path = currentRoute = normalizeRoute(source)
|
1490
|
+
if (!routeByValue(root, arg2, path)) {
|
1491
|
+
if (isDefaultRoute) {
|
1492
|
+
throw new Error("Ensure the default route matches " +
|
1493
|
+
"one of the routes defined in m.route")
|
1494
|
+
}
|
1495
|
+
|
686
1496
|
isDefaultRoute = true
|
687
|
-
m.route(
|
1497
|
+
m.route(arg1, true)
|
688
1498
|
isDefaultRoute = false
|
689
1499
|
}
|
690
|
-
}
|
691
|
-
|
692
|
-
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
var listener = m.route.mode === "hash" ?
|
1503
|
+
"onhashchange" :
|
1504
|
+
"onpopstate"
|
1505
|
+
|
1506
|
+
global[listener] = function () {
|
693
1507
|
var path = $location[m.route.mode]
|
694
1508
|
if (m.route.mode === "pathname") path += $location.search
|
695
|
-
if (currentRoute
|
696
|
-
redirect(path)
|
697
|
-
}
|
698
|
-
};
|
699
|
-
computePreRedrawHook = setScroll;
|
700
|
-
window[listener]()
|
701
|
-
}
|
702
|
-
//config: m.route
|
703
|
-
else if (arguments[0].addEventListener || arguments[0].attachEvent) {
|
704
|
-
var element = arguments[0];
|
705
|
-
var isInitialized = arguments[1];
|
706
|
-
var context = arguments[2];
|
707
|
-
var vdom = arguments[3];
|
708
|
-
element.href = (m.route.mode !== 'pathname' ? $location.pathname : '') + modes[m.route.mode] + vdom.attrs.href;
|
709
|
-
if (element.addEventListener) {
|
710
|
-
element.removeEventListener("click", routeUnobtrusive);
|
711
|
-
element.addEventListener("click", routeUnobtrusive)
|
1509
|
+
if (currentRoute !== normalizeRoute(path)) redirect(path)
|
712
1510
|
}
|
713
|
-
|
714
|
-
|
715
|
-
|
1511
|
+
|
1512
|
+
computePreRedrawHook = setScroll
|
1513
|
+
global[listener]()
|
1514
|
+
|
1515
|
+
return
|
1516
|
+
}
|
1517
|
+
|
1518
|
+
// config: m.route
|
1519
|
+
if (root.addEventListener || root.attachEvent) {
|
1520
|
+
var base = m.route.mode !== "pathname" ? $location.pathname : ""
|
1521
|
+
root.href = base + modes[m.route.mode] + vdom.attrs.href
|
1522
|
+
if (root.addEventListener) {
|
1523
|
+
root.removeEventListener("click", routeUnobtrusive)
|
1524
|
+
root.addEventListener("click", routeUnobtrusive)
|
1525
|
+
} else {
|
1526
|
+
root.detachEvent("onclick", routeUnobtrusive)
|
1527
|
+
root.attachEvent("onclick", routeUnobtrusive)
|
716
1528
|
}
|
1529
|
+
|
1530
|
+
return
|
717
1531
|
}
|
718
|
-
//m.route(route, params, shouldReplaceHistoryEntry)
|
719
|
-
|
720
|
-
var oldRoute = currentRoute
|
721
|
-
currentRoute =
|
722
|
-
|
1532
|
+
// m.route(route, params, shouldReplaceHistoryEntry)
|
1533
|
+
if (isString(root)) {
|
1534
|
+
var oldRoute = currentRoute
|
1535
|
+
currentRoute = root
|
1536
|
+
|
1537
|
+
var args = arg1 || {}
|
723
1538
|
var queryIndex = currentRoute.indexOf("?")
|
724
|
-
var params
|
725
|
-
|
1539
|
+
var params
|
1540
|
+
|
1541
|
+
if (queryIndex > -1) {
|
1542
|
+
params = parseQueryString(currentRoute.slice(queryIndex + 1))
|
1543
|
+
} else {
|
1544
|
+
params = {}
|
1545
|
+
}
|
1546
|
+
|
1547
|
+
for (var i in args) if (hasOwn.call(args, i)) {
|
1548
|
+
params[i] = args[i]
|
1549
|
+
}
|
1550
|
+
|
726
1551
|
var querystring = buildQueryString(params)
|
727
|
-
var currentPath
|
728
|
-
|
1552
|
+
var currentPath
|
1553
|
+
|
1554
|
+
if (queryIndex > -1) {
|
1555
|
+
currentPath = currentRoute.slice(0, queryIndex)
|
1556
|
+
} else {
|
1557
|
+
currentPath = currentRoute
|
1558
|
+
}
|
1559
|
+
|
1560
|
+
if (querystring) {
|
1561
|
+
currentRoute = currentPath +
|
1562
|
+
(currentPath.indexOf("?") === -1 ? "?" : "&") +
|
1563
|
+
querystring
|
1564
|
+
}
|
729
1565
|
|
730
|
-
var
|
1566
|
+
var replaceHistory =
|
1567
|
+
(arguments.length === 3 ? arg2 : arg1) === true ||
|
1568
|
+
oldRoute === root
|
731
1569
|
|
732
|
-
if (
|
1570
|
+
if (global.history.pushState) {
|
1571
|
+
var method = replaceHistory ? "replaceState" : "pushState"
|
733
1572
|
computePreRedrawHook = setScroll
|
734
|
-
computePostRedrawHook = function() {
|
735
|
-
|
736
|
-
|
1573
|
+
computePostRedrawHook = function () {
|
1574
|
+
global.history[method](null, $document.title,
|
1575
|
+
modes[m.route.mode] + currentRoute)
|
1576
|
+
}
|
737
1577
|
redirect(modes[m.route.mode] + currentRoute)
|
738
|
-
}
|
739
|
-
else {
|
1578
|
+
} else {
|
740
1579
|
$location[m.route.mode] = currentRoute
|
741
1580
|
redirect(modes[m.route.mode] + currentRoute)
|
742
1581
|
}
|
743
1582
|
}
|
744
|
-
}
|
745
|
-
|
746
|
-
|
1583
|
+
}
|
1584
|
+
|
1585
|
+
m.route.param = function (key) {
|
1586
|
+
if (!routeParams) {
|
1587
|
+
throw new Error("You must call m.route(element, defaultRoute, " +
|
1588
|
+
"routes) before calling m.route.param()")
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
if (!key) {
|
1592
|
+
return routeParams
|
1593
|
+
}
|
1594
|
+
|
747
1595
|
return routeParams[key]
|
748
|
-
}
|
749
|
-
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
m.route.mode = "search"
|
1599
|
+
|
750
1600
|
function normalizeRoute(route) {
|
751
1601
|
return route.slice(modes[m.route.mode].length)
|
752
1602
|
}
|
1603
|
+
|
753
1604
|
function routeByValue(root, router, path) {
|
754
|
-
routeParams = {}
|
1605
|
+
routeParams = {}
|
755
1606
|
|
756
|
-
var queryStart = path.indexOf("?")
|
1607
|
+
var queryStart = path.indexOf("?")
|
757
1608
|
if (queryStart !== -1) {
|
758
|
-
routeParams = parseQueryString(
|
1609
|
+
routeParams = parseQueryString(
|
1610
|
+
path.substr(queryStart + 1, path.length))
|
759
1611
|
path = path.substr(0, queryStart)
|
760
1612
|
}
|
761
1613
|
|
762
1614
|
// Get all routes and check if there's
|
763
1615
|
// an exact match for the current path
|
764
|
-
var keys = Object.keys(router)
|
765
|
-
var index = keys.indexOf(path)
|
766
|
-
|
767
|
-
|
768
|
-
|
1616
|
+
var keys = Object.keys(router)
|
1617
|
+
var index = keys.indexOf(path)
|
1618
|
+
|
1619
|
+
if (index !== -1){
|
1620
|
+
m.mount(root, router[keys [index]])
|
1621
|
+
return true
|
769
1622
|
}
|
770
1623
|
|
771
|
-
for (var route in router) {
|
1624
|
+
for (var route in router) if (hasOwn.call(router, route)) {
|
772
1625
|
if (route === path) {
|
773
|
-
m.mount(root, router[route])
|
1626
|
+
m.mount(root, router[route])
|
774
1627
|
return true
|
775
1628
|
}
|
776
1629
|
|
777
|
-
var matcher = new RegExp("^" + route
|
1630
|
+
var matcher = new RegExp("^" + route
|
1631
|
+
.replace(/:[^\/]+?\.{3}/g, "(.*?)")
|
1632
|
+
.replace(/:[^\/]+/g, "([^\\/]+)") + "\/?$")
|
778
1633
|
|
779
1634
|
if (matcher.test(path)) {
|
780
|
-
|
781
|
-
|
782
|
-
var
|
783
|
-
|
1635
|
+
/* eslint-disable no-loop-func */
|
1636
|
+
path.replace(matcher, function () {
|
1637
|
+
var keys = route.match(/:[^\/]+/g) || []
|
1638
|
+
var values = [].slice.call(arguments, 1, -2)
|
1639
|
+
forEach(keys, function (key, i) {
|
1640
|
+
routeParams[key.replace(/:|\./g, "")] =
|
1641
|
+
decodeURIComponent(values[i])
|
1642
|
+
})
|
784
1643
|
m.mount(root, router[route])
|
785
|
-
})
|
1644
|
+
})
|
1645
|
+
/* eslint-enable no-loop-func */
|
786
1646
|
return true
|
787
1647
|
}
|
788
1648
|
}
|
789
1649
|
}
|
1650
|
+
|
790
1651
|
function routeUnobtrusive(e) {
|
791
|
-
e = e || event
|
792
|
-
if (e.ctrlKey || e.metaKey || e.which === 2) return
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
1652
|
+
e = e || event
|
1653
|
+
if (e.ctrlKey || e.metaKey || e.shiftKey || e.which === 2) return
|
1654
|
+
|
1655
|
+
if (e.preventDefault) {
|
1656
|
+
e.preventDefault()
|
1657
|
+
} else {
|
1658
|
+
e.returnValue = false
|
1659
|
+
}
|
1660
|
+
|
1661
|
+
var currentTarget = e.currentTarget || e.srcElement
|
1662
|
+
var args
|
1663
|
+
|
1664
|
+
if (m.route.mode === "pathname" && currentTarget.search) {
|
1665
|
+
args = parseQueryString(currentTarget.search.slice(1))
|
1666
|
+
} else {
|
1667
|
+
args = {}
|
1668
|
+
}
|
1669
|
+
|
1670
|
+
while (currentTarget && !/a/i.test(currentTarget.nodeName)) {
|
1671
|
+
currentTarget = currentTarget.parentNode
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
// clear pendingRequests because we want an immediate route change
|
1675
|
+
pendingRequests = 0
|
1676
|
+
m.route(currentTarget[m.route.mode]
|
1677
|
+
.slice(modes[m.route.mode].length), args)
|
799
1678
|
}
|
1679
|
+
|
800
1680
|
function setScroll() {
|
801
|
-
if (m.route.mode
|
802
|
-
|
1681
|
+
if (m.route.mode !== "hash" && $location.hash) {
|
1682
|
+
$location.hash = $location.hash
|
1683
|
+
} else {
|
1684
|
+
global.scrollTo(0, 0)
|
1685
|
+
}
|
803
1686
|
}
|
1687
|
+
|
804
1688
|
function buildQueryString(object, prefix) {
|
805
1689
|
var duplicates = {}
|
806
1690
|
var str = []
|
807
|
-
|
1691
|
+
|
1692
|
+
for (var prop in object) if (hasOwn.call(object, prop)) {
|
808
1693
|
var key = prefix ? prefix + "[" + prop + "]" : prop
|
809
1694
|
var value = object[prop]
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
1695
|
+
|
1696
|
+
if (value === null) {
|
1697
|
+
str.push(encodeURIComponent(key))
|
1698
|
+
} else if (isObject(value)) {
|
1699
|
+
str.push(buildQueryString(value, key))
|
1700
|
+
} else if (isArray(value)) {
|
1701
|
+
var keys = []
|
1702
|
+
duplicates[key] = duplicates[key] || {}
|
1703
|
+
/* eslint-disable no-loop-func */
|
1704
|
+
forEach(value, function (item) {
|
1705
|
+
/* eslint-enable no-loop-func */
|
815
1706
|
if (!duplicates[key][item]) {
|
816
1707
|
duplicates[key][item] = true
|
817
|
-
|
1708
|
+
keys.push(encodeURIComponent(key) + "=" +
|
1709
|
+
encodeURIComponent(item))
|
818
1710
|
}
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
1711
|
+
})
|
1712
|
+
str.push(keys.join("&"))
|
1713
|
+
} else if (value !== undefined) {
|
1714
|
+
str.push(encodeURIComponent(key) + "=" +
|
1715
|
+
encodeURIComponent(value))
|
1716
|
+
}
|
823
1717
|
}
|
824
1718
|
return str.join("&")
|
825
1719
|
}
|
1720
|
+
|
826
1721
|
function parseQueryString(str) {
|
827
|
-
if (str
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
1722
|
+
if (str === "" || str == null) return {}
|
1723
|
+
if (str.charAt(0) === "?") str = str.slice(1)
|
1724
|
+
|
1725
|
+
var pairs = str.split("&")
|
1726
|
+
var params = {}
|
1727
|
+
|
1728
|
+
forEach(pairs, function (string) {
|
1729
|
+
var pair = string.split("=")
|
832
1730
|
var key = decodeURIComponent(pair[0])
|
833
|
-
var value = pair.length
|
1731
|
+
var value = pair.length === 2 ? decodeURIComponent(pair[1]) : null
|
834
1732
|
if (params[key] != null) {
|
835
|
-
if (
|
1733
|
+
if (!isArray(params[key])) params[key] = [params[key]]
|
836
1734
|
params[key].push(value)
|
837
1735
|
}
|
838
1736
|
else params[key] = value
|
839
|
-
}
|
1737
|
+
})
|
1738
|
+
|
840
1739
|
return params
|
841
1740
|
}
|
1741
|
+
|
842
1742
|
m.route.buildQueryString = buildQueryString
|
843
1743
|
m.route.parseQueryString = parseQueryString
|
844
|
-
|
1744
|
+
|
845
1745
|
function reset(root) {
|
846
|
-
var cacheKey = getCellCacheKey(root)
|
847
|
-
clear(root.childNodes, cellCache[cacheKey])
|
1746
|
+
var cacheKey = getCellCacheKey(root)
|
1747
|
+
clear(root.childNodes, cellCache[cacheKey])
|
848
1748
|
cellCache[cacheKey] = undefined
|
849
1749
|
}
|
850
1750
|
|
851
1751
|
m.deferred = function () {
|
852
|
-
var deferred = new Deferred()
|
853
|
-
deferred.promise = propify(deferred.promise)
|
1752
|
+
var deferred = new Deferred()
|
1753
|
+
deferred.promise = propify(deferred.promise)
|
854
1754
|
return deferred
|
855
|
-
}
|
1755
|
+
}
|
1756
|
+
|
856
1757
|
function propify(promise, initialValue) {
|
857
|
-
var prop = m.prop(initialValue)
|
858
|
-
promise.then(prop)
|
859
|
-
prop.then = function(resolve, reject) {
|
1758
|
+
var prop = m.prop(initialValue)
|
1759
|
+
promise.then(prop)
|
1760
|
+
prop.then = function (resolve, reject) {
|
860
1761
|
return propify(promise.then(resolve, reject), initialValue)
|
861
|
-
}
|
1762
|
+
}
|
1763
|
+
|
1764
|
+
prop.catch = prop.then.bind(null, null)
|
862
1765
|
return prop
|
863
1766
|
}
|
864
|
-
//Promiz.mithril.js | Zolmeister | MIT
|
865
|
-
//a modified version of Promiz.js, which does not conform to Promises/A+
|
866
|
-
//
|
867
|
-
//
|
868
|
-
|
869
|
-
|
870
|
-
|
1767
|
+
// Promiz.mithril.js | Zolmeister | MIT
|
1768
|
+
// a modified version of Promiz.js, which does not conform to Promises/A+
|
1769
|
+
// for two reasons:
|
1770
|
+
//
|
1771
|
+
// 1) `then` callbacks are called synchronously (because setTimeout is too
|
1772
|
+
// slow, and the setImmediate polyfill is too big
|
1773
|
+
//
|
1774
|
+
// 2) throwing subclasses of Error cause the error to be bubbled up instead
|
1775
|
+
// of triggering rejection (because the spec does not account for the
|
1776
|
+
// important use case of default browser error handling, i.e. message w/
|
1777
|
+
// line number)
|
1778
|
+
|
1779
|
+
var RESOLVING = 1
|
1780
|
+
var REJECTING = 2
|
1781
|
+
var RESOLVED = 3
|
1782
|
+
var REJECTED = 4
|
871
1783
|
|
872
|
-
|
1784
|
+
function Deferred(onSuccess, onFailure) {
|
1785
|
+
var self = this
|
1786
|
+
var state = 0
|
1787
|
+
var promiseValue = 0
|
1788
|
+
var next = []
|
873
1789
|
|
874
|
-
self
|
1790
|
+
self.promise = {}
|
1791
|
+
|
1792
|
+
self.resolve = function (value) {
|
875
1793
|
if (!state) {
|
876
|
-
promiseValue = value
|
877
|
-
state = RESOLVING
|
1794
|
+
promiseValue = value
|
1795
|
+
state = RESOLVING
|
878
1796
|
|
879
1797
|
fire()
|
880
1798
|
}
|
881
|
-
return this
|
882
|
-
};
|
883
1799
|
|
884
|
-
|
1800
|
+
return self
|
1801
|
+
}
|
1802
|
+
|
1803
|
+
self.reject = function (value) {
|
885
1804
|
if (!state) {
|
886
|
-
promiseValue = value
|
887
|
-
state = REJECTING
|
1805
|
+
promiseValue = value
|
1806
|
+
state = REJECTING
|
888
1807
|
|
889
1808
|
fire()
|
890
1809
|
}
|
891
|
-
return this
|
892
|
-
};
|
893
1810
|
|
894
|
-
|
895
|
-
|
1811
|
+
return self
|
1812
|
+
}
|
1813
|
+
|
1814
|
+
self.promise.then = function (onSuccess, onFailure) {
|
1815
|
+
var deferred = new Deferred(onSuccess, onFailure)
|
1816
|
+
|
896
1817
|
if (state === RESOLVED) {
|
897
1818
|
deferred.resolve(promiseValue)
|
898
|
-
}
|
899
|
-
else if (state === REJECTED) {
|
1819
|
+
} else if (state === REJECTED) {
|
900
1820
|
deferred.reject(promiseValue)
|
901
|
-
}
|
902
|
-
else {
|
1821
|
+
} else {
|
903
1822
|
next.push(deferred)
|
904
1823
|
}
|
1824
|
+
|
905
1825
|
return deferred.promise
|
906
|
-
}
|
1826
|
+
}
|
907
1827
|
|
908
1828
|
function finish(type) {
|
909
|
-
state = type || REJECTED
|
910
|
-
next.map(function(deferred) {
|
911
|
-
state === RESOLVED
|
1829
|
+
state = type || REJECTED
|
1830
|
+
next.map(function (deferred) {
|
1831
|
+
if (state === RESOLVED) {
|
1832
|
+
deferred.resolve(promiseValue)
|
1833
|
+
} else {
|
1834
|
+
deferred.reject(promiseValue)
|
1835
|
+
}
|
912
1836
|
})
|
913
1837
|
}
|
914
1838
|
|
915
|
-
function thennable(then,
|
916
|
-
if (((promiseValue != null &&
|
1839
|
+
function thennable(then, success, failure, notThennable) {
|
1840
|
+
if (((promiseValue != null && isObject(promiseValue)) ||
|
1841
|
+
isFunction(promiseValue)) && isFunction(then)) {
|
917
1842
|
try {
|
918
1843
|
// count protects against abuse calls from spec checker
|
919
|
-
var count = 0
|
920
|
-
then.call(promiseValue, function(value) {
|
921
|
-
if (count++) return
|
922
|
-
promiseValue = value
|
923
|
-
|
1844
|
+
var count = 0
|
1845
|
+
then.call(promiseValue, function (value) {
|
1846
|
+
if (count++) return
|
1847
|
+
promiseValue = value
|
1848
|
+
success()
|
924
1849
|
}, function (value) {
|
925
|
-
if (count++) return
|
926
|
-
promiseValue = value
|
927
|
-
|
1850
|
+
if (count++) return
|
1851
|
+
promiseValue = value
|
1852
|
+
failure()
|
928
1853
|
})
|
929
|
-
}
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
failureCallback()
|
1854
|
+
} catch (e) {
|
1855
|
+
m.deferred.onerror(e)
|
1856
|
+
promiseValue = e
|
1857
|
+
failure()
|
934
1858
|
}
|
935
1859
|
} else {
|
936
|
-
|
1860
|
+
notThennable()
|
937
1861
|
}
|
938
1862
|
}
|
939
1863
|
|
940
1864
|
function fire() {
|
941
1865
|
// check if it's a thenable
|
942
|
-
var then
|
1866
|
+
var then
|
943
1867
|
try {
|
944
1868
|
then = promiseValue && promiseValue.then
|
945
|
-
}
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
state = REJECTING;
|
1869
|
+
} catch (e) {
|
1870
|
+
m.deferred.onerror(e)
|
1871
|
+
promiseValue = e
|
1872
|
+
state = REJECTING
|
950
1873
|
return fire()
|
951
1874
|
}
|
952
|
-
|
953
|
-
|
1875
|
+
|
1876
|
+
if (state === REJECTING) {
|
1877
|
+
m.deferred.onerror(promiseValue)
|
1878
|
+
}
|
1879
|
+
|
1880
|
+
thennable(then, function () {
|
1881
|
+
state = RESOLVING
|
954
1882
|
fire()
|
955
|
-
}, function() {
|
956
|
-
state = REJECTING
|
1883
|
+
}, function () {
|
1884
|
+
state = REJECTING
|
957
1885
|
fire()
|
958
|
-
}, function() {
|
1886
|
+
}, function () {
|
959
1887
|
try {
|
960
|
-
if (state === RESOLVING &&
|
961
|
-
promiseValue =
|
962
|
-
}
|
963
|
-
|
964
|
-
promiseValue = failureCallback(promiseValue);
|
1888
|
+
if (state === RESOLVING && isFunction(onSuccess)) {
|
1889
|
+
promiseValue = onSuccess(promiseValue)
|
1890
|
+
} else if (state === REJECTING && isFunction(onFailure)) {
|
1891
|
+
promiseValue = onFailure(promiseValue)
|
965
1892
|
state = RESOLVING
|
966
1893
|
}
|
967
|
-
}
|
968
|
-
|
969
|
-
|
970
|
-
promiseValue = e;
|
1894
|
+
} catch (e) {
|
1895
|
+
m.deferred.onerror(e)
|
1896
|
+
promiseValue = e
|
971
1897
|
return finish()
|
972
1898
|
}
|
973
1899
|
|
974
1900
|
if (promiseValue === self) {
|
975
|
-
promiseValue = TypeError()
|
1901
|
+
promiseValue = TypeError()
|
976
1902
|
finish()
|
977
|
-
}
|
978
|
-
else {
|
1903
|
+
} else {
|
979
1904
|
thennable(then, function () {
|
980
1905
|
finish(RESOLVED)
|
981
1906
|
}, finish, function () {
|
@@ -985,175 +1910,236 @@ var m = (function app(window, undefined) {
|
|
985
1910
|
})
|
986
1911
|
}
|
987
1912
|
}
|
988
|
-
m.deferred.onerror = function(e) {
|
989
|
-
if (type.call(e) === "[object Error]" && !e.constructor.toString().match(/ Error/)) throw e
|
990
|
-
};
|
991
1913
|
|
992
|
-
m.
|
993
|
-
|
1914
|
+
m.deferred.onerror = function (e) {
|
1915
|
+
if (type.call(e) === "[object Error]" &&
|
1916
|
+
!/ Error/.test(e.constructor.toString())) {
|
1917
|
+
pendingRequests = 0
|
1918
|
+
throw e
|
1919
|
+
}
|
1920
|
+
}
|
1921
|
+
|
1922
|
+
m.sync = function (args) {
|
1923
|
+
var deferred = m.deferred()
|
1924
|
+
var outstanding = args.length
|
1925
|
+
var results = new Array(outstanding)
|
1926
|
+
var method = "resolve"
|
1927
|
+
|
994
1928
|
function synchronizer(pos, resolved) {
|
995
|
-
return function(value) {
|
996
|
-
results[pos] = value
|
997
|
-
if (!resolved) method = "reject"
|
1929
|
+
return function (value) {
|
1930
|
+
results[pos] = value
|
1931
|
+
if (!resolved) method = "reject"
|
998
1932
|
if (--outstanding === 0) {
|
999
|
-
deferred.promise(results)
|
1933
|
+
deferred.promise(results)
|
1000
1934
|
deferred[method](results)
|
1001
1935
|
}
|
1002
1936
|
return value
|
1003
1937
|
}
|
1004
1938
|
}
|
1005
1939
|
|
1006
|
-
var deferred = m.deferred();
|
1007
|
-
var outstanding = args.length;
|
1008
|
-
var results = new Array(outstanding);
|
1009
1940
|
if (args.length > 0) {
|
1010
|
-
|
1011
|
-
|
1012
|
-
}
|
1941
|
+
forEach(args, function (arg, i) {
|
1942
|
+
arg.then(synchronizer(i, true), synchronizer(i, false))
|
1943
|
+
})
|
1944
|
+
} else {
|
1945
|
+
deferred.resolve([])
|
1013
1946
|
}
|
1014
|
-
else deferred.resolve([]);
|
1015
1947
|
|
1016
1948
|
return deferred.promise
|
1017
|
-
}
|
1018
|
-
function identity(value) {return value}
|
1949
|
+
}
|
1019
1950
|
|
1020
|
-
function
|
1021
|
-
if (options.dataType && options.dataType.toLowerCase() === "jsonp") {
|
1022
|
-
var callbackKey = "mithril_callback_" + new Date().getTime() + "_" + (Math.round(Math.random() * 1e16)).toString(36);
|
1023
|
-
var script = $document.createElement("script");
|
1024
|
-
|
1025
|
-
window[callbackKey] = function(resp) {
|
1026
|
-
script.parentNode.removeChild(script);
|
1027
|
-
options.onload({
|
1028
|
-
type: "load",
|
1029
|
-
target: {
|
1030
|
-
responseText: resp
|
1031
|
-
}
|
1032
|
-
});
|
1033
|
-
window[callbackKey] = undefined
|
1034
|
-
};
|
1951
|
+
function identity(value) { return value }
|
1035
1952
|
|
1036
|
-
|
1037
|
-
|
1953
|
+
function handleJsonp(options) {
|
1954
|
+
var callbackKey = "mithril_callback_" +
|
1955
|
+
new Date().getTime() + "_" +
|
1956
|
+
(Math.round(Math.random() * 1e16)).toString(36)
|
1038
1957
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1958
|
+
var script = $document.createElement("script")
|
1959
|
+
|
1960
|
+
global[callbackKey] = function (resp) {
|
1961
|
+
script.parentNode.removeChild(script)
|
1962
|
+
options.onload({
|
1963
|
+
type: "load",
|
1964
|
+
target: {
|
1965
|
+
responseText: resp
|
1966
|
+
}
|
1967
|
+
})
|
1968
|
+
global[callbackKey] = undefined
|
1969
|
+
}
|
1970
|
+
|
1971
|
+
script.onerror = function () {
|
1972
|
+
script.parentNode.removeChild(script)
|
1973
|
+
|
1974
|
+
options.onerror({
|
1975
|
+
type: "error",
|
1976
|
+
target: {
|
1977
|
+
status: 500,
|
1978
|
+
responseText: JSON.stringify({
|
1979
|
+
error: "Error making jsonp request"
|
1980
|
+
})
|
1981
|
+
}
|
1982
|
+
})
|
1983
|
+
global[callbackKey] = undefined
|
1984
|
+
|
1985
|
+
return false
|
1986
|
+
}
|
1987
|
+
|
1988
|
+
script.onload = function () {
|
1989
|
+
return false
|
1990
|
+
}
|
1991
|
+
|
1992
|
+
script.src = options.url +
|
1993
|
+
(options.url.indexOf("?") > 0 ? "&" : "?") +
|
1994
|
+
(options.callbackKey ? options.callbackKey : "callback") +
|
1995
|
+
"=" + callbackKey +
|
1996
|
+
"&" + buildQueryString(options.data || {})
|
1997
|
+
|
1998
|
+
$document.body.appendChild(script)
|
1999
|
+
}
|
2000
|
+
|
2001
|
+
function createXhr(options) {
|
2002
|
+
var xhr = new global.XMLHttpRequest()
|
2003
|
+
xhr.open(options.method, options.url, true, options.user,
|
2004
|
+
options.password)
|
2005
|
+
|
2006
|
+
xhr.onreadystatechange = function () {
|
2007
|
+
if (xhr.readyState === 4) {
|
2008
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
2009
|
+
options.onload({type: "load", target: xhr})
|
2010
|
+
} else {
|
2011
|
+
options.onerror({type: "error", target: xhr})
|
1069
2012
|
}
|
1070
|
-
};
|
1071
|
-
if (options.serialize === JSON.stringify && options.data && options.method !== "GET") {
|
1072
|
-
xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8")
|
1073
|
-
}
|
1074
|
-
if (options.deserialize === JSON.parse) {
|
1075
|
-
xhr.setRequestHeader("Accept", "application/json, text/*");
|
1076
|
-
}
|
1077
|
-
if (typeof options.config === FUNCTION) {
|
1078
|
-
var maybeXhr = options.config(xhr, options);
|
1079
|
-
if (maybeXhr != null) xhr = maybeXhr
|
1080
2013
|
}
|
1081
2014
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
throw "Request data should be either be a string or FormData. Check the `serialize` option in `m.request`";
|
2015
|
+
if (isFunction(options.onreadystatechange)) {
|
2016
|
+
options.onreadystatechange()
|
1085
2017
|
}
|
1086
|
-
|
1087
|
-
|
2018
|
+
}
|
2019
|
+
|
2020
|
+
if (options.serialize === JSON.stringify &&
|
2021
|
+
options.data &&
|
2022
|
+
options.method !== "GET") {
|
2023
|
+
xhr.setRequestHeader("Content-Type",
|
2024
|
+
"application/json; charset=utf-8")
|
2025
|
+
}
|
2026
|
+
|
2027
|
+
if (options.deserialize === JSON.parse) {
|
2028
|
+
xhr.setRequestHeader("Accept", "application/json, text/*")
|
2029
|
+
}
|
2030
|
+
|
2031
|
+
if (isFunction(options.config)) {
|
2032
|
+
var maybeXhr = options.config(xhr, options)
|
2033
|
+
if (maybeXhr != null) xhr = maybeXhr
|
2034
|
+
}
|
2035
|
+
|
2036
|
+
var data = options.method === "GET" || !options.data ? "" : options.data
|
2037
|
+
|
2038
|
+
if (data && !isString(data) && data.constructor !== global.FormData) {
|
2039
|
+
throw new Error("Request data should be either be a string or " +
|
2040
|
+
"FormData. Check the `serialize` option in `m.request`")
|
2041
|
+
}
|
2042
|
+
|
2043
|
+
xhr.send(data)
|
2044
|
+
return xhr
|
2045
|
+
}
|
2046
|
+
|
2047
|
+
function ajax(options) {
|
2048
|
+
if (options.dataType && options.dataType.toLowerCase() === "jsonp") {
|
2049
|
+
return handleJsonp(options)
|
2050
|
+
} else {
|
2051
|
+
return createXhr(options)
|
1088
2052
|
}
|
1089
2053
|
}
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
var
|
1094
|
-
|
2054
|
+
|
2055
|
+
function bindData(options, data, serialize) {
|
2056
|
+
if (options.method === "GET" && options.dataType !== "jsonp") {
|
2057
|
+
var prefix = options.url.indexOf("?") < 0 ? "?" : "&"
|
2058
|
+
var querystring = buildQueryString(data)
|
2059
|
+
options.url += (querystring ? prefix + querystring : "")
|
2060
|
+
} else {
|
2061
|
+
options.data = serialize(data)
|
1095
2062
|
}
|
1096
|
-
else xhrOptions.data = serialize(data);
|
1097
|
-
return xhrOptions
|
1098
2063
|
}
|
2064
|
+
|
1099
2065
|
function parameterizeUrl(url, data) {
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
var
|
1104
|
-
url = url.replace(tokens[i], data[key]);
|
2066
|
+
if (data) {
|
2067
|
+
url = url.replace(/:[a-z]\w+/gi, function(token){
|
2068
|
+
var key = token.slice(1)
|
2069
|
+
var value = data[key]
|
1105
2070
|
delete data[key]
|
1106
|
-
|
2071
|
+
return value
|
2072
|
+
})
|
1107
2073
|
}
|
1108
2074
|
return url
|
1109
2075
|
}
|
1110
2076
|
|
1111
|
-
m.request = function(
|
1112
|
-
if (
|
1113
|
-
var deferred = new Deferred()
|
1114
|
-
var isJSONP =
|
1115
|
-
|
1116
|
-
|
1117
|
-
var
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
2077
|
+
m.request = function (options) {
|
2078
|
+
if (options.background !== true) m.startComputation()
|
2079
|
+
var deferred = new Deferred()
|
2080
|
+
var isJSONP = options.dataType &&
|
2081
|
+
options.dataType.toLowerCase() === "jsonp"
|
2082
|
+
|
2083
|
+
var serialize, deserialize, extract
|
2084
|
+
|
2085
|
+
if (isJSONP) {
|
2086
|
+
serialize = options.serialize =
|
2087
|
+
deserialize = options.deserialize = identity
|
2088
|
+
|
2089
|
+
extract = function (jsonp) { return jsonp.responseText }
|
2090
|
+
} else {
|
2091
|
+
serialize = options.serialize = options.serialize || JSON.stringify
|
2092
|
+
|
2093
|
+
deserialize = options.deserialize =
|
2094
|
+
options.deserialize || JSON.parse
|
2095
|
+
extract = options.extract || function (xhr) {
|
2096
|
+
if (xhr.responseText.length || deserialize !== JSON.parse) {
|
2097
|
+
return xhr.responseText
|
2098
|
+
} else {
|
2099
|
+
return null
|
2100
|
+
}
|
2101
|
+
}
|
2102
|
+
}
|
2103
|
+
|
2104
|
+
options.method = (options.method || "GET").toUpperCase()
|
2105
|
+
options.url = parameterizeUrl(options.url, options.data)
|
2106
|
+
bindData(options, options.data, serialize)
|
2107
|
+
options.onload = options.onerror = function (ev) {
|
1124
2108
|
try {
|
1125
|
-
|
1126
|
-
var
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
2109
|
+
ev = ev || event
|
2110
|
+
var response = deserialize(extract(ev.target, options))
|
2111
|
+
if (ev.type === "load") {
|
2112
|
+
if (options.unwrapSuccess) {
|
2113
|
+
response = options.unwrapSuccess(response, ev.target)
|
2114
|
+
}
|
2115
|
+
|
2116
|
+
if (isArray(response) && options.type) {
|
2117
|
+
forEach(response, function (res, i) {
|
2118
|
+
response[i] = new options.type(res)
|
2119
|
+
})
|
2120
|
+
} else if (options.type) {
|
2121
|
+
response = new options.type(response)
|
2122
|
+
}
|
2123
|
+
|
2124
|
+
deferred.resolve(response)
|
2125
|
+
} else {
|
2126
|
+
if (options.unwrapError) {
|
2127
|
+
response = options.unwrapError(response, ev.target)
|
1131
2128
|
}
|
1132
|
-
|
2129
|
+
|
2130
|
+
deferred.reject(response)
|
1133
2131
|
}
|
1134
|
-
|
1135
|
-
}
|
1136
|
-
catch (e) {
|
1137
|
-
m.deferred.onerror(e);
|
2132
|
+
} catch (e) {
|
1138
2133
|
deferred.reject(e)
|
2134
|
+
} finally {
|
2135
|
+
if (options.background !== true) m.endComputation()
|
1139
2136
|
}
|
1140
|
-
|
1141
|
-
};
|
1142
|
-
ajax(xhrOptions);
|
1143
|
-
deferred.promise = propify(deferred.promise, xhrOptions.initialValue);
|
1144
|
-
return deferred.promise
|
1145
|
-
};
|
2137
|
+
}
|
1146
2138
|
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
};
|
1152
|
-
//for internal testing only, do not use `m.deps.factory`
|
1153
|
-
m.deps.factory = app;
|
2139
|
+
ajax(options)
|
2140
|
+
deferred.promise = propify(deferred.promise, options.initialValue)
|
2141
|
+
return deferred.promise
|
2142
|
+
}
|
1154
2143
|
|
1155
2144
|
return m
|
1156
|
-
})
|
1157
|
-
|
1158
|
-
if (typeof module != "undefined" && module !== null && module.exports) module.exports = m;
|
1159
|
-
else if (typeof define === "function" && define.amd) define(function() {return m});
|
2145
|
+
})
|