def cb_plot_testcase(attr, old, new, curr_doc: CurrentDoc):
    """
    Plot given test case to the input plot and show results in the output plots.
    :param attr: not used
    :param old: not used
    :param new: changed attribute 'value' of the dropdown menu testcases (string)
    :return: none
    """

    # bool to show callbacks that a testcase is being plotted
    curr_doc.plotting_test_case = True

    # clear input plot
    cb_button_delete(curr_doc, all_selected=True)

    # start plotting of specific test case
    if new == "single_beam_load":
        vis_init.expand_msg2user(curr_doc, "Single beam lineload test")
        visualisation_tests.single_beam_lineload_visu(curr_doc)
    elif new == "two_beam_lineload":
        vis_init.expand_msg2user(curr_doc, "Two beam lineload test")
        visualisation_tests.two_beam_lineload_visu(curr_doc)
    elif new == "fin_struc_soft_lab":
        vis_init.expand_msg2user(curr_doc, "Final structure software lab")
        visualisation_tests.example_unterlagen_visu(curr_doc)

    # testcase plotting done
    curr_doc.plotting_test_case = False
def cb_button_element_click(curr_doc: CurrentDoc, button_enum):
    """
    Callback for all element buttons:
    - deselects previous button and select new button or deselect selected button if it already was selected before
    - saves selected one as Enum in 'button_activated' and change it's background color
    :param button_enum: Enum that corresponds to the button that was clicked (ElementSupportEnum)
    :return: none
    """

    # unselect button if same button is selected again
    if not curr_doc.button_activated == -1 and curr_doc.button_activated == button_enum.value:
        button_style = button_enum.name
        curr_doc.buttons[curr_doc.button_activated].css_classes = [
            button_style
        ]

        # update the currently activated button to none
        curr_doc.button_activated = -1
        curr_doc.data_sources.ds_active_button.data['type'] = [-1]
        curr_doc.data_sources.ds_active_button.trigger(
            'data', curr_doc.data_sources.ds_active_button.data,
            curr_doc.data_sources.ds_active_button.data)

        # reset datasource for first node for line elements
        data = dict(type=[], node_x=[], node_y=[], name_node1=[])
        curr_doc.data_sources.ds_1st_chosen.data = data
        return

    # unselect previously selected button, if one was selected before at all
    if not curr_doc.button_activated == -1:
        found, button_style, value = eLnum.value_in_enum(
            eLnum.ElSupEnum, curr_doc.button_activated)
        curr_doc.buttons[curr_doc.button_activated].css_classes = [
            button_style
        ]
    else:
        reset_element_info(curr_doc)

    # change background color of newly selected element button
    button_style = button_enum.name + '_selected'
    curr_doc.buttons[button_enum.value].css_classes = [button_style]

    # update the currently activated button
    curr_doc.button_activated = button_enum.value
    curr_doc.data_sources.ds_active_button.data['type'] = [
        curr_doc.button_activated
    ]
    curr_doc.data_sources.ds_active_button.trigger(
        'data', curr_doc.data_sources.ds_active_button.data,
        curr_doc.data_sources.ds_active_button.data)

    # reset datasource for first node for line elements
    data = dict(type=[], node_x=[], node_y=[], name_node1=[])
    curr_doc.data_sources.ds_1st_chosen.data = data
def cb_elinfo_beam(attr, old, new, curr_doc: CurrentDoc):
    """
    Callback for the beam CheckboxGroup.
    Set EI and/ or EA to be infinite.
    :param attr: not used
    :param old: not used
    :param new: changed attribute 'active' of the beam CheckboxGroup (list)
    :return: none
    """

    # block user input while element info box gets changed (because of cb_get_textinput)
    curr_doc.elinfo_input_blocked = True

    elinfo = curr_doc.input_element_info
    element_index = curr_doc.elinfo_current_element[2]

    # get current state of EA and EI
    ea_infinite = False
    ei_infinite = False
    if curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] == float("inf"):
        ea_infinite = True
    if curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] == float("inf"):
        ei_infinite = True

    # EA was changed to infinite
    if 0 in new and not ea_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = float("inf")
        elinfo["ea"].disabled = True
    # EA was changed to finite
    elif 0 not in new and ea_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = 1.0
        elinfo["ei"].value = "%3.1f" % 1.0
        elinfo["ea"].disabled = False

    # EI was changed to infinite
    if 1 in new and not ei_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = float("inf")
        elinfo["ei"].disabled = True
    # EI was changed to finite
    elif 1 not in new and ei_infinite:
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = 1.0
        elinfo["ei"].value = "%3.1f" % 1.0
        elinfo["ei"].disabled = False

    # release for user input
    curr_doc.elinfo_input_blocked = False
def cb_get_textinput(attr, old, new, curr_doc: CurrentDoc, key):
    """
    Callback for the TextInputs of the element info box.
    :param attr: not used
    :param old: previous attribute 'value' of the TextInput (string)
    :param new: changed attribute 'value' of the TextInput (string)
    :param key: key of the specific TextInput (string)
    :return: none
    """

    # check whether user input is blocked
    if curr_doc.elinfo_input_blocked:
        return

    element_name = curr_doc.elinfo_current_element[0]
    element_indep = curr_doc.elinfo_current_element[1]
    element_index = curr_doc.elinfo_current_element[2]

    try:
        value = float(new)
    except ValueError:
        curr_doc.div_input.text = "Error: Please enter a number!"
        return

    # TODO: idea - don't accept zeros as TextInput for some keys?
    # if not(key == "angle" or key == "xn_start" or key == "xn_end" or key == "yq_start" or key == "yq_end") \
    #         and value == 0:
    #     vis_init.div_input.text = "Error: Please enter a non-zero number!"
    #     return

    # change data sources and glyphs in plot according to text input
    if key == "angle":
        # convert angle to radians and call method to adapt the angle in the input plot
        angle = math.radians(value)
        if element_indep:
            curr_doc.data_sources.ds_indep_elements.data['angle'][
                element_index] = angle
            vis_editEl.change_angle_indep(curr_doc, element_name, angle,
                                          element_index)
        else:
            curr_doc.data_sources.ds_nodedep_elements.data['angle'][
                element_index] = angle
            vis_editEl.change_angle_nodedep(curr_doc, element_name, angle,
                                            element_index)
    elif key == "k":
        k = curr_doc.data_sources.ds_nodedep_elements.data['k'][element_index]
        curr_doc.data_sources.ds_nodedep_elements.data['k'][
            element_index] = value
        if value < 0 <= k:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=True)
        elif value > 0 >= k:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=False)
    elif key == "force":
        curr_doc.data_sources.ds_nodedep_elements.data['f'][
            element_index] = value
    elif key == "moment":
        moment = curr_doc.data_sources.ds_nodedep_elements.data['moment'][
            element_index]
        curr_doc.data_sources.ds_nodedep_elements.data['moment'][
            element_index] = value
        if value < 0 <= moment:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=True)
        elif value > 0 >= moment:
            vis_editEl.draw_moment_negative(curr_doc,
                                            element_name,
                                            element_index,
                                            negative=False)
    elif key == "h":
        curr_doc.data_sources.ds_nodedep_elements.data['h'][
            element_index] = value
    elif key == "ea":
        curr_doc.data_sources.ds_nodedep_elements.data['ea'][
            element_index] = value
    elif key == "ei":
        curr_doc.data_sources.ds_nodedep_elements.data['ei'][
            element_index] = value
    elif key == "xn_start" or key == "xn_end" or key == "yq_start" or key == "yq_end":
        nodedep_llxn = curr_doc.data_sources.ds_nodedep_elements.data['ll_x_n']
        nodedep_llyq = curr_doc.data_sources.ds_nodedep_elements.data['ll_y_q']
        elinfo = curr_doc.input_element_info
        curr_doc.elinfo_input_blocked = True
        if key == "xn_start":
            xn_start = value
            xn_end = nodedep_llxn[element_index][1]
            yq_start = nodedep_llyq[element_index][0]
            yq_end = nodedep_llyq[element_index][1]
            if (xn_start == 0 and xn_end == 0 and yq_start == 0
                    and yq_end == 0):
                yq_start, yq_end = -1, -1
                elinfo["yq_start"].value = "%3.1f" % yq_start
                elinfo["yq_end"].value = "%3.1f" % yq_end
            elif (xn_start > 0 and xn_end <= 0) or (xn_start < 0
                                                    and xn_end >= 0):
                xn_end = 0
                elinfo["xn_end"].value = "%3.1f" % xn_end
        elif key == "xn_end":
            xn_start = nodedep_llxn[element_index][0]
            xn_end = value
            yq_start = nodedep_llyq[element_index][0]
            yq_end = nodedep_llyq[element_index][1]
            if (xn_end == 0 and xn_start == 0 and yq_start == 0
                    and yq_end == 0):
                yq_start, yq_end = -1, -1
                elinfo["yq_start"].value = "%3.1f" % yq_start
                elinfo["yq_end"].value = "%3.1f" % yq_end
            elif (xn_end > 0 and xn_start <= 0) or (xn_end < 0
                                                    and xn_start >= 0):
                xn_start = 0
                elinfo["xn_start"].value = "%3.1f" % xn_start
        elif key == "yq_start":
            xn_start = nodedep_llxn[element_index][0]
            xn_end = nodedep_llxn[element_index][1]
            yq_start = value
            yq_end = nodedep_llyq[element_index][1]
            if (yq_start == 0 and yq_end == 0 and xn_end == 0
                    and xn_start == 0):
                xn_start, xn_end = 1, 1
                elinfo["xn_start"].value = "%3.1f" % xn_start
                elinfo["xn_end"].value = "%3.1f" % xn_end
            elif (yq_start > 0 and yq_end <= 0) or (yq_start < 0
                                                    and yq_end >= 0):
                yq_end = 0
                elinfo["yq_end"].value = "%3.1f" % yq_end
        else:
            xn_start = nodedep_llxn[element_index][0]
            xn_end = nodedep_llxn[element_index][1]
            yq_start = nodedep_llyq[element_index][0]
            yq_end = value
            if (yq_end == 0 and yq_start == 0 and xn_end == 0
                    and xn_start == 0):
                xn_start, xn_end = 1, 1
                elinfo["xn_start"].value = "%3.1f" % xn_start
                elinfo["xn_end"].value = "%3.1f" % xn_end
            elif (yq_end > 0 and yq_start <= 0) or (yq_end < 0
                                                    and yq_start >= 0):
                yq_start = 0
                elinfo["yq_start"].value = "%3.1f" % yq_start
        curr_doc.elinfo_input_blocked = False
        # check lineload before change
        if not (xn_start == 0 and xn_end == 0 and yq_start == 0
                and yq_end == 0):
            load_x_n = (xn_start, xn_end)
            load_y_q = (yq_start, yq_end)
            # change data source
            nodedep_llxn[element_index] = load_x_n
            nodedep_llyq[element_index] = load_y_q
            # draw adapted line load
            local = curr_doc.data_sources.ds_nodedep_elements.data['ll_local'][
                element_index]
            vis_editEl.draw_lineload(curr_doc, element_name, load_x_n,
                                     load_y_q, local, element_index)
        else:
            curr_doc.div_input.text = "Error: Invalid line load values, one value has to be non-zero!"
            return
    elif key == "dT":
        tt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][1]
        at = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][2]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (value, tt, at)
    elif key == "T":
        dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][0]
        at = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][2]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (dt, value, at)
    elif key == "aT":
        dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][0]
        tt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index][1]
        curr_doc.data_sources.ds_nodedep_elements.data['dT_T'][
            element_index] = (dt, tt, value)

    # tell user about changed value
    curr_doc.div_input.text = "New value accepted!"
def cb_show_element_info(attr,
                         old,
                         new,
                         curr_doc: CurrentDoc,
                         indep=False,
                         nodedep=False):
    """
    Callback to show information about an element in the input plot when ds_element_info was changed. ds_element_info
    gets changed by cb_plot_tap when a tap in the input plot was caught without an element button being active.
    Also called when an element was just added to the plot and it was not an element of a test case.
    :param attr: not used
    :param old: not used
    :param new: changed ds_element_info.data when no button element is active and a tap in the plot was caught (dict)
    :param indep: True if just added element is a node independent one (bool)
    :param nodedep: True if just added element is a node independent one (bool)
    :return: none
    """

    # block user input while element info box gets changed (because of cb_get_textinput)
    curr_doc.elinfo_input_blocked = True

    reset_element_info(curr_doc)
    elinfo = curr_doc.input_element_info

    indep_x = curr_doc.data_sources.ds_indep_elements.data['x']
    indep_y = curr_doc.data_sources.ds_indep_elements.data['y']
    indep_t = curr_doc.data_sources.ds_indep_elements.data['type']
    indep_n = curr_doc.data_sources.ds_indep_elements.data['name']
    indep_a = curr_doc.data_sources.ds_indep_elements.data['angle']

    nodedep_n1 = curr_doc.data_sources.ds_nodedep_elements.data['name_node1']
    nodedep_n2 = curr_doc.data_sources.ds_nodedep_elements.data['name_node2']
    nodedep_t = curr_doc.data_sources.ds_nodedep_elements.data['type']
    nodedep_n = curr_doc.data_sources.ds_nodedep_elements.data['name']
    nodedep_x = curr_doc.data_sources.ds_nodedep_elements.data['x']
    nodedep_y = curr_doc.data_sources.ds_nodedep_elements.data['y']
    nodedep_l = curr_doc.data_sources.ds_nodedep_elements.data['length']
    nodedep_dt = curr_doc.data_sources.ds_nodedep_elements.data['dT_T']
    nodedep_k = curr_doc.data_sources.ds_nodedep_elements.data['k']
    nodedep_h = curr_doc.data_sources.ds_nodedep_elements.data['h']
    nodedep_ei = curr_doc.data_sources.ds_nodedep_elements.data['ei']
    nodedep_ea = curr_doc.data_sources.ds_nodedep_elements.data['ea']
    nodedep_m = curr_doc.data_sources.ds_nodedep_elements.data['moment']
    nodedep_f = curr_doc.data_sources.ds_nodedep_elements.data['f']
    nodedep_lll = curr_doc.data_sources.ds_nodedep_elements.data['ll_local']
    nodedep_llxn = curr_doc.data_sources.ds_nodedep_elements.data['ll_x_n']
    nodedep_llyq = curr_doc.data_sources.ds_nodedep_elements.data['ll_y_q']
    nodedep_a = curr_doc.data_sources.ds_nodedep_elements.data['angle']

    # check if element was just added
    if indep:
        index = len(indep_x) - 1
        name = indep_n[index]
    elif nodedep:
        index = len(curr_doc.data_sources.ds_nodedep_elements.data['x']) - 1
        name = nodedep_n[index]
    # search for element in data sources if it was not just added
    else:
        # initialize for element search
        index = -1
        name = False
        tap_x = new['tap_x'][0]
        tap_y = new['tap_y'][0]
        # check if tap was on an node independent element in the input plot
        for i in range(0, len(indep_x)):
            if abs(indep_x[i] - tap_x) < curr_doc.catch_radius and abs(
                    indep_y[i] - tap_y) < curr_doc.catch_radius:
                index = i
                name = indep_n[index]
                indep = True
                break
        # check if tap was on a node dependent element
        else:
            # search in glyph data source of type spring, load_point, load_moment or temp
            ds_glyph = curr_doc.data_sources.ds_glyph_springsPointMomentTemp
            ds_glyph_x = ds_glyph.data['glyph_x']
            ds_glyph_y = ds_glyph.data['glyph_y']
            for j in range(len(ds_glyph_x)):
                if abs(ds_glyph_x[j] - tap_x) < curr_doc.catch_radius and abs(
                        ds_glyph_y[j] - tap_y) < curr_doc.catch_radius:
                    name = ds_glyph.data['name_user'][j]
                    break
            # search in glyph data source of beams
            else:
                ds_glyph = curr_doc.data_sources.ds_glyph_beam
                ds_glyph_x = ds_glyph.data['x']
                ds_glyph_y = ds_glyph.data['y']
                for j in range(len(ds_glyph_x)):
                    if abs(ds_glyph_x[j] -
                           tap_x) < curr_doc.catch_radius and abs(
                               ds_glyph_y[j] - tap_y) < curr_doc.catch_radius:
                        name = ds_glyph.data['name_user'][j]
                        break
                # search in glyph data source of line loads
                else:
                    ds_glyph = curr_doc.data_sources.ds_glyph_lineload
                    ds_glyph_x = ds_glyph.data['glyph_x']
                    ds_glyph_y = ds_glyph.data['glyph_y']
                    for j in range(len(ds_glyph_x)):
                        if abs(ds_glyph_x[j] -
                               tap_x) < curr_doc.catch_radius and abs(
                                   ds_glyph_y[j] -
                                   tap_y) < curr_doc.catch_radius:
                            name = ds_glyph.data['name_user'][j]
                            break
            # get index of element in data source of node dependent elements
            if name:
                for i in range(len(nodedep_n)):
                    if name == nodedep_n[i]:
                        index = i
                        break

    # reaction if no element was found for that tap
    if index == -1:
        curr_doc.div_input.text = "No button element active and no element of graph clicked."
        return

    # make information about current element global for the callbacks and get enum_type
    if indep:
        curr_doc.elinfo_current_element = (name, True, index)
        enum_type = indep_t[index]
    else:
        curr_doc.elinfo_current_element = (name, False, index)
        enum_type = nodedep_t[index]

    # adapt element info box
    elinfo["name"].value = name
    if indep:
        # set values
        elinfo["x"].value = "%3.1f" % indep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % indep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        # enable input
        if not enum_type == eLnum.ElSupEnum.NODE.value and not enum_type == eLnum.ElSupEnum.JOINT.value:
            elinfo["angle"].disabled = False
            elinfo["angle"].value = "%3.1f" % math.degrees(indep_a[index])
    elif enum_type == eLnum.ElSupEnum.SPRING_SUPPORT.value or enum_type == eLnum.ElSupEnum.SPRING_MOMENT_SUPPORT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["k"].value = "%3.1f" % nodedep_k[index]
        # enable input
        elinfo["angle"].disabled = False
        elinfo["k"].disabled = False
    elif enum_type == eLnum.ElSupEnum.SPRING.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["k"].value = "%3.1f" % nodedep_k[index]
        # enable input
        elinfo["k"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_POINT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["force"].value = "%3.1f" % nodedep_f[index]
        # enable input
        elinfo["angle"].disabled = False
        elinfo["force"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_MOMENT.value:
        # set values
        elinfo["x"].value = "%3.1f" % nodedep_x[index]
        curr_doc.div_element_info["x"].text = "x:"
        elinfo["y"].value = "%3.1f" % nodedep_y[index]
        curr_doc.div_element_info["y"].text = "y:"
        elinfo["moment"].value = "%3.1f" % nodedep_m[index]
        # enable input
        elinfo["moment"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_TEMP.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["dT"].value = "%3.1f" % nodedep_dt[index][0]
        elinfo["T"].value = "%3.1f" % nodedep_dt[index][1]
        elinfo["aT"].value = "%3.1f" % nodedep_dt[index][2]
        # enable input
        elinfo["dT"].disabled = False
        elinfo["T"].disabled = False
        elinfo["aT"].disabled = False
    elif enum_type == eLnum.ElSupEnum.BEAM.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["angle"].value = "%3.1f" % math.degrees(nodedep_a[index])
        elinfo["length"].value = "%3.1f" % nodedep_l[index]
        elinfo["h"].value = "%3.1f" % nodedep_h[index]
        ea = nodedep_ea[index]
        ei = nodedep_ei[index]
        active = []
        if ea == float("inf"):
            active.append(0)
        else:
            elinfo["ea"].value = "%3.1f" % ea
            elinfo["ea"].disabled = False

        if ei == float("inf"):
            active.append(1)
        else:
            elinfo["ei"].value = "%3.1f" % ei
            elinfo["ei"].disabled = False
        curr_doc.group_element_info["beam"].active = active
        # enable input
        curr_doc.group_element_info["beam"].disabled = False
        elinfo["h"].disabled = False
    elif enum_type == eLnum.ElSupEnum.LOAD_LINE.value:
        # set values
        elinfo["x"].value = nodedep_n1[index]
        curr_doc.div_element_info["x"].text = "node 1:"
        elinfo["y"].value = nodedep_n2[index]
        curr_doc.div_element_info["y"].text = "node 2:"
        elinfo["length"].value = "%3.1f" % nodedep_l[index]
        elinfo["xn_start"].value = "%3.1f" % nodedep_llxn[index][0]
        elinfo["xn_end"].value = "%3.1f" % nodedep_llxn[index][1]
        elinfo["yq_start"].value = "%3.1f" % nodedep_llyq[index][0]
        elinfo["yq_end"].value = "%3.1f" % nodedep_llyq[index][1]
        local = nodedep_lll[index]
        if local:
            curr_doc.group_element_info["ll"].active = 0
            curr_doc.div_element_info["xn"].text = "* n"
            curr_doc.div_element_info["yq"].text = "* q"
        else:
            curr_doc.group_element_info["ll"].active = 1
            curr_doc.div_element_info["xn"].text = "* p<sub>x</sub>"
            curr_doc.div_element_info["yq"].text = "* p<sub>y</sub>"
        # enable input
        curr_doc.group_element_info["ll"].disabled = False
        elinfo["xn_start"].disabled = False
        elinfo["xn_end"].disabled = False
        elinfo["yq_start"].disabled = False
        elinfo["yq_end"].disabled = False

    # resize element info box to children truly containing information (WARNING: too slow!)
    # vis_init.layout_element_info.children[1].children.insert(0, vis_init.children_element_info["del"])
    # vis_init.layout_element_info.children[1].children.insert(0, vis_init.children_element_info["name"])
    # for key in elinfo:
    #     if not key == "name" and not key == "del":
    #         if not elinfo[key].value == "":
    #             child = vis_init.children_element_info[key]
    #             child_index = len(vis_init.layout_element_info.children[1].children) - 1
    #             vis_init.layout_element_info.children[1].children.insert(child_index, child)
    # show element info box
    # vis_init.layout_element_info.visible = True

    # release for user input
    curr_doc.elinfo_input_blocked = False
def cb_adapt_plot_indep(attr, old, new, curr_doc: CurrentDoc):
    """
    Callback to react when the data source of the input plot has changed through cb_plot_tab.
    :param attr: not used
    :param old: not used, because doesn't show correct values after cb_button_delete run (dict)
    :param new: updated ds_input.data (dict)
    :return: none
    """

    # check if element was added or deleted or just edited
    if not len(new['x']) == curr_doc.len_ds_input:
        curr_doc.len_ds_input = len(new['x'])

        # reset "same" of plot elements, used to check which entries of ds_input still exist and are plotted
        curr_doc.data_sources.ds_indep_elements.data['same'] = [False] * len(
            curr_doc.data_sources.ds_indep_elements.data['same'])

        ds_input_x = new['x']
        ds_input_y = new['y']
        ds_input_t = new['type']
        ds_indep_x = curr_doc.data_sources.ds_indep_elements.data['x']
        ds_indep_y = curr_doc.data_sources.ds_indep_elements.data['y']
        ds_indep_t = curr_doc.data_sources.ds_indep_elements.data['type']
        ds_indep_n = curr_doc.data_sources.ds_indep_elements.data['name']
        ds_indep_s = curr_doc.data_sources.ds_indep_elements.data['same']

        # get image glyphs that were already there before ds_input changed
        same = [False] * len(ds_input_x)
        for i in range(len(ds_input_x)):
            for j in range(len(ds_indep_x)):
                # let it get first fitting object several times - prevents new creation of same object in input plot
                if ds_input_x[i] == ds_indep_x[j] and ds_input_y[
                        i] == ds_indep_y[j]:
                    if ds_input_t[i] == ds_indep_t[j]:
                        ds_indep_s[j] = True
                        same[i] = True
                    else:
                        same[i] = True
                        curr_doc.div_input.text = "An object already exists at this position. " \
                                                  "New object wasn't created!"
                    break

        # delete image glyphs if their corresponding entries in d_input were deleted (cb_button_delete)
        # adaptive while condition necessary because length of ds_indep_x changes through deletion!
        i = 0
        while True:
            if i == len(ds_indep_x):
                break
            if not ds_indep_s[i]:
                vis_elToP.delete_indep(curr_doc, name=ds_indep_n[i], index=i)
            else:
                i += 1

        # create new image glyphs for elements that were added to ds_input
        for i in range(len(ds_input_x)):
            if not same[i]:
                # configure name and increse object id
                name = str(ds_input_t[i]) + "-" + str(curr_doc.object_id)
                curr_doc.object_id += 1

                # adapt name in ds_input
                ds_input_data = curr_doc.data_sources.ds_input.data.copy()
                ds_input_data['name_user'][i] = name
                curr_doc.data_sources.ds_input.data = ds_input_data
                # dat_src.ds_input.trigger('data', dat_src.ds_input.data, ds_input_data)

                if curr_doc.plotting_test_case:
                    try:
                        angle = curr_doc.test_case_angle[
                            curr_doc.test_case_count_indep]
                    except:
                        angle = 0.0
                    curr_doc.test_case_count_indep += 1
                else:
                    angle = 0.0
                # add node independent element
                vis_elToP.add_indep(curr_doc,
                                    x=ds_input_x[i],
                                    y=ds_input_y[i],
                                    enum_type=ds_input_t[i],
                                    name=name,
                                    angle=angle)
def cb_button_delete(curr_doc: CurrentDoc, all_selected=False, single=False):
    """
    Callback for both delete buttons of the input plot and the "delete element" button of the element info box.
    :param all_selected: bool, if True button "delete all" was clicked and method deletes all plot elements,
    if False method deletes only selected elements in the plot (bool)
    :param single: Tuple of a single element displayed in the element info box that shall be deleted of the form
    (name (string), indep (bool), index (int))
    :return: none
    """

    if all_selected:
        curr_doc.object_id = 0
        # delete all entries of the ColumnDataSource corresponding to the input plot and update the plot
        data = dict(x=[], y=[], type=[], name_user=[])
        curr_doc.data_sources.ds_input.data = data
        curr_doc.data_sources.ds_input.trigger(
            'data', curr_doc.data_sources.ds_input.data,
            curr_doc.data_sources.ds_input.data)
        # deactivate currently activated button element
        if not curr_doc.button_activated == -1:
            found, button_enum = eLnum.get_enum_of_value(
                eLnum.ElSupEnum, curr_doc.button_activated)
            cb_button_element_click(curr_doc, button_enum)
    elif single:
        if curr_doc.elinfo_current_element[1]:
            del curr_doc.data_sources.ds_input.data['x'][
                curr_doc.elinfo_current_element[2]]
            del curr_doc.data_sources.ds_input.data['y'][
                curr_doc.elinfo_current_element[2]]
            del curr_doc.data_sources.ds_input.data['type'][
                curr_doc.elinfo_current_element[2]]
            del curr_doc.data_sources.ds_input.data['name_user'][
                curr_doc.elinfo_current_element[2]]
            curr_doc.data_sources.ds_input.trigger(
                'data', curr_doc.data_sources.ds_input.data,
                curr_doc.data_sources.ds_input.data)
        else:
            vis_elToP.delete_nodedep(
                curr_doc,
                name_nodedep=curr_doc.elinfo_current_element[0],
                index_nodedep=curr_doc.elinfo_current_element[2])
        reset_element_info(curr_doc)
    else:
        s_x = curr_doc.data_sources.ds_input_selected.data['x']
        s_y = curr_doc.data_sources.ds_input_selected.data['y']
        len_s = len(s_x)
        len_ds = len(curr_doc.data_sources.ds_input.data['x'])

        # delete selected elements from the datasource of the input plot (dat_src.ds_input) and update
        for i in range(len_s):
            for j in range(len_ds):
                if s_x[i] == curr_doc.data_sources.ds_input.data['x'][
                        j] and s_y[i] == curr_doc.data_sources.ds_input.data[
                            'y'][j]:
                    del curr_doc.data_sources.ds_input.data['x'][j]
                    del curr_doc.data_sources.ds_input.data['y'][j]
                    del curr_doc.data_sources.ds_input.data['type'][j]
                    del curr_doc.data_sources.ds_input.data['name_user'][j]
                    len_ds -= 1
                    break
        curr_doc.data_sources.ds_input.trigger(
            'data', curr_doc.data_sources.ds_input.data,
            curr_doc.data_sources.ds_input.data)

        # reset datasource for selected element
        curr_doc.data_sources.ds_input_selected.data = dict(x=[], y=[])

    # reset datasource for first node for line elements
    data = dict(type=[], node_x=[], node_y=[], name_node1=[])
    curr_doc.data_sources.ds_1st_chosen.data = data
'''###############################
IMPORTS
###############################'''
# general imports
import numpy                       as np

# import bokeh module
from bokeh.models              import ColumnDataSource
from bokeh.io                  import curdoc
from bokeh.models.callbacks    import CustomJS

# import local file
from Classes.ColumnDataSources import ColumnDataSources
from Classes.CurrentDocument   import CurrentDoc
from testing_collection        import test_runner as test_lib
import vis_initialization          as vis_init


'''###############################
# INITIALIZATION MAIN
###############################'''

curr_doc = CurrentDoc(curdoc())
vis_init.initialize(curr_doc, max_indep_elements=20, catch_radius=0.15)
# curdoc().add_root(doc_layout)

run_tests = False  # run tests or not
if run_tests:
    test_lib.run_tests(curr_doc)
def initialize(curr_doc: CurrentDoc, max_indep_elements=20, catch_radius=0.15):
    """
    Creates and configures all bokeh objects and starts the web app.
    :param max_indep_elements: maximum number of node independent elements accepted in the input plot (int)
    :param catch_radius: radius in which to select an element in input plot (double)
    :return: none
    """

    curr_doc.catch_radius = catch_radius
    '''
    ###############################
    # TEXT BOXES
    ###############################
    '''
    # Div object showing x and y position of the cursor in the input plot
    div_xy = Div(width=75, height=25)

    # Div object showing hints for the graphical input into the input plot through the element buttons
    div_input_width = curr_doc.plot_input.plot_width - div_xy.width - 10
    curr_doc.div_input = Div(width=div_input_width, height=div_xy.height)

    # Div object showing general text messages to the user
    curr_doc.div_msg = Div(css_classes=["MSG_BOX"],
                           text=" ",
                           width=curr_doc.plot_input.plot_width,
                           height=100)
    # doc.div_msg = Div(css_classes=["MSG_BOX"], text=" ", width=doc.plot_input.plot_width, height=100)
    '''
    ###############################
    # INPUT PLOT
    ###############################
    '''
    # style and configuration of the input plot
    # this plot is used to allow the user to connect mechanical elements
    tooltips = [("name", "@name_user"), ("(x, y)", "(@x{0.0}, @y{0.0})")]
    curr_doc.plot_input = figure(
        # plot_width=600, plot_height=300,
        tools="pan,wheel_zoom,reset,lasso_select,hover,save",
        toolbar_location="above",
        # x_axis_label='x', y_axis_label='y',
        x_minor_ticks=10,
        y_minor_ticks=10,
        x_range=Range1d(start=0, end=5),
        y_range=Range1d(start=0, end=5),
        match_aspect=True,
        aspect_scale=1.0,
        tooltips=tooltips,
    )
    curr_doc.plot_input.toolbar.logo = None
    configure_input_plot(curr_doc, curr_doc.plot_input, div_xy,
                         max_indep_elements)
    '''
    ###############################
    # OUTPUT PLOTS
    ###############################
    '''
    # initialize plots for the output after calculations
    plot_output_width = 800
    plot_output_height = 250
    curr_doc.plot_normal_f = figure(plot_width=plot_output_width,
                                    plot_height=plot_output_height,
                                    active_scroll="wheel_zoom")
    curr_doc.plot_normal_f.toolbar.logo = None
    curr_doc.plot_normal_f.title.text = 'Normal force'

    curr_doc.plot_normal_disp = figure(plot_width=plot_output_width,
                                       plot_height=plot_output_height,
                                       active_scroll="wheel_zoom")
    curr_doc.plot_normal_disp.toolbar.logo = None
    curr_doc.plot_normal_disp.title.text = 'Normal displacement'

    curr_doc.plot_shear_f = figure(plot_width=plot_output_width,
                                   plot_height=plot_output_height,
                                   active_scroll="wheel_zoom")
    curr_doc.plot_shear_f.toolbar.logo = None
    curr_doc.plot_shear_f.title.text = 'Shear force'

    curr_doc.plot_moment = figure(plot_width=plot_output_width,
                                  plot_height=plot_output_height,
                                  active_scroll="wheel_zoom")
    curr_doc.plot_moment.toolbar.logo = None
    curr_doc.plot_moment.title.text = 'Bending moment'

    curr_doc.plot_shear_disp = figure(plot_width=plot_output_width,
                                      plot_height=plot_output_height,
                                      active_scroll="wheel_zoom")
    curr_doc.plot_shear_disp.toolbar.logo = None
    curr_doc.plot_shear_disp.title.text = 'Shear displacement'

    curr_doc.plot_shear_angle = figure(plot_width=plot_output_width,
                                       plot_height=plot_output_height,
                                       active_scroll="wheel_zoom")
    curr_doc.plot_shear_angle.toolbar.logo = None
    curr_doc.plot_shear_angle.title.text = 'Rotation angle'

    curr_doc.plot_list = [
        curr_doc.plot_normal_f, curr_doc.plot_normal_disp,
        curr_doc.plot_shear_f, curr_doc.plot_moment, curr_doc.plot_shear_angle,
        curr_doc.plot_shear_disp
    ]

    # add plot renderer
    ds = ColumnDataSource(data=dict(x=[], y=[]))
    curr_doc.plot_normal_f.circle('x', 'y', source=ds)
    curr_doc.plot_normal_disp.circle('x', 'y', source=ds)
    curr_doc.plot_shear_f.circle('x', 'y', source=ds)
    curr_doc.plot_moment.circle('x', 'y', source=ds)
    curr_doc.plot_shear_disp.circle('x', 'y', source=ds)
    curr_doc.plot_normal_f.circle('x', 'y', source=ds)
    curr_doc.plot_shear_angle.circle('x', 'y', source=ds)
    '''
    ###############################
    # TEST CASES
    ###############################
    '''
    menu_tc = [("Single beam load", "single_beam_load"),
               ("Two beam lineload", "two_beam_lineload"),
               ("Angled structure", "fin_struc_soft_lab")]
    dropdown_tc = Dropdown(label="Show test case", menu=menu_tc, width=150)
    dropdown_tc.on_change('value',
                          partial(vis_cbs.cb_plot_testcase, curr_doc=curr_doc))
    # partial(vis_cbs.cb_get_textinput, key=key_elinfo_n)
    '''
    ###############################
    # CALCULATE AND DELETE BUTTONS
    ###############################
    '''
    # add and configure a button to start calculations
    button_calc = Button(label="Calculate", width=240)
    button_calc.on_click(
        partial(vis_cbs.cb_button_calculation, curr_doc, button_calc))

    # add and configure a button to delete selected elements of the input graph
    b_del_w = int((button_calc.width - 10) / 2)
    button_del_selected = Button(label="Delete selected", width=b_del_w)
    button_del_selected.on_click(
        partial(vis_cbs.cb_button_delete,
                curr_doc=curr_doc,
                all_selected=False))

    # add and configure a button to delete selected elements of the input graph
    button_del_all = Button(label="Delete all", width=b_del_w)
    button_del_all.on_click(
        partial(vis_cbs.cb_button_delete, curr_doc=curr_doc,
                all_selected=True))
    '''
    ############################################
    # BOX OF ELEMENTS TO SELECT FOR INPUT PLOT
    ############################################
    '''
    # titles for groups of mechanical elements
    text_supports = Div(text="Supports:", width=100, height=20)
    text_springs = Div(text="Springs:", width=100, height=20)
    text_node = Div(text="Node:", width=100, height=20)
    text_joints = Div(text="Joints:", width=100, height=20)
    text_elements = Div(text="Line elements:", width=100, height=20)
    text_loads = Div(text="Loads:", width=100, height=20)

    b_height = 50
    b_line_width = 72

    # configure buttons for mechanical supports
    button_support_clamped = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_CLAMPED.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.SUPPORT_CLAMPED.value] = button_support_clamped
    button_support_clamped.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_CLAMPED))

    button_support_normal = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_NORMAL_FORCE.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.SUPPORT_NORMAL_FORCE.value] = button_support_normal
    button_support_normal.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_NORMAL_FORCE))

    button_support_transverse = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_TRANSVERSE_FORCE.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SUPPORT_TRANSVERSE_FORCE.
                     value] = button_support_transverse
    button_support_transverse.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_TRANSVERSE_FORCE))

    button_support_fixed_conti = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_FIXED_CONTINUOUS.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SUPPORT_FIXED_CONTINUOUS.
                     value] = button_support_fixed_conti
    button_support_fixed_conti.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_FIXED_CONTINUOUS))

    button_support_fixed_joint = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_FIXED_JOINT.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.SUPPORT_FIXED_JOINT.value] = button_support_fixed_joint
    button_support_fixed_joint.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_FIXED_JOINT))

    button_support_roller_conti = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_ROLLER_CONTINUOUS.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SUPPORT_ROLLER_CONTINUOUS.
                     value] = button_support_roller_conti
    button_support_roller_conti.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_ROLLER_CONTINUOUS))

    button_support_roller_joint = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SUPPORT_ROLLER_JOINT.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SUPPORT_ROLLER_JOINT.
                     value] = button_support_roller_joint
    button_support_roller_joint.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SUPPORT_ROLLER_JOINT))

    button_spring_support = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SPRING_SUPPORT.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.SPRING_SUPPORT.value] = button_spring_support
    button_spring_support.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SPRING_SUPPORT))

    button_spring_moment_support = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.SPRING_MOMENT_SUPPORT.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SPRING_MOMENT_SUPPORT.
                     value] = button_spring_moment_support
    button_spring_moment_support.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SPRING_MOMENT_SUPPORT))

    # configure curr_doc.buttons for connectors
    button_node = Button(label="",
                         css_classes=[eLnum.ElSupEnum.NODE.name],
                         width=b_height,
                         height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.NODE.value] = button_node
    button_node.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.NODE))

    button_joint = Button(label="",
                          css_classes=[eLnum.ElSupEnum.JOINT.name],
                          width=b_height,
                          height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.JOINT.value] = button_joint
    button_joint.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.JOINT))

    button_joint_normal = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.JOINT_NORMAL_FORCE.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.JOINT_NORMAL_FORCE.value] = button_joint_normal
    button_joint_normal.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.JOINT_NORMAL_FORCE))

    button_joint_transverse = Button(
        label="",
        css_classes=[eLnum.ElSupEnum.JOINT_TRANSVERSE_FORCE.name],
        width=b_height,
        height=b_height)
    curr_doc.buttons[
        eLnum.ElSupEnum.JOINT_TRANSVERSE_FORCE.value] = button_joint_transverse
    button_joint_transverse.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.JOINT_TRANSVERSE_FORCE))

    button_spring = Button(label="",
                           css_classes=[eLnum.ElSupEnum.SPRING.name],
                           width=b_line_width,
                           height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.SPRING.value] = button_spring
    button_spring.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.SPRING))

    # configure buttons for mechanical 1D or 2D elements
    # button_rod = Button(label="", css_classes=[eLnum.ElSupEnum.ROD.name], width=b_line_width, height=b_height)
    # curr_doc.buttons[eLnum.ElSupEnum.ROD.value] = button_rod
    # button_rod.on_click(partial(vis_cbs.cb_button_element_click, button_enum=eLnum.ElSupEnum.ROD))

    button_beam = Button(label="",
                         css_classes=[eLnum.ElSupEnum.BEAM.name],
                         width=b_line_width,
                         height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.BEAM.value] = button_beam
    button_beam.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.BEAM))

    # configure buttons for mechanical loads
    button_load_point = Button(label="",
                               css_classes=[eLnum.ElSupEnum.LOAD_POINT.name],
                               width=b_height,
                               height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.LOAD_POINT.value] = button_load_point
    button_load_point.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.LOAD_POINT))

    button_load_moment = Button(label="",
                                css_classes=[eLnum.ElSupEnum.LOAD_MOMENT.name],
                                width=b_height,
                                height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.LOAD_MOMENT.value] = button_load_moment
    button_load_moment.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.LOAD_MOMENT))

    button_load_random = Button(label="",
                                css_classes=[eLnum.ElSupEnum.LOAD_LINE.name],
                                width=b_line_width,
                                height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.LOAD_LINE.value] = button_load_random
    button_load_random.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.LOAD_LINE))

    button_load_temp = Button(label="",
                              css_classes=[eLnum.ElSupEnum.LOAD_TEMP.name],
                              width=b_height,
                              height=b_height)
    curr_doc.buttons[eLnum.ElSupEnum.LOAD_TEMP.value] = button_load_temp
    button_load_temp.on_click(
        partial(vis_cbs.cb_button_element_click,
                curr_doc=curr_doc,
                button_enum=eLnum.ElSupEnum.LOAD_TEMP))
    '''
    ###############################
    # ELEMENT INFO BOX
    ###############################
    '''
    elinfo_object_height = 26
    elinfo_label_width1 = 60
    elinfo_label_width2 = 40
    elinfo_input_width = 60

    # labels for values of an element of the input plot
    text_elinfo_name = Div(text="name:",
                           width=elinfo_label_width1,
                           height=elinfo_object_height)
    text_elinfo_x = Div(text="x:",
                        width=elinfo_label_width1,
                        height=elinfo_object_height)
    curr_doc.div_element_info["x"] = text_elinfo_x
    text_elinfo_y = Div(text="y:",
                        width=elinfo_label_width1,
                        height=elinfo_object_height)
    curr_doc.div_element_info["y"] = text_elinfo_y
    text_elinfo_angle1 = Div(text="angle:",
                             width=elinfo_label_width1,
                             height=elinfo_object_height)
    text_elinfo_angle2 = Div(text="°",
                             width=elinfo_label_width2 - 20,
                             height=elinfo_object_height)
    spacer_y_a = Div(text="",
                     width=text_elinfo_angle2.width,
                     height=elinfo_object_height)
    text_elinfo_k1 = Div(text="spring:",
                         width=elinfo_label_width1,
                         height=elinfo_object_height)
    text_elinfo_k2 = Div(text="* k",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)
    text_elinfo_length1 = Div(text="length:",
                              width=elinfo_label_width1,
                              height=elinfo_object_height)
    text_elinfo_length2 = Div(text="* l",
                              width=elinfo_label_width2,
                              height=elinfo_object_height)
    text_elinfo_force1 = Div(text="force:",
                             width=elinfo_label_width1,
                             height=elinfo_object_height)
    text_elinfo_force2 = Div(text="* F",
                             width=elinfo_label_width2,
                             height=elinfo_object_height)
    text_elinfo_moment1 = Div(text="moment:",
                              width=elinfo_label_width1,
                              height=elinfo_object_height)
    text_elinfo_moment2 = Div(text="* M",
                              width=elinfo_label_width2,
                              height=elinfo_object_height)
    text_elinfo_beam = Div(text="BEAM",
                           width=elinfo_object_height,
                           height=elinfo_label_width1,
                           css_classes=["ELINFO_VERTICAL_TEXT"])
    text_elinfo_h = Div(text="* h",
                        width=elinfo_label_width2,
                        height=elinfo_object_height)
    text_elinfo_ei = Div(text="* EI",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)
    text_elinfo_ea = Div(text="* EA",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)
    text_elinfo_lineload = Div(text="LINE LOAD",
                               width=elinfo_object_height,
                               height=elinfo_label_width1,
                               css_classes=["ELINFO_VERTICAL_TEXT"])
    text_elinfo_xn = Div(text="* n",
                         width=elinfo_label_width2 - 10,
                         height=elinfo_object_height)
    curr_doc.div_element_info["xn"] = text_elinfo_xn
    text_elinfo_yq = Div(text="* q",
                         width=elinfo_label_width2 - 10,
                         height=elinfo_object_height)
    curr_doc.div_element_info["yq"] = text_elinfo_yq
    text_elinfo_temp = Div(text="TEMP.",
                           width=elinfo_object_height,
                           height=elinfo_label_width1,
                           css_classes=["ELINFO_VERTICAL_TEXT"])
    text_elinfo_dt = Div(text="* dT",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)
    text_elinfo_tt = Div(text="* T",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)
    text_elinfo_at = Div(text="* &alpha;T",
                         width=elinfo_label_width2,
                         height=elinfo_object_height)

    # text inputs showing the current value of an input plot element and taking input for a value change
    # name
    input_elinfo_name = TextInput(value="-",
                                  width=elinfo_input_width,
                                  height=elinfo_object_height,
                                  disabled=True)
    key_elinfo_n = "name"
    curr_doc.input_element_info[key_elinfo_n] = input_elinfo_name
    input_elinfo_name.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_n))

    # xy
    input_elinfo_x = TextInput(value="-",
                               width=elinfo_input_width,
                               height=elinfo_object_height,
                               disabled=True)
    key_elinfo_x = "x"
    curr_doc.input_element_info[key_elinfo_x] = input_elinfo_x
    input_elinfo_x.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_x))

    input_elinfo_y = TextInput(value="-",
                               width=elinfo_input_width,
                               height=elinfo_object_height,
                               disabled=True)
    key_elinfo_y = "y"
    curr_doc.input_element_info[key_elinfo_y] = input_elinfo_y
    input_elinfo_y.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_y))

    # angle
    input_elinfo_angle = TextInput(value="-",
                                   width=elinfo_input_width,
                                   height=elinfo_object_height,
                                   disabled=True)
    key_elinfo_angle = "angle"
    curr_doc.input_element_info[key_elinfo_angle] = input_elinfo_angle
    input_elinfo_angle.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_angle))

    # spring constant
    input_elinfo_k = TextInput(value="-",
                               width=elinfo_input_width,
                               height=elinfo_object_height,
                               disabled=True)
    key_elinfo_k = "k"
    curr_doc.input_element_info[key_elinfo_k] = input_elinfo_k
    input_elinfo_k.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_k))

    # length
    input_elinfo_length = TextInput(value="-",
                                    width=elinfo_input_width,
                                    height=elinfo_object_height,
                                    disabled=True)
    key_elinfo_length = "length"
    curr_doc.input_element_info[key_elinfo_length] = input_elinfo_length
    input_elinfo_k.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_length))

    # point load
    input_elinfo_force = TextInput(value="-",
                                   width=elinfo_input_width,
                                   height=elinfo_object_height,
                                   disabled=True)
    key_elinfo_f = "force"
    curr_doc.input_element_info[key_elinfo_f] = input_elinfo_force
    input_elinfo_force.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_f))

    # moment
    input_elinfo_moment = TextInput(value="-",
                                    width=elinfo_input_width,
                                    height=elinfo_object_height,
                                    disabled=True)
    key_elinfo_m = "moment"
    curr_doc.input_element_info[key_elinfo_m] = input_elinfo_moment
    input_elinfo_moment.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_m))

    # beam
    input_elinfo_h = TextInput(value="-",
                               width=elinfo_input_width,
                               height=elinfo_object_height,
                               disabled=True)
    key_elinfo_h = "h"
    curr_doc.input_element_info[key_elinfo_h] = input_elinfo_h
    input_elinfo_h.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc, key=key_elinfo_h))

    check_elinfo_beam = CheckboxGroup(labels=["EA -> inf.", "EI -> inf."],
                                      active=[],
                                      disabled=True,
                                      width=elinfo_input_width +
                                      elinfo_label_width1)
    curr_doc.group_element_info["beam"] = check_elinfo_beam
    check_elinfo_beam.on_change(
        'active', partial(vis_cbs.cb_elinfo_beam, curr_doc=curr_doc))

    input_elinfo_ea = TextInput(value="-",
                                width=elinfo_input_width,
                                height=elinfo_object_height,
                                disabled=True)
    key_elinfo_ea = "ea"
    curr_doc.input_element_info[key_elinfo_ea] = input_elinfo_ea
    input_elinfo_ea.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc,
                key=key_elinfo_ea))

    input_elinfo_ei = TextInput(value="-",
                                width=elinfo_input_width,
                                height=elinfo_object_height,
                                disabled=True)
    key_elinfo_ei = "ei"
    curr_doc.input_element_info[key_elinfo_ei] = input_elinfo_ei
    input_elinfo_ei.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc,
                key=key_elinfo_ei))

    # line load
    radio_elinfo_ll = RadioGroup(
        labels=["local", "global"],
        active=0,
        disabled=True,  # "angle"
        width=elinfo_input_width + elinfo_label_width1)
    curr_doc.group_element_info["ll"] = radio_elinfo_ll
    radio_elinfo_ll.on_change(
        'active', partial(vis_cbs.cb_elinfo_lineload, curr_doc=curr_doc))

    input_elinfo_xns = TextInput(value="-",
                                 width=elinfo_input_width,
                                 height=elinfo_object_height + 20,
                                 disabled=True,
                                 title="start")
    key_elinfo_xns = "xn_start"
    curr_doc.input_element_info[key_elinfo_xns] = input_elinfo_xns
    input_elinfo_xns.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_xns))

    input_elinfo_xne = TextInput(value="-",
                                 width=elinfo_input_width,
                                 height=elinfo_object_height + 20,
                                 disabled=True,
                                 title="end")
    key_elinfo_xne = "xn_end"
    curr_doc.input_element_info[key_elinfo_xne] = input_elinfo_xne
    input_elinfo_xne.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_xne))

    input_elinfo_yqs = TextInput(value="-",
                                 width=elinfo_input_width,
                                 height=elinfo_object_height,
                                 disabled=True)
    key_elinfo_yqs = "yq_start"
    curr_doc.input_element_info[key_elinfo_yqs] = input_elinfo_yqs
    input_elinfo_yqs.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_yqs))

    input_elinfo_yqe = TextInput(value="-",
                                 width=elinfo_input_width,
                                 height=elinfo_object_height,
                                 disabled=True)
    key_elinfo_yqe = "yq_end"
    curr_doc.input_element_info[key_elinfo_yqe] = input_elinfo_yqe
    input_elinfo_yqe.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput,
                curr_doc=curr_doc,
                key=key_elinfo_yqe))

    # temperature load
    input_elinfo_dt = TextInput(value="-",
                                width=elinfo_input_width,
                                height=elinfo_object_height,
                                disabled=True)
    key_elinfo_dt = "dT"
    curr_doc.input_element_info[key_elinfo_dt] = input_elinfo_dt
    input_elinfo_dt.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc,
                key=key_elinfo_dt))

    input_elinfo_tt = TextInput(value="-",
                                width=elinfo_input_width,
                                height=elinfo_object_height,
                                disabled=True)
    key_elinfo_tt = "T"
    curr_doc.input_element_info[key_elinfo_tt] = input_elinfo_tt
    input_elinfo_tt.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc,
                key=key_elinfo_tt))

    input_elinfo_at = TextInput(value="-",
                                width=elinfo_input_width,
                                height=elinfo_object_height,
                                disabled=True)
    key_elinfo_at = "aT"
    curr_doc.input_element_info[key_elinfo_at] = input_elinfo_at
    input_elinfo_at.on_change(
        'value',
        partial(vis_cbs.cb_get_textinput, curr_doc=curr_doc,
                key=key_elinfo_at))

    # button for deleting an input plot element
    button_elinfo_del = Button(label="Delete element",
                               width=elinfo_label_width1 + elinfo_input_width +
                               elinfo_label_width2,
                               height=elinfo_object_height + 10,
                               disabled=False)
    button_elinfo_del.on_click(
        partial(vis_cbs.cb_button_delete, curr_doc=curr_doc, single=True))
    '''
    ###############################
    # CHECKBOXES FOR OUTPUT PLOTS
    ###############################
    '''
    check_labels = ["Min/ max values", "Start/ end values", "Zero points"]
    check_init_active = [1]

    check_plot_nf = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)  # inline=False
    text_plot_nf = Div(text="", width=100, height=10)
    check_plot_nf.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_normal_f,
                div=text_plot_nf))

    check_plot_nd = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)
    text_plot_nd = Div(text="", width=100, height=10)
    check_plot_nd.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_normal_disp,
                div=text_plot_nd))

    check_plot_sf = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)
    text_plot_sf = Div(text="", width=100, height=10)
    check_plot_sf.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_shear_f,
                div=text_plot_sf))

    check_plot_mo = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)
    text_plot_mo = Div(text="", width=100, height=10)
    check_plot_mo.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_moment,
                div=text_plot_mo))

    check_plot_sa = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)
    text_plot_sa = Div(text="", width=100, height=10)
    check_plot_sa.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_shear_angle,
                div=text_plot_sa))

    check_plot_sd = CheckboxGroup(labels=check_labels,
                                  active=check_init_active)
    text_plot_sd = Div(text="", width=100, height=10)
    check_plot_sd.on_change(
        'active',
        partial(vis_cbs.cb_toggle_characteristic_values,
                curr_doc=curr_doc,
                output_plot=curr_doc.plot_shear_disp,
                div=text_plot_sd))
    '''
    ###############################
    # IMAGES: SIGN CONVENTION
    ###############################
    '''
    sign_convention_N = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_N_u.png' width=100 height=60>",
        width=100,
        height=60)
    sign_convention_u = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_N_u.png' width=100 height=60>",
        width=100,
        height=60)
    sign_convention_SF = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_SF_w.png' width=100 height=60>",
        width=100,
        height=60)
    sign_convention_M = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_M.png' width=100 height=60>",
        width=100,
        height=60)
    sign_convention_phi = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_phi.png' width=100 height=60>",
        width=100,
        height=60)
    sign_convention_w = Div(
        text=
        "<img src='/System_of_Beams/static/images/sign_convention_SF_w.png' width=100 height=60>",
        width=100,
        height=60)
    '''
    ###############################
    # HTML  LATEX DESCRIPTION
    ###############################
    '''
    # latex packages not working with updated bokeh!
    # description_filename = join(dirname(__file__), "description.html")
    # description = LatexDiv(text=open(description_filename).read(), render_as_text=False, width=910)
    '''
    ####################################
    # CREATE LAYOUT AND START DOCUMENT
    ####################################
    '''
    spacer = Div(text="", width=20, height=20)
    minispacer = Div(text="", width=0, height=0)

    # element buttons
    layout_supports_1 = row(button_support_clamped, button_support_normal,
                            button_support_transverse)
    layout_supports_2 = row(button_support_fixed_joint,
                            button_support_fixed_conti,
                            button_support_roller_joint,
                            button_support_roller_conti)
    layout_springs = row(button_spring_support, button_spring_moment_support)
    layout_node = row(button_node)
    layout_joints = row(button_joint, button_joint_normal,
                        button_joint_transverse, button_spring)
    layout_elements = row(button_beam)  # button_rod,
    layout_loads = row(button_load_point, button_load_moment,
                       button_load_random, button_load_temp)
    layout_input_elements = column(
        spacer, text_supports, layout_supports_1, layout_supports_2,
        row(column(text_springs, layout_springs), spacer, minispacer,
            column(text_node, layout_node)), text_joints, layout_joints,
        text_elements, layout_elements, text_loads, layout_loads)

    # element info box
    elinfo_name = row(text_elinfo_name, input_elinfo_name)
    curr_doc.children_element_info[key_elinfo_n] = elinfo_name
    elinfo_xy = row(text_elinfo_x, input_elinfo_x, spacer_y_a, text_elinfo_y,
                    input_elinfo_y)
    curr_doc.children_element_info[key_elinfo_x] = elinfo_xy
    elinfo_angle_length = row(text_elinfo_angle1, input_elinfo_angle,
                              text_elinfo_angle2, text_elinfo_length1,
                              input_elinfo_length, text_elinfo_length2)
    curr_doc.children_element_info[key_elinfo_angle] = elinfo_angle_length
    elinfo_k = row(text_elinfo_k1, input_elinfo_k, text_elinfo_k2)
    curr_doc.children_element_info[key_elinfo_k] = elinfo_k
    elinfo_force = row(text_elinfo_force1, input_elinfo_force,
                       text_elinfo_force2)
    curr_doc.children_element_info[key_elinfo_f] = elinfo_force
    elinfo_moment = row(text_elinfo_moment1, input_elinfo_moment,
                        text_elinfo_moment2)
    curr_doc.children_element_info[key_elinfo_m] = elinfo_moment
    elinfo_beam = row(
        minispacer, column(minispacer, text_elinfo_beam),
        column(check_elinfo_beam, row(input_elinfo_h, text_elinfo_h)),
        column(row(input_elinfo_ea, text_elinfo_ea),
               row(input_elinfo_ei, text_elinfo_ei)))
    curr_doc.children_element_info[key_elinfo_h] = elinfo_beam
    elinfo_lineload = row(
        minispacer, column(spacer, text_elinfo_lineload),
        column(spacer, radio_elinfo_ll),
        column(row(input_elinfo_xns, input_elinfo_xne),
               row(input_elinfo_yqs, input_elinfo_yqe)),
        column(spacer, text_elinfo_xn, text_elinfo_yq))
    curr_doc.children_element_info[key_elinfo_ei] = elinfo_lineload
    elinfo_dt = row(
        minispacer, column(minispacer, text_elinfo_temp),
        column(
            row(input_elinfo_dt, text_elinfo_dt, minispacer, input_elinfo_at,
                text_elinfo_at), row(input_elinfo_tt, text_elinfo_tt)))
    curr_doc.children_element_info[key_elinfo_dt] = elinfo_dt
    curr_doc.layout_element_info = row(
        minispacer,
        column(minispacer, elinfo_name, elinfo_xy, elinfo_angle_length, spacer,
               elinfo_k, elinfo_force, elinfo_moment, spacer, elinfo_beam,
               spacer, elinfo_lineload, spacer, elinfo_dt, spacer,
               button_elinfo_del, minispacer),
        minispacer,
        css_classes=["ELEMENT_INFO_BOX"],
        margin=(20, 0, 0, 20),
        visible=True)

    # input plot with element buttons, delete and calculation buttons and divs
    user_input = row(curr_doc.plot_input, spacer, layout_input_elements)
    user_input_info = row(curr_doc.div_input, div_xy, spacer,
                          button_del_selected, button_del_all)
    user_msg = row(curr_doc.div_msg, spacer, button_calc)

    # output plots and check boxes for characteristic values
    user_output = column(
        row(curr_doc.plot_normal_f, spacer,
            column(spacer, check_plot_nf, sign_convention_N)),
        row(curr_doc.plot_normal_disp, spacer,
            column(spacer, check_plot_nd, sign_convention_u)),
        row(curr_doc.plot_shear_f, spacer,
            column(spacer, check_plot_sf, sign_convention_SF)),
        row(curr_doc.plot_moment, spacer,
            column(spacer, check_plot_mo, sign_convention_M)),
        row(curr_doc.plot_shear_angle, spacer,
            column(spacer, check_plot_sa, sign_convention_phi)),
        row(curr_doc.plot_shear_disp, spacer,
            column(spacer, check_plot_sd, sign_convention_w)))

    # assemble complete layout
    doc_layout = column(
        spacer, row(spacer, spacer, dropdown_tc),
        row(column(user_input, user_input_info, user_msg), minispacer,
            curr_doc.layout_element_info), spacer, user_output)

    # add layout
    curr_doc.curr_doc.add_root(doc_layout)
    # set title of browser tab
    curr_doc.curr_doc.title = split(dirname(__file__))[-1].replace(
        '_', ' ').replace('-', ' ')
    doc_title = split(dirname(__file__))[-1].replace('_',
                                                     ' ').replace('-', ' ')