void Object.frozen

void Object.frozen

--- src/changeDetector.coffee
+++ src/changeDetector.coffee
@@ -339,10 +339,6 @@
         (new Date()).getTime()

-isFrozen = Object.isFrozen or ->
-    false
-
-
 notEqual = (a, b) ->
     if a is null or b is null
         return true
@@ -380,25 +376,15 @@
                     a1 = f$.isArray value
                     if a0 is a1
                         if a0
-                            if isFrozen last
+                            if notEqual last, value
                                 mutated = true
-                            else
-                                if notEqual last, value
-                                    mutated = true
-                            if mutated
-                                if isFrozen value
-                                    w.value = value
-                                else
-                                    w.value = value.slice()
+                                w.value = value.slice()
                     else
                         mutated = true
-                        if not a1
+                        if a1
+                            w.value = value.slice()
+                        else
                             w.value = null
-                    if mutated and a1
-                        if isFrozen value
-                            w.value = value
-                        else
-                            w.value = value.slice()
                 else if w.deep
                     if not alight.utils.equal last, value
                         mutated = true


remove remainders of clearAccessToken

remove remainders of clearAccessToken

--- src/client/channel.coffee
+++ src/client/channel.coffee
@@ -64,7 +64,6 @@
       remote    :
         message        : {},
         ready          : {},
-        clearAccessToken : {},
         refreshUser    : {}
       local:
         message         : @onMessage


only show hint if player got less than 3 stars

--- src/layout/mixins/done_dialog.coffee
+++ src/layout/mixins/done_dialog.coffee
@@ -29,6 +29,12 @@

   next = (world) -> window.open world.get('next_url'), '_self'

+  star_count = ->
+    metrics
+      .map((key) -> level.get(key) <= goals[key])
+      .filter((key) -> key == true)
+      .length
+
   star_for = (key) ->
     return star.empty if level.get(key) > goals[key]
     star.fill
@@ -45,14 +51,17 @@

   share_message = "I programmed a turtle to solve this level on the Hour of NODE. Can you?"

-  hint = 'Hint: Use fewer clicks, bricks, or ticks to improve your score'
   messages = [
     { class: 'stars', name: star_string() }
     { class: 'metrics', name: metrics.join ' | ' }
     { class: 'message', name: level.get('message') }
     { class: 'share-button', name: share_message }
-    { class: 'hint', hint }
+    { class: 'publish' }
   ]
+
+  if star_count() < metrics.length
+    hint = 'Hint: Use fewer clicks, bricks, or ticks to improve your score'
+    messages.push class: 'hint', name: hint

   if level.get('completion', false)? && level.get('last_level')
     messages.push


Setup programs

Setup programs

--- src/config/sprites.coffee
+++ src/config/sprites.coffee
@@ -15,11 +15,12 @@
     dict = {_LABEL: args.name, _EXPORTS: SPRITE_EXPORTS}
     child = world.add_child dict
     child.put my.key.authority, world.make_world(args)
-    world.call 'reprogram', args
+    world.call 'setup_programs', args.programs
     world.send 'inspect', child
-  reprogram: (world, args) ->
-    setter = (value) -> world.put 'programs', value
-    world.send 'programs', args.programs, setter
+  setup_programs: (world, programs) ->
+    world.send 'format_programs', programs, (value) ->
+      world.send 'store_programs', value, (value) ->
+        world.put 'programs', value

   p: [0, 0]
   v: [1, 0]


style consistency changes, double quotes and string intepolation

--- app/assets/javascripts/admin/views/task/task_overview_view.coffee
+++ app/assets/javascripts/admin/views/task/task_overview_view.coffee
@@ -152,7 +152,7 @@

     @ui.dateRangeInput.daterangepicker(
       locale:
-        format: 'L'
+        format: "L"
       startDate: moment().subtract(@DEFAULT_TIME_PERIOD_TIME, @DEFAULT_TIME_PERIOD_UNIT).format("L")
       endDate: moment().format("L")
       opens: "left"
@@ -186,7 +186,7 @@
         modelValue: -> return "#{@model.get("name")}"
       name: "team"
       events:
-        change: 'teamChanged'
+        change: "teamChanged"
     )

     @teamRegion.show(teamSelectionView)
@@ -215,7 +215,7 @@
         range: minMaxHours
       )

-      sliderEl.noUiSlider.on('update', (values, handle) =>
+      sliderEl.noUiSlider.on("update", (values, handle) =>
         @chosenMinHours = Math.round(+values[0])
         @chosenMaxHours = Math.round(+values[1])
         @ui.rangeSliderLabel1.html("#{@chosenMinHours}h")
@@ -281,19 +281,19 @@

   buildGraph : ->

-    width  = $('.graph').width() - @OPTIONS_MARGIN - $('.overview-options').width()
+    width  = $(".graph").width() - @OPTIONS_MARGIN - $(".overview-options").width()
     height = $(window).height() - 50 - $(".graph").offset().top

-    @svg = d3.select('.graph')
+    @svg = d3.select(".graph")
       .html("")
-      .append('svg')
-      .attr('width', width)
-      .attr('height', height)
-
-    @container = @svg.append("g")
-
-    @svgEdges = @container.append('svg:g').selectAll('path')
-    @svgNodes = @container.append('svg:g').selectAll('g')
+      .append("svg")
+      .attr("width", width)
+      .attr("height", height)
+
+    @container = @svg.append("svg:g")
+
+    @svgEdges = @container.append("svg:g").selectAll("path")
+    @svgNodes = @container.append("svg:g").selectAll("g")

     @setupPanAndZoom()

@@ -323,36 +323,36 @@

     # append the svg path elements
     @svgEdges = @svgEdges.data(edges)
-      .enter().append('svg:path')
-      .attr('class', 'link')
-      .attr('stroke', (d) -> d.color )
-      .attr('stroke-dasharray', (d) => if d.color is @FUTURE_TASK_EDGE_COLOR then '10,10')
+      .enter().append("svg:path")
+      .attr("class", "link")
+      .attr("stroke", (d) -> d.color )
+      .attr("stroke-dasharray", (d) => if d.color is @FUTURE_TASK_EDGE_COLOR then "10,10")

     # append the container for the svg node elements
     @svgNodes = @svgNodes.data(nodes, (d) -> d.id)
-    svgNodesContainer = @svgNodes.enter().append('svg:g')
-      .attr('id', (d) -> d.id )
+    svgNodesContainer = @svgNodes.enter().append("svg:g")
+      .attr("id", (d) -> d.id )

     # add the label to the svg node container
-    svgNodesContainer.append('svg:text')
-      .attr('class', 'id')
+    svgNodesContainer.append("svg:text")
+      .attr("class", "id")
       .text( (d) -> d.text )
       .each( (d) ->
         d.width = @getBBox().width + TEXT_PADDING
         d.height = RECT_HEIGHT
       )
-      .attr('x', (d) -> d.width / 2 )
-      .attr('y', RECT_HEIGHT / 2)
+      .attr("x", (d) -> d.width / 2 )
+      .attr("y", RECT_HEIGHT / 2)

     # add the rectangle to the svg node container
-    svgNodesContainer.insert('svg:rect', ':first-child')
-      .attr('class', 'node')
-      .attr('width', (d) -> d.width )
-      .attr('height', RECT_HEIGHT)
-      .attr('rx', (d) -> if d.type is "user" then 3 else 10 )
-      .attr('ry', (d) -> if d.type is "user" then 3 else 10 )
-      .style('fill', (d) -> if d.color then d.color else 'white')
-      .style('stroke', (d) -> if d.color then d3.rgb(d.color).darker().toString() else 'black')
+    svgNodesContainer.insert("svg:rect", ":first-child")
+      .attr("class", "node")
+      .attr("width", (d) -> d.width )
+      .attr("height", RECT_HEIGHT)
+      .attr("rx", (d) -> if d.type is "user" then 3 else 10 )
+      .attr("ry", (d) -> if d.type is "user" then 3 else 10 )
+      .style("fill", (d) -> if d.color then d.color else "white")
+      .style("stroke", (d) -> if d.color then d3.rgb(d.color).darker().toString() else "black")

     @zoomOnce = _.once(=> @zoomToFitScreen())

@@ -365,7 +365,7 @@
       .symmetricDiffLinkLengths(200)
       .avoidOverlaps(true)
       .convergenceThreshold(0.10)
-      .on('tick', @tick.bind(@))
+      .on("tick", @tick.bind(@))

     # unconstrained, user-constrained, overlap-constrained iterations
     @force.start(15, 0, 10)
@@ -380,7 +380,7 @@

     # update the position of the edges
     # distribute the start and end point on the x-axis depending on their direction
-    @svgEdges.attr('d', (d) ->
+    @svgEdges.attr("d", (d) ->
       deltaX = d.target.x - d.source.x
       deltaY = d.target.y - d.source.y
       dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
@@ -391,12 +391,12 @@
       sourceY = d.source.y
       targetX = d.target.x - (targetPadding * normX)
       targetY = d.target.y
-      return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY
+      return "M#{sourceX},#{sourceY}L#{targetX},#{targetY}"
     )

     # update the position of the nodes
-    @svgNodes.attr('transform', (d) ->
-      return 'translate(' + (d.x - d.width / 2) + ',' + (d.y - d.height / 2) + ')'
+    @svgNodes.attr("transform", (d) ->
+      return "translate(#{d.x - d.width / 2},#{d.y - d.height / 2})"
     )

     # this will only be called after the first tick
@@ -469,7 +469,7 @@
     @zoom = d3.behavior.zoom()
       .scaleExtent([0.1, 10])
       .on("zoom", =>
-        @container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")")
+        @container.attr("transform", "translate(#{d3.event.translate})scale(#{d3.event.scale})")
       )

     @svg.call(@zoom)
@@ -483,7 +483,7 @@
         html: true,
         trigger: "hover",
         content: @createUserTooltip(user),
-        container: 'body'
+        container: "body"
       )
     )


Add specs for none selected and some selected callbacks.

--- spec/javascripts/thumb_selector_spec.coffee
+++ spec/javascripts/thumb_selector_spec.coffee
@@ -3,7 +3,7 @@
     loadFixtures 'thumb_selector.html'
     window.thumb_selector_init()

-  describe 'inidividual selection', ->
+  describe 'individual selection', ->
     it 'gives its parents the selected class when checked', ->
       $('#check1').prop('checked', true).trigger('change')

@@ -36,6 +36,13 @@
     expect($('#check2')).not.toBeChecked()
     expect($('#check3')).not.toBeChecked()

-  xit 'disables disable-none-selected when none are selected'
+  it 'disables disable-none-selected when none are selected', ->
+    $('#div1').click()
+    $('.select-none').click()

-  xit 'enables enable-some-selected when some are selected'
+    expect($('.disable-none-selected')).toHaveClass('disabled')
+
+  it 'enables enable-some-selected when some are selected', ->
+    $('#div1').click()
+    expect($('.enable-some-selected')).not.toHaveClass('disabled')
+


err, response

err, response

--- routes/lib/speaker.coffee
+++ routes/lib/speaker.coffee
@@ -23,6 +23,7 @@

   respon = (err, response) ->
     if err?
+      console.log(err, response)
       setTimeout ()->
         this.body = thebody
         speaker(message,channel)


latex editor: fix horiz scroll

latex editor: fix horiz scroll

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -2091,7 +2091,7 @@
         if opts.delta?
             if not @zoom_width?
                 @zoom_width = 160   # NOTE: hardcoded also in editor.css class .salvus-editor-pdf-preview-image
-            max_width = @zoom_width#images.css('max-width')
+            max_width = @zoom_width
             max_width += opts.delta
         else if opts.width?
             max_width = opts.width
@@ -2104,6 +2104,13 @@
                 'max-width'   : max_width
                 width         : max_width
             @scroll_into_view(n : n, highlight_line:false, y:$(window).height()/2)
+
+        @recenter()
+
+    recenter: () =>
+        container_width = @output.find(":first-child:first").width()
+        content_width = @output.find("img:first-child:first").width()
+        @output.scrollLeft((content_width - container_width)/2)

     watch_scroll: () =>
         if @_f?
@@ -2260,6 +2267,7 @@
                 @last_page = n-1
         else
             # update page
+            recenter = (@last_page == 0)
             that = @
             page = @output.find(".salvus-editor-pdf-preview-page-#{n}")
             if page.length == 0
@@ -2324,14 +2332,16 @@
                 setTimeout((()->img.attr('src',url)), 2000)
             img.on('error', load_error)

+            if recenter
+                img.one 'load', () =>
+                    @recenter()
+
             if @zoom_width?
                 max_width = @zoom_width
-                margin_left = "#{-(max_width-100)/2}%"
                 max_width = "#{max_width}%"
                 img.css
                     'max-width'   : max_width
                     width         : max_width
-                    'margin-left' : margin_left

             #page.find(".salvus-editor-pdf-preview-text").text(p.text)
         cb()


add haskell editor syntax highlighting support.

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -44,6 +44,8 @@
     f90    : 'text/x-fortran'
     f95    : 'text/x-fortran'
     h      : 'text/x-c++hdr'
+    hs     : 'text/x-haskell'
+    lhs    : 'text/x-haskell'
     html   : 'htmlmixed'
     java   : 'text/x-java'
     jl     : 'text/x-julia'
@@ -53,11 +55,9 @@
     md     : 'markdown'
     mysql  : 'text/x-sql'
     patch  : 'text/x-diff'
-
     gp     : 'text/pari'
     go     : 'text/x-go'
     pari   : 'text/pari'
-
     php    : 'php'
     py     : 'python'
     pyx    : 'python'
@@ -1053,6 +1053,8 @@
             sync_interval     : 750    # minimum time (in ms) between synchronizing text with hub. -- used in sync version below

             completions_size  : 20    # for tab completions (when applicable, e.g., for sage sessions)
+
+        #console.log("mode =", opts.mode)

         @project_id = @editor.project_id
         @element = templates.find(".salvus-editor-codemirror").clone()


Fixed marko indent

Fixed marko indent

--- src/beautifiers/marko-beautifier.coffee
+++ src/beautifiers/marko-beautifier.coffee
@@ -17,12 +17,13 @@

       indent = ''

-      for i in [0...indent_size - 1] by 1
+      for i in [0..indent_size - 1]
         indent += indent_char

       prettyprintOptions =
         syntax : options.syntax
         filename: if context.filePath then context.filePath else require.resolve('marko-prettyprint')
+        indent: indent

       try
         resolve(markoPrettyprint(text, prettyprintOptions))


Fixed typos, handling all connection errors in apiary reporter

--- src/reporters/apiary-reporter.coffee
+++ src/reporters/apiary-reporter.coffee
@@ -8,6 +8,8 @@

 packageConfig = require './../../package.json'
 logger = require './../logger'
+
+CONNECTION_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE']

 class ApiaryReporter
   constructor: (emitter, stats, tests, config, runner) ->
@@ -130,11 +132,10 @@
       data = @_transformTestToReporter test
       data.result = 'error'

-      connectionErrors = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE']
-      if connectionErrors.indexOf(error.code) > -1
+      if CONNECTION_ERRORS.indexOf(error.code) > -1
         data.resultData.errors.push "Error connecting to server under test!"
       else
-        data.resultData.errors.push "Unhandled error occured when executing the transaciton."
+        data.resultData.errors.push "Unhandled error occured when executing the transaction."

       path = '/apis/' + @configuration['apiSuite'] + '/tests/steps?testRunId=' + @remoteId
       @_performRequest path, 'POST', data, (error, response, parsedBody) ->
@@ -183,7 +184,7 @@
         realResponse: test['actual']
         expectedResponse: test['expected']
         result: test['results']
-        warinings: []
+        warnings: []
         errors: []

     return data
@@ -251,7 +252,7 @@

       req.on 'error', (error) =>
         @serverError = true
-        if error.code == 'ECONNREFUSED'
+        if CONNECTION_ERRORS.indexOf(error.code) > -1
           logger.error "Apiary reporter: Error connecting to Apiary test reporting API."
           callback()
         else


fix error logger

fix error logger

--- src/scripts/idcf-vm-auto-stop.coffee
+++ src/scripts/idcf-vm-auto-stop.coffee
@@ -45,8 +45,8 @@
       message = machines.map((i) -> i.displayname).join '\n'
       robot.messageRoom config.room, message
     .catch (e) ->
-      robot.logger.error 'hubot-idcf-vm: error'
+      robot.logger.error 'hubot-idcf-vm-auto-stop: error'
       robot.logger.error e
-      res.send 'hubot-idcf-vm: error'
+      res.send 'hubot-idcf-vm-auto-stop: error'

   new CronJob config.cron, action, null, true, 'Asia/Tokyo'


Fix handoff error if last listener was lost before sending

When a handoff starts, each slave puts together an array of listeners that
are "boarding"—waiting to be sent to the new process.  When it gets an ack
that the listener arrived, it runs a function to send the next listener out
of that list.  When there are no more listeners waiting to board, the ack
function triggers the callback.

If a listener has disconnected in the time between assembling the list and its
turn for flight, the sending code skips the listener and moves on to the next
one.

The bug was that if the last listener in the boarding line had dropped, the
ack function never got a chance to trigger the callback.  This fix adds an
additional spot to trigger the callback in that situation.

--- src/streammachine/slave/index.coffee
+++ src/streammachine/slave/index.coffee
@@ -254,12 +254,16 @@
                             @_inflight = lobj.opts.key

                         else
-                            @log.debug "Lost listener #{lobj.opts.id} during taxi. Moving on."
+                            @log.info "Lost listener #{lobj.opts.id} during taxi. Moving on."
                             sFunc()

             else
                 # all done!
                 @log.info "Last listener is in flight."
+
+                if !@_inflight
+                    @log.info "All listeners are arrived or lost."
+                    cb?()

         # -- register a listener for acks -- #


fix runtime error caused by parsing https://github.com/artillery/universe/pull/2630

--- prviewer.coffee
+++ prviewer.coffee
@@ -255,6 +255,7 @@
       reviewers = {}

       # Extract reviewers from pull title.
+      pull.displayTitle = pull.title
       if match = pull.title.match /^([\w\/]+): /
         # Strip the names out of the title.
         pull.displayTitle = pull.title.substr match[0].length


Removed dead code

Removed dead code

--- core/app/backbone/views/evidence/react_add_comment.coffee
+++ core/app/backbone/views/evidence/react_add_comment.coffee
@@ -62,7 +62,6 @@
           storageKey: "add_comment_to_fact_#{@model().fact.id}"
           onChange: @_onTextareaChange
           onSubmit: @_submit
-          focus: @props.focus
         _div [
           'add-comment-controls'
           'add-comment-controls-visible' if @state.controlsOpened


cleaned up

cleaned up

--- platform/src/app.coffee
+++ platform/src/app.coffee
@@ -131,8 +131,6 @@
   $("#resetButton").on "click", ->
     currentFrame = 0
     currentModel = currentModel.atVersion(0)
-    #TODO remove all console.log
-    console.log("setupButtonHandlers... will call currentLogger.logWarning")
     currentLogger.logWarning("reset button: on click;")
     resetConsoleContent()

@@ -302,8 +300,6 @@

 # Reset console content
 resetConsoleContent = ->
-  #TODO remove all console.log
-  console.log("resetConsoleContent... will call currentLogger.logWarning")
   currentLogger.logWarning("resetConsoleContent")
   editors.console.setValue("");
   editors.console.selection.clearSelection();
@@ -322,8 +318,6 @@

   prefixedLog = (prefix) ->
     (text) ->
-      #TODO remove all console.log
-      console.log(prefix+": "+text)
       editors.console.selection.clearSelection()
       editors.console.navigateFileEnd()
       editors.console.insert(prefix+": "+text)
@@ -336,9 +330,7 @@

   resetConsoleContent()

-  #TODO remove all console.log
-  console.log("Main: document.ready, preparing editors.console... will call currentLogger.logWarning")
-  currentLogger.logWarning("Main: document.ready, preparing editors.console")
+  currentLogger.logWarning("Main: document ready, prepared console")

   # A hash needs to be set, or we won't be able to load the code
   if not window.location.hash then window.location.hash = "optics"


during play, editors are now read-only

during play, editors are now read-only

--- platform/src/app.coffee
+++ platform/src/app.coffee
@@ -110,19 +110,21 @@
   # TODO: lock code down in play mode so it can't be changed

   $("#playButton").on "click", ->
-    if $(this).text() == "Play"
+    if isPlaying
+      isPlaying = false
+      for editorId, editor of editors then editor.setReadOnly(false)
+      $(this).button "option",
+        label: "Play"
+        icons:
+          primary: "ui-icon-play"
+    else
       isPlaying = true
+      for editorId, editor of editors then editor.setReadOnly(true)
       handleAnimation()
       $(this).button "option",
         label: "Pause"
         icons:
           primary: "ui-icon-pause"
-    else
-      isPlaying = false
-      $(this).button "option",
-        label: "Play"
-        icons:
-          primary: "ui-icon-play"

   $("#resetButton").on "click", ->
     currentFrame = 0


Show id value in job modal

Show id value in job modal

--- app/assets/javascripts/sidekiq/monitor/abstract_jobs_table.js.coffee
+++ app/assets/javascripts/sidekiq/monitor/abstract_jobs_table.js.coffee
@@ -120,6 +120,10 @@

+              ID
+              #{id}
+

+

               JID
               #{jid}


environments: add subscription to `ResourcesContainer` if it exists

--- client/Environments/views/resourcescontainer.coffee
+++ client/Environments/views/resourcescontainer.coffee
@@ -9,7 +9,7 @@
     payment = KD.singleton "paymentController"
     payment.fetchSubscriptionsWithPlans tags: [tag], (err, [subscription]) =>
       return new KDNotificationView title: err  if err
-      @subscriptions.addSubView new SubscriptionUsageView null, subscription
+      @subscriptions.addSubView new SubscriptionUsageView null, subscription  if subscription

   viewAppended: ->
     @addSubView @titleBar = new KDCustomHTMLView


Remove duplicate shell app dir cp

Remove duplicate shell app dir cp

--- tasks/build-task.coffee
+++ tasks/build-task.coffee
@@ -11,12 +11,6 @@

     rm shellAppDir
     mkdir path.dirname(buildDir)
-
-    if process.platform is 'darwin'
-      cp 'atom-shell/Atom.app', shellAppDir
-    else if process.platform is 'win32'
-      console.log shellAppDir
-      cp 'atom-shell/', shellAppDir

     if process.platform is 'darwin'
       cp 'atom-shell/Atom.app', shellAppDir


skip unreliable screenreader gradebook test

comment out loading test for screenreader gradebook

Change-Id: I93e922e192d524e4907b6658b08715228a2cd3e8
Reviewed-on: https://gerrit.instructure.com/27915
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
Product-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>

--- app/coffeescripts/ember/screenreader_gradebook/tests/app.spec.coffee
+++ app/coffeescripts/ember/screenreader_gradebook/tests/app.spec.coffee
@@ -23,10 +23,10 @@
     controller = App.__container__.lookup('controller:screenreader_gradebook')
     equal controller.get('assignment_groups').objectAt(0).name, 'AG1'

-  test 'fetches submissions', ->
-    controller = App.__container__.lookup('controller:screenreader_gradebook')
-    equal controller.get('submissions').objectAt(0).submissions[0].grade, '3'
-    equal controller.get('submissions').objectAt(1).submissions[0].grade, '9'
+  #test 'fetches submissions', ->
+    #controller = App.__container__.lookup('controller:screenreader_gradebook')
+    #equal controller.get('submissions').objectAt(0).submissions[0].grade, '3'
+    #equal controller.get('submissions').objectAt(1).submissions[0].grade, '9'

   test 'fetches sections', ->
     controller = App.__container__.lookup('controller:screenreader_gradebook')


show reviewed posts by default

show reviewed posts by default

--- client/controllers/curatorInbox/curatorInbox.coffee
+++ client/controllers/curatorInbox/curatorInbox.coffee
@@ -24,7 +24,7 @@
     endDate: new Date()
   @textFilter = new ReactiveTable.Filter('curator-inbox-article-filter', ['title'])
   @reviewFilter = new ReactiveTable.Filter('curator-inbox-review-filter', ['reviewed'])
-  @reviewFilter.set($ne: true)
+  @reviewFilter.set(null)
   @selectedSourceId = new ReactiveVar null
   @query = new ReactiveVar null


Fix typo

Fix typo

--- client/app/lib/components/sidebarstacksection/sidebarnostacks.coffee
+++ client/app/lib/components/sidebarstacksection/sidebarnostacks.coffee
@@ -27,7 +27,7 @@
       return showErrorNotification err  if err
       @setState
         isGroupAdmin : isAdmin
-        isready      : yes
+        isReady      : yes

   handleOnClick: (event) ->


include commands to update indexes in docHistory migration - not called

--- migrations/3_pack_docHistory_collection.coffee
+++ migrations/3_pack_docHistory_collection.coffee
@@ -73,6 +73,21 @@
 		console.log "#{doc_id} processing"
 		PackManager._packDocHistory doc_id, {}, (err) ->
 			markDocAsProcessed doc_id, callback
+
+updateIndexes = (callback) ->
+	async.series [
+		(cb) ->
+			console.log "create index"
+			db.docHistory.ensureIndex { project_id: 1, "meta.end_ts": 1, "meta.start_ts": -1 }, { background: true }, cb
+		(cb) ->
+			console.log "drop index"
+			db.docHistory.dropIndex { project_id: 1, "meta.end_ts": 1 }, cb
+		(cb) ->
+			console.log "drop index"
+			db.docHistory.dropIndex { project_id: 1, "pack.0.meta.end_ts": 1, "meta.end_ts": 1}, cb
+	], (err, results) ->
+		console.log "all done"
+		callback(err)

 exports.migrate = (client, done = ->)->
 	getDocIds (err, ids)->


chatworkに対応するようにした。

chatworkに対応するようにした。

--- read_rss.coffee
+++ read_rss.coffee
@@ -49,29 +49,40 @@
 feedparser = require 'feedparser'
 request    = require 'request'

-prefix     = '[read_rss]:'
+prefix     = '[read_rss]'
 timezone   = "Asia/Tokyo"
 schedule   = '0 */5 * * * *' # *(sec) *(min) *(hour) *(day) *(month) *(day of the week)
 # schedule   = '0 * * * * *' # *(sec) *(min) *(hour) *(day) *(month) *(day of the week)
 configFile = process.env.RSS_CONFIG_FILE or '../rss_list.json'
 label      = process.env.RSS_LABEL       or 'read_rss'
 type       = process.env.RSS_TARGET_TYPE
+headerFile = process.env.RSS_CUSTOM_HEADERS

-try
-  data = fs.readFileSync configFile, 'utf-8'
+read_json = (file) ->
   try
-    rss = JSON.parse(data)
-    console.log "#{prefix} success to load file: #{configFile}."
+    data = fs.readFileSync file, 'utf-8'
+    try
+      json = JSON.parse(data)
+      console.log "#{prefix} success to load file: #{file}."
+      return json
+    catch
+      console.log "#{prefix} Error on parsing the json file: #{file}"
+      return
   catch
-    console.log "#{prefix} Error on parsing the json file: #{configFile}"
+    console.log "#{prefix} Error on reading the json file: #{file}"
     return
-catch
-  console.log "#{prefix} Error on reading the json file: #{configFile}"
+
+rss     = read_json configFile
+headers = read_json headerFile if headerFile
+
+unless type == "irc" or type == "http_post" or "chatwork"
+  console.log "Please set the value of RSS_TARGET_TYPE."
   return

-unless type == "irc" or type == "http_post"
-  console.log "Please set the value of RSS_TARGET_TYPE."
-  return
+if type == "chatwork"
+  unless headers
+    console.log "Please set the value of RSS_CUSTOM_HEADERS."
+    return

 module.exports = (robot) ->

@@ -124,9 +135,25 @@
         when "irc"
           robot.send { "room": t }, msg
         when "http_post"
+          if headers
+            request.post
+              url: t
+              headers: headers
+              form: {"source": msg}
+            , (err, response, body) ->
+              console.log "err: #{err}" if err?
+          else
+            request.post
+              url: t
+              form: {"source": msg}
+            , (err, response, body) ->
+              console.log "err: #{err}" if err?
+        when "chatwork"
+          msg = encodeURIComponent "[info]#{msg}[/info]"
+          url = "#{t}?body=#{msg}"
           request.post
-            url: t
-            form: {"source": msg}
+            url: url
+            headers: headers
           , (err, response, body) ->
             console.log "err: #{err}" if err?


should have been deleted

should have been deleted

--- resources/assets/coffee/react/_components/supporter-icon.coffee
+++ resources/assets/coffee/react/_components/supporter-icon.coffee
@@ -1,32 +0,0 @@
-###
-#    Copyright (c) ppy Pty Ltd <contact@ppy.sh>.
-#
-#    This file is part of osu!web. osu!web is distributed with the hope of
-#    attracting more community contributions to the core ecosystem of osu!.
-#
-#    osu!web is free software: you can redistribute it and/or modify
-#    it under the terms of the Affero GNU General Public License version 3
-#    as published by the Free Software Foundation.
-#
-#    osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
-#    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-#    See the GNU Affero General Public License for more details.
-#
-#    You should have received a copy of the GNU Affero General Public License
-#    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
-###
-
-el = React.createElement
-{span, i} = ReactDOMFactories
-
-bn = 'supporter-icon'
-
-@SupporterIcon = () ->
-  blockClass = bn
-
-  span
-    className: "#{bn} fa-stack"
-    title: osu.trans('users.show.is_supporter')
-
-    i className: "far fa-circle fa-stack-2x"
-    i className: "#{bn}__heart fas fa-heart fa-stack-1x"


allow non supporter user to see mods filter

--- resources/assets/coffee/react/beatmapset-page/scoreboard.coffee
+++ resources/assets/coffee/react/beatmapset-page/scoreboard.coffee
@@ -69,7 +69,7 @@
             type: type
             active: @props.type == type

-      if currentUser.is_supporter && @props.isScoreable
+      if @props.isScoreable
         div
           className: classWithModifiers('beatmapset-scoreboard__mods', initial: @props.enabledMods.length == 0)
           for mod in mods


Follower count is a primitive now

Follower count is a primitive now

--- resources/assets/coffee/react/modding-profile/detail-bar.coffee
+++ resources/assets/coffee/react/modding-profile/detail-bar.coffee
@@ -53,7 +53,7 @@
           el FriendButton,
             userId: @props.user.id
             showFollowerCounter: true
-            followers: @props.user.follower_count[0]
+            followers: @props.user.follower_count
             modifiers: ['profile-page']
             alwaysVisible: true
         if @state.currentUser.id != @props.user.id && !isBlocked


Workspace: Focus previewPane address bar.

--- client/landing/CollaborativeWorkspace/panes/collaborativepreviewpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/collaborativepreviewpane.coffee
@@ -42,6 +42,13 @@
       message: "$0 opened #{url}"
       by     : KD.nick()

+  viewAppended: ->
+    super
+
+    # TODO: Find a better way without wait
+    KD.utils.wait 200, =>
+      @previewer.viewerHeader.pageLocation.getDomElement().focus()
+

 class CollaborativePreview extends PreviewPane


remove console.log

remove console.log

--- tasks/test/karma.coffee
+++ tasks/test/karma.coffee
@@ -41,7 +41,6 @@
 		]

 	karma.server.start options, (code) ->
-		console.log "Karma exited with code: #{code}"
 		if code > 0
 			notify 'Karma tests failed', false
 		else


Fix New_memory_Face function call

Fix New_memory_Face function call

As shown in the freetype2 project example: https://github.com/ericfreese/node-freetype2/blob/master/example/index.js

Co-Authored-By: Charliiee <62d2fcb701c7fb0ebfa73d5ab92cb693b45b42af@users.noreply.github.com>

--- lib/font-viewer.coffee
+++ lib/font-viewer.coffee
@@ -52,7 +52,9 @@
     fs.readFile @getPath(), (err, buffer) ->
       chars = []
       gindex = {}
-      face = ft.New_Memory_Face(buffer, 0);
+      face = {}
+      ft.New_Memory_Face(buffer, 0, face);
+      face = face.face
       charcode = ft.Get_First_Char(face, gindex)

       while gindex.gindex != 0


Client: Activity: Test: Deleted unused DropboxWrapperMixin file

--- client/activity/lib/components/dropbox/dropboxwrappermixin.coffee
+++ client/activity/lib/components/dropbox/dropboxwrappermixin.coffee
@@ -1,69 +0,0 @@
-React          = require 'kd-react'
-ReactDOM       = require 'react-dom'
-scrollToTarget = require 'app/util/scrollToTarget'
-
-
-module.exports = DropboxWrapperMixin =
-
-  isActive: ->
-
-    { items, visible } = @props
-    visible ?= yes
-    return items.size > 0 and visible
-
-
-  hasSingleItem: -> @props.items.size is 1
-
-
-  confirmSelectedItem: ->
-
-    @handleSelectedItemConfirmation?()
-
-    selectedValue = @formatSelectedValue()
-    @props.onItemConfirmed? selectedValue
-    @close()
-
-
-  componentDidUpdate: (prevProps, prevState) ->
-
-    { selectedItem, visible } = @props
-
-    return  if prevProps.selectedItem is selectedItem or not selectedItem
-    return  if visible? and not visible
-
-    containerElement = @refs.dropbox.getContentElement()
-    itemElement      = ReactDOM.findDOMNode @refs[@getItemKey selectedItem]
-
-    scrollToTarget containerElement, itemElement
-
-
-  moveToPrevPosition: (keyInfo) ->
-
-    if keyInfo.isLeftArrow
-      @close()
-      return no
-
-    @moveToPrevAction()  unless @hasSingleItem()
-    return yes
-
-
-  moveToNextPosition: (keyInfo) ->
-
-    if keyInfo.isRightArrow
-      @close()
-      return no
-
-    @moveToNextAction()  unless @hasSingleItem()
-    return yes
-
-
-  onItemSelected: (index) -> @onItemSelectedAction index
-
-
-  close: -> @closeAction no
-
-
-  getItemKey: (item) -> item.get 'id'
-
-
-  updatePosition: (inputDimensions) -> @refs.dropbox.setInputDimensions inputDimensions


Add enabled option to provider

Add enabled option to provider

--- lib/operator-config.coffee
+++ lib/operator-config.coffee
@@ -94,6 +94,9 @@
       currentObject[property] =
         if value.default? then value.default else value

+    # Enable character alignment by default
+    currentObject.enabled ?= true
+
     return convertedConfig

   ###
@@ -126,8 +129,9 @@
   getConfig: (character, languageScope = 'base') ->
     for id, config of @settings
       if config.selector? and config.selector.indexOf(languageScope) isnt -1
-        return config[character]
+        return config[character] if config[character]?.enabled

+    # default settings for `character`
     return @settings.aligner[character]

   ###


Fix misuse of coffeescript existential operator

for path in project.paths? was being transpiled to
_ref = project.paths != null;

--- lib/project-manager.coffee
+++ lib/project-manager.coffee
@@ -95,7 +95,8 @@

   getCurrentProject: (projects) ->
     for title, project of projects
-      for path in project.paths?
+      continue if not project.paths?
+      for path in project.paths
         if path is atom.project.getPath()
           return project
     return false


Initial implementation for support to expose file on Amazon.

--- lib/provider_amazon.coffee
+++ lib/provider_amazon.coffee
@@ -168,11 +168,45 @@
 		###

 		#
-		# Doesn't do anything at this stage.
-		#
-		@status node_name, (err, state, address) ->
+		# First we verify the status of the node to check if the state is correct.
+		#
+		verify_status = (callback) =>
+			@status node_name, (err, state, address) ->
+				return callback err if err
+				return callback new Error "node #{node_name} is not ready" if state != 'running'
+				return callback null
+
+		#
+		# Next we check the exposed files and folders.
+		#
+		prepare_exposed = (callback) =>
+			try
+				node = @get_node node_name
+			catch e
+				node = null
+
+			return callback null if not node?.expose?
+
+			handle_exposure = (exposure, callback) =>
+				source_path = path_extra.resolve path_extra.dirname(@manifest.meta.location), exposure.src
+
+				fs.stat source_path, (err, stats) =>
+					return callback new Error "cannot expose #{exposure.src} because it does not exist" if err
+
+					if stats.isDirectory()
+						# TODO: add code here
+						return callback null
+					else
+						# TODO: add code here
+						return callback null
+
+			async.eachSeries ({src: src, dst: dst} for src, dst of node.expose), handle_exposure, callback
+
+		#
+		# Action on the tasks.
+		#
+		async.waterfall [verify_status, prepare_exposed], (err, state, address) ->
 			return callback err if err
-			return callback new Error "node #{node_name} is not ready" if state != 'running'
 			return callback null

 	status: (node_name, callback) ->


Lint packager specs.

Lint packager specs.

--- test/units/tasks/package.spec.coffee
+++ test/units/tasks/package.spec.coffee
@@ -15,11 +15,11 @@
     beforeEach -> addFileMatchers this
     it 'should apply the operators on the file', ->
       path = "#{Neat.root}/packages/fixtures.coffee"
+      repl = (m,n) -> "#{files[parseInt n]}.coffee"
       expected = String(readFileSync expected)
         .squeeze('\n')
         .strip()
-        .replace /\#\{file\[(\d+)\]\}/g, (m,n) ->
-          "#{files[parseInt n]}.coffee"
+        .replace /\#\{file\[(\d+)\]\}/g, repl

       expect(@result[path].squeeze('\n').strip()).toEqual(expected)


removed unneeded code

removed unneeded code

--- FormValidator.coffee
+++ FormValidator.coffee
@@ -243,16 +243,6 @@

     _get_value_info: (element, data) ->
         {type, preprocess} = data
-        # usedValFunc = true
-        # if type isnt "checkbox" and type isnt "radio"
-        #     value = element.val()
-        # else
-        #     if type isnt "checkbox" or type isnt "radio"
-        #         value = if element.prop("checked") is true then "checked" else "unchecked"
-        #
-        # if not value?
-        #     usedValFunc = false
-        #     value = element.text()
         {method, value} = @_get_value(element, data)

         value_has_changed = value isnt data.value


Update jQueryPrototyping.coffee

Update jQueryPrototyping.coffee

--- prototyping/jQueryPrototyping.coffee
+++ prototyping/jQueryPrototyping.coffee
@@ -75,6 +75,8 @@
         # this is the wrapping
         wrapper.append @
         return @
+    matches: (selector) ->
+        return @filter(selector).length is @length

 ##############################################
 # $.Color prototyping


Fix core's plugin method not returning instance.

Occurs if context gets passed.

--- src/js/jquery.extension.hlf.core.coffee
+++ src/js/jquery.extension.hlf.core.coffee
@@ -159,7 +159,7 @@
           #   same plugin instance.
           $el = if createOptions.asSharedInstance is yes then $context else @first()
           instance = $el.data namespace.toString('data')
-          return instance if instance? and instance.$el? and not arguments.length
+          return instance if instance? and instance.$el? and not options?
         # - Otherwise, continue creating the instance by preparing the options
         #   and deciding the main element before passing things onto
         #   `_createPluginInstance`.


dead LOC

dead LOC

--- lib/transforms/join.coffee
+++ lib/transforms/join.coffee
@@ -44,15 +44,6 @@
     super _(@options).extend objectMode: true

   finished: () => @_writableState.finished
-
-  store: (key, value) =>
-    if not @cache[key]?
-      @cache[key] = value
-    else
-      # object already here..., merge them
-      for k, v of value
-        @cache[key][k] = [@cache[key][k]] unless _(@cache[key][k]).isArray()
-        @cache[key][k].push v

   _write: (obj, encoding, cb) =>
     @_hash ?= hash_fn false, @options.on


Fix issue with pinning Markdown previews

--- lib/pinned-tabs.coffee
+++ lib/pinned-tabs.coffee
@@ -92,6 +92,7 @@
     activePane = atom.workspace.getActivePane()
     for pane in atom.workspace.getPanes()
       continue if @state.data[pane.id] == undefined
+
       for target in @state.data[pane.id]
         for item in pane.getItems()
           if target == @getItemID item
@@ -103,6 +104,9 @@
               if item.getTitle
                 title = paneNode.querySelector '.title[data-name="' + item.getTitle() + '"]'
                 tab = title.parentNode if title != null
+              if !tab && item.filePath
+                  title = paneNode.querySelector '.title[data-path="' + item.filePath.replace(/\\/g, '\\\\') + '"]'
+                  tab = title.parentNode if title != null
               if item.element && item.element.classList.contains 'about'
                 tab = paneNode.querySelector '.tab[data-type="AboutView"]'
               if item.element && item.element.classList.contains 'settings-view'
@@ -165,7 +169,9 @@

   getItemID: (item) ->
     if item.getURI
-      return item.getURI()
+      uri = item.getURI()
+      uri = 'markdown-preview://' + item.editorForId(item.editorId).getFileName() if uri.match(/markdown-preview:\/\//)
+      return uri
     else if item.getTitle
       return item.getTitle()


Check isArray instead of not undefined

Check isArray instead of not undefined

Added a potential fix for #29 by checking whether the state data for the
current pane is an array (instead of just not undefined) when closing a
tab.

--- lib/pinned-tabs.coffee
+++ lib/pinned-tabs.coffee
@@ -88,7 +88,7 @@
       delete @state.data[pane.id] if @state.data[pane.id] != undefined

     atom.workspace.onWillDestroyPaneItem ({item, pane}) =>
-      return if @state.data[pane.id] == undefined
+      return if not Array.isArray @state.data[pane.id]
       @state.data[pane.id] = @state.data[pane.id].filter (id) => id isnt @getItemID(item)

   initTabs: ->


Indentation & Spacing fixes

Indentation & Spacing fixes

--- lib/pinned-tabs.coffee
+++ lib/pinned-tabs.coffee
@@ -53,7 +53,7 @@
                         tab.classList.add 'pinned'
                     else if state.indexOf(info.itemURI) >= 0
                         tab.classList.add 'pinned'
-            ), 1
+        ), 1

     serialize: ->
         @PinnedTabsState.serialize()
@@ -113,7 +113,7 @@
     pin: (e) ->
         return unless info = @getTabInformation e

-        if info.tabIsPinned
+        if info.isPinned
             index = @PinnedTabsState.data.indexOf info.itemId
             @PinnedTabsState.data.splice(index, 1) if index >= 0
             info.pane.moveItem(info.item, info.unpinIndex)
@@ -121,7 +121,7 @@
             @PinnedTabsState.data.push info.itemId
             info.pane.moveItem(info.item, info.pinIndex)

-        setTimeout (-> e.classList.toggle 'pinned' ), 1
+        setTimeout (-> e.classList.toggle 'pinned'), 1

     getTabInformation: (e) ->
         return if e == null
@@ -154,7 +154,7 @@
             itemId: itemId || itemURI,
             pane: pane,

-            tabIsPinned: e.classList.contains 'pinned'
+            isPinned: e.classList.contains 'pinned'
         }


Use skinny arrow for WrapGuide.initialize()

--- lib/wrap-guide-view.coffee
+++ lib/wrap-guide-view.coffee
@@ -13,7 +13,7 @@

   defaultColumn: 80

-  initialize: (@editor) =>
+  initialize: (@editor) ->
     @observeConfig 'editor.fontSize', => @updateGuide()
     @subscribe @editor, 'editor:path-changed', => @updateGuide()
     @subscribe @editor, 'editor:min-width-changed', => @updateGuide()


:art: minor refactoring

:art: minor refactoring

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -259,18 +259,18 @@
 # TODO: make it extendable when repeated
 class Fold extends TextObject
   @extend()
-  getRowRangeForBufferRow: (bufferRow, inclusive=null) ->
+  getRowRangeForBufferRow: (bufferRow) ->
     for currentRow in [bufferRow..0] by -1
       [startRow, endRow] = @editor.languageMode.rowRangeForCodeFoldAtBufferRow(currentRow) ? []
       continue unless startRow? and startRow <= bufferRow <= endRow
-      startRow += 1 unless inclusive
+      startRow += 1 unless @inclusive
       return [startRow, endRow]

   select: ->
     @eachSelection (selection) =>
       [startRow, endRow] = selection.getBufferRowRange()
       row = if selection.isReversed() then startRow else endRow
-      if rowRange = @getRowRangeForBufferRow(row, @inclusive)
+      if rowRange = @getRowRangeForBufferRow(row)
         selectLines(selection, rowRange)
     @status()


big fixex

big fixex

--- lib/wink-switch.coffee
+++ lib/wink-switch.coffee
@@ -86,11 +86,9 @@

   class WinkLightSwitch extends WinkBinarySwitch
-    #env.devices.PowerSwitch
-    #@_wink_state

-    constructor: (@config) ->
-      super(@config)
+    constructor: (@config, @plugin) ->
+      super(@config, @plugin)
       @switch_type = 'light_switch'


Routes: fixed a compile error bc of lack of new lines on concatenated source files

--- Builder.coffee
+++ Builder.coffee
@@ -486,13 +486,13 @@

   getRoutes:->

-    routesSrc = ''
+    routesSrc = '\n'

     for own name, project of @projectsToBuild

       if fs.existsSync project.routes
         console.log "Routes added for \"#{name} App\""
-        routesSrc += fs.readFileSync project.routes, 'utf-8'
+        routesSrc += "#{fs.readFileSync project.routes, 'utf-8'}\n"
       else
         console.warn "No routes found for \"#{name} App\""


:bug: fix method calling condition of updateWrapperWidth

--- src/renderer/components/doc-detail-container-component.coffee
+++ src/renderer/components/doc-detail-container-component.coffee
@@ -11,11 +11,14 @@
       @context.ctx.routeAction.navigate(document.location.pathname.match(/^(.+)\/[^\/]+$/)[1])

   updateWrapperWidth: ->
-    @setState
-      wrapperWidth: React.findDOMNode(@refs.docDetailBase).clientWidth
+    if @props.argu.route_arr[1]?.toString() == 'local'
+      @setState
+        wrapperWidth: React.findDOMNode(@refs.docDetailBase).clientWidth
+
+  componentDidUpdate: ->
+    @updateWrapperWidth()

   componentDidMount: ->
-    @updateWrapperWidth()
     window.addEventListener 'resize', @updateWrapperWidth.bind(@)

   componentWillUnmount: ->


Sort stats by kill/death

Sort stats by kill/death

--- src/public/scripts/views/overlay.coffee
+++ src/public/scripts/views/overlay.coffee
@@ -125,34 +125,25 @@

 				statsFormatted[id].deaths = deaths

-			###
-			sortedKills = []
-			for id, kills of stats.kills
-				deaths = stats.deaths[id] || 0
-				console.log id, kills
-				if deaths is 0
-					kdr = kills
+			sortedStats = []
+			for id, stat of statsFormatted
+				if stat.deaths is 0
+					kdr = stat.kills
 				else
-					kdr = Math.round(kills / deaths, 2)
-				sortedKills.push([id, kills, deaths, kdr])
-			console.log stats.kills
+					kdr = Math.round(stat.kills / stat.deaths, 2)
+				sortedStats.push([id, stat.kills, stat.deaths, kdr])

-			sortedKills.sort(( a, b ) ->
+			sortedStats.sort(( a, b ) ->
 				a[1] - b[1]
 			)
-			console.log sortedKills
-			###
+
 			@display('stats', ( ) =>
 				@_statsVisible = true
 				statRows = $('#statRows')
 				statRows.empty()
 				rank = 1
-				for id, stat of statsFormatted
-					if stat.deaths is 0
-						kdr = stat.kills
-					else
-						kdr = Math.round(stat.kills / stat.deaths, 2)
-					statRows.append("
#{rank++}	#{id}	#{stat.kills}	#{stat.deaths}	#{kdr}
")
+				for stat in sortedStats
+					statRows.append("
#{rank++}	#{stat[0]}	#{stat[1]}	#{stat[2]}	#{stat[3]}
")
 			, ( ) =>
 				@_statsVisible = false
 			)


use process id so link process to smoke test

--- test/smoke/coffee/SmokeTests.coffee
+++ test/smoke/coffee/SmokeTests.coffee
@@ -6,9 +6,7 @@

 buildUrl = (path) -> "http://#{Settings.internal.clsi.host}:#{Settings.internal.clsi.port}/#{path}"

-random = require("crypto").randomBytes(4).toString("hex")
-
-url = buildUrl("project/smoketest-#{random}/compile")
+url = buildUrl("project/smoketest-#{process.pid}/compile")

 describe "Running a compile", ->
 	before (done) ->


Accomodate different fake window sizes with regex

--- test/user/login-gateway.spec.coffee
+++ test/user/login-gateway.spec.coffee
@@ -20,6 +20,7 @@
     Testing.renderComponent( LoginGateway, props: @props ).then ({dom, element}) =>
       Testing.actions.click(dom.querySelector('a.login'))
       expect(@props.window.open).to.have.been.calledWith(
-        '/test-login?parent=http%3A%2F%2Flocalhost%3A9880%2Fcontext.html', 'oxlogin', 'toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no,width=1000,height=800,top=320,left=780'
+        '/test-login?parent=http%3A%2F%2Flocalhost%3A9880%2Fcontext.html', 'oxlogin',
+        sinon.match(/toolbar=no,location=yes,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no,width=\d+,height=\d+,top=\d+,left=\d+/)
       )
       expect( element.getDOMNode().textContent ).to.include('Click to reopen window')


koding-worker: if condition fix

koding-worker: if condition fix

--- node_modules_koding/koding-worker/main.coffee
+++ node_modules_koding/koding-worker/main.coffee
@@ -157,7 +157,7 @@
           queue.bind 'workerExchange', "output.worker.#{@worker.uuid}"
           queue.on "queueBindOk", =>
             unless @binary
-              if @worker.message.option is "one" or "version"
+              if @worker.message.option is "one" or @worker.message.option is "version"
                 @startReporter "add"
               else
                 @fetchWorkerExchange (workerExchange)=>


Add missing greater-than character in tests

--- services/web/test/unit/coffee/User/UserGetterTests.coffee
+++ services/web/test/unit/coffee/User/UserGetterTests.coffee
@@ -45,7 +45,7 @@
 				error.should.exist
 				done()

-	describe "getUserFullEmails", -
+	describe "getUserFullEmails", ->
 		beforeEach ->
 			@request.callsArgWith(1, null, { statusCode: 200 }, [])


Added test of saving a Config to filesystem

--- test/config-test.coffee
+++ test/config-test.coffee
@@ -24,7 +24,6 @@
     'Create a new Config':
       topic: ->
         new config.Config
-

       'and we do c.getHostname(http://example.com)':
         topic: (config) ->
@@ -82,14 +81,13 @@
           'the "spell-check" plugin options is an object': (plugin) ->
             assert.equal _.isObject(plugin.opts), true

- .addBatch
+.addBatch
     'Save a Config to filesystem':
       topic: ->
         new config.Config

       'when we do c.save()':
         topic: (config) ->
-          config.open()
           config.addHostname 'http://example.com'
           config.save()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -12,7 +12,7 @@
   player.include Flickerable

 test 'it should change the alpha value when the flicker duration has elapsed', ->
-
+  player.

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -41,7 +41,9 @@
   boss.flicker
     duration: 2

-
+  equals boss.I.flickerAlpha, 0.5
+  equals player.I.flickerFrequency, 3
+  equals player.I.flickerDuration, 30

 test 'it should change the alpha value when the flicker duration has elapsed', ->
   player = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -2,6 +2,7 @@

 test "()", ->
   gameObject = GameObject()
+  ok gameObject

 test "construct", ->
   gameObject = GameObject.construct


adding elapsedTime

adding elapsedTime

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -39,7 +39,9 @@
   gameObject.update()

 test "elapsedTime", ->
-  gameObject = GameObje
+  gameObject = GameObject()
+
+  game

 test "[event] destroy", 2, ->
   GameObject


Reorganize tests

Reorganize tests

--- test/reader_spec.coffee
+++ test/reader_spec.coffee
@@ -9,26 +9,24 @@
     @file  = './test/fixtures/dummy.html'
     @dummy = fs.readFileSync @file

-  describe '#constructor', ->
-    it 'accepts a file as source argument', (done)->
-      reader = new Reader @file
-      expect(reader.sourceType).to.be.eql 'file'
-      reader.getStream (err, stream) ->
-        expect(err).to.be.null
-        expect(stream).to.be.instanceof Stream
+  beforeEach ->
+    @reader = new Reader()
+
+  describe '#getStream', ->
+    it 'fails when no source is passed to constructor', (done) ->
+      @reader.getStream (err, stream) ->
+        expect(err).to.be.instanceof Error
+        expect(err.message).to.be.eql 'a valid source must be provided'
         done()

-    it 'accepts an url as source argument'
-
-  context 'public methods', ->
-    beforeEach ->
-      @reader = new Reader()
-
-    describe '#getStream', ->
-      it 'fails when no source is passed to constructor', (done) ->
-        @reader.getStream (err, stream) ->
-          expect(err).to.be.instanceof Error
-          expect(err.message).to.be.eql 'a valid source must be provided'
+  context 'when reading from a file', ->
+    describe '#constructor', ->
+      it 'accepts a file as source argument', (done)->
+        reader = new Reader @file
+        expect(reader.sourceType).to.be.eql 'file'
+        reader.getStream (err, stream) ->
+          expect(err).to.be.null
+          expect(stream).to.be.instanceof Stream
           done()

     describe '#getFileStream', ->


Fixed bug where projects without start dates were not being updated properly

--- src/models/project.coffee
+++ src/models/project.coffee
@@ -6,7 +6,7 @@
 HEADERS = constants.HEADERS

 class Project
-  constructor: (@name = '', @start, @total, @row = null) ->
+  constructor: (@name = '', @start = moment(), @total, @row = null) ->
     @name = @name.replace '#', ''
   @parse: (row) ->
     if not row


add failing test

add failing test

--- test/encrypt.coffee
+++ test/encrypt.coffee
@@ -782,3 +782,37 @@
           assert.property doc.children[0], '_id'
           assert.notProperty doc.children[0], '_ct'
           done()
+
+    describe 'Encrypted embedded document when parent has validation error', ->
+      before ->
+        ChildModelSchema = mongoose.Schema
+          text: type: String
+
+        ChildModelSchema.plugin encrypt,
+          key: encryptionKey
+          fields: ['text']
+
+        ParentModelSchema = mongoose.Schema
+          text: type: String
+          children: [ChildModelSchema]
+
+        ParentModelSchema.pre 'validate', (next) ->
+            @invalidate 'text', 'invalid', this.text
+            next()
+
+        @ParentModel = mongoose.model 'ParentAgain', ParentModelSchema
+        @ChildModel = mongoose.model 'ChildAgain', ChildModelSchema
+
+      it 'Should return still unencrypt embedded documents after save', ->
+        doc = new @ParentModel
+          text: 'here it is'
+          children: [{text: 'Child unencrypted text'}]
+        doc.save (err) ->
+          assert.ok err, 'There should be a validation error'
+          assert.propertyVal doc, 'text', 'here it is'
+          assert.isArray doc.children
+          assert.isObject doc.children[0]
+          assert.property doc.children[0], 'text', 'Child unencrypted text'
+          assert.property doc.children[0], '_id'
+          assert.notProperty doc.children[0], '_ct'
+


Add test to ensure an event is emitted.

Add test to ensure an event is emitted.

--- test/server_spec.coffee
+++ test/server_spec.coffee
@@ -1,7 +1,25 @@
-{BattleServer} = require('../').server
+sinon = require 'sinon'
+{BattleServer, Player} = require('../').server

 describe 'BattleServer', ->
   it 'can create a new battle', ->
     server = new BattleServer
     battleId = server.createBattle()
     server.battles.should.have.ownProperty battleId
+
+  it "emits the 'start battle' event for each matched player", ->
+    players = [{clientId: 'abc', emit: ->},
+               {clientId: 'def', emit: ->}]
+    mocks = []
+    for player in players
+      mock = sinon.mock(player)
+      mock.expects('emit').once()
+      mocks.push(mock)
+
+    server = new BattleServer()
+    for player in players
+      server.queuePlayer(player, [])
+    server.beginBattles()
+
+    for mock in mocks
+      mock.verify()


KodingProvider: pr fixes

KodingProvider: pr fixes

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -162,7 +162,7 @@
                limit of #{userPlan.storage}GB.""", "UsageLimitReached"
           else if resize < 3
             return callback new KodingError \
-            """New size can't be less then 3GB.""", "WrongParameter"
+            """New size can't be less than 3GB.""", "WrongParameter"

         { ObjectId } = require 'bongo'

@@ -186,7 +186,9 @@
             fieldsToUpdate["meta.alwaysOn"] = alwaysOn
           if resize?

-            if (resize - machine.meta?.storage_size) + usage.storage > userPlan.storage
+            storageSize = machine.meta?.storage_size ? 3
+
+            if (resize - storageSize) + usage.storage > userPlan.storage
               return callback new KodingError \
               """Requested new size exceeds allowed
                  limit of #{userPlan.storage}GB.""", "UsageLimitReached"


DOC: add docs to grade service

DOC: add docs to grade service

--- src/app/common/services/outcome-service.coffee
+++ src/app/common/services/outcome-service.coffee
@@ -22,23 +22,50 @@

     calculateTargets: (unit, source, taskStatusFactor) ->
       outcomes = {}
+      # For each learning outcome (LO) -- produce a map with grades containing task scores
+      # calculated from alignment details, task target grade, and task status factor.
+      #
+      # The Task Status Factor for projects will be a value between 0 and 1
+      # In the unit the taskStatusFactor will always be 1 (100%) to show potential values
+      # for the unit -- in effect removing the task status from unit calculations
       _.each unit.ilos, (outcome) ->
+        # Add grade map for this LO to outcomes map
         outcomes[outcome.id] = {
-          0: []
-          1: []
+          # Using 0..3 so that it can be used to calculate the grade scale below
+          0: [] # Pass grade... -- will contain scores for pass grade tasks
+          1: [] # Credit grade... -- etc.
           2: []
           3: []
         }

+      # For each outcome / task alignment...
       _.each source.task_outcome_alignments, (align) ->
+        # Get the task definition
         td = unit.taskDef(align.task_definition_id)
+        # Store a partial score for this task in the relevant outcomes ( outcomes[outcome id][grade] << score )
+        # At this stage it is just rating * taskFactor (1 to 5 times 0 to 1)
         outcomes[align.learning_outcome_id][td.target_grade].push align.rating * taskStatusFactor(td.id)

+      # Finally reduce all of these into one score for each outcome / grade
       _.each outcomes, (outcome, key) ->
+        # For this outcome
         _.each outcome, (tmp, key1) ->
+          # get a scale for the grade
           scale = Math.pow(2, parseInt(key1,10))
+          # Reduce all task partial scores and * grade scale -- replace array with single value
           outcome[key1] = _.reduce(tmp, ((memo, num) -> memo + num), 0) * scale

+      # Returns map of...
+      # {
+      #   <OutcomeID>: {
+      #     0: <score>
+      #     1: <score> ...
+      #   },
+      #   86: {   <--- OutcomeID 86 --> "Programming Principles"
+      #     0: 27 <--- Pass: 27 score (from rating * task status factor * scale reduced)
+      #     1: 53 ...
+      #   },
+      # }
       outcomes

     calculateTaskContribution: (unit, project, task) ->


Remove tipsy.coffee

Remove tipsy.coffee

--- vendor/assets/javascripts/mice/tipsy.coffee
+++ vendor/assets/javascripts/mice/tipsy.coffee
@@ -1,231 +0,0 @@
-# Mice: tooltip
-
-# Inspired by the original jQuery.tipsy by Jason Frame
-# https://github.com/jaz303/tipsy
-
-# Copyright (c) 2014 Miclle
-# Licensed under MIT (https://github.com/miclle/mice/blob/master/LICENSE)
-
-'use strict';
-
-(($) ->
-
-
-  maybeCall = (thing, ctx) ->
-    if (typeof thing == 'function') then thing.call(ctx) else thing
-
-
-  isElementInDOM = (element) ->
-    (return true if element == document) while element = element.parentNode
-    false
-
-
-  class Tooltip
-    constructor: (@element, @options) ->
-      @.$element = $(@element)
-      @.options = @options
-      @.enabled = true
-      @.fixTitle()
-
-    getOptions: (options) ->
-      options = $.extend({}, this.getDefaults(), @.$element.data(), options)
-
-      options.delay = { show: options.delay, hide: options.delay } if options.delay and typeof options.delay == 'number'
-
-      options
-
-    show: ->
-      title = @.getTitle()
-      if title and @.enabled
-        $tip = @.tip()
-
-        $tip.find('.tooltip-inner')[if @options.html then 'html' else 'text'](title)
-        $tip[0].className = 'tooltip'
-        $tip.remove().css({ top: 0, left:0, visibility: 'hidden', display: 'block'}).prependTo(document.body)
-
-        pos = $.extend({}, @.$element.offset(), {
-          width: @.$element[0].offsetWidth,
-          height: @.$element[0].offsetHeight
-        });
-
-        actualWidth = $tip[0].offsetWidth
-        actualHeight = $tip[0].offsetHeight
-        placement = maybeCall @.options.placement, @.$element[0]
-
-        console.log placement
-
-        switch placement
-          when 'bottom'
-            tp = {top: pos.top + pos.height + @.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
-          when 'top'
-            tp = {top: pos.top - actualHeight - @.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
-          when 'left'
-            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - @.options.offset};
-          when 'right'
-            tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + @.options.offset};
-
-        # if placement.length == 2
-        #   if placement.charAt(1) == 'w'
-        #     tp.left = pos.left + pos.width / 2 - 15
-        #   else
-        #     tp.left = pos.left + pos.width / 2 - actualWidth + 15
-
-        $tip.css(tp).addClass('tooltip-' + placement)
-        $tip.find('.tooltip-arrow')[0].className = 'tooltip-arrow tooltip-arrow-' + placement.charAt(0)
-        if @.options.className then $tip.addClass(maybeCall @.options.className, @.$element[0])
-
-        if @.options.fade
-          $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: @.options.opacity});
-        else
-          $tip.css({visibility: 'visible', opacity: @.options.opacity});
-
-      return
-
-    hide: ->
-      if @.options.fade then @.tip().stop().fadeOut(-> $(@).remove()) else @.tip().remove()
-
-    fixTitle: ->
-      if @.$element.attr('title') or typeof(@.$element.attr('data-original-title')) != 'string'
-        @.$element.attr('data-original-title', @.$element.attr('title') || '').removeAttr('title')
-
-    getTitle: ->
-      @.fixTitle()
-      if typeof @.options.title == 'string'
-        title = @.$element.attr( if @.options.title == 'title' then 'data-original-title' else @.options.title)
-      else if typeof @.options.title == 'function'
-        title = @.options.title.call(@.$element[0])
-
-      title = ('' + title).replace(/(^\s*|\s*$)/, "")
-      title or @.options.fallback
-
-    tip: ->
-      if !@.$tip
-        @.$tip = $('

').html('

');
-        @.$tip.data('tooltip-pointee', @.$element[0]);
-      @.$tip;
-
-    validate: ->
-      if !@.$element[0].parentNode
-        @.hide();
-        @.$element = null;
-        @.options = null;
-
-    enable: ->
-      @.enabled = true
-
-    disable: ->
-      @.enabled = false
-
-    toggleEnabled: ->
-      @.enabled = !@.enabled
-
-
-  $.fn.tooltip = (options) ->
-    if options == true
-      return @.data('tooltip')
-    else if typeof options == 'string'
-      tooltip = @.data('tooltip')
-      if tooltip then tooltip[options]()
-      return @
-
-    # options = $.extend({}, $.fn.tooltip.defaults, options)
-
-    options = $.extend({}, $.fn.tooltip.defaults, @.data(), options)
-
-    get = (element) ->
-      tooltip = $.data(element, 'tooltip')
-      if !tooltip
-        tooltip = new Tooltip(element, $.fn.tooltip.elementOptions(element, options))
-        $.data(element, 'tooltip', tooltip)
-      return tooltip
-
-    enter = ->
-      tooltip = get @
-      tooltip.hoverState = 'in'
-      if options.delayIn == 0
-        tooltip.show()
-      else
-        tooltip.fixTitle()
-        setTimeout((-> tooltip.show() if tooltip.hoverState == 'in' ), options.delayIn)
-
-    leave = ->
-      tooltip = get @
-      tooltip.hoverState = 'out'
-      if options.delayOut == 0
-        tooltip.hide()
-      else
-        setTimeout((-> tooltip.hide() if tooltip.hoverState == 'out' ), options.delayOut)
-
-    if !options.live
-      @.each( ->
-        get(@)
-        return
-      )
-
-    if options.trigger != 'manual'
-      binder  = if options.live then 'live' else 'bind'
-      eventIn = if options.trigger == 'hover' then 'mouseenter' else 'focus'
-      eventOut= if options.trigger == 'hover' then 'mouseleave' else 'blur'
-      @[binder](eventIn, enter)[binder](eventOut, leave)
-
-    @
-
-  # placement:
-  #   northwest north northeast
-  #   west            east
-  #   southwest south southeast
-
-  $.fn.tooltip.defaults = {
-    className: null
-    delayIn: 0
-    delayOut: 0
-    fade: false
-    fallback: ''
-    placement: 's'
-    html: false
-    live: false
-    offset: 0
-    opacity: 0.8
-    title: 'title'
-    trigger: 'hover'
-  };
-
-
-  $.fn.tooltip.revalidate = ->
-    $('.tooltip').each ->
-      pointee = $.data(@, 'tooltip-pointee')
-      $(@).remove() if (!pointee || !isElementInDOM(pointee))
-
-
-  $.fn.tooltip.elementOptions = (ele, options) ->
-    if $.metadata then $.extend({}, options, $(ele).metadata()) else options
-
-
-  $.fn.tooltip.autoNS = ->
-    if $(@).offset().top > ($(document).scrollTop() + $(window).height() / 2) then 's' else 'n'
-
-
-  $.fn.tooltip.autoWE = ->
-    if $(@).offset().left > ($(document).scrollLeft() + $(window).width() / 2) then 'e' else 'w'
-
-
-  $.fn.tooltip.autoBounds = (margin, prefer) ->
-    ->
-      dir = { ns: prefer[0], ew:(if prefer.length > 1 then prefer[1] else false)}
-      boundTop = $(document).scrollTop + margin
-      boundLeft = $(document).scrollLeft + margin
-
-      $this = $(@)
-
-      dir.ns = 'n' if ($this.offset().top < boundTop)
-      dir.ew = 'w' if ($this.offset().left < boundLeft)
-      dir.ew = 'e' if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin)
-      dir.ns = 's' if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin)
-
-      dir.ns + (if dir.ew then dir.ew else '')
-
-
-  return
-
-
-) jQuery


Show progress

Show progress

--- elaboratecharts/coffee/index.coffee
+++ elaboratecharts/coffee/index.coffee
@@ -31,7 +31,7 @@
           enabled: false
       series:
         animation:
-          complete: -> $(window).resize()
+          complete: -> chart.reflow()
     series: []
   chart = $chart.highcharts()

@@ -50,7 +50,7 @@
   for timestamp, topitems of weeklyCharts
     topitems = _.sortBy(_.pairs(topitems), ([key, value]) -> value)
     weeklyCharts[timestamp] = {}
-    for [key, value] in topitems.reverse()[0..9]
+    for [key, value] in topitems.reverse()[0 .. (numberOfArtists - 1)]
       weeklyCharts[timestamp][key] = value

   artists = {}
@@ -82,8 +82,8 @@

 $ ->
   $('#submit').click ->
-    l = Ladda.create(this)
-    l.start()
+    ladda = Ladda.create(this)
+    ladda.start()
     username = $('#username').val().trim()
     numberOfArtists = $('#number-of-artists option:selected').val()
     timeframe = $('#timeframe option:selected').val()
@@ -100,15 +100,21 @@
         when 'last-12-months' then toDate.clone().subtract(12, 'month')
         when 'overall'        then moment.utc(info.registered * 1000)

-      Promise.map spanRange(fromDate, toDate, 1, 'week'), (range) ->
+      ranges = spanRange(fromDate, toDate, 1, 'week')
+      progress = 0
+      Promise.map ranges, (range) ->
         [fromDate, toDate] = range
         params = {username, fromDate, toDate}
         $.getJSON($SCRIPT_ROOT + '/weekly-artist-charts', params)
         .then (weeklyCharts) ->
-          # Put progress bar here
+          progress += 1
+          ladda.setProgress(progress / ranges.length)
           # if weeklyCharts.error?
           #   # Put error handling here
           weeklyCharts
+        .fail ->
+          # Failed to get weekly charts.
+          ladda.stop()
       .then (weeks) ->
         weeklyCharts = {}
         for week in weeks
@@ -116,5 +122,8 @@
             if key != 'error'
               weeklyCharts[key] = value
         drawChart(weeklyCharts, numberOfArtists, cumulative)
-        l.stop()
-        $('.collapse').collapse()
+        ladda.stop()
+        # $('.collapse').collapse()
+    .fail ->
+      # Failed to get user info.
+      ladda.stop()


add new contributor

add new contributor

www

--- views/components/settings/parts/others.cjsx
+++ views/components/settings/parts/others.cjsx
@@ -25,6 +25,7 @@

 Chibaheit

 KochiyaOcean

 马里酱

+
 吴钩霜雪明

         </Col>
       </Grid>


* remove log

* remove log

--- test/test_packer.coffee
+++ test/test_packer.coffee
@@ -267,7 +267,7 @@
           './power' : -> if test? then test else {}

       res_fn = (err, code) ->
-        console.log code
+        # console.log code
         expect(err).to.be.null
         # oh, its better than eval :)
         vm.runInNewContext code, sandbox = {}


That didn't work

That didn't work

--- modules/player/hud.coffee
+++ modules/player/hud.coffee
@@ -90,15 +90,15 @@
         text: "Requested by #{item.requestedBy.name}"
     if item.filters and item.filters.length
       reply.description = "**Filters**: #{@util.displayFilters(item.filters)}"
-    if estimated
-      reply.fields.push {
-        name: 'Estimated time for playback:',
-        value: @util.displayTime(estimated)
-      }
     if item.time and item.time > 0
       reply.fields.push {
         name: 'Start at:'
         value: @util.displayTime(item.time)
+      }
+    if estimated
+      reply.fields.push {
+        name: 'Estimated time before playback:',
+        value: @util.displayTime(estimated)
       }
     reply


Referral: use app tell instead of using utisl

--- client/Account/views/referralsystem.coffee
+++ client/Account/views/referralsystem.coffee
@@ -14,12 +14,17 @@
       @instantiateListItems referals or []
     @hideLazyLoader()

-    @on "ShowRedeemReferralPointModal", KD.utils.showRedeemReferralPointModal
-
   loadView: ->
     super
     @addHeader()
     @loadItems()
+
+  showRedeemReferralPointModal:->
+    KD.mixpanel "Referer Redeem Point modal, click"
+
+    appManager = KD.getSingleton "appManager"
+    appManager.tell "Account", "showRedeemReferralPointModal"
+

   addHeader:->

@@ -39,14 +44,10 @@
         appManager = KD.getSingleton "appManager"
         appManager.tell "Account", "showReferrerModal",
           linkView    : getYourReferrerCode
-          top         : 50
-          left        : 35
-          arrowMargin : 110
-

     wrapper.addSubView redeem = new CustomLinkView
       title : "Redeem Your Referrer Points"
-      click : KD.utils.showRedeemReferralPointModal
+      click : => @showRedeemReferralPointModal()

 class AccountReferralSystemList extends KDListView


recompute selected if over total length

recompute selected if over total length

--- addon/components/bs-pagination/component.coffee
+++ addon/components/bs-pagination/component.coffee
@@ -11,9 +11,30 @@
   classNames: ['pagination']
   classTypePrefix: 'pagination'

-  totalLength: 0
-  itemsPerPage: 10
-  itemsPerGroup: 10
+  totalLength: Ember.computed(
+    get: () ->
+      return 0
+    set: (key, val) ->
+      if @selected > val
+        @send('onItemSelected', val)
+      return val
+  )
+  itemsPerPage: Ember.computed(
+    get: () ->
+      return 10
+    set: (key, val) ->
+      if @selected > val
+        @send('onItemSelected', val)
+      return val
+  )
+  itemsPerGroup: Ember.computed(
+    get: () ->
+      return 10
+    set: (key, val) ->
+      if @selected > val
+        @send('onItemSelected', val)
+      return val
+  )

   selected: 1
   currentPageGroup: 0


Include all messages in avatarview, not only incoming ones

--- client/app/CommonViews/messagesList.coffee
+++ client/app/CommonViews/messagesList.coffee
@@ -23,7 +23,7 @@

   fetchMessages:(callback)->
     appManager.tell 'Inbox', 'fetchMessages',
-      as          : 'recipient'
+      # as          : 'recipient'
       limit       : 3
       sort        :
         timestamp : -1


#200 Add return to fight coffeescript madness

--- app/assets/javascripts/oxalis/model/binary/bounding_box.coffee
+++ app/assets/javascripts/oxalis/model/binary/bounding_box.coffee
@@ -12,8 +12,8 @@

     if @boundingBox?

-      @min = [100, 100, 100]
-      @max = [300, 300, 300]
+      @min = [150, 200, 250]
+      @max = [300, 350, 400]

   getBoxForZoomStep : ( zoomStep ) ->
@@ -74,3 +74,5 @@
           index = @cube.getVoxelIndexByVoxelOffset [dx, dy, dz]
           for b in [0...@BYTE_OFFSET]
             bucketData[index + b] = 0
+
+    return


Fix problem with shared feature list for terms

--- app/assets/javascripts/rglossa/components/cwb-multiword.coffee
+++ app/assets/javascripts/rglossa/components/cwb-multiword.coffee
@@ -1,8 +1,8 @@
 # Helper object
 App.CwbMultiwordTerm = Em.Object.extend
-  word:     null
+  word:     ''
   pos:      null
-  features: []
+  features: null
   min:      null
   max:      null

@@ -47,8 +47,7 @@
       else
         dq.push App.CwbMultiwordTerm.create
           word: item.substring(1, item.length-1)
-          min: min
-          max: max
+          features: []

         min = null
         max = null
@@ -139,9 +138,7 @@

   addTerm: ->
     newTerm = App.CwbMultiwordTerm.create
-      word: ''
-      min: null
-      max: null
+      features: []
       isLast: true

     dq = @get('displayedQuery')


Supervisord: restart apps that are consuming memory

--- deployment/supervisord.coffee
+++ deployment/supervisord.coffee
@@ -111,6 +111,13 @@
     \n
     """

+  conf += """
+  [eventlistener:memmon]
+  command=memmon -g environment=4192MB -m sysops+supervisord@koding.com
+  events=TICK_60
+
+  """
+
   fs.writeFileSync "./deployment/generated_files/supervisord.conf", conf

   return conf


Use only one reactiveVar to manage tableExporting state

--- client/controllers/downloadCSVModal.coffee
+++ client/controllers/downloadCSVModal.coffee
@@ -1,10 +1,5 @@
 Template.downloadCSVModal.onCreated ->
-  @prepared = new ReactiveVar(null)
-  @preparing = new ReactiveVar(false)
-
-  @autorun =>
-    if @prepared.get()
-      @preparing.set(false)
+  @tableExport = new ReactiveVar(true)

 Template.downloadCSVModal.onRendered ->
   @data.rendered()
@@ -14,16 +9,15 @@
     row[field]

   preparing: ->
-    Template.instance().preparing.get()
+    not Template.instance().tableExport.get()

 Template.downloadCSVModal.events
   'click .download-csv': (event, instance) ->
-    instance.prepared.set(null)
-    instance.preparing.set(true)
+    instance.tableExport.set(null)
     fileType = $(event.currentTarget).attr('data-type')
     table = instance.$('table')
     if table.length
       # Delay export so UI can respond to change in reactiveVars
       setTimeout ->
-        instance.prepared.set(table.tableExport(type: fileType).length)
+        instance.tableExport.set(table.tableExport(type: fileType).length)
       , 100


добавил фильтр при загрузке фоток

добавил фильтр при загрузке фоток

--- app/assets/javascripts/redde-admin/fileapi.js.coffee
+++ app/assets/javascripts/redde-admin/fileapi.js.coffee
@@ -9,6 +9,7 @@
     autoUpload: true
     dataType: 'html'
     multiple: true
+    accept: "image/*"
     onFileComplete: (e, uiEvt)->
       if uiEvt.status is 200 && uiEvt.error is false
         $('.js-photos').append( uiEvt.result )


separate api from options in constructor

--- client.coffee
+++ client.coffee
@@ -34,27 +34,26 @@

-  constructor: (options) ->
-    @api = options
+  constructor: (@api, options={}) ->
+    {@authorizer} = options
     @shred = new Shred()

     # Validate API specification
     required_fields = ["schemas", "resources", "directory"]
     missing_fields = []
     for field in required_fields
-      unless options[field]
+      unless @api[field]
         missing_fields.push(field)

     if missing_fields.length != 0
       throw new Error("API specification is missing fields: #{missing_fields.join(', ')}")

-    @schema_manager = new SchemaManager(options.schemas...)
-    @authorizer = options.authorizer
-
-    @resource_constructors = @create_resource_constructors(options.resources)
+    @schema_manager = new SchemaManager(@api.schemas...)
+
+    @resource_constructors = @create_resource_constructors(@api.resources)

     @resources = {}
-    @create_directory(options.directory, @resource_constructors)
+    @create_directory(@api.directory, @resource_constructors)

   # Create resource instances using the URLs supplied in the service
@@ -200,9 +199,6 @@
       command.push "  #{url}"
       command.join(" \\\n")

-    authorize: (type, action) ->
-      @patchboard_client.authorizer.call(@, type, action)
-

   # Returns a function intended to be used as a method on a
   # Resource instance.
@@ -248,8 +244,9 @@
           throw new Error("Missing required query param: #{key}")

       if authorization
-        credential = resource.authorize(authorization, name)
-        request.headers["Authorization"] = "#{authorization} #{credential}"
+        if resource.authorize
+          credential = resource.authorize(authorization, name)
+          request.headers["Authorization"] = "#{authorization} #{credential}"

       # copy default headers
       for key, value of default_headers
@@ -269,7 +266,7 @@
           success_handler(response, decorated)
         delete options.on[definition.status]

-      # never silently die on request errors.
+      # never silently fail on request errors.
       # TODO: allow a default request_error handler on Client construction
       request.on.request_error ||= (err) ->
         throw err


Style fixes

Style fixes

--- client.coffee
+++ client.coffee
@@ -117,10 +117,10 @@
 		Plugin.users.iterate (user) !->
 			userLocation = trackAll.ref(user.key())
 			self = (user.key()+"") is (Plugin.userId()+"")
+			if !Geoloc.isSubscribed(user.key())
+				trackAllShow.remove user.key()
+				return
 			if self
-				if !Geoloc.isSubscribed()
-					trackAllShow.remove user.key()
-					return
 				trackSelf = state = Geoloc.track(100,5)
 			Obs.observe !->
 				if !userLocation?.isHash() and !self
@@ -164,17 +164,15 @@
 							Obs.observe !->
 								lastUpdate = userLocation.get('time')
 								if ((new Date()/1000)-lastUpdate) > 60*60 # Make old locations less visible
-									style =
-										opacity: 0.7
+									Dom.style opacity: 0.7
 								else
-									style =
-										opacity: 1
-								Ui.avatar Plugin.userAvatar(if self then Plugin.userId() else userLocation.key()), size: 40, style: style
+									Dom.style opacity: 1
+								Ui.avatar Plugin.userAvatar(if self then Plugin.userId() else userLocation.key()), size: 40
 							Dom.style
 								borderRadius: "50%"
 								backgroundSize: "contain"
 								backgroundRepeat: "no-repeat"
-								backgroundColor: "#FFF"
+								backgroundColor: '#fff'
 						# Popup trigger
 						Dom.onTap !->
 							if showPopup.peek() is userLocation.key()


Fixes #21 - curl equivalents of actions

Fixes #21 - curl equivalents of actions

--- client.coffee
+++ client.coffee
@@ -121,8 +121,8 @@

   # returns a function intended to be bound to a resource instance
   register_action: (name) ->
-    (data) ->
-      request = @_prepare_request(name, data)
+    (options) ->
+      request = @_prepare_request(name, options)
       @patchboard_client.shred.request(request)

   resource_methods:
@@ -141,6 +141,22 @@
         # and relay into the user-supplied error handler.
         throw new Error("No such action defined: #{name}")

+    # returns a string that (when logged to console) can be used as the
+    # curl command that exactly represents this action.
+    curl: (name, options) ->
+      request = @_prepare_request(name, options)
+      {method, url, headers, content} = request
+      agent = headers["User-Agent"]
+      command = []
+      command.push "curl -v -A '#{agent}' -X #{method}"
+      for header, value of headers when header != "User-Agent"
+        command.push "  -H '#{header}: #{value}'"
+
+      if content
+        command.push "  -d '#{JSON.stringify(content)}'"
+      command.push "  #{url}"
+      command.join(" \\\n")
+
     authorize: (type, action) ->
       @patchboard_client.authorizer.call(@, type, action)

@@ -170,7 +186,8 @@
       request =
         url: resource.url
         method: method
-        headers: {}
+        headers:
+          "User-Agent": "patchboard_client"
         query: options.query
         cookieJar: null
         on: {}


hide/show syllabus edit button when editing/not

fixes CNVS-30062

Hides the "Edit" button while editing a syllabus and shows it again
after you finish editing

Test Plan:
* Edit a syllabus
* Stop editing the syllabus in various ways (cancel/submit/trigger an
  error somehow, if that's even possible)
* Verify that the "Edit" button is visible if and only if you are not
  currently editing the syllabus

Change-Id: I0e2e4f7f44b8cf0b8e2ed46b9540b95cf1186a2a
Reviewed-on: https://gerrit.instructure.com/82865
Tested-by: Jenkins
Reviewed-by: Steven Burnett <a0b450117c5d20a22d5c4d52bb8ae13adee003d8@instructure.com>
QA-Review: Deepeeca Soundarrajan <5bcd4b623f00b7fc2942cd1c65c245b30a48a61f@instructure.com>
Product-Review: Andrew Huff <b334cc62d1472ff334a155064665c29200a80f59@instructure.com>

--- app/coffeescripts/behaviors/SyllabusBehaviors.coffee
+++ app/coffeescripts/behaviors/SyllabusBehaviors.coffee
@@ -197,6 +197,7 @@

     $edit_course_syllabus_form.on 'edit', ->
       $edit_course_syllabus_form.show()
+      $edit_syllabus_link.hide()
       $course_syllabus.hide()
       $course_syllabus_details.hide()
       $course_syllabus_body.val($course_syllabus.data('syllabus_body'))
@@ -206,6 +207,7 @@

     $edit_course_syllabus_form.on 'hide_edit', ->
       $edit_course_syllabus_form.hide()
+      $edit_syllabus_link.show()
       $course_syllabus.show()
       text = $.trim $course_syllabus.html()
       $course_syllabus_details.showIf not text