coffeescript-ui
Version:
Coffeescript User Interface System
256 lines (213 loc) • 7.89 kB
text/coffeescript
###
* 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
###
class CUI.Element
constructor: (={}) ->
= CUI.Element.uniqueId++
= CUI.util.getObjectClass(@)
= false
= {}
= []
if CUI.Element.__dont_read_opts
return
if not
console.warn("new "++": CUI.Element::initOpts not called.", opts: )
?(@)
# DEBUG:
# console.error(CUI.util.getObjectClass(@)+"."++" created.")
return
getElementClass: ->
getUniqueId: ->
getOpts: ->
getOpt: (key) ->
[key]
hasOpt: (key) ->
.hasOwnProperty(key)
getSetOpt: (key) ->
@["_"+key]
hasSetOpt: (key) ->
initOpts: ->
= true
debug: {}
onConstruct:
check: Function
onDestroy:
check: Function
# create a new instance from the same opts
copy: ->
return new
# returns the merged opt
mergeOpt: (key, check_map={}) ->
opt = [key] or {}
for k, v of check_map
opt[k] = v
removeOpt: (key) ->
delete([key])
addOpt: (key, check_map, fn="addOpt") ->
CUI.util.assert(CUI.util.isString(key), "#{@__cls}.#{fn}", "key needs to be String", key: key, check_map: check_map)
if CUI.util.isNull(check_map)
return
CUI.util.assert(CUI.util.isPlainObject(check_map), "#{@__cls}.#{fn}", "check_map needs to be Map", key: key, check_map: check_map)
[key] = check_map
@
addOpts: (map) ->
CUI.util.assert(CUI.util.isPlainObject(map), "#{@__cls}.addOpts", "Parameter needs to be Map", map: map)
for k, v of map
@
mergeOpts: (map) ->
CUI.util.assert(CUI.util.isPlainObject(map), "#{@__cls}.mergeOpts", "Parameter needs to be Map", map: map)
for k, v of map
@
__getCheckMap: ->
readOpts: (opts = , cls = , check_map = ) ->
CUI.Element.readOpts.call(@, opts, cls, check_map, true)
# read "style" like opts for layout
readOptsFromAttr: (str) ->
opts = {}
if not str?.split
return opts
for key_value in str.split(";")
if CUI.util.isEmpty(key_value.trim())
continue
[key, value] = key_value.split(":")
key = key.trim()
value = value?.trim()
CUI.util.assert(not CUI.util.isEmpty(key) and not CUI.util.isEmpty(value), "#{@__cls}.readOptsFromAttr", "Parsing error in \"#{str}\".")
if value == "true"
value = true
else if value == "false"
value = false
else if not isNaN(parseInt(value))
value = parseInt(value)
opts[key] = value
opts
# proxy given methods to given element
proxy: (element, methods) ->
CUI.util.assert(element instanceof CUI.Element, "CUI.Element.proxy", "element given must be instance of CUI.Element.", element: element, methods: methods)
CUI.util.assert(CUI.util.isArray(methods), "Element.proxy", "methods given must be Array.", element: element, methods: methods)
for method in methods
do (method) =>
@[method] = =>
element[method].apply(element, arguments)
@
destroy: ->
# clean mapped values
CUI.util.assert(, CUI.util.getObjectClass(@)+".destroy", "__mapped_keys not found, that means the top level constructor was not called.", element: @)
for k in
delete("@_#{k}")
= []
?(@)
= true
isDestroyed: ->
: 0
# this is a hackery function to return just
# the opts keys for a given class
: ->
CUI.Element.__dont_read_opt = true
element = new(@)
delete(CUI.Element.__dont_read_opts)
Object.keys(element.__getCheckMap())
: (opts, cls, check_map, map_values) ->
if map_values != true and map_values != false
if @ != Element
CUI.util.assert(@ != window, "CUI.Element.readOpts", "this cannot be window.")
map_values = true
if not CUI.defaults.asserts
# fast path
set_opts = {}
for k, v of check_map
if opts.hasOwnProperty(k) and opts[k] != undefined
set_opts[k] = @["_#{k}"] = opts[k]
if map_values
.push(k)
else if v.hasOwnProperty("default")
set_opts[k] = @["_#{k}"] = v.default
if map_values
.push(k)
return set_opts
#
CUI.util.assert(CUI.util.isPlainObject(opts), cls, "opts needs to be PlainObject.", opts: opts, check_map: check_map)
CUI.util.assert(CUI.util.isPlainObject(check_map), cls, "check_map needs to be PlainObject.", opts: opts, check_map: check_map)
set_opts = {}
for k, v of check_map
# console.debug "check map", cls, k, v.check, v.check?.name
if opts.hasOwnProperty(k) and opts[k] != undefined
value = opts[k]
exists = true
else if v.hasOwnProperty("default")
value = v.default
exists = true
else
exists = false
if CUI.util.isFunction(v.mandatory)
mandatory = v.mandatory.call(@, value)
else
mandatory = v.mandatory
if not exists
if mandatory
CUI.util.assert(false, cls, "opts.#{k} needs to be set.", opts: opts, check_map: check_map)
else
continue
else if v.deprecated
if v.deprecated.length > 0
post = v.deprecated
else
post = ""
# console.error("%c #{cls}: opts.#{k} is deprecated.", "font-weight: bold; color: red; font-size: 1.2em;", post)
console.warn("#{cls}: opts.#{k} is deprecated.", value)
if v.check and (not CUI.util.isNull(value) or mandatory)
if CUI.util.isArray(v.check)
CUI.util.assert(v.check.indexOf(value) > -1, cls, "opts.#{k} needs to be one of [\"#{v.check.join('\",\"')}\"].", opts: opts)
else if v.check == Boolean or v.check == String or v.check == Function or v.check == Array
CUI.util.assertInstanceOf.call(@, k, v.check, undefined, value)
else if CUI.util.isFunction(v.check) and not v.check.__super__ # super is from coffeescript and shows us that we have a "class" here
CUI.util.assert(CUI.util.isEmpty(v.check.name) or v.check.name == "check", cls, "#{k}.check is \"#{v.check.name}\" but has no \"__super__\" method. Use \"extends CUI.Element\" or \"extends CUI.Dummy\" to fix that.", opts: opts, key: k, value: v)
check = v.check.call(@, value)
if not(CUI.util.isNull(check) or CUI.util.isBoolean(check) or CUI.util.isString(check))
_check = check
console.error("CUI.Element.readOpts: check needs to return Boolean, null, undefined or String.", "opts:", opts, "opt:", v, "return:", _check)
if _check
check = true
else
check = false
if check != true
if CUI.util.isString(check)
err = check
else
err = "needs to match\n\n"+v.check.toString()
CUI.util.assert(false, cls, "opts.#{k}: #{err}.", opts: opts)
else if CUI.util.isPlainObject(v.check)
value = CUI.Element.readOpts(value, cls+" [opts."+k+"]", v.check)
else if CUI.util.isNull(value) and mandatory
CUI.util.assert(false, cls, "opts.#{k} is mandatory, but is #{value}.", opts: opts)
else
CUI.util.assertInstanceOf.call(@, k, v.check, undefined, value)
# convenient mapping this to our space
if map_values
@["_#{k}"] = value
if @ instanceof CUI.Element
.push(k)
set_opts[k] = value
for k, v of opts
# options starting with a "_" are considered private
if v != undefined and not set_opts.hasOwnProperty(k) and not k.startsWith("_")
console.warn("#{cls}: opts.#{k}, not supported. check_map: ", check_map, "opts:", opts)
# delete(opts[k])
# console.warn "#{@__cls}.opts = ", CUI.util.dump(set_opts)
#
set_opts