changed if condition

changed if condition

--- client/app/Applications/Feeder.kdapplication/FeedController.coffee
+++ client/app/Applications/Feeder.kdapplication/FeedController.coffee
@@ -169,8 +169,7 @@
           unless err
             if items.length is 0 and listController.getItemCount() is 0
               @noItemFound.show()
-            filter.activeSort = 'meta.modifiedAt' if not filter.activeSort
-            items = @sortByKey(items, filter.activeSort) #if filter.activeSort
+            items = @sortByKey(items, filter.activeSort) if filter.activeSort
             listController.instantiateListItems items
             @emitCountChanged listController.itemsOrdered.length, filter.name
             if items.length is options.limit and listController.scrollView.getScrollHeight() <= listController.scrollView.getHeight()


fix for each in obj? where ? is bad

fix for each in obj? where ? is bad

--- src/jsonsina.coffee
+++ src/jsonsina.coffee
@@ -33,15 +33,15 @@
   request.get options, (err, res, string)->
     unless err?
       arr = eval string
-
-      for each in arr?
-        each.day = new Date each.day
-        each.open = Number each.open
-        each.low = Number each.low
-        each.high = Number each.high
-        each.close = Number each.close
-        each.volume = Number each.volume
-        #console.log each
+      if arr
+        for each in arr
+          each.day = new Date each.day
+          each.open = Number each.open
+          each.low = Number each.low
+          each.high = Number each.high
+          each.close = Number each.close
+          each.volume = Number each.volume
+          #console.log each
     callback err, arr

 module.exports = history


Change regex formatting to improve readability on GitHub

--- code/some_pleasant.coffee
+++ code/some_pleasant.coffee
@@ -6,11 +6,7 @@

 isNumber = (str) -> not isNaN(str)

-isIdentifier = (str) -> /// ^
-  [^\s(),']   # anything but whitespace, '(', ')', ',', or '''.
-  +           # one or more of the previous
-  $           # end of word
-///.exec str
+isIdentifier = (str) -> /^[^\s(),']+$/.exec str

 isCall = (str) -> str[0] == '(' and str[str.length - 1] == ')'


webserver: exit on fatal error

webserver: exit on fatal error

it should've done this all along, and it's probably a regression.  Having the process exit rather than failing silent is obviously better from an ops perspective.

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -86,10 +86,11 @@
 if basicAuth
   app.use express.basicAuth basicAuth.username, basicAuth.password

-process.on 'uncaughtException',(err)->
+process.on 'uncaughtException', (err) ->
   console.log 'there was an uncaught exception'
   console.log process.pid
   console.error err
+  process.exit(1)

 app.use (req, res, next) ->
   # add referral code into session if there is one


AutocompleteView#buildWordList not async anymore

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -99,48 +99,42 @@
    * Generates the word list from the editor buffer(s)
   ###
   buildWordList: ->
-    deferred = Q.defer()
-    process.nextTick =>
-      wordHash = {}
-      if atom.config.get('autocomplete-plus.includeCompletionsFromAllBuffers')
-        buffers = atom.project.getBuffers()
-      else
-        buffers = [@currentBuffer]
-      matches = []
-
-      p = new Perf "Building word list", {@debug}
-      p.start()
-
-      matches.push(buffer.getText().match(@wordRegex)) for buffer in buffers
-      matches.push(@getCompletionsForCursorScope())
-
-      # Uniqueness workaround
-      words = _.flatten(matches)
-      for word in words
-        wordHash[word] ?= true
-      wordList = Object.keys(wordHash)
-
-      # We can't set the value for the following keys.
-      # Check whether they're in the `words` variable
-      # and add them to `wordList`
-      objectKeyBlacklist = [
-        'toString',
-        'toLocaleString',
-        'valueOf',
-        'hasOwnProperty',
-        'isPrototypeOf',
-        'propertyIsEnumerable',
-        'constructor'
-      ]
-      for word in objectKeyBlacklist when word in words
-        wordList.push word
-      @wordList = wordList
-
-      p.stop()
-
-      deferred.resolve()
-
-    return deferred.promise
+    wordHash = {}
+    if atom.config.get('autocomplete-plus.includeCompletionsFromAllBuffers')
+      buffers = atom.project.getBuffers()
+    else
+      buffers = [@currentBuffer]
+    matches = []
+
+    p = new Perf "Building word list", {@debug}
+    p.start()
+
+    matches.push(buffer.getText().match(@wordRegex)) for buffer in buffers
+    matches.push(@getCompletionsForCursorScope())
+
+    # Uniqueness workaround
+    words = _.flatten(matches)
+    for word in words
+      wordHash[word] ?= true
+    wordList = Object.keys(wordHash)
+
+    # We can't set the value for the following keys.
+    # Check whether they're in the `words` variable
+    # and add them to `wordList`
+    objectKeyBlacklist = [
+      'toString',
+      'toLocaleString',
+      'valueOf',
+      'hasOwnProperty',
+      'isPrototypeOf',
+      'propertyIsEnumerable',
+      'constructor'
+    ]
+    for word in objectKeyBlacklist when word in words
+      wordList.push word
+    @wordList = wordList
+
+    p.stop()

   ###
    * Handles confirmation (the user pressed enter)


Default width to min-width property

Default width to min-width property

--- lib/autocomplete-view.coffee
+++ lib/autocomplete-view.coffee
@@ -160,7 +160,7 @@

   afterAttach: (onDom) ->
     if onDom
-      widestCompletion = 0
+      widestCompletion = parseInt(@css('min-width')) or 0
       @list.find('span').each ->
         widestCompletion = Math.max(widestCompletion, $(this).outerWidth())
       @list.width(widestCompletion)


Reset collection 'synced' on reset

Reset collection 'synced' on reset

--- SingularityUI/app/collections/collection.coffee
+++ SingularityUI/app/collections/collection.coffee
@@ -5,4 +5,5 @@

     constructor: ->
         super
-        @on 'sync', => @synced = true+        @on 'sync',  => @synced = true
+        @on 'reset', => @synced = false


Fix for error message popup when logs are missing

--- SingularityUI/app/controllers/TaskDetail.coffee
+++ SingularityUI/app/controllers/TaskDetail.coffee
@@ -192,6 +192,7 @@
             @collections.alerts.reset(alerts)

     refresh: ->
+        console.log "Refresh"
         @resourcesFetched = false

         @collections.taskCleanups.fetch()
@@ -199,14 +200,14 @@
         @collections.pendingDeploys.fetch()

         @models.task.fetch()
-            .done =>
+           .done =>
                 @collections.files.fetch().error @ignore404
                 @fetchResourceUsage() if @models.task.get('isStillRunning')
                 logPath = if @models.task.get('isStillRunning') then config.runningTaskLogPath else config.finishedTaskLogPath
                 logPath = logPath.replace('$TASK_ID', @taskId)
                 logPath = _.initial(logPath.split('/')).join('/')
                 @collections.logDirectory.path = logPath
-                @collections.logDirectory.fetch()
+                @collections.logDirectory.fetch().error @ignore404
             .success =>
                 @getAlerts()
             .error =>


Added MakerSquare email offer. Not emailing subscribers with next-steps offers.

--- server/routes/mail.coffee
+++ server/routes/mail.coffee
@@ -723,12 +723,14 @@
         'talent-buddy': isAdult
         'coding-campus': isAdult and Math.random() < 0.5  # TODO: geodetect UT and give priority
         'viking': isAdult and isFast
+        'maker-square': isAdult and isFast
         #'mv-code-club': isKid  # TODO: geodetect, get landing page URL
       nAdditionalOffers = 4 - _.filter(offers).length
       possibleAdditionalOffers = ['code-school', 'one-month', 'learnable', 'pluralsight']
       for offer in _.sample possibleAdditionalOffers, nAdditionalOffers
         offers[offer] = true
-      # TODO: adjust template to not include any offers if user.isPremium()
+      if user.isPremium()
+        offers = null
       # TODO: do something with the preferredLanguage?
       context =
         email_id: sendwithus.templates.next_steps_email


DEMO: demonstrate multiline label without explicit overflow and with a non-active overflow

--- demo/src/demos/LabelDemo.coffee
+++ demo/src/demos/LabelDemo.coffee
@@ -156,6 +156,26 @@
 			centered: true
 		@__addLabel("multiline: true, Icon, centered: true",label)

+		label = new CUI.Label
+			multiline: true
+			manage_overflow: false
+			text: """
+			A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines.
+			A good and long text that includes a veryverysuperlongword to show that it can break into lines.
+			A good and long text that includes a veryverysuperlongword to show that it can break into lines.
+			A good and long text that includes a veryverysuperlongword to show that it can break into lines.
+			"""
+			icon: "rotate_left"
+		@__addLabel("multiline: true, manage_overflow: false, Icon", label)
+
+		label = new CUI.Label
+			multiline: true
+			manage_overflow: true
+			text: """
+			Aaaa that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that includes a veryverysuperlongword to show that it can break into lines. A good and long text that.
+			"""
+			icon: "rotate_left"
+		@__addLabel("multiline: true, manage_overflow: true but not active, Icon", label)

 		label = new CUI.Label
 			class: "cui-label-demo-multiline-with-limited-width"


text align field should use "left" instead of "start"

--- static/coffee/renderers/annotation/legend.coffee
+++ static/coffee/renderers/annotation/legend.coffee
@@ -156,7 +156,7 @@
   legend_padding : 10
   legend_spacing : 3
   orientation : "top_right"
-  label_text_align : "start"
+  label_text_align : "left"
   label_text_baseline : "middle"
   datapoint : null
 })


Remove debug message

Remove debug message

--- hamwerk/server/main.coffee
+++ hamwerk/server/main.coffee
@@ -56,7 +56,6 @@
             throw new Meteor.Error(403, "This class doesn't belong to you")

 createOnboarding = (user) ->
-    console.log "Creating onboarding for #{user.username}..."
     userId = user._id
     assignments = [
         ["Check off this assignment as complete by pressing the black box to its left", "today"]


Settle -> settle

Settle -> settle

--- game/index.coffee
+++ game/index.coffee
@@ -30,7 +30,7 @@
   promises.push pinManager.openPin(playerConfig.button, 'in down')
   promises.push pinManager.openPin(playerConfig.led, 'out up')

-Promise.settled(promises).then (results)->
+Promise.settle(promises).then (results)->
   console.log 'Setup. ', results

   ###


change set parameters

change set parameters

--- src/shapes/Line.coffee
+++ src/shapes/Line.coffee
@@ -22,21 +22,29 @@

 	# edit

-	set: (args...) ->
-		if args.length == 2
-			@points = [args[0], args[1]]
-		else  # len == 4
-			@points = [new Geom2D.Point(args[0], args[1]), new Geom2D.Point(args[2], args[3])]
+	set: (a1, a2, a3, a4) ->
+		if p4?
+			@points[0].set(a1, a2)
+			@points[1].set(a3, a4)
+		else
+			@points[0] = a1
+			@points[1] = a2
 		@onPointChange()
 		@

-	setPoint1: (p1) ->
-		@points[0].copy(p1)
+	setPoint1: (a1, a2) ->
+		if p2?
+			@points[0].set(a1, a2)
+		else
+			@points[0].copy(a1)
 		@onPointChange()
 		@

-	setPoint2: (p2) ->
-		@points[1].copy(p2)
+	setPoint2: (a1, a2) ->
+		if p2?
+			@points[1].set(a1, a2)
+		else
+			@points[1].copy(a1)
 		@onPointChange()
 		@


Revert "Fix command name  =>"

Revert "Fix command name  =>"

This reverts commit 998f44f98862d96fae2f91e33e19ed2a6e064db4.

--- scripts/turtleme.coffee
+++ scripts/turtleme.coffee
@@ -2,13 +2,13 @@
 #   hubotに亀の写真を拾ってきてもらう
 #
 # Commands:
-#   hubot turtle me - Queries Google Images for turtle and returns a random top result.
+#   hubot turtleme - Queries Google Images for turtle and returns a random top result.

 # Hubotのスクリプトはモジュールとして記述し，
 # Hubot起動時にrequireされてexportした関数が呼び出されます

 module.exports = (robot) ->
-  robot.respond /turtle me/i, (msg) ->
+  robot.respond /turtleme/i, (msg) ->
     turtleMe msg, (url) ->
       msg.send url


Removed implicit return value from functions in the executor module

--- lib/debugger/executor.coffee
+++ lib/debugger/executor.coffee
@@ -1,59 +1,69 @@
-{BufferedProcess, Emitter} = require('atom')
-path                       = require('path')
+{BufferedProcess, Emitter} = require 'atom'
+path                       = require 'path'

 class Executor
   constructor: ->
     @resetFlags()

   startDebugger: ->
-    debuggerPath = path.join(__dirname, "debugger.jar")
-    command = "java"
-    args = ["-jar", debuggerPath]
-    stdout = (output) => @handleOutput(output)
-    exit = (code) => @handleExit(code)
-    @process = new BufferedProcess({command, args, stdout, exit})
-    @emitter = new Emitter()
+    debuggerPath = path.join __dirname, 'debugger.jar'
+    command = 'java'
+    args = ['-jar', debuggerPath]
+    stdout = (output) => @handleOutput output
+    exit = (code) => @handleExit code
+    @process = new BufferedProcess {command, args, stdout, exit}
+    @emitter = new Emitter
+    return
+
+  stopDebugger: ->
+    @process.kill()
+    @emitStop()
+    @emitter.dispose()
+    return

   handleExit: (code) ->
     @emitStop()
     @emitter.dispose()
     @resetFlags()
+    return

   handleOutput: (output) ->
-    if (output.indexOf('!!!VIEWCHANNELREADY!!!') > -1)
+    if output.indexOf('!!!VIEWCHANNELREADY!!!') > -1
       @viewChannelReady = true

-    if (output.indexOf('!!!RUNTIMECHANNELREADY!!!') > -1)
+    if output.indexOf('!!!RUNTIMECHANNELREADY!!!') > -1
       @runtimeChannelReady = true

-    if (@viewChannelReady && @runtimeChannelReady)
+    if @viewChannelReady && @runtimeChannelReady
       @emitReady()
       @resetFlags()

-  stopDebugger: ->
-    @process.kill()
-    @emitStop()
-    @emitter.dispose()
+    return

   emitStop: ->
-    @emitter.emit('execution-stopped')
+    @emitter.emit 'execution-stopped'
+    return

   onStop: (callback) ->
-    @emitter.on('execution-stopped', callback)
+    @emitter.on 'execution-stopped', callback
+    return

   emitReady: ->
-    @emitter.emit('debugger-ready')
+    @emitter.emit 'debugger-ready'
+    return

   onReady: (callback) ->
-    @emitter.on('debugger-ready', callback)
+    @emitter.on 'debugger-ready', callback
+    return

   resetFlags: ->
     @runtimeChannelReady = false
     @viewChannelReady = false
+    return

 module.exports =
 class ExecutorProvider
   instance = null

   @getInstance: ->
-    instance ?= new Executor()+    instance ?= new Executor


Add some logging to the domain repository

--- lib/domain_repository.coffee
+++ lib/domain_repository.coffee
@@ -14,13 +14,21 @@

   transact: (operation, callback) ->
     @logger.log "transaction", "starting"
+    # TODO: prevent parallel operation.
+    # Two solutions:
+    # 1. make it blocking, queue operations
+    # 2. isolate aggregates per transaction (how?)
     operation (err) =>
       if err?
         @logger.alert "transaction", "rolling back (#{err})"
-        @rollback callback
+        @rollback =>
+          @logger.log "transaction", "rolled back"
+          callback null
       else
         @logger.log "transaction", "comitting"
-        @commit callback
+        @commit =>
+          @logger.log "transaction", "committed"
+          callback null

   createNewUid: (callback) ->
     @store.createNewUid callback


Special case DOM ranges for cursor targets

Fixes selecting attachments in MS Edge 17

--- src/trix/models/selection_manager.coffee
+++ src/trix/models/selection_manager.coffee
@@ -3,8 +3,8 @@
 #= require trix/observers/selection_change_observer

 {getDOMSelection, getDOMRange, setDOMRange, elementContainsNode,
- nodeIsCursorTarget, innerElementIsActive, handleEvent, normalizeRange,
- rangeIsCollapsed, rangesAreEqual} = Trix
+ nodeIsCursorTarget, nodeIsTextNode, innerElementIsActive, handleEvent,
+ normalizeRange, rangeIsCollapsed, rangesAreEqual} = Trix

 class Trix.SelectionManager extends Trix.BasicObject
   constructor: (@element) ->
@@ -107,9 +107,21 @@
       @findContainerAndOffsetFromLocation(locationRange[1]) ? rangeStart

     if rangeStart? and rangeEnd?
+      [startContainer, startOffset] = rangeStart
+      [endContainer, endOffset] = rangeEnd
+
       domRange = document.createRange()
-      domRange.setStart(rangeStart...)
-      domRange.setEnd(rangeEnd...)
+
+      if nodeIsCursorTarget(startContainer) and nodeIsTextNode(startContainer)
+        domRange.setStartBefore(startContainer.parentNode)
+      else
+        domRange.setStart(startContainer, startOffset)
+
+      if nodeIsCursorTarget(endContainer) and nodeIsTextNode(endContainer)
+        domRange.setEndBefore(endContainer.parentNode)
+      else
+        domRange.setEnd(endContainer, endOffset)
+
       domRange

   createLocationRangeFromDOMRange: (domRange, options) ->


Update test.iced (#2137)

Update test.iced (#2137)

--- src/gulp_modules/test.iced
+++ src/gulp_modules/test.iced
@@ -3,7 +3,7 @@
 task 'test', "runs all tests", (done) ->
     run 'test-dotnet',
       'test-go'
-      'test-java'
+#      'test-java'
       'test-node'
       'test-python'
       'test-ruby'


Fix for #5

Fix for #5

Use the atom-workspace tag instead of the workspace class.

--- keymaps/icon-font-picker.cson
+++ keymaps/icon-font-picker.cson
@@ -7,5 +7,5 @@

 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps
-'.workspace':
+'atom-workspace':
   'cmd-shift-f': 'icon-font-picker:toggle'


Remove unneeded resolved task shell path

--- src/stdlib/task.coffee
+++ src/stdlib/task.coffee
@@ -11,8 +11,7 @@
   start: ->
     throw new Error("Task already started") if @worker?

-    taskShellPath = fs.resolveOnLoadPath('task-shell', ['js', 'coffee'])
-    blob = new Blob(["require('coffee-script'); require('#{taskShellPath}');"], type: 'text/javascript')
+    blob = new Blob(["require('coffee-script'); require('task-shell');"], type: 'text/javascript')
     @worker = new Worker(URL.createObjectURL(blob))
     @worker.onmessage = ({data}) =>
       if @aborted


:hammer: Add jschardet license override

:hammer: Add jschardet license override

--- build/tasks/license-overrides.coffee
+++ build/tasks/license-overrides.coffee
@@ -60,3 +60,17 @@
         ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
         IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       """
+  'jschardet@1.1.0':
+    license: 'LGPL'
+    source: 'README.md in the repository'
+    sourceText: """
+      JsChardet
+      =========
+
+      Port of python's chardet (http://chardet.feedparser.org/).
+
+      License
+      -------
+
+      LGPL
+    """


Fix capstone in progress check

Fix capstone in progress check

--- ozaria/site/views/play/level/tome/CastButtonView.ozar.coffee
+++ ozaria/site/views/play/level/tome/CastButtonView.ozar.coffee
@@ -80,12 +80,12 @@
   onNextButtonClick: (e) ->
     if @winnable and @options.level.get('ozariaType') == 'capstone'
       @options.session.recordScores @world?.scores, @options.level
-      capstoneStage = @options.capstoneStage # passed in from PlayLevelView->TomeView
+      capstoneStage = @options.capstoneStage # passed in from PlayLevelView->TomeView, it is the Capstone Stage that has been just completed
       finalStage = GoalManager.maxCapstoneStage(@options.level.get('additionalGoals'))
       args = {
         showModal: true
         manual: true
-        capstoneInProgress: capstoneStage <= finalStage
+        capstoneInProgress: capstoneStage < finalStage
         isCapstone: true
       }
       Backbone.Mediator.publish 'level:show-victory', args


Fixing screenFrame and canvasFrame

Fixing screenFrame and canvasFrame


Still support the old animate syntax

Still support the old animate syntax

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -884,12 +884,6 @@

 	##############################################################
 	## ANIMATION
-
-	animate: (options) ->
-		# console.warn "Layer.animate is deprecated: please use Layer.animateTo instead"
-		properties = options.properties
-		delete options.properties
-		@animateTo(properties, options)

 	animateToState: (stateName, options={}) ->
 		properties = @_stateMachine.switchTo stateName
@@ -911,6 +905,14 @@
 		if typeof properties == 'string'
 			stateName = properties
 			return @animateToState stateName, options
+
+		#Support the old properties syntax
+		if properties.properties?
+			# console.warn "Using Layer.animate with 'properties' key is deprecated: please use 'options' key to provide animation options instead"
+			options = properties
+			properties = options.properties
+			delete options.properties
+
 		_.defaults(options,properties.options,@options)
 		delete properties.options


changed borderColors to Color class

changed borderColors to Color class

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -173,7 +173,7 @@
 	# Border properties
 	# Todo: make this default, for compat we still allow strings but throw a warning
 	# @define "borderRadius", layerProperty(@, "borderRadius", "borderRadius", 0, _.isNumber
-	@define "borderColor", layerProperty(@, "borderColor", "border", null, _.isString)
+	@define "borderColor", layerProperty(@, "borderColor", "border", null, Color.isColor, Color.toColor)
 	@define "borderWidth", layerProperty(@, "borderWidth", "border", 0, _.isNumber)

 	@define "force2d", layerProperty(@, "force2d", "webkitTransform", false, _.isBoolean)


Remove the triage label from jira-bot

Remove the triage label from jira-bot

--- src/jira.coffee
+++ src/jira.coffee
@@ -84,7 +84,7 @@
       reporter = user[0] if user and user.length is 1
       quoteRegex = /`{1,3}([^]*?)`{1,3}/
       labelsRegex = /#\S+\s?/g
-      labels = ["triage"]
+      labels = []
       [__, command, message] = msg.match

       desc = message.match(quoteRegex)[1] if quoteRegex.test(message)


Clone object instead of reference

Clone object instead of reference

--- src/tasks/_util.coffee
+++ src/tasks/_util.coffee
@@ -95,9 +95,11 @@

 #-- constants
 util.konstan = (type) ->
+	extend = require('util')._extend
+
 	parse_item = (item) -> "data.konstan['#{item.split('.').join("']['")}']"

-	data = konstan: util.pkg.nwayo.konstan
+	data = konstan: extend {}, util.pkg.nwayo.konstan
 	options = data.konstan.__options[type] or {}
 	delete data.konstan.__options


#40 Add the metadatas processing in the collection.

--- lib/models/collection.coffee
+++ lib/models/collection.coffee
@@ -34,9 +34,15 @@
       }
       # Call add model foreach model.
       models.forEach(@addModel, @)
-
+    ###
+      Process the collection metdatas.
+    ###
+    processMetadatas: ->
+      this.metadatas = metadataBuilder.getMetadatas(_.pick(this, "modelName", "metadatas"))
+      # Change the idAttribute depending on the metadatas.
     initialize: (options)->
       options = options or {}
+
       # Changes inside the collection.
       @changes = {
         # The newly created models.
@@ -46,6 +52,7 @@
         # The deleted models.
         deletes: {}
       }
+      @processMetadatas()
       # Bind models events on order to track changes.
       @on('add',(model)=> @addModel(model))
       @on('remove', (model)=> @deleteModel(model))


DOM event keybinding docs

DOM event keybinding docs

--- src/keys.coffee
+++ src/keys.coffee
@@ -2,13 +2,19 @@
 # Based on keymaster.js by Thomas Fuchs
 # https://github.com/madrobby/keymaster/
 #
-# Ex:
+# Two ways to bind actions to keys:
+#
+# 1. Directly on the keydown event
+#   Kona.Keys.keydown = (key) ->
+#     switch key
+#       when 'left'  then shape.direction.dx = -moveSpeed
+#       when 'right' then shape.direction.dx = moveSpeed
+#       when 'up'    then shape.jump()
+#
+# 2. Bind a handler function to a keypress
+#     Kona.keys.bind 'a', ->
+#       console.log "You pressed a!"

-# TODO: KEYDOWN/UP EX
-
-
-#   Kona.keys.bind 'a', ->
-#     console.log "You pressed a!"

 Kona.Keys =
   # Contains info on keys and their associated handler(s)


refactory: pool_words as object

refactory: pool_words as object

--- src/kick.coffee
+++ src/kick.coffee
@@ -6,13 +6,43 @@
 stage_width = 1000

 animate_id = null
-pool_words = []
 shoot_word = null
 shoot_name = null

 lvl = 1
 point = 0
 player_die = false
+
+pool_words = new class PoolWord
+    constructor: ->
+        @words = []
+        @actions = ['box', 'kick', 'punch', 'strike']
+
+    add: (word=null) ->
+        if not word?
+            word = @actions.random()
+        @words.push(new ShootingWord(word))
+
+    autofill: (lvl) ->
+        for [1..lvl-@words.length]
+            pool_words.add()
+
+    get: (c) ->
+        for word, i in @words
+            if c == word.remain[0]
+                return @words.pop(i)
+
+    move: ->
+        for word in @words
+            word.move()
+            word.update()
+
+    attack: ->
+        @words.some (word) -> word.left <= 0
+
+    draw: ->
+        for word in @words
+            $('#playground').append(word.show)

 inventory = new class Inventory
     constructor: ->
@@ -69,9 +99,7 @@
         @remain = @full

-action_words = ['box', 'kick', 'punch', 'strike']
-
-pool_words.push(new ShootingWord('kick'))
+pool_words.add('kick')

 draw = ->
     $('#point').html(point)
@@ -84,10 +112,9 @@
             shoot_word.move()
             player_die = true if shoot_word.update()
             $('#playground').append(shoot_word.show)
-    for word in pool_words
-        word.move()
-        player_die = true if word.update()
-        $('#playground').append(word.show)
+    pool_words.move()
+    player_die = true if pool_words.attack()
+    pool_words.draw()
     if player_die
         clearInterval(animate_id)
         animate_id = null
@@ -101,16 +128,13 @@
     if event.keyCode in [8, 27, 46] # backspace, escape, delete
         if shoot_word?
             shoot_word.reset()
-            pool_words.push(shoot_word)
+            pool_words.add(shoot_word.full)
             shoot_word = null

 $(document).keypress (event) ->
     c = String.fromCharCode(event.charCode)
     if not shoot_word?
-        for word, i in pool_words
-            if c == word.remain[0]
-                shoot_word = pool_words.pop(i)
-                break
+        shoot_word = pool_words.get(c)
     if not shoot_word? and shoot_name?
         if c == shoot_name.remain[0]
             shoot_word = shoot_name
@@ -121,5 +145,4 @@
         inventory.add(shoot_word.full)
         shoot_word = null
         lvl += 1
-        for i in [1..lvl-pool_words.length]
-            pool_words.push(new ShootingWord(action_words.random()))
+        pool_words.autofill(lvl)


ability to cancel current shooting word

ability to cancel current shooting word

--- src/kick.coffee
+++ src/kick.coffee
@@ -42,6 +42,9 @@
              .append($('<b>').html(@remain))
         @left <= 0

+    reset: ->
+        @remain = @full
+

 action_words = ['box', 'kick', 'punch', 'strike']

@@ -66,6 +69,13 @@
 $(document).ready ->
     setInterval(draw, 12)

+$(document).keydown (event) ->
+    if event.keyCode in [8, 27, 46] # backspace, escape, delete
+        if shoot_word?
+            shoot_word.reset()
+            pool_words.push(shoot_word)
+            shoot_word = null
+
 $(document).keypress (event) ->
     c = String.fromCharCode(event.charCode)
     if not shoot_word?
@@ -73,10 +83,9 @@
             if c == word.remain[0]
                 shoot_word = pool_words.pop(i)
                 break
-
     if c == shoot_word?.remain[0]
         shoot_word.shot()
-    if shoot_word? and not shoot_word.remain
+    if shoot_word?.remain == ''
         point += shoot_word.full.length
         keep_words.push(shoot_word)
         shoot_word = null


use new partial support

use new partial support

--- manager/app/assets/javascripts/inventory/inventory_views.js.coffee
+++ manager/app/assets/javascripts/inventory/inventory_views.js.coffee
@@ -77,18 +77,21 @@
   class exports.Host extends Stark.View
     el: "div.inventory_content"
     template: "inventory/host"
+
     render: ->
       super
-      @partial(exports.HostMetadata, { metadata: @host.get("metadata") })
+      @partial exports.HostMetadata,
+        { metadata: @host.get("metadata") },
+        "div.host div.metadata"

   class exports.HostMetadata extends Stark.View
-    el: "div.host div.metadata"
     template: "inventory/_metadata"

     after_render: ->
       # show a popover for long values
       $("tbody tr").each (i, el) ->
-        dc = $(el).attr("data-content")
+        el = $(el)
+        dc = el.attr("data-content")
         if dc.length > 40
-          $(el).attr("data-content", "
#{dc}
")
-          $(el).popover()
+          el.attr("data-content", "
#{dc}
")
+          el.popover()


kicad: add slot support for footprint

kicad: add slot support for footprint

--- src/kicad-generator.coffee
+++ src/kicad-generator.coffee
@@ -125,7 +125,10 @@
             sprintf("  (pad %s %s %s (at #{@f} #{@f}) (size #{@f} #{@f}) (layers %s)"
               patObj.name, patObj.type, patObj.shape, patObj.x, patObj.y, patObj.width, patObj.height, patObj.layer)
           )
-          if patObj.hole? then fs.writeSync fd, sprintf("\n    (drill #{@f})", patObj.hole)
+          if patObj.slotWidth? and patObj.slotHeight?
+            fs.writeSync fd, sprintf("\n    (drill oval #{@f} #{@f})", patObj.slotWidth, patObj.slotHeight)
+          else if patObj.hole?
+            fs.writeSync fd, sprintf("\n    (drill #{@f})", patObj.hole)
           if patObj.mask? then fs.writeSync fd, sprintf("\n    (solder_mask_margin #{@f})", patObj.mask)
           if patObj.paste? then fs.writeSync fd, sprintf("\n    (solder_paste_margin #{@f})", patObj.paste)

@@ -270,6 +273,7 @@
     obj = shape
     obj.visible ?= true
     if obj.shape is 'rectangle' then obj.shape = 'rect'
+    if obj.shape is 'circle' and obj.width != obj.height then obj.shape = 'oval'

     layers =
       topCopper: 'F.Cu'


Automatically randomize confusion turns.

--- data/bw/moves.coffee
+++ data/bw/moves.coffee
@@ -47,6 +47,10 @@
     @afterSuccessfulHit = (battle, user, target, damage) ->
       if battle.rng.next("secondary effect") >= chance
         return
+
+      # TODO: Maybe find a better way to do this.
+      if Klass == Attachment.Confusion
+        options.turns ||= battle.rng.nextInt(1, 4)

       target.attach(new Klass(options))


Small improvement for omit new

Small improvement for omit new

--- framer/Extras/OmitNew.coffee
+++ framer/Extras/OmitNew.coffee
@@ -1,12 +1,7 @@
 exports.enable = (module=window) ->

-	ClassWrapper = (Klass)->
-		CreateWrapper = (args...) ->
-			if @ is window
-				return new Klass args...
-			else
-				@prototype = new Klass args...
-		return CreateWrapper
+	ClassWrapper = (Klass) -> (args...) ->
+		@prototype = new Klass(args...)

 	module.Frame = ClassWrapper(Framer.Frame)
 	module.Layer = ClassWrapper(Framer.Layer)


Use only subscribed streams

Use only subscribed streams

--- hubot-zulip/adapter.coffee
+++ hubot-zulip/adapter.coffee
@@ -42,7 +42,6 @@

     run: ->
         @connected = false
-        streams = process.env.HUBOT_ZULIP_STREAMS?.split ','

         @zulip = new zulip.Client
             client_name: "Hubot"
@@ -52,7 +51,7 @@

         @zulip.registerEventQueue
             event_types: ['message']
-            all_public_streams: true
+            all_public_streams: !process.env.HUBOT_ZULIP_ONLY_SUBSCRIBED_STREAMS?

         @zulip.on 'registered', (resp) =>
             if not @connected
@@ -66,8 +65,6 @@

         @zulip.on 'message', (msg) =>
             return if msg.sender_email is @zulip.email
-            return if streams? and msg.type is 'stream' and
-                msg.display_recipient not in streams

             room = room_for_message(msg)
             author = @robot.brain.userForId msg.sender_email,


Avoid maintaining patchesSinceLastStoppedChangingEvent state

Just use the history.

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -157,13 +157,13 @@
       params?.text

     @emitter = new Emitter
-    @patchesSinceLastStoppedChangingEvent = []
     @id = crypto.randomBytes(16).toString('hex')
     @buffer = new NativeTextBuffer(text)
     @debouncedEmitDidStopChangingEvent = debounce(@emitDidStopChangingEvent.bind(this), @stoppedChangingDelay)
     @textDecorationLayers = new Set()
     maxUndoEntries = params?.maxUndoEntries ? @defaultMaxUndoEntries
     @history = new History(this, maxUndoEntries)
+    @lastStoppedChangingCheckpoint = @history.createCheckpoint({})
     @nextMarkerLayerId = 0
     @nextDisplayLayerId = 0
     @defaultMarkerLayer = new MarkerLayer(this, String(@nextMarkerLayerId++))
@@ -1810,7 +1810,6 @@
     hunks = patch.getChanges()
     if hunks.length > 0
       @emitter.emit 'did-change-text', {changes: Object.freeze(normalizePatchChanges(hunks))}
-      @patchesSinceLastStoppedChangingEvent.push(patch)
       @debouncedEmitDidStopChangingEvent()

   # Identifies if the buffer belongs to multiple editors.
@@ -1824,12 +1823,12 @@
     return if @destroyed

     modifiedStatus = @isModified()
-    composedChanges = Patch.compose(@patchesSinceLastStoppedChangingEvent).getChanges()
+    composedChanges = @getChangesSinceCheckpoint(@lastStoppedChangingCheckpoint)
+    @lastStoppedChangingCheckpoint = @history.createCheckpoint({})
     @emitter.emit(
       'did-stop-changing',
-      {changes: Object.freeze(normalizePatchChanges(composedChanges))}
+      {changes: Object.freeze(composedChanges)}
     )
-    @patchesSinceLastStoppedChangingEvent = []
     @emitModifiedStatusChanged(modifiedStatus)

   emitModifiedStatusChanged: (modifiedStatus) ->


Add spliceArray as a public TextBuffer class method

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -114,6 +114,7 @@
   @Point: Point
   @Range: Range
   @newlineRegex: newlineRegex
+  @spliceArray: spliceArray

   encoding: null
   stoppedChangingDelay: 300


Make sure modified buffers are preserved regardless of setting

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -1473,9 +1473,10 @@
         @reload()

     @fileSubscriptions.add @file.onDidDelete =>
+      modified = @getText() != @cachedDiskContents
       @emitter.emit 'did-delete'
       @updateCachedDiskContents()
-      if @shouldDestroyOnFileDelete()
+      if not modified and @shouldDestroyOnFileDelete()
         @destroy()
       else
         @emitModifiedStatusChanged(true)


Delegate TextEditor.getMarkerCount to default marker layer.

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -1852,7 +1852,7 @@
   #
   # Returns a {Number}.
   getMarkerCount: ->
-    @buffer.getMarkerCount()
+    @defaultMarkerLayer.getMarkerCount()

   destroyMarker: (id) ->
     @getMarker(id)?.destroy()


Fix a inconsistent getLineCount() use

Fix a inconsistent getLineCount() use

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -3215,7 +3215,7 @@
   # top of the visible area.
   setFirstVisibleScreenRow: (screenRow, fromView) ->
     unless fromView
-      maxScreenRow = @getLineCount() - 1
+      maxScreenRow = @getScreenLineCount() - 1
       unless @config.get('editor.scrollPastEnd')
         height = @displayBuffer.getHeight()
         lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
@@ -3233,7 +3233,7 @@
     height = @displayBuffer.getHeight()
     lineHeightInPixels = @displayBuffer.getLineHeightInPixels()
     if height? and lineHeightInPixels?
-      Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getLineCount() - 1)
+      Math.min(@firstVisibleScreenRow + Math.floor(height / lineHeightInPixels), @getScreenLineCount() - 1)
     else
       null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -26,7 +26,7 @@
     @name centerText
     @methodOf TextScreen#
     @param {PixieCanvas} canvas The canvas to draw on
-
+
     ###
     centerText: (canvas, text, options={}) ->
       font = options.font || I.font


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -7,7 +7,7 @@
   self = GameState(I).extend
     centerText: (canvas, options={}) ->
       font = options.font || I.font
-      si
+      size = o
       canvas.font "#{size}px #{font}"

       canvas.centerText


Removed curly brackets from snippet prefix, to make expansion easier.

--- snippets/language-latex2e.cson
+++ snippets/language-latex2e.cson
@@ -1,36 +1,36 @@
 '.text.tex.latex':
-    # These snippets are available everywhere
-    '\\begin{}...\\end{}':
-      'prefix': '\\begin{}'
-      'body': '\\\\begin{${1:env}}$2\n\t$0\n\\\\end{${1:env}}'
-      'description': 'Begin new environment'
-      'descriptionMoreURL': 'https://www.sharelatex.com/learn/Environments'
-    '\\usepackage{}':
-        'prefix': '\\usepackage{}'
-        'body': '\\\\usepackage$2{${1:<package>}}\n$0'
-        'description': 'Load a package'
-        'descriptionMoreURL': 'https://www.sharelatex.com/learn/Creating_a_document_in_LaTeX#!#The_preamble_of_a_document'
-    '\\documentclass{}':
-        'prefix': '\\documentclass{}'
-        'body': '\\\\documentclass[${2:optional}]{${1:<class>}}\n$0'
-        'description': 'Set document class'
-        'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Document_Structure#Document_classes'
-    '\\begin{align}':
-        'prefix': '\\align'
-        'body': '\\%\n\\\\begin{align$2}\n\t$1$3\n\\\\end{align$2}'
-        'description': 'Begin amsmath align env`ironment'
-        'descriptionMoreURL': 'https://www.sharelatex.com/learn/Aligning_equations_with_amsmath#!#Aligning_several_equations'
+  # These snippets are available everywhere
+  '\\begin{}...\\end{}':
+    'prefix': '\\begin'
+    'body': '\\\\begin{${1:env}}$2\n\t$0\n\\\\end{${1:env}}'
+    'description': 'Begin new environment'
+    'descriptionMoreURL': 'https://www.sharelatex.com/learn/Environments'
+  '\\usepackage{}':
+    'prefix': '\\usepackage'
+    'body': '\\\\usepackage$2{${1:<package>}}\n$0'
+    'description': 'Load a package'
+    'descriptionMoreURL': 'https://www.sharelatex.com/learn/Creating_a_document_in_LaTeX#!#The_preamble_of_a_document'
+  '\\documentclass{}':
+    'prefix': '\\documentclass'
+    'body': '\\\\documentclass[${2:optional}]{${1:<class>}}\n$0'
+    'description': 'Set document class'
+    'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Document_Structure#Document_classes'
+  '\\begin{align}':
+    'prefix': '\\align'
+    'body': '\\%\n\\\\begin{align$2}\n\t$1$3\n\\\\end{align$2}'
+    'description': 'Begin amsmath align env`ironment'
+    'descriptionMoreURL': 'https://www.sharelatex.com/learn/Aligning_equations_with_amsmath#!#Aligning_several_equations'

-'.text.tex.latex .string.other.math.display.latex':
-    # These snippets are available in mathmode
-    'fraction':
-        'prefix': '\\frac{}'
-        'body': '\\\\frac{$1}{$2}$0'
-        'description': 'Display math fraction'
-        'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Mathematics'
-    'text':
-        'prefix': '\\text{}'
-        'body': '\\\\text{$1}$0'
-        'description': 'Text in mathmode'
-        'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Mathematics#Adding_text_to_equations'
+'.text.tex.latex .string.other.math':
+  # These snippets are available in mathmode
+  'fraction':
+    'prefix': '\\frac'
+    'body': '\\\\frac{$1}{$2}$0'
+    'description': 'Display math fraction'
+    'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Mathematics'
+  'text':
+    'prefix': '\\text'
+    'body': '\\\\text{$1}$0'
+    'description': 'Text in mathmode'
+    'descriptionMoreURL': 'https://en.wikibooks.org/wiki/LaTeX/Mathematics#Adding_text_to_equations'


Fix toaster config to pickup correct Test src folders

--- test/toaster.coffee
+++ test/toaster.coffee
@@ -2,7 +2,8 @@
 toast

 	folders: {
-		'src/app': 'AppEngine'
+		'src/specs': 'AppEngine'
+		'src/integration-tests': 'AppEngine'
 	}

 	# EXCLUDED FOLDERS (optional)


fix(contenteditable): hook up onClick handler for extensions

Summary: Add missing code to make onClick work in contenteditable extensions.

Test Plan: manual

Reviewers: juan, evan, bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D2395

--- src/components/contenteditable/contenteditable.cjsx
+++ src/components/contenteditable/contenteditable.cjsx
@@ -195,6 +195,7 @@
       onBlur: @_onBlur
       onFocus: @_onFocus
       onKeyDown: @_onKeyDown
+      onClick: @_onClick
       onCompositionEnd: @_onCompositionEnd
       onCompositionStart: @_onCompositionStart
     return handlers
@@ -274,6 +275,9 @@

   _onKeyDown: (event) =>
     @dispatchEventToExtensions("onKeyDown", event)
+
+  _onClick: (event) =>
+    @dispatchEventToExtensions("onClick", event)

   # We must set the `inCompositionEvent` flag in addition to tearing down
   # the selecton listeners. While the composition event is in progress, we


Back and h1 renaming

Back and h1 renaming

--- public/dreamcatcher/controllers/ib_browser_controller.coffee
+++ public/dreamcatcher/controllers/ib_browser_controller.coffee
@@ -9,6 +9,9 @@
   '.subGenres span click': (el) ->
     @genre = el.text()

+    $(".backArrow span").text("Home")
+    $("h1").text(@genre)
+
     $.get "/artists?genre=#{@genre}&section=#{@section}",(artists) ->
       $("#genreList").hide()
       $("#genreList").after(artists)
@@ -16,6 +19,9 @@

   'tr.artist click': (el) ->
     @artist = $("h2:first",el).text()
+
+    $(".backArrow span").text(@genre)
+    $("h1").text(@artist)

     $.get "/albums?artist=#{@artist}&section=#{@section}&genre=#{@genre}",(albums) ->
       $("#artistList").hide()


No need to separate between child objects and lists anymore

--- jquery.transparency.coffee
+++ jquery.transparency.coffee
@@ -19,11 +19,9 @@
   template = context.clone()
   data     = [data] unless $.isArray(data)

-  # Iterate over the list of objects
   for object in data
     values  = select(object, (key, value) -> typeof value == "string")
-    objects = select(object, (key, value) -> typeof value == "object"  && not $.isArray(value))
-    lists   = select(object, (key, value) -> $.isArray(value) )
+    objects = select(object, (key, value) -> typeof value == "object")
     result  = template.clone()

     for key, value of values
@@ -32,9 +30,6 @@
       result.find(".#{klass}").each ->
         assign $(this), attribute, value

-    for key, value of lists
-      result.find(".#{key}").render(value)
-
     for key, value of objects
       result.find(".#{key}").render(value)

@@ -42,18 +37,3 @@
     context.before(result)

   return context.remove() # Remove the original template from dom
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


Add initjs configurations for responde_with

--- app/assets/javascripts/neighborly/neighborly.js.coffee
+++ app/assets/javascripts/neighborly/neighborly.js.coffee
@@ -5,6 +5,9 @@
   configs:
     turbolinks: true
     pjax: false
+    respond_with:
+      'Create': 'New'
+      'Update': 'Edit'

   modules: -> []


OX-497 Draw outline with Bresenham algorithm

--- app/assets/javascripts/oxalis/model/volumelayer.coffee
+++ app/assets/javascripts/oxalis/model/volumelayer.coffee
@@ -9,6 +9,13 @@
 MODE_NORMAL      = 0
 MODE_SUB         = 1
 MODE_ADD         = 2
+
+
+# Macros
+swapMacro = (a, b) ->
+  __tmp = a
+  a = b
+  b = __tmp

 class VolumeLayer

@@ -152,77 +159,72 @@
       for y in [0...height]
         map[x][y] = false

-    @getOutlineVoxels(map)
+    setMap = (x, y) =>
+      map[x - minCoord2d[0]][y - minCoord2d[1]] = true
+
+    @drawOutlineVoxels(setMap)

     res = []
-    # Check every voxel in this cuboid
-    #startTime = new Date().getTime()
-    #for x in [@minCoord[0]..@maxCoord[0]]
-    #  for y in [@minCoord[1]..@maxCoord[1]]
-    #    for z in [@minCoord[2]..@maxCoord[2]]
-    #      if @containsVoxel([x, y, z])
-    #        res.push([x, y, z])
-    #for p in outline
-    #  res.push(@get3DCoordinate(p))
     for x in [0...width]
       for y in [0...height]
         if map[x][y]
           res.push(@get3DCoordinate([x + minCoord2d[0], y + minCoord2d[1]]))

-    #console.log(res)
     return res

-  getOutlineVoxels : (map) ->
-
-    start = new Date().getTime()
-
-    # Get first voxel within layer
-    minCoord2d = @get2DCoordinate(@minCoord)
-    maxCoord2d = @get2DCoordinate(@maxCoord)
-    offsetX    = minCoord2d[0]
-    offsetY    = minCoord2d[1]
-    p = [ Math.round((minCoord2d[0] + maxCoord2d[0]) / 2),
-          minCoord2d[1] ]
-    while not @contains2dCoordinate(p)
-      p[1]++
-
-    # Set up directions
-    directionRight = { xDiff :  1, yDiff :  0}
-    directionDown  = { xDiff :  0, yDiff :  1}
-    directionLeft  = { xDiff : -1, yDiff :  0}
-    directionUp    = { xDiff :  0, yDiff : -1}
-    directions     = [directionRight, directionDown,
-                      directionLeft, directionUp]
-
-    # Some helper methods
-    directionIndex = 0
-    direction = directions[directionIndex]
-    nextDir = ->
-      directionIndex = (directionIndex + 1) % 4
-      return direction = directions[directionIndex]
-    prevDir = ->
-      directionIndex = (directionIndex + 3) % 4
-      return direction = directions[directionIndex]
-    applyDir = (point) ->
-      return [point[0] + direction.xDiff, point[1] + direction.yDiff]
-
-    console.log "Before loop", new Date().getTime() - start
-    start = new Date().getTime()
-
-    inCount = 0
-    while inCount < 10
-      if map[p[0] - offsetX][p[1] - offsetY]
-        inCount++
+  drawOutlineVoxels : (setMap) ->
+
+    for i in [0...@contourList.length]
+
+      p1 = @get2DCoordinate(  @contourList[i]  )
+      p2 = @get2DCoordinate(  @contourList[(i+1) % @contourList.length]  )
+
+      @drawLine2d(p1[0], p1[1], p2[0], p2[1], setMap)
+
+  # Source: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm#Simplification
+  drawLine2d : (x, y, x1, y1, draw) ->
+
+    x_inc = if (dx = x1 - x) < 0 then -1 else 1
+    y_inc = if (dy = y1 - y) < 0 then -1 else 1
+
+    dx = Math.abs(dx)
+    dy = Math.abs(dy)
+
+    dx2 = dx << 1
+    dy2 = dy << 1
+
+    draw(x, y)
+
+    if dx >= dy
+
+      d = dx
+      mode = 0
+
+    else
+
+      swapMacro(y, x)
+      swapMacro(y_inc, x_inc)
+      swapMacro(dy2, dx2)
+      d = dy
+      mode = 1
+
+    err = dy2 - d
+
+    for i in [0...d]
+
+      if err > 0
+        y += y_inc
+        err -= dx2
+
+      err += dy2
+      x   += x_inc
+
+      if mode
+        draw(y, x)
       else
-        inCount = 0
-        map[p[0] - offsetX][p[1] - offsetY] = true
-      prevDir()
-      while not @contains2dCoordinate( applyDir(p) )
-        nextDir()
-      p = applyDir(p)
-
-    console.log "After Loop", new Date().getTime() - start
-    start = new Date().getTime()
+        draw(x, y)
+
+    return

   get2DCoordinate : (coord3d) ->


Added IdeaToggle Coffee class

Added IdeaToggle Coffee class

--- app/assets/javascripts/views/idea_view_model.js.coffee
+++ app/assets/javascripts/views/idea_view_model.js.coffee
@@ -5,22 +5,30 @@
     @projectName = ko.computed => @project?.name()
     @newTitle = ko.observable()
     @newDescription = ko.observable()
+    @toggler = new WinniVote.IdeaToggle @id()

   edit: =>
     @newTitle @title()
     @newDescription @description()
-    @toggleEditing 'content', 'form'
+    @toggler.toggle()

-  cancel: =>
-    @toggleEditing 'form', 'content'
+  cancel: =>
+    @toggler.toggle()

   save: =>
     @title @newTitle()
     @description @newDescription()
     @update()
-    @toggleEditing 'form', 'content'
-
-  # Switches between idea content div and idea edit div
-  toggleEditing: (fromName, toName)  =>
-    id = "#idea-#{@id()}"
-    $("#{id} .idea-#{fromName}").fadeOut 'fast', -> $("#{id} .idea-#{toName}").fadeIn 'fast'+    @toggler.toggle()
+
+class WinniVote.IdeaToggle
+
+  constructor: (id) ->
+    @idea_id  = "#idea-#{id}"
+    @source = -> $("#{@idea_id} .idea-content")
+    @target = -> $("#{@idea_id} .idea-form")
+
+  toggle: () =>
+    [source, target] = [@source(), @target()]
+    source.fadeOut 'fast', -> target.fadeIn 'fast'
+    [@source, @target] = [@target, @source]


pass more class tests

pass more class tests

--- test/classes.coffee
+++ test/classes.coffee
@@ -2,23 +2,23 @@

   suite 'Class Definition', ->

-    #test 'Overriding the static property new doesn't clobber Function::new', ->
-    #
-    #  class OneClass
-    #    @new: 'new'
-    #    function: 'function'
-    #    constructor: (name) -> @name = name
-    #
-    #  class TwoClass extends OneClass
-    #  delete TwoClass.new
-    #
-    #  Function.prototype.new = -> new this arguments...
-    #
-    #  ok (TwoClass.new('three')).name is 'three'
-    #  ok (new OneClass).function is 'function'
-    #  ok OneClass.new is 'new'
-    #
-    #  delete Function.prototype.new
+    test "Overriding the static property new doesn't clobber Function::new", ->
+
+      class OneClass
+        @new: 'new'
+        function: 'function'
+        constructor: (name) -> @name = name
+
+      class TwoClass extends OneClass
+      delete TwoClass.new
+
+      Function.prototype.new = -> new this arguments...
+
+      ok (TwoClass.new('three')).name is 'three'
+      ok (new OneClass).function is 'function'
+      ok OneClass.new is 'new'
+
+      delete Function.prototype.new

     #test 'basic classes, again, but in the manual prototype style', ->
     #
@@ -101,15 +101,15 @@
       instance = new obj.klass
       ok instance.method() is 'value'

-    #test 'Implicit objects as static properties', ->
-    #
-    #  class Static
-    #    @static =
-    #      one: 1
-    #      two: 2
-    #
-    #  ok Static.static.one is 1
-    #  ok Static.static.two is 2
+    test 'Implicit objects as static properties', ->
+
+      class Static
+        @static =
+          one: 1
+          two: 2
+
+      ok Static.static.one is 1
+      ok Static.static.two is 2

     #test 'classes with static-level implicit objects', ->
     #
@@ -423,19 +423,19 @@
       m = new Mini
       eq (func() for func in m.generate()).join(' '), '10 10 10'

-    #test 'contructor called with varargs', ->
-    #
-    #  class Connection
-    #    constructor: (one, two, three) ->
-    #      [@one, @two, @three] = [one, two, three]
-    #
-    #    out: ->
-    #      "#{@one}-#{@two}-#{@three}"
-    #
-    #  list = [3, 2, 1]
-    #  conn = new Connection list...
-    #  ok conn instanceof Connection
-    #  ok conn.out() is '3-2-1'
+    test 'contructor called with varargs', ->
+
+      class Connection
+        constructor: (one, two, three) ->
+          [@one, @two, @three] = [one, two, three]
+
+        out: ->
+          "#{@one}-#{@two}-#{@three}"
+
+      list = [3, 2, 1]
+      conn = new Connection list...
+      ok conn instanceof Connection
+      ok conn.out() is '3-2-1'

     test 'classes wrapped in decorators', ->

@@ -449,32 +449,33 @@
       ok (new Test).prop  is 'value'
       ok (new Test).prop2 is 'value2'

-    #test 'ensure that constructors invoked with splats return a new object', ->
-    #
-    #  args = [1, 2, 3]
-    #  Type = (@args) ->
-    #  type = new Type args
-    #
-    #  ok type and type instanceof Type
-    #  ok type.args and type.args instanceof Array
-    #  ok v is args[i] for v, i in type.args
-    #
-    #  Type1 = (@a, @b, @c) ->
-    #  type1 = new Type1 args...
-    #
-    #  ok type1 instanceof   Type1
-    #  eq type1.constructor, Type1
-    #  ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
-    #
-    #  # Ensure that constructors invoked with splats cache the function.
-    #  called = 0
-    #  get = -> if called++ then false else class Type
-    #  new get() args...
+    test 'ensure that constructors invoked with splats return a new object', ->
+
+      args = [1, 2, 3]
+      Type = (@args) ->
+      type = new Type args
+
+      ok type and type instanceof Type
+      ok type.args and type.args instanceof Array
+      ok v is args[i] for v, i in type.args
+
+      Type1 = (@a, @b, @c) ->
+      type1 = new Type1 args...
+
+      ok type1 instanceof   Type1
+      eq type1.constructor, Type1
+      ok type1.a is args[0] and type1.b is args[1] and type1.c is args[2]
+
+      # Ensure that constructors invoked with splats cache the function.
+      called = 0
+      get = -> if called++ then false else class Type
+      new get() args...

     test '`new` shouldn\'t add extra parens', ->

       ok new Date().constructor is Date

+    # FAIL
     # TODO: this test belongs with the operator tests
     #test '`new` works against bare function', ->
     #


Find specific user in findAdmin

Find specific user in findAdmin

--- server/models/registeredUser.coffee
+++ server/models/registeredUser.coffee
@@ -9,7 +9,7 @@
     aboutme: String

 registeredUserSchema.statics.findAdmin = (list) ->
-    RegisteredUser.findOne({admin: true})
+    RegisteredUser.findOne({admin: true, username: "pkalinin"})

 registeredUserSchema.plugin(passportLocalMongoose);


Fixed issue where unbuffered js blocks don't get highlighted

--- grammars/pug.cson
+++ grammars/pug.cson
@@ -16,15 +16,7 @@
     name: "comment.other.doctype.jade"
   }
   {
-    begin: "^(\\s*)-$"
-    comment: "Unbuffered code block."
-    end: "^(?!(\\1\\s)|\\s*$)"
-    name: "source.js"
-    patterns: [
-      {
-        include: "source.js"
-      }
-    ]
+    include: "#unbuffered_code_block"
   }
   {
     begin: "^(\\s*)(script)(?=[.#(\\s])((?![^\\n]*type=)|(?=[^\\n]*type=['\"](text|application)/javascript['\"]))"
@@ -475,6 +467,9 @@
       }
       {
         include: "#unbuffered_code"
+      }
+      {
+        include: "#unbuffered_code_block"
       }
       {
         include: "#mixin_definition"
@@ -1146,4 +1141,14 @@
         include: "source.js"
       }
     ]
+  unbuffered_code_block:
+    begin: "^(\\s*)-(\r|\n)"
+    comment: "Unbuffered code block."
+    end: "^(?!(\\1\\s)|\\s*$)"
+    name: "source.js"
+    patterns: [
+      {
+        include: "source.js"
+      }
+    ]
 scopeName: "source.pug"


Fixing listener test

Fixing listener test

--- test/di_test.coffee
+++ test/di_test.coffee
@@ -12,4 +12,4 @@
     require('../src/di')(@robot)

   it 'registers a respond listener', ->
-    expect(@robot.respond).to.have.been.calledWith(/di\s*(?:me)?\s*(.+)?/i)
+    expect(@robot.respond).to.have.been.calledWith(/di\s+?me?\s+?(.+)/i)


:do_not_litter::arrow_up_small: Updated in browser at strd6.github.io/editor

--- main.coffee
+++ main.coffee
@@ -1,7 +1,7 @@
 global.appData = ->
   ENV.APP_STATE

-modelData = ENV.APP_STATE ?=
+modelData = ENV?.APP_STATE ?=
   source: ""
   url: ""


added api server

added api server

--- main.coffee
+++ main.coffee
@@ -10,6 +10,7 @@

 auth      = require "./src/qqauth"
 api       = require "./src/qqapi"
+apiserver = require "./src/apiserver"
 QQBot     = require "./src/qqbot"
 defaults  = require './src/defaults'
 config    = require './config'
@@ -25,7 +26,7 @@
 get_tokens = (isneedlogin, options,callback)->

   if isneedlogin
-    auth.login options , (cookies,auth_info)=>
+    auth.login options , (cookies,auth_info)->
       defaults.data KEY_COOKIES, cookies
       defaults.data KEY_AUTH   , auth_info
       defaults.save()
@@ -40,7 +41,10 @@
 # 获取好友，群，群成员信息，然后进入守护模式
 # TODO: 获取信息 + 守护模式 同步状态
 run = ->
-  isneedlogin = process.argv.pop().trim() isnt 'nologin'
+  # 设置api server
+  apiserver.run(config['api-port'], config['api-token'])
+
+  isneedlogin = process.argv.pop().trim() isnt 'nologin'
   get_tokens isneedlogin , config , (cookies,auth_info)->
     bot = new QQBot(cookies,auth_info,config)

@@ -50,7 +54,15 @@
         process.exit(1)
       log.info "Entering runloop, Enjoy!"
       bot.runloop()
-
+
+      apiserver.on_groupmessage (name,message,res)->
+        group = bot.get_group {name:name}
+        bot.send_message_to_group group, message, (ret,e)->
+          resp_ret = {bot:ret}
+          if e
+            resp_ret.err = 1
+            resp_ret.msg = "#{e}"
+          res.endjson resp_ret
 run()


added lottery and sell can no longer rob a bankrupped person added death info

--- main.coffee
+++ main.coffee
@@ -34,6 +34,7 @@
 weapons = {}
 dougu = {}
 stomach = []
+lottery = []
 student_group = undefined
 kfcCustomers = undefined
 not_same_again = true
@@ -111,11 +112,11 @@

         list_one = bot.groupmember_info[student_group.gid].minfo
         for suspect in list_one
-          members[suspect.nick] = { nickname: suspect.nick, nice: 0, objects: ["蛋挞","可乐", "面包","烤翅","逻辑"], gold: 10000000, water: 0, life: 1500, firstseen:true}
+          members[suspect.nick] = { nickname: suspect.nick, nice: 0, objects: ["蛋挞","可乐", "面包","烤翅","逻辑"], gold: 3000, water: 0, life: 1500, firstseen:true}

         bot.request.get {url:"http://api.hitokoto.us/rand",json:true}, (e,r,data)->
           if data and data.hitokoto
-            student_group.send "大家好啊，我是男神唐马儒，我能联系到美国肯打鸡和俄国军火商，同时我也是专业鉴黄师~\n\n今日名言警句：\n"+data.hitokoto + "\n--" + data.source
+            student_group.send "大家好啊，我是男神唐马儒，我能联系到美国肯打鸡、俄国军火商和中国体育彩票，同时我也是专业鉴黄师~\n\n今日名言警句：\n"+data.hitokoto + "\n--" + data.source
             return
           else
             student_group.send "大家好啊，我是男神唐马儒，我家经营肯打鸡，同时我也是专业鉴黄师~。。"
@@ -128,10 +129,24 @@
             mouth_open = true
             student_group.send "这儿呢"

+          if (lottery.length>9)
+            lottery_winner = Math.floor(Math.random() * (9 + 1) + 0)
+            winner_name = lottery[lottery_winner]
+            lottery_winner_second = Math.floor(Math.random() * (8 + 1) + 0)
+            winner_name_second = lottery[lottery_winner_second]
+            lottery_winner_third = Math.floor(Math.random() * (7 + 1) + 0)
+            winner_name_third = lottery[lottery_winner_third]
+            members[winner_name].gold = members[winner_name].gold+1200
+            members[winner_name_third].gold = members[winner_name_third].gold+600
+            members[winner_name_second].gold = members[winner_name_second].gold+600
+            student_group.send "【彩票开奖】恭喜"+members[winner_name].nickname+"获得一等奖1500新币！！！\n"+"恭喜"+members[winner_name_second].nickname+"和"+members[winner_name_third].nickname+"获得二等奖600新币！"
+
           if (last_command is content) and (last_commander is message.from_user.nick)
             not_same_again = false
           else
             not_same_again = true
+            last_command = content
+            last_commander = message.from_user.nick

           if ((mouth_open) is true) and ((not_same_again) is true)

@@ -150,8 +165,11 @@
                     can_use = false
                     break
                 if can_use is true
-                  student_group.send "玩家"+message.from_user.nick+"更名为"+toNick
-                  members[message.from_user.nick].nickname = toNick
+                  if ((toNick is "唐马儒") or (toNick is "肯打鸡")) is true
+                    student_group.send "想作死请去打劫唐马儒，别自己改名叫唐马儒。"
+                  else
+                    student_group.send "玩家"+message.from_user.nick+"更名为"+toNick
+                    members[message.from_user.nick].nickname = toNick

             if (content.indexOf("肯打鸡购买") is 0) is true
@@ -164,6 +182,15 @@
                   members[message.from_user.nick].gold = members[message.from_user.nick].gold - foods[thingToBuy].price
                 else
                   student_group.send members[message.from_user.nick].nickname+"你个穷鬼也想买"+thingToBuy+"？你有"+foods[thingToBuy].price+"新币么？"
+
+            if (content.indexOf("购买彩票") is 0) is true
+              if (members[message.from_user.nick].gold >= 100) is true
+                student_group.send members[message.from_user.nick].nickname+"购买彩票一张，消费100新币！祝您中奖！"
+                members[message.from_user.nick].objects.push
+                members[message.from_user.nick].gold = members[message.from_user.nick].gold - 100
+                lottery.push message.from_user.nick
+              else
+                student_group.send members[message.from_user.nick].nickname+"，你没钱还敢来买彩票？"

             if (content.indexOf("军火商购买") is 0) is true
               toBuy = content.indexOf("买")
@@ -230,7 +257,8 @@
                     rob_damage = 0-actual_damage

                   actual_damage = actual_damage + rob_damage
-
+                  if (members[actualPersonToRob].gold <= 0) is true
+                    rob_gold = 0
                   members[message.from_user.nick].life = members[message.from_user.nick].life - self_damage + get_life
                   members[message.from_user.nick].gold = members[message.from_user.nick].gold + rob_gold
                   dissappear_ran = Math.floor(Math.random() * (100 + 1) + 0)
@@ -243,7 +271,8 @@
                   members[actualPersonToRob].gold = members[actualPersonToRob].gold - rob_gold

                 student_group.send members[message.from_user.nick].nickname+"使用"+usedWeapon+"袭击了"+personToRob+"，造成"+actual_damage+"伤害并抢夺了"+rob_gold+"新币！"+members[message.from_user.nick].nickname+"在战斗中自身受到"+self_damage+"伤害！"+dissappear_to_say
-
+                if(members[actualPersonToRob].life <= 0)
+                  student_group.send members[actualPersonToRob].nickname+"被"+members[message.from_user.nick].nickname+"残忍的杀害了！"
             if (content.indexOf("查看我的状态") is 0) is true
               things_to_show = ""
               for itemz in members[message.from_user.nick].objects
@@ -282,11 +311,12 @@
                   student_group.send "这东西不能吃！"

-            if (content.indexOf("扔掉") is 0) is true
-              toThrow = content.indexOf("掉")
+            if (content.indexOf("出售") is 0) is true
+              toThrow = content.indexOf("售")
               laji = content.substring(toThrow+1)
               if (laji in members[message.from_user.nick].objects) is true
-                student_group.send members[message.from_user.nick].nickname+"丢弃了"+laji+"。"
+                student_group.send members[message.from_user.nick].nickname+"出售了"+laji+"，换回"+(members[message.from_user.nick].objects[laji].price*0.4)+"新币"
+                members[message.from_user.nick].gold = members[message.from_user.nick].gold+(members[message.from_user.nick].objects[laji].price*0.4)
                 members[message.from_user.nick].objects.splice(members[message.from_user.nick].objects.indexOf(laji), 1)
               else
                 if ((laji of foods) or (laji of weapons)) is true


Removed deprecated code.

Removed deprecated code.

--- main.coffee
+++ main.coffee
@@ -37,14 +37,12 @@

 hide_window_timeout = null
 hide_window = ->
-    disable_render()
-    hide_window_timeout = setTimeout ewin.hide, 200
+    ewin.hide()
     setTimeout show_window, 60000 * 5 # 5 min

 show_window = ->
     ewin.show()
     set_inactivity_check?()
-    enable_render?()

 show_window()

@@ -188,13 +186,14 @@
     last_auto_hide_interval = setInterval auto_hide_interval, 1000
     render_all()

+# This function will be filled on componentWillMount
 set_dialog = ->
-enable_render = ->
-disable_render = ->
-
+
+# This is to know the value of the current
+# active dialog out of the component render function
 current_dialog = 0
+
 main_component = Component
-
     componentDidUpdate: (cosas)->
         current_dialog = @state.dialog

@@ -206,18 +205,10 @@
                 set_inactivity_check()
             @setState {dialog}

-        enable_render = =>
-            @setState {render:true}
-
-        disable_render = =>
-            @setState {render:false}
-
     getInitialState: ->
         dialog: 0
-        render: true

     render: ->
-
         working_on_value = last_task
         working_on_submit = ()=>
             if working_on_value
@@ -342,10 +333,7 @@
                 mixins.border3d 0.5
                 mixins.boxShadow theme.shadows.hard
             ]
-            if @state.render
-                dialog
-            else
-                null
+            dialog
             # text_input.ui working_on


better selection semantics

better selection semantics


Initializing date picker with new Date() when model value is empty.

--- src/main.coffee
+++ src/main.coffee
@@ -21,10 +21,10 @@
 		attrs.$observe 'maxdate', (val) ->
 			if val? and angular.isDate val then scope.restrictions.maxdate = val
 		ngModel.$render = -> scope.setDate ngModel.$modelValue
-
+
 		saveFn = $parse attrs.onSave
 		cancelFn = $parse attrs.onCancel
-
+
 		scope.save = ->
 			scope._modelValue = scope.date
 			ngModel.$setDirty()
@@ -37,7 +37,7 @@
 			mindate: undefined
 			maxdate: undefined
 		scope.setDate = (newVal) ->
-			scope.date = if newVal? then new Date newVal else new Date()
+			scope.date = if newVal then new Date newVal else new Date()
 			scope.calendar._year = scope.date.getFullYear()
 			scope.calendar._month = scope.date.getMonth()
 			scope.clock._minutes = scope.date.getMinutes()


Returning object from collides function

Returning object from collides function

--- source/engine.collision.coffee
+++ source/engine.collision.coffee
@@ -19,7 +19,7 @@
   ###
   collides: (bounds, sourceObject, selector=".solid") ->
     self.find(selector).inject false, (collided, object) ->
-      collided or (object != sourceObject) and object.collides(bounds)
+      collided or (object != sourceObject) and object.collides(bounds) and object

   ###*
   Detects collisions between a bounds and the game objects.


more javascript-y

more javascript-y

--- controllers/judges.coffee
+++ controllers/judges.coffee
@@ -1,6 +1,6 @@
+_ = require 'underscore'
 app = require '../config/app'
 Person = app.db.model 'Person'
-_ = require 'underscore'

 # index
 app.get '/judges', (req, res, next) ->
@@ -35,8 +35,8 @@

 # edit (just redirects to person/edit with twitter login)
 app.get '/judges/:judgeId/edit', (req, res, next) ->
-  edit_person_path = "/people/#{req.param('judgeId')}/edit"
+  editPersonPath = "/people/#{req.param('judgeId')}/edit"
   if req.loggedIn
-    res.redirect edit_person_path
+    res.redirect editPersonPath
   else
-    res.redirect "/login/twitter?returnTo=#{edit_person_path}"
+    res.redirect "/login/twitter?returnTo=#{editPersonPath}"


Don't confuse timecop

Don't confuse timecop

--- src/main.coffee
+++ src/main.coffee
@@ -1,7 +1,8 @@
 module.exports =
   activate: ->
-    lps = atom.workspace.getTextEditors()
-      .map((ed) -> ed.getGrammar().packageName)
-    if 'language-haskell' in lps
-      atom.packages.triggerActivationHook? 'language-haskell:grammar-used'
+    setImmediate ->
+      lps = atom.workspace.getTextEditors()
+        .map((ed) -> ed.getGrammar().packageName)
+      if 'language-haskell' in lps
+        atom.packages.triggerActivationHook? 'language-haskell:grammar-used'
   deactivate: ->


resize disin, disout editors

resize disin, disout editors

--- src/main.coffee
+++ src/main.coffee
@@ -617,6 +617,7 @@
         $("#outer").append(disassembler)
         if not cls[current] then cls[current] = new DisassemblerMain()
         cls[current].reinit()
+        resize()

     $("#switchEmu").click =>
@@ -630,8 +631,7 @@
         $("#outer").append(emulator)
         if not cls[current] then cls[current] = new EmulatorMain()
         cls[current].reinit()
-
-    console.log window.location.hash
+        resize()

     if window.location.hash == "#disasm"
         $("#switchDis").click()
@@ -643,6 +643,8 @@
         $(".CodeMirror").height(height)
         $("#register").height(height5 * 3)
         $("#ram").height(height5 * 2)
+        $("#disin").height(height5 * 3)
+        $("#disout").height(height5 * 2)

     $(window).resize resize
     resize()


Stop requiring 'events'

Stop requiring 'events'

--- src/main.coffee
+++ src/main.coffee
@@ -1,6 +1,5 @@
 binding = require '../build/Release/pathwatcher.node'
 {HandleMap} = binding
-{EventEmitter} = require 'events'
 {Emitter} = require 'event-kit'
 fs = require 'fs'
 path = require 'path'


Add version to usage message

Add version to usage message

--- src/main.coffee
+++ src/main.coffee
@@ -16,8 +16,11 @@
   process.env[if process.platform is 'win32' then 'USERPROFILE' else 'HOME']

 parseCommandLine = ->
+  version = fs.readFileSync(path.join(__dirname, '..', '..', 'version'), 'utf8')
+
   options = optimist(process.argv[1..])
   options.usage """
+    Atom #{version}

     Usage: atom [options] [file ..]
   """
@@ -33,7 +36,6 @@
   testMode = true if args['test']
   wait = true if args['wait']
   pid = args['pid']
-  version = String fs.readFileSync(path.join(__dirname, '..', '..', 'version'))

   if args['resource-path']
     resourcePath = args['resource-path']


docs(jqLite): Updated formatting

docs(jqLite): Updated formatting

--- src/main.coffee
+++ src/main.coffee
@@ -5,12 +5,11 @@
 @name angular.element

 @description
-Angular comes with jqLite, a tiny, API-compatible subset of jQuery.
-However, its functionality is very limited and MacGyver extends
-jqLite to make sure MacGyver components work properly.
+Angular comes with jqLite, a tiny, API-compatible subset of jQuery. However, its
+functionality is very limited and MacGyver extends jqLite to make sure MacGyver
+components work properly.

-Real jQuery will continue to take precedence over jqLite and all
-functions MacGyver extends.
+Real jQuery will continue to take precedence over jqLite and all functions MacGyver extends.

 MacGyver adds the following methods:
 - [height()](http://api.jquery.com/height/) - Does not support set


activity feed: error check buckets have entries in them

--- client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -165,8 +165,9 @@
     for item in @itemsOrdered
       data = item.getData()

+      continue  if typeof data.group is "string"
       continue  unless data.group
-      continue  if typeof data.group is "string"
+      continue  unless data.group[0]

       if data.group[0].constructorName is followee.bongo_.constructorName
         if data.anchor && data.anchor.id is follower.id


Change twitter text to Vote for

Change twitter text to Vote for

--- client/view/handlebar-helpers.coffee
+++ client/view/handlebar-helpers.coffee
@@ -30,7 +30,8 @@
 Handlebars.registerHelper 'twitterShareProposalUrl', (proposal)->
   if proposal
     url = Router.fullPath('proposal', id: proposal.id)
-    shareText = "Submitted to reversim conf: \"#{proposal.title}\""
+    # shareText = "Submitted to reversim conf: \"#{proposal.title}\""
+    shareText = "Vote for: \"#{proposal.title}\""
     "https://twitter.com/share?url=#{encodeURIComponent(url)}&text=#{encodeURIComponent(shareText)}&via=reversim"

 Handlebars.registerHelper 'fbShareProposalUrl', (proposal)->


[tests] Close server only if it was started

--- test/helpers.coffee
+++ test/helpers.coffee
@@ -33,7 +33,7 @@
             setTimeout done, 1000

 helpers.after = (done) ->
-    @server.close()
+    @server.close() if @server?
     helpers.cleanDb ->
         ds.del "/data/#{userID}/", done


BEGONE DERP LOG

BEGONE DERP LOG

--- src/live_controller.coffee
+++ src/live_controller.coffee
@@ -35,7 +35,6 @@
   return window[helperClassName]

 window.onpopstate = (event) ->
-  console.log("derp")
   event ?= {}
   state = event.state || {}
   method = state.method || "get"


Trim tag pattern

Trim tag pattern

--- src/extensions/outline-view/src/outline-view.coffee
+++ src/extensions/outline-view/src/outline-view.coffee
@@ -76,7 +76,7 @@
     @miniEditor.focus()

   getTagLine: (tag) ->
-    pattern = tag.pattern?.replace(/(^^\/\^)|(\$\/$)/g, '') # Remove leading /^ and trailing $/
+    pattern = $.trim(tag.pattern?.replace(/(^^\/\^)|(\$\/$)/g, '')) # Remove leading /^ and trailing $/
     return unless pattern
     for line, index in fs.read(@rootView.project.resolve(tag.file)).split('\n')
       return new Point(index, 0) if pattern is $.trim(line)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -18,6 +18,6 @@

   equals obj.I.maxHealth, 150

-test "should ", ->
+test "should set ", ->

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -27,6 +27,6 @@
     name: 'turbo'

   equals obj.I.turbo, 100
-  equals obj.
+  equals obj.I.maxTurbo,

 module()


clarify FileHelper documentation

clarify FileHelper documentation

--- lib/utils/file_helper.coffee
+++ lib/utils/file_helper.coffee
@@ -6,14 +6,14 @@
 yaml_parser = require("./yaml_parser")

 class FileHelper
+
   ###*
    * [constructor description]
-   * @param {[type]} file [description]
-   * @return {undefined}
+   * @param {string} file - path to a file
    * @constructor
   ###
+
   constructor: (file) ->
-    # set paths
     @path = file
     @contents = fs.readFileSync(file, 'utf8')
     @export_path = output_path(file)
@@ -23,10 +23,10 @@

   ###*
    * [parse_dynamic_content description]
-   * @return {[type]} [description]
    * @public
    * @uses set_paths
   ###
+
   parse_dynamic_content: ->
     front_matter_string = yaml_parser.match(@contents)
     if front_matter_string
@@ -60,6 +60,7 @@
    * @public
    * @uses set_paths, FileHelper.parse_dynamic_content
   ###
+
   set_layout: ->
     # make sure a layout actually has to be set
     layouts_set = Object.keys(roots.project.layouts).length > 0
@@ -84,16 +85,18 @@
    * as locals so they are available in html templates
    * @param {String} contents - compiled contents
   ###
+
   set_dynamic_locals: ->
     @dynamic_locals.contents = @contents
     roots.project.locals.site[@category_name].push(@dynamic_locals)

   ###*
    * [locals description]
-   * @param {[type]} extra [description]
-   * @return {[type]} [description]
+   * @param {Object} extra - any extra properties to be added to locals
+   * @return {Object} - modified locals object
    * @public
   ###
+
   locals: (extra) ->
     locals = _.clone(roots.project.locals)

@@ -112,11 +115,11 @@

   ###*
    * write FileHelper.contents to FileHelper.path
-   * @return {[type]} [description]
+   * @return {string} content - string of content to write
    * @public
   ###
+
   write: (content) ->
-
     # if content is passed in, write that
     @contents = content if content


round ms away if duration > 10s

round ms away if duration > 10s

--- lib/actions/views/logs.coffee
+++ lib/actions/views/logs.coffee
@@ -89,7 +89,7 @@

 updateLog = (log) ->
   log.date = TimeFormat.formatDateTime(log.start * 1000)
-  log.duration = if log.time then TimeFormat.formatDuration(log.time * 1000) else "<running>"
+  log.duration = if log.time? then TimeFormat.formatDuration(log.time * 1000) else "<running>"
   if log.exception || log.fail_reason
     log.classes = "error"
     if log.exception && log.fail_reason
@@ -153,6 +153,11 @@
   @formatDuration = (timestamp_ms) ->
     if timestamp_ms == 0
       return "0ms"
+    showMs = true
+    # round 10.5s to 11ms
+    if timestamp_ms > 10000 && ( (timestamp_ms % 1000) > 500)
+      timestamp_ms += 1000
+      showMs = false
     arr = []
     if timestamp_ms > (3600 * 1000)
       arr.push Math.floor( timestamp_ms / (3600 * 1000) )
@@ -166,7 +171,7 @@
       arr.push Math.floor( timestamp_ms / (1000) )
       arr.push "s"
     timestamp_ms %= (1000)
-    if timestamp_ms > 0
+    if timestamp_ms > 0 && showMs
       arr.push Math.floor( timestamp_ms )
       arr.push "ms"
     arr.join("")


rename ::observe -> ::handleEvents

rename ::observe -> ::handleEvents

--- lib/autohide-tree-view.coffee
+++ lib/autohide-tree-view.coffee
@@ -74,7 +74,7 @@
     # wait until the tree view package is activated
     atom.packages.activatePackage('tree-view').then (treeViewPkg) =>
       @registerTreeView treeViewPkg
-      @observe()
+      @handleEvents()
       # start with pushEditor = true, we'll change it back later
       # activating the package looks much better this way
       @conf.pushEditor = true
@@ -112,7 +112,7 @@
       atom.views.getView(@treeView.panel)?.style.width = ''
       [@treeView, @treeViewEl] = []

-  observe: ->
+  handleEvents: ->
     # observe config
     @disposables.add atom.config.onDidChange 'autohide-tree-view', ({newValue}) => @conf = clone newValue
     @disposables.add atom.config.onDidChange 'autohide-tree-view.pushEditor', => @update()


:white_check_mark: :checkered_flag: Allow specs to pass on Windows

--- spec/notifications-spec.coffee
+++ spec/notifications-spec.coffee
@@ -311,7 +311,10 @@

             button = fatalError.querySelector('.btn')
             expect(button.textContent).toContain 'Create issue on the notifications package'
-            expect(button.getAttribute('href')).toContain 'atom/notifications/issues/new'
+            unless process.platform is 'win32'
+              expect(button.getAttribute('href')).toContain 'atom/notifications/issues/new'
+            else
+              expect(button.getAttribute('href')).toContain 'git.io/cats'

             expect(issueBody).toMatch /Atom Version\*\*: [0-9].[0-9]+.[0-9]+/ig
             expect(issueBody).not.toMatch /Unknown/ig
@@ -376,7 +379,8 @@
             a + 1
           catch e
             # Pull the file path from the stack
-            filePath = e.stack.split('\n')[1].match(/\(([^:]+)/)[1]
+            # For Windows we have to ignore the starting drive letter (eg C:\)
+            filePath = e.stack.split('\n')[1].match(/\(((\w:\\)[^:]+|([^:]+))/)[1]
             window.onerror.call(window, e.toString(), filePath, 2, 3, message: e.toString(), stack: undefined)

           notificationContainer = workspaceElement.querySelector('atom-notifications')
@@ -419,7 +423,10 @@

           button = fatalError.querySelector('.btn')
           expect(button.textContent).toContain 'Create issue on atom/atom'
-          expect(button.getAttribute('href')).toContain 'atom/atom/issues/new'
+          unless process.platform is 'win32'
+            expect(button.getAttribute('href')).toContain 'atom/atom/issues/new'
+          else
+            expect(button.getAttribute('href')).toContain 'git.io/cats'

           expect(issueBody).toContain 'ReferenceError: a is not defined'
           expect(issueBody).toContain '**Thrown From**: Atom Core'
@@ -458,7 +465,10 @@
           fatalNotification = fatalError.querySelector('.fatal-notification')
           expect(button.textContent).toContain 'Create issue'
           expect(fatalNotification.textContent).toContain 'You can help by creating an issue'
-          expect(button.getAttribute('href')).toContain 'github.com/atom/notifications/issues/new'
+          unless process.platform is 'win32'
+            expect(button.getAttribute('href')).toContain 'github.com/atom/notifications/issues/new'
+          else
+            expect(button.getAttribute('href')).toContain 'git.io/cats'

       describe "when the error has not been reported", ->
         beforeEach ->
@@ -487,10 +497,16 @@
               button = fatalError.querySelector('.btn')
               encodedMessage = encodeURI(truncatedMessage)
               expect(button.textContent).toContain 'Create issue'
-              expect(button.getAttribute('href')).toContain "github.com/atom/notifications/issues/new?title=#{encodedMessage}&body="
+              unless process.platform is 'win32'
+                expect(button.getAttribute('href')).toContain "github.com/atom/notifications/issues/new?title=#{encodedMessage}&body="
+              else
+                expect(button.getAttribute('href')).toContain 'git.io/cats'

         describe "when the system is darwin", ->
           beforeEach ->
+            UserUtilities = require '../lib/user-utilities'
+            spyOn(UserUtilities, 'getPlatform').andReturn 'darwin'
+
             generateFakeAjaxResponses()
             generateException()
             fatalError = notificationContainer.querySelector('atom-notification.fatal')