laplace.coffee | |
---|---|
LaplaceLaplace is a very simple in place editor written in object oriented CoffeeScript and distributed as a jQuery (or Zepto) plugin. This is the annotated source code. | do ($ = jQuery) -> |
The Laplace class takes care of a single editable value in the page. | class Laplace |
The constructor function is responsible for initializing options and adding an edit link that will display on hover. | constructor: (el, opts) ->
@el = $ el
for opt in ["type", "name", "values", "edit-label", "save-label", "cancel-label", "url", "method"]
@setOption opt, opts[opt] |
Laplace needs to accept two syntaxes for values for radio buttons and selects. One is of the form:
Which gets transformed into:
The other syntax:
Gets transformed into: | @values = for value in @values
if Object::toString.call(value) is '[object Array]'
[label, value] = value
else
label = value
[label, value]
@makeEditLink()
|
Options are evaluated in a hierarchy of precedence:
| setOption: (name, defaultValue) ->
if dataValue = @el.closest("[data-#{name}]").data(name)
@[name] = dataValue
else
@[name] = defaultValue
|
Creates a link next to the target element that shows on hover of the parent element. | makeEditLink: ->
$editLink = $ "<a href='#' class='laplace-edit-label'>#{@['edit-label']}</a>"
$editLink.click(@edit)
@el.parent().append($editLink)
$editLink.hide()
@el.parent().hover (-> $editLink.show()), (-> $editLink.hide())
|
When we need to edit a field we need to dynamically construct the HTML interface. | edit: =>
@el.parent().find('.laplace-edit-label').remove()
code = switch @type
when "select"
options = for [label, value] in @values
"""<option value="#{value}" #{if @el.text() is label then 'selected' else ''}>#{label}</option>"""
"""<select name="#{@name}">#{options.join("\n")}</select>"""
when "radio-buttons"
options = for [label, value] in @values
selected = if @el.text() is label then 'checked' else ''
"""<label><input type="radio" value="#{value}" #{selected} name="#{@name}" />#{label}</label>"""
options.join("\n")
when "textarea"
"""<textarea name="#{@name}">#{@el.text()}</textarea>"""
else
"""<input type="#{@type}" name="#{@name}" value="#{@el.text()}" />"""
code += """<a href='#' class='laplace-save'>#{@["save-label"]}</a> <a href='#' class='laplace-cancel'>#{@["cancel-label"]}</a>"""
@editor = $ "<span>#{code}</span>"
@editor.find('a.laplace-cancel').click @cancel
@editor.find('a.laplace-save').click @save
@el.replaceWith(@editor)
return false
|
Canceling is just about restoring the original element and re-adding the edit link. | cancel: =>
@editor.replaceWith @el
@makeEditLink()
|
When saving, the selected value is immediately displayed to the user so he has instant feedback about what is going on. In the meantime we fire an AJAX call to the server. | save: =>
payload = {}
if @type is 'radio-buttons'
val = @editor.find("input:radio[name=#{@name}]:checked").val()
else
val = @editor.find("[name=#{@name}]").val()
payload[@name] = val
console.log payload
$.ajax
type: @method
url: @url
data: payload
success: @success
@cancel()
@el.text(val)
|
When the AJAX call completes we simply tell our element to display the new content. | success: (data) =>
console.log data
@el.html(data)
|
Finally we package it all up as a jQuery plugin, allowing mass setting of options. | $.fn.laplace = (options = {}) ->
opts = $.extend({}, $.fn.laplace.defaults, options)
@each ->
@laplace = new Laplace @, opts
|
The default options are exposed like this so, perhaps for localization purposes, they can be globally overrriden. | $.fn.laplace.defaults = {
"type": "text"
"edit-label": "Edit"
"save-label": "Save"
"cancel-label": "Cancel"
"method": "POST"
"values": []
}
|