1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | !function(exports) {
|
7 | var fn, lastView, lastParams, lastStr, lastUrl, syncResume
|
8 | , capture = 1
|
9 | , fnStr = ""
|
10 | , reStr = ""
|
11 | , views = View.views = {}
|
12 | , escapeRe = /[.*+?^=!:${}()|\[\]\/\\]/g
|
13 | , parseRe = /\{([\w%.]+?)\}|.[^{\\]*?/g
|
14 |
|
15 | exports.View = View
|
16 |
|
17 | function View(route, el, parent) {
|
18 | var view = views[route]
|
19 | if (view) {
|
20 | if (el) {
|
21 | view.el = el
|
22 | view.parent = parent && View(parent)
|
23 | }
|
24 | return view
|
25 | }
|
26 | view = this
|
27 | if (!(view instanceof View)) return new View(route, el, parent)
|
28 | views[view.route = route] = view
|
29 | view.el = el
|
30 | view.parent = parent && View(parent)
|
31 |
|
32 | if (route.charAt(0) != "#") {
|
33 | var params = "m[" + (view.seq = capture++) + "]?("
|
34 | , _re = route.replace(parseRe, function(_, key) {
|
35 | return key ?
|
36 | (params += "o['" + key + "']=m[" + (capture++) + "],") && "([^/]+?)" :
|
37 | _.replace(escapeRe, "\\$&")
|
38 | })
|
39 |
|
40 | fnStr += params + "'" + route + "'):"
|
41 | reStr += (reStr ? "|(" : "(") + _re + ")"
|
42 | fn = 0
|
43 | }
|
44 | }
|
45 |
|
46 | View.prototype = {
|
47 | show: function(_params) {
|
48 | var parent
|
49 | , params = lastParams = _params || {}
|
50 | , view = lastView = this
|
51 | , tmp = params._v || view
|
52 | , close = view.isOpen && view
|
53 |
|
54 | View.active = view.route
|
55 |
|
56 | for (; tmp; tmp = parent) {
|
57 | syncResume = params._v = tmp
|
58 | tmp.emit("ping", params)
|
59 | View.emit("ping", params, tmp)
|
60 | syncResume = null
|
61 | if (lastParams != params) return
|
62 | if (parent = tmp.parent) {
|
63 | if (parent.child && parent.child != tmp) {
|
64 | close = parent.child
|
65 | }
|
66 | parent.child = tmp
|
67 | }
|
68 | if (!tmp.el) {
|
69 | if (tmp.file) {
|
70 | xhr.load(
|
71 | tmp.file
|
72 | .replace(/^|,/g, "$&" + (View.base || ""))
|
73 | .split(","),
|
74 | view.wait()
|
75 | )
|
76 | tmp.file = null
|
77 | } else {
|
78 | View("404").show(Object.assign({}, params))
|
79 | }
|
80 | return
|
81 | }
|
82 | }
|
83 |
|
84 | for (tmp in params) if (tmp.charAt(0) != "_") {
|
85 | if (syncResume = param[tmp] || param["*"]) {
|
86 | syncResume.call(view, params[tmp], tmp, params)
|
87 | syncResume = null
|
88 | }
|
89 | }
|
90 |
|
91 | bubbleDown(params, close)
|
92 | },
|
93 | wait: function() {
|
94 | var params = lastParams
|
95 | params._p = 1 + (params._p | 0)
|
96 | return function() {
|
97 | if (--params._p || lastParams != params || syncResume) return
|
98 | if (params._d) {
|
99 | bubbleDown(params)
|
100 | } else if (params._v) {
|
101 | lastView.show(params)
|
102 | }
|
103 | }
|
104 | }
|
105 | }
|
106 |
|
107 | function bubbleDown(params, close) {
|
108 | var tmp
|
109 | , view = params._v
|
110 | , parent = view && view.parent
|
111 | if (!view || params._p && /{/.test(view.route)) {
|
112 | return closeView(close)
|
113 | }
|
114 | if (parent && !view.isOpen || view === close) {
|
115 | closeView(close, view)
|
116 | El.scope(
|
117 | view.isOpen = view.el.cloneNode(true),
|
118 | El.scope(tmp = parent.isOpen || parent.el)
|
119 | )
|
120 | El.append(tmp, view.isOpen)
|
121 | El.render(view.isOpen)
|
122 | parent.emit("openChild", view, close)
|
123 | view.emit("open", params)
|
124 | View.emit("open", params, view)
|
125 | if (view.kb) El.addKb(view.kb)
|
126 | close = null
|
127 | }
|
128 | if (params._d = params._v = view.child) {
|
129 | bubbleDown(params, close)
|
130 | }
|
131 | if (lastView == view) {
|
132 | view.emit("show", params)
|
133 | View.emit("show", params, view)
|
134 | blur()
|
135 | }
|
136 | }
|
137 |
|
138 | function closeView(view, open) {
|
139 | if (view && view.isOpen) {
|
140 | view.parent.emit("closeChild", view, open)
|
141 | closeView(view.child)
|
142 | El.kill(view.isOpen)
|
143 | view.isOpen = null
|
144 | if (view.kb) El.rmKb(view.kb)
|
145 | view.emit("close")
|
146 | }
|
147 | }
|
148 |
|
149 | Event.asEmitter(View)
|
150 | Event.asEmitter(View.prototype)
|
151 |
|
152 | View.base = "view/"
|
153 | View.home = "home"
|
154 |
|
155 | View.get = get
|
156 | function get(url, params) {
|
157 | if (!fn) {
|
158 | fn = Function(
|
159 | "var r=/^\\/?(?:" + reStr + ")[\\/\\s]*$/;" +
|
160 | "return function(i,o,d){var m=r.exec(i);return m!==null?(" + fnStr + "d):d}"
|
161 | )()
|
162 | }
|
163 | return View(fn(url || View.home, params || {}, "404"))
|
164 | }
|
165 |
|
166 | View.show = function(url, _params) {
|
167 | if (url === true) {
|
168 | url = lastUrl
|
169 | lastUrl = 0
|
170 | }
|
171 | var params = _params || {}
|
172 | , view = get(url, params)
|
173 | if (!view.isOpen || lastUrl != url) {
|
174 | params._u = lastUrl = url
|
175 | view.show(El.data.route = params)
|
176 | }
|
177 | }
|
178 |
|
179 | View.param = param
|
180 | function param(name, cb) {
|
181 | [].concat(name).forEach(function(n) {
|
182 | param[n] = cb
|
183 | })
|
184 | }
|
185 |
|
186 | View.def = function(str) {
|
187 | for (var match, re = /(\S+) (\S+)/g; match = re.exec(str);) {
|
188 | match[1].split(",").map(function(view) {
|
189 | view = View(defMap(view, lastStr))
|
190 | view.file = (view.file ? view.file + "," : "") +
|
191 | match[2].split(",").map(function(file) {
|
192 | return views[file] ? views[file].file : defMap(file, lastStr)
|
193 | })
|
194 | })
|
195 | }
|
196 | }
|
197 |
|
198 | View.blur = blur
|
199 | function blur() {
|
200 |
|
201 |
|
202 | try {
|
203 | var el = document.activeElement
|
204 | , tag = el && el.tagName
|
205 | if (tag === "A" || tag === "BUTTON") el.blur()
|
206 | } catch(e) {}
|
207 | }
|
208 |
|
209 | View.url = defMap
|
210 | function defMap(str, _last) {
|
211 | var chr = str.charAt(0)
|
212 | , slice = str.slice(1)
|
213 | , last = _last || lastUrl
|
214 | return (
|
215 | chr === "+" ? last + slice :
|
216 | chr === "%" ? ((chr = last.lastIndexOf(slice.charAt(0))), (chr > 0 ? last.slice(0, chr) : last)) + slice :
|
217 | (lastStr = str)
|
218 | )
|
219 | }
|
220 |
|
221 | }(this)
|
222 |
|