container.coffee | |
---|---|
Collections = Continuum.Collections
safebind = Continuum.safebind
class PlotContextView extends Continuum.ContinuumView
initialize : (options) ->
@views = {}
@views_rendered = [false]
@child_models = []
super(options)
@render()
delegateEvents: ->
safebind(this, @model, 'destroy', @remove)
safebind(this, @model, 'change', @render)
super()
generate_remove_child_callback : (view) ->
callback = () =>
return null
return callback
build_children : () -> | |
createdviews = Continuum.buildviews( @model, @views, @mget('children'), {'render_loop': true, 'scale' : 1.0}) | created_views = Continuum.build_views(
@views, @mget_obj('children'), {})
window.pc_created_views = created_views
window.pc_views = @views
return null
events : |
'click .jsp' : 'newtab' | 'click .plotclose' : 'removeplot'
'click .closeall' : 'closeall'
'keydown .plottitle' : 'savetitle'
size_textarea : (textarea) ->
scrollHeight = $(textarea).height(0).prop('scrollHeight')
$(textarea).height(scrollHeight)
savetitle : (e) =>
if e.keyCode == 13 #enter
e.preventDefault()
plotnum = parseInt($(e.currentTarget).parent().attr('data-plot_num'))
s_pc = @model.resolve_ref(@mget('children')[plotnum])
s_pc.set('title', $(e.currentTarget).val())
s_pc.save()
$(e.currentTarget).blur()
return false
@size_textarea($(e.currentTarget))
closeall : (e) =>
@mset('children', [])
@model.save()
removeplot : (e) =>
plotnum = parseInt($(e.currentTarget).parent().attr('data-plot_num'))
s_pc = @model.resolve_ref(@mget('children')[plotnum])
view = @views[s_pc.get('id')]
view.remove();
newchildren = (x for x in @mget('children') when x.id != view.model.id)
@mset('children', newchildren)
@model.save()
return false
render : () ->
super()
@build_children()
for own key, val of @views
val.$el.detach()
@$el.html('')
@$el.append("<div><a class='closeall' href='#'>Close All Plots</a></div>")
@$el.append("<br/>")
to_render = []
tab_names = {}
for modelref, index in @mget('children')
view = @views[modelref.id]
node = $("<div class='jsp' data-plot_num='#{index}'></div>" )
@$el.append(node)
title = view.model.get('title')
node.append($("<textarea class='plottitle'>#{title}</textarea>"))
node.append($("<a class='plotclose'>[close]</a>"))
node.append(view.el)
_.defer(() =>
for textarea in @$el.find('.plottitle')
@size_textarea($(textarea))
)
return null
class PlotContextViewState extends Continuum.HasProperties
defaults :
maxheight : 600
maxwidth : 600
selected : 0
class PlotContextViewWithMaximized extends PlotContextView
initialize : (options) ->
@selected = 0
@viewstate = new PlotContextViewState(
maxheight : options.maxheight
maxwidth : options.maxwidth
)
super(options)
safebind(this, @viewstate, 'change', @render)
safebind(this, @model, 'change:children', () =>
selected = @viewstate.get('selected')
if selected > @model.get('children') - 1
@viewstate.set('selected', 0)
)
events :
'click .maximize' : 'maximize'
'click .plotclose' : 'removeplot'
'click .closeall' : 'closeall'
'keydown .plottitle' : 'savetitle'
maximize : (e) ->
plotnum = parseInt($(e.currentTarget).parent().attr('data-plot_num'))
@viewstate.set('selected', plotnum)
render : () ->
super()
@build_children()
for own key, val of @views
val.$el.detach()
@$el.html('')
main = $("<div class='plotsidebar'><div>")
@$el.append(main)
@$el.append("<div class='maxplot'>")
main.append("<div><a class='closeall' href='#'>Close All Plots</a></div>")
main.append("<br/>")
to_render = []
tab_names = {}
for modelref, index in @mget('children')
view = @views[modelref.id]
node = $("<div class='jsp' data-plot_num='#{index}'></div>" )
main.append(node)
title = view.model.get('title')
node.append($("<textarea class='plottitle'>#{title}</textarea>"))
node.append($("<a class='maximize'>[max]</a>"))
node.append($("<a class='plotclose'>[close]</a>"))
node.append(view.el)
if @mget('children').length > 0
modelref = @mget('children')[@viewstate.get('selected')]
model = @model.resolve_ref(modelref)
@maxview = new model.default_view(
model : model
)
@$el.find('.maxplot').append(@maxview.$el)
else
@maxview = null
_.defer(() =>
for textarea in main.find('.plottitle')
@size_textarea($(textarea))
if @maxview
width = model.get('width')
height = model.get('height')
maxwidth = @viewstate.get('maxwidth')
maxheight = @viewstate.get('maxheight')
widthratio = maxwidth/width
heightratio = maxheight/height
ratio = _.min([widthratio, heightratio])
newwidth = ratio * width
newheight = ratio * height
@maxview.viewstate.set('height', newheight)
@maxview.viewstate.set('width', newwidth)
)
return null |
we should take this out, don't need plot context for single plot | class SinglePlotContextView extends Continuum.ContinuumView
initialize : (options) ->
@views = {}
@views_rendered = [false]
@child_models = []
@target_model_id = options.target_model_id
super(options)
@render()
delegateEvents: ->
safebind(this, @model, 'destroy', @remove)
safebind(this, @model, 'change', @render)
super()
generate_remove_child_callback : (view) ->
callback = () =>
return null
return callback
single_plot_children : () ->
return _.filter(@mget_obj('children'), (child) => child.id == @target_model_id)
build_children : () ->
created_views = Continuum.build_views(@views, @single_plot_children(), {})
window.pc_created_views = created_views
window.pc_views = @views
return null
events : |
'click .jsp' : 'newtab' | 'click .plotclose' : 'removeplot'
removeplot : (e) =>
plotnum = parseInt($(e.currentTarget).parent().attr('data-plot_num'))
s_pc = @model.resolve_ref(@mget('children')[plotnum])
view = @views[s_pc.get('id')]
view.remove();
newchildren = (x for x in @mget('children') when x.id != view.model.id)
@mset('children', newchildren)
@model.save()
return false
render : () ->
super()
@build_children()
for own key, val of @views
val.$el.detach()
@$el.html('')
to_render = []
tab_names = {}
for modelref, index in @single_plot_children()
console.log("modelref.id ", modelref.id)
view = @views[modelref.id]
node = $("<div class='jsp' data-plot_num='#{index}'></div>" )
@$el.append(node)
title = view.model.get('title')
node.append($("<p>#{title}</p>"))
node.append(view.el)
return null
class Bokeh.PlotView extends Continuum.ContinuumView
default_options : {scale:1.0}
view_options : ->
_.extend({plot_model : @model, plot_view : @}, @options)
build_renderers : ->
Continuum.build_views(@renderers, @mget_obj('renderers'), @view_options())
build_axes : ->
Continuum.build_views(@axes, @mget_obj('axes'), @view_options())
build_tools : -> |
buildviews(@model, @tools, @mget('tools'), @modelspecs()) | Continuum.build_views(@tools, @mget_obj('tools'), @view_options())
build_overlays : -> |
add ids of renderer views into the overlay spec | Continuum.build_views(@overlays, @mget_obj('overlays'), @view_options())
bind_overlays : ->
for overlayspec in @mget('overlays')
@overlays[overlayspec.id].bind_events(this)
bind_tools : ->
for toolspec in @mget('tools')
@tools[toolspec.id].bind_events(this)
tagName : 'div'
events :
"mousemove .main_can_wrapper" : "_mousemove"
"mousedown .main_can_wrapper" : "_mousedown"
_mousedown : (e) ->
for f in @mousedownCallbacks
f(e, e.layerX, e.layerY)
_mousemove : (e) ->
for f in @moveCallbacks
f(e, e.layerX, e.layerY)
initialize : (options) ->
@throttled = _.throttle(@render_deferred_components, 50)
super(_.defaults(options, @default_options))
height = if options.height then options.height else @mget('height')
width = if options.width then options.width else @mget('width')
offset = if options.offset then options.offset else @mget('offset')
if options.border_space
border_space = options.border_space
else
border_space = @mget('border_space')
@viewstate = new Bokeh.ViewState(
height : height
width : width
offset : offset
border_space : border_space
)
@renderers = {}
@axes = {}
@tools = {}
@overlays = {}
@eventSink = _.extend({}, Backbone.Events)
atm = new Bokeh.ActiveToolManager(@eventSink)
@moveCallbacks = []
@mousedownCallbacks = []
@keydownCallbacks = []
@render_init()
@render()
@build_subviews()
return this
render_init : () -> |
FIXME template | @$el.append($("""
<div class='button_bar'/>
<div class='all_can_wrapper'>
<div class='main_can_wrapper can_wrapper'>
<div class='_shader' />
<canvas class='main_can'></canvas>
</div>
<div class='x_can_wrapper can_wrapper'>
<canvas class='x_can'></canvas>
</div>
<div class='y_can_wrapper can_wrapper'>
<canvas class='y_can'></canvas>
</div>
</div>
"""))
@$el.addClass("plot_wrap")
@canvas = @$el.find('canvas.main_can')
@x_can = @$el.find('canvas.x_can')[0]
@y_can = @$el.find('canvas.y_can')[0]
@main_can_wrapper = @$el.find('.main_can_wrapper')
@x_can_wrapper = @$el.find('.x_can_wrapper')
@y_can_wrapper = @$el.find('.y_can_wrapper')
build_subviews : ()->
@build_renderers()
@build_axes()
@build_tools()
@build_overlays()
@bind_tools()
@bind_overlays()
bind_bokeh_events : () ->
safebind(this, @viewstate, 'change', @render)
safebind(this, @model, 'change:renderers', @build_renderers)
safebind(this, @model, 'change:axes', @build_axes)
safebind(this, @model, 'change:tools', @build_tools)
safebind(this, @model, 'change', @render)
safebind(this, @viewstate, 'change', @render)
safebind(this, @model, 'destroy', () => @remove()) |
FIXME document throughly when render is called vs renderdeferred should we have a "renderinit" "render" and a "rendercanvas" function adddom is called at instatiation. "render" is called for plot resizing. rendercanvas is called when changes to the canvas are desired. A ScatterRendererView would only have a "rendercanvas function | render : () ->
height = @viewstate.get('height')
width = @viewstate.get('width')
border_space = @viewstate.get('border_space')
outerheight = @viewstate.get('outerheight')
outerwidth = @viewstate.get('outerwidth')
super()
@$el.attr("width", outerwidth)
.attr('height', outerheight)
bord = border_space
xcw = @x_can_wrapper
w = width
h = height
o_w = outerwidth
o_h = outerheight
@main_can_wrapper.attr('style', "left:#{bord}px; height:#{h}px; width:#{w}px")
@x_can_wrapper.attr('style', "left:#{bord}px; top:#{h}px; height:#{bord}px; width:#{w}px")
@y_can_wrapper.attr('style', "width:#{bord}px; height:#{h}px;")
@$el.attr("style", "height:#{o_h}px; width:#{o_w}px")
wh = (el, w, h) ->
$(el).attr('width', w)
$(el).attr('height',h)
wh(@canvas, w, h)
wh(@x_can, w, bord)
wh(@y_can, bord, h)
@x_can_ctx = @x_can.getContext('2d')
@y_can_ctx = @y_can.getContext('2d')
@ctx = @canvas[0].getContext('2d')
@render_end()
render_deferred_components: (force) -> |
console.log("plotview render deferred components", @constructor, new Date() - 1) | all_views = _.flatten(_.map([@tools, @axes, @renderers, @overlays], _.values))
@ctx.clearRect(0,0, @viewstate.get('width'), @viewstate.get('height'))
for v in all_views
v.render()
class GridPlotContainerView extends Continuum.ContinuumView
tagName : 'div'
className:"gridplot_container"
default_options : { scale:1.0}
set_child_view_states : () ->
viewstates = []
for row in @mget('children')
viewstaterow = (@childviews[x.id].viewstate for x in row)
viewstates.push(viewstaterow)
@viewstate.set('childviewstates', viewstates)
initialize : (options) ->
super(_.defaults(options, @default_options))
@viewstate = new Bokeh.GridViewState();
@childviews = {}
@build_children()
@render()
return this
bind_bokeh_events : ->
safebind(this, @model, 'change:children', @build_children)
safebind(this, @model, 'change', @render)
safebind(this, @viewstate, 'change', @render)
safebind(this, @model, 'destroy', () => @remove()) |
FIXME make binding of this style equivalent to above safebind calls document semantics of when these events should be bound bokeh events | b_events : {
"change:children model" : "build_children",
"change model": "render",
"change viewstate" : "render",
"destroy model" : "remove"}
build_children : ->
childmodels = []
for row in @mget_obj('children')
for plot in row
childmodels.push(plot)
Continuum.build_views(@childviews, childmodels, {})
@set_child_view_states()
render : ->
super()
for view in _.values(@childviews)
view.$el.detach()
@$el.html('')
row_heights = @viewstate.get('layout_heights')
col_widths = @viewstate.get('layout_widths')
y_coords = [0]
_.reduceRight(row_heights[1..]
,
(x, y) ->
val = x + y
y_coords.push(val)
return val
, 0
)
y_coords.reverse()
x_coords = [0]
_.reduce(col_widths[..-1]
,
(x,y) ->
val = x + y
x_coords.push(val)
return val
, 0
)
plot_divs = []
last_plot = null
for row, ridx in @mget('children')
for plotspec, cidx in row
view = @childviews[plotspec.id]
ypos = @viewstate.position_child_y(view.viewstate.get('outerheight'),
y_coords[ridx])
xpos = @viewstate.position_child_x(view.viewstate.get('outerwidth'),
x_coords[cidx])
plot_wrapper = $("<div class='gp_plotwrapper'></div>")
plot_wrapper.attr(
'style',
"left:#{xpos}px; top:#{ypos}px")
plot_wrapper.append(view.$el)
@$el.append(plot_wrapper)
height = @viewstate.get('outerheight')
width = @viewstate.get('outerwidth')
@$el.attr('style', "height:#{height}px;width:#{width}px")
@render_end()
class GridPlotContainer extends Continuum.HasParent
type : 'GridPlotContainer'
default_view : Bokeh.GridPlotContainerView
GridPlotContainer::defaults = _.clone(GridPlotContainer::defaults)
_.extend(GridPlotContainer::defaults
,
children : [[]]
border_space : 0
)
class GridPlotContainers extends Continuum.Collection
model : GridPlotContainer
class Plot extends Continuum.HasParent
type : 'Plot'
default_view : Bokeh.PlotView
parent_properties : ['background_color', 'foreground_color',
'width', 'height', 'border_space', 'unselected_color']
Plot::defaults = _.clone(Plot::defaults)
_.extend(Plot::defaults , {
'data_sources' : {},
'renderers' : [],
'axes' : [],
'legends' : [],
'tools' : [],
'overlays' : [],
'usedialog' : false,
'title' : 'Plot' |
axes fit here | })
Plot::display_defaults = _.clone(Plot::display_defaults)
_.extend(Plot::display_defaults
,
background_color : "#eee"
foreground_color : "#333"
unselected_color : "#ccc"
)
class Plots extends Continuum.Collection
model : Plot
class PlotContext extends Continuum.HasParent
type : 'PlotContext',
default_view : Bokeh.PlotContextView
url : () ->
return super()
defaults :
children : []
render_loop : true
class PlotContexts extends Backbone.Collection
model : PlotContext
class Bokeh.ViewState extends Continuum.HasParent |
This Viewstate has height/width/border_space information Primarily used by PlotViews | initialize : (attrs, options)->
super(attrs, options)
@register_property('outerwidth',
() -> @get('width') + 2 * @get('border_space')
, false)
@add_dependencies('outerwidth', this, ['width', 'border_space'])
@register_property('outerheight',
() -> @get('height') + 2 * @get('border_space')
, false)
@add_dependencies('outerheight', this, ['height', 'border_space'])
collections : Collections |
transform our coordinate space to the underlying device (svg) | xpos : (x) ->
return x
ypos : (y) ->
return @get('height') - y |
vectorized versions of xpos/ypos, operates in place | v_xpos : (xx) ->
return xx
v_ypos : (yy) ->
height = @get('height')
for y, idx in yy
yy[idx] = height - y
return yy |
transform underlying device (svg) to our coordinate space | rxpos : (x) ->
return x
rypos : (y) ->
return @get('height') - y |
compute a childs position in the underlying device | position_child_x : (childsize, offset) ->
return @xpos(offset)
position_child_y : (childsize, offset) ->
return @ypos(offset) - childsize
defaults :
parent : null
display_defaults:
width : 200
height : 200
position : 0
offset : [0,0]
border_space : 30
class Bokeh.GridViewState extends Bokeh.ViewState
setup_layout_properties : () =>
@register_property('layout_heights', @layout_heights, true)
@register_property('layout_widths', @layout_widths, true)
for row in @get('childviewstates')
for viewstate in row
@add_dependencies('layout_heights', viewstate, 'outerheight')
@add_dependencies('layout_widths', viewstate, 'outerwidth')
initialize : (attrs, options) ->
super(attrs, options)
@setup_layout_properties()
safebind(this, this, 'change:childviewstates', @setup_layout_properties)
@register_property('height', () ->
return _.reduce(@get('layout_heights'), ((x, y) -> x + y), 0)
, true)
@add_dependencies('height', @, 'layout_heights')
@register_property('width', () ->
return _.reduce(@get('layout_widths'), ((x, y) -> x + y), 0)
, true)
@add_dependencies('width', @, 'layout_widths')
maxdim : (dim, row) ->
if row.length == 0
return 0
else
return _.max(_.map(row, ((x) -> return x.get(dim))))
layout_heights : () =>
row_heights=(@maxdim('outerheight',row) for row in @get('childviewstates'))
return row_heights
layout_widths : () =>
num_cols = @get('childviewstates')[0].length
columns = ((row[n] for row in @get('childviewstates')) for n in _.range(num_cols))
col_widths = (@maxdim('outerwidth', col) for col in columns)
return col_widths
Bokeh.GridViewState::defaults = _.clone(Bokeh.GridViewState::defaults)
_.extend(Bokeh.GridViewState::defaults
,
childviewstates : [[]]
border_space : 0
)
if not Continuum.Collections.GridPlotContainer
Continuum.Collections.GridPlotContainer = new GridPlotContainers
if not Continuum.Collections.Plot
Continuum.Collections.Plot = new Plots
if not Continuum.Collections.PlotContext
Continuum.Collections.PlotContext = new PlotContexts() |
backwards compatability | if not Continuum.Collections.CDXPlotContext
Continuum.Collections.CDXPlotContext = Continuum.Collections.PlotContext
Bokeh.GridPlotContainerView = GridPlotContainerView
Bokeh.SinglePlotContextView = SinglePlotContextView
Bokeh.PlotContextViewWithMaximized = PlotContextViewWithMaximized
Bokeh.PlotContextView = PlotContextView
|