blob: d5f997482c1f4b03a8db167305362635a83a493c [file] [log] [blame]
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ## t5/core/zone
#
# Provides a default handler for events related to zones. A zone is any kind of
# client-side element that can be updated; a zone will normally have a unique id.
# Typically, a client-side zone element is rendered by, and corresponds to, a server-side
# core/Zone component; however, certain other components (such as core/ProgressiveDisplay) may
# also be treated as zones.
#
# Most often, a zone is any element with attribute `data-container-type=zone` and corresponds
# to a core/Zone server-side component.
define ["./dom", "./events", "./ajax", "./console", "./forms", "underscore"],
(dom, events, ajax, console, forms, _) ->
# For a given element that may have the `data-update-zone` attribute, locates the
# zone element. May return null if the zone can not be found (after logging an error
# to the console).
#
# * element - starting point for determining zone
findZone = (element) ->
zoneId = element.attr "data-update-zone"
if zoneId is "^"
zone = element.findParent "[data-container-type=zone]"
if zone is null
throw new Error "Unable to locate containing zone for #{element}."
return zone
zone = dom zoneId
if zone is null
throw new Error "Unable to locate zone '#{zoneId}'."
return zone
dom.onDocument "click", "a[data-update-zone]", (event) ->
element = @closest "[data-update-zone]"
unless element
throw new Error "Could not locate containing element with data-update-zone attribute."
zone = findZone element
if zone
zone.trigger events.zone.refresh, url: element.attr "href"
event.nativeEvent.preventDefault()
return
dom.onDocument "submit", "form[data-update-zone]", ->
zone = findZone this
if zone
formParameters = forms.gatherParameters this
zone.trigger events.zone.refresh,
url: (@attr "action")
parameters: formParameters
return false
dom.onDocument "submit", "form[data-async-trigger]", ->
formParameters = forms.gatherParameters this
@addClass "ajax-update"
ajax (@attr "action"),
data: formParameters
complete: => @removeClass "ajax-update"
return false
dom.onDocument events.zone.update, (event) ->
@trigger events.zone.willUpdate
content = event.memo.content
# The server may have passed down the empty string for the content; that removes the existing content.
# On the other hand, the server may have not provided a content key; in that case, content is undefined
# which means to leave the existing content alone.
#
# Note that currently, the willUpdate and didUpdate events are triggered even when the zone is not actually
# updated. That may be a bug.
unless content is undefined
@update content
@trigger events.initializeComponents
@trigger events.zone.didUpdate
dom.onDocument events.zone.refresh, (event) ->
# This event may be triggered on an element inside the zone, rather than on the zone itself. Scan upwards
# to find the actual zone.
zone = @closest "[data-container-type=zone]"
# A Zone inside a form will render some additional parameters to coordinate updates with the Form on the server.
attr = zone.attr "data-zone-parameters"
parameters = attr and JSON.parse attr
simpleIdParams = if zone.attr "data-simple-ids" then {"t:suppress-namespaced-ids": true}
ajax event.memo.url,
data: _.extend { "t:zoneid": zone.element.id }, simpleIdParams, parameters, event.memo.parameters
success: (response) ->
zone.trigger events.zone.update, content: response.json?.content
dom.onDocument "click", "a[data-async-trigger]", (event)->
link = @closest 'a[data-async-trigger]'
link.addClass "ajax-update"
ajax (link.attr "href"),
complete: -> link.removeClass "ajax-update"
event.nativeEvent.preventDefault()
return
# Locates a zone element by its unique id attribute, and (deferred, to a later event loop cycle),
# performs a standard refresh of the zone. This is primarily used by the core/ProgressiveDisplay component.
#
# * id - client id of the element
# * url - URL to use to refresh the element.
deferredZoneUpdate = (id, url) ->
_.defer ->
zone = dom id
if zone is null
console.error "Could not locate element '#{id}' to update."
return
zone.trigger events.zone.refresh, { url }
# Most of this module is document-level event handlers, but there's also some exports:
return { deferredZoneUpdate, findZone }