<commit_before>########################################################
# AngularJS service to manage entries
########################################################

angular.module('feedbunch').service 'entrySvc',
['$rootScope', '$http', '$window', 'openEntrySvc', 'timerFlagSvc', 'changeUnreadCountSvc',
'currentFolderSvc', 'currentFeedSvc', 'findSvc', 'readSvc', 'feedsFoldersSvc',
'lazyLoadingSvc', 'tourSvc', 'animationsSvc', 'menuCollapseSvc',
($rootScope, $http, $window, openEntrySvc, timerFlagSvc, changeUnreadCountSvc,
currentFolderSvc, currentFeedSvc, findSvc, readSvc, feedsFoldersSvc,
lazyLoadingSvc, tourSvc, animationsSvc, menuCollapseSvc)->

  # CSRF token for POST
  token = $('meta[name="csrf-token"]').attr 'content'

  # Web worker to perform Ajax calls in a different thread.
  # A single worker associated with this service runs at all times.
  worker = new Worker '<%= asset_path 'workers/change_entry_state_worker' %>'
  worker.onmessage = (e) ->
    # Entry has changed state in the server
    if e.data.status == 200
      entry = findSvc.find_entry e.data.id
      entry.changing_state = false
      feedsFoldersSvc.reset_refresh_timer()
    else if e.data.status == 401 || e.data.status == 422
      $window.location.href = '/login'
    else
      timerFlagSvc.start 'error_changing_entry_state'

  #--------------------------------------------
  # PRIVATE FUNCTION - Mark a single entry as read or unread.
  # Receives as arguments an entry and a boolean indicating whether to mark
  # it as read (true) or unread (false).
  #--------------------------------------------
  change_entry_state = (entry, read)->
    # Mark entry as read or unread in the model
    entry.read = read
    entry.changing_state = true

    if read
      state = "read"
      changeUnreadCountSvc.update_unread_count entry, false
    else
      state = "unread"
      changeUnreadCountSvc.update_unread_count entry, true

    # Change the entry state in a separate thread using a web worker
    worker.postMessage {token: token, id: entry.id, state: state}

  #--------------------------------------------
  # PRIVATE FUNCTION - Set the feed with the passed ID as the currently selected one, and
  # load its list of entries
  #--------------------------------------------
  read_feed = (feed_id)->
    feed = findSvc.find_feed feed_id
    if feed?
      currentFeedSvc.set feed
      readSvc.read_entries_page()

  #--------------------------------------------
  # PRIVATE FUNCTION - Mark all entries in the currently selected feed or folder as read.
  #--------------------------------------------
  change_entries_read = ->
    if $rootScope.entries && $rootScope.entries?.length > 0
      # Mark entries as read in the model
      first_entry = $rootScope.entries[0]
      for entry in $rootScope.entries
        entry.read = true
        entry.changing_state = true

      # Find out if the user wants to mark as read a whole feed, a whole folder, or all entries in
      # all subscribed feeds.
      current_feed = currentFeedSvc.get()
      current_folder = currentFolderSvc.get()
      if current_feed
        whole_feed = "true"
        whole_folder = "false"
        all_entries = "false"
        changeUnreadCountSvc.zero_feed_count current_feed.id
      else if current_folder && current_folder?.id != "all"
        whole_feed = "false"
        whole_folder = "true"
        all_entries = "false"
        changeUnreadCountSvc.zero_folder_count current_folder
      else if current_folder && current_folder?.id == "all"
        whole_feed = "false"
        whole_folder = "false"
        all_entries = "true"
        changeUnreadCountSvc.zero_folder_count 'all'
      else
        return

      $http.put("/api/entries/update.json", entry: {id: first_entry.id, state: 'read', whole_feed: whole_feed, whole_folder: whole_folder, all_entries: all_entries})
      .success ->
        timerFlagSvc.start 'success_mark_all_read'
        # after marking multiple entries as read, reset the timer that updates feeds every minute
        feedsFoldersSvc.reset_refresh_timer()
        for entry in $rootScope.entries
          entry.changing_state = false
      .error (data, status)->
        timerFlagSvc.start 'error_changing_entry_state' if status!=0

  service =

    #--------------------------------------------
    # Set (if opening) or unset (if closing) the currently open entry. If opening, mark it as read.
    # Receives as arguments:
    # - entry to be opened or closed
    #--------------------------------------------
    toggle_open_entry: (entry)->
      if entry?
        if openEntrySvc.is_open entry
          # User is closing the open entry
          openEntrySvc.close entry
        else
          openEntrySvc.open entry
          if !entry.read
            # User is opening an unread entry, mark it as read
            change_entry_state entry, true
          # lazy load images
          lazyLoadingSvc.load_entry_images entry
          tourSvc.show_entry_tour()

    #--------------------------------------------
    # Mark a single entry as unread
    #--------------------------------------------
    read_entry: (entry)->
      if entry?
        change_entry_state entry, true if !entry.read

    #--------------------------------------------
    # Mark a single entry as unread
    #--------------------------------------------
    unread_entry: (entry)->
      if entry?
        change_entry_state entry, false if entry.read

    #--------------------------------------------
    # Mark all entries as read
    #--------------------------------------------
    mark_all_read: ->
      animationsSvc.highlight_read_all_button()
      menuCollapseSvc.close()
      change_entries_read()

    #--------------------------------------------
    # Return the title of the feed to which an entry belongs
    #--------------------------------------------
    entry_feed_title: (entry)->
      feed = findSvc.find_feed entry.feed_id
      if feed
        return feed.title
      else
        # If an entry is retrieved without a corresponding feed in the scope,
        # immediately load it from the server
        feedsFoldersSvc.load_feed entry.feed_id
        return ''

  return service
]<commit_msg>Better control of when entrySvc doesn't have to perform any work.<commit_after>########################################################
# AngularJS service to manage entries
########################################################

angular.module('feedbunch').service 'entrySvc',
['$rootScope', '$http', '$window', 'openEntrySvc', 'timerFlagSvc', 'changeUnreadCountSvc',
'currentFolderSvc', 'currentFeedSvc', 'findSvc', 'readSvc', 'feedsFoldersSvc',
'lazyLoadingSvc', 'tourSvc', 'animationsSvc', 'menuCollapseSvc',
($rootScope, $http, $window, openEntrySvc, timerFlagSvc, changeUnreadCountSvc,
currentFolderSvc, currentFeedSvc, findSvc, readSvc, feedsFoldersSvc,
lazyLoadingSvc, tourSvc, animationsSvc, menuCollapseSvc)->

  # CSRF token for POST
  token = $('meta[name="csrf-token"]').attr 'content'

  # Web worker to perform Ajax calls in a different thread.
  # A single worker associated with this service runs at all times.
  worker = new Worker '<%= asset_path 'workers/change_entry_state_worker' %>'
  worker.onmessage = (e) ->
    # Entry has changed state in the server
    if e.data.status == 200
      entry = findSvc.find_entry e.data.id
      entry.changing_state = false
      feedsFoldersSvc.reset_refresh_timer()
    else if e.data.status == 401 || e.data.status == 422
      $window.location.href = '/login'
    else
      timerFlagSvc.start 'error_changing_entry_state'

  #--------------------------------------------
  # PRIVATE FUNCTION - Mark a single entry as read or unread.
  # Receives as arguments an entry and a boolean indicating whether to mark
  # it as read (true) or unread (false).
  #--------------------------------------------
  change_entry_state = (entry, read)->
    # If entry is already in the requested state, nothing to be done
    if entry.read != read
      # Mark entry as read or unread in the model
      entry.read = read
      entry.changing_state = true

      if read
        state = "read"
        changeUnreadCountSvc.update_unread_count entry, false
      else
        state = "unread"
        changeUnreadCountSvc.update_unread_count entry, true

      # Change the entry state in a separate thread using a web worker
      worker.postMessage {token: token, id: entry.id, state: state}

  #--------------------------------------------
  # PRIVATE FUNCTION - Set the feed with the passed ID as the currently selected one, and
  # load its list of entries
  #--------------------------------------------
  read_feed = (feed_id)->
    feed = findSvc.find_feed feed_id
    if feed?
      currentFeedSvc.set feed
      readSvc.read_entries_page()

  #--------------------------------------------
  # PRIVATE FUNCTION - Mark all entries in the currently selected feed or folder as read.
  #--------------------------------------------
  change_entries_read = ->
    if $rootScope.entries && $rootScope.entries?.length > 0
      # Mark entries as read in the model
      first_entry = $rootScope.entries[0]
      for entry in $rootScope.entries
        entry.read = true
        entry.changing_state = true

      # Find out if the user wants to mark as read a whole feed, a whole folder, or all entries in
      # all subscribed feeds.
      current_feed = currentFeedSvc.get()
      current_folder = currentFolderSvc.get()
      if current_feed
        whole_feed = "true"
        whole_folder = "false"
        all_entries = "false"
        changeUnreadCountSvc.zero_feed_count current_feed.id
      else if current_folder && current_folder?.id != "all"
        whole_feed = "false"
        whole_folder = "true"
        all_entries = "false"
        changeUnreadCountSvc.zero_folder_count current_folder
      else if current_folder && current_folder?.id == "all"
        whole_feed = "false"
        whole_folder = "false"
        all_entries = "true"
        changeUnreadCountSvc.zero_folder_count 'all'
      else
        return

      $http.put("/api/entries/update.json", entry: {id: first_entry.id, state: 'read', whole_feed: whole_feed, whole_folder: whole_folder, all_entries: all_entries})
      .success ->
        timerFlagSvc.start 'success_mark_all_read'
        # after marking multiple entries as read, reset the timer that updates feeds every minute
        feedsFoldersSvc.reset_refresh_timer()
        for entry in $rootScope.entries
          entry.changing_state = false
      .error (data, status)->
        timerFlagSvc.start 'error_changing_entry_state' if status!=0

  service =

    #--------------------------------------------
    # Set (if opening) or unset (if closing) the currently open entry. If opening, mark it as read.
    # Receives as arguments:
    # - entry to be opened or closed
    #--------------------------------------------
    toggle_open_entry: (entry)->
      if entry?
        if openEntrySvc.is_open entry
          openEntrySvc.close entry
        else
          openEntrySvc.open entry
          change_entry_state entry, true
          lazyLoadingSvc.load_entry_images entry
          tourSvc.show_entry_tour()

    #--------------------------------------------
    # Mark a single entry as unread
    #--------------------------------------------
    read_entry: (entry)->
      if entry?
        change_entry_state entry, true if !entry.read

    #--------------------------------------------
    # Mark a single entry as unread
    #--------------------------------------------
    unread_entry: (entry)->
      if entry?
        change_entry_state entry, false if entry.read

    #--------------------------------------------
    # Mark all entries as read
    #--------------------------------------------
    mark_all_read: ->
      animationsSvc.highlight_read_all_button()
      menuCollapseSvc.close()
      change_entries_read()

    #--------------------------------------------
    # Return the title of the feed to which an entry belongs
    #--------------------------------------------
    entry_feed_title: (entry)->
      feed = findSvc.find_feed entry.feed_id
      if feed
        return feed.title
      else
        # If an entry is retrieved without a corresponding feed in the scope,
        # immediately load it from the server
        feedsFoldersSvc.load_feed entry.feed_id
        return ''

  return service
]
{'fraction_non_alphanumeric': 0.20285096500360375, 'fraction_numerical': 0.0019219988788339874, 'mean_word_length': 1.915713285080551, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '135825', 'n_tokens_mistral': 3378, 'n_tokens_neox': 3289, 'n_words': 1004}