• Jump To … +
    server.coffee src/actionknob.coffee src/autosem.coffee src/bitbucket_kba.coffee src/browserlog.coffee src/datareduction.coffee src/dci.coffee src/dciknob.coffee src/deeseeeye.coffee src/dnd.coffee src/doof.coffee src/formurla-mngr.coffee src/fractalpanel.coffee src/fractalpanel_test.coffee src/front.coffee src/ingestor.coffee src/kbabitbucket.coffee src/knobctrl.coffee src/lib_test.coffee src/nanoclock.coffee src/noodb.coffee src/noodbabstract.coffee src/noodbbrowser.coffee src/noodbbrowser_test.coffee src/noodbsec.coffee src/noorauth.coffee src/noorplugin.coffee src/noorquery.coffee src/noorvm.coffee src/noorwrite.coffee src/quadparser.coffee src/quadparsern3.coffee src/rbac.coffee src/reactor.coffee src/rebase.coffee src/rsrcidx.coffee src/sandboxactions.coffee src/screen_ctx.coffee src/spogi.coffee src/tabular_widget.coffee src/visctrl.coffee src/voicesknob.coffee src/whowhen.coffee src/xsd2native.coffee
  • ¶

    FractalPanel

    FractalPanel is an extensible dashboard layout system integrated with FormURLa so the page URL dynamically represents and controls that layout. Drag and drop operations within and between the panels can create new panels. Furthermore, a system of knobbies in the corners and around the permitter of the panes provide per-pane context-sensitive menus and controls. Overall the system functions as the Tiling Window Manager for the Nooron Collaboratory.

    Here is an example of an URL and the resultant layout

    /beside(above(a,b),beside(c,d))
      +---+---+---+
      | a |   |   |
      +---+ c | d |
      | b |   |   |
      +---+----+--+
    

    In this example a, b, c and d can be any “FormURLa Function” such as print(a), graph(...), chart(...), etc where … represents arguments.

    Knobbies

    Each of the panes is by default provided with ‘knobbies’ around the permitter such as:

    • visualization – offers alternative visualizations for the current data
    • voices – provides for selection of discriminators to govern data sourcing
    • vantage – provides nameable ‘worldviews’ to govern data display
    • action – displays dynamically sourced menu items
    • search – searches with the pane
    • close – button to close the pane

    When clicked each of the knobbies (except the close button) displays a pluggable panel to act as a visualization to make the choice or perform the needed action. The visualizations used may themselves be chosen by the users. This provides for the excitement of (pointless) infinite descent.

    Dragging and dropping (Dragon Droppings)

    Objects within the visualzations represent semantically addressable entities. Once dragging of those entities begins then North, South, East and West ‘drop zones’ appear on the edges of all panes which could be split on those sides to provide new places to display the entity when droppped.

    golden_fraction = 61.803
    golden_percent = "#{golden_fraction}%"
    golden_complement = "#{100 - golden_fraction}%"
    direction =
      north: 'height'
      south: 'height'
      east: 'width'
      west: 'width'
      inside: false
    orientation =
      north: 'horizontal'
      south: 'horizontal'
      east: 'vertical'
      west: 'vertical'
      horizontal: ['north', 'south']
      vertical: ['west', 'east']
    other =
      north: 'south'
      south: 'north'
      east: 'west'
      west: 'east'
    div_loc =
      north: 'bottom'
      south: 'bottom'
      east: 'right'
      west: 'right'
    side_to_seq = # should standardize on 'side' over 'edge' because 'inside' is better than false
      north: 'first'
      south: 'second'
      east: 'second'
      west: 'first'
      inside: 'first'
    colors = [
      "rgba(255, 0, 0, 0.5)"
      "rgba(0, 255, 0, 0.5)"
      "rgba(0, 0, 255, 0.5)"
      "rgba(255, 255, 0, 0.5)"
      "rgba(255, 127, 0, 0.5)"
      "rgba(127, 0, 127, 0.5)"
    ]
    
    frac_and_elem_agree = (frac) ->
      frac_elem_id = frac.elem.attr('id')
      if frac.frac_id isnt frac_elem_id
        equality = "#{frac.frac_id} <> #{frac_elem_id}"
        console.warn equality
        console.warn "frac",frac
        debugger
  • ¶

    else console.info “#{frac.frac_id} == #{frac_elem_id}”

      return
    
    
    lineage =
      2: [1]
      3: [1, 5]
      4: [1]
      6: [5]
      5: [4]
    check_lineage = (a_frac) ->
      return
      id = a_frac.frac_id
      if a_frac.parent_frac
        pid = a_frac.parent_frac.frac_id
      if id? and pid? and lineage[id]?
        if pid in lineage[id]
          console.warn "LINEAGE: #{id} in #{lineage[id]} as expected"
        else
          msg = "LINEAGE: expected #{id} in #{lineage[id]} not #{pid}"
          console.error msg
          alert msg
      else
        msg = "LINEAGE: frac_id:#{id}, parent_frac.frac_id: #{pid}"
        console.error msg
    
    class FractalPanel
  • ¶

    This class is essentially a wrapper around split-pane-component divs, represented as @elem Like a window has a pane, a FractalPanel always has at least one FractalComponent. FractalPanels can have either one or two FractalCompents. If one then its ‘side’ is ‘inside’ otherwise the two FractalCompents have sides ‘south’ and ‘north’ or ‘east’ and ‘west’. When a FractalComponent which is a single child is told to split it calls the add_fracinner method on its parent_frac, a FractalPanel, which creates another FracalCompnent and a moveable divider between them.

      constructor: (parent, @name, args) -> # FractalPanel
  • ¶

    console.log “FractalPanel.constructor()”

        args ?= {}
        @propagate ?= args.propagate or {}
        @defaults =
          always_show_NEWS_buttons: false
          inject_tree_path: false
          color_panels_uniquely: false
          make_frame: false
          always_show_close_button: false
          suppress_content_area: false
          second_frac: false
          content_area_editable: false
          show_NEWS_handle:
            N: true
            E: true
            W: true
            S: true
        _.extend(@, @defaults, @propagate, args)
    
        @parent = $(parent) # the parent_elem
        if @make_frame
          frame = $("""<div class="split-pane-frame"></div>""")
          @parent.append(frame)
          @parent = frame
        @direction = false # when split, becomes either 'width' or 'height'
    
        if not @elem
          @elem = $('<div></div>')
          @elem.addClass('fractalpanel_outer')
          @elem.addClass('split-pane')
          FractalPanel.assign_random_id(@elem, "fp_")
          @frac_id = @elem.attr("id")
  • ¶

    ##

          console.log "@parent_frac", @parent_frac
          console.log("just added FractalPanel.id: #{@frac_id} " +
            "to parent: #{@parent_frac?frac_id}")
  • ¶

    ##

          @parent.append(@elem)
        else
          alert "@elem is already defined"
    
        if not @first_frac?
          @first_frac = new FractalComponent(@, "oink", {propagate: @propagate})
        if not @first_frac.parent_frac
          @first_frac.parent_frac = @
  • ¶

    @first_frac.fracs_and_elems_agree(“ORIGIN+0”) # should fail

        if not @elem.children().length
          @elem.append(@first_frac.elem)
          @first_frac.parent_frac = @
        @splitPane()
        @first_frac.fracs_and_elems_agree("ORIGIN+1")
        if not @parent_frac
          @first_frac.elem.css('height','100%')
        if @first_frac.parent_frac isnt @
          debugger
        check_lineage(@)
    
      @last_ids: {}
      @color_idx: 0
      @next_id: (prefix) ->
        if not @last_ids[prefix]
          @last_ids[prefix] = 0
        @last_ids[prefix] = @last_ids[prefix] + 1
        return prefix + @last_ids[prefix]
      @get_color: ->
        retval = colors[@color_idx]
        @color_idx++
        if @color_idx >= colors.length - 1
          color_idx = 0
        return retval
      @assign_random_id: (what, prefix) ->
  • ¶

    rand_id = prefix + new Date().getTime()

        rand_id = @next_id(prefix)
        $(what).attr('id', rand_id)
        rand_id
    
      deknob: -> # remove all the knobs
        $('.fractalpanel_knob_corner').remove()
        $('.fractalpanel_knob_edge').remove()
    
      discontent: -> # remove all contents
        $('.fractalpanel_content_area').html('')
    
      move_divider_to: (x,y) ->
  • ¶

    Accept x or y (not both) which are int values in pixels and move the divider there

        if x?
          pct = 100 * (1 - (x/parseInt(@elem.css('width').replace(/px/,'')))) + "%"
          @set_right(pct)
        if y?
          pct = 100 * (1 - (y/parseInt(@elem.css('height').replace(/px/,'')))) + "%"
          @set_bottom(pct)
    
      set_bottom: (bottom) ->
        $(@north).css('bottom',bottom)
        $(@divider).css('bottom',bottom)
        $(@south).css('height',bottom)
    
      set_right: (right) ->
        $(@west).css('right',right)
        $(@divider).css('right',right)
        $(@east).css('width',right)
    
      split_frac_on_side: (id, side) ->
        frac = @get_frac(id)
        if frac
          return frac.split(side)
    
      get_frac: (id) -> # FractalPanel
        if @first_frac.frac_id is id
          return @first_frac
        if @second_frac and @second_frac.frac_id is id
          return @second_frac
        return @first_frac.get_frac(id) or (@second_frac and @second_frac.get_frac(id))
    
      show_drop_zones: (args) => # FractalPanel
        @first_frac.show_drop_zones(args)
        if @second_frac
          @second_frac.show_drop_zones(args)
    
      hide_drop_zones: => # FractalPanel
        @first_frac.hide_drop_zones()
        if @second_frac
          @second_frac.hide_drop_zones()
    
      reorient: (a_frac, new_edge_id, proportion) -> # FractalPanel
  • ¶

    alert(“why is ‘E’ ‘x’ leaving div.id=2 as fractalpanel_west?”) change the side (north|south|east|west|false) of a_frac

        old_edge_id = a_frac.my_side #or 'inside'
  • ¶

    console.group “reorient(#{a_frac.frac_id}, new_edge_id:#{new_edge_id}, proportion:#{proportion}) old_edge_id:#{old_edge_id}”

        a_frac.my_side = new_edge_id
        elem = a_frac.elem
        was_inside = old_edge_id is 'inside'
  • ¶

    console.log “was”,old_edge_id debugger

        if was_inside
          elem.css("height","")
          elem.css("width","")
        else
          elem.removeClass("fractalpanel_#{old_edge_id}")
          elem.css(direction[old_edge_id],"")  # eg width
          elem.css(div_loc[old_edge_id],"")    # eg right
  • ¶

    console.log “now”,new_edge_id

        now_inside = new_edge_id is 'inside'
        if now_inside
          elem.css("margin-#{div_loc[new_edge_id]}", "") # eg margin-bottom
          elem.css("#{div_loc[new_edge_id]}", "") # eg bottom
          elem.css('height', '100%')
        else
          elem.addClass("fractalpanel_#{new_edge_id}")
          if side_to_seq[new_edge_id] is 'first'
            elem.css(div_loc[new_edge_id], proportion)
          else
            elem.css(direction[new_edge_id], proportion)
        console.groupEnd()
    
      replace: (goner_frac, comer_frac) -> # FractalPanel
  • ¶

    replace the goner with the comer then do goner.tear_down()

        the_goner_side = goner_frac.my_side
        the_direction = direction[the_goner_side]
        console.groupCollapsed "#{@frac_id}.replace(#{goner_frac.frac_id}, #{comer_frac.frac_id}) the_direction:", the_direction, "the_goner_side:", the_goner_side
        if @first_frac is goner_frac
          which = 'first'
        else if @second_frac is goner_frac
          which = 'second'
        else
          msg = [goner_frac.frac_id, "can't be replaced by",comer_frac.frac_id,
            "because it's not on",@frac_id].join(' ')
          throw msg
        old_div_loc = goner_frac.elem.css(div_loc[the_goner_side])
        goner_frac.elem.before(comer_frac.elem)
        if the_direction
          proportion = goner_frac.elem.css(the_direction) or golden_complement
        else
          proportion = golden_complement
        if side_to_seq[the_goner_side] is 'first'
          proportion = old_div_loc
        @reorient(comer_frac, the_goner_side, proportion)
        @[comer_frac.my_side] = comer_frac
        @[which + '_frac'] = comer_frac
        comer_frac.parent_frac = @
  • ¶

    @[which] = comer_frac.elem # DEPRECATE should be eliminated when @second and @first go

        check_lineage(comer_frac)
        check_lineage(comer_frac.parent_frac)
        console.groupEnd()
    
      tear_up: -> # FractalPanel
  • ¶

    tear_up() should collapse any redundant upward nesting but this might be covered by remove_inner and replace. Though perhaps tear_up() comes into play when a branch looses its only leaf.

        console.error "tear_up() is NOOP"
        return @
    
      tear_down: -> # FractalPanel
  • ¶

    console.log “FractalPanel.tear_down() id:#{@frac_id}”

        if @first_frac
          @first_frac.elem.remove()
        if @second_frac
          @second_frac.elem.remove()
        if @elem
          @elem.remove()
          @elem = false
    
      splitPane: -> # FractalPanel
        $('div.split-pane').splitPane()
        $('.split-pane-resize-shim + .split-pane-resize-shim').remove()
    
      add_fracpanel: (edge_id, inner_frac) -> # FractalPanel
  • ¶

    Make a new FractalPanel, move inner_frac within it, then split the FractalPanel Recipe:

    • make a shallow_clone_component of inner_frac.elem
    • put the shallow_clone component after the original
    • make a new FractalPanel within the shallow_clone_component
    • move the original within the shallow_clone_component
        console.groupCollapsed "#{@frac_id}.add_fracpanel(#{edge_id}," +
          " #{inner_frac.my_side})"
        clone_side = inner_frac.my_side
        component = inner_frac.elem
        clone_elem = @clone_component(component) # make shallow clone
        component.after(clone_elem) # put the clone after the original
        clone_elem.css('background-color', '')
        clone_elem.css('margin-bottom', '')
        clone_id = FractalPanel.assign_random_id(clone_elem, "fp_")
        clone_frac = new FractalComponent @, "#{clone_elem.attr('id')}",
          elem: clone_elem
          suppress_content_area: true
          suppress_corner_buttons: true
          show_NEWS_handle: {} # suppress handles on FractalComponents containing FractalPanels
          frac_id: clone_id
          my_side: clone_side
        @[side_to_seq[clone_side]+"_frac"] = clone_frac
  • ¶

    @[side_to_seq[clone_side]] = clone_elem

        @[clone_side] = clone_frac
        component.css('margin-bottom', '')
        if not (inner_frac.my_side is 'inside')
          divider_location = div_loc[inner_frac.my_side]
          tmp = component.css(divider_location)
          component.css(divider_location, "")
          new_dir = direction[edge_id]
        new_fracpanel = new FractalPanel clone_elem, edge_id,
          propagate: @propagate
  • ¶

    my_side: ‘inside’ #inner_frac.my_side

          my_side: inner_frac.my_side
          parent_frac: clone_frac
          first_frac: inner_frac
  • ¶

    first: component

        @[inner_frac.my_side] = new_fracpanel
        clone_frac.panel = new_fracpanel
        retval = new_fracpanel.add_fracinner(edge_id)
        console.groupEnd()
        return retval
    
      clone_component: (component) -> # FractalPanel
        clone = $('<div></div>')
        for attr in component[0].attributes
          if attr.specified
            clone.attr(attr.name, attr.value)
        return clone
    
      add_fracinner: (edge_id) -> # FractalPanel
        console.groupCollapsed "#{@frac_id}.add_fracinner(#{edge_id})"
        @divider = $("<div></div>")
        @divider.addClass("split-pane-divider")
        @divider.attr('title', @name)
        @reorient(@first_frac, other[edge_id], "50%")
        new_component = new FractalComponent @, "#{@name}.#{edge_id}",
          propagate: @propagate
          my_side: edge_id
        @second_frac = new_component
  • ¶

    @second = @second_frac.elem

        @reorient(@second_frac, edge_id, "50%")
        if edge_id in ['east','south'] # the new divs should come AFTER
          @first_frac.elem.after(@second_frac.elem)
          @first_frac.elem.after(@divider)
          fraction = golden_complement
        else # the new divs should come before
          @first_frac.elem.before(@second_frac.elem)
          @first_frac.elem.before(@divider)
          fraction = golden_percent
        @north = false
        @south = false
        @east = false
        @west = false
        @inside = false
        @[edge_id] = @second_frac.elem
        @[@first_frac.my_side] = @first_frac.elem
        if edge_id in ['east','west']
          @elem.addClass("vertical-percent")
          @divider.addClass('vertical-divider')
          @direction = "width"
          @west.css("right", fraction)
          @east.css("width", fraction)
        else
          @north.css('height','')
          @elem.addClass("horizontal-percent")
          @divider.addClass('horizontal-divider')
          @direction = "height"
          @north.css("bottom", fraction)
          @south.css("height", fraction)
        @divider_location = div_loc[edge_id]
        @divider.css(@divider_location, fraction)
        @second_frac.update_close_button()
        @splitPane()
        @first_frac.fracs_and_elems_agree("add_fracinnger() first_frac:")
        @second_frac.fracs_and_elems_agree("add_fracinner() second_frac:")
        console.groupEnd()
        return new_component
    
      remove_inner: (frac_inner) -> # FractalPanel
  • ¶

    console.groupCollapsed “FractalPanel_#{@frac_id}.remove_inner(#{frac_inner.frac_id} (will replace #{@parent_frac?frac_id}.#{@parent_frac?my_side}))”

        victim_frac_id = frac_inner.frac_id
        @first_frac.fracs_and_elems_agree("ZZZZZ")
        if @second_frac
          @second_frac.fracs_and_elems_agree("YYYYY")
        if not @elem
          alert "elem is missing"
        if frac_inner is @first_frac
          remaining = "first_frac"
          removing = "first_frac"
          @first_frac = @second_frac
        else
          remaining = "first_frac"
          removing = "second_frac"
        frac_inner.elem.remove()
  • ¶

    @first_frac.fracs_and_elems_agree(“UUUUU”)

        @second_frac = false
  • ¶

    @second = false # TODO remove

        if @divider
          @divider.remove()
        @divider = false
        @north = false
        @south = false
        @east = false
        @west = false
        @direction = false
        @remove_percent_classes()
        if @parent_frac # ie is this NOT the root frac?
          if @first_frac # is there, in fact, still an inner frac?
            @first_frac.fracs_and_elems_agree("AAAAA", false)
            if @parent_frac.parent_frac?
              @parent_frac.parent_frac.replace(@parent_frac, @first_frac)
            else
              alert "while #{victim_frac_id}.close() #{@frac_id} has no grandmother"
            @first_frac.fracs_and_elems_agree("BBBBB")
            @parent_frac.tear_down()
  • ¶

    @reorient(@first_frac, ‘inside’) @first_frac.fracs_and_elems_agree(“CCCCC”)

        else
          if false
            console.warn "there is no @parent_frac"
            console.warn "     my_side:", @my_side
            console.warn "  first_frac:", @first_frac
            console.warn "  first_frac:", @first_frac?frac_id
            console.warn " second_frac:", @second_frac
            console.warn " second_frac:", @second_frac?frac_id
          if @first_frac
            @reorient(@first_frac, 'inside')
        console.groupEnd()
    
      remove_percent_classes: -> # FractalPanel
        @elem.removeClass("vertical-percent")
        @elem.removeClass("horizontal-percent")
    
      is_already_split: () ->
        return @divider?
    
      find_furthest_ancestor_with_same_direction: (direction_to_match) ->
        if not direction_to_match? # commencing recursion at this level
          direction_to_match = @direction
        if @parent_frac?
          if @parent_frac.direction is direction_to_match
            return @parent_frac.find_furthest_ancestor_with_same_direction(direction_to_match)
          else
            return @ # we are the furthest parent with matching direction
        else
          return @ # we are the top of the tree
    
    class FractalComponent
  • ¶

    This class is essentially a wrapper around split-pane-component divs, represented as @elem. A FractalCompnent always has a @parent_frac which is a FractalPanel.

      constructor: (@parent_frac, @name, args) -> # FractalComponent
  • ¶

    console.log “FractalComponent.constructor()”

        args ?= {}
        @propagate ?= args.propagate or {}
        @defaults =
          always_show_NEWS_buttons: true
          inject_tree_path: false
          color_panels_uniquely: false
          make_frame: false
          always_show_close_button: false
          suppress_corner_buttons: false
          my_side: 'inside' # will be north, south, east, or west after split
          insert_my_side_on_mouseover: false
          content_area_filler: ''
          action_button__title: "Action Menu..."
          vantages_button__title: "Vantages Menu..."
          visualization_button__title: "Visualization Menu..."
          voices_button__title: "Voices Menu..."
          close_button__title: "Close"
        _.extend(@, @defaults, @propagate, args)
    
        if not @elem?
          @elem = $("<div></div>")
        @elem.addClass('fractalpanel_inner')
        @elem.addClass('split-pane-component')
        if not @parent_frac?
          @elem.height("100%")
        if @color_panels_uniquely # add arg to set colors like this
          color_spec = FractalPanel.get_color()
  • ¶

    @elem.text(color_spec)

          @elem.css("background-color", color_spec)
        if not @frac_id?
          @frac_id = FractalPanel.assign_random_id(@elem, "fp_")
        @add_NEWS_handles()
        if not @suppress_corner_buttons
          @add_TBLR_handles()
        if not @suppress_content_area
          @add_content_area()
  • ¶

    @fracs_and_elems_agree(“FractalComponent():”)

        check_lineage(@)
    
      get_frac: (id) -> # FractalComponent
        return @panel and @panel.get_frac(id)
    
      add_content_area: -> # FractalComponent
        content_area_div = """
          <div class="fractalpanel_content_area">
            #{@content_area_filler}
          </div>
        """
        @content_area = $(content_area_div)
        if @content_area_editable
          @content_area.attr('contentEditable','true')
        FractalPanel.assign_random_id(@content_area, "ca_")
        @elem.append(@content_area)
        if @insert_my_side_on_mouseover
          @content_area.mouseenter () =>
            @content_area.append "<div>#{@my_side}</div>"
        @show_identifier_if_desired()
    
      show_identifier_if_desired: -> # FractalComponent
        if @inject_tree_path
          @content_area.append """<h1>[ #{@elem.attr('id')} ]</h1>"""
  • ¶

    @content_area.append(‘

    line

    \n’.repeat(100))

      fracs_and_elems_agree: (label, expecting) -> # FractalComponent
        expecting ?= true
        parent_elem = @elem.parent()
        parent_elem_id = parent_elem.attr('id')
        if @parent_frac.frac_id isnt parent_elem_id
          inequality = "#{label}: " +
            "#{@frac_id}.parent_frac.frac_id:#{@parent_frac.frac_id} " +
            "<> @elem.parent().attr('id'):#{parent_elem_id}"
          console.error inequality
  • ¶

    console.error “parent_elem”, parent_elem

          if expecting
            debugger
    
      close: (target) -> # FractalComponent
  • ¶

    console.error “#{@frac_id}.close() parent_frac?frac_id:#{@parent_frac?frac_id}” console.error @

        frac_and_elem_agree(@)
        @fracs_and_elems_agree("CLOSE")
        if @parent_frac
          @parent_frac.remove_inner(@)
        else
          alert "No parent!?!"
    
      tear_down: -> # FractalComponent
  • ¶

    console.log “FractalComponent.tear_down() id:#{@frac_id}”

        @elem.remove()
        @elem = false
        @elem = false
    
      should_show_close_button: -> # FractalComponent
  • ¶

    Do not show the close button if this fracpanel is the only one.

        return @always_show_close_button or not not @parent_frac
    
      update_close_button: -> # FractalComponent
        if @close_button
          if @should_show_close_button()
            @close_button.show()
          else
            @close_button.hide()
    
      make_visualization_menu_panel: ->
        if true # just to indent
          vis_menu = $("""
            <div class="visualization_menu"></div>
            """)
  • ¶

    TODO make visualization_menu work like action_menu and voices_menu

          @visualization_menu_panel = new FractalPanel vis_menu, "thingy",
            make_frame: false
            suppress_corner_buttons: false
            propagate:
              always_show_NEWS_buttons: false
              make_frame: false
              color_panels_uniquely: true
              inject_tree_path: false
              suppress_corner_buttons: false
              suppress_content_area: false
              show_NEWS_handle:
                E: true
                S: true
                N: true
                W: true
    
    
          vis_id = FractalPanel.assign_random_id(vis_menu, "vis_")
          @elem.append(vis_menu)
  • ¶

    vis_menu.on “mouseleave”, () => @visualization_menu_panel.parent.hide()

          forMan = @get_formurlaManager()
          menuVizFunc = forMan.resolve_function('visualizations') # embed VisualizationMenu
          src = 'visualizations(g=nrn:visualizationKB)'
          formurla = forMan.compile_formurla(src)
          expression = forMan.get_first_expression(formurla)
          @visualization_picker = menuVizFunc.apply(forMan,[@visualization_menu_panel.first_frac, expression])
          @visualization_picker.visualization_picker_for = @visualization_instance
  • ¶

    TODO will be made redundant when visualization_menu works like voices_menu

          @visualization_picker.knob_which_controls_me =
            hide_popup: =>
              @visualization_menu_panel.elem.hide()
    
      set_knob_label: (knob, label) ->
        if knob?
          knob.find(".knob_label").text(label)
    
      hide_visualization_menu_panel: ->
        @elem.parent().parent().hide()
    
      add_TBLR_handles: -> # FractalComponent
  • ¶

    TL TR BR BL Create panel control bar

        @controlBar = "<div class='fractalpanel_controls_bar'></div>"
        @elem.append(@controlBar)
    
        @add_visualization_knobby()
        @add_voices_knobby()
        @add_vantage_knobby()
        @add_action_knobby()
        @add_search_knobby()
        @add_close_knobby()
    
        @attach_all_knob_controllers()
        @update_close_button()
        return
    
      add_visualization_knobby: -> # FractalComponent
  • ¶

    the Visualization selection button

        @TL_handle = $("""
            <div class="fractalpanel_knob">
            <i class="fa fa-line-chart"></i>
            <div class="knob_label"></div>
          </div>
          """)
        @elem.children('.fractalpanel_controls_bar').append(@TL_handle)
        @visualization_button = @TL_handle
  • ¶

    @visualization_button.hide()

        @visualization_button.click (evt) =>
          if not @visualization_menu_panel?
            @make_visualization_menu_panel()
          @visualization_menu_panel.parent.show()
        return
    
      add_voices_knobby: -> # FractalComponent
  • ¶

    the “Voices” AKA “Discriminators” button

        @TL1_handle = $("""
          <div class="voices_knob fractalpanel_knob">
            <i class="fa fa-users"></i>
            <div class="knob_label"></div>
          </div>
          """)
        @elem.children('.fractalpanel_controls_bar').append(@TL1_handle)
        @voices_button = @TL1_handle
  • ¶

    @voices_button.attr(“title”, @voices_button__title) @voices_button.hide()

        return
    
      add_vantage_knobby: -> # FractalComponent
  • ¶

    the “Vantage” button TODO figure out a more principled place to put the bugs button is great for vantage

        @TL2_handle = $("""
          <div class="fractalpanel_knob">
            <a class="fa fa-bug" href="https://bitbucket.org/nooron/nooron/issues"
              target="bugs" title="Bugs? Suggestions?"></>
            <!--<div class="knob_label"></div>-->
          </div>
          """)
        @elem.children('.fractalpanel_controls_bar').append(@TL2_handle)
        @vantages_button = @TL2_handle
  • ¶

    @vantages_button.attr(“title”, @vantages_button__title) @vantages_button.hide()

        return
    
      add_action_knobby: -> # FractalComponent
  • ¶

    the Bottom Left action button

        @BL_handle = $("""
          <div class="fractalpanel_knob fractalpanel_knob_corner fractalpanel_knob_bl">
            <i class="fa fa-gears"></i>
          </div>
          """)
        @elem.append(@BL_handle)
        @action_button = @BL_handle
        @action_button.hide()
        return
    
      add_search_knobby: -> # FractalComponent
  • ¶

    the Bottom Right search button

        @BR_handle = $("""
          <div class="fractalpanel_knob fractalpanel_knob_corner fractalpanel_knob_br">
            <i class="fa fa-search"></i>
          </div>
          """)
        @elem.append(@BR_handle)
        @search_button = @BR_handle
        @search_button.attr("title","Search...")
        @search_button.hide()
        return
    
      add_close_knobby: -> # FractalComponent
  • ¶

    the Top Right close button

        @TR_handle = $("""
          <div class="fractalpanel_knob fractalpanel_knob_corner fractalpanel_knob_tr">
            <i class="fa fa-times"></i>
          </div>
          """)
        @elem.append(@TR_handle)
        @close_button = @TR_handle
        @close_button.attr("title", @close_button__title)
        @close_button.click(@close_button_handler)
        return
    
      attach_all_knob_controllers: ->
        for which_button in ['action_button', 'visualization_button', 'voices_button'] #, 'vantages_button']
          title_key = which_button + '__title'
          class_key = which_button + '__class'
          ctlr_key = which_button + '__ctlr'
          if @[title_key]?
            @[which_button].attr("title",@[title_key])
  • ¶

    @[which_button].show()

          if @[class_key]? # class_key is assumed to be a subclass of KnobController
            @[ctlr_key] = new @[class_key](@, @[which_button])
            @[which_button].show()
    
      close_button_handler: (evt) =>
        if @custom_close_button_handler
          @custom_close_button_handler(evt)
        else
          @close(evt.target)
    
      add_NEWS_handles: ->
        if @show_NEWS_handle.N
          @N_handle = $("""
            <div data-edge="north" class="fractalpanel_knob_north fractalpanel_knob_edge fractalpanel_knob">
              N
            </div>
          """)
          @elem.append(@N_handle)
    
        if @show_NEWS_handle.E
          @E_handle = $("""
            <div data-edge="east" class="fractalpanel_knob_east fractalpanel_knob_edge fractalpanel_knob">
              E
            </div>
          """)
          @elem.append(@E_handle)
    
        if @show_NEWS_handle.W
          @W_handle = $("""
            <div data-edge="west" class="fractalpanel_knob_west fractalpanel_knob_edge fractalpanel_knob">
              W
            </div>
          """)
          @elem.append(@W_handle)
    
        if @show_NEWS_handle.S
          @S_handle = $("""
            <div data-edge="south" class="fractalpanel_knob_south fractalpanel_knob_edge fractalpanel_knob">
              S
            </div>
          """)
          @elem.append(@S_handle)
        @NEWS_handles().click @NEWS_click
        if not @always_show_NEWS_buttons
          @NEWS_handles().hide()
    
      NEWS_click: (evt) => # FractalComponent
  • ¶

    It is not expected that showing the handles will be a typical use since what should be shown there? Generally the handles will have something dropped on them which will provide a clue as to what should displayed in the new FractalPanel

        edge_id = $(evt.target).data("edge")
        @split(edge_id)
    
      split: (edge_id, ancestor_to_split) -> # FractalComponent
        ancestor_to_split ?= @parent_frac
  • ¶

    if ancestor_to_split is @parent_frac alert(“splitting #{ancestor_to_split.frac_id} on side #{edge_id}”)

        if ancestor_to_split.is_already_split()
  • ¶

    Make a new Pane and put this Component inside it, then split that Pane

          return ancestor_to_split.add_fracpanel(edge_id, @)
        else
  • ¶

    split the Panel this Component is in

          return ancestor_to_split.add_fracinner(edge_id)
    
      add_sibling: (edge_id) ->
  • ¶

    WIP see BUILTIN_above and BUILTIN_beside, motivated by issue #130

        @split(edge_id, @parent_frac.find_furthest_ancestor_with_same_direction())
    
      NEWS_handles: -> # FractalComponent
        $(@elem).find(".fractalpanel_knob_edge")
    
      TLBR_handles: -> # FractalComponent
        $(@elem).find(".fractalpanel_knob_corner")
    
      show_drop_zones: (args) => # FractalComponent
        @NEWS_handles().show(args ? {})
        @NEWS_handles().droppable
          hoverClass: 'hovering_drop_target'
          tolerance: 'pointer' # 'pointer' minimizes multi-handle overlap 'fit|intersect|pointer|touch'
          drop: args.drop_handler or (evt, ui) =>
            alert("pass args.drop_handler to show_drop_zones(args) to handle the drop")
    
      hide_drop_zones: -> # FractalComponent
        @NEWS_handles().hide()
  • ¶

    @child??hide_drop_zones() # PROPAGATE hide_drop_zones() down to any children

      hide_all_buttons: ->
        @close_button.hide()
        @search_button.hide()
        @visualization_button.hide()
        @voices_button.hide()
        @vantages_button.hide()
        @action_button.hide()
    
      show_all_buttons: ->
        @close_button.show()
        @search_button.show()
        @visualization_button.show()
        @voices_button.show()
        @vantages_button.show()
        @action_button.show()
    
      register_visualization: (@visualization_instance, vis_func_name) ->
        if @visualization_button # some visualization might not have a visualization picker present
          @set_knob_label(@visualization_button, @visualization_instance.get_title())
  • ¶

    vis_func_name like “vis-func-provid”

        @elem.addClass(vis_func_name)
    
      get_formurlaManager: ->
        @visualization_instance.formurlaManager
    
    (exports ? this).FractalPanel = FractalPanel
    (exports ? this).direction = direction
    (exports ? this).orientation = orientation
    (exports ? this).other = other
    (exports ? this).div_loc = div_loc
    (exports ? this).sid_to_seq = side_to_seq