1 | var KISSY = require('kissy'),
|
2 | Mock = require('./mock'),
|
3 | Random = require('./random'),
|
4 | Util = require('./util');
|
5 |
|
6 |
|
7 | (function(undefined) {
|
8 | if (typeof KISSY === 'undefined') return
|
9 |
|
10 | var Mock4XTpl = {
|
11 | debug: false
|
12 | }
|
13 |
|
14 | var XTemplate;
|
15 |
|
16 | KISSY.use('xtemplate', function(S, T) {
|
17 | XTemplate = T
|
18 | })
|
19 |
|
20 | if (!this.Mock) module.exports = Mock4XTpl
|
21 |
|
22 | Mock.xtpl = function(input, options, helpers, partials) {
|
23 | return Mock4XTpl.mock(input, options, helpers, partials)
|
24 | }
|
25 | Mock.xparse = function(input) {
|
26 | return XTemplate.compiler.parse(input)
|
27 | }
|
28 |
|
29 | |
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | Mock4XTpl.mock = function(input, options, helpers, partials) {
|
39 | helpers = helpers ? Util.extend({}, helpers, XTemplate.RunTime.commands) :
|
40 | XTemplate.RunTime.commands
|
41 | partials = partials ? Util.extend({}, partials, XTemplate.RunTime.subTpls) :
|
42 | XTemplate.RunTime.subTpls
|
43 |
|
44 |
|
45 | return this.gen(input, null, options, helpers, partials, {})
|
46 | }
|
47 |
|
48 | Mock4XTpl.parse = function(input) {
|
49 | return XTemplate.compiler.parse(input)
|
50 | }
|
51 |
|
52 | Mock4XTpl.gen = function(node, context, options, helpers, partials, other) {
|
53 | if (typeof node === 'string') {
|
54 | if (Mock4XTpl.debug) {
|
55 | console.log('[tpl ]\n', node)
|
56 | }
|
57 | var ast = this.parse(node)
|
58 | options = this.parseOptions(node, options)
|
59 | var data = this.gen(ast, context, options, helpers, partials, other)
|
60 | return data
|
61 | }
|
62 |
|
63 | context = context || [{}]
|
64 | options = options || {}
|
65 |
|
66 | node.type = node.type
|
67 |
|
68 |
|
69 | if (this[node.type] === Util.noop) return
|
70 |
|
71 | options.__path = options.__path || []
|
72 |
|
73 | if (Mock4XTpl.debug) {
|
74 | console.log()
|
75 | console.group('[' + node.type + ']', JSON.stringify(node))
|
76 | console.log('[context]', '[before]', context.length, JSON.stringify(context))
|
77 | console.log('[options]', '[before]', options.__path.length, JSON.stringify(options))
|
78 | console.log('[other ]', '[before]', JSON.stringify(other))
|
79 | }
|
80 |
|
81 | var preLength = options.__path.length
|
82 | this[node.type](node, context, options, helpers, partials, other)
|
83 |
|
84 | if (Mock4XTpl.debug) {
|
85 | console.log('[__path ]', '[after ]', options.__path);
|
86 | }
|
87 |
|
88 | if (!other.hold ||
|
89 | typeof other.hold === 'function' && !other.hold(node, options, context)) {
|
90 | options.__path.splice(preLength)
|
91 | }
|
92 |
|
93 | if (Mock4XTpl.debug) {
|
94 | console.log('[context]', '[after ]', context.length, JSON.stringify(context))
|
95 | console.groupEnd()
|
96 | }
|
97 |
|
98 | return context[context.length - 1]
|
99 | }
|
100 |
|
101 |
|
102 | |
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | Mock4XTpl.parseOptions = function(input, options) {
|
110 | var rComment = /<!--\s*\n*Mock\s*\n*([\w\W]+?)\s*\n*-->/g;
|
111 | var comments = input.match(rComment),
|
112 | ret = {},
|
113 | i, ma, option;
|
114 | for (i = 0; comments && i < comments.length; i++) {
|
115 | rComment.lastIndex = 0
|
116 | ma = rComment.exec(comments[i])
|
117 | if (ma) {
|
118 |
|
119 | option = new Function('return ' + ma[1])
|
120 | option = option()
|
121 | Util.extend(ret, option)
|
122 | }
|
123 | }
|
124 | return Util.extend(ret, options)
|
125 | }
|
126 |
|
127 | Mock4XTpl.parseVal = function(expr, object) {
|
128 |
|
129 | function queryArray(prop, context) {
|
130 | if (typeof context === 'object' && prop in context) return [context[prop]]
|
131 |
|
132 | var ret = []
|
133 | for (var i = 0; i < context.length; i++) {
|
134 | ret.push.apply(ret, query(prop, [context[i]]))
|
135 | }
|
136 | return ret
|
137 | }
|
138 |
|
139 | function queryObject(prop, context) {
|
140 | if (typeof context === 'object' && prop in context) return [context[prop]]
|
141 |
|
142 | var ret = [];
|
143 | for (var key in context) {
|
144 | ret.push.apply(ret, query(prop, [context[key]]))
|
145 | }
|
146 | return ret
|
147 | }
|
148 |
|
149 | function query(prop, set) {
|
150 | var ret = [];
|
151 | for (var i = 0; i < set.length; i++) {
|
152 | if (typeof set [i] !== 'object') continue
|
153 | if (prop in set [i]) ret.push(set [i][prop])
|
154 | else {
|
155 | ret.push.apply(ret, Util.isArray(set [i]) ?
|
156 | queryArray(prop, set [i]) :
|
157 | queryObject(prop, set [i]))
|
158 | }
|
159 | }
|
160 | return ret
|
161 | }
|
162 |
|
163 | function parse(expr, context) {
|
164 | var parts = typeof expr === 'string' ? expr.split('.') : expr.slice(0),
|
165 | set = [context];
|
166 | while (parts.length) {
|
167 | set = query(parts.shift(), set)
|
168 | }
|
169 | return set
|
170 | }
|
171 |
|
172 | return parse(expr, object)
|
173 | }
|
174 |
|
175 | Mock4XTpl.val = function(name, options, context, def) {
|
176 | if (name !== options.__path[options.__path.length - 1]) throw new Error(name + '!==' + options.__path)
|
177 | if (def !== undefined) def = Mock.mock(def)
|
178 | if (options) {
|
179 | var mocked = Mock.mock(options)
|
180 | if (Util.isString(mocked)) return mocked
|
181 |
|
182 |
|
183 | var ret = Mock4XTpl.parseVal(options.__path, mocked)
|
184 | if (ret.length > 0) return ret[0]
|
185 |
|
186 | if (name in mocked) {
|
187 | return mocked[name]
|
188 | }
|
189 | }
|
190 | if (Util.isArray(context[0])) return {}
|
191 | return def !== undefined ? def : name
|
192 | }
|
193 |
|
194 | Mock4XTpl.program = function(node, context, options, helpers, partials, other) {
|
195 |
|
196 | for (var i = 0; i < node.statements.length; i++) {
|
197 | this.gen(node.statements[i], context, options, helpers, partials, other)
|
198 | }
|
199 |
|
200 | for (var j = 0; node.inverse && j < node.inverse.length; j++) {
|
201 | this.gen(node.inverse[j], context, options, helpers, partials, other)
|
202 | }
|
203 | }
|
204 |
|
205 | Mock4XTpl.block = function(node, context, options, helpers, partials, other) {
|
206 | var contextLength = context.length
|
207 |
|
208 |
|
209 | this.gen(node.tpl, context, options, helpers, partials, Util.extend({}, other, {
|
210 | |
211 |
|
212 |
|
213 |
|
214 |
|
215 | def: {},
|
216 | hold: true
|
217 | }))
|
218 |
|
219 |
|
220 | var currentContext = context[0],
|
221 | mocked, i, len;
|
222 | if (Util.type(currentContext) === 'array') {
|
223 | mocked = this.val(options.__path[options.__path.length - 1], options, context)
|
224 | len = mocked && mocked.length || Random.integer(3, 7)
|
225 | for (i = 0; i < len; i++) {
|
226 |
|
227 | currentContext.push(mocked && mocked[i] !== undefined ? mocked[i] : {})
|
228 |
|
229 | options.__path.push(i)
|
230 | context.unshift(currentContext[currentContext.length - 1])
|
231 |
|
232 | this.gen(node.program, context, options, helpers, partials, other)
|
233 |
|
234 | options.__path.pop()
|
235 | context.shift()
|
236 | }
|
237 | } else this.gen(node.program, context, options, helpers, partials, other)
|
238 |
|
239 | if (!other.hold ||
|
240 | typeof other.hold === 'function' && !other.hold(node, options, context)) {
|
241 | context.splice(0, context.length - contextLength)
|
242 | }
|
243 | }
|
244 |
|
245 | Mock4XTpl.tpl = function(node, context, options, helpers, partials, other) {
|
246 | if (node.params && node.params.length) {
|
247 | other = Util.extend({}, other, {
|
248 | def: {
|
249 | 'each': [],
|
250 | 'if': '@BOOL(2,1,true)',
|
251 | 'unless': '@BOOL(2,1,false)',
|
252 | 'with': {}
|
253 | }[node.path.string],
|
254 | hold: {
|
255 | 'each': true,
|
256 | 'if': function(_, __, ___, name, value) {
|
257 | return typeof value === 'object'
|
258 | },
|
259 | 'unless': function(_, __, ___, name, value) {
|
260 | return typeof value === 'object'
|
261 | },
|
262 | 'with': true,
|
263 | 'include': false
|
264 | }[node.path.string]
|
265 | })
|
266 |
|
267 | for (var i = 0, input; i < node.params.length; i++) {
|
268 | if (node.path.string === 'include') {
|
269 | input = partials && partials[node.params[i].value]
|
270 | } else input = node.params[i]
|
271 | if (input) this.gen(input, context, options, helpers, partials, other)
|
272 | }
|
273 |
|
274 | if (node.hash) {
|
275 | this.gen(node.hash, context, options, helpers, partials, other)
|
276 | }
|
277 | } else {
|
278 | this.gen(node.path, context, options, helpers, partials, other)
|
279 | }
|
280 | }
|
281 | Mock4XTpl.tplExpression = function(node, context, options, helpers, partials, other) {
|
282 | this.gen(node.expression, context, options, helpers, partials, other)
|
283 | }
|
284 |
|
285 | Mock4XTpl.content = Util.noop
|
286 | Mock4XTpl.unaryExpression = Util.noop
|
287 |
|
288 | Mock4XTpl.multiplicativeExpression =
|
289 | Mock4XTpl.additiveExpression = function(node, context, options, helpers, partials, other) {
|
290 |
|
291 | this.gen(node.op1, context, options, helpers, partials, Util.extend({}, other, {
|
292 | def: function() {
|
293 | return node.op2.type === 'number' ?
|
294 | node.op2.value.indexOf('.') > -1 ?
|
295 | Random.float(-Math.pow(10, 10), Math.pow(10, 10), 1, Math.pow(10, 6)) :
|
296 | Random.integer() :
|
297 | undefined
|
298 | }()
|
299 | }))
|
300 | this.gen(node.op2, context, options, helpers, partials, Util.extend({}, other, {
|
301 | def: function() {
|
302 | return node.op1.type === 'number' ?
|
303 | node.op1.value.indexOf('.') > -1 ?
|
304 | Random.float(-Math.pow(10, 10), Math.pow(10, 10), 1, Math.pow(10, 6)) :
|
305 | Random.integer() :
|
306 | undefined
|
307 | }()
|
308 | }))
|
309 | }
|
310 |
|
311 | Mock4XTpl.relationalExpression = function(node, context, options, helpers, partials, other) {
|
312 | this.gen(node.op1, context, options, helpers, partials, other)
|
313 | this.gen(node.op2, context, options, helpers, partials, other)
|
314 | }
|
315 |
|
316 | Mock4XTpl.equalityExpression = Util.noop
|
317 | Mock4XTpl.conditionalAndExpression = Util.noop
|
318 | Mock4XTpl.conditionalOrExpression = Util.noop
|
319 | Mock4XTpl.string = Util.noop
|
320 | Mock4XTpl.number = Util.noop
|
321 | Mock4XTpl.boolean = Util.noop
|
322 |
|
323 | Mock4XTpl.hash = function(node, context, options, helpers, partials, other) {
|
324 | var pairs = node.value,
|
325 | key;
|
326 | for (key in pairs) {
|
327 | this.gen(pairs[key], context, options, helpers, partials, other)
|
328 | }
|
329 | }
|
330 |
|
331 | Mock4XTpl.id = function(node, context, options, helpers, partials, other) {
|
332 | var contextLength = context.length
|
333 |
|
334 | var parts = node.parts,
|
335 | currentContext = context[node.depth],
|
336 | i, len, cur, def, val;
|
337 |
|
338 | function fix(currentContext, index, length, name, val) {
|
339 | var type = Util.type(currentContext[name]),
|
340 | valType = Util.type(val);
|
341 | val = val === 'true' ? true :
|
342 | val === 'false' ? false : val
|
343 | if (type === 'undefined') {
|
344 |
|
345 | if (index < length - 1 && !Util.isObjectOrArray(val)) {
|
346 | currentContext[name] = {}
|
347 | } else {
|
348 | currentContext[name] = Util.isArray(val) && [] || val
|
349 | }
|
350 | } else {
|
351 |
|
352 |
|
353 | if (index < length - 1 && type !== 'object' && type !== 'array') {
|
354 | currentContext[name] = Util.isArray(val) && [] || {}
|
355 | } else {
|
356 | |
357 |
|
358 |
|
359 |
|
360 |
|
361 | if (type !== 'object' && type !== 'array' &&
|
362 | valType !== 'object' && valType !== 'array') {
|
363 | currentContext[name] = val
|
364 | }
|
365 | }
|
366 |
|
367 |
|
368 | }
|
369 | return currentContext[name]
|
370 | }
|
371 |
|
372 | if (Util.isArray(currentContext)) currentContext = context[node.depth + 1]
|
373 |
|
374 | for (i = 0, len = parts.length; i < len; i++) {
|
375 | |
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | if (i === 0 && parts[i] === 'this') continue
|
383 | if (/^(xindex|xcount|xkey)$/.test(parts[i])) continue
|
384 | if (i === 0 && len === 1 && parts[i] in helpers) continue
|
385 |
|
386 | options.__path.push(parts[i])
|
387 |
|
388 | cur = parts[i]
|
389 |
|
390 | def = i === len - 1 ?
|
391 | other.def !== undefined ? other.def :
|
392 | context[0][cur] : {}
|
393 | val = this.val(cur, options, context, def)
|
394 |
|
395 | if (Mock4XTpl.debug) {
|
396 | console.log('[def ]', JSON.stringify(def))
|
397 | console.log('[val ]', JSON.stringify(val))
|
398 | }
|
399 |
|
400 | val = fix(currentContext, i, len, cur, val)
|
401 |
|
402 | if (Util.isObjectOrArray(currentContext[cur])) {
|
403 | context.unshift(currentContext = currentContext[cur])
|
404 | }
|
405 | }
|
406 |
|
407 | if (!other.hold ||
|
408 | typeof other.hold === 'function' && !other.hold(node, options, context, cur, val)) {
|
409 | context.splice(0, context.length - contextLength)
|
410 | }
|
411 | }
|
412 |
|
413 | }).call(this);
|
414 |
|
\ | No newline at end of file |