Write new Notes coffee class to handle comments

Signed-off-by: Dmitriy Zaporozhets <be23d75b156792e5acab51b196a2deb155d35d6a@gmail.com>

error: pathspec 'app/assets/javascripts/notes.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -0,0 +1,397 @@
+class Notes
+  constructor: (notes_url, note_ids) ->
+    @notes_url = notes_url
+    @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
+    @note_ids = note_ids
+    @initRefresh()
+    @setupMainTargetNoteForm()
+    @cleanBinding()
+    @addBinding()
+
+  addBinding: ->
+    # add note to UI after creation
+    $(document).on "ajax:success", ".js-main-target-form", @addNote
+    $(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote
+
+        # change note in UI after update
+    $(document).on "ajax:success", "form.edit_note", @updateNote
+
+    # Edit note link
+    $(document).on "click", ".js-note-edit", @showEditForm
+    $(document).on "click", ".note-edit-cancel", @cancelEdit
+
+    # remove a note (in general)
+    $(document).on "click", ".js-note-delete", @removeNote
+
+    # delete note attachment
+    $(document).on "click", ".js-note-attachment-delete", @removeAttachment
+
+    # Preview button
+    $(document).on "click", ".js-note-preview-button", @previewNote
+
+    # reset main target form after submit
+    $(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
+
+    # attachment button
+    $(document).on "click", ".js-choose-note-attachment-button", @chooseNoteAttachment
+
+    # reply to diff/discussion notes
+    $(document).on "click", ".js-discussion-reply-button", @replyToDiscussionNote
+
+    # add diff note
+    $(document).on "click", ".js-add-diff-note-button", @addDiffNote
+
+  cleanBinding: ->
+    $(document).off "ajax:success", ".js-main-target-form"
+    $(document).off "ajax:success", ".js-discussion-note-form"
+    $(document).off "ajax:success", "form.edit_note"
+    $(document).off "click", ".js-note-edit"
+    $(document).off "click", ".note-edit-cancel"
+    $(document).off "click", ".js-note-delete"
+    $(document).off "click", ".js-note-attachment-delete"
+    $(document).off "click", ".js-note-preview-button"
+    $(document).off "ajax:complete", ".js-main-target-form"
+    $(document).off "click", ".js-choose-note-attachment-button"
+    $(document).off "click", ".js-discussion-reply-button"
+    $(document).off "click", ".js-add-diff-note-button"
+
+
+  initRefresh: ->
+    setInterval =>
+      @refresh()
+    , 15000
+
+  refresh: ->
+    @getContent()
+
+  getContent: ->
+    $.ajax
+      url: @notes_url
+      dataType: "json"
+      success: (data) =>
+        notes = data.notes
+        $.each notes, (i, note) =>
+          # render note if it not present in loaded list
+          # or skip if rendered
+          if $.inArray(note.id, @note_ids) == -1
+            @note_ids.push(note.id)
+            @renderNote(note)
+
+
+  ###
+  Render note in main comments area.
+
+  Note: for rendering inline notes use renderDiscussionNote
+  ###
+  renderNote: (note) ->
+    $('ul.main-notes-list').append(note.html)
+
+  ###
+  Render note in discussion area.
+
+  Note: for rendering inline notes use renderDiscussionNote
+  ###
+  renderDiscussionNote: (note) ->
+    form = $("form[rel='" + note.discussion_id + "']")
+    row = form.closest("tr")
+
+    # is this the first note of discussion?
+    if row.is(".js-temp-notes-holder")
+      # insert the note and the reply button after the temp row
+      row.after note.discussion_html
+
+      # remove the note (will be added again below)
+      row.next().find(".note").remove()
+
+    # append new note to all matching discussions
+    $(".notes[rel='" + note.discussion_id + "']").append note.html
+
+    # cleanup after successfully creating a diff/discussion note
+    @removeDiscussionNoteForm(form)
+
+  ###
+  Shows the note preview.
+
+  Lets the server render GFM into Html and displays it.
+
+  Note: uses the Toggler behavior to toggle preview/edit views/buttons
+  ###
+  previewNote: (e) ->
+    e.preventDefault()
+    form = $(this).closest("form")
+    preview = form.find(".js-note-preview")
+    noteText = form.find(".js-note-text").val()
+    if noteText.trim().length is 0
+      preview.text "Nothing to preview."
+    else
+      preview.text "Loading..."
+      $.post($(this).data("url"),
+        note: noteText
+      ).success (previewData) ->
+        preview.html previewData
+
+  ###
+  Called in response the main target form has been successfully submitted.
+
+  Removes any errors.
+  Resets text and preview.
+  Resets buttons.
+  ###
+  resetMainTargetForm: ->
+    form = $(".js-main-target-form")
+
+    # remove validation errors
+    form.find(".js-errors").remove()
+
+    # reset text and preview
+    previewContainer = form.find(".js-toggler-container.note_text_and_preview")
+    previewContainer.removeClass "on"  if previewContainer.is(".on")
+    form.find(".js-note-text").val("").trigger "input"
+
+  ###
+  Called when clicking the "Choose File" button.
+
+  Opens the file selection dialog.
+  ###
+  chooseNoteAttachment: ->
+    form = $(this).closest("form")
+    form.find(".js-note-attachment-input").click()
+
+  ###
+  Shows the main form and does some setup on it.
+
+  Sets some hidden fields in the form.
+  ###
+  setupMainTargetNoteForm: ->
+
+    # find the form
+    form = $(".js-new-note-form")
+
+    # insert the form after the button
+    form.clone().replaceAll $(".js-main-target-form")
+    form = form.prev("form")
+
+    # show the form
+    @setupNoteForm(form)
+
+    # fix classes
+    form.removeClass "js-new-note-form"
+    form.addClass "js-main-target-form"
+
+    # remove unnecessary fields and buttons
+    form.find("#note_line_code").remove()
+    form.find(".js-close-discussion-note-form").remove()
+
+  ###
+  General note form setup.
+
+  deactivates the submit button when text is empty
+  hides the preview button when text is empty
+  setup GFM auto complete
+  show the form
+  ###
+  setupNoteForm: (form) ->
+    disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+    form.removeClass "js-new-note-form"
+
+    # setup preview buttons
+    form.find(".js-note-edit-button, .js-note-preview-button").tooltip placement: "left"
+    previewButton = form.find(".js-note-preview-button")
+    form.find(".js-note-text").on "input", ->
+      if $(this).val().trim() isnt ""
+        previewButton.removeClass("turn-off").addClass "turn-on"
+      else
+        previewButton.removeClass("turn-on").addClass "turn-off"
+
+
+    # remove notify commit author checkbox for non-commit notes
+    form.find(".js-notify-commit-author").remove()  if form.find("#note_noteable_type").val() isnt "Commit"
+    GitLab.GfmAutoComplete.setup()
+    form.show()
+
+
+  ###
+  Called in response to the new note form being submitted
+
+  Adds new note to list.
+  ###
+  addNote: (xhr, note, status) =>
+    @note_ids.push(note.id)
+    @renderNote(note)
+
+  ###
+  Called in response to the new note form being submitted
+
+  Adds new note to list.
+  ###
+  addDiscussionNote: (xhr, note, status) =>
+    @note_ids.push(note.id)
+    @renderDiscussionNote(note)
+
+  ###
+  Called in response to the edit note form being submitted
+
+  Updates the current note field.
+  ###
+  updateNote: (xhr, note, status) =>
+    note_li = $("#note_" + note.id)
+    note_li.replaceWith(note.html)
+
+  ###
+  Called in response to clicking the edit note link
+
+  Replaces the note text with the note edit form
+  Adds a hidden div with the original content of the note to fill the edit note form with
+  if the user cancels
+  ###
+  showEditForm: (e) ->
+    e.preventDefault()
+    note = $(this).closest(".note")
+    note.find(".note-text").hide()
+
+    # Show the attachment delete link
+    note.find(".js-note-attachment-delete").show()
+    GitLab.GfmAutoComplete.setup()
+    form = note.find(".note-edit-form")
+    form.show()
+    form.find("textarea").focus()
+
+  ###
+  Called in response to clicking the edit note link
+
+  Hides edit form
+  ###
+  cancelEdit: (e) ->
+    e.preventDefault()
+    note = $(this).closest(".note")
+    note.find(".note-text").show()
+    note.find(".js-note-attachment-delete").hide()
+    note.find(".note-edit-form").hide()
+
+  ###
+  Called in response to deleting a note of any kind.
+
+  Removes the actual note from view.
+  Removes the whole discussion if the last note is being removed.
+  ###
+  removeNote: ->
+    note = $(this).closest(".note")
+    notes = note.closest(".notes")
+
+    # check if this is the last note for this line
+    if notes.find(".note").length is 1
+
+      # for discussions
+      notes.closest(".discussion").remove()
+
+      # for diff lines
+      notes.closest("tr").remove()
+
+    note.remove()
+
+  ###
+  Called in response to clicking the delete attachment link
+
+  Removes the attachment wrapper view, including image tag if it exists
+  Resets the note editing form
+  ###
+  removeAttachment: ->
+    note = $(this).closest(".note")
+    note.find(".note-attachment").remove()
+    note.find(".note-text").show()
+    note.find(".js-note-attachment-delete").hide()
+    note.find(".note-edit-form").hide()
+
+  ###
+  Called when clicking on the "reply" button for a diff line.
+
+  Shows the note form below the notes.
+  ###
+  replyToDiscussionNote: (e) =>
+    form = $(".js-new-note-form")
+    replyLink = $(e.target)
+    replyLink.hide()
+
+    # insert the form after the button
+    form.clone().insertAfter replyLink
+
+    # show the form
+    @setupDiscussionNoteForm(replyLink, replyLink.next("form"))
+
+  ###
+  Shows the diff or discussion form and does some setup on it.
+
+  Sets some hidden fields in the form.
+
+  Note: dataHolder must have the "discussionId", "lineCode", "noteableType"
+  and "noteableId" data attributes set.
+  ###
+  setupDiscussionNoteForm: (dataHolder, form) =>
+    # setup note target
+    form.attr "rel", dataHolder.data("discussionId")
+    form.find("#note_commit_id").val dataHolder.data("commitId")
+    form.find("#note_line_code").val dataHolder.data("lineCode")
+    form.find("#note_noteable_type").val dataHolder.data("noteableType")
+    form.find("#note_noteable_id").val dataHolder.data("noteableId")
+    @setupNoteForm form
+    form.find(".js-note-text").focus()
+    form.addClass "js-discussion-note-form"
+
+  ###
+  General note form setup.
+
+  deactivates the submit button when text is empty
+  hides the preview button when text is empty
+  setup GFM auto complete
+  show the form
+  ###
+  setupNoteForm: (form) =>
+    disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+    form.removeClass "js-new-note-form"
+    form.removeClass "js-new-note-form"
+    GitLab.GfmAutoComplete.setup()
+    form.show()
+
+  ###
+  Called when clicking on the "add a comment" button on the side of a diff line.
+
+  Inserts a temporary row for the form below the line.
+  Sets up the form and shows it.
+  ###
+  addDiffNote: (e) =>
+    e.preventDefault()
+    link = e.target
+    form = $(".js-new-note-form")
+    row = $(link).closest("tr")
+    nextRow = row.next()
+
+    # does it already have notes?
+    if nextRow.is(".notes_holder")
+      $.proxy(@replyToDiscussionNote, nextRow.find(".js-discussion-reply-button")).call()
+    else
+      # add a notes row and insert the form
+      row.after "

"
+      form.clone().appendTo row.next().find(".notes_content")
+
+      # show the form
+      @setupDiscussionNoteForm $(link), row.next().find("form")
+
+  ###
+  Called in response to "cancel" on a diff note form.
+
+  Shows the reply button again.
+  Removes the form and if necessary it's temporary row.
+  ###
+  removeDiscussionNoteForm: (form)->
+    row = form.closest("tr")
+
+    # show the reply button (will only work for replies)
+    form.prev(".js-discussion-reply-button").show()
+    if row.is(".js-temp-notes-holder")
+      # remove temporary row for diff lines
+      row.remove()
+    else
+      # only remove the form
+      form.remove()
+
+@Notes = Notes


Add CoffeeScript for uploads and progress display.

error: pathspec 'app/assets/javascripts/s3_relay.coffee' did not match any file(s) known to git

--- app/assets/javascripts/s3_relay.coffee
+++ app/assets/javascripts/s3_relay.coffee
@@ -0,0 +1,51 @@
+uploadFile = (fileField) ->
+  form = fileField.parent()
+  fileInput = document.getElementById("file")
+  file = fileInput.files[0]
+
+  $.ajax
+    type: "GET"
+    url: "/signatures/new"
+    success: (response) ->
+      formData = new FormData()
+      xhr = new XMLHttpRequest()
+      endpoint = response.endpoint
+
+      formData.append("AWSAccessKeyID", response.awsaccesskeyid)
+      formData.append("x-amz-server-side-encryption", response.x_amz_server_side_encryption)
+      formData.append("key", response.key)
+      formData.append("success_action_status", response.success_action_status)
+      formData.append("acl", response.acl)
+      formData.append("policy", response.policy)
+      formData.append("signature", response.signature)
+      formData.append("file", file)
+
+      fileField.val("")
+
+      uuid = response.uuid
+      form.append("

")
+      progressBar = $("#progress-#{uuid}")
+
+      xhr.upload.addEventListener "progress", (ev) ->
+        # Progress...
+        percentage = ((ev.position / ev.totalSize) * 100.0).toFixed(2) + "%"
+        progressBar.text("Uploading: #{file.name} - " + percentage)
+
+      xhr.onreadystatechange = (ev) ->
+        if xhr.readyState is 4
+          if xhr.status == 201
+            progressBar.text("Uploading: #{file.name} - Complete")
+
+            url = $("Location", xhr.responseXML).text()
+            # TODO: Pass URL to app, retrieve private URL and display link
+          else
+            progressBar.text("File could not be uploaded")
+            console.log $("Message", xhr.responseXML).text()
+
+      xhr.open "POST", endpoint, true
+      xhr.send formData
+
+jQuery ->
+
+  $("form.s3_relay").on "change", "input[type='file']", ->
+    uploadFile($(this))


fix dragging to backlog

fix dragging to backlog

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -299,7 +299,7 @@
         newSprint = @scope.sprintsById[newSprintId]

         # Move to closed sprint
-        if !newSprint
+        if !newSprint && newSprintId
             newSprint = @scope.closedSprintsById[newSprintId]
             movedToClosedSprint = true if newSprint


template fix

template fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/keboola/kbc-ui-doctool-recipes.git/'


:bug: Fix right click starting a drag gesture

--- lib/minimap-element.coffee
+++ lib/minimap-element.coffee
@@ -345,14 +345,15 @@
     for config, callback of configs
       @subscriptions.add atom.config.observe config, callback

-  mousePressedOverCanvas: ({pageY, target}) ->
+  mousePressedOverCanvas: ({which, pageY, target}) ->
+    return if which isnt 1
+
     y = pageY - target.getBoundingClientRect().top
     row = Math.floor(y / @minimap.getLineHeight()) + @minimap.getFirstVisibleScreenRow()

     scrollTop = row * @minimap.textEditor.getLineHeightInPixels() - @minimap.textEditor.getHeight() / 2

     @minimap.textEditor.setScrollTop(scrollTop)
-
   relayMousewheelEvent: (e) =>
     editorElement = atom.views.getView(@minimap.textEditor)

@@ -366,7 +367,8 @@
   #    ##     ## ##   ##   ##     ##
   #    ########   ####  ## ########

-  startDrag: ({pageY}) ->
+  startDrag: ({which, pageY}) ->
+    return if which isnt 1
     {top} = @visibleArea.getBoundingClientRect()
     {top: offsetTop} = @getBoundingClientRect()

@@ -387,6 +389,7 @@
       document.body.removeEventListener('mouseout', mouseupHandler)

   drag: (e, initial) ->
+    return if e.which isnt 1
     y = e.pageY - initial.offsetTop - initial.dragOffset

     ratio = y / (@minimap.getVisibleHeight() - @minimap.getTextEditorScaledHeight())


Ensure @compositionInProgress Is Never undefined

--- lib/suggestion-list.coffee
+++ lib/suggestion-list.coffee
@@ -4,6 +4,7 @@
 module.exports =
 class SuggestionList extends Model
   constructor: ->
+    @compositionInProgress = false
     @emitter = new Emitter
     @subscriptions = new CompositeDisposable
     # Allow keyboard navigation of the suggestion list


Update configuration

Update configuration

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ehmicky/myos.git/'


Update configuration

Update configuration


Add robocode effect board

Add robocode effect board

error: pathspec 'examples/robocode2.coffee' did not match any file(s) known to git

--- examples/robocode2.coffee
+++ examples/robocode2.coffee
@@ -0,0 +1,217 @@
+
+dgram = require('dgram')
+
+tty = require('tty')
+process.stdin.resume()
+tty.setRawMode(true)
+
+
+randInt = (min, max) ->
+  Math.floor(Math.random() * (max - min + 1)) + min
+
+
+class EffectClient
+
+  constructor: ({@min, @max, @nick, @ip, @port}) ->
+    @client = dgram.createSocket("udp4")
+    @reset()
+
+  getRandId: -> randInt @min, @max
+
+  reset: ->
+    @packet = [ 1 ]
+    @packet.push 0
+    for c in @nick
+      @packet.push c.charCodeAt 0
+    @packet.push 0
+
+  setAll: (r, g, b) ->
+    for i in [@min..@max]
+      @set i, r, g, b
+
+  set: (i, r, g, b) ->
+    @packet.push 1
+    @packet.push i
+    @packet.push 0
+
+    if typeof r is "object"
+      {r,g,b} = r
+
+    @packet.push r
+    @packet.push g
+    @packet.push b
+
+  send: ->
+    buf = new Buffer @packet
+    @client.send buf, 0, buf.length, @port, @ip
+    @reset()
+
+
+
+class Player
+
+  constructor: (@client) ->
+
+  loop: (program) ->
+    @stop()
+    @program = program
+    @pos = @program.length
+    @tick()
+
+  stop: ->
+    clearTimeout @timeout if @timeout
+    @program = null
+
+
+  tick: =>
+    if not @program
+      return
+
+    @pos = (@pos + 1) % @program.length
+
+    action = @program[@pos]
+
+    if typeof action is "number"
+      @client.send()
+      @timeout = setTimeout @tick, action
+      return
+
+    if Array.isArray action
+      @client.set action...
+      process.nextTick @tick
+      return
+
+
+    throw new Error "Bad action"
+
+
+
+client = new EffectClient
+  min: 0
+  max: 38
+  nick: "epe"
+  ip: process.argv[2] or "localhost"
+  port: 9909
+
+
+extend = (a) ->
+  a.sleep = (time) -> @push time
+  a.set = (i, r, g, b) -> @push [i, r, g, b]
+  a.all = (r, g, b) ->
+    for i in [client.min..client.max]
+      @push [i, r, g, b]
+  return a
+
+RED = [255, 0, 0]
+GREEN = [0, 255, 0]
+BLUE = [0, 0, 255]
+BLACK = [0, 0, 0]
+WHITE = [255, 255, 255]
+
+mapping =
+
+  o: do ->
+    p = extend []
+    p.all BLACK...
+    p.sleep 1000
+    return p
+
+
+  p: do ->
+    p = extend []
+    p.all WHITE...
+    p.sleep 1000
+    return p
+
+  r: do ->
+    p = extend []
+
+    p.all BLACK
+    len = 10
+
+    for i in [client.min..client.max]
+      for j in [0..len]
+        p.set i-j, BLACK...
+
+      for j in [0..len]
+        p.set i+j, RED...
+
+      p.sleep 40
+
+    return p
+
+  b: do ->
+    p = extend []
+
+    p.all BLACK...
+    p.sleep 0
+
+    for i in [0..255] by 5
+      p.all 0, 0, i
+      p.sleep 20
+
+    for i in [0..255] by 5
+      p.all 0, 0, 255-i
+      p.sleep 20
+
+
+    return p
+
+
+  h: do ->
+    p = extend []
+    p.all GREEN...
+    p.sleep 100
+    p.all BLUE...
+    p.sleep 100
+    return p
+
+  j: do ->
+    p = extend []
+    p.all WHITE...
+    p.sleep 100
+    p.all RED...
+    p.sleep 100
+    return p
+
+  k: do ->
+    p = extend []
+
+    p.all BLACK
+
+    for i in [client.min..client.max]
+      p.set i-1, BLACK...
+      p.set i-2, BLACK...
+      p.set i, BLUE...
+      p.set i+1, BLUE...
+      p.set i+2, BLUE...
+      p.sleep 100
+
+
+
+    return p
+
+
+player = new Player client
+
+
+process.stdin.on 'keypress', (char, key) ->
+  if char.charCodeAt(0) is 27 # esc
+    process.exit 0
+
+  return if not key # Ignore bad keys
+
+  console.log ("#{ k }: #{ p.length }" for k, p of mapping).join ", "
+
+  if key.name is "s" # stop
+    console.log "Stopping"
+    player.stop()
+    return
+
+  if program = mapping[key.name]
+    console.log "Playing", key.name
+    player.loop program
+  else
+    console.log "Unkown program #{ char }"
+
+


Update pt-PT.coffee

Update pt-PT.coffee

--- app/locale/pt-PT.coffee
+++ app/locale/pt-PT.coffee
@@ -517,19 +517,19 @@
     matt_title: "Programador"
     matt_blurb: "Ciclista"

-#  teachers:
-#    title: "CodeCombat for Teachers"
-#    preparation_title: "Preparation"
+  teachers:
+    title: "CodeCombat para Professores"
+    preparation_title: "Preparação"
 #    preparation_1: "CodeCombat is free to play for the core level progression and does not require students to sign up. We encourage teachers to"
 #    preparation_play_campaign: "play through the campaign"
 #    preparation_2: "to try it out, but the only thing you absolutely need to do to be ready is ensure students have access to a computer."
 #    preparation_3: "It is not necessary for teachers to be comfortable with computer science concepts for students to have fun learning with CodeCombat."
-#    violent_title: "Is it violent?"
+    violent_title: "É violento?"
 #    violent_1: "We get this from teachers a lot due to our name. Although CodeCombat does contain cartoon violence, there is nothing graphic in either the visuals or language."
 #    violent_2: "If you are comfortable having your students play Angry Birds, you will be comfortable with CodeCombat."
-#    for_girls_title: "Is it for girls?"
+    for_girls_title: "É para raparigas?"
 #    for_girls_1: "There are three game modes in CodeCombat: building, puzzles, and combat. We have intentionally designed each to appeal to both boys and girls and think that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players."
-#    what_cover_title: "What do we cover?"
+    what_cover_title: "O que abordamos?"
 #    what_cover_1: "There are 20 levels in the Hour of Code tutorial that teach and reinforce 6 specific computer science concepts:"
 #    what_cover_notation_1: "Formal notation"
 #    what_cover_notation_2: "- builds an understanding of the importance of syntax in programming."
@@ -998,7 +998,7 @@
     play_counts: "Número de Jogos"
     feedback: "Feedback"
     payment_info: "Informações de Pagamento"
-#    campaigns: "Campaigns"
+    campaigns: "Campanhas"

   delta:
     added: "Adicionados/as"


Export telepath.Document as Document from core

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -1,5 +1,5 @@
 {View, $$, $$$} = require '../src/space-pen-extensions'
-{Point, Range} = require 'telepath'
+{Document, Point, Range} = require 'telepath'

 module.exports =
   _: require '../src/underscore-extensions'
@@ -9,6 +9,7 @@
   BufferedNodeProcess: require '../src/buffered-node-process'
   BufferedProcess: require '../src/buffered-process'
   Directory: require '../src/directory'
+  Document: Document
   EventEmitter: require '../src/event-emitter'
   File: require '../src/file'
   fs: require '../src/fs-utils'


Git -> GitRepository

Git -> GitRepository

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -9,7 +9,7 @@
   Range: Range

 Object.defineProperty module.exports, 'Git', get: ->
-  deprecate "Please require `GitRepository` instead of `Git`: `{Git} = require 'atom'`"
+  deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`"
   module.exports.GitRepository

 # The following classes can't be used from a Task handler and should therefore


Dont error when throwing an error that is not JSON

--- lib/middleware/helpers.coffee
+++ lib/middleware/helpers.coffee
@@ -7,8 +7,9 @@
     res.statusCode  = e.error.status
     try
       parsed        = JSON.parse e.text
+      errorText     = parsed.error
     catch e
-      parsed        = e.text
+      errorText        = e.text

-    next new Error(parsed.error)
+    next new Error(error)
   next()


Update main.coffee

Update main.coffee

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jasssonpet/TelerikAcademy.git/'


added Bandango.RetencionSerializer

added Bandango.RetencionSerializer

error: pathspec 'app/assets/javascripts/serializers/retencion_serializer.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/serializers/retencion_serializer.js.coffee
+++ app/assets/javascripts/serializers/retencion_serializer.js.coffee
@@ -0,0 +1,10 @@
+Bandango.RetencionSerializer = DS.ActiveModelSerializer.extend
+  serialize: (retencion, options) ->
+    unless retencion.get("inForm")
+      {
+        concepto_retencion_fuente_id: retencion.get("conceptoRetencionFuente.id")
+        numero_autorizacion: retencion.get("numeroAutorizacion")
+        numero_serie_establecimiento: retencion.get("numeroSerieEstablecimiento")
+        numero_serie_punto_emision: retencion.get("numeroSeriePuntoEmision")
+        numero_serie_secuencial: retencion.get("numeroSerieSecuencial")
+      }


sign in/sign up using username

sign in/sign up using username

error: pathspec 'app/client/bootstrap/accounts.coffee' did not match any file(s) known to git

--- app/client/bootstrap/accounts.coffee
+++ app/client/bootstrap/accounts.coffee
@@ -0,0 +1,22 @@
+AccountsTemplates.addField
+  _id: 'username'
+  type: 'text'
+  required: true
+#  TODO: Uncomment after server-side checker implement
+#  func: (value) ->
+#    if (Meteor.isClient) {
+#      self = this
+#      Meteor.call"userExists", value, (err, userExists) ->
+#        unless userExists
+#          self.setSuccess()
+#        else
+#          self.setError userExists
+#        self.setValidating false
+#    else if Meteor.isServer
+#      Meteor.call "userExists", value
+
+AccountsTemplates.addFiles
+  _id: 'fullname'
+  type: 'text'
+  displayname: 'Fullname'
+  required: true


Remove common scripts from user-created levels

--- app/lib/scripts/commonScripts.coffee
+++ app/lib/scripts/commonScripts.coffee
@@ -1,85 +0,0 @@
-module.exports = [
-  {
-    "channel": "surface:sprite-selected",
-    "noteChain": [
-      {
-        "sprites": [
-          {
-            "id": "Captain Anya",
-            "say": {
-              "text": "What do you need help with, boss?",
-              "responses": [
-                {
-                  "text": "What do I do next?",
-                  "channel": "help-next"
-                },
-                {
-                  "text": "I'm lost",
-                  "channel": "help-overview"
-                },
-                {
-                  "text": "Never mind.",
-                  "channel": "end-current-script"
-                }
-              ]
-            }
-          }
-        ]
-      }
-    ],
-    "eventPrereqs": [
-      {
-        "eventProps": [
-          "sprite",
-          "thang",
-          "id"
-        ],
-        "equalTo": "Captain Anya"
-      }
-    ],
-    "id": "Anya Clicked Guide"
-    "repeats": true
-  },
-  {
-    "channel": "help-next",
-    "noteChain": [
-      {
-        "sprites": [
-          {
-            "id": "Captain Anya",
-            "say": {
-              "text": "Look for a list of objectives on the upper left. They tell you what to focus on."
-            }
-          }
-        ],
-        "dom": {
-          "highlight": {
-            "target": "#primary-goals-list"
-          }
-        }
-      }
-    ],
-    "id": "Clicked Help Next"
-  },
-  {
-    "channel": "help-overview",
-    "noteChain": [
-      {
-        "sprites": [
-          {
-            "id": "Captain Anya",
-            "say": {
-              "text": "Click the 'Guide' button on the upper right for an overview of the level and some hints."
-            }
-          }
-        ],
-        "dom": {
-          "highlight": {
-            "target": "#docs-button"
-          }
-        }
-      }
-    ],
-    "id": "Clicked I'm Lost"
-  }
-]


Make language consistent

Make language consistent

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -339,7 +339,7 @@

           expect(events).toEqual ['input:d', 'input:o', 'dog']

-    describe "when the a binding specifies a keyup handler", ->
+    describe "when the binding specifies a keyup handler", ->
       [events, elementA] = []

       beforeEach ->
@@ -368,27 +368,27 @@
         advanceClock(keymapManager.getPartialMatchTimeout())
         expect(events).toEqual ['y-up-ctrl-keyup']

-      it "dispatches a command when modifier is lifted before the character", ->
+      it "dispatches the command when modifier is lifted before the character", ->
         keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
         keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
         advanceClock(keymapManager.getPartialMatchTimeout())
         expect(events).toEqual ['y-ctrl-keyup']

-      it "dispatches keyup command when extra user-generated keyup events are not specified in the binding", ->
+      it "dispatches the command when extra user-generated keyup events are not specified in the binding", ->
         keymapManager.handleKeyboardEvent(buildKeydownEvent('x', ctrl: true, target: elementA))
         keymapManager.handleKeyboardEvent(buildKeyupEvent('x', ctrl: true, target: elementA)) # not specified in binding
         keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
         advanceClock(keymapManager.getPartialMatchTimeout())
         expect(events).toEqual ['x-ctrl-keyup']

-      it "does _not_ dispatch keyup command when extra user-generated keydown events are not specified in the binding", ->
+      it "does _not_ dispatch the command when extra user-generated keydown events are not specified in the binding", ->
         keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
         keymapManager.handleKeyboardEvent(buildKeydownEvent('z', ctrl: true, target: elementA)) # not specified in binding
         keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
         advanceClock(keymapManager.getPartialMatchTimeout())
         expect(events).toEqual ['y-keydown']

-      it "dispatches commands with multiple keyup keystrokes specified", ->
+      it "dispatches the command when multiple keyup keystrokes are specified", ->
         keymapManager.handleKeyboardEvent(buildKeydownEvent('a', target: elementA))
         keymapManager.handleKeyboardEvent(buildKeydownEvent('b', target: elementA))
         keymapManager.handleKeyboardEvent(buildKeydownEvent('c', target: elementA))


Fixed a bug where sometimes new worlds would hide the hud speaker unnecessarily.

--- app/views/play/level/hud_view.coffee
+++ app/views/play/level/hud_view.coffee
@@ -66,8 +66,9 @@
     @clearSpeaker()

   onNewWorld: (e) ->
+    hadThang = @thang
     @thang = e.world.thangMap[@thang.id] if @thang
-    if not @thang
+    if hadThang and not @thang
       @setThang null, null

   setThang: (thang, thangType) ->


Removed error message for offline cameras

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/evercam/evercam-dashboard.git/'


fix hound error

fix hound error


Added ability to clear current selection of autocomplete component

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/UMNLibraries/ikidowinan.git/'


Updated Danger Minefield icon.

Updated Danger Minefield icon.

--- app/views/play_view.coffee
+++ app/views/play_view.coffee
@@ -224,8 +224,8 @@
         name: 'Danger! Minefield'
         difficulty: 3
         id: 'danger-minefield'
-        image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
-        description: "Learn how to find prime numbers while defusing mines ! - by Alexandru Caciulescu"
+        image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
+        description: "Learn how to find prime numbers while defusing mines! - by Alexandru Caciulescu"
       }
       {
         name: 'Find the Spy'
@@ -241,16 +241,6 @@
         image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
         description: "Collect a hundred mushrooms in just five lines of code - by Nathan Gossett"
       }
-
-
-      #{
-      #  name: 'Enemy Artillery'
-      #  difficulty: 1
-      #  id: 'enemy-artillery'
-      #  image: '/file/db/level/526dba94a188322044000a40/mobile_artillery_icon.png'
-      #  description: "Take cover while shells fly, then strike! - by mcdavid1991"
-      #  disabled: true
-      #}
     ]

     context.campaigns = [


Trigger 'cursor-move' events when cursor view changes

--- src/app/cursor-view.coffee
+++ src/app/cursor-view.coffee
@@ -22,10 +22,6 @@
       @needsAutoscroll = (autoscroll ? true) and @cursor?.isLastCursor()
       @editor.requestDisplayUpdate()

-      # TODO: Move idle/active to the cursor model
-#       @removeIdleClassTemporarily() unless bufferChange
-      @trigger 'cursor-move', {bufferChange}
-
     @cursor.on 'change-visibility.cursor-view', (visible) =>
       @needsUpdate = true
       @needsAutoscroll = visible and @cursor.isLastCursor()
@@ -47,6 +43,8 @@
     unless _.isEqual(@lastPixelPosition, pixelPosition)
       changedPosition = true
       @css(pixelPosition)
+#       @removeIdleClassTemporarily() unless bufferChange
+      @trigger 'cursor-move'

     @setVisible(@cursor.isVisible() and not @editor.isFoldedAtScreenRow(screenPosition.row))


Change timeout to 30000

Change timeout to 30000

--- tests/spacejamTest.coffee
+++ tests/spacejamTest.coffee
@@ -4,14 +4,9 @@
 expect = require("chai").expect

 describe "SpaceJam Test", ->
-  @timeout 30000
+  @timeout 40000

   spacejamChild = new ChildProcess()
-
-#  before ->
-#    delete process.env.PORT
-#    delete process.env.ROOT_URL
-#    delete process.env.MONGO_URL

   afterEach ->
@@ -29,7 +24,7 @@
       "--settings"
       "settings.json"
       "--timeout"
-      "10000"
+      "30000"
       "--port"
       testPort
       "--log-level"
@@ -53,7 +48,7 @@
       "http://localhost:#{testPort}/"
       "failure"
       "--timeout"
-      "10000"
+      "30000"
       "--port"
       testPort
       "--log-level"
@@ -103,7 +98,7 @@
       "--settings"
       "settings.json"
       "--timeout"
-      "10000"
+      "30000"
       "--port"
       testPort
       "--log-level"
@@ -130,7 +125,7 @@
       "--settings"
       "settings.json"
       "--timeout"
-      "10000"
+      "30000"
       "--port"
       testPort
       "--log-level"


Added as and where as keywords.

Added as and where as keywords.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/pyfisch/atom-language-rust.git/'


Update de-DE.coffee

Update de-DE.coffee

--- app/locale/de-DE.coffee
+++ app/locale/de-DE.coffee
@@ -275,7 +275,7 @@
     ask_teacher_1: "Frage deinen Lehrer nach deinem Klassencode."
     ask_teacher_2: "In keiner Klasse? Erstelle ein stattdessen "
     ask_teacher_3: "Einzelkonto"
-    ask_teacher_4: " anstatt."
+    ask_teacher_4: "."
     about_to_join: "Du trittst bei:"
     enter_parent_email: "Gib die E-Mail-Adresse deiner Eltern ein:"
     parent_email_error: "Beim Verschicken der E-Mail ist etwas schiefgelaufen. Überprüfe die E-Mail-Adresse und versuche es erneut."
@@ -288,18 +288,19 @@
     sso_connected: "Erfolgreich verbunden mit:"
     select_your_starting_hero: "Wähle deinen Anfangshelden:"
     you_can_always_change_your_hero_later: "Du kannst ihn später jederzeit wechseln."
-#    finish: "Finish"
-#    teacher_ready_to_create_class: "You're ready to create your first class!"
-#    teacher_students_can_start_now: "Your students will be able to start playing the first course, Introduction to Computer Science, immediately."
-#    teacher_list_create_class: "On the next screen you will be able to create a new class."
-#    teacher_list_add_students: "Add students to the class by clicking the View Class link, then sending your students the Class Code or URL. You can also invite them via email if they have email addresses."
-#    teacher_list_resource_hub_1: "Check out the"
-#    teacher_list_resource_hub_2: "Course Guides"
-#    teacher_list_resource_hub_3: "for solutions to every level, and the"
-#    teacher_list_resource_hub_4: "Resource Hub"
-#    teacher_list_resource_hub_5: "for curriculum guides, activities, and more!"
-#    teacher_additional_questions: "That’s it! If you need additional help or have questions, reach out to __supportEmail__."
-#    dont_use_our_email_silly: "Don't put our email here! Put your parent's email."
+    finish: "Abschließen"
+    teacher_ready_to_create_class:  "Erstelle jetzt Deine erste Klasse!" #"You're ready to create your first class!"
+    teacher_students_can_start_now: "Gleich können Deine Schüler mit dem ersten Kurs, Einführung in die Computerwissenschaft, beginnen."
+    teacher_list_create_class: "Auf der nächsten Seite kannst Du eine neue Klasse erstellen."
+    teacher_list_add_students: "Füge Schüler zur Klassen hinzu, indem du auf Klasse anzeigen klickst und ihnen den Klassencode oder die URL schickst. Du kannst auch Schüler via E-Mail einladen, wenn Sie E-Mail-Adressen haben."
+    teacher_list_resource_hub_1: "Beachte auch"
+                                 "Check out the"
+    teacher_list_resource_hub_2: "Kursanleitungen"
+    teacher_list_resource_hub_3: "mit Lösungen für alle Level und den"
+    teacher_list_resource_hub_4: "Resource Hub"
+    teacher_list_resource_hub_5: "for Curriculumleitfäden, Aktivitäten und mehr!"
+    teacher_additional_questions: "Fertig! Wenn Du weitere Hilfe benötigst oder Fragen hast, wende Dich an __supportEmail__."
+    dont_use_our_email_silly: "Bitte gib die E-Mail Deiner Eltern an, nicht unsere!"

   recover:
     recover_account_title: "Account-Wiederherstellung"
@@ -318,7 +319,7 @@
     back: "Zurück" # When used as an action verb, like "Navigate backward"
     coming_soon: "Demnächst!"
     continue: "Weiter" # When used as an action verb, like "Continue forward"
-#    next: "Next"
+    next: "Weiter"
     default_code: "Standardcode"
     loading: "Lade..."
     overview: "Übersicht"
@@ -343,8 +344,8 @@
     submit_patch: "Patch einreichen"
     submit_changes: "Änderungen einreichen"
     save_changes: "Änderungen speichern"
-    required_field: "Benötigte Felder" # {change}
-#    valid_phone: "Enter a valid phone number."
+    required_field: "Pflichtfeld" #war benötigte Felder
+    valid_phone: "Gib eine gültige Telefonnummer an."

   general:
     and: "und"
@@ -697,7 +698,7 @@
     kids_message_1: "Kids! Wir senden Euren Eltern eine E-Mail, damit Sie das Abonnement für Euch bestellen können"
     kids_message_2: "Frag Deine Eltern"
     support_part1: "Brauchst Du Hilfe bei den Zahlungsoptionen? Schicke eine E-Mail an"
-#    support_part2: "support@codecombat.com"
+    support_part2: "support@codecombat.com"
     support_part3: "wenn Du Fragen hast."

   announcement:
@@ -990,10 +991,10 @@
     approve_funds: "Zahlung genehmigen"
     no_purchaser_role: "Keine Rolle bei Kaufentscheidungen"
     district_label: "Bezirk"
-#    district_name: "District Name"
+    district_name: "Bezirksname"
     district_na: "Falls nicht zutreffend, gib n. v. ein"
-    organization_label: "Name der Schule/Bezirk" # {change}
-#    school_name: "School Name"
+    organization_label: "Schule" # {change}
+    school_name: "Schulname"
     city: "Stadt"
     state: "Bundesland"
     country: "Land"
@@ -1002,14 +1003,14 @@
     education_level_label: "Bildungslevel der Schüler"
     education_level_help: "Wählen Sie so viele wie zutreffen."
     elementary_school: "Grundschule"
-    high_school: "Gymnasium"
+    high_school: "Sekundarstufe II"
     please_explain: "(bitte erklären)"
-    middle_school: "Mittelschule"
+    middle_school: "Sekundarstufe I"
     college_plus: "Hochschule oder höher"
     referrer: "Wie hast du von uns erfahren?"
     referrer_help: "zum Beispiel: von einem anderen Lehere, auf einer Konferenz, von deinen Schülern, Code.org, etc."
     anything_else: "Weitere Dinge die wir wissen sollten?"
-    thanks_header: "Danke dass Sie eine Demo beantragen!" # {change}
+    thanks_header: "Anforderung erhalten!"
     thanks_sub_header: "Vielen Dank für dein Interesse an CodeCombat für deine Schule."
     thanks_p: "Wir melden uns bald. Fragen? Schreiben Sie uns eine Email:" # {change}
     back_to_classes: "Zurück zu den Klassen"


Add mailhook script using cloudmailin.com

error: pathspec 'scripts/mailin.coffee' did not match any file(s) known to git

--- scripts/mailin.coffee
+++ scripts/mailin.coffee
@@ -0,0 +1,134 @@
+# Description:
+#   Mailin hooks
+#
+# Configuration:
+#   HUBOT_TOKEN - hubot API Token
+#
+# Dependencies:
+#   Slack Adapter - This script is for Slack
+#   cloudmailin.com - This script has a route for mailin
+
+cheerio = require "cheerio"
+fs = require "fs"
+request = require "request"
+
+module.exports = (robot) ->
+
+  robot.router.post "/hubot/mailhook", (req, res) ->
+    query = req.query
+    data = req.body
+    unless process.env.HUBOT_TOKEN? and process.env.HUBOT_TOKEN in [query.token, body.token]
+      res.writeHead 403
+      res.end "NG"
+      return
+
+    from = data.envelope?.from
+
+    switch from
+      when "notifications@disqus.net", "customer@hokuken.com"
+        parser = new DisqusMailParser data
+        # for Slack
+        [message, data] = parser.createCustomMessage()
+        console.log "Disqus mail parser catched"
+        post message, data
+      else
+        res.writeHead 404
+        res.end "Parser Not Found"
+        return
+
+    res.end "OK"
+
+  post = (message, attachments...) ->
+    robot.logger.info "Post to hubot hook via mailin script"
+    path = "/services/hooks/hubot"
+    data =
+      username: robot.name
+      channel: message.room
+      attachments: attachments
+
+    robot.adapter.post? path, {form: {payload: JSON.stringify data}}
+
+  class MailParser
+    constructor: (data) ->
+      @data = data
+      @parse()
+
+    subject: ->
+      @data.headers?.Subject
+
+    parse: ->
+
+    # Create Data Object for Custom message of slack
+    # @return [message, data]
+    #   message: {room}
+    #   data: {text, fallback, pretext, color, fields}
+    createCustomMessage: ->
+
+
+  class DisqusMailParser extends MailParser
+
+    parse: ->
+      html = @data.html
+      $ = cheerio.load html
+      parser = @
+
+      # Get the Disqus shortname and the link to page
+      $("h3").first().children("a").each ->
+        parser.shortname = $(this).text()
+        parser.url = $(this).attr "href"
+
+      # Get the user data
+      $("a > img[title$=profile]").each ->
+        title = $(this).attr "title"
+        username = title.match(/^Visit (.+)'s profile/i)[1]
+        parser.user = {
+          name: username
+          avatar: $(this).attr "src"
+          profile: $(this).parent().attr "href"
+        }
+
+      # Get the post content
+      $("h4").first().next("p").each ->
+        $contents = $(this).nextUntil("table")
+        parser.datetime = $contents.last().text()
+        parser.contents = $contents.map((i, p) ->
+            $(p).text()
+          ).get().join "\n"
+        parser.digest = parser.contents.replace(/\n/g, " ").substr 0, 40
+
+    toObject: ->
+      {
+        subject: @subject()
+        from: @data.envelope.from
+        shortname: @shortname
+        url: @url
+        user: @user
+        datetime: @datetime
+        contents: @contents
+        digest: @digest
+      }
+
+    createCustomMessage: ->
+      [
+        {
+          room: "#hubot"
+        }
+        {
+          pretext: "*#{@subject()}* : <#{@url}|view comment>"
+          fallback: "*#{@subject()}* : <#{@url}|view comment>"
+          color: "#26c281"
+          fields: [
+            {
+              title: "User"
+              value: "Posted by <#{@user.profile}|#{@user.name}>"
+              short: false
+            },
+            {
+              title: "Comment"
+              value: @contents
+              short: true
+            }
+          ]
+          mrkdwn_in: ["pretext", "fallback"]
+        }
+      ]


q object.

q object.

error: pathspec 'js/q/q.coffee' did not match any file(s) known to git

--- js/q/q.coffee
+++ js/q/q.coffee
@@ -0,0 +1,254 @@
+# Author : Jeeyoung Kim
+# Implementing angularjs promises, for fun and profit.
+
+Q = {
+  defer: () -> new Defer()
+  fail: (value) ->
+    d = Q.defer()
+    d.fail(value)
+    d.promise
+
+  all: (promises) ->
+    # from an array of promises, returns a
+    count = promises.length
+    defer = Q.defer()
+    if count == 0
+      # short circuit.
+      defer.success([])
+      return defer.promise
+    successes = (null for promise of promises)
+    failures =  (null for promise of promises)
+    hasFailure = false
+    decrement = () ->
+      count--
+      if count == 0
+        if hasFailure
+          defer.failure(failures)
+        else
+          defer.success(successes)
+    for index, promise of promises
+      do (index) ->
+        promise.then (s, f) ->
+          successes[index] = s
+          failures[index] = f
+          if f isnt null
+            hasFailure = true
+          decrement()
+    return defer.promise
+}
+
+STATE_NEW = 0
+STATE_SUCCESS = 1
+STATE_FAIL = 2
+
+class Defer
+  constructor: () ->
+    @queued = []
+    @value = null
+    @state = STATE_NEW
+    @promise = {
+      then: (async) =>
+        defer = new Defer()
+        wrapped = (success, failure) =>
+          try
+            successValue = async(success, failure)
+          catch e
+            failValue = e
+
+          if successValue? and successValue.is_promise
+            # chaining the promise.
+            successValue.then (s, f) ->
+              if f isnt null
+                defer.fail f
+              else
+                defer.success s
+          else
+            # non-promise value.
+            if failValue?
+              defer.fail(failValue)
+            else
+              defer.success(successValue)
+
+        if @state is STATE_NEW
+          @queued.push wrapped
+        else
+          @triggerOne wrapped
+        return defer.promise
+      is_promise: true
+    }
+
+
+  success: (value) ->
+    # mark the given promise as success.
+    if (operate = (@state is STATE_NEW))
+      @value = value
+      @state = STATE_SUCCESS
+      @triggerAll()
+    return operate
+
+  fail: (value) ->
+    # fail the given promise.
+    if (operate = (@state is STATE_NEW))
+      @value = value
+      @state = STATE_FAIL
+      @triggerAll()
+    return operate
+
+  triggerOne: (queue) ->
+    if @state is STATE_SUCCESS
+      queue(@value, null) # success mode.
+    else
+      queue(null, @value) # fail mode.
+
+  triggerAll: =>
+    for idx, queue of @queued
+      @triggerOne(queue)
+
+
+test = ->
+  testall_empty()
+  testall_normal()
+  testchain()
+  testchain_failures()
+  testduplicate()
+  testfan()
+  testhello()
+  testhello_fail()
+  testoutoforder()
+  testreturnpromise()
+
+assert = (value) =>
+  unless value
+    console.error 'failure'
+
+testhello = ->
+  # Hello to the world of promises.
+  run = false
+  d = Q.defer()
+  d.promise.then (s, f) => (assert s is 'world'; run = true)
+  d.success('world')
+  assert run
+
+testhello_fail = ->
+  # test failure methods.
+  # defer.fail - resolving as failure.
+  # throwing - returing exception as failure.
+  # failed promises
+  run = false
+  d = Q.defer()
+  d.promise
+    .then (s, f) => (assert f is 'hello'; throw 'world')
+    .then (s, f) => (assert f is 'world'; Q.fail('everybody'))
+    .then (s, f) => (assert f is 'everybody'; run = true)
+  d.fail 'hello'
+  assert run
+
+testchain = ->
+  # test interaction of chained promises.
+  run = false
+  d = Q.defer()
+  d.promise.then((s, f) => s + 1)
+   .then((s, f) => s * 2)
+   .then((s, f) =>
+     assert(s == 12)
+     run = true
+   )
+  d.success(5)
+  assert(run)
+
+testchain_failures = ->
+  # testing chain of mixed failure & success.
+  run = false
+  d = Q.defer()
+  d.promise
+    .then((s, f) => Q.fail s + 1)
+    .then((s, f) => f + 1)
+    .then((s, f) => throw s + 1)
+    .then((s, f) =>
+      assert f == 4
+      run = true
+    )
+  d.success(1)
+  assert run
+
+testoutoforder = ->
+  # resolve the defer before chaining promise. chained promise should resolve.
+  run = false
+  d = Q.defer()
+  p = d.promise
+  d.success(1)
+  p.then((s, f) => run = true)
+  assert(run)
+
+testreturnpromise = ->
+  # promise retuns promise.
+  run = false
+  start = Q.defer()
+  response = Q.defer()
+  chained = start.promise.then (s, f) ->
+    return unless s == 10
+    return response.promise
+  chained.then (s, f) =>
+    if s == 12
+      run = true
+  start.success 10
+  assert(not run)
+  response.success 12
+  assert(run)
+
+testfan = ->
+  # test the fanout.
+  d = Q.defer()
+  p = d.promise
+  count = 5
+  decrement = -> count--
+  p.then((s, f) => decrement())
+  p.then((s, f) => decrement())
+  p.then((s, f) => decrement())
+  p.then((s, f) => decrement())
+  p.then((s, f) => decrement())
+  d.success('start')
+  assert(count == 0)
+
+testduplicate = ->
+  # test the duplicate resolution.
+  d = Q.defer()
+  count = 0
+  increment = -> count++
+  d.promise.then((s, f) ->
+    assert(s == 1)
+    increment()
+  )
+  assert(count == 0)
+  assert(d.success(1))
+  assert(count == 1)
+  assert(not d.success(1))
+  assert(not d.fail(1))
+  assert(count == 1)
+
+testall_normal = ->
+  a = Q.defer()
+  b = Q.defer()
+  c = Q.defer()
+  run = false
+  Q.all([a.promise, b.promise, c.promise]).then (s, f) ->
+    assert(s[0] == 'a')
+    assert(s[1] == 'b')
+    assert(s[2] == 'c')
+    run = true
+  assert(not run)
+  a.success('a')
+  assert(not run)
+  b.success('b')
+  assert(not run)
+  c.success('c')
+  assert(run)
+
+testall_empty = ->
+  run = false
+  Q.all([]).then ->
+    run = true
+  assert(run)
+
+test()
+


mute log on testing

mute log on testing

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stevelacy/portal.git/'


Add nickname support (requires discord.js#indev)

remote: Repository unavailable due to DMCA takedown.
remote: See the takedown notice for more details:
remote:   https://github.com/github/dmca/blob/master/2018/2018-09-28-TeraPC.md.
fatal: unable to access 'https://github.com/meishuu/tera-discord-relay.git/': The requested URL returned error: 403


add invoice creation script (ugly!)

add invoice creation script (ugly!)

error: pathspec 'salvus/invoice.coffee' did not match any file(s) known to git

--- salvus/invoice.coffee
+++ salvus/invoice.coffee
@@ -0,0 +1,94 @@
+misc = require('misc')
+
+render_invoice_to_pdf = (invoice, customer, charge) ->
+    PDFDocument = require('pdfkit')
+    doc = new PDFDocument
+
+    doc.pipe(require('fs').createWriteStream('receipt.pdf'))
+
+    doc.image('static/favicon-128.png', 268, 15, {width: 64, align: 'center'})
+    y = 100
+    c1 = 100
+    if invoice.paid
+        doc.fontSize(35).text('SageMath, Inc. - Receipt', c1, y)
+    else
+        doc.fontSize(35).text('SageMath, Inc. - Invoice', c1, y)
+
+    y += 60
+    c2 = 360
+    doc.fontSize(16)
+    doc.fillColor('#555')
+    doc.text("Date", c1, y)
+    doc.text("ID")
+    doc.text("Account")
+    doc.text("Email")
+    if invoice.paid
+        doc.text("Card charged")
+
+    doc.fillColor('black')
+    doc.text(misc.stripe_date(invoice.date), c2, y)
+    doc.text(invoice.id.slice(invoice.id.length-6).toLowerCase())
+    doc.text(customer.description)
+    doc.text(customer.email)
+    if invoice.paid
+        doc.text("#{charge.source.brand} ending #{charge.source.last4}")
+
+    y += 120
+    doc.fontSize(24).text("Items", c1, y)
+
+    y += 40
+    doc.fontSize(12)
+    v = []
+    for x in invoice.lines.data
+        v.push
+            desc   : misc.trunc(x.description,60)
+            amount : "USD $#{x.amount/100}"
+
+    for i in [0...v.length]
+        if i == 0
+            doc.text("#{i+1}. #{v[i].desc}", c1, y)
+        else
+            doc.text("#{i+1}. #{v[i].desc}")
+    doc.moveDown()
+    if invoice.paid
+        doc.text("PAID")
+    else
+        doc.text("DUE")
+
+    for i in [0...v.length]
+        if i == 0
+            doc.text(v[i].amount, c2+90, y)
+        else
+            doc.text(v[i].amount)
+    doc.moveDown()
+    doc.text("USD $#{invoice.total/100}")
+
+    y += 300
+    doc.fontSize(14)
+    doc.text("Contact us with any questions by emailing billing@sagemath.com.", c1, y)
+    if not invoice.paid
+        doc.moveDown()
+        doc.text("To pay, sign into your account at https://cloud.sagemath.com and add a payment method in the billing tab under account settings.")
+    else
+        doc.text("Thank you for using https://cloud.sagemath.com.")
+
+    doc.end()
+
+exports.go = () ->
+    charge =
+        source :
+            brand : 'Visa'
+            last4 : 'xxxx'
+    customer =
+        email       : 'address@stuff'
+        description : 'John Smith'
+    invoice =
+        date : 1431729520
+        paid : true
+        id   : 'in_162...'
+        lines :
+            data : [{description:'...', amount:1000}, {description:"WA state sales tax", amount:902}]
+        total : 1902
+
+    render_invoice_to_pdf(invoice, customer, charge)
+


add clonedSum attr to config of stack template while cloning

--- workers/social/lib/social/models/computeproviders/stacktemplate.coffee
+++ workers/social/lib/social/models/computeproviders/stacktemplate.coffee
@@ -467,6 +467,7 @@

       cloneData.config           ?= {}
       cloneData.config.clonedFrom = @getId()
+      cloneData.config.clonedSum = @getAt 'template.sum'

       { custom } = cloneData.credentials or {}
       custom    ?= []


Made the ladder tab better able to handle unloaded gplus and facebook apis.

--- app/views/play/ladder/ladder_tab.coffee
+++ app/views/play/ladder/ladder_tab.coffee
@@ -35,10 +35,10 @@
     @teams = teamDataFromLevel @level
     @leaderboards = {}
     @refreshLadder()
-    @checkFriends() if window.FB and window.gapi
+    @checkFriends()

   checkFriends: ->
-    return if @checked
+    return if @checked or (not window.FB) or (not window.gapi)
     @checked = true

     @loadingFacebookFriends = true


add Dinner command

add Dinner command

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/tachikomapocket/Tomohiro-_-Tomochan.git/'


Add script for resume channel

Add script for resume channel

error: pathspec 'scripts/resume.coffee' did not match any file(s) known to git

--- scripts/resume.coffee
+++ scripts/resume.coffee
@@ -0,0 +1,4 @@
+module.exports = (robot) ->
+  names = ['Dana', 'Freia', 'Jhishan', 'Kyle', 'Brenton', 'Terri', 'Abhi', 'Drew']
+  robot.hear /Resume queen, who is next?/i, (res) ->
+    res.send res.random names


require NODE_ENV

require NODE_ENV

--- apps/artist_2/routes.coffee
+++ apps/artist_2/routes.coffee
@@ -4,7 +4,7 @@
 request = require 'superagent'
 Backbone = require 'backbone'
 ReferrerParser = require 'referer-parser'
-{ APPLICATION_NAME } = require '../../config'
+{ APPLICATION_NAME, NODE_ENV } = require '../../config'
 cache = require '../../lib/cache'
 Artist = require '../../models/artist'
 Nav = require './nav'


exclude promo auctions

exclude promo auctions

--- apps/auctions/routes.coffee
+++ apps/auctions/routes.coffee
@@ -40,10 +40,12 @@
     -(Date.parse auction.get('end_at'))

   auctions.fetch
-    data: size: 20, sort: '-created_at'
+    cache: true
+    data: published: true, size: 20, sort: '-created_at'
     success: (collection, response, options) ->
       [preview, open, closed] = _.map ['preview', 'open', 'closed'], (state) ->
-        auctions.where auction_state: state
+        auctions.select (auction) ->
+          auction.get('auction_state') is state and !auction.isAuctionPromo()

       promo = auctions.select (auction) -> auction.isAuctionPromo()


Update cities sitemap to use featured cities json

--- apps/sitemaps/routes.coffee
+++ apps/sitemaps/routes.coffee
@@ -4,7 +4,7 @@
 request = require 'superagent'
 moment = require 'moment'
 async = require 'async'
-{ Cities } = require 'places'
+PartnerFeaturedCities = require '../../collections/partner_featured_cities'
 { NODE_ENV, API_URL, POSITRON_URL, FUSION_URL, APP_URL, ARTSY_EDITORIAL_CHANNEL } = require('sharify').data
 artsyXapp = require 'artsy-xapp'
 PAGE_SIZE = 100
@@ -96,8 +96,13 @@
   res.render('misc', pretty: true)

 @cities = (req, res, next) ->
-  res.set 'Content-Type', 'text/xml'
-  res.render('cities', pretty: true, citySlugs: _.pluck(Cities, 'slug'))
+  console.log 'cities'
+  partnerFeaturedCities = new PartnerFeaturedCities()
+  partnerFeaturedCities.fetch
+    success: ->
+      console.log 'partnerFeaturedCities', partnerFeaturedCities.models
+      res.set 'Content-Type', 'text/xml'
+      res.render 'cities', pretty: true, citySlugs: partnerFeaturedCities.pluck('slug')

 @artworksPage = (template) -> (req, res, next) ->
   request


Add all category to every user

Add all category to every user

[#100417500]

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/netguru/people.git/'


Clean up formatting

Clean up formatting

--- models/location.coffee
+++ models/location.coffee
@@ -1,9 +1,8 @@
-_                              = require 'underscore'
-Backbone                       = require 'backbone'
-{ compactObject } = require './mixins/compact_object.coffee'
+_                   = require 'underscore'
+Backbone            = require 'backbone'
+{ compactObject }   = require './mixins/compact_object.coffee'

 module.exports = class Location extends Backbone.Model
-
   defaults: ->
     raw: ''
     address: ''
@@ -18,37 +17,37 @@

   lines: ->
     _.compact([
-      @get 'address' || ''
-      @get 'address_2' || ''
-      @cityStatePostalCode() || ''
-      @get 'country' || ''
+      @get 'address' or ''
+      @get 'address_2' or ''
+      @cityStatePostalCode() or ''
+      @get 'country' or ''
     ])

   cityState: ->
     _.compact([
-      @get 'city' || ''
-      @get 'state' || ''
+      @get 'city' or ''
+      @get 'state' or ''
     ]).join(', ')

   cityStateCountry: ->
     _.compact([
-      @get 'city' || ''
-      @get 'state' || ''
-      @get 'country' || ''
+      @get 'city' or ''
+      @get 'state' or ''
+      @get 'country' or ''
     ]).join(', ')

   cityStatePostalCode: ->
     _.compact([
-      @cityState() || ''
-      @get('postal_code') || ''
+      @cityState() or ''
+      @get('postal_code') or ''
     ]).join(' ')

   singleLine: ->
     _.compact([
-      @get 'city' || ''
+      @get 'city' or ''
       _.compact([
-        @get 'address' || ''
-        @get 'address_2' || ''
+        @get 'address' or ''
+        @get 'address_2' or ''
       ]).join(' ')
     ]).join(', ')

@@ -56,9 +55,11 @@
     telephone = "Tel: #{@get('phone')}" if @get('phone')
     _.compact(_.flatten([@lines(), telephone])).join '<br/>'

-  displayAddress: -> if @lines() then @lines().join(", ") else ""
+  displayAddress: ->
+    if @lines() then @lines().join(", ") else ""

-  displayName: -> if @has("display") then @get("display") else @get("name")
+  displayName: ->
+    if @has("display") then @get("display") else @get("name")

   getMapsLocation: ->
     if @get('coordinates')
@@ -67,7 +68,7 @@
       @displayAddress()

   toJSONLD: ->
-    address = [@get('address') || '', @get('address_2') || ''].join('')
+    address = [@get('address') or '', @get('address_2') or ''].join('')
     compactObject {
       "@type": "Place"
       name: @get('name')


Write dummy states for articles and people

error: pathspec 'app/assets/javascripts/hyper_admin/angularjs/states.js.coffee' did not match any file(s) known to git

--- app/assets/javascripts/hyper_admin/angularjs/states.js.coffee
+++ app/assets/javascripts/hyper_admin/angularjs/states.js.coffee
@@ -0,0 +1,36 @@
+angular.module("hyperadmin")
+  .config ($locationProvider, $stateProvider, $urlRouterProvider) ->
+    $locationProvider.html5Mode true
+
+    $stateProvider
+      .state "list_articles",
+        url: "/admin/articles"
+        templateUrl: "/admin/articles.html"
+      .state "new_article",
+        url: "/admin/articles/new"
+        templateUrl: "/admin/articles/new.html"
+      .state "show_article",
+        url: "/admin/articles/:id"
+        templateUrl: (params) ->
+          "/admin/articles/#{params.id}.html"
+      .state "edit_article",
+        url: "/admin/articles/:id/edit"
+        templateUrl: (params) ->
+          "/admin/articles/#{params.id}/edit.html"
+
+      .state "list_people",
+        url: "/admin/people"
+        templateUrl: "/admin/people.html"
+      .state "new_person",
+        url: "/admin/people/new"
+        templateUrl: "/admin/people/new.html"
+      .state "show_person",
+        url: "/admin/people/:id"
+        templateUrl: (params) ->
+          "/admin/people/#{params.id}.html"
+      .state "edit_person",
+        url: "/admin/people/:id/edit"
+        templateUrl: (params) ->
+          "/admin/people/#{params.id}/edit.html"
+
+    $urlRouterProvider.otherwise "/"


testing uglifier gem source map creation

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jrjamespdx/prc-119.git/'


better method signature for external calculation fn substitution

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/alienfast/bootstrap-password.git/'


updating local server

updating local server

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/stevelacy/rialu-node-msi.git/'


Only check deprecation flag for callNow usage

--- src/config.coffee
+++ src/config.coffee
@@ -377,10 +377,10 @@
         Pass a `scope` in an options hash as the third argument instead.
       """
       [scopeDescriptor, keyPath, callback] = arguments
-    else if Grim.includeDeprecations and arguments.length is 3 and (_.isString(arguments[0]) and _.isObject(arguments[1]))
+    else if arguments.length is 3 and (_.isString(arguments[0]) and _.isObject(arguments[1]))
       [keyPath, options, callback] = arguments
       scopeDescriptor = options.scope
-      if options.callNow?
+      if Grim.includeDeprecations and options.callNow?
         Grim.deprecate """
           Config::observe no longer takes a `callNow` option. Use ::onDidChange instead.
           Note that ::onDidChange passes its callback different arguments.


remove duplicate switch case

remove duplicate switch case

--- src/cursor.coffee
+++ src/cursor.coffee
@@ -94,8 +94,6 @@
         Grim.deprecate("Use Cursor::onDidChangePosition instead")
       when 'destroyed'
         Grim.deprecate("Use Cursor::onDidDestroy instead")
-      when 'destroyed'
-        Grim.deprecate("Use Cursor::onDidDestroy instead")
       else
         Grim.deprecate("::on is no longer supported. Use the event subscription methods instead")
     super


Fix for handling more than 10 cids

Fix for handling more than 10 cids

--- src/spine.coffee
+++ src/spine.coffee
@@ -227,10 +227,11 @@
   @idCounter: 0

   @resetIdCounter: ->
-    ids        = (model.cid for model in @all()).sort((a, b) -> a > b)
+    parseCID = (cid) ->
+      cid     = cid?.replace?(/^c-/, '') or cid
+      parseInt(cid, 10)
+    ids        = (parseCID(model.cid) for model in @all()).sort((a, b) -> a > b)
     lastID     = ids[ids.length - 1]
-    lastID     = lastID?.replace?(/^c-/, '') or lastID
-    lastID     = parseInt(lastID, 10)
     @idCounter = (lastID + 1) or 0

   @uid: (prefix = '') ->


Added missing file

Added missing file

error: pathspec 'lib/ot.coffee' did not match any file(s) known to git

--- lib/ot.coffee
+++ lib/ot.coffee
@@ -0,0 +1,69 @@
+# This contains the master OT functions for the database. They look like ot-types style operational transform
+# functions, but they're a bit different. These functions understand versions and can deal with out of bound
+# create & delete operations.
+
+otTypes = require 'ot-types'
+
+exports.normalize = (opData) ->
+  if opData.create
+    # We should store the full URI of the type, not just its short name
+    opData.create.type = otTypes[opData.create.type].uri
+
+# This is the super apply function that takes in snapshot data (including the type) and edits it in-place.
+# Returns an error string or null for success.
+exports.apply = (data, opData) ->
+  return 'Missing data' unless typeof opData is 'object'
+  return 'Missing op' unless typeof (opData.op or opData.create) is 'object' or opData.del is true
+
+  if !data.type
+    # The document doesn't exist, although it might have once existed. Here we will only allow creates.
+    create = opData.create
+    return 'Document does not exist' unless typeof create is 'object'
+
+    type = otTypes[create.type]
+    return "Type not found" unless type
+
+    snapshot = type.create create.data
+
+    data.data = snapshot
+    data.type = type.uri
+    data.v++
+
+  else if opData.del
+    delete data.data
+    delete data.type
+    data.v++
+
+  else
+    # Apply the op data
+    op = opData.op
+    return 'Missing op' unless typeof op is 'object'
+    type = otTypes[data.type]
+    return 'Type not found' unless type
+
+    try
+      data.data = type.apply data.data, op
+    catch e
+      console.log e.stack
+      return e.message
+    data.v++
+
+  return
+
+exports.transform = (type, opData, appliedOpData) ->
+  return 'Document was deleted' if appliedOpData.del
+
+  return 'Document created remotely' if appliedOpData.create # type will be null / undefined in this case.
+
+  return 'Document does not exist' unless type
+
+  if typeof type is 'string'
+    type = otTypes[type]
+    return "Type not found" unless type
+
+  opData.op = type.transform opData.op, appliedOpData.op, 'left'
+  opData.v++
+
+  return
+
+


Client: Activity: Feed: Added SinglePublicChannelRecentMessages route

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/andrewjcasal/koding.git/'


fixed font.sizes working with textwrap

fixed font.sizes working with textwrap

--- src/font/sizes.coffee
+++ src/font/sizes.coffee
@@ -3,16 +3,16 @@
 module.exports = (words, style, opts) ->

   opts   = {} unless opts
-  tester = opts.parent or fontTester("svg").append("text")
+  tester = opts.parent or fontTester("svg").append("g")
   style  = style or {}
   sizes  = []
   words  = [ words ] unless words instanceof Array
-  tspans = tester.selectAll("tspan").data(words)
+  tspans = tester.selectAll("text").data(words)
   attr   =
     x: 0
     y: 0

-  tspans.enter().append("tspan")
+  tspans.enter().append("text")
     .text String
     .attr "position", "absolute"
     .attr "top", "0px"
@@ -21,17 +21,17 @@
     .attr attr
     .each (d) ->
       opts.mod this if typeof opts.mod is "function"
+    .each (d) ->
+
       children = d3.select(this).selectAll("tspan")
       if children.size()
-        width = []
-        children.each ->
-          width.push @getComputedTextLength()
+        width  = []
+        children.each -> width.push getWidth(this)
         width = d3.max width
       else
-        width = @getComputedTextLength()
-      height = @offsetHeight or
-               @getBoundingClientRect().height or
-               @.parentNode.getBBox().height
+        width = getWidth(this)
+      height = getHeight(this)
+
       sizes.push
         height: height
         text:   d
@@ -40,3 +40,10 @@
   tspans.remove()
   tester.remove() unless opts.parent
   sizes
+
+getWidth = (elem) ->
+  elem.getComputedTextLength()
+getHeight = (elem) ->
+  elem.offsetHeight or
+  elem.getBoundingClientRect().height or
+  elem.parentNode.getBBox().height


log.info instead of console.info for track uploaded msg

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Qbuer/groovebasin.git/'


bug fix

bug fix

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/bubblegroup/bubblebot.git/'


Don't write / update snapshot when the "quite" mode is on


:art: Rename to `shouldUpdateLinesState`

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -75,7 +75,7 @@
     @updateHiddenInputState() if @shouldUpdateHiddenInputState
     @updateContentState() if @shouldUpdateContentState
     @updateDecorations() if @shouldUpdateDecorations
-    @updateTilesState() if @shouldUpdateTilesState
+    @updateTilesState() if @shouldUpdateLinesState
     @updateCursorsState() if @shouldUpdateCursorsState
     @updateOverlaysState() if @shouldUpdateOverlaysState
     @updateLineNumberGutterState() if @shouldUpdateLineNumberGutterState
@@ -97,7 +97,7 @@
     @shouldUpdateHiddenInputState = false
     @shouldUpdateContentState = false
     @shouldUpdateDecorations = false
-    @shouldUpdateTilesState = false
+    @shouldUpdateLinesState = false
     @shouldUpdateCursorsState = false
     @shouldUpdateOverlaysState = false
     @shouldUpdateLineNumberGutterState = false
@@ -116,7 +116,7 @@
       @shouldUpdateContentState = true
       @shouldUpdateDecorations = true
       @shouldUpdateCursorsState = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateLineNumberGutterState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateGutterOrderState = true
@@ -132,7 +132,7 @@
       @shouldUpdateScrollbarsState = true
       @shouldUpdateContentState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateLineNumberGutterState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateGutterOrderState = true
@@ -220,6 +220,8 @@
     @updateState()

   updateState: ->
+    @shouldUpdateLinesState = true
+
     @updateContentDimensions()
     @updateScrollbarDimensions()
     @updateStartRow()
@@ -324,7 +326,7 @@
       tile.height = @tileSize * @lineHeight
       tile.display = "block"

-      @updateLinesState(tile, startRow, endRow)
+      @updateLinesState(tile, startRow, endRow) if @shouldUpdateLinesState

       visibleTiles[startRow] = true

@@ -782,7 +784,7 @@
       @shouldUpdateVerticalScrollState = true
       @shouldUpdateHiddenInputState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateCursorsState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateCustomGutterDecorationState = true
@@ -805,7 +807,7 @@
     @state.content.scrollingVertically = false
     if @mouseWheelScreenRow?
       @mouseWheelScreenRow = null
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateCustomGutterDecorationState = true

@@ -822,7 +824,7 @@
       @shouldUpdateCursorsState = true
       @shouldUpdateOverlaysState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true

       @emitDidUpdateState()

@@ -870,7 +872,7 @@
       @shouldUpdateVerticalScrollState = true
       @shouldUpdateScrollbarsState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateCursorsState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateCustomGutterDecorationState = true
@@ -898,7 +900,7 @@
       @shouldUpdateScrollbarsState = true
       @shouldUpdateContentState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateCursorsState = true unless oldContentFrameWidth?

       @emitDidUpdateState()
@@ -964,7 +966,7 @@
       @shouldUpdateScrollbarsState = true
       @shouldUpdateHiddenInputState = true
       @shouldUpdateDecorations = true
-      @shouldUpdateTilesState = true
+      @shouldUpdateLinesState = true
       @shouldUpdateCursorsState = true
       @shouldUpdateLineNumbersState = true
       @shouldUpdateCustomGutterDecorationState = true
@@ -1016,7 +1018,7 @@
     @shouldUpdateHiddenInputState = true
     @shouldUpdateContentState = true
     @shouldUpdateDecorations = true
-    @shouldUpdateTilesState = true
+    @shouldUpdateLinesState = true
     @shouldUpdateCursorsState = true
     @shouldUpdateOverlaysState = true

@@ -1114,7 +1116,8 @@
         intersectsVisibleRowRange = true

       if intersectsVisibleRowRange
-        @shouldUpdateTilesState = true if decoration.isType('line')
+        if decoration.isType('line')
+          @shouldUpdateLinesState = true
         if decoration.isType('line-number')
           @shouldUpdateLineNumbersState = true
         else if decoration.isType('gutter')
@@ -1139,7 +1142,7 @@
         decoration.getMarker().getScreenRange())
       @addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
       if decoration.isType('line') or Decoration.isType(oldProperties, 'line')
-        @shouldUpdateTilesState = true
+        @shouldUpdateLinesState = true
       if decoration.isType('line-number') or Decoration.isType(oldProperties, 'line-number')
         @shouldUpdateLineNumbersState = true
       if (decoration.isType('gutter') and not decoration.isType('line-number')) or
@@ -1155,7 +1158,8 @@
   didDestroyDecoration: (decoration) ->
     if decoration.isType('line') or decoration.isType('gutter')
       @removeFromLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
-      @shouldUpdateTilesState = true if decoration.isType('line')
+      if decoration.isType('line')
+        @shouldUpdateLinesState = true
       if decoration.isType('line-number')
         @shouldUpdateLineNumbersState = true
       else if decoration.isType('gutter')
@@ -1180,7 +1184,8 @@

     if decoration.isType('line') or decoration.isType('gutter')
       @addToLineDecorationCaches(decoration, decoration.getMarker().getScreenRange())
-      @shouldUpdateTilesState = true if decoration.isType('line')
+      if decoration.isType('line')
+        @shouldUpdateLinesState = true
       if decoration.isType('line-number')
         @shouldUpdateLineNumbersState = true
       else if decoration.isType('gutter')


[Gutter] Kill old (now unused) state hanging off @state.gutters

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -210,9 +210,6 @@
         overlays: {}
       gutters:
         sortedDescriptions: []
-        customDecorations: {}
-        lineNumberGutter:
-          lineNumbers: {}
     # Shared state that is copied into ``@state.gutters`.
     @sharedGutterStyles = {}
     @customGutterDecorations = {}
@@ -257,12 +254,10 @@

   updateVerticalScrollState: ->
     @state.content.scrollHeight = @scrollHeight
-    @state.gutters.scrollHeight = @scrollHeight # TODO jssln Remove
     @sharedGutterStyles.scrollHeight = @scrollHeight
     @state.verticalScrollbar.scrollHeight = @scrollHeight

     @state.content.scrollTop = @scrollTop
-    @state.gutters.scrollTop = @scrollTop # TODO jssln Remove
     @sharedGutterStyles.scrollTop = @scrollTop
     @state.verticalScrollbar.scrollTop = @scrollTop

@@ -425,7 +420,6 @@
       @gutterBackgroundColor
     else
       @backgroundColor
-    @state.gutters.backgroundColor = @sharedGutterStyles.backgroundColor # TODO jssln Remove

   didAddGutter: (gutter) ->
     gutterDisposables = new CompositeDisposable
@@ -466,8 +460,6 @@
         styles: @sharedGutterStyles,
         content,
       })
-    @state.gutters.customDecorations = @customGutterDecorations # TODO jssln Remove
-    @state.gutters.lineNumberGutter = @lineNumberGutter # TODO jssln Remove

   # Updates the decoration state for the gutter with the given gutterName.
   # @customGutterDecorations is an {Object}, with the form:


bug fix

bug fix


commands -> cmd

commands -> cmd

--- modules/config/index.coffee
+++ modules/config/index.coffee
@@ -22,6 +22,6 @@
       dynamicNick: false
     }

-    @commands = new Commands @
+    @cmd = new Commands @

 module.exports = ConfigModule


Remove RPM directory before rebuilding

Remove RPM directory before rebuilding

--- build/tasks/mkrpm-task.coffee
+++ build/tasks/mkrpm-task.coffee
@@ -25,7 +25,10 @@

     {name, version, description} = grunt.file.readJSON('package.json')
     buildDir = grunt.config.get('atom.buildDir')
+
     rpmDir = path.join(buildDir, 'rpm')
+    rm rpmDir
+
     installDir = grunt.config.get('atom.installDir')
     shareDir = path.join(installDir, 'share', 'atom')
     iconName = path.join(shareDir, 'resources', 'app', 'resources', 'atom.png')


Tweaking the exponential display

Tweaking the exponential display

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/robert-blankenship/web-calc.git/'