unpoly-rails 0.56.7 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of unpoly-rails might be problematic. Click here for more details.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  3. data/dist/unpoly.js +1569 -793
  4. data/dist/unpoly.min.js +4 -4
  5. data/lib/assets/javascripts/unpoly.coffee +2 -0
  6. data/lib/assets/javascripts/unpoly/browser.coffee.erb +25 -41
  7. data/lib/assets/javascripts/unpoly/bus.coffee.erb +20 -6
  8. data/lib/assets/javascripts/unpoly/classes/cache.coffee +23 -13
  9. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +87 -0
  10. data/lib/assets/javascripts/unpoly/classes/focus_tracker.coffee +29 -0
  11. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +7 -4
  12. data/lib/assets/javascripts/unpoly/classes/record.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/request.coffee +38 -45
  14. data/lib/assets/javascripts/unpoly/classes/response.coffee +16 -1
  15. data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +26 -0
  16. data/lib/assets/javascripts/unpoly/classes/store/session.coffee +59 -0
  17. data/lib/assets/javascripts/unpoly/cookie.coffee +56 -0
  18. data/lib/assets/javascripts/unpoly/dom.coffee.erb +67 -39
  19. data/lib/assets/javascripts/unpoly/feedback.coffee +2 -2
  20. data/lib/assets/javascripts/unpoly/form.coffee.erb +23 -12
  21. data/lib/assets/javascripts/unpoly/history.coffee +2 -2
  22. data/lib/assets/javascripts/unpoly/layout.coffee.erb +118 -99
  23. data/lib/assets/javascripts/unpoly/link.coffee.erb +12 -5
  24. data/lib/assets/javascripts/unpoly/log.coffee +6 -5
  25. data/lib/assets/javascripts/unpoly/modal.coffee.erb +9 -2
  26. data/lib/assets/javascripts/unpoly/motion.coffee.erb +2 -6
  27. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +2 -2
  28. data/lib/assets/javascripts/unpoly/params.coffee.erb +522 -0
  29. data/lib/assets/javascripts/unpoly/popup.coffee.erb +3 -3
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +42 -34
  31. data/lib/assets/javascripts/unpoly/{syntax.coffee → syntax.coffee.erb} +59 -117
  32. data/lib/assets/javascripts/unpoly/{util.coffee → util.coffee.erb} +206 -171
  33. data/lib/unpoly/rails/version.rb +1 -1
  34. data/package.json +1 -1
  35. data/spec_app/Gemfile.lock +1 -1
  36. data/spec_app/app/assets/javascripts/integration_test.coffee +0 -4
  37. data/spec_app/app/assets/stylesheets/integration_test.sass +7 -1
  38. data/spec_app/app/controllers/pages_controller.rb +4 -0
  39. data/spec_app/app/views/form_test/basics/new.erb +34 -5
  40. data/spec_app/app/views/form_test/submission_result.erb +2 -2
  41. data/spec_app/app/views/form_test/uploads/new.erb +15 -2
  42. data/spec_app/app/views/hash_test/unpoly.erb +30 -0
  43. data/spec_app/app/views/pages/start.erb +2 -1
  44. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +17 -2
  45. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +5 -0
  46. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +1 -1
  47. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +5 -0
  48. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +8 -8
  49. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +58 -20
  50. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +78 -0
  51. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +31 -0
  52. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +50 -0
  53. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +67 -0
  54. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +113 -0
  55. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +133 -45
  56. data/spec_app/spec/javascripts/up/form_spec.js.coffee +13 -13
  57. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +110 -26
  58. data/spec_app/spec/javascripts/up/link_spec.js.coffee +1 -1
  59. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +1 -0
  60. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +52 -51
  61. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +2 -2
  62. data/spec_app/spec/javascripts/up/params_spec.coffee +768 -0
  63. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +75 -36
  64. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +48 -15
  65. data/spec_app/spec/javascripts/up/util_spec.js.coffee +148 -131
  66. metadata +17 -5
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
@@ -56,7 +56,7 @@ up.popup = (($) ->
56
56
  @param {string} [config.position='bottom-right']
57
57
  Defines where the popup is attached to the opening element.
58
58
 
59
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
59
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
60
60
  @param {string} [config.history=false]
61
61
  Whether opening a popup will add a browser history entry.
62
62
  @param {string} [config.openAnimation='fade-in']
@@ -199,7 +199,7 @@ up.popup = (($) ->
199
199
  @param {string} [options.position='bottom-right']
200
200
  Defines where the popup is attached to the opening element.
201
201
 
202
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
202
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
203
203
  @param {string} [options.html]
204
204
  A string of HTML from which to extract the popup contents. No network request will be made.
205
205
  @param {string} [options.confirm]
@@ -405,7 +405,7 @@ up.popup = (($) ->
405
405
  @param [up-position]
406
406
  Defines where the popup is attached to the opening element.
407
407
 
408
- Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
408
+ Valid values are `'bottom-right'`, `'bottom-left'`, `'top-right'` and `'top-left'`.
409
409
  @param {string} [up-confirm]
410
410
  A message that will be displayed in a cancelable confirmation dialog
411
411
  before the popup is opened.
@@ -157,7 +157,7 @@ up.proxy = (($) ->
157
157
 
158
158
  \#\#\# Example
159
159
 
160
- up.request('/search', data: { query: 'sunshine' }).then(function(response) {
160
+ up.request('/search', params: { query: 'sunshine' }).then(function(response) {
161
161
  console.log('The response text is %o', response.text);
162
162
  }).catch(function() {
163
163
  console.error('The request failed');
@@ -192,14 +192,8 @@ up.proxy = (($) ->
192
192
  requests, if available. If set to `false` a network connection will always be attempted.
193
193
  @param {Object} [options.headers={}]
194
194
  An object of additional HTTP headers.
195
- @param {Object|Array|FormData} [options.data={}]
196
- Parameters that should be sent as the request's payload.
197
-
198
- Parameters may be passed as one of the following forms:
199
-
200
- 1. An object where keys are param names and the values are param values
201
- 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
202
- 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
195
+ @param {Object|FormData|string|Array} [options.params={}]
196
+ [Parameters](/up.params) that should be sent as the request's payload.
203
197
  @param {string} [options.timeout]
204
198
  A timeout in milliseconds.
205
199
 
@@ -214,23 +208,30 @@ up.proxy = (($) ->
214
208
  @stable
215
209
  ###
216
210
  makeRequest = (args...) ->
217
- options = u.extractOptions(args)
218
- options.url = args[0] if u.isGiven(args[0])
211
+ if u.isString(args[0])
212
+ url = args.shift()
219
213
 
220
- ignoreCache = (options.cache == false)
214
+ # We cannot use u.extractOptions() since sometimes the last argument
215
+ # is an up.Request instead of a basic object.
216
+ requestOrOptions = args.shift() || {}
221
217
 
222
- request = up.Request.wrap(options)
218
+ if url
219
+ requestOrOptions.url = url
220
+
221
+ request = up.Request.wrap(requestOrOptions)
223
222
 
224
223
  # Non-GET requests always touch the network
225
- # unless `options.cache` is explicitly set to `true`.
224
+ # unless `request.cache` is explicitly set to `true`.
226
225
  # These requests are never cached.
227
226
  if !request.isSafe()
228
227
  # We clear the entire cache before an unsafe request, since we
229
228
  # assume the user is writing a change.
230
229
  clear()
231
230
 
231
+ ignoreCache = (request.cache == false)
232
+
232
233
  # If we have an existing promise matching this new request,
233
- # we use it unless `options.cache` is explicitly set to `false`.
234
+ # we use it unless `request.cache` is explicitly set to `false`.
234
235
  if !ignoreCache && (promise = get(request))
235
236
  up.puts 'Re-using cached response for %s %s', request.method, request.url
236
237
  else
@@ -241,7 +242,7 @@ up.proxy = (($) ->
241
242
  promise.catch (e) ->
242
243
  remove(request)
243
244
 
244
- if !options.preload
245
+ if !request.preload
245
246
  # This might actually make `pendingCount` higher than the actual
246
247
  # number of outstanding requests. However, we need to cover the
247
248
  # following case:
@@ -264,7 +265,7 @@ up.proxy = (($) ->
264
265
 
265
266
  \#\#\# Example
266
267
 
267
- up.request('/search', data: { query: 'sunshine' }).then(function(text) {
268
+ up.request('/search', params: { query: 'sunshine' }).then(function(text) {
268
269
  console.log('The response text is %o', text);
269
270
  }).catch(function() {
270
271
  console.error('The request failed');
@@ -286,14 +287,8 @@ up.proxy = (($) ->
286
287
  @param {Object} [request.headers={}]
287
288
  An object of additional header key/value pairs to send along
288
289
  with the request.
289
- @param {Object|Array|FormData} [options.data]
290
- Parameters that should be sent as the request's payload.
291
-
292
- Parameters may be passed as one of the following forms:
293
-
294
- 1. An object where keys are param names and the values are param values
295
- 2. An array of `{ name: 'param-name', value: 'param-value' }` objects
296
- 3. A [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object
290
+ @param {Object|FormData|string|Array} [options.params]
291
+ [Parameters](/up.params) that should be sent as the request's payload.
297
292
  @param {string} [request.timeout]
298
293
  A timeout in milliseconds for the request.
299
294
 
@@ -305,7 +300,7 @@ up.proxy = (($) ->
305
300
  Use [`up.request()`](/up.request) instead.
306
301
  ###
307
302
  ajax = (args...) ->
308
- up.log.warn('up.ajax() has been deprecated. Use up.request() instead.')
303
+ up.warn('up.ajax() has been deprecated. Use up.request() instead.')
309
304
  new Promise (resolve, reject) ->
310
305
  pickResponseText = (response) -> resolve(response.text)
311
306
  makeRequest(args...).then(pickResponseText, reject)
@@ -418,7 +413,6 @@ up.proxy = (($) ->
418
413
  @event up:proxy:recover
419
414
  @stable
420
415
  ###
421
-
422
416
  loadOrQueue = (request) ->
423
417
  if pendingCount < config.maxRequests
424
418
  load(request)
@@ -563,7 +557,7 @@ up.proxy = (($) ->
563
557
  ###
564
558
  clear = cache.clear
565
559
 
566
- up.bus.renamedEvent('up:proxy:received', 'up:proxy:loaded')
560
+ up.bus.deprecateRenamedEvent('up:proxy:received', 'up:proxy:loaded')
567
561
 
568
562
  preloadAfterDelay = ($link) ->
569
563
  delay = parseInt(u.presentAttr($link, 'up-delay')) || config.preloadDelay
@@ -592,20 +586,34 @@ up.proxy = (($) ->
592
586
  @function up.proxy.preload
593
587
  @param {string|Element|jQuery}
594
588
  The element whose destination should be preloaded.
589
+ @param {object} options
590
+ Options that will be passed to the function making the HTTP requests.
595
591
  @return
596
592
  A promise that will be fulfilled when the request was loaded and cached
597
593
  @experimental
598
594
  ###
599
- preload = (linkOrSelector) ->
595
+ preload = (linkOrSelector, options) ->
600
596
  $link = $(linkOrSelector)
601
597
 
602
598
  if up.link.isSafe($link)
603
- up.log.group "Preloading link %o", $link.get(0), ->
599
+ preloadEventAttrs = { message: ['Preloading link %o', $link.get(0)], $element: $link, $link: $link }
600
+ up.bus.whenEmitted('up:link:preload', preloadEventAttrs).then ->
604
601
  variant = up.link.followVariantForLink($link)
605
- variant.preloadLink($link)
602
+ variant.preloadLink($link, options)
606
603
  else
607
604
  Promise.reject(new Error("Won't preload unsafe link"))
608
605
 
606
+ ###**
607
+ This event is [emitted](/up.emit) before a link is [preloaded](/up.preload).
608
+
609
+ @event up:link:preload
610
+ @param {jQuery} event.$link
611
+ The link element that will be preloaded.
612
+ @param event.preventDefault()
613
+ Event listeners may call this method to prevent the link from being preloaded.
614
+ @stable
615
+ ###
616
+
609
617
  ###**
610
618
  @internal
611
619
  ###
@@ -615,11 +623,11 @@ up.proxy = (($) ->
615
623
  ###**
616
624
  @internal
617
625
  ###
618
- wrapMethod = (method, data, appendOpts) ->
626
+ wrapMethod = (method, params) ->
619
627
  if u.contains(config.wrapMethods, method)
620
- data = u.appendRequestData(data, up.protocol.config.methodParam, method, appendOpts)
628
+ params = up.params.add(params, up.protocol.config.methodParam, method)
621
629
  method = 'POST'
622
- [method, data]
630
+ [method, params]
623
631
 
624
632
  ###**
625
633
  Links with an `up-preload` attribute will silently fetch their target
@@ -23,9 +23,6 @@ up.syntax = (($) ->
23
23
 
24
24
  u = up.util
25
25
 
26
- DESTRUCTIBLE_CLASS = 'up-destructible'
27
- DESTRUCTORS_KEY = 'up-destructors'
28
-
29
26
  SYSTEM_MACRO_PRIORITIES = {
30
27
  '[up-back]': -100 # sets [up-href] to previous URL
31
28
  '[up-drawer]': -200 # sets [up-modal] and makes link followable
@@ -213,17 +210,11 @@ up.syntax = (($) ->
213
210
  such as timeouts and event handlers bound to the document.
214
211
  The destructor is *not* expected to remove the element from the DOM, which
215
212
  is already handled by [`up.destroy()`](/up.destroy).
216
-
217
- The function may also return an array of destructor functions.
218
213
  @stable
219
214
  ###
220
- compiler = (selector, args...) ->
221
- # Developer might still call top-level compiler registrations even when we don't boot
222
- # due to an unsupported browser. In that case do no work and exit early.
223
- return unless up.browser.isSupported()
224
- callback = args.pop()
225
- options = u.options(args[0])
226
- insertCompiler(compilers, selector, options, callback)
215
+ registerCompiler = (args...) ->
216
+ compiler = buildCompiler(args...)
217
+ insertCompiler(compilers, compiler)
227
218
 
228
219
  ###**
229
220
  Registers a [compiler](/up.compiler) that is run before all other compilers.
@@ -266,72 +257,36 @@ up.syntax = (($) ->
266
257
  See [`up.compiler()`](/up.compiler) for details.
267
258
  @stable
268
259
  ###
269
- macro = (selector, args...) ->
270
- # Developer might still call top-level compiler registrations even when we don't boot
271
- # due to an unsupported browser. In that case do no work and exit early.
272
- return unless up.browser.isSupported()
273
- callback = args.pop()
274
- options = u.options(args[0])
260
+ registerMacro = (args...) ->
261
+ macro = buildCompiler(args...)
275
262
  if isBooting
276
- options.priority = detectSystemMacroPriority(selector) ||
277
- up.fail('Unregistered priority for system macro %o', selector)
278
- insertCompiler(macros, selector, options, callback)
263
+ macro.priority = detectSystemMacroPriority(macro.selector) ||
264
+ up.fail('Unregistered priority for system macro %o', macro.selector)
265
+ insertCompiler(macros, macro)
279
266
 
280
- detectSystemMacroPriority = (fullMacroSelector) ->
267
+ detectSystemMacroPriority = (macroSelector) ->
281
268
  for substr, priority of SYSTEM_MACRO_PRIORITIES
282
- if fullMacroSelector.indexOf(substr) >= 0
269
+ if macroSelector.indexOf(substr) >= 0
283
270
  return priority
284
271
 
285
- buildCompiler = (selector, options, callback) ->
286
- selector: selector
287
- callback: callback
288
- isSystem: isBooting
289
- priority: options.priority || 0
290
- batch: options.batch
291
- keep: options.keep
292
-
293
- insertCompiler = (queue, selector, options, callback) ->
294
- # Silently discard any compilers that are registered on unsupported browsers
295
- return unless up.browser.isSupported()
296
- newCompiler = buildCompiler(selector, options, callback)
272
+ buildCompiler = (selector, args...) ->
273
+ callback = args.pop()
274
+ options = u.extractOptions(args)
275
+ options = u.options(options,
276
+ selector: selector,
277
+ isSystem: isBooting,
278
+ priority: 0,
279
+ batch: false
280
+ keep: false
281
+ )
282
+ return u.assign(callback, options)
283
+
284
+ insertCompiler = (queue, newCompiler) ->
297
285
  index = 0
298
- while (oldCompiler = queue[index]) && (oldCompiler.priority >= newCompiler.priority)
286
+ while (existingCompiler = queue[index]) && (existingCompiler.priority >= newCompiler.priority)
299
287
  index += 1
300
288
  queue.splice(index, 0, newCompiler)
301
289
 
302
- applyCompiler = (compiler, $jqueryElement, nativeElement) ->
303
- up.puts ("Compiling '%s' on %o" unless compiler.isSystem), compiler.selector, nativeElement
304
- if compiler.keep
305
- value = if u.isString(compiler.keep) then compiler.keep else ''
306
- $jqueryElement.attr('up-keep', value)
307
- returnValue = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)])
308
- addDestructor($jqueryElement, returnValue)
309
-
310
- ###**
311
- Tries to find a list of destructors in a compiler's return value.
312
-
313
- @param {Object} returnValue
314
- @return {Function|undefined}
315
- @internal
316
- ###
317
- normalizeDestructor = (returnValue) ->
318
- if u.isFunction(returnValue)
319
- returnValue
320
- else if u.isArray(returnValue) && u.all(returnValue, u.isFunction)
321
- u.sequence(returnValue...)
322
-
323
- addDestructor = ($element, newDestructor) ->
324
- if newDestructor = normalizeDestructor(newDestructor)
325
- $element.addClass(DESTRUCTIBLE_CLASS)
326
- # The initial destructor function is a function that removes the destructor class and data.
327
- elementDestructor = $element.data(DESTRUCTORS_KEY) || -> removeDestructors($element)
328
- elementDestructor = u.sequence(elementDestructor, newDestructor)
329
- $element.data(DESTRUCTORS_KEY, elementDestructor)
330
-
331
- removeDestructors = ($element) ->
332
- $element.removeData(DESTRUCTORS_KEY)
333
- $element.removeClass(DESTRUCTIBLE_CLASS)
334
-
335
290
  ###**
336
291
  Applies all compilers on the given element and its descendants.
337
292
  Unlike [`up.hello()`](/up.hello), this doesn't emit any events.
@@ -342,30 +297,21 @@ up.syntax = (($) ->
342
297
  @internal
343
298
  ###
344
299
  compile = ($fragment, options) ->
345
- options = u.options(options)
346
- $skipSubtrees = $(options.skip)
347
-
348
- up.log.group "Compiling fragment %o", $fragment.get(0), ->
349
- for queue in [macros, compilers]
350
- for compiler in queue
351
- $matches = u.selectInSubtree($fragment, compiler.selector)
352
-
353
- # Exclude all elements that are descendants of the subtrees we want to keep.
354
- # We only do this if `options.skip` was given, since the exclusion
355
- # process below is very expensive (we had a case where compiling 100 slements
356
- # took 1.5s because of this).
357
- if $skipSubtrees.length
358
- $matches = $matches.filter ->
359
- $match = $(this)
360
- u.all $skipSubtrees, (skipSubtree) ->
361
- $match.closest(skipSubtree).length == 0
362
-
363
- if $matches.length
364
- up.log.group ("Compiling '%s' on %d element(s)" unless compiler.isSystem), compiler.selector, $matches.length, ->
365
- if compiler.batch
366
- applyCompiler(compiler, $matches, $matches.get())
367
- else
368
- $matches.each -> applyCompiler(compiler, $(this), this)
300
+ orderedCompilers = macros.concat(compilers)
301
+ compileRun = new up.CompilePass($fragment, orderedCompilers, options)
302
+ compileRun.compile()
303
+
304
+ ###**
305
+ @function up.syntax.destructor
306
+ @internal
307
+ ###
308
+ registerDestructor = (element, destructor) ->
309
+ element = u.element(element)
310
+ unless destructors = element.upDestructors
311
+ destructors = []
312
+ element.upDestructors = destructors
313
+ element.classList.add('up-can-clean')
314
+ destructors.push(destructor)
369
315
 
370
316
  ###**
371
317
  Runs any destroyers on the given fragment and its descendants.
@@ -376,20 +322,18 @@ up.syntax = (($) ->
376
322
  @internal
377
323
  ###
378
324
  clean = ($fragment) ->
379
- $destructibles = u.selectInSubtree($fragment, ".#{DESTRUCTIBLE_CLASS}")
380
- u.each $destructibles, (destructible) ->
381
- # The destructor function may be undefined at this point.
382
- # Although destructible elements should always have an destructor function, we might be
383
- # destroying a clone of such an element. E.g. Unpoly creates a clone when keeping an
384
- # [up-keep] element, and that clone still has the .up-destructible class.
385
- if destructor = $(destructible).data(DESTRUCTORS_KEY)
386
- destructor()
325
+ cleanables = u.selectInSubtree($fragment, '.up-can-clean')
326
+ u.each cleanables, (cleanable) ->
327
+ if destructors = cleanable.upDestructors
328
+ destructor() for destructor in destructors
329
+ # We do not actually remove the #upDestructors property or .up-can-* classes for performance reasons.
330
+ # The element we just cleaned is about to be removed from the DOM.
387
331
 
388
332
  ###**
389
333
  Checks if the given element has an [`up-data`](/up-data) attribute.
390
334
  If yes, parses the attribute value as JSON and returns the parsed object.
391
335
 
392
- Returns an empty object if the element has no `up-data` attribute.
336
+ Returns `undefined` if the element has no `up-data` attribute.
393
337
 
394
338
  \#\#\# Example
395
339
 
@@ -406,8 +350,8 @@ up.syntax = (($) ->
406
350
  @return
407
351
  The JSON-decoded value of the `up-data` attribute.
408
352
 
409
- Returns an empty object (`{}`) if the element has no (or an empty) `up-data` attribute.
410
- @stable
353
+ Returns `undefined` if the element has no (or an empty) `up-data` attribute.
354
+ @experimental
411
355
  ###
412
356
 
413
357
  ###**
@@ -453,13 +397,9 @@ up.syntax = (($) ->
453
397
  A serialized JSON string
454
398
  @stable
455
399
  ###
456
- data = (elementOrSelector) ->
457
- $element = $(elementOrSelector)
458
- json = $element.attr('up-data')
459
- if u.isString(json) && u.trim(json) != ''
460
- JSON.parse(json)
461
- else
462
- {}
400
+ readData = (elementOrSelector) ->
401
+ if element = u.element(elementOrSelector)
402
+ u.jsonAttr(element, 'up-data') || {}
463
403
 
464
404
  ###**
465
405
  Resets the list of registered compiler directives to the
@@ -468,20 +408,22 @@ up.syntax = (($) ->
468
408
  @internal
469
409
  ###
470
410
  reset = ->
471
- isSystem = (compiler) -> compiler.isSystem
472
- compilers = u.select(compilers, isSystem)
473
- macros = u.select(macros, isSystem)
411
+ compilers = u.select(compilers, 'isSystem')
412
+ macros = u.select(macros, 'isSystem')
474
413
 
475
414
  up.on 'up:framework:booted', -> isBooting = false
476
415
  up.on 'up:framework:reset', reset
477
416
 
478
- compiler: compiler
479
- macro: macro
417
+ <% if ENV['JS_KNIFE'] %>knife: eval(Knife.point)<% end %>
418
+ compiler: registerCompiler
419
+ macro: registerMacro
420
+ destructor: registerDestructor
480
421
  compile: compile
481
422
  clean: clean
482
- data: data
423
+ data: readData
483
424
 
484
425
  )(jQuery)
485
426
 
486
427
  up.compiler = up.syntax.compiler
428
+ up.destructor = up.syntax.destructor
487
429
  up.macro = up.syntax.macro