<commit_before>class @Annotatable
    _debug: false

    # selectors for the annotatable xmodule 
    toggleAnnotationsSelector:  '.annotatable-toggle-annotations'
    toggleInstructionsSelector: '.annotatable-toggle-instructions'
    instructionsSelector:       '.annotatable-instructions'
    sectionSelector:            '.annotatable-section'
    spanSelector:               '.annotatable-span'
    replySelector:              '.annotatable-reply'

    # these selectors are for responding to events from the annotation capa problem type
    problemXModuleSelector:     '.xmodule_CapaModule'
    problemSelector:            'section.problem'
    problemInputSelector:       'section.problem .annotation-input'
    problemReturnSelector:      'section.problem .annotation-return'

    constructor: (el) ->
        console.log 'loaded Annotatable' if @_debug
        @el = el
        @$el = $(el)
        @init()

    $: (selector) ->
        $(selector, @el)

    init: () ->
        @initEvents()
        @initTips()

    initEvents: () ->
        # Initialize toggle handlers for the instructions and annotations sections
        [@annotationsHidden, @instructionsHidden] = [false, false]
        @$(@toggleAnnotationsSelector).bind 'click', @onClickToggleAnnotations
        @$(@toggleInstructionsSelector).bind 'click', @onClickToggleInstructions

        # Initialize handler for 'reply to annotation' events that scroll to
        # the associated problem. The reply buttons are part of the tooltip
        # content. It's important that the tooltips be configured to render
        # as descendants of the annotation module and *not* the document.body.
        @$el.delegate @replySelector, 'click', @onClickReply

        # Initialize handler for 'return to annotation' events triggered from problems.
        #   1) There are annotationinput capa problems rendered on the page
        #   2) Each one has an embedded return link (see annotation capa problem template).
        # Since the capa problem injects HTML content via AJAX, the best we can do is
        # is let the click events bubble up to the body and handle them there. 
        $('body').delegate @problemReturnSelector, 'click', @onClickReturn
  
    initTips: () ->
        # tooltips are used to display annotations for highlighted text spans
        @$(@spanSelector).each (index, el) =>
            $(el).qtip(@getSpanTipOptions el)

    getSpanTipOptions: (el) ->
        content:
            title:
                text: @makeTipTitle(el)
            text: @makeTipContent(el)
        position:
            my: 'bottom center' # of tooltip
            at: 'top center' # of target
            target: $(el) # where the tooltip was triggered (i.e. the annotation span)
            container: @$el
            adjust:
                y: -5
        show:
            event: 'click mouseenter'
            solo: true
        hide:
            event: 'click mouseleave'
            delay: 500,
            fixed: true # don't hide the tooltip if it is moused over
        style:
            classes: 'ui-tooltip-annotatable'
        events:
            show: @onShowTip

    onClickToggleAnnotations: (e) => @toggleAnnotations()

    onClickToggleInstructions: (e) => @toggleInstructions()

    onClickReply: (e) => @replyTo(e.currentTarget)

    onClickReturn: (e) => @returnFrom(e.currentTarget)

    onShowTip: (event, api) =>
        event.preventDefault() if @annotationsHidden
<commit_msg>Modified tooltip positioning on non-overlapping annotation spans.<commit_after>class @Annotatable
    _debug: false

    # selectors for the annotatable xmodule 
    toggleAnnotationsSelector:  '.annotatable-toggle-annotations'
    toggleInstructionsSelector: '.annotatable-toggle-instructions'
    instructionsSelector:       '.annotatable-instructions'
    sectionSelector:            '.annotatable-section'
    spanSelector:               '.annotatable-span'
    replySelector:              '.annotatable-reply'

    # these selectors are for responding to events from the annotation capa problem type
    problemXModuleSelector:     '.xmodule_CapaModule'
    problemSelector:            'section.problem'
    problemInputSelector:       'section.problem .annotation-input'
    problemReturnSelector:      'section.problem .annotation-return'

    constructor: (el) ->
        console.log 'loaded Annotatable' if @_debug
        @el = el
        @$el = $(el)
        @init()

    $: (selector) ->
        $(selector, @el)

    init: () ->
        @initEvents()
        @initTips()

    initEvents: () ->
        # Initialize toggle handlers for the instructions and annotations sections
        [@annotationsHidden, @instructionsHidden] = [false, false]
        @$(@toggleAnnotationsSelector).bind 'click', @onClickToggleAnnotations
        @$(@toggleInstructionsSelector).bind 'click', @onClickToggleInstructions

        # Initialize handler for 'reply to annotation' events that scroll to
        # the associated problem. The reply buttons are part of the tooltip
        # content. It's important that the tooltips be configured to render
        # as descendants of the annotation module and *not* the document.body.
        @$el.delegate @replySelector, 'click', @onClickReply

        # Initialize handler for 'return to annotation' events triggered from problems.
        #   1) There are annotationinput capa problems rendered on the page
        #   2) Each one has an embedded return link (see annotation capa problem template).
        # Since the capa problem injects HTML content via AJAX, the best we can do is
        # is let the click events bubble up to the body and handle them there. 
        $('body').delegate @problemReturnSelector, 'click', @onClickReturn
  
    initTips: () ->
        # tooltips are used to display annotations for highlighted text spans
        @$(@spanSelector).each (index, el) =>
            $(el).qtip(@getSpanTipOptions el)

    getSpanTipOptions: (el) ->
        content:
            title:
                text: @makeTipTitle(el)
            text: @makeTipContent(el)
        position:
            my: 'bottom center' # of tooltip
            at: 'top center' # of target
            target: $(el) # where the tooltip was triggered (i.e. the annotation span)
            container: @$el
            adjust:
                y: -5
        show:
            event: 'click mouseenter'
            solo: true
        hide:
            event: 'click mouseleave'
            delay: 500,
            fixed: true # don't hide the tooltip if it is moused over
        style:
            classes: 'ui-tooltip-annotatable'
        events:
            show: @onShowTip
            move: @onMoveTip

    onClickToggleAnnotations: (e) => @toggleAnnotations()

    onClickToggleInstructions: (e) => @toggleInstructions()

    onClickReply: (e) => @replyTo(e.currentTarget)

    onClickReturn: (e) => @returnFrom(e.currentTarget)

    onShowTip: (event, api) =>
        event.preventDefault() if @annotationsHidden

    onMoveTip: (event, api, position) =>
        ###
        This method handles an edge case in which a tooltip is displayed above
        a non-overlapping span like this:

                             (( TOOLTIP ))
                                  \/
        text text text ... text text text ...... <span span span>
        <span span span>

        The problem is that the tooltip looks disconnected from both spans, so
        we should re-position the tooltip to appear above the span.
        ###

        tip = api.elements.tooltip
        adjust_y = api.options.position?.adjust?.y || 0
        target = api.elements.target
        rects = $(target).get(0).getClientRects()
        is_non_overlapping = (rects?.length == 2 and rects[0].left > rects[1].right)

        if is_non_overlapping
            focus_rect = rects[0]
            rect_center = focus_rect.left + (focus_rect.width / 2)
            rect_top = focus_rect.top
            tip_width = $(tip).width()
            tip_height = $(tip).height()
            tip_left = rect_center - (tip_width / 2)
            tip_top =  window.pageYOffset + rect_top - tip_height + adjust_y
            win_width = $(window).width()
            if tip_left + tip_width > win_width
                tip_left = win_width - tip_width
            position.left = tip_left
            position.top = tip_top

{'fraction_non_alphanumeric': 0.09071403027774466, 'fraction_numerical': 0.0023840743831207534, 'mean_word_length': 2.231895223420647, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5829725', 'n_tokens_mistral': 2246, 'n_tokens_neox': 2153, 'n_words': 698}