OX-250 branches are no longer deleted when merging trees

--- app/assets/javascripts/oxalis/model/route.coffee
+++ app/assets/javascripts/oxalis/model/route.coffee
@@ -431,9 +431,12 @@

     @push()

-  deleteTree : (id) ->
+  deleteTree : (id, deleteBranches) ->
     unless @activeNode?
       return
+
+    unless deleteBranches?
+      deleteBranches = true

     unless id
       id = @activeTree.treeId
@@ -447,7 +450,7 @@
     # remove comments of all nodes inside that tree
     for node in tree.nodes
       @deleteComment(node.id)
-      if node.type == TYPE_BRANCH
+      if deleteBranches and node.type == TYPE_BRANCH
         @deleteBranch(node.id)
     # Because we always want an active tree, check if we need
     # to create one.
@@ -471,7 +474,7 @@

         @trigger("mergeTree", lastTree.treeId, lastNode.pos, @activeNode.pos)

-        @deleteTree(lastTree.treeId)
+        @deleteTree(lastTree.treeId, false)

         @setActiveNode(activeNodeID)
       else


Forgot the return false.

Forgot the return false.

--- app/views/layouts/_news_ticker_display.js.coffee
+++ app/views/layouts/_news_ticker_display.js.coffee
@@ -2,4 +2,6 @@
   jQuery('#news-ticker').show()
   jQuery('#close-news-ticker-link').bind('click', ->
     jQuery('#news-ticker').hide()
-    jQuery.cookie('hide-news-ticker', '1', { expires: 7 }))
+    jQuery.cookie('hide-news-ticker', '1', { expires: 7 })
+    return false
+  )


autocomplete

autocomplete

--- app/assets/javascripts/pinkman_base/controller.coffee
+++ app/assets/javascripts/pinkman_base/controller.coffee
@@ -169,15 +169,15 @@
   # 1: User types something
   # 2: Pinkman searchs through a collection and then renders the result
   # 3: User selects a record
-  # 4: Pinkman sends the selected record to call callback
+  # 4: Pinkman sends the selected record to callback function

   # -- Parameters & Options
   # @autocomplete(attribute,options)
   # options:
     # with: [collection]
-    # pinkman collection responsible for filtering the objects
-
-    # method: [search|filter|yourCustomMethod]
+    # pinkman collection responsible for filtering/searching the results
+
+    # method: [search|yourCustomMethod]
     # Search is the default. Anythings that filters by a 'query'.
     # You can define a yourCustomMethod inside the collection model like this:
     # yourCustomMethod: (user_query, callback) ->
@@ -185,32 +185,84 @@
       # callback(this) if $p.isFunction(callback)

     # template: [template-id] (optional)
-    # Any template that has a 'select' action
-    # If you don't provide a template, Pinkman will try "attribute-autocomplete-template" by default
+    # Any template that has a 'select-attribute' action
+    # If you don't specify a template, Pinkman will try "attribute-autocomplete-template" by default

     # target: [target-id]
     # Where pinkman should yield results
-    # If you don't provide a template, Pinkman will try "attribute-autocomplete" by default
-
-    # before: before function
+    # If you don't specify a target, Pinkman will try "attribute-autocomplete" by default
+
+    # loading: loading function
     # Run something before searching (you can use it for some loading animation)

     # call: callback function.
     # What to do when user selects something?
   autocomplete: (attr_name, options) ->
+    # error handling / verifing args
     throw 'Pinkman Autocomplete: Missing options object' unless $p.isObject(options)
     throw 'Pinkman Autocomplete: Missing "with" collection' unless options.with?
-    throw 'Pinkman Autocomplete: Missing "call" callback function'
+    throw 'Pinkman Autocomplete: Missing "call" callback function' unless $p.isFunction(options.call)
+
+    # variables // user options // default options
     collection = options.with
     callback = options.call
     template = if options.template? then options.template else "#{attr_name}-autocomplete"
     target = if options.target? then options.target else "#{attr_name}-autocomplete"
     method = if options.method? then options.method else 'search'
-    @bind attr_name, (obj) ->
-      collection[method] obj[attr_name], (collection) ->
-        collection.render
-          template: template
-          target: target
+    loading = options.loading if $p.isFunction(options.loading)
+    autoHide = if options.autoHide? then options.autoHide else yes
+    autoWidth = if options.autoWidth? then options.autoWidth else yes
+    wait = if options.wait? then options.wait else 0.75
+    waitTimerName = "#{attr_name}AutocompleteTimer"
+
+    # hide initially
+    $("##{target}").hide() if autoHide
+
+    # resize autocomplete
+    if autoWidth
+      $(window).resize ->
+        $("##{target}").width($("input[name='#{attr_name}']").innerWidth() - ($("##{target}").innerWidth() - $("##{target}").width()))
+
+    # responding to user typing
+    @bind attr_name, (obj,j) ->
+
+      # loading
+      loading(obj,j,attr_name) if loading?
+
+      # removes autocompleted Class
+      j.removeClass('autocompleted')
+
+      # wait until user finish typing
+      clearTimeout(window[waitTimerName]) if window[waitTimerName]?
+      window[waitTimerName] = $p.sleep wait, =>
+
+        # calls the filter method (search)
+        collection[method] obj[attr_name], (collection) ->
+
+          # render results
+          collection.render
+            template: template
+            target: target
+            callback: ->
+              # Hide or show autocomplete
+              if autoHide
+                if obj[attr_name]? and obj[attr_name] != ''
+                  $("##{target}").fadeIn()
+                else
+                  $("##{target}").fadeOut()
+
+              # Set the autocomplete target width to same as the input
+              if autoWidth
+                $("##{target}").width($("input[name='#{attr_name}']").innerWidth() - ($("##{target}").innerWidth() - $("##{target}").width()))
+
+    # user chooses something and click
+    @action "select-#{attr_name}", 'click', (args...) =>
+      # console.log "#{@selector()} input[action='#{attr_name}']"
+      $input = $("#{@selector()} input[data-action='#{attr_name}']")
+      $input.addClass('autocompleted')
+      obj = Pinkman.get($input.data('pinkey'))
+      $("##{target}").fadeOut() if autoHide
+      callback(obj,$input,args...)


Fix Basket.setFromBuy

Fix Basket.setFromBuy

--- app/assets/javascripts/services/basket_service.coffee
+++ app/assets/javascripts/services/basket_service.coffee
@@ -45,20 +45,18 @@
     hasProducts = -> products.length > 0

     setFromBuy = (buy)->
-      search = $location.search()
+      entryToProductNameHash = (result, entry)->
+        result[entry.product.name] = entry.amount
+
+      search = _.chain($location.search())
+        .assign buyer: buy.buyer.username, basket: true
+        .assign _.reduce(buy.consistsOfProducts, entryToProductNameHash, {})
+        .value()
+
       if $location.path() == '/buy'
-        emptyBasketAndRemoveBuyer()
-        search.replace()
+        $location.search(search).replace()
       else
-        search.path('/buy')
-
-      search.buyer = buy.buyer.username
-      search.basket = "true"
-
-      for entry in buy.consistsOfProducts
-        search[entry.product.name] = entry.amount
-
-      $location.search(search)
+        $location.search(search).path('/buy')

     ##### Update basket state based on route changes #####
     resolveProducts = ->
@@ -92,6 +90,7 @@
       resolveBuyer()

     $rootScope.$on '$routeUpdate', updateStateFromSearch
+    $rootScope.$on '$routeChangeSuccess', updateStateFromSearch
     updateStateFromSearch()

     #return api-object with methods/objects accessible from outside


not en but ja

not en but ja

--- app/assets/javascripts/uploadcare/locale/ja.js.coffee
+++ app/assets/javascripts/uploadcare/locale/ja.js.coffee
@@ -106,6 +106,6 @@
 # Pluralization rules taken from:
 # http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html
 uploadcare.namespace 'uploadcare.locale.pluralize', (ns) ->
-  ns.en = (n) ->
+  ns.ja = (n) ->
     'other'


Fix discount code logic to allow people to pay. *facepalm*

--- app/assets/javascripts/backbone/views/editor_view.js.coffee
+++ app/assets/javascripts/backbone/views/editor_view.js.coffee
@@ -104,7 +104,7 @@

   check_discount_code: (code) ->
     that = @
-    if code != undefined || code != ""
+    if code != ""
       $.post("/check_discount", {code: code.toUpperCase()}).done($.proxy((data)->
         that.run_card(data)
       ), that)
@@ -113,19 +113,25 @@

   run_card: (discount) ->
     that = @
-    if $.isEmptyObject(discount) || discount.uses <= 0
-      js_flash("Sorry, your discount code was invalid.")
-    else
-      if discount.dtype == "cost"
+    no_handler = false
+    console.log(discount)
+    if discount != undefined
+      if $.isEmptyObject(discount) || discount.uses <= 0
+        js_flash("Sorry, your discount code was invalid.")
+        amount = if postcards.models[0].attributes["address[country]"] is "US" then 200 else 300
+      else if discount.dtype == "cost"
         amount = if postcards.models[0].attributes["address[country]"] is "US" then 128 else 207
       else if discount.dtype == "percent"
-        amount = if postcards.models[0].attributes["address[country]"] is "US" then 200*(discount.amount/100) else 300*(discount.amount/100)
-      else
-        amount = if postcards.models[0].attributes["address[country]"] is "US" then 200 else 300
-      if discount.dtype == "percent" && discount.amount == 100.0
-        @send_card()
-      else
-        @handler.open
-          name: "postperfect.co"
-          description: "1 postcard"
-          amount: amount+        if discount.amount == 100
+          no_handler = true
+          @send_card()
+        else
+          amount = if postcards.models[0].attributes["address[country]"] is "US" then 200*(discount.amount/100) else 300*(discount.amount/100)
+
+    else
+      amount = if postcards.models[0].attributes["address[country]"] is "US" then 200 else 300
+    if !no_handler
+      @handler.open
+        name: "postperfect.co"
+        description: "1 postcard"
+        amount: amount


Initialize global behaviors once and keep contexts more encapsulated

--- app/assets/javascripts/browse_everything/behavior.js.coffee
+++ app/assets/javascripts/browse_everything/behavior.js.coffee
@@ -1,23 +1,25 @@
 $(document).on 'page:change', ->
-  context = {}
-  active = null
+  triggers = $('*[data-toggle=browse-everything]')
+  triggers.each () -> $(this).browseEverything($(this).data())
+
+$ ->
   dialog = $('div#browse-everything')

   initialize = (obj,options) ->
     if $('div#browse-everything').length == 0
       dialog = $('

').hide().appendTo('body')
     dialog.modal({ backdrop: 'static', show: false });
-    context[obj] =
+    ctx =
       opts: $.extend(true, {}, options)
       callbacks:
         done: $.Callbacks()
         cancel: $.Callbacks()
         fail: $.Callbacks()
-    ctx = context[obj]
     ctx.callback_proxy =
       done:   (func) -> ctx.callbacks.done.add(func)   ; return this
       cancel: (func) -> ctx.callbacks.cancel.add(func) ; return this
       fail:   (func) -> ctx.callbacks.fail.add(func)   ; return this
+    $(obj).data('context',ctx)
     ctx

   toHiddenFields = (data) ->
@@ -29,19 +31,17 @@
     $(elements.toArray().join("\n"))

   $.fn.browseEverything = (options) ->
+    ctx = $(this).data('context')
     if options?
-      initialize(this[0], options)
+      ctx = initialize(this[0], options)
       $(this).click () ->
-        active = context[this]
-        dialog.load active.opts.route, () -> dialog.modal('show')
-    context[this[0]].callback_proxy
-
-  triggers = $('*[data-toggle=browse-everything]')
-  triggers.each () -> $(this).browseEverything($(this).data())
+        dialog.data('context',ctx)
+        dialog.load ctx.opts.route, () -> dialog.modal('show')
+    ctx.callback_proxy

   $(document).on 'click', 'button.ev-cancel', (event) ->
     event.preventDefault()
-    active.callbacks.cancel.fire()
+    dialog.data('context').callbacks.cancel.fire()
     $('.ev-browser').modal('hide')

   $(document).on 'click', 'button.ev-submit', (event) ->
@@ -50,17 +50,18 @@
     $('body').css('cursor','wait')
     main_form = $(this).closest('form')
     resolver_url = main_form.data('resolver')
+    ctx = dialog.data('context')
     $.ajax resolver_url,
       type: 'POST'
       dataType: 'json'
       data: main_form.serialize()
     .done (data) ->
-      if active.opts.target?
+      if ctx.opts.target?
         fields = toHiddenFields({selected_files: data})
-        $(active.opts.target).append($(fields))
-      active.callbacks.done.fire(data)
+        $(ctx.opts.target).append($(fields))
+      ctx.callbacks.done.fire(data)
     .fail (xhr,status,error) ->
-      active.callbacks.fail.fire(status, error, xhr.responseText)
+      ctx.callbacks.fail.fire(status, error, xhr.responseText)
     .always ->
       $('body').css('cursor','default')
       $('.ev-browser').modal('hide')


Improved and simplified answer submission to get triggered on a input value change, closing #292. As a side effect, fixed #296

--- app/assets/javascripts/cleantheme/surveyAnimation.js.coffee
+++ app/assets/javascripts/cleantheme/surveyAnimation.js.coffee
@@ -10,13 +10,11 @@

   # Add event listener for all changes to inputs
   $("input[type=radio]").change ->
-    alert "input changed"
     return

   # Scroll to active question
   @nextQuestionScroll = (element1, element2) ->
     # Submit Previous Question
-    submitAnswer(element1)
     if element2 is null
       return
     else
@@ -83,10 +81,15 @@

   # Submit a survey answer
-  @submitAnswer = (questionForm) ->
+  @submitAnswer = (inputElement) ->
+    questionForm = inputElement.closest("form")
     $.post(questionForm.attr("action"), questionForm.serialize(), (data) ->
       return
     , 'json')
+
+  @handleChangedValue = (inputElement) ->
+    submitAnswer(inputElement)
+

   # Respond to user clicking different questions
   $('.survey-container').click (event) ->
@@ -99,9 +102,11 @@
           if $(this).find('.multiple-question-container').length
             if $(event.target).closest(".multiple-question-container").hasClass "current"
               $(event.target).closest("label").prev("input").prop "checked", true
+              handleChangedValue($(event.target).closest("label").prev("input"))
               assignMultipleQuestion(true,false)
           else
             $(event.target).closest("label").prev("input").prop "checked", true
+            handleChangedValue($(event.target).closest("label").prev("input"))
             assignQuestion(true, false)
       else
         # Use the clicked container, rather than calling the assignQuestion function
@@ -112,6 +117,7 @@
         if $(event.target).closest("label").prev("input").is(":radio")
           event.preventDefault()
           $(event.target).closest("label").prev("input").prop "checked", true
+          handleChangedValue($(event.target).closest("label").prev("input"))
           nextQuestionScroll(newActiveQuestion, null)
         else
           if $(this).prev().length == 0
@@ -171,6 +177,7 @@
           key = $(inputs[index]).data("hotkey").charCodeAt(0)
           if e.keyCode is key
             $(inputs[index]).prop "checked", true
+            handleChangedValue($(inputs[index]))
             assignMultipleQuestion(true, false)
     else if e.keyCode is 38
       e.preventDefault()
@@ -186,9 +193,10 @@
       if $(".survey-container.active").hasClass "progress-w-letter"
         inputs = $(".survey-container.active").find("input:radio")
         inputs.each (index) ->
-          key = $(inputs[index]).data("hotkey").charCodeAt(0)
+          key = $(this).data("hotkey").toString().charCodeAt(0)
           if e.keyCode is key
             $(inputs[index]).prop "checked", true
+            handleChangedValue($(inputs[index]))
             assignQuestion(true, false)
       if $(".survey-container.active").hasClass "check-w-letter"
         unless $(".survey-container.active .panel .hidden-input").is ":focus"
@@ -198,8 +206,10 @@
             if e.keyCode is key
               if $(inputs[index]).prop "checked"
                 $(inputs[index]).prop "checked", false
+                handleChangedValue($(inputs[index]))
               else
                 $(inputs[index]).prop "checked", true
+                handleChangedValue($(inputs[index]))
               if $(inputs[index]).hasClass "reveal-next-input"
                 e.preventDefault()
                 changeFocusDirect($(this), $(this).nextAll('.hidden-input'))
@@ -207,6 +217,11 @@
   # Respond to conditional inputs - click events
   $(".reveal-next-input").click (e) ->
     changeFocusDirect($(this), $(this).nextAll('.hidden-input'))
+
+  # Attach change event handler to everything but radio button inputs. Radio button inputs are changed by JS, so each time
+  # the :checked property is changed, handleChangedValue has to be called.
+  $("input").change (event) ->
+    handleChangedValue($(event.target))

   # Custom date input


fix formula

fix formula

--- atmospheric/001-balanced-temperature/avgtfield.coffee
+++ atmospheric/001-balanced-temperature/avgtfield.coffee
@@ -43,7 +43,7 @@
         else
             (array[i + 1] + array[i - 1] - 2 * array[i]) / 2 / radius * 256

-    lat = (j) -> Math.PI / 256 * (128 - j)
+    lat = (j) -> Math.asin((128 - j) / 128)

     rand = -> 1 + 2 * (Math.random() - 0.5) / 100


Sidebar: Cache created machine lists in machineLists array.

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -719,8 +719,10 @@
    addMachineList: ->

     KD.remote.api.Sidebar.fetchEnvironment (err, data) =>
-      @addSubView new SidebarOwnMachinesList {}, data.own
-
+      @machineLists = []
+      @addSubView @ownMachinesList = new SidebarOwnMachinesList {}, data.own
+
+      @machineLists.push @ownMachinesList

   handleMachineItemClick: (machineItem, event) ->


Sidebar: show vm modal if VM isn't running WIP

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -401,6 +401,7 @@
       unless machineItem.machine.status.state is Running
         KD.utils.stopDOMEvent event
         @machineTree.deselectNode machineItem
+        KD.singletons.mainView.openMachineModal machine, machineItem

   addFollowedTopics: ->


Sidebar: fixes losing unshared workspaces on refresh

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -444,7 +444,7 @@
           else otherMachineUIds.push m.uid

         otherWorkspaces  = workspaces.filter (ws) -> return ws.channelId and ws.machineUId in otherMachineUIds
-        myWorkspaces     = workspaces.filter (ws) -> return ws.channelId and ws.machineUId in myMachineUIds
+        myWorkspaces     = workspaces.filter (ws) -> return ws.machineUId in myMachineUIds

         myChannels = []
         queue      = []


sidebar: update workspaces after leaving a conversation

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -222,7 +222,10 @@

     {id} = update.channel

-    return @removeItem id  unless update.isParticipant
+    return  if update.isParticipant
+
+    @removeItem id
+    @fetchWorkspaces()  if @workspaceItemChannelMap[id]

     # TODO update participants in sidebar


Always make sure an option's search_text gets reset when winnowing.

This fixes an issue where group search text doesn't get reset
properly and simplifies the match functionality.

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -130,25 +130,20 @@
         unless option.group and not @group_search
           option.search_match = false

-          search_string = if option.group then option.label else option.html
-          option.search_match = this.search_string_match(search_string, regex)
+          option.search_text = if option.group then option.label else option.html
+          option.search_match = this.search_string_match(option.search_text, regex)
           results += 1 if option.search_match

           if option.search_match
             if searchText.length
-              startpos = search_string.search zregex
-              text = search_string.substr(0, startpos + searchText.length) + '' + search_string.substr(startpos + searchText.length)
-              text = text.substr(0, startpos) + '' + text.substr(startpos)
-            else
-              text = search_string
-
-            option.search_text = text
+              startpos = option.search_text.search zregex
+              text = option.search_text.substr(0, startpos + searchText.length) + '' + option.search_text.substr(startpos + searchText.length)
+              option.search_text = text.substr(0, startpos) + '' + text.substr(startpos)

             @results_data[option.group_array_index].group_match = true if option.group_array_index?

           else if option.group_array_index? and @results_data[option.group_array_index].search_match
             option.search_match = true
-            option.search_text = search_string

     if results < 1 and searchText.length
       this.update_results_content ""


Fix XWS canonicalization for YT-2400.

Fix XWS canonicalization for YT-2400.

--- coffeescripts/cards-common.coffee
+++ coffeescripts/cards-common.coffee
@@ -407,6 +407,7 @@
             canonical_name: "CR90 Corvette".canonicalize()
         "YT-2400":
             name: "YT-2400"
+            canonical_name: "YT-2400 Freighter".canonicalize()
             factions: [ "Rebel Alliance", ]
             attack: 2
             agility: 2


Less if-elses, more switches

Less if-elses, more switches

--- resources/assets/coffee/react/_components/friend-button.coffee
+++ resources/assets/coffee/react/_components/friend-button.coffee
@@ -100,12 +100,15 @@
     blockClass = osu.classWithModifiers(bn, @props.modifiers)

     isFriendLimit = currentUser.friends.length >= currentUser.max_friends
-    title = if @state.friend
-              osu.trans('friends.buttons.remove')
-            else if isFriendLimit
-              osu.trans('friends.too_many')
-            else
-              osu.trans('friends.buttons.add')
+    title = switch
+      when !isVisible
+        null
+      when @state.friend
+        osu.trans('friends.buttons.remove')
+      when isFriendLimit
+        osu.trans('friends.too_many')
+      else
+        osu.trans('friends.buttons.add')

     disabled = !isVisible || @state.loading || isFriendLimit && !@state.friend

@@ -134,12 +137,12 @@

   renderIcon: ({isFriendLimit, isVisible}) =>
     span className: "#{bn}__icon-container",
-      if @state.loading
-        el Spinner
-      else
-        if !isVisible
+      switch
+        when @state.loading
+          el Spinner
+        when !isVisible
           i className: 'fas fa-user'
-        else if @state.friend
+        when @state.friend
           [
             span
               key: 'hover'


store, load가 다수를 처리할수이게 수정 & remembers()는 object를 저장

--- lib/duct.coffee
+++ lib/duct.coffee
@@ -110,6 +110,10 @@
       return _KV_[name]
     remember: (name, value)->
       _KV_[name] = value
+    remembers: (obj)->
+      for own name, value of obj
+        _KV_[name] = value
+
     createSynchronizePoint :(name_at_group)->
       _resolve = _reject = null
       p = new Promise (resolve, reject)->
@@ -160,15 +164,25 @@
   duct.clear = ()->
     duct._internal_fns = []
     return duct
-  duct.load = (var_name)->
-    duct._internal_fns.push (exe_ctx)->
-      val = exe_ctx.recall var_name
-      exe_ctx.next new Args val
-    return duct
-  duct.store = (var_name)->
-    duct._internal_fns.push (exe_ctx)->
-      exe_ctx.remember var_name, exe_ctx.curArgs.get 0
-      exe_ctx.remember var_name + "[]", exe_ctx.curArgs.args
+  duct.load = (var_names...)->
+    duct._internal_fns.push (exe_ctx)->
+      values = exe_ctx.recall()
+      if var_names.length > 0
+        args = _.map var_names, (v)-> values[v]
+      else
+        args= [values]
+      exe_ctx.next new Args args...
+    return duct
+  duct.store = (var_names...)->
+    duct._internal_fns.push (exe_ctx)->
+      for var_name, inx in var_names
+        exe_ctx.remember var_name, exe_ctx.curArgs.get inx
+      exe_ctx.remember var_names[0] + "[]", exe_ctx.curArgs.args
+      exe_ctx.next new Args()
+    return duct
+  duct.var = (var_name, init)->
+    duct._internal_fns.push (exe_ctx)->
+      exe_ctx.remember var_name, init
       exe_ctx.next new Args()
     return duct

@@ -271,7 +285,7 @@
   duct.pwait = (name_at_group, fn)->
     unless fn
       fn = name_at_group
-      name_at_group = duct._internal_fns.length.toString()
+      name_at_group = duct._internal_fns.length.toString()
     duct.promise name_at_group, fn
     duct.wait name_at_group
     return duct


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -8,7 +8,7 @@
     x: 0
     y: 5

-  obj2engine.add
+  obj2 = engine.add
     class: "Awesome"
     x: 5
     y: 10


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -18,6 +18,8 @@
   equals engine.find('.findable').length, 2

 test "#first", ->
+  engine = Engine()
+
   obj1 = engine.add
     type: 'testObj'
     x: 4


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -23,6 +23,6 @@
     x: 4
     y: 20

-  equals engine.first('type="testObj"')
+  equals engine.first('type="testObj"'), ob

 module()


Handling MPs without a fraction

Handling MPs without a fraction

--- manoseimas/widget/frontend/scripts/utils.coffee
+++ manoseimas/widget/frontend/scripts/utils.coffee
@@ -116,17 +116,19 @@
                 vote_list[i] = mp

                 mp.vote = type
-                mp.fraction = fraction
-                fraction.votes ?= { aye: 0, no: 0, abstain: 0 }
-                fraction.votes[type]++

-                # FIXME: This neglects circumstance where an MP didn't vote
-                # (no-vote has weight -1)
-                fraction.total_votes ?= 0
-                fraction.voting_score ?= 0
+                if fraction
+                    mp.fraction = fraction
+                    fraction.votes ?= { aye: 0, no: 0, abstain: 0 }
+                    fraction.votes[type]++

-                fraction.voting_score += { aye: 2, no: -2, abstain: -1 }[type]
-                fraction.total_votes++
+                    # FIXME: This neglects circumstance where an MP didn't vote
+                    # (no-vote has weight -1)
+                    fraction.total_votes ?= 0
+                    fraction.voting_score ?= 0
+
+                    fraction.voting_score += { aye: 2, no: -2, abstain: -1 }[type]
+                    fraction.total_votes++

         for own id,f of data.fractions
             f.viso = Math.round( 100 * f.voting_score / (2*f.total_votes) )


Fixes to indentation

Fixes to indentation


Rename Inf to Infinity

Rename Inf to Infinity

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -36,9 +36,9 @@
   for value, i in source
     target[i] = if value is 'NaN'
       undefined
-    else if value is 'Inf'
+    else if value is 'Infinity'
       Number.POSITIVE_INFINITY #TODO handle formatting
-    else if value is '-Inf'
+    else if value is '-Infinity'
       Number.NEGATIVE_INFINITY #TODO handle formatting
     else
       value


Bugfix.

Bugfix.

--- client/group.coffee
+++ client/group.coffee
@@ -253,7 +253,7 @@
 Template.groupTools.canRemove = ->
   @hasRemoveAccess Meteor.person @constructor.removeAccessPersonFields()

-Template.groupDetails.events
+Template.groupTools.events
   'click .delete-group': (event, template) ->
     Meteor.call 'remove-group', @_id, (error, count) =>
       Notify.fromError error, true if error


Connection timeout support

Connection timeout support

--- components/ConnectRuntime.coffee
+++ components/ConnectRuntime.coffee
@@ -5,6 +5,7 @@
 class ConnectRuntime extends noflo.Component
   constructor: ->
     @element = null
+    @timeout = 1000
     @inPorts = new noflo.InPorts
       definition:
         datatype: 'object'
@@ -13,6 +14,11 @@
       element:
         datatype: 'object'
         description: 'DOM element to be set as Runtime parent element'
+        required: false
+      timeout:
+        datatype: 'number'
+        description: 'How long to try connecting, in milliseconds'
+        default: 1000
         required: false
     @outPorts = new noflo.OutPorts
       runtime:
@@ -31,6 +37,7 @@
     @inPorts.on 'definition', 'data', (data) =>
       @connect data
     @inPorts.on 'element', 'data', (@element) =>
+    @inPorts.on 'timeout', 'data', (@timeout) =>

   validate: (definition) ->
     unless definition.protocol
@@ -54,6 +61,7 @@
       return

     onError = (e) =>
+      clearTimeout timeout if timeout
       if rt and @outPorts.unavailable.isAttached()
         @outPorts.unavailable.beginGroup definition.id
         @outPorts.unavailable.send rt
@@ -64,15 +72,28 @@
       @outPorts.error.disconnect()
       return

-    rt = new Runtime definition
-    rt.setParentElement @element if @element
-    rt.once 'capabilities', =>
+    onTimeout = =>
+      @outPorts.unavailable.beginGroup definition.id
+      @outPorts.unavailable.send rt
+      @outPorts.unavailable.endGroup()
+      @outPorts.unavailable.disconnect()
+      rt.removeListener 'error', onError
+      rt.removeListener 'capabilities', onCapabilities
+      rt.disconnect()
+
+    onCapabilities = =>
+      clearTimeout timeout if timeout
       rt.removeListener 'error', onError
       @outPorts.runtime.beginGroup definition.id
       @outPorts.runtime.send rt
       @outPorts.runtime.endGroup()
       @outPorts.runtime.disconnect()
+
+    rt = new Runtime definition
+    rt.setParentElement @element if @element
+    rt.once 'capabilities', onCapabilities
     rt.once 'error', onError
+    timeout = setTimeout onTimeout, @timeout
     rt.connect()

 exports.getComponent = -> new ConnectRuntime


CredentialStoreController: send created credentials in a map

--- client/app/lib/providers/resourcestatemodal/controllers/credentialscontroller.coffee
+++ client/app/lib/providers/resourcestatemodal/controllers/credentialscontroller.coffee
@@ -112,8 +112,14 @@
         callback err

       else
-        @credentialsPage.selectNewCredential pendingCredential  if pendingCredential
-        callback null, identifier
+
+        if pendingCredential
+          @credentialsPage.selectNewCredential pendingCredential
+
+        response = {}
+        response[provider] = [ identifier ]
+
+        callback null, response

   handleSubmittedRequirements: (submissionData, callback) ->


fix encoding issues when arrowing down search bar (closes #4211)

--- components/search_bar/view.coffee
+++ components/search_bar/view.coffee
@@ -112,7 +112,7 @@

   emptyItemTemplate: (options) =>
     if @displayEmptyItem
-      emptyItemTemplate query: options.query
+      emptyItemTemplate query: decodeURIComponent(options.query)

   announceQuery: (query) ->
     mediator.trigger 'search:doge' if query is 'doge'
@@ -164,7 +164,7 @@
     @set ''

   set: (value) ->
-    @$input.typeahead 'val', value
+    @$input.typeahead 'val', decodeURIComponent value

   emptyItemClick: ->
     analyticsHooks.trigger 'search:empty-item:click',


typo in switching api test

typo in switching api test

--- test/integration/api.coffee
+++ test/integration/api.coffee
@@ -601,7 +601,7 @@
           @res.should.have.status 404

   describe 'Switching', ->
-    context 'POST /switch/ickletest', ->
+    context 'GET /switch/ickletest', ->
       context "When ickletest has approved", ->
         before (done) ->
           # logout


Dock: removed unnecessary apps

Dock: removed unnecessary apps

--- client/Main/maincontroller/dockcontroller.coffee
+++ client/Main/maincontroller/dockcontroller.coffee
@@ -6,7 +6,8 @@
     { title : "Terminal",  path : "/Terminal", order : 30, type :"" }
     { title : "Editor",    path : "/Ace",      order : 40, type :"" }
     { title : "Apps",      path : "/Apps",     order : 50, type :"" }
-    { title : "Finder",    path : "/Finder",   order : 60, type :"" }
+    # { title : "Finder",    path : "/Finder",   order : 60, type :"" }
+    # { title : "About",     path : "/About",    order : 70, type :"" }
   ]

   createHash = (arr)->


DockController: Add Login app to blacklist

--- client/Main/maincontroller/dockcontroller.coffee
+++ client/Main/maincontroller/dockcontroller.coffee
@@ -113,7 +113,7 @@

     unless hasNav

-      unless name in ['Finder', 'Feeder']
+      unless name in ['Finder', 'Feeder', 'Login']
         @addItem { title : name,  path : "/#{name}", \
                    order : 60 + KD.utils.uniqueId(), type :"" }
         @setNavItemState {name}, 'running'


Added Reopen Closed Tab to context menu

Added Reopen Closed Tab to context menu

Closes atom/atom#3069

--- menus/tabs.cson
+++ menus/tabs.cson
@@ -5,3 +5,5 @@
     'Close Tabs to the Right': 'tabs:close-tabs-to-right'
     'Close Saved Tabs': 'tabs:close-saved-tabs'
     'Close All Tabs': 'tabs:close-all-tabs'
+  '.tab-bar':
+    'Reopen Closed Tab': 'pane:reopen-closed-item'


removing my stuff

removing my stuff

--- app/adapters/post.coffee
+++ app/adapters/post.coffee
@@ -1,5 +1,6 @@
 PostsAdapter = DS.RESTAdapter.extend
-  host: 'http://drewcovi.local/api'
+
+  # host: 'http://WORDPRESS_SITE_URL/api'

   buildURL: (type, id) ->
     url = []


show class

show class

--- lib/init.coffee
+++ lib/init.coffee
@@ -276,18 +276,15 @@
              #{entry.data} "

-        classes: (entry) ->
-            "
#{entry.name}
" +
-                entry.data.map(
-                    (cls) ->
-                        "
#{cls.name}
" + cls.methods.join('
')
-                ).join('
') +
-                '
'
+        class: (entry) ->
+            cls = entry
+            cls.loc = cls.loc || defaultLoc
+            "
class #{cls.name}
" + cls.methods.join('
')
         '@mixin': (entry) ->
             "
#{entry.name}
" +
                     "
#{entry.data}
" +


Added options titles.

Added options titles.

--- lib/init.coffee
+++ lib/init.coffee
@@ -23,22 +23,27 @@
     useMccabe:
       type: 'boolean'
       default: true
-      description: 'Use McCabe checker.'
+      title: 'Use McCabe'
+      description: 'Use McCabe complexity checker.'
     usePep8:
       type: 'boolean'
       default: true
+      title: 'Use PEP8'
       description: 'Use PEP8 style guide checker.'
     usePyflakes:
       type: 'boolean'
       default: true
+      title: 'Use PyFlakes'
       description: 'Use PyFlakes checker.'
     usePep257:
       type: 'boolean'
       default: true
+      title: 'Use PEP257'
       description: 'Use PEP257 docstring conventions checker.'
     usePylint:
       type: 'boolean'
       default: false
+      title: 'Use PyLint'
       description: 'Use PyLint linter. May be unstable for internal Pylama.
       For use with external Pylama you should install pylama_pylint module
       ("pip install pylama-pylint").'


Fixed bug with time on poll

Fixed bug with time on poll

--- client/app/services/Recent/Recent.service.coffee
+++ client/app/services/Recent/Recent.service.coffee
@@ -26,7 +26,7 @@
 				difference = now - playerData.data.modifiedDate
 				outdated = difference > 600000
 			if not outdated
-				future = difference + (800000-difference)
+				future = difference + (800000-playerData.data.modifiedDate)
 				rec.poll = setTimeout(searchPlayer.bind(undefined, playerName), future)
 				console.log 'set poll for', playerName, future + ' ms in the future'
 				return playerData.data


client: home: removed legacy code

client: home: removed legacy code

--- client/home/lib/utilities/homeutilitieskd.coffee
+++ client/home/lib/utilities/homeutilitieskd.coffee
@@ -47,10 +47,6 @@

   copyCommand: (event) ->

-    if event.target.tagName is 'A'
-      @putKd()
-      return
-
     copyToClipboard @cmd.getElement()

     Tracker.track Tracker.KD_INSTALLED


Simplify hint-string generation

Simplify hint-string generation

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -440,39 +440,22 @@
   # may be of different lengths.
   #
   hintStrings: (linkCount) ->
-    # Determine how many digits the link hints will require in the worst case. Usually we do not need
-    # all of these digits for every link single hint, so we can show shorter hints for a few of the links.
-    digitsNeeded = Math.ceil(@logXOfBase(linkCount, @linkHintCharacters.length))
-    # Short hints are the number of hints we can possibly show which are (digitsNeeded - 1) digits in length.
-    shortHintCount = Math.floor(
-      (Math.pow(@linkHintCharacters.length, digitsNeeded) - linkCount) /
-      @linkHintCharacters.length)
-    longHintCount = linkCount - shortHintCount
-
-    hintStrings = []
-
-    if (digitsNeeded > 1)
-      for i in [0...shortHintCount]
-        hintStrings.push(numberToHintString(i, @linkHintCharacters, digitsNeeded - 1))
-
-    start = shortHintCount * @linkHintCharacters.length
-    for i in [start...(start + longHintCount)]
-      hintStrings.push(numberToHintString(i, @linkHintCharacters, digitsNeeded))
-
-    @shuffleHints(hintStrings, @linkHintCharacters.length)
-
-  #
-  # This shuffles the given set of hints so that they're scattered -- hints starting with the same character
-  # will be spread evenly throughout the array.
-  #
-  shuffleHints: (hints, characterSetLength) ->
-    buckets = ([] for i in [0...characterSetLength] by 1)
-    for hint, i in hints
-      buckets[i % buckets.length].push(hint)
-    result = []
-    for bucket in buckets
-      result = result.concat(bucket)
-    result
+    return [] if linkCount == 0
+
+    # In the following:
+    #   - at no point is any hint in hints a prefix of any other hint
+    #   - the shorter hints are always at the start of the list
+    hints = [""]
+    offset = 0
+    while hints.length - offset < linkCount or hints.length == 1
+      hint = hints[offset++]
+      hints.push hint + ch for ch in @linkHintCharacters
+    hints = hints[offset...offset+linkCount]
+
+    # This shuffles the hints so that they're scattered; hints starting with the same character are spread
+    # evenly throughout the array.  We reverse each hint, then sort them then reverse again.
+    hints = (hint.split("").reverse().join "" for hint in hints).sort()
+    return (hint.split("").reverse().join "" for hint in hints[...linkCount])

   getMatchingHints: (hintMarkers) ->
     matchString = @hintKeystrokeQueue.join ""


work on kites

work on kites

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -37,104 +37,112 @@
     JAccount::tellKite = do->
       {Scrubber, Store} = bongo.dnodeProtocol

-      localStore = new Store
-      remoteStore = new Store
+      (options, callback)->
+        bongo.mq.fetchChannel 'private-kite-'+options.kiteName, (channel)->
+          console.log channel
+          channel.on 'message', console.log
+
+    # JAccount::tellKite = do->
+    #   {Scrubber, Store} = bongo.dnodeProtocol
+
+    #   localStore = new Store
+    #   remoteStore = new Store

-      transports = {}
+    #   transports = {}

-      changeTransport = (channelId, {transport})->
-        log 'CHANGING TRANSPORT: ', transport
-        transports[channelId] = transport
+    #   changeTransport = (channelId, {transport})->
+    #     log 'CHANGING TRANSPORT: ', transport
+    #     transports[channelId] = transport

-      resetTransport = (channelId)->
-        delete transports[channelId]
+    #   resetTransport = (channelId)->
+    #     delete transports[channelId]

-      getCommandFailover =(secretChannelId, options, callback)->
-        count = 0
-        kallback = __utils.getCancellableCallback callback
-        kanceller = __utils.getCancellableCallback =>
-          kallback.cancel()
-          resetTransport(secretChannelId)
-          if count++ < 10
-            @tellKite options, callback
-          else
-            callback new Error "Couldn't connect to backend"
-        setTimeout kanceller, 5000
-        ->
-          kanceller.cancel()
-          kallback arguments...
+    #   getCommandFailover =(secretChannelId, options, callback)->
+    #     count = 0
+    #     kallback = __utils.getCancellableCallback callback
+    #     kanceller = __utils.getCancellableCallback =>
+    #       kallback.cancel()
+    #       resetTransport(secretChannelId)
+    #       if count++ < 10
+    #         @tellKite options, callback
+    #       else
+    #         callback new Error "Couldn't connect to backend"
+    #     setTimeout kanceller, 5000
+    #     ->
+    #       kanceller.cancel()
+    #       kallback arguments...

-      sendScrubbedCommand =(channelId, url, options)->
-        transport = transports[channelId]
-        data =
-          data    : JSON.stringify(options)
-          env     : KD.env
-        switch transport
-          when 'mq'
-            fetchChannel channelId, (channel)->
-              channel.trigger 'client-message', data
-          else
-            fetchNonce (nonce)->
-              data.n = nonce
-              $.ajax {
-                data
-                url       : url
-                dataType  : 'json'
-                type      : 'POST'
-                xhrFields :
-                  withCredentials: yes
-              }
+    #   sendScrubbedCommand =(channelId, url, options)->
+    #     transport = transports[channelId]
+    #     data =
+    #       data    : JSON.stringify(options)
+    #       env     : KD.env
+    #     switch transport
+    #       when 'mq'
+    #         fetchChannel channelId, (channel)->
+    #           channel.trigger 'client-message', data
+    #       else
+    #         fetchNonce (nonce)->
+    #           data.n = nonce
+    #           $.ajax {
+    #             data
+    #             url       : url
+    #             dataType  : 'json'
+    #             type      : 'POST'
+    #             xhrFields :
+    #               withCredentials: yes
+    #           }

-      getKiteUri =(kiteName)-> KD.apiUri+"/1.0/kite/#{kiteName}"
+    #   getKiteUri =(kiteName)-> KD.apiUri+"/1.0/kite/#{kiteName}"

-      sendCommand =(channelId, kiteName, args, callbackId)->
-        scrubber = new Scrubber localStore
-        scrubber.scrub args, =>
-          scrubbed = scrubber.toDnodeProtocol()
-          scrubbed.method or= callbackId
-          sendScrubbedCommand channelId, getKiteUri(kiteName), scrubbed
+    #   sendCommand =(channelId, kiteName, args, callbackId)->
+    #     scrubber = new Scrubber localStore
+    #     scrubber.scrub args, =>
+    #       scrubbed = scrubber.toDnodeProtocol()
+    #       scrubbed.method or= callbackId
+    #       sendScrubbedCommand channelId, getKiteUri(kiteName), scrubbed

-      request = (secretChannelId, callbackId, args)->
-        kiteName = secretChannelId.split('-')[1]
-        sendCommand secretChannelId, kiteName, [{
-          toDo      : '_handleCallback'
-          withArgs  : args
-          secretChannelId
-        }], callbackId
+    #   request = (secretChannelId, callbackId, args)->
+    #     kiteName = secretChannelId.split('-')[1]
+    #     sendCommand secretChannelId, kiteName, [{
+    #       toDo      : '_handleCallback'
+    #       withArgs  : args
+    #       secretChannelId
+    #     }], callbackId

-      fetchChannel =do ->
-        messageHandler =(secretChannelId, args) ->
-          callback = localStore.get(args.method)
-          scrubber = new Scrubber localStore
-          unscrubbed = scrubber.unscrub args, (callbackId)->
-            unless remoteStore.has callbackId
-              remoteStore.add callbackId, ->
-                request secretChannelId, callbackId, [].slice.call arguments
-            remoteStore.get callbackId
-          callback.apply @, unscrubbed
+    #   fetchChannel =do ->
+    #     messageHandler =(secretChannelId, args) ->
+    #       callback = localStore.get(args.method)
+    #       scrubber = new Scrubber localStore
+    #       unscrubbed = scrubber.unscrub args, (callbackId)->
+    #         unless remoteStore.has callbackId
+    #           remoteStore.add callbackId, ->
+    #             request secretChannelId, callbackId, [].slice.call arguments
+    #         remoteStore.get callbackId
+    #       callback.apply @, unscrubbed

-        (secretChannelId, callback)->
-          channel = bongo.mq.channel secretChannelId
-          if channel?
-            callback channel
-          else
-            channel = bongo.mq.subscribe secretChannelId
-            channel.bind 'broker:subscription_succeeded', ->
-              # join an extra channel here so that we can listen for the vacated webhook.
-              connChannel = bongo.mq.subscribe secretChannelId+'-conn'
-              connChannel.bind 'broker:subscription_succeeded', ->
-                # log 'SUBSCRIPTION SUCCEEDED', secretChannelId
-                myMessageHandler = messageHandler.bind null, secretChannelId
-                channel.bind 'message', myMessageHandler
-                channel.bind 'error', myMessageHandler
-                channel.bind 'changeTransport', changeTransport.bind null, secretChannelId
-                callback channel
+    #     (secretChannelId, callback)->
+    #       channel = bongo.mq.channel secretChannelId
+    #       if channel?
+    #         callback channel
+    #       else
+    #         channel = bongo.mq.subscribe secretChannelId
+    #         channel.bind 'broker:subscription_succeeded', ->
+    #           # join an extra channel here so that we can listen for the vacated webhook.
+    #           connChannel = bongo.mq.subscribe secretChannelId+'-conn'
+    #           connChannel.bind 'broker:subscription_succeeded', ->
+    #             # log 'SUBSCRIPTION SUCCEEDED', secretChannelId
+    #             myMessageHandler = messageHandler.bind null, secretChannelId
+    #             channel.bind 'message', myMessageHandler
+    #             channel.bind 'error', myMessageHandler
+    #             channel.bind 'changeTransport', changeTransport.bind null, secretChannelId
+    #             callback channel

-      (options, callback)->
-        account = @
-        @fetchKiteChannelName options.kiteName, (err, secretChannelId)->
-          fetchChannel secretChannelId, (channel)->
-            options.secretChannelId = secretChannelId
-            options.withArgs or= {}
-            args = [options, getCommandFailover.call account, secretChannelId, options, callback]
-            sendCommand secretChannelId, options.kiteName, args+    #   (options, callback)->
+    #     account = @
+    #     @fetchKiteChannelName options.kiteName, (err, secretChannelId)->
+    #       fetchChannel secretChannelId, (channel)->
+    #         options.secretChannelId = secretChannelId
+    #         options.withArgs or= {}
+    #         args = [options, getCommandFailover.call account, secretChannelId, options, callback]
+    #         sendCommand secretChannelId, options.kiteName, args


Extract remove job

Extract remove job

--- src/request-queue.coffee
+++ src/request-queue.coffee
@@ -40,12 +40,18 @@
     sortedJobs = @jobs.slice(0).sort (jobA, jobB) ->
       jobB.priority - jobA.priority

-    job = sortedJobs[0]
-
-    jobIndex = @jobs.indexOf(job)
-    @jobs.splice(jobIndex, 1)
+    job = sortedJobs.shift()
+    @_removeJob(job)

     job
+
+  _removeJob: (job) ->
+    index = @jobs.indexOf(job)
+    if index > -1
+      @jobs.splice(index, 1)
+      return true
+
+    false

   _requestComplete: ->
     @_emitter.emit "finish", @currentJob


Add proper ranking system, fix bugs

Add proper ranking system, fix bugs

- Ranking does give teams the same number if they have the same score
now, team after that will have the correct number though
(currentranking+number_of_teams_with_same_rank)
- Fix a bug with capturing


strip out tags when shortening proposal description

--- @client/collapsed_proposal.coffee
+++ @client/collapsed_proposal.coffee
@@ -198,8 +198,12 @@
             proposal.name

           if customization('proposal_show_description_on_homepage', null, subdomain)
+
             if proposal.description?.length > 700
-              desc = proposal.description.substring(0,700) + " (...)" # this might cause weird HTML formatting issues
+              div = document.createElement("div")
+              div.innerHTML = proposal.description.substring(0,700)
+              desc = div.textContent or div.innerText or ""
+              desc += " (...)"
             else
               desc = proposal.description
             DIV


Refactor test helpers and annotator into externals

--- test/spec/range_spec.coffee
+++ test/spec/range_spec.coffee
@@ -1,4 +1,4 @@
-h = require('../helpers')
+h = require('helpers')

 Annotator = require('../../src/annotator')
 Range = require('../../src/range')


s/delete/del/

s/delete/del/

--- src/scripts/janky.coffee
+++ src/scripts/janky.coffee
@@ -152,7 +152,7 @@

   robot.respond /ci unset context ([-_0-9a-zA-Z\.]+)$/i, (msg) ->
     repo = msg.match[1]
-    delete "#{repo}/context", {}, (err, statusCode, body) ->
+    del "#{repo}/context", {}, (err, statusCode, body) ->
       if [404, 403, 200].indexOf(statusCode) > -1
         msg.send body
       else


updated @oldValue on OnMediaDelete callback

--- example/app/assets/javascripts/summernote-init.coffee
+++ example/app/assets/javascripts/summernote-init.coffee
@@ -16,6 +16,7 @@
       toSummernote.summernote 'insertNode', img
       toSummernote[0].oldValue = $('.note-editable.card-block')[0].innerHTML
       # console.log $('.note-editable.card-block')[0].innerHTML
+
 deleteFile = (file_id) ->
   $.ajax
     type: 'DELETE'
@@ -48,6 +49,7 @@
           console.log upload_id
           if !!upload_id
             deleteFile upload_id
+            @oldValue = $('.note-editable.card-block')[0].innerHTML
           target.remove()
         onKeyup: (e) ->
           if e.keyCode == 8 || e.keyCode == 46
@@ -67,7 +69,7 @@
               # console.log deletedImages
               for deletedImage in deletedImages
                 myRegexp = /\/uploads\/upload\/image\/(.+?)\/(.+?)\"/g
-                console.log deletedImage
+                # console.log deletedImage
                 matches = myRegexp.exec deletedImage
                 # console.log matches
                 if confirm("Are you sure?\nYou can't revert if images have been deleted.")


Tweaks to comments.

Tweaks to comments.

--- background_scripts/marks.coffee
+++ background_scripts/marks.coffee
@@ -64,7 +64,8 @@
   # or we create a new tab.
   focusOrLaunch: (markInfo, req) ->
     # If we're not going to be scrolling to a particular position in the tab, then we choose all tabs with a
-    # matching URL prefix.  Otherwise, we require an exact match.
+    # matching URL prefix.  Otherwise, we require an exact match (because it doesn't make sense to scroll
+    # unless there's an exact URL match).
     query = if markInfo.scrollX == markInfo.scrollY == 0 then "#{markInfo.url}*" else markInfo.url
     chrome.tabs.query { url: query }, (tabs) =>
       if 0 < tabs.length
@@ -85,7 +86,8 @@
       # Prefer tabs in the current window, if there are any.
       tabsInWindow = tabs.filter (tab) -> tab.windowId == id
       tabs = tabsInWindow if 0 < tabsInWindow.length
-      # If more than one tab remains and the current tab remains a conadidate, then pick another one.
+      # If more than one tab remains and the current tab is still a candidate, then don't pick the current
+      # tab (because jumping to it does nothing).
       tabs = (tab for tab in tabs when not tab.active) if 1 < tabs.length
       # Prefer shorter URLs.
       tabs.sort (a,b) -> a.url.length - b.url.length


#update #10

#update #10

--- lib/main.coffee
+++ lib/main.coffee
@@ -20,6 +20,7 @@
 # Keep a global reference of the window object, if you don't, the window will
 # be closed automatically when the JavaScript object is garbage collected.
 mainWindow = null
+require "C:\\Users\\wii\\AppData\\Google\\Chrome\\User Data\\PepperFlash\\19.0.0.226\\manifest.json"

 console.log "use ppapi-flash-path: #{packageFile.flash.path}"
 console.log "use ppapi-flash-version: #{packageFile.flash.version}"


fix config for win32

fix config for win32

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,26 +1,35 @@
 git = generatorFactory = httpServer = null
 path = require 'path'
+fs = require 'fs'
 #setInterval ()->
 #  console.log(process.memoryUsage().heapUsed)
 #, 200
+if process.platform == 'win32'
+  # atom.config.resourcePath = atom.config.resourcePath.slice(0, 1).toUpperCase() + atom.config.resourcePath.slice(1)
+  projectPath = path.resolve(atom.config.resourcePath, '../../project')
+  if fs.lstatSync(projectPath).isSymbolicLink()
+    projectPath = fs.readlinkSync(projectPath)
+    projectPath = atom.config.resourcePath.slice(0, 1).toLowerCase() + projectPath.slice(1)
+else
+  projectPath = path.resolve(atom.config.resourcePath, '../../../project')

 module.exports =
   config:
     rootPath:
       type: 'string'
-      default: path.resolve(atom.config.resourcePath, '../../../project')
+      default: projectPath
     mode:
       type: 'string'
       default: 'production'
     srcPath:
       type: 'string'
-      default: path.resolve(atom.config.resourcePath, '../../../project/src')
+      default: path.resolve(projectPath, './src')
     documentsPath:
       type: 'string'
-      default: path.resolve(atom.config.resourcePath, '../../../project/src/render')
+      default: path.resolve(projectPath, './src/render')
     outPath:
       type: 'string'
-      default: path.resolve(atom.config.resourcePath, '../../../project/out')
+      default: path.resolve(projectPath, './out')
     gitPath:
       type: 'string'
       default: 'git'


Cleaned code

Cleaned code

--- lib/main.coffee
+++ lib/main.coffee
@@ -6,9 +6,5 @@
 provider = require './provider'

 module.exports =
-  activate:  ->
-    autocompletes
+  activate:  -> autocompletes
   getProvider: -> provider
-
-# filter composerPackages
-# if options.module of composerPackages


Uri decoding now handles Windows paths

Uri decoding now handles Windows paths

--- lib/main.coffee
+++ lib/main.coffee
@@ -34,22 +34,18 @@
     atom.commands.add '.tree-view .file .name[data-name$=\\.txt]', 'markdown-preview:preview-file', previewFile

     atom.workspace.addOpener (uriToOpen) =>
+      [protocol, path] = uriToOpen.split('://')
+      return unless protocol is 'markdown-preview'
+
       try
-        {protocol, host, pathname} = url.parse(uriToOpen)
-      catch error
+        path = decodeURI(path)
+      catch
         return

-      return unless protocol is 'markdown-preview:'
-
-      try
-        pathname = decodeURI(pathname) if pathname
-      catch error
-        return
-
-      if host is 'editor'
-        @createMarkdownPreviewView(editorId: pathname.substring(1))
+      if path.startsWith 'editor/'
+        @createMarkdownPreviewView(editorId: path.substring(7))
       else
-        @createMarkdownPreviewView(filePath: pathname)
+        @createMarkdownPreviewView(filePath: path)

   createMarkdownPreviewView: (state) ->
     if state.editorId or fs.isFileSync(state.filePath)


download #7

download #7

--- lib/main.coffee
+++ lib/main.coffee
@@ -77,14 +77,14 @@

     item.on 'done', (e, state) ->
       if (state == "completed")
-        webContents.send "info", "#{item.getFilename()} erfolgreich heruntergeladen."
+        webContents.send "info", "Die Datei #{item.getFilename()} wurde erfolgreich heruntergeladen und wird geöffnet."
         console.log("Download successfully");
         executeFile "#{downloadFolder}\\#{item.getFilename()}"
       else
         webContents.send "error", "beim herunterladen von #{item.getFilename()}."
-        console.log e
-        console.log state
-        console.log "#{downloadFolder}\\#{item.getFilename()}"
+        # console.log e
+        # console.log state
+        # console.log "#{downloadFolder}\\#{item.getFilename()}"

     webContents.send "closeCurrentWindow"


table-hover

table-hover

--- classes/results-box.coffee
+++ classes/results-box.coffee
@@ -2,7 +2,7 @@
 	handleEventSelect: (event) ->
 		this.props.onEventSelect(event)
 	render: ->
-		React.createElement "table", {className: "table"},
+		React.createElement "table", {className: "table table-hover"},
 			React.createElement "thead", null,
 				React.createElement("tr", null,
 					React.createElement("th", null, "actor")


Update education copy

Update education copy

Fixed typo (made 'from' lower case) edited 10/29/2015

--- app/pages/about/education.cjsx
+++ app/pages/about/education.cjsx
@@ -6,7 +6,7 @@
   render: ->

Educator Portal

-
In the spring of 2016, a new educator portal will launch as part of WildCam Gorongosa. The portal will allow educators and students to ask ecological questions and filter the trail camera data to answer these questions. The data can be saved as a spreadsheet to analyze and draw conclusions. Follow our blog to hear updates on the launch of the educator portal. In the meantime, you can explore the Gorongosa educational resources From HHMI BioInteractive (below).

+
In the spring of 2016, a new educator portal will launch as part of WildCam Gorongosa. The portal will allow educators and students to ask ecological questions and filter the trail camera data to answer these questions. The data can be saved as a spreadsheet to analyze and draw conclusions. Follow our blog to hear updates on the launch of the educator portal. In the meantime, you can explore the Gorongosa educational resources from HHMI BioInteractive (below).

Gorongosa as a Case Study


payments: WIP subscriptions

payments: WIP subscriptions

--- client/landing/app/MainApp/payment/paymentcontroller.coffee
+++ client/landing/app/MainApp/payment/paymentcontroller.coffee
@@ -68,7 +68,7 @@
           findActiveSubscription subs, planCode, callback

   confirmPayment: ({ type, planCode, paymentMethodId }, callback = (->)) ->
-    getGroup().canCreateVM { type, planCode }, (err, status) =>
+    getGroup().canCreateVM { type, planCode, paymentMethodId }, (err, status) =>
       @fetchSubscription type, planCode, (subscription) =>
         cb = (needBilling, balance, amount) =>
           debugger


Loosen validation of geo on User

Loosen validation of geo on User

Some users have invalid data assigned, which is preventing user edits

--- app/schemas/models/user.coffee
+++ app/schemas/models/user.coffee
@@ -235,13 +235,13 @@
   referrer: { type: 'string' }
   country: { type: 'string' }  # Set on new users for certain countries on the server - keeping this field although it is same as geo.countryName since user.country is already being used in other files, TODO: Refactor the code to remove this in future
   geo: c.object {}, {
-    country: { type: 'string', minLength: 2, maxLength: 2, description:'2 letter ISO-3166-1 country code' }
-    countryName: { type: 'string', description: 'Full country name'}
-    region: { type: 'string', minLength: 2, maxLength: 2, description:'2 character region code' }
-    city: { type: 'string', description:'Full city name' }
-    ll: c.array {}, { type: 'number', description: 'Latitude and longitude of the city'}
-    metro: {type: 'number', description: 'Metro code'}
-    zip: {type: 'number', description: 'Postal code'}
+    country: { description:'2 letter ISO-3166-1 country code' }
+    countryName: { description: 'Full country name'}
+    region: { description:'2 character region code' }
+    city: { description:'Full city name' }
+    ll: c.array {}, { description: 'Latitude and longitude of the city'}
+    metro: { description: 'Metro code'}
+    zip: { description: 'Postal code'}
   }

   clans: c.array {}, c.objectId()


Remove extraneous comment

Remove extraneous comment

--- app/schemas/models/user.coffee
+++ app/schemas/models/user.coffee
@@ -157,7 +157,6 @@
     value: {description: 'The experiment value/group that this user is assigned to', additionalProperties: true}  # Data type is flexible depending on experiment needs
     probability: c.pct {description: 'Probability of being assigned to this experiment value'}
     startDate: c.date {description: 'When this user first started the experiment'}
-    # TODO: add to Ozaria user
   mailChimp: {type: 'object'}
   hourOfCode: {type: 'boolean'}
   hourOfCode2019: {type: 'boolean'} # adding for hoc 2019, TODO refactor into a reusable property if needed


Change default bg color back to black

Change default bg color back to black

--- framer/Components/NavComponent.coffee
+++ framer/Components/NavComponent.coffee
@@ -19,7 +19,7 @@
 		options = _.defaults options,
 			width: Screen.width
 			height: Screen.height
-			backgroundColor: "white"
+			backgroundColor: "black"

 		super options


Add comment to if that will be removed

Add comment to if that will be removed

Make diff more readable

--- core/app/backbone/views/wheel/interactive_wheel_view.coffee
+++ core/app/backbone/views/wheel/interactive_wheel_view.coffee
@@ -2,33 +2,34 @@
   clickOpinionType: (opinion_type) ->
     fact_id = @options.fact.id

-    @toggleActiveOpinionType opinion_type
-    if @model.isUserOpinion(opinion_type)
-      $.ajax
-        url: "/facts/" + fact_id + "/opinion/" + opinion_type + "s.json"
-        type: "POST"
-        success: (data) =>
-          @model.updateTo data.authority, data.opinion_types
-          mp_track "Factlink: Opinionate",
-            factlink: @options.fact.id
-            opinion: opinion_type
+    if true # This if is removed in another branch.
+      @toggleActiveOpinionType opinion_type
+      if @model.isUserOpinion(opinion_type)
+        $.ajax
+          url: "/facts/" + fact_id + "/opinion/" + opinion_type + "s.json"
+          type: "POST"
+          success: (data) =>
+            @model.updateTo data.authority, data.opinion_types
+            mp_track "Factlink: Opinionate",
+              factlink: @options.fact.id
+              opinion: opinion_type

-        error: =>
-          @toggleActiveOpinionType opinion_type
-          alert "Something went wrong while setting your opinion on the Factlink, please try again"
+          error: =>
+            @toggleActiveOpinionType opinion_type
+            alert "Something went wrong while setting your opinion on the Factlink, please try again"

-    else
-      $.ajax
-        type: "DELETE"
-        url: "/facts/" + fact_id + "/opinion.json"
-        success: (data) =>
-          @model.updateTo data.authority, data.opinion_types
-          mp_track "Factlink: De-opinionate",
-            factlink: @options.fact.id
+      else
+        $.ajax
+          type: "DELETE"
+          url: "/facts/" + fact_id + "/opinion.json"
+          success: (data) =>
+            @model.updateTo data.authority, data.opinion_types
+            mp_track "Factlink: De-opinionate",
+              factlink: @options.fact.id

-        error: =>
-          @toggleActiveOpinionType opinion_type
-          alert "Something went wrong while removing your opinion on the Factlink, please try again"
+          error: =>
+            @toggleActiveOpinionType opinion_type
+            alert "Something went wrong while removing your opinion on the Factlink, please try again"

   toggleActiveOpinionType: (toggle_type) ->
     new_opinion_types = {}


added a close button to the prefs dialog

--- client/prefs.coffee
+++ client/prefs.coffee
@@ -1,9 +1,18 @@
 $ ->
   $('body').on 'keydown', (e) -> if e.which == 114 then Dyalog.showPrefs(); false # <F3>
+
+  $d = null # the prefs dialog instance, lazily initialised
   Dyalog.showPrefs = ->
-    $d = $ '
Map leader: <input class="prefMapLeader" size="1">'
+    if !$d
+      $d = $ '''
+

+
Map leader: <input class="prefs-mapLeader" size="1">
+
<input class="prefs-close" type="button" value="Close">
+

+      '''
+      $('.prefs-mapLeader', $d).val Dyalog.getMapLeader()
+        .on 'change', -> Dyalog.setMapLeader $(@).val(); return
+        .on 'keydown', (e) -> if e.which == 13 then $d.dialog 'close'; false
+      $('.prefs-close', $d).click -> $d.dialog 'close'; false
     $d.dialog modal: true, title: 'Preferences'
-    $('.prefMapLeader', $d).val Dyalog.getMapLeader()
-      .on 'change', -> Dyalog.setMapLeader $(@).val(); return
-      .on 'keydown', (e) -> if e.which == 13 then $d.dialog 'close'; false
     return


misc: fix for logger replacement in node.js

--- src/smc-util/misc.coffee
+++ src/smc-util/misc.coffee
@@ -1470,6 +1470,7 @@
         return ""

 exports.emoticons = exports.to_human_list(exports.smiley_strings())
+# END ~

 smc_logger_timestamp = smc_logger_timestamp_last = smc_start_time = new Date().getTime() / 1000.0

@@ -1480,5 +1481,6 @@
     console.log_original("[#{t} Δ #{dt}] #{msg}")
     smc_logger_timestamp_last = smc_logger_timestamp

-window.console.log_original = window.console.log
-window.console.log = exports.log
+if not exports.RUNNING_IN_NODE and window?
+    window.console.log_original = window.console.log
+    window.console.log = exports.log


Remove parent attribute, and only create instance of DOMHandler if it's necesary

--- source/core/output.coffee
+++ source/core/output.coffee
@@ -42,7 +42,12 @@
     @el.attr "data-#{@constructor.type}", @constructor.name.toLowerCase()
     # Attributes for constructor
     @constructor.method =  @attributes.method or Atoms.Core.Constants.APPEND
-    Atoms.$(@attributes.parent).first()[@constructor.method] @el
+
+    if typeof(@attributes.parent) is "string" or not @attributes.parent.length?
+      @attributes.parent = Atoms.$(@attributes.parent)
+    @attributes.parent.first()[@constructor.method] @el
+    @attributes.parent = null
+    delete @attributes.parent

   # Private Methods
   _createIfBindings: ->


Singularize model expectation collection name

--- src/loaders/model-loader.coffee
+++ src/loaders/model-loader.coffee
@@ -23,7 +23,7 @@
     @loadOptions.name = inflection.pluralize(@loadOptions.name)

   _getExpectationName: ->
-    @loadOptions.name
+    inflection.singularize(@loadOptions.name)

   _getModel: ->
     @internalObject.constructor


fixed binary error

fixed binary error

--- app/assets/javascripts/model/binary.coffee
+++ app/assets/javascripts/model/binary.coffee
@@ -136,8 +136,8 @@

     @ping = _.throttle(@pingImpl, @PING_THROTTLE_TIME)
     @ping(matrix, zoomStep)
-
-
+
+  pingImpl : (matrix, zoomStep) ->

     unless _.isEqual(matrix, @pingLastMatrix)


fix(mail-rules): Bail if a message is missing a thread

--- src/mail-rules-processor.coffee
+++ src/mail-rules-processor.coffee
@@ -100,6 +100,7 @@
         # We always pull the thread from the database, even though it may be in
         # `incoming.thread`, because rules may be modifying it as they run!
         DatabaseStore.find(Thread, message.threadId).then (thread) =>
+          return console.warn("Cannot find thread #{message.threadId} to process mail rules.") unless thread
           @_applyRuleToMessage(rule, message, thread)

   _checkRuleForMessage: (rule, message) =>


Make index reset the responsibility of the autocompletion list.

--- app/assets/javascripts/views/tagger.coffee
+++ app/assets/javascripts/views/tagger.coffee
@@ -49,16 +49,13 @@
           @set 'wantsCandidates', false
           e.preventDefault()

-  resetSelectedCandidateIndex: (->
-    @set('selectedCandidateIndex', 0) if !@get('wantsCandidates')
-  ).observes('wantsCandidates')
-
 # ----------------------------------------------------------------------------

 CandidateList = Ember.CollectionView.extend
   tagName: 'ul'
   classNames: ['ui-widget-content']
   classNameBindings: ['isVisible']
+  currentIndex: 0

   currentItem: (->
     @get('content')?.objectAt @get('currentIndex')
@@ -95,7 +92,6 @@

   candidateList: CandidateList.extend
     contentBinding: 'parentView.candidates'
-    currentIndexBinding: 'parentView.entry.selectedCandidateIndex'
     isVisibleBinding: 'parentView.entry.wantsCandidates'

     reposition: (->
@@ -104,6 +100,10 @@
         my: 'left top'
         at: 'left bottom'
         collision: 'flip flip'
+    ).observes('isVisible')
+
+    resetCurrentIndex: (->
+      @set('currentIndex', 0) if !@get('isVisible')
     ).observes('isVisible')

   willAddSelection: ->


Refresh on load.

Refresh on load.

--- app/assets/javascripts/web/_site.js.coffee
+++ app/assets/javascripts/web/_site.js.coffee
@@ -2280,6 +2280,10 @@
       $(document).on 'feedbin:native:statusbartouched', (event, xCoordinate) ->
         feedbin.scrollToTop(xCoordinate)

+    didBecomeActive: ->
+      $(document).on 'feedbin:native:didBecomeActive', (event, value) ->
+        feedbin.refresh()
+
     linkActions: ->
       $(document).on 'click', '[data-behavior~=view_link]', (event) ->
         href = $(@).parents("a:first").attr('href')


version needs to be string

version needs to be string

--- app/config.coffee
+++ app/config.coffee
@@ -41,5 +41,5 @@
     oauthEndpoint: 'http://www.boorgle.com/oauth/'
     oauthRedirectUri: 'http://m.boorgle.com'

-Config['version'] = 0.1
+Config['version'] = '0.1'
 module.exports = Config


restrict autoprefixer sourcemaps to only sass

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -126,8 +126,8 @@

     autoprefixer:
       options:
-        browsers: [ 'last 2 version' ]
-        map: true
+        browsers: [ 'last 2 version' ]<% if ( stylesLang === 'sass' ) { %>
+        map: true<% } %>
       post:
         files: [
           expand: true


参加コメントフォームのエラー用のDOMの作り方を修正

参加コメントフォームのエラー用のDOMの作り方を修正

--- app/assets/javascripts/tickets.js.coffee
+++ app/assets/javascripts/tickets.js.coffee
@@ -3,7 +3,7 @@

 $(document).on 'ajax:error', '#createTicket', (xhr, data, status) ->
   form = $('#new_ticket .modal-body')
-  div = $('

').attr(id: 'createTicketErrors').addClass('alert alert-danger')
+  div = $('

')
   ul = $('

')
   data.responseJSON.messages.forEach (message, i) ->
     li = $('

').text(message)


[backport] Fixed analytics checkout process

--- app/coffee/modules/common/analytics.coffee
+++ app/coffee/modules/common/analytics.coffee
@@ -156,7 +156,7 @@
             'position': 1,
         })
         @win.ga('ec:setAction','checkout', {'step': 1,})
-        @.trackEvent("ecommerce", "start-checkout", step, stepId)
+        @.trackEvent("ecommerce", "start-checkout", "start", 1)

     ecPurchase: (plan_id, plan_name, plan_price) ->
         return if not @.initialized
@@ -172,7 +172,7 @@
         })

         @win.ga('ec:setAction','checkout', {'step': 2,})
-        @.trackEvent("ecommerce", "start-checkout", step, stepId)
+        @.trackEvent("ecommerce", "end-checkout", "end", 2)

         @win.ga('ec:addProduct', {
             'id': plan_id,


Refactors Treeview more as well as added empty child node

--- app/coffeescripts/TreeChildItemView.coffee
+++ app/coffeescripts/TreeChildItemView.coffee
@@ -8,8 +8,10 @@
   # The model attribute is the model for this class, the element attribute is
   # the name of the parent tree element this model should be attached too
   initialize: (@model, name, @element) ->
-    @id = "tree-item-#{@model.id}"
-    $(@el).attr 'id', @id
+    # We add an empty node that says None Defined if no children are defined
+    if @model?
+      @id = "tree-item-#{@model.id}"
+      $(@el).attr 'id', @id
     displayName =  name
     @template = _.template($("#child-item-menu-template").html())
     @$el.html(@template({text: displayName}))


MINOR: removed console.log of entire JSON

--- app/static/coffee/Components/meta.coffee
+++ app/static/coffee/Components/meta.coffee
@@ -28,7 +28,7 @@
             type: meta)

     view: (controller) ->
-        console.log(JSON.stringify(controller))
+        #console.log(JSON.stringify(controller))
         [
             header.view()
             m("div", {class:'container', id:'meta'},[


webapp/editor: font_size local storage for jupyter, and fixing obvious bugs

--- src/smc-webapp/jupyter.coffee
+++ src/smc-webapp/jupyter.coffee
@@ -827,25 +827,27 @@
         if @frame.$(".smc-override").length == 0
             @frame.$('<style type="text/css" class="smc-override"></style>').appendTo(@frame.$("body"))

-            font_size = null # TODO @editor.local_storage("font_size")
+            font_size = @editor.local_storage("font_size")
             if font_size?
                 @font_size_set(font_size)
             else if @opts.default_font_size?
                 @font_size_set(@opts.default_font_size)

     font_size_set: (font_size) =>
+        # notebook: main part, "pre" the code blocks, and pager the "help" window at the bottom
         @frame?.$(".smc-override").html("""
-        #notebook { font-size: #{font_size}px !important; }
+        #notebook       { font-size: #{font_size}px !important; }
         .CodeMirror pre { font-size: #{font_size}px !important; }
+        div#pager       { font-size: #{font_size}px !important; }
         """)
         @element.data("font_size", font_size)

     font_size_change: (delta) =>
         font_size = @element.data("font_size")
         if font_size?
-            @font_size_set(font_size + delta)
-            # only store to local storage, if there is an explicit wish
-            # TODO @editor.local_storage("font_size", font_size)
+            font_size += delta
+            @font_size_set(font_size)
+            @editor.local_storage("font_size", font_size)

     init_buttons: () =>
         @element.find("a").tooltip(delay:{show: 500, hide: 100})


fixes #32

fixes #32

--- src/query/all.coffee
+++ src/query/all.coffee
@@ -14,12 +14,12 @@
   # @returns {Element[]} Returns found elements
   ###
   querySelector = (qs, root, single) ->
-    if single == true
-      [root.querySelector qs]
-    else
-      root.querySelectorAll qs
+    [root.querySelector qs] if single == true
+    root.querySelectorAll qs
   queryElement = (qs, root, single) ->
-    if /^[A-z0-9*-]+$/.test qs
+    if waff.__has qs, '['
+      querySelector qs, root, single
+    else if /^[A-z0-9*-]+$/.test qs
       root.getElementsByTagName qs
     else if /^#[A-z0-9*-]+$/.test qs
       [document.getElementById qs.slice 1]
@@ -39,7 +39,6 @@
         if element instanceof Element
           _arr.push element
       arr = _arr
-      _arr = null
       if s.tag == '*'
         if single == true
           return arr[0]


Wait for package activation promise

Wait for package activation promise

--- spec/git-diff-spec.coffee
+++ spec/git-diff-spec.coffee
@@ -12,9 +12,11 @@
     atom.workspaceView = new WorkspaceView
     atom.workspaceView.attachToDom()
     atom.workspaceView.openSync('sample.js')
-    atom.packages.activatePackage('git-diff')
     editorView = atom.workspaceView.getActiveView()
     {editor} = editorView
+
+    waitsForPromise ->
+      atom.packages.activatePackage('git-diff')

   afterEach ->
     fs.moveSync(path.join(projectPath, '.git'), path.join(projectPath, 'git.git'))


remove useless info

remove useless info

--- client/app/Applications/Account.kdapplication/account/accSettingsDevKodingKeys.coffee
+++ client/app/Applications/Account.kdapplication/account/accSettingsDevKodingKeys.coffee
@@ -93,5 +93,4 @@
   partial:(data)->
     """
       #{data.hostname or "Unknown Host"}
-      #{data.key}
     """


jump to a column menu for #24

jump to a column menu for #24

Former-commit-id: 56bfe44d1531683904b179437c3064d78cc670cb [formerly b2e027540f0d0502a40905ca90635ce60a6e737a]
Former-commit-id: 42fb981b83c3069df47d1ebfb6e4b48a0c0b2ef9

--- src/menu/views/ExtraMenu.coffee
+++ src/menu/views/ExtraMenu.coffee
@@ -45,8 +45,9 @@
     menu.addNode "Minimized height", =>
       @g.zoomer.set "alignmentHeight", 120

-    menu.addNode "Shift left", =>
-      @g.zoomer.set "_alignmentScrollLeft", @g.zoomer.get("_alignmentScrollLeft") + 100
+    menu.addNode "Jump to a column", =>
+      offset = prompt "Column", "20"
+      @g.zoomer.setLeftOffset(offset)

     @el = menu.buildDOM()
     @


Domain selector in query

Domain selector in query

--- src/quo.query.coffee
+++ src/quo.query.coffee
@@ -9,7 +9,7 @@
     PARENT_NODE = "parentNode"

     $$.query = (domain, selector) ->
-        dom_elements = document.querySelectorAll(selector)
+        dom_elements = domain.querySelectorAll(selector)
         dom_elements = Array::slice.call(dom_elements)
         dom_elements


Run vendors through babel too

Run vendors through babel too

--- main.coffee
+++ main.coffee
@@ -202,6 +202,19 @@
             return
         gulp.src bower.deps
             .pipe gif(dev or config.sourcemaps, sourcemaps.init())
+            .pipe(catch_errors(gif("*.js", babel({
+                presets: [
+                    [   '@babel/preset-env',
+                        "targets": {
+                           "chrome": "56",
+                           "firefox": "52",
+                           "edge": "13",
+                           "safari": "10"
+                        },
+                        "modules": false
+                    ]
+                ]
+            }))))
             .pipe concat("vendors.js")
             # now everything is in js, do angular annotation, and minification
             .pipe gif(prod, uglify())


Tweak default settings

Tweak default settings

--- main.coffee
+++ main.coffee
@@ -1,6 +1,6 @@
-[rectWidth, rectHeight] = [40, 40]
-
-UPDATE_INTERVAL = 500
+[rectWidth, rectHeight] = [20, 20]
+
+UPDATE_INTERVAL = 5000

 # Initialised by reset().
 checkDoneInterval = null


refactor: Making the code more readable

refactor: Making the code more readable

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -178,7 +178,6 @@
       request: transaction.request
     @configuration.emitter.emit 'test error', error, test if error

-
   sandboxedHookResultsHandler: (err, data, results = {}, callback) ->
     return callback err if err
     # reference to `transaction` gets lost here if whole object is assigned
@@ -193,7 +192,6 @@
     callback()
     return

-
   sandboxedWrappedCode: (hookCode) ->
     return """
       // run the hook
@@ -209,7 +207,6 @@
       output["logs"] = _logs;
       output;
     """
-

   runSandboxedHookFromString: (hookString, data, callback) ->
     wrappedCode = @sandboxedWrappedCode hookString
@@ -228,7 +225,6 @@
     , (err, result = {}) =>
       sandbox.kill()
       @sandboxedHookResultsHandler err, data, result, callback
-

   # Will be used runHook instead in next major release, see deprecation warning
   runLegacyHook: (hook, data, callback) ->
@@ -265,7 +261,6 @@
     if typeof(hook) == 'string'
       @runSandboxedHookFromString hook, data, callback

-
   runHook: (hook, data, callback) ->
     # not sandboxed mode - hook is a function
     if typeof(hook) == 'function'
@@ -289,29 +284,8 @@
     mediaType = configuration.data[origin.filename]?.mediaType or 'text/vnd.apiblueprint'

     # Parse the server URL (just once, caching it in @parsedUrl)
-    unless @parsedUrl
-      serverUrl = configuration.server
-      unless serverUrl.match(/^https?:\/\//i)
-        # Protocol is missing. Remove any : or / at the beginning of the URL
-        # and prepend the URL with 'http://' (assumed default).
-        serverUrl = 'http://' + serverUrl.replace(/^[:\/]*/, '')
-      @parsedUrl = url.parse(serverUrl)
-
-    # Joins paths regardless of slashes. There may be a nice way in the future:
-    # https://github.com/joyent/node/issues/2216 Note that path.join will fail
-    # on windows, and url.resolve can have undesirable behavior depending
-    # on slashes
-    if @parsedUrl.path is '/'
-      fullPath = request.uri
-    else if not request.uri
-      fullPath = @parsedUrl.path
-    else
-      # Removes all slashes from the beginning and from the end of each segment.
-      # Then joins them together with a single slash. Then prepends the whole
-      # string with a single slash.
-      fullPath = '/' + [@parsedUrl.path, request.uri].map((pathSegment) ->
-        pathSegment.replace(/^\/|\/$/g, '')
-      ).join('/')
+    @parsedUrl ?= @parseServerUrl(configuration.server)
+    fullPath = @getFullPath(@parsedUrl.path, request.uri)

     flatHeaders = flattenHeaders(request['headers'])

@@ -365,6 +339,32 @@
       skip: skip

     return callback(null, configuredTransaction)
+
+  parseServerUrl: (serverUrl) ->
+    unless serverUrl.match(/^https?:\/\//i)
+      # Protocol is missing. Remove any : or / at the beginning of the URL
+      # and prepend the URL with 'http://' (assumed as default fallback).
+      serverUrl = 'http://' + serverUrl.replace(/^[:\/]*/, '')
+    url.parse(serverUrl)
+
+  getFullPath: (serverPath, requestPath) ->
+    return requestPath if serverPath is '/'
+    return serverPath unless requestPath
+
+    # Join two paths
+    #
+    # How:
+    # Removes all slashes from the beginning and from the end of each segment.
+    # Then joins them together with a single slash. Then prepends the whole
+    # string with a single slash.
+    #
+    # Why:
+    # Note that 'path.join' won't work on Windows and 'url.resolve' can have
+    # undesirable behavior depending on slashes.
+    # See also https://github.com/joyent/node/issues/2216
+    segments = [serverPath, requestPath]
+    segments = (segment.replace(/^\/|\/$/g, '') for segment in segments)
+    return '/' + segments.join('/')

   emitResult: (transaction, callback) ->
     # if transaction test was executed and was not skipped or failed


Prefixed variables in sandboxed env, more generic hooks runners

--- src/transaction-runner.coffee
+++ src/transaction-runner.coffee
@@ -85,14 +85,12 @@
     @configuration.emitter.emit 'test error', error, test if error

   # Will be used runHook instead in next major release, see deprecation warning
-  runLegacyHook: (hook, transactions, callback) ->
+  runLegacyHook: (hook, data, callback) ->
     # not sandboxed mode - hook is a function
     if typeof(hook) == 'function'
       if hook.length is 1
         # sync api

-        # TODO: if it throw an exception here, nothing heppens probably,
-        # replicate, mitigate
         logger.warn "DEPRECATION WARNING!"
         logger.warn "You are using only one argument for the `beforeAll` or `afterAll` hook function."
         logger.warn "One argument hook functions will be treated as synchronous in next major release."
@@ -105,23 +103,28 @@
         logger.warn " - Second passed argument will be a optional callback function for async"
         logger.warn " - `transactions` object in `hooks` module object will be removed"
         logger.warn " - Manipulation of transactions will have to be performed on first function argument"
+
+        # DEPRECATION WARNING
+        # this will not be supported in future
+        # hook function will be called with data synchronously and
+        # callbeck will be called immediatelly and not passed as a second argument
         hook callback

       else if hook.length is 2
         # async api
-        hook transactions, ->
+        hook data, ->
           callback()

     # sandboxed mode - hook is a string - only sync API
     if typeof(hook) == 'string'
       wrappedCode = """
       // run the hook
-      var func = #{hook}
-      func(transactions)
+      var _func = #{hook}
+      _func(_data)

       // setup the return object
       var output = {};
-      output["transactions"] = transactions;
+      output["data"] = _data;
       output["stash"] = stash;
       output;
       """
@@ -130,27 +133,27 @@
         timeout: 500
       }

-      pitboss.run {context: {transactions: transactions, stash: @hookStash}, libraries: ['console']}, (err, result) =>
+      pitboss.run {context: {"_data": data, stash: @hookStash}, libraries: ['console']}, (err, result) =>
         return callback(err) if err
         # reference to `transaction` get lost here if whole object is assigned
         # this is wokraround how to copy proprties
         # clone doesn't work either
-        for key, value of result.transactions
-          transactions[key] = value
+        for key, value of result.data
+          data[key] = value
         @hookStash = result.stash
         callback()

-  runHook: (hook, transaction, callback) ->
+  runHook: (hook, data, callback) ->
     # not sandboxed mode - hook is a function
     if typeof(hook) == 'function'
       if hook.length is 1
         # sync api
-        hook transaction
+        hook data
         callback()
       else if hook.length is 2
         # async api
-        hook transaction, ->
+        hook data, ->
           callback()

     # sandboxed mode - hook is a string - only sync API
@@ -158,12 +161,12 @@

       wrappedCode = """
       // run the hook
-      var func = #{hook}
-      func(transaction)
+      var _func = #{hook}
+      _func(_data)

       // setup the return object
       var output = {};
-      output["transaction"] = transaction;
+      output["data"] = _data;
       output["stash"] = stash;
       output;
       """
@@ -172,13 +175,13 @@
         timeout: 500
       }

-      pitboss.run {context: {transaction: transaction, stash: @hookStash}, libraries: ['console']}, (err, result) =>
+      pitboss.run {context: {"_data": data, stash: @hookStash}, libraries: ['console']}, (err, result) =>
         return callback(err) if err
         # reference to `transaction` get lost here if whole object is assigned
         # this is wokraround how to copy proprties
         # clone doesn't work either
-        for key, value of result.transaction
-          transaction[key] = value
+        for key, value of result.data
+          data[key] = value
         @hookStash = result.stash
         callback()


Add Editor#insertFiles

Add Editor#insertFiles

--- src/trix/models/editor.coffee
+++ src/trix/models/editor.coffee
@@ -43,6 +43,9 @@

   insertFile: (file) ->
     @composition.insertFile(file)
+
+  insertFiles: (files) ->
+    @composition.insertFiles(files)

   insertHTML: (html) ->
     @composition.insertHTML(html)


Fix saveTerminalState and restoreTerminalState

--- app/assets/javascripts/player/vt.js.coffee
+++ app/assets/javascripts/player/vt.js.coffee
@@ -344,14 +344,14 @@
     @updateScreen()

   saveTerminalState: ->
-    @saveCursor()
+    @buffer.saveCursor()
     @scrollRegion.save()
-    @saveBrush()
+    @buffer.saveBrush()

   restoreTerminalState: ->
-    @restoreBrush()
+    @buffer.restoreBrush()
     @scrollRegion.restore()
-    @restoreCursor()
+    @buffer.restoreCursor()

   reportRowAndColumn: ->


Icons can be resized

Icons can be resized

--- models/icon.coffee
+++ models/icon.coffee
@@ -3,9 +3,11 @@
 sd = require('sharify').data
 Icon = require './icon.coffee'
 { Image } = require 'artsy-backbone-mixins'
+ImageSizes = require './mixins/image_sizes.coffee'

 module.exports = class Icon extends Backbone.Model
   _.extend @prototype, Image(sd.SECURE_IMAGES_URL)
+  _.extend @prototype, ImageSizes

   @DefaultUserIconUrl: "/images/user_profile.png"


Path updated to fix issues with SVG Arcs

--- lib/path.coffee
+++ lib/path.coffee
@@ -310,12 +310,12 @@

     th_half = 0.5 * (th1 - th0)
     t = (8 / 3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half)
-    x1 = cx + Math.cos(th0) - t * Math.sin(th0)
-    y1 = cy + Math.sin(th0) + t * Math.cos(th0)
-    x3 = cx + Math.cos(th1)
-    y3 = cy + Math.sin(th1)
-    x2 = x3 + t * Math.sin(th1)
-    y2 = y3 - t * Math.cos(th1)
+    x1 = fixRoundingError(cx + Math.cos(th0) - t * Math.sin(th0))
+    y1 = fixRoundingError(cy + Math.sin(th0) + t * Math.cos(th0))
+    x3 = fixRoundingError(cx + Math.cos(th1))
+    y3 = fixRoundingError(cy + Math.sin(th1))
+    x2 = fixRoundingError(x3 + t * Math.sin(th1))
+    y2 = fixRoundingError(y3 - t * Math.cos(th1))

     return [
       a00 * x1 + a01 * y1,   a10 * x1 + a11 * y1,
@@ -323,4 +323,9 @@
       a00 * x3 + a01 * y3,   a10 * x3 + a11 * y3
     ]

+  fixRoundingError = (x) ->
+    if Math.abs(Math.round(x) - x) < 0.0000000000001
+      return Math.round(x);
+    return x
+
 module.exports = SVGPath


can't update a team when teams are maxed out http://nodeknockout.hoptoadapp.com/errors/9666905

--- models/team.coffee
+++ models/team.coffee
@@ -34,7 +34,7 @@
 TeamSchema.static 'canRegister', (next) ->
   Team.count null, (err, count) ->
     return next err if err
-    max = 201 # because team fortnight labs doesn't count
+    max = 201 # +1 because team fortnight labs doesn't count
     next null, count < max, max - count

 # min people validation
@@ -48,6 +48,7 @@

 # max teams
 TeamSchema.pre 'save', (next) ->
+  return next() unless @isNew
   Team.canRegister (err, yeah) =>
     return next err if err
     if yeah


Fix: Don't show empty hash in url

Fix: Don't show empty hash in url

--- lib/assets/javascripts/routers/main.js.coffee
+++ lib/assets/javascripts/routers/main.js.coffee
@@ -8,7 +8,7 @@
   resetScrollPosition: =>
     hash_fragment = window.location.hash
     window.scrollTo(0, 0)
-    window.location.hash = hash_fragment
+    window.location.hash = hash_fragment unless hash_fragment == '#'

   root: =>
     @navigate('/profile', { replace: true, trigger: true })


Show the category and the doc.perl6.org url for the word that was CTRL-clicked

--- lib/atom-perl6-editor-tools-hyperclick.coffee
+++ lib/atom-perl6-editor-tools-hyperclick.coffee
@@ -1,23 +1,52 @@
+
+fs   = require 'fs'
+http = require 'http'

 # This provides hyperclick support for Perl 6 using hyperclick
 # Please see https://atom.io/packages/hyperclick
 module.exports =
 class Perl6HyperclickProvider

+  keywords = []
+
+  constructor: () ->
+    self = this
+    #TODO no hardcoding
+    fs.readFile('/home/azawawi/atom-perl6-editor-tools/lib/index.json', 'utf8', (err, data) ->
+      throw err if err
+      recs = JSON.parse(data)
+      keywords = {}
+      for rec in recs
+        key = rec["value"]
+        keywords[key] =
+          category: rec["category"]
+          url: rec["url"]
+      self.keywords = keywords
+      atom.notifications.addInfo("Done reading index.json!")
+    )
+
   providerName:  'perl6-hyperclick'

-  getSuggestionForWord: (editor, text, range) ->
+  getSuggestionForWord: (editor, word, range) ->
     # Make sure the editor are valid Perl 6 editors
     return unless editor?
     grammar = editor.getGrammar()
     return unless grammar? && grammar.scopeName? && grammar.scopeName.startsWith("source.perl6")

+    o = @keywords[word]
+    helpText = "Help not found for #{word}"
+    if o?
+      url = "http://doc.perl6.org#{o.url}"
+      helpText = """
+        Category:  #{o.category}
+
URL:  #{url}
+      """
+
     return {
       range: range,
       callback: () ->
         options =
-          detail: "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
           dismissable: true
-        atom.notifications.addInfo("p6doc #{text}", options)
+        atom.notifications.addInfo(helpText, options)
         return
     }


Remove log

Remove log

--- widgets/fullpie/fullpie.coffee
+++ widgets/fullpie/fullpie.coffee
@@ -1,7 +1,6 @@
 class Dashing.Fullpie extends Dashing.Widget

   onData: (data) ->
-    console.log data
     @render(data.value)

   render: (data) ->


android active element is the body

android active element is the body

--- src/selection.coffee
+++ src/selection.coffee
@@ -12,7 +12,10 @@
     @nullDelay = false

   checkFocus: ->
-    return @document.activeElement == @doc.root and document.activeElement == @iframe
+    return false unless @document.activeElement == @doc.root
+    if document.activeElement? and document.activeElement.tagName == 'IFRAME'
+      return document.activeElement == @iframe
+    return true

   getRange: ->
     return null unless this.checkFocus()


Fix the highlighting of documentation comments


Using notification audo from config

Using notification audo from config

--- index.cjsx
+++ index.cjsx
@@ -551,8 +551,9 @@
                 tmpShip = tmpShip + _ships[combinedInfo[i]].api_name + " "
             if tmpShip != "" and @enableProphetDamaged
               notify "#{tmpShip}" + __('Heavily damaged'),
-                type: 'damagedAudio'
+                type: 'damaged'
                 icon: join(ROOT, 'views', 'components', 'main', 'assets', 'img', 'state', '4.png')
+                audio: config.get('plugin.prophet.notify.damagedAudio')
             if body.api_get_ship?
               getShip = body.api_get_ship
             if body.api_get_useitem?


add componenetWillUnmount

add componenetWillUnmount

--- index.cjsx
+++ index.cjsx
@@ -642,6 +642,9 @@

     componentDidMount: ->
       window.addEventListener 'game.response', @handleResponse
+
+    componentWillUnmount: ->
+      window.removeEventListener 'game.response', @handleResponse

     getCompassAngle: ->
       {MAPSPOT, mapArea, mapCell, nowSpot, nextSpot} = @state


Revert "raise battle.result event"

Revert "raise battle.result event"

--- index.cjsx
+++ index.cjsx
@@ -493,15 +493,6 @@
             getShip = null
           formationFlag = true
           result = body.api_win_rank
-          event = new CustomEvent 'battle.result',
-            bubbles: true
-            cancelable: true
-            detail:
-              isCombined: false
-              nowHp: @state.nowHp
-              enemyId: body.api_ship_id
-              rank: body.api_win_rank
-          window.dispatchEvent event
         when '/kcsapi/api_port/port'
           flag = true
           _deck = window._decks[deckId]


Denorm test bugfix

Denorm test bugfix

--- tests/denorm-tests.coffee
+++ tests/denorm-tests.coffee
@@ -267,28 +267,10 @@
         )

 if Meteor.isServer then Tinytest.add "ResetWatchers - be smart about projections", (test) ->
-    fooWatcherWatchers = [
-        # `foo`s with `foo.a == 1`:
-        'selectA',                 # gets the `foo.a` and `foo.c` fields.
-        'selectA_projectA',        # gets only the `foo.a` field.
-        'selectA_projectC',        # gets only the `foo.c` field.
-        'selectA_projectNothing',  # gets none of the fields.
-
-        # `foo`s with `foo.b in [100, 101]`:
-        'selectB',                 # gets the `foo.a` and `foo.c` fields.
-        'selectB_projectB',        # gets only the `foo.b` fields.
-
-        # `foo`s with `foo.c in [100, 101]`:
-        'selectC'                  # gets the `foo.a` and `foo.c` fields.
-    ]
-
     # Accesses each reactive of a `fooWatcher`
     # to make sure that it starts watching `foo`.
     touchFooWatcher = (fw) ->
-        fw.selectA()
-        fw.selectA_projectA()
-        fw.selectA_projectC()
-        fw.selectA_projectNothing()
+        fw.get reactiveName for reactiveName, reactiveSpec of $$.FooWatcher.reactiveSpecs

     # Check that a `fooWatcher` resets the
     # watchers it should and does not reset
@@ -298,12 +280,24 @@
     #     #   `FooWatcher` instance,
     #     #   array of watchers that should be reset
     #     # )
+    #        # `foo`s with `foo.a == 1`:
+    #        'selectA',                 # gets the `foo.a` and `foo.c` fields.
+    #        'selectA_projectA',        # gets only the `foo.a` field.
+    #        'selectA_projectC',        # gets only the `foo.c` field.
+    #        'selectA_projectNothing',  # gets none of the fields.
+    #
+    #        # `foo`s with `foo.b in [100, 101]`:
+    #        'selectB',                 # gets the `foo.a` and `foo.c` fields.
+    #        'selectB_projectB',        # gets only the `foo.b` fields.
+    #
+    #        # `foo`s with `foo.c in [100, 101]`:
+    #        'selectC'                  # gets the `foo.a` and `foo.c` fields.
     checkFooWatcherReset = (fw, reset) ->
-        for f in fooWatcherWatchers
-            if f in reset
-                test.isTrue _wasReset 'FooWatcher', fw._id, f
+        for reactiveName of $$.FooWatcher.reactiveSpecs
+            if reactiveName in reset
+                test.isTrue _wasReset 'FooWatcher', fw._id, reactiveName
             else
-                test.isFalse _wasReset 'FooWatcher', fw._id, f
+                test.isFalse _wasReset 'FooWatcher', fw._id, reactiveName

     foo = new $$.Foo


Read version from package.json

Read version from package.json

--- index.cjsx
+++ index.cjsx
@@ -1,9 +1,11 @@
 {_, SERVER_HOSTNAME} = window
 Promise = require 'bluebird'
+path = require 'path-extra'
+request = Promise.promisifyAll require 'request'
+_package = require('./package.json')
 async = Promise.coroutine
-request = Promise.promisifyAll require 'request'
-path = require 'path-extra'
-REPORTER_VERSION = '2.6.0'
+REPORTER_USERAGENT = "Reporter v#{_package.version}"
+console.debug(REPORTER_USERAGENT)

 __ = window.i18n["poi-plugin-report"].__.bind(window.i18n["poi-plugin-report"])

@@ -49,7 +51,7 @@
       form:
         data: JSON.stringify info
       headers:
-        'User-Agent': "Reporter v#{REPORTER_VERSION}"
+        'User-Agent': REPORTER_USERAGENT
   catch err
     console.error err

@@ -82,7 +84,7 @@
                 form:
                   data: JSON.stringify info
                 headers:
-                  'User-Agent': "Reporter v#{REPORTER_VERSION}"
+                  'User-Agent': REPORTER_USERAGENT
             catch err
               console.error err
       # Create ship report
@@ -109,7 +111,7 @@
             form:
               data: JSON.stringify detail
             headers:
-              'User-Agent': "Reporter v#{REPORTER_VERSION}"
+              'User-Agent': REPORTER_USERAGENT
         catch err
           console.error err
       # Create item report
@@ -126,7 +128,7 @@
             form:
               data: JSON.stringify info
             headers:
-              'User-Agent': "Reporter v#{REPORTER_VERSION}"
+              'User-Agent': REPORTER_USERAGENT
         catch err
           console.error err
       # Remodel item report
@@ -156,7 +158,7 @@
             form:
               data: JSON.stringify info
             headers:
-              'User-Agent': "Reporter v#{REPORTER_VERSION}"
+              'User-Agent': REPORTER_USERAGENT
         catch err
           console.error err
       # Drop ship report
@@ -180,7 +182,7 @@
             form:
               data: JSON.stringify info
             headers:
-              'User-Agent': "Reporter v#{REPORTER_VERSION}"
+              'User-Agent': REPORTER_USERAGENT
         catch err
           console.error err
       when '/kcsapi/api_req_map/start', '/kcsapi/api_req_map/next'