Permalink
Browse files

revamping the examples and tests

  • Loading branch information...
1 parent 4575bba commit 132cc4bb8492d427efa25ad83ffc10d48ddaf422 @vogievetsky committed Mar 5, 2012
Showing with 3,265 additions and 9,001 deletions.
  1. +5 −1 .gitignore
  2. +1 −1 LICENSE
  3. +620 −1,926 dvl.coffee
  4. +936 −2,379 dvl.js
  5. +0 −215 examples/brushlink/brush.html
  6. +0 −75 examples/brushlink/gendata.js
  7. +0 −235 examples/brushlink/histogram.js
  8. +0 −253 examples/brushlink/scatter.js
  9. +0 −221 examples/brushlink/time.js
  10. +0 −89 examples/donothing/lines.html
  11. +0 −170 examples/dropdown/list.html
  12. +0 −468 examples/heatmap/figue.js
  13. +0 −207 examples/heatmap/heatmap.html
  14. +0 −220 examples/heatmap/heatmap_cluster.html
  15. +0 −212 examples/heatmap/heatmap_text.html
  16. +0 −349 examples/heatmap/mx_heatmap.html
  17. +0 −3 examples/heatmap/mx_heatmap_data.js
  18. +0 −79 examples/heatmap/mx_heatmap_mesures.js
  19. +0 −1 examples/heatmap/text.js
  20. +0 −210 examples/histogram/histogram_text.html
  21. +0 −1 examples/histogram/text.js
  22. +0 −59 examples/panels/panels.html
  23. +0 −38 examples/select/select.html
  24. BIN examples/table/sort_arrow_down.png
  25. BIN examples/table/sort_arrow_up.png
  26. BIN examples/table/sort_none.png
  27. +0 −131 examples/table/table.html
  28. +0 −151 examples/table/table2.html
  29. +0 −169 examples/time/mouse.html
  30. +0 −144 examples/time/random-walk.html
  31. +0 −155 examples/time/simple.html
  32. 0 {js → lib/jquery}/jquery-1.6.2.min.js
  33. 0 {js → lib/jquery}/jquery-ui-1.8.7.custom.js
  34. +24 −0 lib/sizzle/LICENSE
  35. +1,376 −0 lib/sizzle/sizzle.js
  36. +28 −0 lib/sizzle/sizzle.min.js
  37. +34 −0 package.json
  38. +105 −0 test/core/register.js
  39. +121 −0 test/core/varibale.js
  40. +15 −0 test/env.js
  41. +0 −99 tests/add_change.js
  42. +0 −96 tests/add_listen.js
  43. +0 −48 tests/add_listen_run.js
  44. +0 −51 tests/add_listen_run_change.js
  45. +0 −30 tests/circular_register.js
  46. +0 −49 tests/notify_group.js
  47. +0 −21 tests/register.js
  48. +0 −149 tests/register2.js
  49. +0 −59 tests/register_change.js
  50. +0 −21 tests/register_const.js
  51. +0 −79 tests/run_tests.html
  52. +0 −24 tests/unregister.js
  53. +0 −113 tests/unregister2.js
View
6 .gitignore
@@ -1 +1,5 @@
-copy
+copy
+node_modules
+cpiper
+ideas
+_old
View
2 LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2011, Vadim Ogievetsky
+Copyright (c) 2012, Vadim Ogievetsky
All rights reserved.
Redistribution and use in source and binary forms, with or without
View
2,546 dvl.coffee
@@ -1,16 +1,8 @@
-"use strict"
-# DVL by Vadim Ogievetsky
-#
+# Vadim Ogievetsky
+
# DVL is a framework for building highly interactive user interfaces and data visualizations dynamically with JavaScript.
# DVL is based the concept that the data in a program should be the programmer’s main focus.
-# Check that we have everything we need.
-
-
-throw 'd3 is needed for now.' unless d3
-throw 'protovis is needed for now.' unless pv
-throw 'jQuery is needed for now.' unless jQuery
-
`
function lift(fn) {
@@ -45,7 +37,6 @@ function lift(fn) {
}
`
-
Array::filter ?= (fun, thisp) ->
throw new TypeError() if typeof fun isnt 'function'
@@ -61,10 +52,15 @@ debug = ->
console.log.apply(console, arguments)
return arguments[0]
-window.dvl =
- version: '0.98'
-(->
+dvl = { version: '1.0.0' }
+this.dvl = dvl
+if typeof module isnt 'undefined' and module.exports
+ module.exports = dvl
+ dvl.dvl = dvl
+
+
+do ->
array_ctor = (new Array).constructor
date_ctor = (new Date).constructor
regex_ctor = (new RegExp).constructor
@@ -77,7 +73,7 @@ window.dvl =
else
return 'regex' if v?.constructor == regex_ctor
return typeof(v)
-)()
+
dvl.util = {
strObj: (obj) ->
@@ -222,15 +218,17 @@ dvl.util = {
curRecording = null
class DVLConst
- constructor: (@value, @name) ->
- @name or= 'obj'
- @id = @name + '_const' + nextObjId
+ constructor: (val) ->
+ @value = val ? null
+ @id = nextObjId
@changed = false
constants[@id] = this
nextObjId += 1
return this
- toString: -> "|#{@id}:#{@value}|"
+ toString: ->
+ tag = if @n then @n + ':' else ''
+ return "[#{@tag}#{@value}]"
set: -> this
setLazy: -> this
update: -> this
@@ -240,8 +238,6 @@ dvl.util = {
resetChanged: -> null
notify: -> null
remove: -> null
- push: (value) -> this
- shift: -> undefined
gen: ->
that = this
if dvl.typeOf(@value) == 'array'
@@ -254,13 +250,20 @@ dvl.util = {
@value.length
else
Infinity
+ name: ->
+ if arguments.length is 0
+ return @n ? '<anon_const>'
+ else
+ @n = arguments[0]
+ return this
+
- dvl.const = (value, name) -> new DVLConst(value, name)
+ dvl.const = (value) -> new DVLConst(value)
class DVLDef
- constructor: (@value, @name) ->
- @name or= 'obj'
- @id = @name + '_' + nextObjId
+ constructor: (val) ->
+ @value = val ? null
+ @id = nextObjId
@prev = null
@changed = false
@vgen = undefined
@@ -283,12 +286,15 @@ dvl.util = {
@lazy = null
return
- toString: -> "|#{@id}:#{@value}|"
+ toString: ->
+ tag = if @n then @n + ':' else ''
+ return "[#{@tag}#{@value}]"
hasChanged: -> @changed
resetChanged: ->
@changed = false
return this
set: (val) ->
+ val = val ? null
@prev = @value unless @changed
@value = val
@vgen = undefined
@@ -313,16 +319,6 @@ dvl.util = {
return if dvl.util.isEqual(val, @value)
this.set(val)
dvl.notify(this)
- push: (val) ->
- @value.push val
- @changed = true
- # TODO: make prev work
- this
- shift: ->
- # TODO: make prev work
- val = @value.shift()
- @changed = true
- return val
get: ->
@resolveLazy()
return @value
@@ -357,8 +353,14 @@ dvl.util = {
throw "Cannot remove variable #{@id} because it has changers."
delete variables[@id]
return null
+ name: ->
+ if arguments.length is 0
+ return @n ? '<anon>'
+ else
+ @n = arguments[0]
+ return this
- dvl.def = (value, name) -> new DVLDef(value, name)
+ dvl.def = (value) -> new DVLDef(value)
dvl.knows = (v) ->
return v and v.id and (variables[v.id] or constants[v.id])
@@ -1738,19 +1740,11 @@ dvl.bind = (args) ->
listen = [parent, data, join, text, html, transition, transitionExit]
- prependStatic = (c) ->
- t = typeof c
- if t is 'string'
- return c + ' ' + staticClass
- if t is 'function'
- return (d,i) -> c.call(this,d,i) + ' ' + staticClass
- return null
-
attrList = {}
for k, v of args.attr
v = dvl.wrapConstIfNeeded(v)
if k is 'class' and staticClass
- v = dvl.apply { args: v, fn: prependStatic }
+ v = dvl.op.concat(v, ' ' + staticClass)
listen.push(v)
attrList[k] = v
@@ -1770,7 +1764,7 @@ dvl.bind = (args) ->
listen.push(v)
onList[k] = v
- out = dvl.def(null, 'out')
+ out = dvl.def(null, 'selection')
dvl.register {
listen
@@ -1829,7 +1823,6 @@ dvl.bind = (args) ->
s[a.fn](a.a1, a.a2) for a in preTrans
-
if _transition and _transition.duration?
t = s.transition()
t.duration(_transition.duration or 1000)
@@ -1840,11 +1833,12 @@ dvl.bind = (args) ->
t[a.fn](a.a1, a.a2) for a in postTrans
- s.exit().remove()
+ ex = s.exit().remove()
+ out.set(s).notify() unless e.empty() and ex.empty()
else
s = _parent.selectAll(self).remove()
+ out.set(s).notify()
- out.set(s).notify()
return
}
@@ -2022,1954 +2016,402 @@ dvl.svg.mouse = ({parent, width, height, outX, outY, fnX, fnY}) ->
-# Gens # --------------------------------------------------
-dvl.gen = {}
-dvl.gen.fromFn = (fn) ->
- gen = dvl.def(null, 'fn_generator')
- gen.setGen(fn, Infinity)
- return gen
+# HTML # --------------------------------------------------
+dvl.html = {}
-dvl.gen.fromValue = (value, acc, fn, name) ->
- value = dvl.wrapConstIfNeeded(value)
- acc = dvl.wrapConstIfNeeded(acc or dvl.identity)
- fn = dvl.wrapConstIfNeeded(fn or dvl.identity)
+##-------------------------------------------------------
+##
+## Output to an HTML attribute
+##
+dvl.html.out = ({selector, data, fn, format, invalid, hideInvalid, attr, style, text}) ->
+ throw 'must have data' unless data
+ data = dvl.wrapConstIfNeeded(data)
+ format = format ? fn
- gen = dvl.def(null, name or 'value_generator')
+ throw 'must have selector' unless selector
+ selector = dvl.wrapConstIfNeeded(selector)
- makeGen = ->
- a = acc.get()
- f = fn.get()
- v = value.get()
- if a? and f? and v?
- rv = f(a(v))
- g = -> rv
+ format = dvl.wrapConstIfNeeded(format or dvl.identity)
+ invalid = dvl.wrapConstIfNeeded(invalid or null)
+ hideInvalid = dvl.wrapConstIfNeeded(hideInvalid or false)
- gen.setGen(g)
- else
- gen.setGen(null)
+ if attr
+ what = dvl.wrapConstIfNeeded(attr)
+ out = (selector, string) -> d3.select(selector).attr(what.get(), string)
+ else if style
+ what = dvl.wrapConstIfNeeded(style)
+ out = (selector, string) -> d3.select(selector).style(what.get(), string)
+ else if text
+ out = (selector, string) -> d3.select(selector).text(string)
+ else
+ out = (selector, string) -> d3.select(selector).html(string)
- dvl.notify(gen)
+ updateHtml = () ->
+ s = selector.get()
+ a = format.get()
+ d = data.get()
+ if s?
+ if a? and d?
+ sel = out(s, a(d))
+ sel.style('display', null) if hideInvalid.get()
+ else
+ inv = invalid.get()
+ out(s, inv)
+ d3.select(s).style('display', 'none') if hideInvalid.get()
+ return
- dvl.register({fn:makeGen, listen:[value, acc, fn], change:[gen], name:'value_make_gen'})
- return gen
+ dvl.register({fn:updateHtml, listen:[data, selector, format], name:'html_out'})
+ return
-dvl.gen.fromGen = (generator, fn, name) ->
- generator = dvl.wrapConstIfNeeded(generator)
- fn = dvl.wrapConstIfNeeded(fn or dvl.identity)
+##-------------------------------------------------------
+##
+## Create HTML list
+##
+dvl.html.list = ({selector, names, values, links, selection, selections, onSelect, onEnter, onLeave, icons, extras, classStr, listClassStr, sortFn}) ->
+ throw 'must have selector' unless selector
+ selection = dvl.wrapVarIfNeeded(selection, 'selection')
+ selections = dvl.wrapVarIfNeeded(selections or [], 'selections')
+ sortFn = dvl.wrapConstIfNeeded(sortFn)
- gen = dvl.def(null, name or 'generator_generator')
+ values = dvl.wrapConstIfNeeded(values)
+ names = dvl.wrapConstIfNeeded(names or values)
+ links = dvl.wrapConstIfNeeded(links)
- makeGen = ->
- _generator = generator.gen()
- _fn = fn.get()
- if _generator? and _fn?
- g = (i) -> _fn(_generator(i))
+ icons or= []
+ for i in icons
+ i.position or= 'right'
- gen.setGen(g, generator.len)
- else
- gen.setGen(null)
+ if listClassStr?
+ listClassStr = dvl.wrapConstIfNeeded(listClassStr)
+ else
+ classFn = dvl.def(null, 'class_fn')
+ dvl.register {
+ listen: [selection, selections]
+ change: [classFn]
+ fn: ->
+ _selection = selection.get()
+ _selections = selections.get()
- dvl.notify(gen)
+ if _selection
+ if _selections
+ f = (value) ->
+ (if value is _selection then 'is_selection' else 'isnt_selection') + ' ' +
+ (if value in _selections then 'is_selections' else 'isnt_selections')
+ else
+ f = (value) ->
+ (if value is _selection then 'is_selection' else 'isnt_selection')
+ else
+ if _selections
+ f = (value) ->
+ (if value in _selections then 'is_selections' else 'isnt_selections')
+ else
+ f = null
- dvl.register({fn:makeGen, listen:[generator, fn], change:[gen], name:'generator_make_gen'})
- return gen
+ classFn.set(f).notify()
+ return
+ }
+ listClassStr = dvl.gen.fromArray(values, null, classFn)
-dvl.gen.fromArray = (data, acc, fn, name) ->
- data = dvl.wrapConstIfNeeded(data)
- acc = dvl.wrapConstIfNeeded(acc or dvl.identity)
- fn = dvl.wrapConstIfNeeded(fn or dvl.identity)
- gen = dvl.def(null, name or 'array_generator')
+ ul = d3.select(selector).append('ul').attr('class', classStr)
- d = []
- makeGen = ->
- _acc = acc.get()
- _fn = fn.get()
- _data = data.get()
- if _acc? and _fn? and _data? and _data.length > 0
- d = _data
- g = (i) ->
- i = i % d.length
- _fn(_acc(d[i], i))
+ dvl.register {
+ name: 'update_html_list'
+ listen: [names, values, links]
+ fn: ->
+ len = Math.min(
+ values.len(),
+ names.len(),
+ links.len() or Infinity
+ )
+ len = 1 if len is Infinity
- gen.setGen(g, _data.length)
- else
- gen.setGen(null)
+ ng = names.gen()
+ vg = values.gen()
+ lg = links.gen()
+ cs = listClassStr.gen()
- dvl.notify(gen)
+ onClick = (i) ->
+ val = vg(i)
+ if onSelect?(val, i) isnt false
+ link = lg(i)
+ selection.set(val)
- dvl.register({fn:makeGen, listen:[data, acc, fn], change:[gen], name:'array_make_gen'})
- return gen
+ sl = (selections.get() or []).slice()
+ i = sl.indexOf(val)
+ if i is -1
+ sl.push(val)
+ _sortFn = sortFn.get()
+ if typeof _sortFn is 'function'
+ sl.sort(_sortFn)
+ else
+ sl.sort()
+ else
+ sl.splice(i,1)
+ selections.set(sl)
+ dvl.notify(selection, selections)
+ window.location.href = link if link
+ return
-dvl.gen.fromRowData = dvl.gen.fromArray
-dvl.gen.fromColumnData = (data, acc, fn, name) ->
- data = dvl.wrapConstIfNeeded(data)
- acc = dvl.wrapConstIfNeeded(acc or dvl.identity)
- fn = dvl.wrapConstIfNeeded(fn or dvl.identity)
+ myOnEnter = (i) ->
+ val = vg(i)
+ onEnter?(val, i)
+ return
- gen = dvl.def(null, name or 'column_generator')
+ myOnLeave = (i) ->
+ val = vg(i)
+ onLeave?(val, i)
+ return
- d = []
- makeGen = ->
- a = acc.get()
- f = fn.get()
- dObj = data.get()
- if a? and f? and dObj? and d = a(dObj)
- g = (i) ->
- i = i % d.length
- f(d[i])
+ addIcons = (el, position) ->
+ icons.forEach (icon) ->
+ return unless icon.position is position
- gen.setGen(g, d.length)
- else
- gen.setGen(null)
+ classStr = 'icon_cont ' + position
+ classStr += ' ' + icon.classStr if icon.classStr
- dvl.notify(gen)
+ el.append('div')
+ .attr('class', classStr)
+ .attr('title', icon.title)
+ .on('click', (i) ->
+ val = values.gen()(i)
+ d3.event.stopPropagation() if icon.onSelect?(val, i) is false
+ return
+ ).on('mouseover', (i) ->
+ val = values.gen()(i)
+ d3.event.stopPropagation() if icon.onEnter?(val, i) is false
+ return
+ ).on('mouseout', (i) ->
+ val = values.gen()(i)
+ d3.event.stopPropagation() if icon.onLeave?(val, i) is false
+ return
+ ).append('div')
+ .attr('class', 'icon')
- dvl.register({fn:makeGen, listen:[data, acc, fn], change:[gen], name:'array_make_gen'})
- return gen
+ return
+ return
+ sel = ul.selectAll('li').data(d3.range(len))
+ a = sel.enter().append('li').append('a')
-dvl.gen.equal = (genA, genB, retTrue, retFalse) ->
- retTrue = true if retTrue is undefined
- retFalse = false if retFalse is undefined
- retTrue = dvl.wrapConstIfNeeded(retTrue)
- retFalse = dvl.wrapConstIfNeeded(retFalse)
-
- gen = dvl.def(null, 'equal_generator')
-
- makeGen = ->
- a = genA.gen()
- b = genB.gen()
- ha = a?
- hb = b?
- rtg = retTrue.gen()
- rfg = retFalse.gen()
- rtl = retTrue.len()
- rfl = retFalse.len()
- if ha and ha
- lenA = genA.len() || Infinity
- lenB = genB.len() || Infinity
- gen.setGen(((i) -> if a(i) == b(i) then rtg(i) else rfg(i)), Math.min(lenA, lenB, rtl, rfl))
- else if not ha and not hb
- gen.setGen(rtg, rtl)
- else
- gen.setGen(rfg, rfl)
-
- dvl.notify(gen)
-
- dvl.register({fn:makeGen, listen:[genA, genB, retTrue, retFalse], change:[gen], name:'equal_make_gen'})
- return gen
-
-
-generator_maker_maker = (combiner, name) ->
- return () ->
- args = Array.prototype.slice.apply(arguments)
- gen = dvl.def(null, name + '_generator')
+ addIcons a, 'left'
+ a.append('span')
+ addIcons a, 'right'
- makeGen = ->
- valid = (args.length > 0)
- gens = []
- lens = []
- for arg in args
- arg_gen = arg.gen()
- if arg_gen is null
- valid = false
- break
- gens.push arg_gen
- lens.push arg.len()
+ cont = sel
+ .attr('class', cs)
+ .on('click', onClick)
+ .on('mouseover', myOnEnter)
+ .on('mouseout', myOnLeave)
+ .select('a')
+ .attr('href', lg)
- if valid
- g = (i) ->
- gis = []
- gis.push cgen(i) for cgen in gens
- return combiner.apply(null, gis)
- gen.setGen(g, Math.min.apply(null, lens))
- else
- gen.setGen(null)
+ cont.select('span')
+ .text(ng)
- dvl.notify(gen)
+ sel.exit().remove()
return
+ }
- dvl.register({fn:makeGen, listen:args, change:[gen], name:name + '_make_gen'})
- return gen
-
-
-dvl.gen.add = generator_maker_maker(((a,b,c) -> a+b+(c||0)), 'add')
-dvl.gen.sub = generator_maker_maker(((a,b,c) -> a-b-(c||0)), 'sub')
-
-# SVG # ---------------------------------------------------
-
-dvl.svg or= {}
+ dvl.register {
+ name: 'update_class_list'
+ listen: [listClassStr]
+ fn: -> ul.selectAll('li').attr('class', listClassStr.gen())
+ }
-(->
- processOptions = (options, mySvg, myClass) ->
- throw 'No panel defined.' unless options.panel
- out =
- mySvg: mySvg
- myClass: myClass
-
- if options
- out.duration = dvl.wrapConstIfNeeded(options.duration or dvl.zero)
- out.classStr = options.classStr
- out.clip = options.clip
-
- if options.on
- out.on = {}
- eventData = options.eventData or dvl.identity
- for k, f of options.on
- do (f) ->
- out.on[k] = (i) ->
- f(eventData.gen()(i))
-
- out.visible = dvl.wrapConstIfNeeded(options.visible ? true)
+ return {
+ selection
+ selections
+ node: ul.node()
+ }
- return out
+dvl.html.dropdownList = ({selector, names, selectionNames, values, links, selection, selections, onSelect, onEnter, onLeave, classStr, listClassStr, menuAnchor, menuOffset, title, icons, sortFn, keepOnClick}) ->
+ throw 'must have selector' unless selector
+ selection = dvl.wrapVarIfNeeded(selection, 'selection')
+ selections = dvl.wrapVarIfNeeded(selections, 'selections')
+ menuAnchor = dvl.wrapConstIfNeeded(menuAnchor or 'left')
+ menuOffset = dvl.wrapConstIfNeeded(menuOffset or { x:0, y:0 })
- processProps = (props) ->
- throw 'No props defined.' unless props
- p = {}
- for k, v of props
- p[k] = dvl.wrapConstIfNeeded(v)
- return p
+ values = dvl.wrapConstIfNeeded(values)
+ names = dvl.wrapConstIfNeeded(names or values)
+ selectionNames = dvl.wrapConstIfNeeded(selectionNames or names)
+ links = if links then dvl.wrapConstIfNeeded(links) else null
+ title = dvl.wrapConstIfNeeded(title) if title
+ icons or= []
+ menuOpen = false
+ getClass = ->
+ (classStr ? '') + ' ' + (if menuOpen then 'open' else 'closed')
- gen_subHalf = generator_maker_maker(((a,b) -> a-b/2), 'sub_half')
- gen_subDouble = generator_maker_maker(((a,b) -> (a-b)*2), 'sub_double')
+ divCont = d3.select(selector)
+ .append('div')
+ .attr('class', getClass())
+ .style('position', 'relative')
- processDim2 = (props, panelWidth, left, right) ->
- if not props[left]
- if props[right]
- props[left] = dvl.gen.sub(panelWidth, props[right])
- else
- props[left] = dvl.zero
- #else
- # We have everything we need to know
+ selectedDiv = divCont.append('div')
+ .attr('class', 'selected')
- return
+ valueSpan = selectedDiv.append('span')
+ open = () ->
+ sp = $(selectedDiv.node())
+ pos = sp.position()
+ height = sp.outerHeight(true)
+ anchor = menuAnchor.get()
+ offset = menuOffset.get()
+ menuCont
+ .style('display', null)
+ .style('top', (pos.top + height + offset.y) + 'px')
- processDim3 = (props, panelWidth, left, width, right) ->
- if props[left]
- if not props[width]
- props[width] = dvl.gen.sub(panelWidth, props[left], props[right])
- #else
- # We have everything we need to know
+ if anchor is 'left'
+ menuCont.style('left', (pos.left + offset.x) + 'px')
else
- if props[width]
- props[left] = dvl.gen.sub(panelWidth, props[width], props[right])
- else
- props[left] = dvl.zero
- props[width] = panelWidth
+ menuCont.style('right', (pos.left - offset.x) + 'px')
+ menuOpen = true
+ divCont.attr('class', getClass())
return
-
- processDim4 = (props, panelWidth, left, width, right, center) ->
- if props[left]
- if not props[width]
- if props[center]
- props[width] = gen_subDouble(props[canter], props[left])
- else
- props[width] = dvl.gen.sub(panelWidth, props[left], props[right])
- #else
- # We have everything we need to know
- else
- if props[width]
- if props[center]
- props[left] = gen_subHalf(props[center], props[width])
- else
- props[left] = dvl.gen.sub(panelWidth, props[width], props[right])
- else
- if props[center]
- props[left] = dvl.gen.sub(props[center], dvl.const(10))
- props[width] = dvl.const(20)
- else
- props[left] = dvl.zero
- props[width] = panelWidth
-
+ close = () ->
+ menuCont.style('display', 'none')
+ menuOpen = false
+ divCont.attr('class', getClass())
return
+ myOnSelect = (text, i) ->
+ close() unless keepOnClick
+ return onSelect?(text, i)
- removeUndefined = (obj) ->
- for k,p of obj
- delete obj[k] if p is undefined
- obj
-
-
- initGroup = (panel, options) ->
- g = panel.g.append('svg:g')
- g.attr('class', options.classStr) is options.classStr
- #g.attr('transform', 'translate(0,0)')
- #g.attr('width', panel.width.get())
- #g.attr('height', panel.height.get())
-
- return g
+ icons.forEach (icon) ->
+ icon_onSelect = icon.onSelect
+ icon.onSelect = (val, i) ->
+ close() unless keepOnClick
+ return icon_onSelect?(val, i)
+ return
+ menuCont = divCont.append('div')
+ .attr('class', 'menu_cont')
+ .style('position', 'absolute')
+ .style('z-index', 1000)
+ .style('display', 'none')
- initClip = (panel, g, options) ->
- if options.clip
- cpid = getNextClipPathId()
- cp = g.append('svg:clipPath')
- .attr('id', cpid)
- .append('svg:rect')
- .attr('x', 0)
- .attr('y', 0)
+ dvl.html.list {
+ selector: menuCont.node()
+ names
+ values
+ links
+ sortFn
+ selection
+ selections
+ onSelect: myOnSelect
+ onEnter
+ onLeave
+ classStr: 'list'
+ listClassStr
+ icons
+ }
- dvl.register {
- name: 'clip_rect'
- listen: [panel.width, panel.height]
- fn: ->
- cp
- .attr('width', panel.width.get())
- .attr('height', panel.height.get())
- return
- }
+ $(window).bind('click', (e) ->
+ return if $(menuCont.node()).find(e.target).length
- g.attr('clip-path', 'url(#' + cpid + ')')
- return cp
+ if selectedDiv.node() is e.target or $(selectedDiv.node()).find(e.target).length
+ if menuOpen
+ close()
+ else
+ open()
else
- return null
-
- calcLength = (props) ->
- length = +Infinity
- for what, gen of props
- l = gen.len()
- length = l if l < length
- return if length == Infinity then 1 else length
-
-
- nextClipPathId = 0
- getNextClipPathId = ->
- nextClipPathId += 1
- return 'cp_' + nextClipPathId
-
-
- selectEnterExit = (g, options, props, numMarks) ->
- if props.key and props.key.gen()
- key_gen = props.key.gen()
- id_gen = (i) -> 'i_' + String(key_gen(i)).replace(/[^\w-:.]/g, '')
- join = (i) -> if this.getAttribute then this.getAttribute('id') else key_gen(i)
-
- sel = g.selectAll("#{options.mySvg}.#{options.myClass}").data(pv.range(0, numMarks), join)
-
- sel.exit().remove()
-
- m = sel.enter().append("svg:#{options.mySvg}")
- m.attr('id', id_gen) if props.key and props.key.gen()
- m.attr('class', options.myClass)
-
- if options.on
- m.on(what, onFn) for what, onFn of options.on
-
- return m
-
-
- reselectUpdate = (g, options, duration) ->
- m = g.selectAll("#{options.mySvg}.#{options.myClass}")
- m = m.transition().duration(duration) if duration > 0
- return m
-
-
-
- selectUpdate = (g, options, props, numMarks, duration) ->
- if props.key and props.key.gen()
- key_gen = props.key.gen()
- id_gen = (i) -> 'i_' + String(key_gen(i)).replace(/[^\w-:.]/g, '')
- join = (i) -> if this.getAttribute then this.getAttribute('id') else key_gen(i)
-
- sel = g.selectAll("#{options.mySvg}.#{options.myClass}").data(pv.range(0, numMarks), join)
-
- sel.exit().remove()
-
- m = sel.enter().append("svg:#{options.mySvg}")
- m.attr('id', id_gen) if props.key and props.key.gen()
- m.attr('class', options.myClass)
-
- if options.on
- m.on(what, onFn) for what, onFn of options.on
-
- proc = proc_attr[options.myClass]
-
- proc.tran(m, props, true)
-
- proc.imm(sel, props)
- sel = sel.transition().duration(duration) if duration > 0
-
- proc.tran(sel, props)
- return
-
-
- makeAnchors = (anchors, options) ->
- anchor = []
- for a, info of anchors
- av = dvl.def(null, "#{options.myClass}_anchor_#{a}")
- anchor[a] = av
- lazy = dvl.alwaysLazy(av, info.calc)
- dvl.register({fn:lazy, listen:info.dep, change:[av], name:"lazy_anchor_#{a}"})
-
- return anchor
-
-
- dvl.svg.canvas = ({selector, classStr, width, height, margin, onEvent}) ->
- throw 'no selector' unless selector
- width = dvl.wrapConstIfNeeded(width ? 600)
- height = dvl.wrapConstIfNeeded(height ? 400)
- margin = dvl.wrapConstIfNeeded(margin or { top: 0, bottom: 0, left: 0, right: 0 })
-
- canvasWidth = dvl.def(null, 'svg_panel_width')
- canvasHeight = dvl.def(null, 'svg_panel_height')
-
- svg = d3.select(selector).append('svg:svg')
- svg.attr('class', classStr) if classStr
- vis = svg.append('svg:g').attr('class', 'main')
- bg = vis.append('svg:rect').attr('class', 'background')
-
- if onEvent
- bg.on(what, onFn) for what, onFn of onEvent
-
- resize = ->
- _width = width.get()
- _height = height.get()
- _margin = margin.get()
- if _width and _height and _margin
- w = _width - _margin.left - _margin.right
- h = _height - _margin.top - _margin.bottom
-
- canvasWidth.update(w)
- canvasHeight.update(h)
-
- svg
- .attr('width', _width)
- .attr('height', _height)
-
- vis
- .attr('transform', "translate(#{_margin.left},#{_margin.top})")
- .attr('width', w)
- .attr('height', h)
-
- bg
- .attr('width', w)
- .attr('height', h)
- else
- canvasWidth.update(null)
- canvasHeight.update(null)
-
- return
-
- dvl.register {
- name: 'canvas_resize'
- listen: [width, height, margin]
- change: [canvasWidth, canvasHeight]
- fn: resize
- }
-
- return {
- svg
- g: vis
- width: canvasWidth
- height: canvasHeight
- }
-
-
- listen_attr = {}
- update_attr = {}
- proc_attr = {}
-
-
- listen_attr.panels = ['left', 'top', 'width', 'height']
- update_attr.panels = (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left = p.left
- top = p.top
- if prev or left.hasChanged() or top.hasChanged()
- left_gen = left[gen]()
- top_gen = top[gen]()
- m.attr('transform', ((i) -> "translate(#{left_gen(i)},#{top_gen(i)})"))
-
- width = p.width
- m.attr('width', width[gen]()) if width and (prev or width.hasChanged())
-
- height = p.height
- m.attr('height', height[gen]()) if height and (prev or height.hasChanged())
- return
-
- dvl.svg.panels = (options) ->
- o = processOptions(options, 'g', 'panels')
- o.clip = false unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- processDim3(p, panel.width, 'left', 'width', 'right')
- processDim3(p, panel.height, 'top', 'height', 'bottom')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- content = options.content
-
- widths = []
- heights = []
-
- render = ->
- len = calcLength(p)
-
- if len > 0
- m = selectEnterExit(g, o, p, len)
- update_attr[o.myClass](m, p, true)
-
- dimChange = panel.width.hasChanged() or panel.height.hasChanged()
- if dimChange
- dur = 0
- else
- dur = o.duration.get()
-
- m = g.selectAll('g')
- update_attr[o.myClass](m, p)
-
- ms = m[0]
- msLen = ms.length
- i = 0
- wg = p.width.gen()
- hg = p.height.gen()
- while i < msLen
- if not widths[i]
- widths[i] = dvl.def(wg(i), 'width_' + i)
- heights[i] = dvl.def(hg(i), 'height_' + i)
-
- content(i, {
- g: d3.select(ms[i])
- width: widths[i]
- height: heights[i]
- })
- i++
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'panels_render'})
- return
-
-
- listen_attr.line = ['left', 'top', 'stroke']
- update_attr.line = (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left = p.left
- if (prev or left.hasChanged())
- left_gen = left[gen]()
- m.attr('x1', left_gen)
- m.attr('x2', (i) -> left_gen(i+1))
- #m.style('display', ((i) -> left_gen(i+1)))
-
- top = p.top
- if (prev or top.hasChanged())
- top_gen = top[gen]()
- m.attr('y1', top_gen)
- m.attr('y2', (i) -> top_gen(i+1))
-
- stroke = p.stroke
- m.style('stroke', stroke[gen]()) if stroke and (prev or stroke.hasChanged())
- return
-
- dvl.svg.line = (options) ->
- o = processOptions(options, 'line', 'line')
- o.clip = true unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- processDim2(p, panel.width, 'left', 'right')
- processDim2(p, panel.height, 'top', 'bottom')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors =
- midpoint:
- dep: [p.left, p.top]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:(x(i) + x(i+1)) / 2, y:(y(i) + y(i+1)) / 2 }
- i += 1
- return as
-
- render = ->
- len = Math.max(0, calcLength(p) - 1)
-
- if o.visible.get()
- m = selectEnterExit(g, o, p, len)
- update_attr[o.myClass](m, p, true)
-
- if panel.width.hasChanged() or panel.height.hasChanged()
- dur = 0
- else
- dur = o.duration.get()
-
- m = reselectUpdate(g, o, dur)
- update_attr[o.myClass](m, p)
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height, o.visible]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'render_line'})
- makeAnchors(anchors, o)
-
-
- dvl.svg.area = (options) ->
- o = processOptions(options, 'path', 'area')
- o.clip = false unless o.clip?
- p = processProps(options.props)
- processDim3(p, panel.width, 'left', 'width', 'right')
- processDim3(p, panel.height, 'top', 'height', 'bottom')
- panel = options.panel
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors =
- midpoint:
- dep: [p.x, p.y]
- calc: ->
- length = calcLength(p)
- x = p.x.gen()
- y = p.y.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:(x(i) + x(i+1)) / 2, y:(y(i) + y(i+1)) / 2 }
- i += 1
- return as
-
- a = g.append('svg:path')
- .attr('fill', "#ff0000")
-
- render = ->
- len = calcLength(p)
- x = p.x.gen()
- y = p.y.gen()
-
- if len > 0 and x and y and o.visible.get()
- dimChange = panel.width.hasChanged() or panel.height.hasChanged()
- dur = if dimChange then 0 else o.duration.get()
-
- af = d3.svg.area()
- .x(x)
- .y1(y)
- .y0(panel.height.gen())
-
- a.attr('d', af(d3.range(len)));
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- dvl.register({fn:render, listen:[panel.width, panel.height, o.visible, p.x, p.y], name:'render_area'})
- makeAnchors(anchors, o)
-
-
- listen_attr.lines = ['left1', 'left2', 'top1', 'top2', 'stroke']
- update_attr.lines = (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left1 = p.left1
- m.attr('x1', left1[gen]()) if (prev or left1.hasChanged())
-
- left2 = p.left2
- m.attr('x2', left2[gen]()) if (prev or left2.hasChanged())
-
- top1 = p.top1
- m.attr('y1', top1[gen]()) if (prev or top1.hasChanged())
-
- top2 = p.top2
- m.attr('y2', top2[gen]()) if (prev or top2.hasChanged())
-
- stroke = p.stroke
- m.style('stroke', stroke[gen]()) if stroke and (prev or stroke.hasChanged())
- return
-
- dvl.svg.lines = (options) ->
- o = processOptions(options, 'line', 'lines')
- o.clip = true unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- p.left1 or= p.left
- p.left2 or= p.left
- p.right1 or= p.right
- p.right2 or= p.right
- p.top1 or= p.top
- p.top2 or= p.top
- p.bottom1 or= p.bottom
- p.bottom2 or= p.bottom
- removeUndefined(p)
- processDim2(p, panel.width, 'left1', 'right1')
- processDim2(p, panel.width, 'left2', 'right2')
- processDim2(p, panel.height, 'top1', 'bottom1')
- processDim2(p, panel.height, 'top2', 'bottom2')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors =
- midpoint1:
- dep: [p.left1, p.top1]
- calc: ->
- length = calcLength(p)
- x = p.left1.gen()
- y = p.top1.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:(x(i) + x(i+1)) / 2, y:(y(i) + y(i+1)) / 2 }
- i += 1
- return as
-
- midpoint2:
- dep: [p.left2, p.top2]
- calc: ->
- length = calcLength(p)
- x = p.left2.gen()
- y = p.top2.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:(x(i) + x(i+1)) / 2, y:(y(i) + y(i+1)) / 2 }
- i += 1
- return as
-
- center:
- dep: [p.left1, p.left2, p.top1, p.top2]
- calc: ->
- length = calcLength(p)
- x1 = p.left1.gen()
- y1 = p.top1.gen()
- x2 = p.left2.gen()
- y2 = p.top2.gen()
- as = []
- i = 0
- while i < length
- as.push { x:(x1(i) + x2(i)) / 2, y:(y1(i) + y2(i)) / 2 }
- i += 1
- return as
-
- render = ->
- len = calcLength(p)
-
- if o.visible.get()
- m = selectEnterExit(g, o, p, len)
- update_attr[o.myClass](m, p, true)
-
- if panel.width.hasChanged() or panel.height.hasChanged()
- dur = 0
- else
- dur = o.duration.get()
-
- m = reselectUpdate(g, o, dur)
- update_attr[o.myClass](m, p)
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height, o.visible]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'lines_render'})
- makeAnchors(anchors, o)
-
-
- listen_attr.bars = ['left', 'top', 'width', 'height', 'fill', 'stroke']
- update_attr.bars = (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left = p.left
- top = p.top
- if prev or left.hasChanged() or top.hasChanged()
- left_gen = left[gen]()
- top_gen = top[gen]()
- m.attr('transform', ((i) -> "translate(#{left_gen(i)},#{top_gen(i)})"))
-
- width = p.width
- m.attr('width', width[gen]()) if width and (prev or width.hasChanged())
-
- height = p.height
- m.attr('height', height[gen]()) if height and (prev or height.hasChanged())
-
- fill = p.fill
- m.attr('fill', fill[gen]()) if fill and (prev or fill.hasChanged())
-
- stroke = p.stroke
- m.attr('stroke', stroke[gen]()) if stroke and (prev or stroke.hasChanged())
- return
-
- dvl.svg.bars = (options) ->
- o = processOptions(options, 'rect', 'bars')
- o.clip = true unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- processDim4(p, panel.width, 'left', 'width', 'right', 'centerX')
- processDim4(p, panel.height, 'top', 'height', 'bottom', 'centerY')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors =
- center:
- dep: [p.left, p.top, p.width, p.height]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- w = p.width.gen()
- h = p.height.gen()
- as = []
- i = 0
- while i < length
- as.push { x:x(i) + w(i) / 2, y: y(i) + h(i) / 2 }
- i += 1
- return as
-
- render = ->
- len = calcLength(p)
-
- if len > 0 and o.visible.get()
- m = selectEnterExit(g, o, p, len)
- update_attr[o.myClass](m, p, true)
-
- dimChange = panel.width.hasChanged() or panel.height.hasChanged()
- if dimChange
- dur = 0
- else
- dur = o.duration.get()
-
- m = reselectUpdate(g, o, dur)
- update_attr[o.myClass](m, p)
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height, o.visible]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'bars_render'})
- makeAnchors(anchors, o)
-
-
- listen_attr.labels = ['left', 'top', 'baseline', 'align', 'text', 'color']
- update_attr.labels = (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left = p.left
- top = p.top
- angle = p.angle
- if prev or left.hasChanged() or top.hasChanged() or (angle and angle.hasChanged())
- left_gen = left[gen]()
- top_gen = top[gen]()
- if angle
- angle_gen = angle[gen]()
- m.attr('transform', ((i) -> "translate(#{left_gen(i)},#{top_gen(i)}) rotate(#{angle_gen(i)})"))
- else
- m.attr('transform', ((i) -> "translate(#{left_gen(i)},#{top_gen(i)})"))
-
- baseline = p.baseline
- if baseline and (prev or baseline.hasChanged())
- baseline_gen = baseline[gen]()
- m.attr('dy', ((i) ->
- pi = baseline_gen(i)
- if pi is 'top' then '.71em' else if pi is 'middle' then '.35em' else null))
-
- align = p.align
- m.attr('text-anchor', align[gen]()) if align and (prev or align.hasChanged())
-
- color = p.color
- m.style('fill', color[gen]()) if color and (prev or color.hasChanged())
- return
-
- dvl.svg.labels = (options) ->
- o = processOptions(options, 'text', 'labels')
- o.clip = false unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- processDim2(p, panel.width, 'left', 'right')
- processDim2(p, panel.height, 'top', 'bottom')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors = {}
-
- render = ->
- len = calcLength(p)
-
- if len > 0 and o.visible.get()
- text = p.text.gen()
- m = selectEnterExit(g, o, p, len)
- update_attr[o.myClass](m, p, true)
- m.text(text)
-
- if panel.width.hasChanged() or panel.height.hasChanged()
- dur = 0
- else
- dur = o.duration.get()
-
- #m = reselectUpdate(g, o, dur)
- m = g.selectAll("#{o.mySvg}.#{o.myClass}")
- m.text(text) if p.text.hasChanged()
- m = m.transition().duration(dur) if dur > 0
- update_attr[o.myClass](m, p)
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height, o.visible]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'labels_render'})
- makeAnchors(anchors, o)
-
-
- listen_attr.dots = ['left', 'top', 'radius', 'fill', 'stroke']
- proc_attr.dots = {
- imm: (m, p) ->
- fill = p.fill
- m.style('fill', fill.gen()) if fill and fill.hasChanged()
-
- stroke = p.stroke
- m.style('stroke', stroke.gen()) if stroke and stroke.hasChanged()
- return
-
- tran: (m, p, prev) ->
- gen = if prev then 'genPrev' else 'gen'
-
- left = p.left
- m.attr('cx', left[gen]()) if left and (prev or left.hasChanged())
-
- top = p.top
- m.attr('cy', top[gen]()) if top and (prev or top.hasChanged())
-
- radius = p.radius
- m.attr('r', radius[gen]()) if radius and (prev or radius.hasChanged())
-
- fill = p.fill
- m.style('fill', fill[gen]()) if fill and (prev or fill.hasChanged())
-
- stroke = p.stroke
- m.style('stroke', stroke[gen]()) if stroke and (prev or stroke.hasChanged())
- return
- }
-
- dvl.svg.dots = (options) ->
- o = processOptions(options, 'circle', 'dots')
- o.clip = true unless o.clip?
- p = processProps(options.props)
- panel = options.panel
- processDim2(p, panel.width, 'left', 'right')
- processDim2(p, panel.height, 'top', 'bottom')
- g = initGroup(panel, o)
- clip = initClip(panel, g, o)
-
- anchors =
- left:
- dep: [p.left, p.top, p.radius]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- r = p.radius.gen()
- as = []
- i = 0
- while i < length
- as.push { x:x(i) - r(i), y:y(i) }
- i += 1
- return as
-
- right:
- dep: [p.left, p.top, p.radius]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- r = p.radius.gen()
- as = []
- i = 0
- while i < length
- as.push { x:x(i) + r(i), y:y(i) }
- i += 1
- return as
-
- top:
- dep: [p.left, p.top, p.radius]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- r = p.radius.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:x(i), y:y(i) - r(i) }
- i += 1
- return as
-
- bottom:
- dep: [p.left, p.top, p.radius]
- calc: ->
- length = calcLength(p)
- x = p.left.gen()
- y = p.top.gen()
- r = p.radius.gen()
- as = []
- i = 0
- while i < length-1
- as.push { x:x(i), y:y(i) + r(i) }
- i += 1
- return as
-
- render = ->
- len = calcLength(p)
-
- if len > 0 and o.visible.get()
- if panel.width.hasChanged() or panel.height.hasChanged()
- dur = 0
- else
- dur = o.duration.get()
-
- selectUpdate(g, o, p, len, dur)
-
- # m = selectEnterExit(g, o, p, len)
- # update_attr[o.myClass](m, p, true)
-
- # m = reselectUpdate(g, o, dur)
- # update_attr[o.myClass](m, p)
-
-
-
- g.style('display', null)
- else
- g.style('display', 'none')
-
- return
-
- listen = [panel.width, panel.height, o.visible]
- listen.push p[k] for k in listen_attr[o.myClass]
- dvl.register({fn:render, listen:listen, name:'dots_renderer'})
- makeAnchors(anchors, o)
-)()
-
-
-# HTML # --------------------------------------------------
-
-dvl.html = {}
-
-##-------------------------------------------------------
-##
-## Output to an HTML attribute
-##
-dvl.html.out = ({selector, data, fn, format, invalid, hideInvalid, attr, style, text}) ->
- throw 'must have data' unless data
- data = dvl.wrapConstIfNeeded(data)
- format = format ? fn
-
- throw 'must have selector' unless selector
- selector = dvl.wrapConstIfNeeded(selector)
-
- format = dvl.wrapConstIfNeeded(format or dvl.identity)
- invalid = dvl.wrapConstIfNeeded(invalid or null)
- hideInvalid = dvl.wrapConstIfNeeded(hideInvalid or false)
-
- if attr
- what = dvl.wrapConstIfNeeded(attr)
- out = (selector, string) -> d3.select(selector).attr(what.get(), string)
- else if style
- what = dvl.wrapConstIfNeeded(style)
- out = (selector, string) -> d3.select(selector).style(what.get(), string)
- else if text
- out = (selector, string) -> d3.select(selector).text(string)
- else
- out = (selector, string) -> d3.select(selector).html(string)
-
- updateHtml = () ->
- s = selector.get()
- a = format.get()
- d = data.get()
- if s?
- if a? and d?
- sel = out(s, a(d))
- sel.style('display', null) if hideInvalid.get()
- else
- inv = invalid.get()
- out(s, inv)
- d3.select(s).style('display', 'none') if hideInvalid.get()
- return
-
- dvl.register({fn:updateHtml, listen:[data, selector, format], name:'html_out'})
- return
-
-
-##-------------------------------------------------------
-##
-## Create HTML list
-##
-dvl.html.list = ({selector, names, values, links, selection, selections, onSelect, onEnter, onLeave, icons, extras, classStr, listClassStr, sortFn}) ->
- throw 'must have selector' unless selector
- selection = dvl.wrapVarIfNeeded(selection, 'selection')
- selections = dvl.wrapVarIfNeeded(selections or [], 'selections')
- sortFn = dvl.wrapConstIfNeeded(sortFn)
-
- values = dvl.wrapConstIfNeeded(values)
- names = dvl.wrapConstIfNeeded(names or values)
- links = dvl.wrapConstIfNeeded(links)
-
- icons or= []
- for i in icons
- i.position or= 'right'
-
- if listClassStr?
- listClassStr = dvl.wrapConstIfNeeded(listClassStr)
- else
- classFn = dvl.def(null, 'class_fn')
- dvl.register {
- listen: [selection, selections]
- change: [classFn]
- fn: ->
- _selection = selection.get()
- _selections = selections.get()
-
- if _selection
- if _selections
- f = (value) ->
- (if value is _selection then 'is_selection' else 'isnt_selection') + ' ' +
- (if value in _selections then 'is_selections' else 'isnt_selections')
- else
- f = (value) ->
- (if value is _selection then 'is_selection' else 'isnt_selection')
- else
- if _selections
- f = (value) ->
- (if value in _selections then 'is_selections' else 'isnt_selections')
- else
- f = null
-
- classFn.set(f).notify()
- return
- }
-
- listClassStr = dvl.gen.fromArray(values, null, classFn)
-
-
- ul = d3.select(selector).append('ul').attr('class', classStr)
-
- dvl.register {
- name: 'update_html_list'
- listen: [names, values, links]
- fn: ->
- len = Math.min(
- values.len(),
- names.len(),
- links.len() or Infinity
- )
- len = 1 if len is Infinity
-
- ng = names.gen()
- vg = values.gen()
- lg = links.gen()
- cs = listClassStr.gen()
-
- onClick = (i) ->
- val = vg(i)
- if onSelect?(val, i) isnt false
- link = lg(i)
- selection.set(val)
-
- sl = (selections.get() or []).slice()
- i = sl.indexOf(val)
- if i is -1
- sl.push(val)
- _sortFn = sortFn.get()
- if typeof _sortFn is 'function'
- sl.sort(_sortFn)
- else
- sl.sort()
- else
- sl.splice(i,1)
- selections.set(sl)
-
- dvl.notify(selection, selections)
- window.location.href = link if link
- return
-
- myOnEnter = (i) ->
- val = vg(i)
- onEnter?(val, i)
- return
-
- myOnLeave = (i) ->
- val = vg(i)
- onLeave?(val, i)
- return
-
- addIcons = (el, position) ->
- icons.forEach (icon) ->
- return unless icon.position is position
-
- classStr = 'icon_cont ' + position
- classStr += ' ' + icon.classStr if icon.classStr
-
- el.append('div')
- .attr('class', classStr)
- .attr('title', icon.title)
- .on('click', (i) ->
- val = values.gen()(i)
- d3.event.stopPropagation() if icon.onSelect?(val, i) is false
- return
- ).on('mouseover', (i) ->
- val = values.gen()(i)
- d3.event.stopPropagation() if icon.onEnter?(val, i) is false
- return
- ).on('mouseout', (i) ->
- val = values.gen()(i)
- d3.event.stopPropagation() if icon.onLeave?(val, i) is false
- return
- ).append('div')
- .attr('class', 'icon')
-
- return
- return
-
- sel = ul.selectAll('li').data(d3.range(len))
- a = sel.enter().append('li').append('a')
-
- addIcons a, 'left'
- a.append('span')
- addIcons a, 'right'
-
- cont = sel
- .attr('class', cs)
- .on('click', onClick)
- .on('mouseover', myOnEnter)
- .on('mouseout', myOnLeave)
- .select('a')
- .attr('href', lg)
-
-
- cont.select('span')
- .text(ng)
-
- sel.exit().remove()
- return
- }
-
- dvl.register {
- name: 'update_class_list'
- listen: [listClassStr]
- fn: -> ul.selectAll('li').attr('class', listClassStr.gen())
- }
-
- return {
- selection
- selections
- node: ul.node()
- }
-
-
-dvl.html.dropdownList = ({selector, names, selectionNames, values, links, selection, selections, onSelect, onEnter, onLeave, classStr, listClassStr, menuAnchor, menuOffset, title, icons, sortFn, keepOnClick}) ->
- throw 'must have selector' unless selector
- selection = dvl.wrapVarIfNeeded(selection, 'selection')
- selections = dvl.wrapVarIfNeeded(selections, 'selections')
- menuAnchor = dvl.wrapConstIfNeeded(menuAnchor or 'left')
- menuOffset = dvl.wrapConstIfNeeded(menuOffset or { x:0, y:0 })
-
- values = dvl.wrapConstIfNeeded(values)
- names = dvl.wrapConstIfNeeded(names or values)
- selectionNames = dvl.wrapConstIfNeeded(selectionNames or names)
- links = if links then dvl.wrapConstIfNeeded(links) else null
- title = dvl.wrapConstIfNeeded(title) if title
- icons or= []
-
- menuOpen = false
- getClass = ->
- (classStr ? '') + ' ' + (if menuOpen then 'open' else 'closed')
-
- divCont = d3.select(selector)
- .append('div')
- .attr('class', getClass())
- .style('position', 'relative')
-
- selectedDiv = divCont.append('div')
- .attr('class', 'selected')
-
- valueSpan = selectedDiv.append('span')
-
- open = () ->
- sp = $(selectedDiv.node())
- pos = sp.position()
- height = sp.outerHeight(true)
- anchor = menuAnchor.get()
- offset = menuOffset.get()
- menuCont
- .style('display', null)
- .style('top', (pos.top + height + offset.y) + 'px')
-
- if anchor is 'left'
- menuCont.style('left', (pos.left + offset.x) + 'px')
- else
- menuCont.style('right', (pos.left - offset.x) + 'px')
-
- menuOpen = true
- divCont.attr('class', getClass())
- return
-
- close = () ->
- menuCont.style('display', 'none')
- menuOpen = false
- divCont.attr('class', getClass())
- return
-
- myOnSelect = (text, i) ->
- close() unless keepOnClick
- return onSelect?(text, i)
-
- icons.forEach (icon) ->
- icon_onSelect = icon.onSelect
- icon.onSelect = (val, i) ->
- close() unless keepOnClick
- return icon_onSelect?(val, i)
- return
-
- menuCont = divCont.append('div')
- .attr('class', 'menu_cont')
- .style('position', 'absolute')
- .style('z-index', 1000)
- .style('display', 'none')
-
- dvl.html.list {
- selector: menuCont.node()
- names
- values
- links
- sortFn
- selection
- selections
- onSelect: myOnSelect
- onEnter
- onLeave
- classStr: 'list'
- listClassStr
- icons
- }
-
- $(window).bind('click', (e) ->
- return if $(menuCont.node()).find(e.target).length
-
- if selectedDiv.node() is e.target or $(selectedDiv.node()).find(e.target).length
- if menuOpen
- close()
- else
- open()
- else
- close()
-
- return {
- node: divCont.node()
- selection
- selections
- }
- ).bind('blur', close)
-
- updateSelection = ->
- if title
- valueSpan.text(title.get())
- else
- sel = selection.get()
- if sel?
- len = values.len()
- ng = selectionNames.gen()
- vg = values.gen()
- i = 0
- while i < len
- if vg(i) is sel
- valueSpan.text(ng(i))
- return
- i++
-
- valueSpan.html('&nbsp;')
- return
-
- dvl.register {
- fn:updateSelection
- listen:[selection, selectionNames, values, title]
- name:'selection_updater'
- }
-
- return {
- node: divCont.node()
- menuCont: menuCont.node()
- selection
- }
-
-
-##-------------------------------------------------------
-##
-## Select (dropdown box) made with HTML
-##
-dvl.html.select = ({selector, values, names, selection, onChange, classStr}) ->
- throw 'must have selector' unless selector
- selection = dvl.wrapVarIfNeeded(selection, 'selection')
-
- values = dvl.wrapConstIfNeeded(values)
- names = dvl.wrapConstIfNeeded(names)
-
- selChange = ->
- val = selectEl.node().value
- return if onChange?(val) is false
- selection.update(val)
-
- selectEl = d3.select(selector)
- .append('select')
- .attr('class', classStr or null)
- .on('change', selChange)
-
- selectEl.selectAll('option')
- .data(d3.range(values.len()))
- .enter().append('option')
- .attr('value', values.gen())
- .text(names.gen())
-
-
- dvl.register {
- listen: [selection]
- fn: ->
- if selectEl.node().value isnt selection.get()
- selectEl.node().value = selection.get()
- return
- }
-
-
- #updateSelection = () ->
- # selectEl
-
- selChange()
- #dvl.register({fn: updateSelection, listen:[], change:[selection]})
- return selection
-
-
-##-------------------------------------------------------
-##
-## Table made with HTML
-##
-## This module draws an HTML table that can be sorted
-##
-## selector: Where to append the table.
-## classStr: The class to add to the table.
-## ~rowClassGen: The generator for row classes
-## ~visible: Toggles the visibility of the table. [true]
-## columns: A list of columns to drive the table.
-## column:
-## id: The id by which the column will be identified.
-## ~title: The title of the column header.
-## ~headerTooltip: The popup tool tip (title element text) of the column header.
-## classStr: The class given to the 'th' and 'td' elements in this column, if not specified will default to the id.
-## cellClassGen: The class generator for the cell
-## ~cellClick: The generator of click handlers
-## -gen: The generator that drives the column data.
-## ~sortable: Toggles wheather the column is sortable or not. [true]
-## -sortGen: The generator that will drive the sorting, if not provided then gen will be used instead. [gen]
-## ~hoverGen: The generator for the (hover) title.
-## ~showIndicator: Toggle the display of the sorting indicator for this column. [true]
-## ~reverseIndicator: Reverses the asc / desc directions of the indicator for this column. [false]
-## ~visible: Toggles the visibility of the column
-##
-## sort:
-## ~on: The id of the column on which to sort.
-## ~onIndicator: The id of the column on which the indicator is palced (defaults to sort.on)
-## ~order: The order of the sort. Must be one of {'asc', 'desc', 'none'}.
-## ~modes: The order rotation that is allowed. Must be an array of [{'asc', 'desc', 'none'}].
-## ~autoOnClick: Toggle wheather the table will be sorted (updating sort.on and/or possibly sort.order) automaticaly when clicked. [true]
-## ~indicator: [true / false]
-##
-## ~showHeader: Toggle showing the header [true]
-## ~onHeaderClick: Callback or url when the header of a column is clicked.
-## ~headerTooltip: The default herder tooltip (title element text).
-## ~rowLimit: The maximum number of rows to show; if null all the rows are shown. [null]
-##
-dvl.html.table = ({selector, classStr, rowClassGen, visible, columns, showHeader, sort, onHeaderClick, headerTooltip, rowLimit, htmlTitles}) ->
- throw 'selector has to be a plain string.' if dvl.knows(selector)
- throw 'columns has to be a plain array.' if dvl.knows(columns)
- throw 'sort has to be a plain object.' if dvl.knows(sort)
-
- visible = dvl.wrapConstIfNeeded(visible ? true)
-
- showHeader = dvl.wrapConstIfNeeded(showHeader ? true)
- onHeaderClick = dvl.wrapConstIfNeeded(onHeaderClick)
- headerTooltip = dvl.wrapConstIfNeeded(headerTooltip or null)
-
- rowLimit = dvl.wrapConstIfNeeded(rowLimit or null)
-
- sort = sort or {}
-
- sortOn = dvl.wrapVarIfNeeded(sort.on)
- sortOnIndicator = dvl.wrapVarIfNeeded(sort.onIndicator ? sortOn)
- sortOnClick = dvl.wrapConstIfNeeded(sort.autoOnClick ? true)
- sortModes = dvl.wrapConstIfNeeded(sort.modes or ['asc', 'desc', 'none'])
- modes = sortModes.get()
- sortOrder = dvl.wrapVarIfNeeded(sort.order or (if modes.length > 0 then modes[0] else 'none'))
-
- listen = [rowClassGen, visible, showHeader, headerTooltip, rowLimit, sortOn, sortOnIndicator, sortModes, sortOrder]
- listenColumnVisible = []
-
- sortIndicator = dvl.wrapConstIfNeeded(sort.indicator)
- listen.push sortIndicator
-
- numRows = dvl.def(null, 'num_rows')
-
- goOrCall = (arg, id, that) ->
- t = typeof(arg)
- if t is 'function'
- arg.call(that, id)
- else if t is 'string'
- window.location.href = arg
- return
-
- # flatten possible merge header columns
- if columns.length and columns[0].columns
- topHeader = []
- newColumns = []
- for tc in columns
- continue unless tc.columns and tc.columns.length isnt 0
- topHeader.push { title: dvl.wrapConstIfNeeded(tc.title), classStr: tc.classStr, span: tc.columns.length }
- listen.push tc.title
- for c in tc.columns
- newColumns.push c
- columns = newColumns
-
- # process columns
- for i, c of columns
- c.title = dvl.wrapConstIfNeeded(c.title or '')
- c.sortable = dvl.wrapConstIfNeeded(c.sortable ? true)
- c.showIndicator = dvl.wrapConstIfNeeded(c.showIndicator ? true);
- c.reverseIndicator = dvl.wrapConstIfNeeded(c.reverseIndicator or false);
- c.headerTooltip = dvl.wrapConstIfNeeded(c.headerTooltip or null)
- c.cellClick = dvl.wrapConstIfNeeded(c.cellClick or null)
- c.visible = dvl.wrapConstIfNeeded(c.visible ? true)
- c.hideHeader = dvl.wrapConstIfNeeded(c.hideHeader)
- c.renderer = if typeof(c.renderer) is 'function' then c.renderer else dvl.html.table.renderer[c.renderer or 'text']
- c.cellClassGen = if c.cellClassGen then dvl.wrapConstIfNeeded(c.cellClassGen) else null
- listen.push c.title, c.showIndicator, c.reverseIndicator, c.gen, c.sortGen, c.hoverGen, c.headerTooltip, c.cellClick, c.cellClassGen
- listenColumnVisible.push c.visible, c.hideHeader
- if c.renderer.depends
- listen.push d for d in c.renderer.depends
- c.uniquClass = 'column_' + i
-
- t = d3.select(selector).append('table')
- t.attr('class', classStr) if classStr
-
- colClass = (c) -> (c.classStr or c.id) + ' ' + c.uniquClass + (if c.sorted then ' sorted' else '') + (if c.sortable.get() then ' sortable' else ' unsortable')
-
- thead = t.append('thead')
- th = thead.append('tr').attr('class', 'top_header') if topHeader
- h = thead.append('tr')
- b = t.append('tbody')
-
- if topHeader
- th.selectAll('th')
- .data(topHeader)
- .enter().append('th')
- .attr('class', (d) -> d.classStr or null)
- .attr('colspan', (d) -> d.span)
- .append('div')
- .text((d) -> d.title.get());
-
- sel = h.selectAll('th')
- .data(columns)
- .enter().append('th')
- .on('click', (c) ->
- return unless c.id?
-
- goOrCall(onHeaderClick.get(), c.id, this)
-
- if sortOnClick.get() and c.sortable.get()
- if sortOn.get() is c.id
- modes = sortModes.get()
- si = modes.indexOf(sortOrder.get())
- sortOrder.set(modes[(si+1) % modes.length]).notify()
- else
- sortOn.set(c.id).notify()
- )
-
- sel.append('span') # title text container
-
- si = sortIndicator.get();
- if si
- sel.append('div')
- .attr('class', 'sort_indicator')
- .style('display', (c) -> if c.sortable.get() then null else 'none')
-
- tableLength = ->
- length = +Infinity
- for c in columns
- l = c.gen.len()
- length = l if l < length
- length = 1 if length == Infinity
-
- length
-
- makeTable = ->
- length = tableLength()
- r = pv.range(length)
-
- if visible.hasChanged()
- t.style('display', if visible.get() then null else 'none')
-
- if showHeader.hasChanged()
- thead.style('display', if showHeader.get() then null else 'none')
-
- if topHeader
- th.selectAll('th > div')
- .data(topHeader)
- .text((d) -> d.title.get());
-
- if headerTooltip.hasChanged()
- h.attr('title', headerTooltip.get());
-
- if sort
- sortOnId = sortOn.get()
- sortOnIndicatorId = sortOnIndicator.get()
- sortCol = null
- sortIndicatorCol = null
- for c in columns
- if c.sorted = (c.id is sortOnId)
- sortCol = c
- throw "sort on column marked unsortable (#{sortOnId})" unless sortCol.sortable.get()
-
- if c.sortedIndicator = (c.id is sortOnIndicatorId)
- sortIndicatorCol = c
-
- _sortOrder = sortOrder.get()
-
- if _sortOrder and sortCol
- sortGen = (sortCol.sortGen or sortCol.gen).gen()
- numeric = sortGen and typeof(sortGen(0)) is 'number'
-
- dir = String(_sortOrder).toLowerCase()
- if dir is 'desc'
- if numeric
- sortFn = (i,j) ->
- si = sortGen(i)
- sj = sortGen(j)
- if isNaN(si)
- if isNaN(sj)
- return 0
- else
- return 1
- else
- if isNaN(sj)
- return -1
- else
- return sj - si
- else
- sortFn = (i,j) ->
- return sortGen(j).toLowerCase().localeCompare(sortGen(i).toLowerCase())
- r.sort(sortFn)
- else if dir is