Fix water splashes

Fix water splashes

--- app/scripts/components/utils/WaterSplashes.coffee
+++ app/scripts/components/utils/WaterSplashes.coffee
@@ -16,7 +16,7 @@
     return if window.Game.explosionMode?
     @cooldown -= fd.dt
     if (@y + @h + @detectionOffset > @sealevel) and (@y < @sealevel) and (@cooldown <= 0)
-      speed = @waterSplashSpeed ? @speed
+      speed = @waterSplashSpeed ? @defaultSpeed
       @cooldown = 70
       upwards = 1
       if @_lastWaterY isnt @y


Improve wording

Improve wording

--- app/scripts/lazerscripts/stage1/stage1boss.coffee
+++ app/scripts/lazerscripts/stage1/stage1boss.coffee
@@ -151,7 +151,7 @@
         @moveTo(x: .75, y: .41)
         @sequence(
           @wait 500
-          @say 'Drone Commander', 'We have control now! You will suffer!\nEarths defences are in our hands!'
+          @say 'Drone Commander', 'We have control over the AI now! You will suffer!\nEarths defences are in our hands!'
         )
       )
       @laugh()


New boss animation

New boss animation

--- app/scripts/lazerscripts/stage1/stage1boss.coffee
+++ app/scripts/lazerscripts/stage1/stage1boss.coffee
@@ -211,3 +211,56 @@
       )
     )

+class Game.Scripts.Stage1BossLeaving extends Game.Scripts.Stage1Boss
+  spawn: ->
+    Crafty.e('LargeDrone').drone(
+      health: 134000
+      x: Crafty.viewport.width + 40
+      y: Crafty.viewport.height * .5
+      speed: 100
+    )
+
+  execute: ->
+    @inventoryAdd 'item', 'lasers', ->
+      Crafty.e('PowerUp').powerUp(contains: 'lasers', marking: 'L')
+
+    console.log 'in leaving script'
+
+    @sequence(
+      @animate 'slow', -1, 'eye'
+      @pickTarget('PlayerControlledShip')
+      @moveTo(@targetLocation(), x: .845)
+      @attackCycle()
+      @leaveScreen()
+    )
+
+  leaveScreen: ->
+    @sequence(
+      @animate 'emptyWing', 0, 'wing'
+      => @entity.eye.destroy() # TODO: This is buggy when scaling enemy
+      @sendToBackground(0.9, -100)
+      @parallel(
+        @moveTo(x: -.15, speed: 200)
+        @scale(0.7, duration: 3000)
+      )
+      => @entity.flip('X')
+      @sendToBackground(0.7, -800)
+      @parallel(
+        @moveTo('MiliBase', speed: 200)
+        @scale(0.3, duration: 3000)
+      )
+    )
+
+  attackCycle: ->
+    @repeat 4, @sequence(
+      @async @runScript(Game.Scripts.Stage1BossRocket, @location())
+      @animate 'emptyWing', 0, 'wing'
+      @parallel(
+        @moveTo(@targetLocation(offsetY: -20), x: .845)
+        @sequence(
+          @animate 'reload', 0, 'wing'
+          @wait 1000
+        )
+      )
+    )
+


ssh: Move from resin-sdk-preconfigured to resin-sdk

Change-type: patch
Signed-off-by: Will Boyce <37d41699bdee4fcb969ca499eb0f8b82c60d59cc@resin.io>

--- lib/actions/ssh.coffee
+++ lib/actions/ssh.coffee
@@ -58,7 +58,7 @@
 		normalizeUuidProp(params)
 		child_process = require('child_process')
 		Promise = require('bluebird')
-		resin = require('resin-sdk-preconfigured')
+		resin = require('resin-sdk').fromSharedOptions()
 		_ = require('lodash')
 		bash = require('bash')
 		hasbin = require('hasbin')


breaking long lines created by 803a7d06e304854396216bbdf983e9eefd40a1dd

--- nodes.coffee
+++ nodes.coffee
@@ -1699,7 +1699,8 @@
     body     = @ensureBlock(@body)
     bodyc    = body.compile o
     if body.expressions.length is 1 and !@elseBody and !child and bodyc and -1 is bodyc.indexOf '\n'
-      return "#{@tab}if (#{cond}) #{bodyc.replace /^\s+/, ''}"
+      separator = if cond.length + bodyc.length > 100 then "\n#{@tab}#{TAB}" else ' '
+      return "#{@tab}if (#{cond})#{separator}#{bodyc.replace /^\s+/, ''}"
     bodyc    = "\n#{bodyc}\n#{@tab}" if bodyc
     ifPart   = "if (#{cond}) {#{bodyc}}"
     ifPart   = @tab + ifPart unless child


test registration with invalid token

test registration with invalid token

--- services/web/test/acceptance/coffee/ProjectInviteTests.coffee
+++ services/web/test/acceptance/coffee/ProjectInviteTests.coffee
@@ -280,7 +280,7 @@
 					(cb) => revokeInvite(@sendingUser, @projectId, @invite._id, cb)
 				], done

-			describe 'registration prompt workflow', ->
+			describe 'registration prompt workflow with valid token', ->

 				it 'should redirect to the register page', (done) ->
 					Async.series [
@@ -300,3 +300,28 @@
 						(cb) =>
 							expectAcceptInviteAndRedirect @user, @invite, cb
 					], done
+
+			describe 'registration prompt workflow with non-valid token', ->
+
+				before (done)->
+					@user.logout done
+
+				it 'should redirect to the register page', (done) ->
+					Async.series [
+						(cb) => expectInviteRedirectToRegister(@user, @link, cb)
+					], done
+
+				it 'should display invalid-invite if the user registers a new account', (done) ->
+					badLink = @link.replace(@invite.token, 'not_a_real_token')
+					Async.series [
+						(cb) =>
+							expectInviteRedirectToRegister @user, badLink, (err, redirectUrl) =>
+								@_redir = redirectUrl
+								cb()
+						(cb) =>
+							expectRegistrationRedirectToInvite @user, "some_email@example.com", @_redir, badLink, cb
+						(cb) =>
+							expectInvalidInvitePage @user, badLink, cb
+						(cb) =>
+							expectNoProjectAccess @user, @invite.projectId, cb
+					], done


Handle socket closed events.

Handle socket closed events.

--- lib/connection2.coffee
+++ lib/connection2.coffee
@@ -164,6 +164,8 @@
     @socket.connect(@config.options.port, @config.server)
     @socket.on('error', @socketError)
     @socket.on('connect', @socketConnect)
+    @socket.on('close', @socketClose)
+    @socket.on('end', @socketClose)

     @messageIo = new MessageIO(@socket, @config.options.packetSize, @debug)
     @messageIo.on('packet', (packet) =>
@@ -217,6 +219,10 @@
   socketConnect: =>
     @debug.log("connected to #{@config.server}:#{@config.options.port}")
     @dispatchEvent('socketConnect')
+
+  socketClose: =>
+    @debug.log("connection to #{@config.server}:#{@config.options.port} closed")
+    @transitionTo(@STATE.FINAL)

   sendPreLogin: ->
     payload = new PreloginPayload()


Add comment

Add comment

--- calculate_member_activity.coffee
+++ calculate_member_activity.coffee
@@ -4,6 +4,8 @@
 moment = require 'moment'

 # What different activities are worth.
+# Points have a 1/2 life of six months. So if a member created a post
+# exactly six months ago, that action is now worth 2.5 points.
 COMMENT = 10
 LIKE = 1
 POST = 5


After open a file take focus from file tree

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -122,6 +122,7 @@
       when "file"
         @openFile nodeView
         @emit "file.opened", nodeData
+        @setBlurState()

   openFile:(nodeView, event)->


user can upload same image after remove team logo

--- client/home/lib/myteam/components/hometeamsettings/container.coffee
+++ client/home/lib/myteam/components/hometeamsettings/container.coffee
@@ -36,8 +36,8 @@

   onUploadInput: ->

+    [file] = @refs.view.input.files

-    [file] = @refs.view.input.files
     return  unless file
     @setState { loading: yes }
     reader = new FileReader
@@ -46,6 +46,7 @@
       name = "#{@state.team.get 'slug'}-logo-#{Date.now()}.png"
       mimeType = file.type
       content = file
+
       TeamFlux.actions.uploads3({ name, content, mimeType })
       .then ({ url }) =>
         dataToUpdate =
@@ -66,7 +67,9 @@
   onClickLogo: -> @refs.view.input.click()

-  onRemoveLogo: -> @updateTeam { dataToUpdate: customize: { logo: DEFAULT_LOGOPATH } }
+  onRemoveLogo: ->
+    @refs.view.input.value = null
+    @updateTeam { dataToUpdate: customize: { logo: DEFAULT_LOGOPATH } }

   onUpdate: ->


Teamwork: Big clean-up. Reimplementing invite, export, share logic.

--- client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -1,14 +1,15 @@
 class CollaborativeWorkspace extends Workspace

   init: ->
+    {createUserList, enableChat} = @getOptions()
     @nickname    = KD.nick()
     @sessionData = []
     @users       = {}
     @createRemoteInstance()
     @createLoader()
     @fetchUsers()
-    @createUserListContainer()
-    @createChat()  if @getOptions().enableChat
+    @createUserListContainer()  if createUserList
+    @createChat()               if enableChat
     @bindRemoteEvents()

   createChat: ->
@@ -258,10 +259,6 @@
       delegate  : this
     }

-  setWatchMode: (targetUsername) ->
-    username = KD.nick()
-    @watchRef.child(username).set targetUsername
-
   addToHistory: (data) ->
     target       = @historyRef.child Date.now()
     data         = message: data  if typeof data is "string"


refactored the mockjax routes

refactored the mockjax routes


add extend param to createConstructor(); add docs

--- obj.coffee
+++ obj.coffee
@@ -2,10 +2,18 @@

   # Return a function which creates a new object with the given prototype and initializes this object with the given
   # constructor function.
-  createConstructor: (prototype, constructor) ->
+  # Also copy optional `extend` object content to returned function.
+  createConstructor: (prototype, constructor, extend) ->
+    # Create function which forwards to given constructor function.
     F = (args) ->
       constructor.apply @, args
+      @ # overwrite contructor return value.
+    # Set functions prototype field.
     F.prototype = prototype
-    ->
+    # Create function which forwards to given constructor while setting prototype to new object.
+    f = ->
       new F arguments
-
+    # Add static fields to function.
+    for key, value of extend
+      f[key] = value
+    f


Revert "test"

Revert "test"

This reverts commit c0f3a55cce9856f8f7d183f3d782c6c0dcc18c50.

--- app/assets/javascripts/gmaps/presenter.coffee
+++ app/assets/javascripts/gmaps/presenter.coffee
@@ -7,7 +7,7 @@
     @infowindow = new (google.maps.InfoWindow)({ maxWidth: 200 })

     @marker_image_default = { url: Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_1') }
-    @marker_image_highlighted = { url: image_path('gmaps_marker_highlighted.svg') }
+    @marker_image_highlighted = { url: Clarat.GMaps.Operation.ConstructMap.markerUrl('gmaps_marker_highlighted') }

     return unless @markers


notes on association autosaving roadmap

notes on association autosaving roadmap

--- test/cases/model/relation/associationCursorTest.coffee
+++ test/cases/model/relation/associationCursorTest.coffee
@@ -1,3 +1,21 @@
+# It seems there can be distinct optimizations for client/server association persistence.
+# For the server, the "context" need to be scoped to the current request, you have to save the models in callbacks.
+# One workaround may be to do things like App.User.env(@).where..., where `env` is the controller, so you're
+# creating a cursor mapped to the current request. This way you can create an identity-map/cache of records on a per-request basis.
+# Otherwise, you have to do what rails does and have the AutosaveAssociations callbacks.
+# On the client, however, it may be better to do something like what ember-data is doing with a global transaction.
+# When any of the models change, they are added to a global bucket, then at some point they are all synced.
+# There would be some complications with hasMany through records b/c of ids using that approach, but
+# it would mean that whenever you call `record.save()`, it doesn't have to go through all those callbacks; it
+# would only have to do what happens in the `set` method for associations, such as `_setHasManyAssociation`, which
+# just nullifies/sets ids on the associated models - and then the global transaction would sync them all in a simple way,
+# just calling save/destroy on each. For now though, we can do this in phases:
+# 1. get it working on client/server the way rails does it with autosave associations.
+# 2. try out a global transaction on the client and see if that simplifies things.
+#   If so, remove the autosave stuff on the client and build something custom.
+# 3. try the `env` thing in the controller, so you're creating an identity map in the controllers
+#   on a per-request basis. This will pave the way for creating "database transactions" (or getting
+#   as close as possible to them with mongodb). We may find this may work like the client-side global transaction.
 class App.AssociationCursorTest extends Tower.Model
   @hasMany 'associationCursorPosts'
   @hasOne 'associationCursorAddress'


From dashboard link directly to /reviewResult

--- client/components/Result.coffee
+++ client/components/Result.coffee
@@ -14,8 +14,6 @@

 export default Result = (props) ->
     r = props.result
-    problem = r.fullTable._id.substr(1)
-    href =  'http://informatics.mccme.ru/moodle/mod/statements/view3.php?chapterid=' + problem + '&submit&user_id=' + r.fullUser._id
     userHref = "/user/" + r.fullUser._id
     problemHref = '/material/' + r.fullTable._id
     problemName = r.fullTable.name
@@ -24,9 +22,8 @@
     return

-                {moment(r.lastSubmitTime).format('YYYY-MM-DD HH:mm:ss')}
+                <Link to="/reviewResult/#{r._id}">{moment(r.lastSubmitTime).format('YYYY-MM-DD HH:mm:ss')}</Link>
                 {" "}[ x {r.attempts} ]
-                {" "}<Link to="/reviewResult/#{r._id}">{"#"}</Link>

                 <Link to={userHref}>{userName}</Link>


:bug: updated non working private attribute and flow exit code

--- lib/linter-flow.coffee
+++ lib/linter-flow.coffee
@@ -1,6 +1,5 @@
 linterPath = atom.packages.getLoadedPackage('linter').path
 Linter = require "#{linterPath}/lib/linter"
-findFile = require "#{linterPath}/lib/util"

 path = require "path"
 fs = require 'fs'
@@ -55,7 +54,7 @@

             last = _.last(msg.message)
             unless msg.message.length < 2
-              toPush.descr += "(#{last.path.replace(atom.project.path, '.')}:#{last.line})"
+              toPush.descr += "(#{last.path.replace(atom.project.getPath(), '.')}:#{last.line})"

             console.log "Message: #{toPush.message}"
             realMessages.push(toPush)
@@ -102,8 +101,8 @@
     @flowPath = @findFlowInPath()

     @flowEnabled = true
-    @flowEnabled &= atom.project.path and fs.existsSync(atom.project.path)
-    @flowEnabled &= fs.existsSync(path.join(atom.project.path, '.flowconfig'))
+    @flowEnabled &= atom.project.getPath() and fs.existsSync(atom.project.getPath())
+    @flowEnabled &= fs.existsSync(path.join(atom.project.getPath(), '.flowconfig'))
     @flowEnabled &= @flowPath?

     unless @flowEnabled
@@ -112,11 +111,11 @@

     @flowPath = path.join(@flowPath, 'flow')

-    flowServer = spawn(@flowPath, ['start', '--all', '--module', 'node', path.resolve(atom.project.path)])
-    flowServer.on 'close', (code) => @flowEnabled &= (code is 0)
+    flowServer = spawn(@flowPath, ['start', '--all', '--module', 'node', path.resolve(atom.project.getPath())])
+    flowServer.on 'close', (code) => @flowEnabled &= (code is 2)

   destroy: ->
-    spawn(@flowPath, ['--stop', path.resolve(atom.project.path)])
+    spawn(@flowPath, ['--stop', path.resolve(atom.project.getPath())])
     console.log "die"

 module.exports = LinterFlow


corrects test

corrects test

--- test/test-userinput.coffee
+++ test/test-userinput.coffee
@@ -12,7 +12,7 @@
             msg = userinput.buildChatMessage sender, 'foo'
             eql msg.conv_id, 'c123'
             eql msg.image_id, undefined
-            eql msg.message_action_type, [[0, '']]
+            eql msg.message_action_type, [[null, '']]
             eql msg.segs, [[0,'foo']]
             eql msg.segsj, [{text:'foo', type:'TEXT'}]
             assert.isNotNull msg.client_generated_id


Collaboration: Pass type option to bind realtime listeners.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -867,7 +867,7 @@
     @changes = @rtm.getFromModel @realTimeDoc, 'changes'
     @changes?.clear()  if @amIHost

-    @rtm.bindRealtimeListeners @changes
+    @rtm.bindRealtimeListeners @changes, 'list'

     @rtm.on 'ValuesAddedToList', (list, value) =>
       @handleChange value.values[0]  if list is @changes


Update test descriptions

Update test descriptions

Minor tweeks for consistency or to give better test description.

--- test/unit/test-test.coffee
+++ test/unit/test-test.coffee
@@ -165,7 +165,7 @@
         assert.equal response.status, 200
         assert.deepEqual response.body, machine

-    describe 'Init a TestFactory', ->
+    describe 'construct a TestFactory', ->

       globStub = {}
       globStub.sync = sinon.spy((location) ->
@@ -213,7 +213,7 @@

   describe '#url', ->

-    describe 'when call with path does not contain param', ->
+    describe 'when called with path that does not contain param', ->
       testFact = new TestFactory()
       test = testFact.create()
       test.request.path = '/machines'
@@ -221,7 +221,7 @@
       it 'should return origin path', ->
         assert.equal test.url(), '/machines'

-    describe 'when call with path contains param', ->
+    describe 'when called with path that contains param', ->
       testFact = new TestFactory()
       test = testFact.create()
       test.request.path = '/machines/{machine_id}/parts/{part_id}'


Update models/list.coffee

Update models/list.coffee

--- lib/models/list.coffee
+++ lib/models/list.coffee
@@ -1,23 +1,28 @@
 {Collection} = require 'backbone'

-##
-# There are multiple places that atomatigit requires lists of items, navigable
-# by command/keybinding or by click. We do our best to abstract out what they
-# have in common.
-#
-
-module.exports =
+# Public: There are multiple places that atomatigit requires lists of items,
+#   navigable by command/keybinding or by click. We do our best to abstract out
+#   what they have in common.
 class List extends Collection
   selectedIndex: 0
   isSublist: false

+  # Public: Leaf??
+  #
+  # Returns the leaf as {???}.
   leaf: ->
     if @selection()
       @selection().leaf()

+  # Public: Returns the selected entry.
+  #
+  # Returns the selected entry as {ListItem}.
   selection: ->
     @at @selectedIndex

+  # Public: Select a {ListItem} from this List.
+  #
+  # i - The item to select as {ListItem}.
   select: (i) ->
     old_selection = @selectedIndex
     if @selection()
@@ -34,10 +39,14 @@

     old_selection != @selectedIndex

+  # Public: Select the next item from the List.
   next: ->
     return false if @selection() and not @selection().allowNext()
     @select (@selectedIndex + 1)

+  # Public: Select the previous item in the List.
   previous: ->
     return false if @selection() and not @selection().allowPrevious()
     @select(@selectedIndex - 1)
+
+module.exports = List


Hide footer on curator inbox page

Hide footer on curator inbox page

--- client/controllers/footer.coffee
+++ client/controllers/footer.coffee
@@ -1,3 +1,7 @@
 Template.footer.helpers
   hideFooter: ->
-    Router.current().route.getName() is 'event-map'
+    pagesWithoutFooter = [
+      'event-map'
+      'curator-inbox'
+    ]
+    Router.current().route.getName() in pagesWithoutFooter


Team: updated

Team: updated

--- client/landing/About/team.coffee
+++ client/landing/About/team.coffee
@@ -31,13 +31,10 @@
         username  : 'geraint'
         title     : 'Director of Infrastructure'
       ,
-        username  : 'aleksey-m'
-        title     : 'System Administrator'
+        username  : 'erdinc'
+        title     : 'Software Engineer'
       ,
         username  : 'ybrs'
-        title     : 'Software Engineer'
-      ,
-        username  : 'erdinc'
         title     : 'Software Engineer'
       ,
         username  : 'szkl'
@@ -61,11 +58,8 @@
         username  : 'canthefason'
         title     : 'Software Engineer'
       ,
-        username  : 'psanchezdl'
-        title     : 'Growth Engineer'
-      ,
-        username  : 'pablostanley'
-        title     : 'Illustrator'
+        username  : 'nesli'
+        title     : 'Chef de Cuisine'
       ,
         username  : 'dicle'
         title     : 'Business Analyst'
@@ -76,14 +70,20 @@
         username  : 'zeynep'
         title     : 'Finance Assistant'
       ,
+        username  : 'juliap'
+        title     : 'Recruiting & HR'
+      ,
+        username  : 'psanchezdl'
+        title     : 'Growth Engineer'
+      ,
         username  : 'selin'
         title     : 'Data Scientist'
-      ,
-        username  : 'juliapracht'
-        title     : 'Recruiting & HR'
-      ,
-        username  : 'nesli'
-        title     : 'Chef de Cuisine'
+      # ,
+      #   username  : 'pablostanley'
+      #   title     : 'Illustrator'
+      # ,
+      #   username  : 'aleksey-m'
+      #   title     : 'System Administrator'
       # ,
       #   username  : 'leeolayvar'
       #   title     : 'Community Manager'


create _putState to combine functionality for setState and new addState

--- client/navigator-location.coffee
+++ client/navigator-location.coffee
@@ -26,11 +26,11 @@
         action.call context, context.location, c
     return

+  # add more state info to the current state
+  addState: (moreState) -> @_putState Nav.state, moreState
+
   # set state in the browser's push api for the current location
-  setState: (state={}) ->
-    location = state?.location ? @_buildLocation()
-    @history.replaceState state, document.title, location
-    return
+  setState: (state={}) -> @_putState state

   # configure with options and set location to current browser location
   # triggering actions
@@ -143,6 +143,12 @@
         if @_the.location?.port? then ':' + @_the.location.port else ''
     return origin

+  _putState: (state, extraState={}) ->
+    location = state?.location ? @_buildLocation()
+    Nav.state = _.extend state, moreState
+    @history.replaceState Nav.state, document.title, location
+    return
+
   # setup Nav based on options.
   _setup: (options) -> # TODO: base path, hashbang. use @_decode
     @_the =


Fixed bug where refreshParameter tasks is always added on inform.

--- acs.coffee
+++ acs.coffee
@@ -33,7 +33,7 @@
   if config.DEBUG_DEVICES[currentRequest.deviceId]
     dump = "# RESPONSE #{new Date(Date.now())}\n" + JSON.stringify(res.headers) + "\n#{res.data}\n\n"
     fs = require('fs').appendFile("debug/#{currentRequest.deviceId}.dump", dump, (err) ->
-      throw new Error(err) if err?
+      throw err if err
     )

   currentRequest.httpResponse.writeHead(res.code, res.headers)
@@ -101,9 +101,13 @@

   if Object.keys(updates).length > 0 or Object.keys(deletes).length > 0
     db.devicesCollection.update({'_id' : currentRequest.deviceId}, {'$set' : updates, '$unset' : deletes}, {}, (err, count) ->
+      throw err if err
       # Clear aliases cache if there's a new aliased parameter that has not been included in aliases cache
       if newAliases?.length
-        db.getAliases((aliases) ->
+        db.redisClient.get('aliases', (err, res) ->
+          throw err if err
+          return if not res?
+          aliases = JSON.parse(res)
           for a in newAliases
             if not aliases[a]?
               db.redisClient.del('aliases', (err, res) ->
@@ -172,6 +176,7 @@
         projection[path + '_timestamp'] = 1

     db.devicesCollection.findOne({'_id' : deviceId}, projection, (err, device) ->
+      throw err if err
       lastBootstrap ?= device?._lastBootstrap
       _tasks = []

@@ -179,7 +184,7 @@
       traverse = (reference, actual) ->
         for k of reference
           continue if k[0] == '_'
-          if not actual[k]?._timestamp? or actual[k]._timestamp > lastBootstrap
+          if not actual[k]?._timestamp? or actual[k]._timestamp < lastBootstrap
             _tasks.push({device: deviceId, name : 'refreshObject', objectName : reference[k]._path})
           else
             traverse(reference[k], actual[k])
@@ -223,10 +228,10 @@
 runTask = (currentRequest, task, methodResponse) ->
   timeDiff = process.hrtime()
   tasks.task(task, methodResponse, (err, status, cwmpResponse, deviceUpdates) ->
-    throw new Error(err) if err?
+    throw err if err
     # TODO handle error
     updateDevice(currentRequest, deviceUpdates, (err) ->
-      throw new Error(err) if err?
+      throw err if err

       timeDiff = process.hrtime(timeDiff)[0] + 1
       if timeDiff > 3 # in seconds
@@ -271,7 +276,7 @@
             nextTask(currentRequest)
           )
         else
-          throw Error('Unknown task status')
+          throw new Error('Unknown task status')
     )
   )

@@ -349,7 +354,7 @@
     else if isTaskExpired(task)
       util.log("#{currentRequest.deviceId}: Task is expired #{task.name}(#{task._id})")
       db.tasksCollection.remove({'_id' : mongodb.ObjectID(String(task._id))}, {safe: true}, (err, removed) ->
-        throw new Error(err) if err?
+        throw err if err
         nextTask(currentRequest)
       )
     else
@@ -397,7 +402,7 @@
     if config.DEBUG_DEVICES[currentRequest.deviceId]
       dump = "# REQUEST #{new Date(Date.now())}\n" + JSON.stringify(httpRequest.headers) + "\n#{httpRequest.getBody()}\n\n"
       require('fs').appendFile("debug/#{currentRequest.deviceId}.dump", dump, (err) ->
-        throw new Error(err) if err
+        throw err if err
       )

     if cwmpRequest.methodRequest?
@@ -430,6 +435,7 @@
                 'InternetGatewayDevice.DeviceInfo.ProductClass' : 1,
               }
               db.devicesCollection.findOne({'_id' : currentRequest.deviceId}, projection, (err, device) ->
+                throw err if err
                 manufacturer = device.InternetGatewayDevice.DeviceInfo.Manufacturer._value
                 productClass = device.InternetGatewayDevice.DeviceInfo.ProductClass._value
                 db.filesCollection.findOne({'metadata.FileType' : '1 Firmware Upgrade Image', 'metadata.Manufacturer' : manufacturer, 'metadata.ProductClass' : productClass, 'metadata.SoftwareVersion' : presetSoftwareVersion}, {_id : 1}, (err, file) ->
@@ -454,7 +460,7 @@
           )
         )
       else
-        throw Error('ACS method not supported')
+        throw new Error('ACS method not supported')
     else if cwmpRequest.methodResponse?
       taskId = cwmpRequest.id


Cleaned up serializer code.

Cleaned up serializer code.

--- lib/server/browser_server/serializer.coffee
+++ lib/server/browser_server/serializer.coffee
@@ -1,6 +1,18 @@
 URL            = require('url')
 Config         = require('../../shared/config')
 NodeCompressor = require('../../shared/node_compressor')
+
+# Depth-first search
+dfs = (node, filter, visit) ->
+    if filter(node)
+        visit(node)
+        tagName = node.tagName?.toLowerCase()
+        if tagName == 'iframe' || tagName == 'frame'
+            dfs(node.contentDocument, filter, visit)
+        else if node.hasChildNodes()
+            for child in node.childNodes
+                dfs(child, filter, visit)
+
 # Each node in the DOM is represented by an object.
 # A serialized DOM (or snapshot) is an array of these objects.
 # Sample record:
@@ -18,101 +30,73 @@
     if document.nodeType != 9
         throw new Error("Couldn't find document")

-    cmds = []
-
     # A filter that skips script tags.
     filter = (node) ->
         if !node? || node.tagName?.toLowerCase() == 'script'
             return false
         return true

+    cmds = []
     dfs root, filter, (node) ->
         typeStr = nodeTypeToString[node.nodeType]
-        func = serializers[typeStr]
-        if func == undefined
-            console.log("Can't create instructions for #{typeStr}")
-            return
-        # Each serializer pushes its command(s) onto the command stack.
-        func(node, cmds, document, resources)
+        switch typeStr
+            when 'Element'
+                attributes = null
+                if node.attributes?.length > 0
+                    attributes = copyElementAttrs(node, document)
+                record =
+                    type   : 'element'
+                    id     : node.__nodeID
+                    parent : node.parentNode.__nodeID
+                    name   : node.tagName
+                if attributes != null
+                    record.attributes = attributes
+                if node.ownerDocument != document
+                    record.ownerDocument = node.ownerDocument.__nodeID
+                if /^i?frame$/.test(node.tagName.toLowerCase())
+                    record.docID = node.contentDocument.__nodeID
+                if node.__registeredListeners?.length
+                    record.events = node.__registeredListeners
+                cmds.push(NodeCompressor.compress(record))

+            when 'Comment', 'Text'
+                # The issue is that JSDOM gives Document 3 child nodes: the HTML
+                # element and a Text element.  We get a HIERARCHY_REQUEST_ERR in the
+                # client browser if we try to insert a Text node as the child of the
+                # Document
+                if node.parentNode.nodeType != 9 # Document node
+                    record =
+                        type   : typeStr.toLowerCase()
+                        id     : node.__nodeID
+                        parent : node.parentNode.__nodeID
+                        value  : node.nodeValue
+                    if node.ownerDocument != document
+                        record.ownerDocument = node.ownerDocument.__nodeID
+                    cmds.push(NodeCompressor.compress(record))
     return cmds

-# Depth-first search
-dfs = (node, filter, visit) ->
-    if filter(node)
-        visit(node)
-        tagName = node.tagName?.toLowerCase()
-        if tagName == 'iframe' || tagName == 'frame'
-            dfs(node.contentDocument, filter, visit)
-        else if node.hasChildNodes()
-            for child in node.childNodes
-                dfs(child, filter, visit)
-
-serializers =
-    Document : () ->
-        undefined
-
-    # TODO: Maybe if node.tagName == '[i]frame]' pass off to a helper.
-    Element : (node, cmds, topDoc, resources) ->
-        tagName = node.tagName.toLowerCase()
-        attributes = null
-        if node.attributes?.length > 0
-            attributes = {}
-            for attr in node.attributes
-                {name, value} = attr
+copyElementAttrs = (node, document) ->
+    tagName = node.tagName.toLowerCase()
+    attrs = {}
+    if node.attributes?.length > 0
+        attributes = {}
+        for attr in node.attributes
+            {name, value} = attr
+            lowercase = name.toLowerCase()
+            if (lowercase == 'src') || ((tagName == 'link') && (lowercase == 'href'))
                 # Don't send src attribute for frames or iframes
-                if /^i?frame$/.test(tagName) && (name.toLowerCase() == 'src')
+                if /^i?frame$/.test(tagName) && (lowercase == 'src')
                     continue
-                lowercase = name.toLowerCase()
-                if (lowercase == 'src') || ((tagName == 'link') && (lowercase == 'href'))
-                    if value
-                        # If we're using the resource proxy, substitute the URL with a
-                        # ResourceProxy number.
-                        if Config.resourceProxy
-                            value = "#{resources.addURL(value)}"
-                        # Otherwise, convert it to an absolute URL.
-                        else
-                            value = URL.resolve(topDoc.location, value)
-                attributes[name] = value
-        record =
-            type   : 'element'
-            id     : node.__nodeID
-            parent : node.parentNode.__nodeID
-            name   : node.tagName
-        if attributes != null
-            record.attributes = attributes
-        if node.ownerDocument != topDoc
-            record.ownerDocument = node.ownerDocument.__nodeID
-        if /^i?frame$/.test(tagName)
-            record.docID = node.contentDocument.__nodeID
-        if node.__registeredListeners?.length
-            record.events = node.__registeredListeners
-        cmds.push(NodeCompressor.compress(record))
-
-    Comment : (node, cmds, topDoc, resources) ->
-        record =
-            type : 'comment'
-            id : node.__nodeID
-            parent : node.parentNode.__nodeID
-            value : node.nodeValue
-        if node.ownerDocument != topDoc
-            record.ownerDocument = node.ownerDocument.__nodeID
-        cmds.push(NodeCompressor.compress(record))
-
-    Text : (node, cmds, topDoc, resources) ->
-        # The issue is that JSDOM gives Document 3 child nodes: the HTML
-        # element and a Text element.  We get a HIERARCHY_REQUEST_ERR in the
-        # client browser if we try to insert a Text node as the child of the
-        # Document
-        if node.parentNode.nodeType != 9 # Document node
-            record =
-                type   : 'text'
-                id     : node.__nodeID
-                parent : node.parentNode.__nodeID
-                value  : node.data
-            if node.ownerDocument != topDoc
-                record.ownerDocument = node.ownerDocument.__nodeID
-            cmds.push(NodeCompressor.compress(record))
+                if value
+                    # If we're using the resource proxy, substitute the URL with a
+                    # ResourceProxy number.
+                    if Config.resourceProxy
+                        value = "#{resources.addURL(value)}"
+                    # Otherwise, convert it to an absolute URL.
+                    else
+                        value = URL.resolve(document.location, value)
+            attrs[name] = value
+    return attrs

 nodeTypeToString = [
     0


quick fix for browser tests.

quick fix for browser tests.


cleanup

cleanup

--- lib/post-window.coffee
+++ lib/post-window.coffee
@@ -34,40 +34,3 @@

   addMessage: (text) ->
     @posts.append "<div>#{text}</div>"
-
-# handleEvents: ->
-  # @subscribe this, 'core:move-up', => @scrollUp()
-  # @subscribe this, 'core:move-down', => @scrollDown()
-  # @subscribe this, 'core:save-as', =>
-  #   @saveAs()
-  #   false
-  # @subscribe this, 'core:copy', =>
-  #   return false if @copyToClipboard()
-
-  # @subscribeToCommand atom.workspaceView, 'post-window:zoom-in', =>
-  #   zoomLevel = parseFloat(@css('zoom')) or 1
-  #   @css('zoom', zoomLevel + .1)
-  #
-  # @subscribeToCommand atom.workspaceView, 'post-window:zoom-out', =>
-  #   zoomLevel = parseFloat(@css('zoom')) or 1
-  #   @css('zoom', zoomLevel - .1)
-  #
-  # @subscribeToCommand atom.workspaceView, 'post-window:reset-zoom', =>
-  #   @css('zoom', 1)
-
-  # copyToClipboard: ->
-  #   return false if @loading
-  #
-  #   selection = window.getSelection()
-  #   selectedText = selection.toString()
-  #   selectedNode = selection.baseNode
-  #
-  #   # Use default copy event handler if there is selected text inside this view
-  #   return false if selectedText and selectedNode? and $.contains(@[0], selectedNode)
-  #
-  #   atom.clipboard.write(@[0].innerHTML)
-  #   true
-  #
-
-  # isEqual: (other) ->
-  #   @[0] is other?[0] # Compare DOM elements


dashboardapp : if group is koding, remove the logo upload form

--- client/Dashboard/views/generalsettingsview.coffee
+++ client/Dashboard/views/generalsettingsview.coffee
@@ -47,10 +47,6 @@
         #           location.replace('/')
         #     , data
       fields:
-        Logo                :
-          label             : "Logo"
-          itemClass         : GroupLogoSettings
-
         Title               :
           label             : "Group Name"
           name              : "title"
@@ -84,6 +80,11 @@
             { title : "Hidden",     value : "hidden" }
           ]

+    if group.slug isnt "koding"
+      formOptions.fields["Logo"] =
+        label       : "Logo"
+        itemClass   : GroupLogoSettings
+
     @settingsForm = new KDFormViewWithFields formOptions, group

     # unless KD.config.roles? and 'owner' in KD.config.roles


fixed beta modal link (top right)

fixed beta modal link (top right)

--- client/app/MainApp/maincontroller/mainview.coffee
+++ client/app/MainApp/maincontroller/mainview.coffee
@@ -156,7 +156,7 @@
       attributes  :
         href      : "#"
       partial     : "What you should know about this beta..."
-      click       : (pubInst, event)=>
+      click       : (event)=>
         if $(event.target).is 'span'
           link.hide()
         else


KDOverlayView: Refactored for KDFramework standards.

--- core/KDOverlayView.coffee
+++ core/KDOverlayView.coffee
@@ -1,49 +1,47 @@
 class KDOverlayView extends KDView

-  constructor: (options={}, data)->
+  constructor: (options = {}, data) ->

-    {isRemovable, animated, color, transparent, parent, opacity} = options
+    options.isRemovable      ?= yes
+    options.animated         ?= no
+    options.color            ?= no
+    options.transparent      ?= no
+    options.opacity          ?= 0.5
+    options.appendToDomBody  ?= yes
+    options.cssClass          = KD.utils.curry "kdoverlay", options.cssClass

-    cssClass =    ["kdoverlay"]
-    cssClass.push  "animated"         if animated
-    cssClass.push  "transparent"      if transparent
-    options.cssClass = cssClass.join " "
-    super
+    super options, data

-    isRemovable ?= yes
+    @setClass "animated"    if options.animated
+    @setClass "transparent" if options.transparent

-    if color
-      @$().css
-        backgroundColor: color
-        opacity: opacity ? 0.5
+    if options.color
+      @setStyle
+        backgroundColor : options.color
+        opacity         : options.opacity

-    if "string" is typeof parent
-      @$().appendTo $(parent)
-    else if parent instanceof KDView
-      @$().appendTo parent.$()
+    if options.delegate instanceof KDView
+      options.delegate.addSubView this
+      @setCss "position", "absolute"
+    else if options.appendToDomBody
+      @appendToDomBody()

-    if animated
-      @utils.defer =>
-        @$().addClass "in"
+    if options.animated
+      @utils.defer => @setClass "in"
       @utils.wait 300, =>
-        @emit "OverlayAdded", @
+        @emit "OverlayAdded", this
     else
-      @emit "OverlayAdded", @
+      @emit "OverlayAdded", this

-    if isRemovable
-      @$().on "click.overlay", @removeOverlay.bind @
+    if options.isRemovable
+      if options.animated
+        @once "click", =>
+          @unsetClass "in"
+          @utils.wait 300, => @remove()
+      else
+        @once "click", => @remove()

-  removeOverlay: ->
-
+  remove: ->
     @emit "OverlayWillBeRemoved"
-    callback = =>
-      @$().off "click.overlay"
-      @destroy()
-      @emit "OverlayRemoved", @
-
-    if @$().hasClass "animated"
-      @$().removeClass "in"
-      @utils.wait 300, =>
-        callback()
-    else
-      callback()
+    @destroy()
+    @emit "OverlayRemoved", this


Restores Hull.data.api.batch

Restores Hull.data.api.batch

--- src/client/api.coffee
+++ src/client/api.coffee
@@ -229,38 +229,35 @@
           setupCollection.call(api, path)

         api.batch = ->
+          err = new Error 'Incorrect arguments passed to Hull.data.api.batch(). Only Arrays, callback and errback are accepted.'
           args      = slice.call(arguments)
-          # next      = args.shift()
           promises  = []
           requests  = []
           responses = []
           callback  = errback = null
+          arg       = null

           # # Parsing the arguments...
-          while (next)
-            type = typeof next
+          while (arg = args.shift())
+            type = typeof arg
             if (type == 'function')
               if !callback
-                callback = next
+                callback = arg
               else if !errback
-                errback = next
+                errback = arg
               else
-                throw new Error('Incorrect arguments passed to Hull.batch(). Only callback & errback can be defined.')
+                throw err
             else
-              requests.push(next)
-
-            next = args.shift()
-
-          _.map(requests, (request)->
-            if _.isString(request)
-              req = [{ path: request, method: 'get' }]
-            else
-              req = [request]
-            promises.push(message.apply(api, req))
-          )
+              throw err unless _.isArray(arg)
+              requests.push(arg)
+
+          throw new Error('No request given. Aborting') unless requests.length
+          promises = _.map requests, (request)->
+            api.apply(api, request).promise()
+

           # Actual request
-          res = core.data.when.apply($, promises)
+          res = core.data.when.apply(undefined, promises)
           res.then(callback, errback)
           res


editable table modal for all bug

editable table modal for all bug

--- app/assets/javascripts/backbone_js/ladas_editable_table_builder.js.coffee
+++ app/assets/javascripts/backbone_js/ladas_editable_table_builder.js.coffee
@@ -307,14 +307,14 @@
       # a javascrip code can be passed, it will be put as onclick javascript of the button
       EditableTableBuilder.html += ' onclick="' + settings.js_code
       EditableTableBuilder.html += '"'
-
+
     else if it_is_link
       if (settings.confirm)
         EditableTableBuilder.html += ' onclick="if (confirm(\'' + settings.confirm + '\')){ load_page(' + stringified_settings + ',this); }; return false;"'
       else
         EditableTableBuilder.html += ' onclick="load_page(' + stringified_settings + ',this); return false;"'

-
+

     EditableTableBuilder.html += '>' + sliced_text


renamed a method

renamed a method

--- coffee/jquery.contactform.coffee
+++ coffee/jquery.contactform.coffee
@@ -29,7 +29,7 @@
       try
         methods.init_mandrill()
         methods.init_recaptcha()
-        methods.bind_submit()
+        methods.bind_submit_handler()
         methods.set_button_states()
       catch error
         console.log error
@@ -69,7 +69,7 @@
       "data-sent-text": config.button_sent_text

   # bind the forms submit event
-  bind_submit: ->
+  bind_submit_handler: ->
     config.form_el.submit (e) ->
       # prevent form from sending
       e.preventDefault()


Only log errors.

Only log errors.

--- coffeelint-junit-reporter.coffee
+++ coffeelint-junit-reporter.coffee
@@ -19,7 +19,7 @@
       data.push
         _attr:
           name: "#{filename}:#{error.lineNumber} #{error.description}"
-          classname: ""
+          classname: error.context
           time: 0
       data.push
         failure:
@@ -32,7 +32,11 @@
       @options.outFile = @options.outFile || process.env.COFFEELINT_JUNIT || 'lint-results.xml';

     publish: =>
-      testsuites = _.map @errorReport.paths, summarize
+      testsuites = _(@errorReport.paths)
+        .mapValues (errors) ->
+          _.filter errors, ({level}) -> level is 'error'
+        .map summarize
+        .value()
       xmlData = xml({testsuites}, {declaration: true, indent: '  '})
       mkdirp.sync path.dirname(@options.outFile)
       fs.writeFileSync @options.outFile, xmlData, 'utf-8'


removed two tap event

removed two tap event

--- src/quo.gestures.coffee
+++ src/quo.gestures.coffee
@@ -121,8 +121,6 @@
                     drag_direction = _swipeDirection(FIRST_TOUCH[0].x, CURRENT_TOUCH[0].x, FIRST_TOUCH[0].y, CURRENT_TOUCH[0].y)
                     _trigger "drag" + drag_direction
                     anyevent = true
-            if not anyevent
-                _trigger "twoTap"
             _cleanGesture()
         EVENT = undefined


Set default project_cache_length_ms to 1 day

`project_cache_length_ms` was only `60*60*24 = 1.5 min` which is a little bit short. Default of one day seams more reasonable.

--- config/settings.defaults.coffee
+++ config/settings.defaults.coffee
@@ -44,5 +44,5 @@
 			url: "http://localhost:3013"

 	smokeTest: false
-	project_cache_length_ms: 60 * 60 * 24
-	parallelFileDownloads:1+	project_cache_length_ms: 1000 * 60 * 60 * 24
+	parallelFileDownloads:1


Moved guard to callee

Moved guard to callee

This makes overwriting the openController method easier

--- core/app/backbone/factlink/base_controller.coffee
+++ core/app/backbone/factlink/base_controller.coffee
@@ -18,12 +18,11 @@

   getRouteFunction: (name) ->
     (args...) ->
-      @openController()
+      @openController() unless @started
       @onAction?()
       @[name](args...)

   openController: ->
-    return if @started
     FactlinkApp.vent.trigger 'controller:switch'
     @bindTo FactlinkApp.vent, 'controller:switch', @closeController, this
     @started = true


Rename rootView to workspaceView

Rename rootView to workspaceView

--- lib/to-the-hubs.coffee
+++ lib/to-the-hubs.coffee
@@ -4,7 +4,7 @@
   activate: ->
     return unless atom.project.getRepo()?

-    atom.rootView.eachPane (pane) ->
+    atom.workspaceView.eachPane (pane) ->
       pane.command 'github:open', ->
         if itemPath = getActivePath()
           GitHubFile.fromPath(itemPath).open()
@@ -22,7 +22,7 @@
           GitHubFile.fromPath(itemPath).copyUrl(getSelectedRange())

 getActivePath = ->
-  atom.rootView.getActivePaneItem()?.getPath?()
+  atom.workspaceView.getActivePaneItem()?.getPath?()

 getSelectedRange = ->
-  atom.rootView.getActivePaneItem()?.getSelectedBufferRange?()
+  atom.workspaceView.getActivePaneItem()?.getSelectedBufferRange?()


#151 For node radius, take devicePixelRatio into account

--- app/assets/javascripts/oxalis/geometries/materials/particle_material_factory.coffee
+++ app/assets/javascripts/oxalis/geometries/materials/particle_material_factory.coffee
@@ -20,6 +20,9 @@
       showRadius :
         type : "i"
         value : 1
+      devicePixelRatio :
+        type : "f"
+        value : window.devicePixelRatio || 1

     attributes =
       size :
@@ -61,6 +64,7 @@
       uniform float baseVoxel;
       uniform float minParticleSize;
       uniform int   showRadius;
+      uniform float devicePixelRatio;
       varying vec3 vColor;
       attribute float size;

@@ -70,8 +74,8 @@
           vColor = color;
           if (showRadius == 1)
             gl_PointSize = max(
-              size / zoomFactor / baseVoxel * 2.0,
-              minParticleSize );
+              size / zoomFactor / baseVoxel,
+              minParticleSize ) * devicePixelRatio;
           else
             gl_PointSize = minParticleSize;
           gl_Position = projectionMatrix * mvPosition;


activity ticker: add signature annotations

--- workers/social/lib/social/models/activityticker.coffee
+++ workers/social/lib/social/models/activityticker.coffee
@@ -1,14 +1,18 @@
 Bongo          = require "bongo"
 {Relationship} = require "jraphical"

-{secure, daisy, dash, Base} = Bongo
+{secure, daisy, dash, signature, Base} = Bongo

 module.exports = class ActivityTicker extends Base
   @share()

   @set
     sharedMethods :
-      static      : ["fetch"]
+      static      :
+        fetch     : [
+          (signature Function)
+          (signature Object, Function)
+        ]

   relationshipNames = ["follower", "like", "member", "user", "reply", "author"]
   constructorNames  = ["JAccount", "JNewApp", "JGroup", "JTag", "JStatusUpdate", "JComment"]
@@ -79,7 +83,8 @@
       callback null, modifiedEvent

-  @fetch = secure (client, options = {}, callback) ->
+  @fetch = secure (client, options, callback) ->
+    [callback, options] = [options, callback]  unless callback
     {connection: {delegate}} = client

     from = options.from or +(new Date())


adds budget items when adding a support

adds budget items when adding a support

--- app/assets/javascripts/backend/budget.js.coffee
+++ app/assets/javascripts/backend/budget.js.coffee
@@ -1,8 +1,7 @@
 (($) ->
   'use strict'
-  $(document).on 'click keyup', "a[data-budget-add='support']", ->
-    $(this).closest('table').find('tr.appendable').each ->
-      $(this).find('td:last').before('foo	')
+  $(document).on 'click keyup', "a[data-association='support']", ->
+    $("a[data-association='item']").click()
     return false

   return


Disable posts refreshing because of race conditions

--- app/assets/javascripts/components/posts.js.cjsx
+++ app/assets/javascripts/components/posts.js.cjsx
@@ -24,11 +24,9 @@
   componentWillMount: ->
     @fetchPosts()
     $(document).on("fetchPosts", @fetchPosts)
-    @timer = setInterval(@fetchPosts, 5000)

   componentWillUnmount: ->
     $(document).off("fetchPosts", @fetchPosts)
-    clearInterval(@timer)

   render: ->
     <div>


removed a call in preview that doesn't exist anymore.

--- app/assets/javascripts/mercury/region.js.coffee
+++ app/assets/javascripts/mercury/region.js.coffee
@@ -66,13 +66,11 @@
   togglePreview: ->
     if @previewing
       @previewing = false
-      @toggleMarkdownPreview() if @type == 'markupable'
       @element.addClass('mercury-region').removeClass('mercury-region-preview')
       @focus() if Mercury.region == @
     else
       @previewing = true
       @element.addClass('mercury-region-preview').removeClass('mercury-region')
-      @toggleMarkdownPreview(true) if @type == 'markupable'
       Mercury.trigger('region:blurred', {region: @})


disabling product tap - for now

disabling product tap - for now

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -96,9 +96,9 @@
                 fetch_product_images(($ @), render_single_product_image)

-            ($ '.products').on('tap', '.product', ->
-                destination = ($ @).find('.product-link > a').attr('href')
-                location.href = destination)
+                #($ '.products').on('tap', '.product', ->
+                #    destination = ($ @).find('.product-link > a').attr('href')
+                #    location.href = destination)

         if gon.page is 'products_show'
             header_fix()


Delete UserService.coffee

Delete UserService.coffee

--- app/assets/javascripts/users/UserService.coffee
+++ app/assets/javascripts/users/UserService.coffee
@@ -1,55 +0,0 @@
-
-class UserService
-
-    @headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
-    @defaultConfig = { headers: @headers }
-
-    constructor: (@$log, @$http, @$q) ->
-        @$log.debug "constructing UserService"
-
-    listUsers: () ->
-        @$log.debug "listUsers()"
-        deferred = @$q.defer()
-
-        @$http.get("/users")
-        .success((data, status, headers) =>
-                @$log.info("Successfully listed Users - status #{status}")
-                deferred.resolve(data)
-            )
-        .error((data, status, headers) =>
-                @$log.error("Failed to list Users - status #{status}")
-                deferred.reject(data)
-            )
-        deferred.promise
-
-    createUser: (user) ->
-        @$log.debug "createUser #{angular.toJson(user, true)}"
-        deferred = @$q.defer()
-
-        @$http.post('/user', user)
-        .success((data, status, headers) =>
-                @$log.info("Successfully created User - status #{status}")
-                deferred.resolve(data)
-            )
-        .error((data, status, headers) =>
-                @$log.error("Failed to create user - status #{status}")
-                deferred.reject(data)
-            )
-        deferred.promise
-
-    updateUser: (firstName, lastName, user) ->
-      @$log.debug "updateUser #{angular.toJson(user, true)}"
-      deferred = @$q.defer()
-
-      @$http.put("/user/#{firstName}/#{lastName}", user)
-      .success((data, status, headers) =>
-              @$log.info("Successfully updated User - status #{status}")
-              deferred.resolve(data)
-            )
-      .error((data, status, header) =>
-              @$log.error("Failed to update user - status #{status}")
-              deferred.reject(data)
-            )
-      deferred.promise
-
-servicesModule.service('UserService', ['$log', '$http', '$q', UserService])


Don't inject pusher instance if it wasn't created

--- assets/scripts/app/initializers/pusher.coffee
+++ assets/scripts/app/initializers/pusher.coffee
@@ -7,11 +7,11 @@
   if config.pusher.key
     application.pusher = new Pusher(config.pusher)

-  application.register 'pusher:main', application.pusher, { instantiate: false }
+    application.register 'pusher:main', application.pusher, { instantiate: false }

-  application.inject('route', 'pusher', 'pusher:main')
+    application.inject('route', 'pusher', 'pusher:main')

-  application.pusher.store = container.lookup('store:main')
+    application.pusher.store = container.lookup('store:main')

 PusherInitializer =


Fixed issue where today? would occasionally return nothing

--- src/scripts/time.coffee
+++ src/scripts/time.coffee
@@ -345,7 +345,7 @@
         else
           attachments.push punch.slackAttachment()
     else
-      earlyToday = moment.tz({hour: 0, minute: 0, second: 0}, tz)
+      earlyToday = now.clone().hour(0).minute(0).second(0)
       report = user.toRawPayroll(earlyToday, now)
       dateArticle = "today"
       for punch in user.punches


changed interstitial text between peer gradings from 'fetched' to 'fetching'

--- common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee
+++ common/lib/xmodule/xmodule/js/src/peergrading/peer_grading_problem.coffee
@@ -230,7 +230,7 @@
     @calibration_feedback_button.click =>
       @calibration_feedback_panel.hide()
       @grading_wrapper.show()
-      @gentle_alert "Calibration essay saved.  Fetched the next essay."
+      @gentle_alert "Calibration essay saved.  Fetching the next essay."
       @is_calibrated_check()

     @interstitial_page_button.click =>
@@ -349,9 +349,9 @@
       graded = @grading_wrapper.data('graded')+1
       @grading_wrapper.data('graded', graded)
       @grading_wrapper.attr('data-graded', graded) #just in case someone wants to read the DOM
-      message = "
Successfully saved your feedback. Fetched the next essay.
"
+      message = "
Successfully saved your feedback. Fetching the next essay.
"
       if graded >= required
-        message = "
Successfully saved your feedback. Fetched the next essay.

+        message = "
Successfully saved your feedback. Fetching the next essay.

You have completed the required number of peer evaluations, but may
           choose to continue grading if you'd like.
"
       @grading_message.fadeIn()


修复 Entity.remove()删除新增记录时偶尔不能刷新的BUG

修复 Entity.remove()删除新增记录时偶尔不能刷新的BUG

--- src/core/model.coffee
+++ src/core/model.coffee
@@ -511,19 +511,19 @@
 		if items or originItems
 			while item
 				if item instanceof cola.Entity
-					matched = (item.parent is items)
-					if !matched and originItems
+					matched = ((item.parent or item._parent) is items)
+					if not matched and originItems
 						if multiOriginItems
 							for oi in originItems
-								if item.parent is oi
+								if (item.parent or item._parent) is oi
 									matched = true
 									break
 						else
-							matched = (item.parent is originItems)
+							matched = ((item.parent or item._parent) is originItems)
 					if matched
 						itemId = cola.Entity._getEntityId(item)
 						return if itemId then itemScopeMap[itemId] else null
-				item = item.parent
+				item = (item.parent or item._parent)
 		return null

 	processMessage: (bindingPath, path, type, arg) ->


chore(cleanup): spacing

chore(cleanup): spacing

--- e2e/reportCard/ReportCardPage.coffee
+++ e2e/reportCard/ReportCardPage.coffee
@@ -1,6 +1,6 @@
 module.exports = class
 	constructor: ->
-		usernameField =  element By.input   'controller.username'
+		usernameField = element By.input   'controller.username'
 		fullName      = element By.binding '{{controller.reportCard.name}}'

 		@fillUsername = (username) ->


bugfix: appending extensions to all FileBank urls

--- source/02__file_tree.coffee
+++ source/02__file_tree.coffee
@@ -104,7 +104,7 @@
             split_path.push("/")                        ## /a/path/

         path = split_path.join("") + tagName.replace(/\-/g, "/")
-        _no_extension = (path.split('.').length <= 1) or (@path in [".", "/"])
+        _no_extension = true

         parser = document.createElement("a")
         parser.href = path


Update resumable.coffee

Update resumable.coffee

--- samples/coffeescript/resumable.coffee
+++ samples/coffeescript/resumable.coffee
@@ -45,6 +45,7 @@
       maxFileSizeErrorCallback: (file, errorCount) ->
         #TODO @getOpt
         alert(file.fileName +' is too large, please upload files less than ' + @formatSize(@getOpt('maxFileSize')) + '.')
+      dragOverClass: 'dragover'
     @opt = {} if not @opt?
     @events = []


add interesting animals experiment config


still getting redis to work

still getting redis to work

--- app.coffee
+++ app.coffee
@@ -9,12 +9,12 @@
 client = null
 if (process.env.REDISTOGO_URL)
   rtg   = require("url").parse(process.env.REDISTOGO_URL)
-  redis = require("redis").createClient(rtg.port, rtg.hostname)
-  redis.auth(rtg.auth.split(":")[1])
+  redis = require("redis")
+  client = redis.createClient(rtg.port, rtg.hostname)
+  client.auth(rtg.auth.split(":")[1])
 else
   redis = require("redis")
-
-client = redis.createClient()
+  client = redis.createClient()

 app = express.createServer()
 app.use assets()


tiny fix for neo4j

tiny fix for neo4j

--- workers/social/lib/social/models/graph/graph.coffee
+++ workers/social/lib/social/models/graph/graph.coffee
@@ -44,6 +44,7 @@
       collectObjects({klass:klass, selector:selector, modelName:modelName})

   fetchFromNeo4j:(query, params, callback)->
+    resultsKey = params['resultsKey'] or "items"
     # gets ids from neo4j, fetches objects from mongo, returns in the same order
     @db.query query, params, (err, results)=>
       if err


bugfix: need to pass the client object here

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1275,11 +1275,10 @@
     success: (client, rest...) -> @fetchBundle rest...

   setBillingInfo: permit 'manage payment methods',
-    success: (client, data, callback)->
-      console.log { client, data, callback }
+    success: (client, data, callback)->
       # TODO: Give credits to existing users
       JRecurlyGroup = require '../recurly/group'
-      JRecurlyGroup.setAccount this, data, callback
+      JRecurlyGroup.setAccount client, this, data, callback

   fetchBillingInfo: permit 'manage payment methods',
     success: (client, callback)->


require shift key for 'auto' scroll zoom

--- bokehjs/src/coffee/tool/event_generators.coffee
+++ bokehjs/src/coffee/tool/event_generators.coffee
@@ -168,12 +168,13 @@
       @eventSink = eventSink
       @plotview.canvas_wrapper.bind("mousewheel",
         (e, delta, dX, dY) =>
-          if @tool_active or not @eventSink.active
+          if @tool_active or (not @eventSink.active and e.shiftKey)
             set_bokehXY(e)
             e.delta = delta
             eventSink.trigger("#{toolName}:zoom", e)
             e.preventDefault()
-            e.stopPropagation())
+            e.stopPropagation()
+      )

       $(document).bind('keydown', (e) =>
         #disable the tool when ESC is pressed


Add JsDoc

Add JsDoc

--- BackofficeBundle/Resources/public/coffee/ContentTypeFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/ContentTypeFormView.coffee
@@ -1,12 +1,29 @@
-ContentTypeFormView = FullPageFormView.extend(
+###*
+ * @namespace OpenOrchestra
+###
+window.OpenOrchestra or= {}
+
+###*
+ * @class ContentTypeFormView
+###
+class OpenOrchestra.ContentTypeFormView extends FullPageFormView
+
   events:
     'change .content_type_change_type': 'changeContentTypeChange'

+  ###*
+   * Rebuild the menu if the form is submitted
+  ###
   onViewReady: ->
     if @options.submitted
       displayRoute = appRouter.generateUrl('listEntities', entityType: @options.entityType)
       refreshMenu(displayRoute, true)

+  ###*
+   * Refresh a field form setting when the type is changed
+   *
+   * @param {Object} event
+  ###
   changeContentTypeChange: (event) ->
     event.preventDefault()
     target = $(event.currentTarget)
@@ -38,7 +55,7 @@
         if $('#' + defaultValueId).hasClass('tinymce')
             tinymce.editors = []
             activateTinyMce(defaultValueView, $('#' + defaultValueId))
-)
+

 jQuery ->
-  appConfigurationView.setConfiguration('content_types', 'editEntity', ContentTypeFormView)
+  appConfigurationView.setConfiguration('content_types', 'editEntity', OpenOrchestra.ContentTypeFormView)


Added class names to KDMultipleChoice items.

--- client/Framework/components/inputs/KDMultipleChoice.coffee
+++ client/Framework/components/inputs/KDMultipleChoice.coffee
@@ -19,8 +19,8 @@

     options.size         or= "small"             # a String tiny/small/big
     options.labels       or= ["ON", "OFF"]       # supports multiple labels as string
-    options.multiple     or= no
-    options.defaultValue or= options.labels[0]
+    options.multiple     ?= no
+    options.defaultValue or= if options.multiple then options.labels[0]

     if not options.multiple and Array.isArray options.defaultValue
       options.defaultValue = options.defaultValue[0]
@@ -33,15 +33,14 @@
     @oldValue     = null
     @currentValue = [] if options.multiple

-    @setValue options.defaultValue, no
-
   setDomElement:(cssClass)->
     {title, labels, name} = @getOptions()
     title = if title then "#{title}" else ''

     labelItems = ""
     for label in labels
-      labelItems += "#{label}"
+      clsName     = "multiple-choice-#{label}"
+      labelItems += "#{label}"

     @domElement = $ """


Use type option for messages

Use type option for messages

--- client/app/CommonViews/comments/commentlistitemview.coffee
+++ client/app/CommonViews/comments/commentlistitemview.coffee
@@ -87,9 +87,10 @@
           appManager.tell "Members", "createContentDisplay", origin

   confirmDeleteComment:(data)->
+    {type} = @getOptions()
     modal = new KDModalView
-      title          : "Delete comment"
-      content        : "
Are you sure you want to delete this comment?
"
+      title          : "Delete #{type}"
+      content        : "
Are you sure you want to delete this #{type}?
"
       height         : "auto"
       overlay        : yes
       buttons        :
@@ -113,12 +114,13 @@
         #   callback   : => modal.destroy()

   pistachio:->
+    {type} = @getOptions()
     if @getData().getAt 'deletedAt'
       @setClass "deleted"
       if @deleter
-        "
{{> @author}}'s comment has been deleted by {{> @deleter}}.
"
+        "
{{> @author}}'s #{type} has been deleted by {{> @deleter}}.
"
       else
-        "
{{> @author}}'s comment has been deleted.
"
+        "
{{> @author}}'s #{type} has been deleted.
"
     else
       """


temporarily disable syntax checking

temporarily disable syntax checking

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -204,6 +204,11 @@
 				when options.try then "silent" # allow use to try compile once
 				when $scope.stop_on_validation_error then "error" # try to compile
 				else "silent" # ignore errors
+			# FIXME: Temporarily disable syntax checking as it is causing
+			# excessive support requests for projects migrated from v1
+			# https://github.com/overleaf/sharelatex/issues/911
+			if checkType == "error"
+				checkType = "silent"
 			return $http.post url, {
 				rootDoc_id: options.rootDocOverride_id or null
 				draft: $scope.draft


Add OSC sending script

Add OSC sending script


Enable searchAllPanes in fuzzy-finder

Enable searchAllPanes in fuzzy-finder

--- config.cson
+++ config.cson
@@ -24,6 +24,8 @@
   "find-and-replace":
     openProjectFindResultsInRightPane: true
   "painless-panes": {}
+  "fuzzy-finder":
+    searchAllPanes: true
 ".cs.source":
   editor:
     tabLength: 4


cleaning up toaster config file

cleaning up toaster config file

--- toaster.coffee
+++ toaster.coffee
@@ -1,4 +1,3 @@
-# => SRC FOLDER
 toast 'src'
 	vendors:[
 		"vendors/history.js"
@@ -7,17 +6,6 @@
 		"vendors/coffeekup.js"
 	]

-	# => OPTIONS (optional, default values listed)
-	# bare: false
-	# packaging: true
-	# expose: ''
-	# minify: true
-	exclude: [
-		# "app/static"
-		# "app/views/templates"
-	]
-
-	# => HTTPFOLDER (optional), RELEASE / DEBUG (required)
 	httpfolder: ''
 	release: 'public/app.js'
 	debug: 'public/app-debug.js'


Fix comparison

Fix comparison

--- src/tools/semver.coffee
+++ src/tools/semver.coffee
@@ -23,9 +23,9 @@
       when 0
         return 0
       when 1
+        return +1
+      when 2
         return -1
-      when 2
-        return +1
       else
         if v = comparator a[i], b[i]
           return v


another mistake

another mistake

--- src/torconnections.iced
+++ src/torconnections.iced
@@ -26,7 +26,7 @@
 {connectiontypes, exit_policy} = require './enums'

 fileDescriptorCommand = 'lsof -a -i 4 -c tor -n'
-ORPort = 9030
+ORPort = 9011
 myIP = "173.255.196.30"

 # this string parsing sucks


Use msg.channel instead msg.room to send welcome gif in welcome-to-zithub scritp


Add unit 2 details in utils.coffee

Add unit 2 details in utils.coffee

--- app/core/utils.ozar.coffee
+++ app/core/utils.ozar.coffee
@@ -111,31 +111,19 @@
 courseIDs =
   ONE_FREE_HOUR: '5d41d731a8d1836b5aa3cba1'
   CHAPTER_ONE: '5d8a57abe8919b28d5113af1'
+  CHAPTER_TWO: '5e2759cce8919b668b7737b1'

 orderedCourseIDs = [
   courseIDs.ONE_FREE_HOUR
   courseIDs.CHAPTER_ONE
+  courseIDs.CHAPTER_TWO
 ]

 courseAcronyms = {}
 courseAcronyms[courseIDs.ONE_FREE_HOUR] = 'P'
 courseAcronyms[courseIDs.CHAPTER_ONE] = 'C1'
-
-hourOfCodeOptions = {
-  campaignId: freeCampaignIds[0],
-  courseId: courseIDs.ONE_FREE_HOUR,
-  name: 'Prologue: Sky Mountain',
-  progressModalAfter: 1500000 #25 mins
-}
-
-registerHocProgressModalCheck = ->
-  hocProgressModalCheck = setInterval(() =>
-    if window.sessionStorage?.getItem('hoc_progress_modal_time') < new Date().getTime()
-      window.sessionStorage.setItem('show_hoc_progress_modal', true)
-      window.sessionStorage.removeItem('hoc_progress_modal_time')
-      clearInterval(hocProgressModalCheck)
-  , 60000) # every 1 min
+courseAcronyms[courseIDs.CHAPTER_TWO] = 'C2'

 # Harcoding module names for simplicity
 # Use db to store these later when we add sophisticated module functionality, right now its only used for UI
@@ -151,6 +139,26 @@
   '5': 'Capstone Intro',
   '6': 'Capstone Project'
 }
+
+# TODO update module names before shipping 2UP
+courseModules[courseIDs.CHAPTER_TWO] = {
+  '1': 'Temp module'
+}
+
+hourOfCodeOptions = {
+  campaignId: freeCampaignIds[0],
+  courseId: courseIDs.ONE_FREE_HOUR,
+  name: 'Prologue: Sky Mountain',
+  progressModalAfter: 1500000 #25 mins
+}
+
+registerHocProgressModalCheck = ->
+  hocProgressModalCheck = setInterval(() =>
+    if window.sessionStorage?.getItem('hoc_progress_modal_time') < new Date().getTime()
+      window.sessionStorage.setItem('show_hoc_progress_modal', true)
+      window.sessionStorage.removeItem('hoc_progress_modal_time')
+      clearInterval(hocProgressModalCheck)
+  , 60000) # every 1 min

 petThangIDs = [
   '578d320d15e2501f00a585bd' # Wolf Pup


Replaced callback with 'chosen' event

Replaced callback with 'chosen' event

--- autocomplete-client.coffee
+++ autocomplete-client.coffee
@@ -201,7 +201,7 @@
     # TODO this is a bit of a hack; see if we can't be smarter
     Meteor.setTimeout =>
       @hideList()
-    , 500
+    , 50

   onItemClick: (doc, e) => @processSelection(doc, @rules[@matched])

@@ -268,8 +268,7 @@
       # TODO this is a hack; see above
       @onBlur()

-    # TODO: behave better if the callback throws an error
-    rule.callback?(doc, @$element) # Notify that the item has been selected
+    @$element.trigger("chosen", doc)
     return

   # Replace the appropriate region


Closes #38 descending sort order in query

Specified by using -field in the query string e.g. ?sort=-date

--- server/common/crudModelFactory.coffee
+++ server/common/crudModelFactory.coffee
@@ -19,7 +19,7 @@
         max = 10000

       if options.sort?
-        sort = options.sort
+        sort = 'field ' + options.sort
       else
         sort = {}

@@ -46,7 +46,7 @@
     else
       callback('options must be specfied for find', null, 0) if callback
       return
-
+
     skipFrom = page * max - max

     if max < 1
@@ -64,7 +64,7 @@
               #safe because max >= 1
               pageCount = Math.ceil(count/max) or 0
               callback(null, results, pageCount) if callback
-
+
   findOne = (query, callback) ->
     if not query? or not query.systemId?
       callback 'Cannot find ' +
@@ -84,7 +84,7 @@
     query[key] = value

     @findOne query, callback
-
+
   findById = (id, systemId, callback) ->
     @findOneBy '_id', id, systemId, callback

@@ -117,7 +117,7 @@
           Resource.remove {_id: resource._id, systemId: systemId}, callback
         else
           callback null
-
+
   find: find
   findById: findById
   findOne: findOne
@@ -126,4 +126,4 @@
   update: update
   destroy: destroy
   name: Resource.modelName
-  count: count+  count: count


Fixed server events.

Fixed server events.

--- app/assets/javascripts/app/models/_application_model.js.coffee
+++ app/assets/javascripts/app/models/_application_model.js.coffee
@@ -157,7 +157,7 @@
       )

       # fetch() all on network notify
-      events = "#{@className}:created #{@className}:updated #{@className}:destroy"
+      events = "#{@className}:create #{@className}:update #{@className}:destroy"
       App.Event.bind(
         events
         =>


[skip] Updated Alerts for Backbone 1.1+ (1m)

--- app/assets/javascripts/houston/alerts/views/alerts_view.coffee
+++ app/assets/javascripts/houston/alerts/views/alerts_view.coffee
@@ -6,7 +6,8 @@
     'click button.suppress-alert-button': 'suppressAlert'
     'click button.unsuppress-alert-button': 'unsuppressAlert'

-  initialize: ->
+  initialize: (options)->
+    @options = options
     @renderAlert = HandlebarsTemplates['houston/alerts/show']
     @alerts = @options.alerts
     @workers = @options.workers


Support pos values in CQP format

Support pos values in CQP format

--- app/assets/javascripts/rglossa/components/cwb-multiword.coffee
+++ app/assets/javascripts/rglossa/components/cwb-multiword.coffee
@@ -62,15 +62,30 @@
     displayedQuery = @get('displayedQuery')

     terms = for term in displayedQuery
-      min  = term.get('min')
-      max  = term.get('max')
-      word = term.get('word')
-      res  = []
+      min      = term.get('min')
+      max      = term.get('max')
+      word     = term.get('word')
+      pos      = term.get('pos')
+      features = term.get('features')
+      attrs    = []
+
+      if pos or features.length
+        if word
+          word = "(word=\"#{word}\" %c)"
+          attrs.push(word)
+
+        if pos
+          pos = "ordkl=\"#{pos.value}\""
+          attrs.push(pos)
+
+        str = '[' + attrs.join(' & ') + ']'
+      else
+        str = if word then "\"#{word}\"" else ''

       if min or max
-        res.push("[]{#{min ? ''},#{max ? ''}}")  # interval
-      res.push word.replace(/\S+/g, '"$&"')      # word
-      res.join(' ')
+        str = "[]{#{min ? ''},#{max ? ''}} " + str
+
+      str

     # Assign the query value to a private variable, which will not be
     # transferred to the query property until we recieve a focusOut event.


Increase informatics timeout

Increase informatics timeout

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -17,7 +17,7 @@

 REQUESTS_LIMIT = 1
 UNKNOWN_GROUP = '7647'
-TIMEOUT = 1000
+TIMEOUT = 1000 * 20
 _requests = 0
 _promises = []


this should be part of the existing button group

--- app/assets/javascripts/templates/staffplan.templates.js.coffee
+++ app/assets/javascripts/templates/staffplan.templates.js.coffee
@@ -98,9 +98,7 @@
         {{/if}}

-

-      {{#if displayReassign}}
-

+        {{#if displayReassign}}
           <button class="btn btn-mini dropdown-toggle" title="Reassign to another user" data-toggle="dropdown">

@@ -113,9 +111,8 @@

             {{/each}}

-

-      {{/if}}
-
+        {{/if}}
+

       """
     show: '''


Decrease informatics poll time

Decrease informatics poll time

--- server/testSystems/Informatics.coffee
+++ server/testSystems/Informatics.coffee
@@ -17,7 +17,7 @@

 REQUESTS_LIMIT = 1
 UNKNOWN_GROUP = '7647'
-TIMEOUT = 1000 * 60
+TIMEOUT = 1000 * 60 / 3

 class InformaticsUser extends TestSystemUser
     constructor: (@id) ->


Add in metric to track client side error count

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -249,6 +249,7 @@

 		webRouter.post '/error/client', (req, res, next) ->
 			logger.warn err: req.body.error, meta: req.body.meta, "client side error"
+			metrics.inc("client-side-error")
 			res.sendStatus(204)

 		webRouter.get '*', ErrorController.notFound


remove loader on filters

remove loader on filters

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -505,7 +505,7 @@
     # Analytics
     $analytics.initialize()

-    $rootscope.$on '$routeChangeStart',  (event, next) ->
+    $rootscope.$on '$routeChangeSuccess',  (event, next) ->
         if next.access && next.access.requiresLogin
             if !$auth.isAuthenticated()
                 $location.path($navUrls.resolve("login"))


Cosmetic fixes on app.coffee.

Cosmetic fixes on app.coffee.

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -29,20 +29,21 @@
     $routeProvider.when("/project/:pslug/issues", {templateUrl: "/partials/issues.html"})
     $routeProvider.when("/project/:pslug/search", {templateUrl: "/partials/search.html"})
     $routeProvider.when("/project/:pslug/issues/:issueref",
-                    {templateUrl: "/partials/issues-detail.html"})
+                        {templateUrl: "/partials/issues-detail.html"})

     $routeProvider.when("/project/:pslug/issues/:issueref/edit",
-                    {templateUrl: "/partials/issues-detail-edit.html"})
+                        {templateUrl: "/partials/issues-detail-edit.html"})

     # Admin
     $routeProvider.when("/project/:pslug/admin/project-profile/details",
-                    {templateUrl: "/partials/admin-project-profile.html"})
+                        {templateUrl: "/partials/admin-project-profile.html"})

     # Auth
     $routeProvider.when("/login", {templateUrl: "/partials/login.html"})
     $routeProvider.when("/register", {templateUrl: "/partials/register.html"})
     $routeProvider.when("/forgot-password", {templateUrl: "/partials/forgot-password.html"})
-    $routeProvider.when("/change-password/:token", {templateUrl: "/partials/change-password-from-recovery.html"})
+    $routeProvider.when("/change-password/:token",
+                        {templateUrl: "/partials/change-password-from-recovery.html"})
     $routeProvider.when("/invitation/:token", {templateUrl: "/partials/invitation.html"})

     $routeProvider.otherwise({redirectTo: '/login'})


Refactor to have less repetition

Refactor to have less repetition

--- resources/assets/coffee/react/profile-page/recent-activities.coffee
+++ resources/assets/coffee/react/profile-page/recent-activities.coffee
@@ -2,83 +2,65 @@

 class ProfilePage.RecentActivities extends React.Component
   _renderEntry: (event) =>
+    # default, empty badge
+    badge = el 'div', className: 'profile-extra-entries__icon'
+
     switch event.type
       when 'rank'
-        el 'li',
-          className: 'profile-extra-entries__item'
-          key: event.id
-          el 'div', className: 'profile-extra-entries__detail',
-            el 'div',
-              className: "profile-score-rank-badge profile-score-rank-badge--#{event.scoreRank} profile-extra-entries__icon"
-            el 'div',
-              className: 'profile-extra-entries__text'
-              dangerouslySetInnerHTML:
-                __html: Lang.get 'events.rank',
-                  user: osu.link(event.user.url, event.user.username)
-                  rank: event.rank
-                  beatmap: osu.link(event.beatmap.url, event.beatmap.title)
-                  mode: Lang.get "common.play_mode.#{event.mode}"
-          el 'div',
-            className: 'profile-extra-entries__time'
-            dangerouslySetInnerHTML: { __html: osu.timeago(event.createdAt) }
+        badge = el 'div',
+          className: "profile-score-rank-badge profile-score-rank-badge--#{event.scoreRank} profile-extra-entries__icon"
+
+        text = el 'div',
+          className: 'profile-extra-entries__text'
+          dangerouslySetInnerHTML:
+            __html: Lang.get 'events.rank',
+              user: osu.link(event.user.url, event.user.username)
+              rank: event.rank
+              beatmap: osu.link(event.beatmap.url, event.beatmap.title)
+              mode: Lang.get "common.play_mode.#{event.mode}"

       when 'rankLost'
-        el 'li',
-          className: 'profile-extra-entries__item'
-          key: event.id
-          el 'div', className: 'profile-extra-entries__detail',
-            el 'div',
-              className: "profile-extra-entries__icon"
-            el 'div',
-              className: 'profile-extra-entries__text'
-              dangerouslySetInnerHTML:
-                __html: Lang.get 'events.rankLost',
-                  user: osu.link(event.user.url, event.user.username)
-                  rank: event.rank
-                  beatmap: osu.link(event.beatmap.url, event.beatmap.title)
-                  mode: Lang.get "common.play_mode.#{event.mode}"
-          el 'div',
-            className: 'profile-extra-entries__time'
-            dangerouslySetInnerHTML: { __html: osu.timeago(event.createdAt) }
-
+        text = el 'div',
+          className: 'profile-extra-entries__text'
+          dangerouslySetInnerHTML:
+            __html: Lang.get 'events.rankLost',
+              user: osu.link(event.user.url, event.user.username)
+              rank: event.rank
+              beatmap: osu.link(event.beatmap.url, event.beatmap.title)
+              mode: Lang.get "common.play_mode.#{event.mode}"

       when 'beatmapUpdate'
-        el 'li',
-          className: 'profile-extra-entries__item'
-          key: event.id
-          el 'div', className: 'profile-extra-entries__detail',
-            el 'div',
-              className: 'profile-extra-entries__icon'
-            el 'div',
-              className: 'profile-extra-entries__text'
-              dangerouslySetInnerHTML:
-                __html: Lang.get 'events.beatmap_update',
-                  user: osu.link(event.user.url, event.user.username)
-                  beatmap: osu.link(event.beatmap.url, event.beatmap.title)
-          el 'div',
-            className: 'profile-extra-entries__time'
-            dangerouslySetInnerHTML: { __html: osu.timeago(event.createdAt) }
+        text = el 'div',
+          className: 'profile-extra-entries__text'
+          dangerouslySetInnerHTML:
+            __html: Lang.get 'events.beatmap_update',
+              user: osu.link(event.user.url, event.user.username)
+              beatmap: osu.link(event.beatmap.url, event.beatmap.title)

       when 'achievement'
-        el 'li',
-          className: 'profile-extra-entries__item'
-          key: event.id
-          el 'div', className: 'profile-extra-entries__detail',
-            el ProfilePage.AchievementBadge, achievement: event.achievement, additionalClasses: 'profile-extra-entries__icon'
-            el 'div',
-              className: 'profile-extra-entries__text'
-              dangerouslySetInnerHTML:
-                __html: Lang.get 'events.achievement',
-                  user: osu.link(event.user.url, event.user.username)
-                  achievement: event.achievement.name
-          el 'div',
-            className: 'profile-extra-entries__time'
-            dangerouslySetInnerHTML: { __html: osu.timeago(event.createdAt) }
+        badge = el ProfilePage.AchievementBadge,
+          achievement: event.achievement
+          additionalClasses: 'profile-extra-entries__icon'
+
+        text = el 'div',
+          className: 'profile-extra-entries__text'
+          dangerouslySetInnerHTML:
+            __html: Lang.get 'events.achievement',
+              user: osu.link(event.user.url, event.user.username)
+              achievement: event.achievement.name

       else
-        el 'li',
-          key: event.id,
-          el 'pre', null, JSON.stringify(event)
+        return null
+
+    el 'li',
+      className: 'profile-extra-entries__item'
+      key: event.id
+      el 'div', className: 'profile-extra-entries__detail',
+        badge
+        text
+      el 'div',
+        className: 'profile-extra-entries__time'
+        dangerouslySetInnerHTML: { __html: osu.timeago(event.createdAt) }

   render: =>


fix: Inital loadTable() is not called

fix: Inital loadTable() is not called

--- app/web/caretaker-manage-devices/caretaker-devices-list.coffee
+++ app/web/caretaker-manage-devices/caretaker-devices-list.coffee
@@ -1,7 +1,7 @@
 Polymer 'caretaker-devices-list',

   ready: ->
-    @loadTable
+    @loadTable()

   edit: (e) ->
     @router.go "/devices/#{e.detail.id}"


Better deprecation message for up.replace() and up.extract()


Set allowed grammars before tests

Set allowed grammars before tests


Core#send

Core#send

--- test/core_object.coffee
+++ test/core_object.coffee
@@ -77,7 +77,7 @@
   o = Core()
     test: true

-  o.send("attrAccessor", "test")
+  # o.send("attrAccessor", "test")

   # ok(o.test())


bugfixes supress starting on serverside

bugfixes supress starting on serverside

--- examples/cluster/frontend/app.coffee
+++ examples/cluster/frontend/app.coffee
@@ -39,7 +39,7 @@
     ]

-    ##
+    ##
     remote:

         ##
@@ -49,38 +49,48 @@
         data:
             debug: true

-        logout: ->
-            @_getAuthManager().logout ()=>
-
-                location.reload()

         ##
         started: ->

-            console.floyd = @
-
-            @identity.once 'login', ()=>
+            if floyd.system.platform is 'remote'

-                display = $ 'input[name=display]'
-                refresh = $ 'button[name=refresh]'
+                ##
+                @identity.once 'login', ()=>
+
+                    $('div.gui').show()
+
+                    display = $ 'input[name=display]'
+                    refresh = $ 'button[name=refresh]'
+                    logout = $ 'button[name=logout]'
+
+                    @lookup 'backend.test', @identity, (err, ctx)=>
+                        return @logger.error(err) if err
+
+                        refresh.click ()=>
+
+                            ctx.echo +new Date(), (err, data)=>
+                                return @logger.error(err) if err
+
+                                display.val data
+
+                        logout.click ()=>
+
+                            @_getAuthManager().logout (err)=>
+                                return @logger.error(err) if err
+
+                                location.reload()
+

-                @lookup 'backend.test', @identity, (err, ctx)=>
-                    return @logger.error(err) if err
-
-                    refresh.click ()=>
-
-                        ctx.echo +new Date(), (err, data)=>
-                            return @logger.error(err) if err
-
-                            display.val data
-
-            if !@identity.login()
-
-                if (user=prompt('username')) && (pass=prompt('password'))
-
+                ##
+                if !@identity.login()
+
+                    user=prompt('username')
+                    pass=prompt('password')
+
                     @_getAuthManager().login user, pass, (err)=>

                         if err
                             alert 'login failed!'
                             location.reload()
-
+


Fix delete link not working for links in inspector view: [#97249676]

https://www.pivotaltracker.com/story/show/97249676

--- src/code/views/link-inspector-view.coffee
+++ src/code/views/link-inspector-view.coffee
@@ -17,7 +17,7 @@
     @props.linkManager.changeLink @props.link, {title: e.target.value}

   deleteLink: ->
-    @props.linkManager.changeLink @props.link, {delete: true}
+    @props.linkManager.changeLink @props.link, {deleted: true}

   pickColor: (e) ->
     @props.linkManager.changeLink @props.link, {color: $(e.target).css('background-color')}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -27,7 +27,8 @@

   enemy.include Flickerable

-  enemy.flicker()
+  enemy.flicker
+

 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/flickerable.coffee
+++ test/flickerable.coffee
@@ -6,7 +6,8 @@
   player.include Flickerable

   equals player.I.flickerAlpha, 0.5
-  equals player.I.flickerFrequency,
+  equals player.I.flickerFrequency, 3
+  equal

 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
@@ -22,7 +22,7 @@
   other = GameObject
     x: 1
     y: 1
-
+
   other2 = GameObject
     x: 10
     y: 10


Added integration test for JSON

Added integration test for JSON

--- test/integration.coffee
+++ test/integration.coffee
@@ -12,7 +12,22 @@

 client = require '../src/client'

-module.exports = testCase {
+# Open the same document from 2 connections.
+# So intense.
+doubleOpen = (c1, c2, docName, type, callback) ->
+	doc1 = doc2 = undefined
+
+	c1.open docName, type, (d, error) ->
+		doc1 = d
+		assert.ok doc1
+		callback doc1, doc2 if doc1 && doc2
+
+	c2.open docName, type, (d, error) ->
+		doc2 = d
+		assert.ok doc2
+		callback doc1, doc2 if doc1 && doc2
+
+module.exports = testCase
 	setUp: (callback) ->
 		# This is needed for types.text.generateRandomOp
 		require './types/text'
@@ -25,13 +40,7 @@
 			@c1 = new client.Connection 'localhost', port
 			@c2 = new client.Connection 'localhost', port

-			@c1.open @name, 'text', (@doc1, error) =>
-				assert.ok @doc1
-				callback() if @doc1 && @doc2
-
-			@c2.open @name, 'text', (@doc2, error) =>
-				assert.ok @doc2
-				callback() if @doc1 && @doc2
+			callback()

 	tearDown: (callback) ->
 		@c1.disconnect()
@@ -41,55 +50,71 @@
 		@server.close()

 	'ops submitted on one document get sent to another': (test) ->
-		[submittedOp, result] = @doc1.type.generateRandomOp @doc1.snapshot
-		@doc1.submitOp submittedOp
+		doubleOpen @c1, @c2, @name, 'text', (doc1, doc2) =>
+			[submittedOp, result] = doc1.type.generateRandomOp doc1.snapshot
+			doc1.submitOp submittedOp

-		@doc2.on 'remoteop', (op) =>
-			test.deepEqual op, submittedOp
-			test.strictEqual @doc2.snapshot, result
-			test.strictEqual @doc2.version, 1
-			test.done()
+			doc2.on 'remoteop', (op) =>
+				test.deepEqual op, submittedOp
+				test.strictEqual doc2.snapshot, result
+				test.strictEqual doc2.version, 1
+				test.done()
+
+	'JSON documents work': (test) ->
+		doubleOpen @c1, @c2, 'jsondocument', 'json', (doc1, doc2) ->
+			test.strictEqual doc1.snapshot, null
+			test.strictEqual doc1.version, 0
+			test.strictEqual doc2.snapshot, null
+			test.strictEqual doc2.version, 0
+			test.ok doc1.created != doc2.created
+
+			doc1.submitOp [{p:[], od:null, oi:{}}]
+
+			doc2.on 'remoteop', (op) ->
+				test.deepEqual doc2.snapshot, {}
+				test.done()

 	'randomized op spam test': (test) ->
-		opsRemaining = 500
+		doubleOpen @c1, @c2, @name, 'text', (doc1, doc2) =>
+			opsRemaining = 500

-		inflight = 0
-		checkSync = null
-		maxV = 0
+			inflight = 0
+			checkSync = null
+			maxV = 0

-		testSome = =>
-			ops = Math.min(Math.floor(Math.random() * 10) + 1, opsRemaining)
-			inflight = ops
-			opsRemaining -= ops
-
-			for k in [0...ops]
-				doc = if Math.random() > 0.4 then @doc1 else @doc2
-				[op, expected] = @doc1.type.generateRandomOp doc.snapshot
+			testSome = =>
+				ops = Math.min(Math.floor(Math.random() * 10) + 1, opsRemaining)
+				inflight = ops
+				opsRemaining -= ops
+
+				for k in [0...ops]
+					doc = if Math.random() > 0.4 then doc1 else doc2
+					[op, expected] = doc1.type.generateRandomOp doc.snapshot

-				checkSync = =>
-					if inflight == 0 && @doc1.version == @doc2.version == maxV
-						# The docs should be in sync.
+					checkSync = =>
+						if inflight == 0 && doc1.version == doc2.version == maxV
+							# The docs should be in sync.

-						# Assert is used here so the test fails immediately if something
-						# goes wrong.
-						assert.strictEqual @doc1.snapshot, @doc2.snapshot
+							# Assert is used here so the test fails immediately if something
+							# goes wrong.
+							assert.strictEqual doc1.snapshot, doc2.snapshot

-						if opsRemaining > 0
-							testSome()
-						else
-							test.done()
+							if opsRemaining > 0
+								testSome()
+							else
+								test.done()

-				doc.submitOp op, ->
-					maxV = Math.max(maxV, doc.version)
-					inflight--
-					checkSync()
+					doc.submitOp op, ->
+						maxV = Math.max(maxV, doc.version)
+						inflight--
+						checkSync()

-		@doc1.on 'remoteop', (op) =>
-			maxV = Math.max(maxV, @doc1.version)
-			checkSync()
-		@doc2.on 'remoteop', (op) =>
-			maxV = Math.max(maxV, @doc2.version)
-			checkSync()
+			doc1.on 'remoteop', (op) =>
+				maxV = Math.max(maxV, doc1.version)
+				checkSync()
+			doc2.on 'remoteop', (op) =>
+				maxV = Math.max(maxV, doc2.version)
+				checkSync()

-		testSome()
-}
+			testSome()
+


Terminate workers on exit

Terminate workers on exit

--- src/pow/server.coffee
+++ src/pow/server.coffee
@@ -22,7 +22,7 @@

   close: ->
     for config, app of @applications
-      app.quit()
+      app.terminate()

     @server.close()


Update output of 'pb last'

Update output of 'pb last'

--- src/pushbullet.coffee
+++ src/pushbullet.coffee
@@ -27,9 +27,8 @@
     options =
       limit: 1,
       modified_after: 0
-
-    msg.send "Pushbullet Pong."
-
+
     pushbullet.history options, (err, res) ->
       if not err
-        console.log(res)
+        push = res.pushes[0]
+        msg.send "Last push was from #{push.sender_name}"


Add note about GENERAL being all caps

Add note about GENERAL being all caps

--- src/rocketchat.coffee
+++ src/rocketchat.coffee
@@ -62,6 +62,7 @@

                                 .catch (err) =>
                                     @robot.logger.error "Unable to Join Room: #{room} Reason: #{err.reason}"
+                                    @robot.logger.error "If joining GENERAL please make sure its using all caps"

                     @emit 'connected'


implement simple page references using LaTeX zref package


document.onHide() delay added

document.onHide() delay added

--- style.coffee
+++ style.coffee
@@ -86,13 +86,20 @@
 		@classes = null
 		@parentSet = false
 		@lastItemParent = null
+		@waiting = false

 		Object.preventExtensions @

 	render: ->
+		if @waiting
+			return
+
 		for child in @children
 			child.render()

+		@renderItem()
+
+	renderItem: ->
 		unless @item
 			return

@@ -102,6 +109,7 @@
 				@classes = utils.clone(classes.items())

 		@item.document.node = @node
+		@item.document.visible = true
 		@updateText()
 		@updateVisibility()
 		@syncClassAttr('')
@@ -114,18 +122,82 @@
 			@setAttr name, val
 		return

-	revert: ->
+	revert: do ->
+		list = []
+		lastDate = 0
+
+		class DocumentHideEvent
+			constructor: ->
+				@delay = 0
+
+		event = new DocumentHideEvent
+
+		revertItemWhenPossible = (style) ->
+			if list.length is 0
+				lastDate = Date.now()
+				requestAnimationFrame sync
+
+			style.waiting = true
+			list.push style
+			return
+
+		sync = ->
+			now = Date.now()
+			diff = now - lastDate
+			lastDate = now
+
+			animationsPending = false
+
+			for style in list
+				for extension in style.item._extensions
+					if extension instanceof Renderer.PropertyAnimation and extension.running and not extension.loop
+						animationsPending = true
+						break
+				if animationsPending
+					break
+
+			if not animationsPending and (event.delay -= diff) <= 0
+				event.delay = 0
+
+				for style in list
+					style.waiting = false
+					style.revertItem()
+					style.file.readyToUse = true
+					assert.notOk style.file.isRendered
+
+				utils.clear list
+			else
+				requestAnimationFrame sync
+
+			return
+
+		->
+			if @waiting or not @item
+				return
+
+			# parent
+			if @isAutoParent and @isScope
+				@item.document.onHide.emit event
+			@item.document.visible = false
+
+			@file.readyToUse = false
+			revertItemWhenPossible @
+
+			for child in @children
+				child.revert()
+			return
+
+	revertItem: ->
 		unless @item
 			return

 		# parent
 		if @isAutoParent
-			if @isScope
-				@item.document.onHide.emit()
 			@lastItemParent = null
 			if !@parentSet
 				@lastItemParent = @item.parent
 			@item.parent = null
+
 		itemDocumentNode = @item.document.node
 		@item.document.node = null

@@ -135,9 +207,6 @@
 				tmpNode._documentStyle = null
 			else
 				break
-
-		for child in @children
-			child.revert()

 		# shared items
 		if itemDocumentNode?._documentStyle is @
@@ -192,6 +261,9 @@
 			item.label

 	updateText: ->
+		if @waiting
+			return
+
 		obj = @getTextObject()
 		node = @node
 		if node.children.length is 1 and node.children[0].name is 'a'
@@ -213,7 +285,7 @@
 		return

 	updateVisibility: ->
-		unless @item
+		if @waiting or not @item
 			return

 		visible = true
@@ -249,6 +321,9 @@

 	setAttr: (name, val) ->
 		assert.instanceOf @, Style
+
+		if @waiting
+			return

 		{funcs} = @file
 		if funcs?.hasOwnProperty(val)
@@ -306,6 +381,9 @@

 	syncClassAttr: (oldVal) ->
 		assert.isString oldVal
+
+		if @waiting
+			return

 		{item} = @
 		unless item
@@ -353,6 +431,9 @@
 		uri

 	reloadItem: ->
+		if @waiting
+			return
+
 		unless utils.isClient
 			return

@@ -426,6 +507,9 @@
 		return;

 	findItemParent: ->
+		if @waiting
+			return
+
 		if @isAutoParent and @item and not @item.parent
 			@parentSet = true
 			{node} = @


clean up old page code

clean up old page code

--- src/umobi.page.coffee
+++ src/umobi.page.coffee
@@ -114,22 +114,14 @@
               overflow: if umobi.support.touch then "hidden" else "auto"
           upage.on "pagereveal", AdjustContentHeight
         else
-          console.log "not using css touch scroll"
           AdjustContentPadding = ->
             contentTop    = if h.get(0) then h.height() else 0
             contentBottom = if f.get(0) then f.height() else 0
+            # use webkit overflow if it's needed
+            # "-webkit-overflow-scrolling": "touch"
             $c.css
               marginTop: contentTop
               marginBottom: contentBottom
-#            AdjustContentPadding = ->
-#              console.log "pagereveal", h.height(), f.height()
-#              $c.css({
-#                "position": "absolute"
-#                "-webkit-overflow-scrolling": "touch"
-#                "overflow": "auto"
-#              })
-#              $c.css("top", h.height() + "px") if h.get(0)
-#              $c.css("bottom", f.height() + "px") if f.get(0)
           upage.on "pagereveal", AdjustContentPadding

       resizeTimeout = null


Use multi-cursor aware methods on TextEditor

--- spec/whitespace-spec.coffee
+++ spec/whitespace-spec.coffee
@@ -46,11 +46,11 @@
         editor = atom.workspace.open('sample.txt').then (o) -> editor = o

       runs ->
-        editor.moveCursorToEndOfLine()
+        editor.moveToEndOfLine()
         editor.insertText("           ")

         # move cursor to next line to avoid ignoreWhitespaceOnCurrentLine
-        editor.moveCursorToBottom()
+        editor.moveToBottom()

         editor.save()
         expect(editor.getText()).toBe 'Some text.\n'
@@ -104,7 +104,7 @@
       editor.insertText "1  \n2\t  \n\t \n3\n"

       # move cursor to bottom for preventing effect of whitespace.ignoreWhitespaceOnCurrentLine
-      editor.moveCursorToBottom()
+      editor.moveToBottom()
       editor.save()
       expect(editor.getText()).toBe "1\n2\n\n3\n"

@@ -116,7 +116,7 @@
       editor.insertText "1  \n2\t  \n\t \n3\n"

       # move cursor to bottom for preventing effect of whitespace.ignoreWhitespaceOnCurrentLine
-      editor.moveCursorToBottom()
+      editor.moveToBottom()
       editor.save()
       expect(editor.getText()).toBe "1\n2\n\t \n3\n"

@@ -160,9 +160,9 @@
       editor.insertText "foo"
       editor.setCursorBufferPosition([0,0])
       editor.selectToEndOfLine()
-      originalSelectionRange = editor.getSelection().getBufferRange()
-      editor.save()
-      newSelectionRange = editor.getSelection().getBufferRange()
+      originalSelectionRange = editor.getLastSelection().getBufferRange()
+      editor.save()
+      newSelectionRange = editor.getLastSelection().getBufferRange()
       expect(originalSelectionRange).toEqual(newSelectionRange)

   describe "when 'whitespace.ensureSingleTrailingNewline' is false", ->


fixing comments to post at beginning

fixing comments to post at beginning

--- app/coffee/dream.coffee
+++ app/coffee/dream.coffee
@@ -34,5 +34,5 @@
     @$commentsInput.val('')
     comment = '

' + currentUser + '

1 seccond ago

' + commentText + '

'

-    @$commentsTarget.children().last().before(comment)
+    @$commentsTarget.children().first().after(comment)
     $('.prevCommentWrap').slideDown()


Activity: markdown preview tooltip

Activity: markdown preview tooltip

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -8,7 +8,7 @@

     options.destroyOnSubmit ?= no

-    @input    = new ActivityInputView defaultValue: options.defaultValue
+    @input = new ActivityInputView defaultValue: options.defaultValue
     @input.on "Escape", @bound "reset"

     @input.on "TokenAdded", (type, token) =>
@@ -53,10 +53,9 @@
       tagName  : "span"
       cssClass : "preview-icon"
       tooltip  :
-        title     : "Preview"
+        title  : "Markdown preview"
       click    : =>
-        if not @preview then @showPreview()
-        else @hidePreview()
+        if not @preview then @showPreview() else @hidePreview()

   submit: (callback) ->
     return  unless value = @input.getValue().trim()
@@ -183,6 +182,7 @@
   showPreview: ->
     return unless value = @input.getValue().trim()
     markedValue = KD.utils.applyMarkdown value
+    return  if markedValue.trim() is "<p>#{value}</p>"
     tags = @input.getTokens().map (token) -> token.data if token.type is "tag"
     tagsExpanded = @utils.expandTokens markedValue, {tags}
     if not @preview
@@ -191,7 +191,6 @@
         partial  : tagsExpanded
         click    : => @hidePreview()
       @input.addSubView @preview
-
     else
       @preview.updatePartial tagsExpanded