UNPKG

coffeescript-ui

Version:
677 lines (539 loc) 15.2 kB
### * coffeescript-ui - Coffeescript User Interface System (CUI) * Copyright (c) 2013 - 2016 Programmfabrik GmbH * MIT Licence * https://github.com/programmfabrik/coffeescript-ui, http://www.coffeescript-ui.org ### CUI.Template.loadTemplateText(require('./DataField.html')); class CUI.DataField extends CUI.DOMElement @changed_marker_css_class: "cui-data-field-changed-marker" @defaults: undo_and_changed_support: false constructor: (opts) -> super(opts) CUI.util.assertImplements(@, ["render"]) if @_name CUI.util.assertImplements(@, ["getDefaultValue"]) @__checkChangedValue = undefined @initTemplate() @addClass("cui-data-field") @maximizeAddClasses() CUI.Events.listen type: "data-changed" node: @DOM call: (ev, info) => @onDataChanged(ev, info) return if @getName() @DOM.setAttribute("cui-data-field-name", @getName()) @init() if @_data and not CUI.util.isFunction(@_data) # console.debug "setting private data: "+@, @_data @setData(@_data) @__initDisabled() if @_hidden @hide() if @_tooltip and (@_tooltip.text or @_tooltip.content) tt_opts = CUI.util.copyObject(@_tooltip) tt_opts.element = @DOM @__tooltip = new CUI.Tooltip(tt_opts) @__opacity = 1 @_onInit?(@) # console.debug "new: "+@ onDataChanged: (ev, info) -> if not info?.element console.warn("#{CUI.util.getObjectClass(@)}[DataField].listen[data-changed]: received event with element not set.", ev, info, @) return @_onDataChanged?(@getData(), info.element, ev, info) initOpts: -> super() @addOpts name: @getNameOpt() maximize: check: Boolean maximize_horizontal: check: Boolean default: false maximize_vertical: check: Boolean default: false padded: check: Boolean default: false data: check: (v) -> CUI.util.isFunction(v?.hasOwnProperty) or CUI.util.isFunction(v) data_not_for_others: default: false check: Boolean disabled: default: false check: (v) -> CUI.util.isBoolean(v) or CUI.util.isFunction(v) disabled_depends_on_data: check: Function tooltip: check: "PlainObject" hidden: check: Boolean form: {} # set undo_support, check_changed, mark_changed all at once undo_and_changed_support: check: Boolean undo_support: default: CUI.DataField.defaults.undo_and_changed_support check: Boolean mark_changed: default: CUI.DataField.defaults.undo_and_changed_support check: Boolean check_changed: default: CUI.DataField.defaults.undo_and_changed_support check: Boolean onDataChanged: check: Function onDataInit: check: Function onInit: check: Function onRender: check: Function render_as_block: check: Boolean readOpts: -> if @opts.hasOwnProperty("undo_and_changed_support") for k in ["undo_support", "check_changed", "mark_changed"] if not @opts.hasOwnProperty(k) @opts[k] = @opts.undo_and_changed_support super() CUI.Layout::maximizeReadOpts.call(@) maximizeAddClasses: -> CUI.Layout::maximizeAddClasses.call(@) getUniqueIdForLabel: -> null initTemplate: -> @registerTemplate(@getTemplate()) getTemplate: -> new CUI.Template name: "data-field" isResizable: -> false init: -> debug: -> console.debug "----"+@+"----", @ if @__data console.debug "data:", @getData() console.debug "value:", @getValue() console.debug "init-value:", @getInitValue() console.debug "check-changed-value:", @getCheckChangedValue() toString: -> "[#{@__cls}[#{@__uniqueId}, #{@_name or '<no name>'}]}" reload: -> @remove() @render() @displayValue() @_onRender?(@) @ remove: -> @callOnOthers("remove") if not @isDestroyed() @empty() @__isRendered = false getNameOpt: -> check: String registerLabel: (lbl) -> _for = @getUniqueIdForLabel() if _for lbl.setAttribute('for', _for) return getLabel: -> @_label getMaximizeHorizontal: -> @__maximize_horizontal setForm: (form) -> CUI.util.assertImplements(form, [ "getFieldsByName" "getFieldByIdx" "getData" "getFields" ]) @__form = form if not @getForm().getFormPath return @ if @__form.getMaximizeHorizontal() if @getOpt("maximize_horizontal") != false @addClass("cui-maximize-horizontal") # @setFormDepth() @ # getFormDepth: -> # parseInt(CUI.dom.getAttribute(@DOM, "cui-form-depth")) # setFormDepth: -> # # update depth # path = @getFormPath() # CUI.dom.setAttribute(@DOM, "cui-form-depth", path.length) # @callOnOthers("setFormDepth") # path.length getFormPath: (include_self=false, path=[], call=0) -> CUI.util.assert(call < 100, "CUI.DataField.getPath", "Recursion detected.") if @getForm()?.getFormPath @getForm().getFormPath(true, path, call+1) if include_self path.push(@) return path getForm: -> @__form # short-cut to get other field getOtherField: (name) -> @__form?.getFieldsByName(name)?[0] getRootForm: -> if @__form @__form.getRootForm() else if @ instanceof CUI.Form @ else null __initDisabled: -> if @_disabled and (@_disabled == true or @_disabled.call(@, @)) @disable() return enable: -> @removeClass("cui-data-field-disabled") @callOnOthers("enable") @ disable: -> @addClass("cui-data-field-disabled") @callOnOthers("disable") @ isDisabled: -> @hasClass("cui-data-field-disabled") isHidden: -> @hasClass("cui-data-field-hidden") isShown: -> !@isHidden() updateData: (data) -> if CUI.util.isFunction(@_data) @__data = @_data.call(@, data, @) else @__data = data @displayValue() unsetData: -> delete @__data if @setDataOnOthers() @callOnOthers("unsetData") return setData: (data, init_data=true) -> if @__data and @_data and not CUI.util.isFunction(@_data) # console.debug "private data already set", @_data # we have private data set, ignore a setData from # e.g. a Form, as the private data is more important # than the Form data return CUI.util.assert(not @__data, "#{@}.setData", "data is already set.", opts: @opts, data: @__data) if CUI.util.isFunction(@_data) @__data = @_data.call(@, data, @) else @__data = data CUI.util.assert(CUI.util.isPlainObject(@__data) or @__data?.hasOwnProperty?(@getName()), "#{@}.setData", "data needs to be PlainObject or have a property \"#{@getName()}\".", data: data, opts: @opts) # console.debug "initData", @__data, @__data.hasOwnProperty if @setDataOnOthers() @callOnOthers("setData", @__data, init_data) if init_data @initData() # console.debug "setting data", @__uniqueId, @__data @ setDataOnOthers: -> not @_data_not_for_others hide: (trigger_event=false) -> @addClass("cui-data-field-hidden") @callOnOthers("hide") if trigger_event CUI.Events.trigger type: "form-check-row-visibility" node: @DOM info: element: @ @ show: (trigger_event=false) -> @removeClass("cui-data-field-hidden") @callOnOthers("show") if trigger_event CUI.Events.trigger type: "form-check-row-visibility" node: @DOM info: element: @ @ isRendered: -> @__isRendered render: -> CUI.util.assert(not @__isRendered, "#{@__cls}.render", "Cannot be called when already rendered.", opts: @opts, dataField: @) # for p of @__tmpl.map # content = @["__#{p}"] # if CUI.util.isNull(content) # continue # @append(content, p) @__isRendered = true @callOnOthers("render") if @isDisabled() @disable() if @isHidden() @hide(true) @_onRender?(@) @ displayValue: -> CUI.util.assert(not @isDestroyed(), "#{@__cls}.displayValue", "DataField already destroyed, cannot display value.", data_field: @) CUI.util.assert(@__isRendered, "#{@__cls}.displayValue", "not rendered yet, cannot display.", opts: @opts, data: @__data) @checkChanged() @callOnOthers("displayValue") start: -> CUI.util.assert(not @__isRendered, "#{@__cls}.start", "Cannot be called when already rendered.", opts: @opts, dataField: @) @__initDisabled() @render() @displayValue() @ getAllDataFields: (all=false)-> @getDataFields(true) getDataFields: (all=false, data_fields = [])-> if @isDataField() or all data_fields.push(@) @callOnOthers("getDataFields", all, data_fields) data_fields renderAsBlock: -> !!@_render_as_block isDataField: -> @hasData() # calls method on all DataFields # inside the "right" side callOnOthers: (func, args...) -> if @getFields other_fields = @getFields(func).slice(0) else other_fields = [] for df in other_fields if not df or not CUI.util.isFunction(df[func]) CUI.util.assert(false, "CUI.DataField.callOnOthers", "Field found in other fields has no Function \"#{func}\".", field: df, other_fields: other_fields) return @ df[func].apply(df, args) @ getData: -> @__data hasData: -> if not CUI.util.isEmpty(@_name) and @__data true else false hasUserData: (data) -> @_name and not CUI.util.isEmpty(data[@_name]) getArrayFromOpt: (opt, event, allowDeferred=false) -> v = @["_#{opt}"] if CUI.util.isFunction(v) arr = v.call(@, @, event) CUI.util.assert(CUI.util.isArray(arr) or (CUI.util.isPromise(arr) and allowDeferred), "#{@__cls}.getArrayFromOpt", "opts.#{opt}(dataField) did not return Array or Promise.", options: arr, opts: @opts) else arr = v return arr getName: -> @_name # data field needs to return the empty value # to init data # getDefaultValue: -> # get the current value of the data field getValue: -> @__data[@_name] checkValue: -> throw new Error("#{@__cls}.checkValue is unimplemtented.") @ setValue: (v, flags = {}) -> if not @hasData() if @isRendered() @displayValue() return @ if v == @getValue() return @ try @checkValue(v, flags) catch e if e not instanceof CUI.CheckValueError throw(e) throw(new Error("#{@__cls}.setValue: "+v+", Error: "+e)) if flags.no_trigger not in [false, true] flags.no_trigger = true @storeValue(v, flags) if @isRendered() @displayValue() @ getInitValue: -> undo = @getUndo() if not undo return undefined undo.values[0] getLastValue: -> CUI.util.assert(@_undo_support, "DataField.getLastValue", "Needs opts.undo_support to be set.", opts: @opts) undo = @getUndo() if not undo return undefined undo.values[undo.idx-1] reset: -> if undo = @getUndo() @__data[@_name] = undo.values[0] undo.values.splice(1) undo.idx = 0 @displayValue() @triggerDataChanged action: "reset" undo_idx: 0 @ undo: -> if undo = @getUndo() if undo.idx == 0 return false @__data[@_name] = undo.values[--undo.idx] @displayValue() @triggerDataChanged action: "undo" undo_idx: undo.idx return true return null @ redo: -> if undo = @getUndo() if undo.idx == undo.values.length - 1 return false @__data[@_name] = undo.values[++undo.idx] @displayValue() @triggerDataChanged action: "redo" undo_idx: undo.idx return true return null @ goto: (idx) -> if undo = @getUndo() if CUI.util.isUndef(undo.values[idx]) return false undo.idx = idx @__data[@_name] = undo.values[undo.idx] # console.debug @__uniqueId, @__data[@_name] @displayValue() @triggerDataChanged action: "goto" undo_idx: idx return true return null # data fields need to initialise the data array initData: -> @_onDataInit?(@, @__data) # console.debug "initData", @__cls, @_name, @__data if not @hasData() return @initValue() undo = @getUndo() if CUI.util.isPlainObject(undo) and CUI.util.isEmptyObject(undo) undo.values = [ @getValue() ] undo.idx = 0 if CUI.util.isUndef(@getCheckChangedValue()) @setCheckChangedValue( if undo @getInitValue() else @getValue() ) if @setDataOnOthers() @callOnOthers("initData") @ initValue: -> if CUI.util.isUndef(@__data[@_name]) # console.debug "initValue", @getName(), @__data[@_name], @getDefaultValue() @__data[@_name] = @getDefaultValue() @ setCheckChangedValue: (value) -> CUI.util.assert(@hasData(), "#{@__cls}.setCheckChangedValue", "Cannot set without data.", opts: @opts, value: value, dataField: @) if @_check_changed == false return undefined @__checkChangedValue = JSON.stringify(value) getCheckChangedValue: -> if @_check_changed == false return undefined CUI.util.assert(@hasData(), "#{@__cls}.getCheckChangedValue", "No data set.", opts: @opts) @__checkChangedValue getUndo: -> if not @hasData() or @_undo_support == false return false if CUI.util.isUndef(@__data._undo) @__data._undo = {} # console.debug @__data, @__data._undo, @_name if CUI.util.isUndef(undo = @__data._undo[@_name]) undo = @__data._undo[@_name] = {} undo # stores a user changed value and # manages old and new values storeValue: (value, flags={}) -> if not @hasData() return false store_last = flags.initial_activate != true and flags.no_store != true if store_last and (undo = @getUndo()) # console.debug(".undo[#{@_name}][#{@undo.idx}]", @__data[@_name], ">", value) undo.values[++undo.idx] = value # clean evertyhing after the undo idx undo.values.splice(undo.idx+1) @__data[@_name] = value @checkChanged() if store_last and not flags.no_trigger # console.debug @__cls+".storeValue: triggering data-changed: ", @ @triggerDataChanged action: "store" undo_idx: undo?.idx @ triggerDataChanged: (info={}) -> info.element = @ CUI.Events.trigger type: "data-changed" node: @DOM info: info isChanged: -> if not @hasData() or @_check_changed == false return undefined @getCheckChangedValue() != JSON.stringify(@getValue()) checkChanged: -> if @_check_changed == false return if @isChanged() @addClass("changed") else @removeClass("changed") @ getChangedMarker: -> if @_mark_changed == false or @_check_changed == false return @checkChanged() CUI.dom.div(CUI.DataField.changed_marker_css_class) destroy: -> @remove(true) @__tooltip?.destroy() super() @new: (field, delete_keys=[], default_data={}) -> if field instanceof CUI.DataField return field CUI.util.assert(CUI.util.isPlainObject(field), "CUI.DataField.new", "field needs to be PlainObject.", field: field, delete_keys: delete_keys, default_data: default_data) field_opts = {} for k, v of field if k == "type" type = v continue if delete_keys.indexOf(k) > -1 continue # if v instanceof CUI.Icon # field_opts[k] = v.copy() # continue field_opts[k] = v for k, v of default_data if field_opts.hasOwnProperty(k) continue field_opts[k] = v CUI.util.assert(CUI.util.isFunction(type), "CUI.DataField.new", "type is unknown: \"#{type}\".", field: field) _field = new type(field_opts) CUI.util.assert(_field instanceof CUI.DataField, "CUI.DataField.new", "field.type needs to be of class DataField, but is #{CUI.util.getObjectClass(_field)}.", field: field) return _field CUI.Events.registerEvent type: "data-changed" bubble: true