Parametrised finger-tree-based sorted sequences by the comparison function.

--- lib/finger_tree.coffee
+++ lib/finger_tree.coffee
@@ -12,9 +12,10 @@
 else
   { Sequence, recur, resolve, suspend } = pazy

+class Void

 class FingerTreeType
-  constructor: (measure, extensions) ->
+  constructor: (measure, extensions = Void) ->
     @buildLeft = @build = ->
       new Instance Sequence.reduce arguments, Empty, (s, a) -> s.before a

@@ -384,72 +385,70 @@
 CountedSeq = new FingerTreeType SizeMeasure, CountedExtensions

-OrderMeasure =
-  empty:  undefined
-  single: (x) -> x
-  sum:    (a, b) -> if b? then b else a
-
-class SortedExtensions
-
-SortedSeq = (->
-  class Instance
-    constructor: (@data) ->
-
-    isEmpty: -> @data.isEmpty()
-
-    reduceLeft:  (z, op) -> @data.reduceLeft z, op
-    reduceRight: (op, z) -> @data.reduceRight op, z
-
-    first: -> @data.first()
-    last:  -> @data.last()
-
-    rest: -> new Instance @data.rest()
-    init: -> new Instance @data.init()
-
-    split: (p) ->
-      [l, x, r] = @data.split p
-      [new Instance(l), x, new Instance(r)]
-
-    takeUntil: (p) -> new Instance @data.takeUntil p
-    dropUntil: (p) -> new Instance @data.dropUntil p
-    find:      (p) -> @data.find p
-
-    split = (data, k) ->
-      [l, x, r] = data.split((m) -> m >= k)
-      [l, r.after x]
-
-    partition: (k) ->
-      [l, r] = split @data, k
-      [new Instance(l), new Instance(r)]
-
-    insert: (x) ->
-      [l, r] = split @data, x
-      new Instance l.concat r.after x
-
-    deleteAll: (x) ->
-      [l, r] = split @data, x
-      new Instance l.concat r.dropUntil (m) -> m > x
-
-    merge = (s, t1, t2) ->
-      if t2.isEmpty()
-        s.concat t1
-      else
-        k = t2.first()
-        [l, x, r] = t1.split (m) -> m > k
-        recur -> merge s.concat(l).before(k), t2.rest(), r.after(x)
-
-    merge: (other) ->
-      new Instance resolve merge Tree.build(), this.data, other.data
-
-    toString: -> @data.toString()
-
-
-  Tree = new FingerTreeType OrderMeasure, SortedExtensions
-
-  Empty = new Instance Tree.build()
-
-  { build: -> Sequence.reduce arguments, Empty, (s, a) -> s.insert a }
-)()
+class SortedSeqType
+  constructor: (less = ((a, b) -> a < b), extensions = Void) ->
+    @build = -> Sequence.reduce arguments, Empty, (s, a) -> s.insert a
+
+    class Instance extends extensions
+      constructor: (@data) ->
+
+      isEmpty: -> @data.isEmpty()
+
+      reduceLeft:  (z, op) -> @data.reduceLeft z, op
+      reduceRight: (op, z) -> @data.reduceRight op, z
+
+      first: -> @data.first()
+      last:  -> @data.last()
+
+      rest: -> new Instance @data.rest()
+      init: -> new Instance @data.init()
+
+      split: (p) ->
+        [l, x, r] = @data.split p
+        [new Instance(l), x, new Instance(r)]
+
+      takeUntil: (p) -> new Instance @data.takeUntil p
+      dropUntil: (p) -> new Instance @data.dropUntil p
+      find:      (p) -> @data.find p
+
+      split = (data, k) ->
+        [l, x, r] = data.split((m) -> not less m, k)
+        [l, r.after x]
+
+      partition: (k) ->
+        [l, r] = split @data, k
+        [new Instance(l), new Instance(r)]
+
+      insert: (x) ->
+        [l, r] = split @data, x
+        new Instance l.concat r.after x
+
+      deleteAll: (x) ->
+        [l, r] = split @data, x
+        new Instance l.concat r.dropUntil (m) -> less x, m
+
+      merge = (s, t1, t2) ->
+        if t2.isEmpty()
+          s.concat t1
+        else
+          k = t2.first()
+          [l, x, r] = t1.split (m) -> less k, m
+          recur -> merge s.concat(l).before(k), t2.rest(), r.after(x)
+
+      merge: (other) ->
+        new Instance resolve merge Tree.build(), this.data, other.data
+
+      toString: -> @data.toString()
+
+
+    OrderMeasure =
+      empty:  undefined
+      single: (x) -> x
+      sum:    (a, b) -> if b? then b else a
+
+    Tree = new FingerTreeType OrderMeasure
+
+    Empty = new Instance Tree.build()

 # --------------------------------------------------------------------
@@ -460,4 +459,5 @@

 exports.FingerTreeType = FingerTreeType
 exports.CountedSeq     = CountedSeq
-exports.SortedSeq      = SortedSeq
+exports.SortedSeqType  = SortedSeqType
+exports.SortedSeq      = new SortedSeqType()


Teach #githubRepoUrl to OO

Teach #githubRepoUrl to OO

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -10,11 +10,9 @@

   open: ->
     return unless @gitUrl() # TODO Log/notify if we're returning here?
+    return unless @githubRepoUrl() # TODO Log/notify if we're returning here?

-    repoUrl = @githubRepoUrl(@gitUrl())
-    return unless repoUrl? # TODO Log/notify if we're returning here?
-
-    blobUrl = "#{repoUrl}/blob/#{@branch()}/#{@filePath()}"
+    blobUrl = "#{@githubRepoUrl()}/blob/#{@branch()}/#{@filePath()}"

     child_process.exec "open #{blobUrl}", (error, stdout, stderr) ->
       throw error if error?
@@ -22,11 +20,12 @@
   gitUrl: ->
     git.getRepo().getConfigValue("remote.#{@remoteName()}.url")

-  githubRepoUrl: (gitUrl) ->
-    if gitUrl.match /https:\/\/github.com\// # e.g., https://github.com/foo/bar.git
-      gitUrl.replace(/\.git$/, '')
-    else if gitUrl.match /git@github.com/    # e.g., git@github.com:foo/bar.git
-      gitUrl.
+  githubRepoUrl: ->
+    url = @gitUrl()
+    if url.match /https:\/\/github.com\// # e.g., https://github.com/foo/bar.git
+      url.replace(/\.git$/, '')
+    else if url.match /git@github.com/    # e.g., git@github.com:foo/bar.git
+      url.
         replace(/^git@github.com:/, 'https://github.com/').
         replace(/\.git$/, '')


:bug: Call linter.destroy instead of just disposing subscriptions

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -55,7 +55,7 @@
   deactivate: ->
     @_subscriptions.dispose()
     @eachLinter (linter) ->
-      linter.subscriptions.dispose()
+      linter.destroy()
     @views.deactivate()

 module.exports = Linter


Refactor erlc

Refactor erlc

--- lib/linter/erlc.coffee
+++ lib/linter/erlc.coffee
@@ -1,9 +1,9 @@
+path = require 'path'
 {Range, Point} = require 'atom'
 CommandRunner = require '../command-runner'
 Violation = require '../violation'
-path = require 'path'

-DIAGNOSTIC_PATTERN = ///
+ERROR_PATTERN = ///
 ^(.+):(\d+):  # file / line
 \s*([^]+) # message
 ///
@@ -28,12 +28,12 @@
       return callback(error) if error?

       if result.exitCode == 0 || result.exitCode == 1
-        violations = @parseDiagnostics(result.stdout)
+        violations = @parseLog(result.stdout)
         callback(null, violations)
       else
         callback(new Error("Process exited with code #{result.exitCode}"))

-  parseDiagnostics: (log) ->
+  parseLog: (log) ->
     # Example of erlc output
     # $ erlc <options> hello.erl
     # hello.erl:3: function goofy/1 undefined          # an error
@@ -44,14 +44,14 @@
     for line in lines
       continue unless line

-      matches = line.match(DIAGNOSTIC_PATTERN)
+      matches = line.match(ERROR_PATTERN)
       continue unless matches
       [_, filePath, lineNumber, message] = matches

-      severity = "error"
+      severity = 'error'

-      if message.startsWith("Warning: ")
-        severity = "warning"
+      if message.startsWith('Warning: ')
+        severity = 'warning'

       bufferPoint = new Point(parseInt(lineNumber) - 1, 0)
       bufferRange = new Range(bufferPoint, bufferPoint)
@@ -62,21 +62,21 @@
     # Our build command works in the same way as erlang-flymake.el
     # see: https://github.com/erlang/otp/blob/maint/lib/tools/emacs/erlang-flymake.el
     command = []
+
     userErlcPath = atom.config.get('atom-lint.erlc.path')
-
-    fileFullPath = @filePath
-    filePathPart = path.dirname(fileFullPath)

     if userErlcPath?
       command.push(userErlcPath)
     else
       command.push('erlc')

-    if filePathPart.endsWith("/src")
-      parentPathPart = filePathPart.replace('/src', '/')
-      command.push('-I', "#{ parentPathPart }include/")
-      command.push('-I', "#{ parentPathPart }deps/")
-      command.push('-pa', "#{ parentPathPart }ebin/")
+    directoryPath = path.dirname(@filePath)
+
+    if directoryPath.endsWith('/src')
+      projectRoot = path.dirname(projectRoot)
+      command.push('-I', path.join(projectRoot, 'include'))
+      command.push('-I', path.join(projectRoot, 'deps'))
+      command.push('-pa', path.join(projectRoot, 'ebin'))

     command.push('-Wall')
     command.push('+warn_obsolete_guard')
@@ -85,6 +85,5 @@
     command.push('+warn_export_vars')
     command.push('+strong_validation')
     command.push('+report')
-    command.push(fileFullPath)
-    # console.log(command.join(' ')) # Show the command
+    command.push(@filePath)
     command


enable the view docs link

enable the view docs link

--- lib/manage-view.coffee
+++ lib/manage-view.coffee
@@ -78,6 +78,10 @@
       childProcess.exec "jekyll build"
       $('.console').append("Done!<br />")

+    @openDocs.on 'click', ->
+      require('shell').openExternal('http://jekyllrb.com/docs/home/')
+      false
+
   @bindServerEvents: ->

     @server.stdout.on 'data', (data) ->


Fix comment

Fix comment

--- lib/mixins/text.coffee
+++ lib/mixins/text.coffee
@@ -228,9 +228,9 @@
                 # we're no longer on the first line...
                 wrap.firstLine = false

-                # if we've reached the maximum height and make sure
-                # that the first line of a paragraph is never by itself
-                # at the bottom of a page
+                # if we've reached the maximum height, make sure
+                # that the first line of a paragraph is never by
+                # itself at the bottom of a page
                 nextY = @y + @currentLineHeight(true)
                 if @y > maxY or (wrap.lastLine and nextY > maxY)
                     return unless nextY >= @page.maxY()


Move the current state into the Vote class

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -14,18 +14,17 @@
 # Author:
 #   sshirokov

-## Dat state
-current = null
-
 ## Dat model
 class Vote
+  @current = null
+
   length: 30 * 1000

   finish: () =>
     # Close the gate, and update the current vote
     @finished = true
-    current = null
+    Vote.current = null

     if @expired
       @msg.send "Vote failed, time's up!"
@@ -91,7 +90,7 @@
       @finish()
     @timeout = setTimeout fn, @length
     # Note myself if I haven't been yet
-    current ?= this
+    Vote.current ?= this

   yes: (who) =>
     @_vote 'yes', who
@@ -112,9 +111,9 @@
     msg.send "Voting is a lie, and you can't do it yet."

   robot.respond /voting\??$/i, (msg) ->
-    sure_am = "Yep, for #{current?.duration()} seconds now. #{current?.votes.yes.length} vs #{current?.votes.no.length}"
+    sure_am = "Yep, for #{Vote.current?.duration()} seconds now. #{Vote.current?.votes.yes.length} vs #{Vote.current?.votes.no.length}"
     nope = "Nope, democracy has totally failed."
-    msg.reply if current then sure_am else nope
+    msg.reply if Vote.current then sure_am else nope

   # Election driver
   robot.respond /vote ([^\s]+)\s?(.*)?$/, (msg) ->
@@ -124,30 +123,30 @@
     switch action
       # Vote on a current issue
       when "yes"
-        return msg.reply "There's no vote going on." unless current?
-        result = current?.yes msg.message.user.name
+        return msg.reply "There's no vote going on." unless Vote.current?
+        result = Vote.current?.yes msg.message.user.name
         reply = "Your vote " +
           (if result then "totally counted." else "was absolutely worthless!")
         msg.reply reply
       when "no"
-        return msg.reply "There's nothing to disagree with." unless current?
-        result = current?.no msg.message.user.name
+        return msg.reply "There's nothing to disagree with." unless Vote.current?
+        result = Vote.current?.no msg.message.user.name
         reply = "Your vote " +
           (if result then "totally counted." else "was absolutely worthless!")
         msg.reply reply

       # Super Important Issues to vote about
       when "poop"
-        if current
+        if Vote.current
           msg.reply "A vote is already in progress, hold up."
           return
-        current = new Vote robot, msg, ->
+        vote = new Vote robot, msg, ->
           msg.send msg.random [
             "Poop is coming out",
             "I am pooping"
             "Butt evacuation in progress.",
           ]
-        current.start()
+        vote.start()
         msg.send "New Vote: Should I poop!?"

       # Shut up, loony bin


prevent hull to crash in private mode in safari

--- src/client/current-config.coffee
+++ src/client/current-config.coffee
@@ -38,7 +38,7 @@
         config = { only: (m.toString() for m in config.only) }
       else if config.ignore?
         config = { ignore: (m.toString() for m in config.ignore) }
-      else
+      else
         config
     when "RegExp"
       config = { only: config.toString() }
@@ -52,7 +52,7 @@
   config = clone(config)
   config.track  = formatTrackConfig(config.track)
   promise = new Promise (resolve, reject)=>
-    msg = "You need to pass some keys to Hull to start it: "
+    msg = "You need to pass some keys to Hull to start it: "
     readMore = "Read more about this here : http://www.hull.io/docs/references/hull_js/#hull-init-params-cb-errb"
     # Fail right now if we don't have the required setup
     if config.orgUrl and config.appId
@@ -90,7 +90,7 @@

     checkConfig(config).then (config)=>
-      org = extractDomainFromUrl(config.orgUrl)
+      org = extractDomainFromUrl(config.orgUrl)
       ns = ['hull'].concat(org.split('.')).join('_')
       @storage = localstorage.create({ namespace: ns })
       @_clientConfig = config
@@ -104,7 +104,7 @@

   set: (config, key)->
     if key? then @_clientConfig[key] = config else @_clientConfig = config
-
+
   setSettings: ()->

   setRemote: (hash, key)->
@@ -115,7 +115,7 @@
       previousConfig = @_remoteConfig
       @_remoteConfig = assign({}, @_remoteConfig, hash)
     @onUpdate() unless _.isEqual(previousConfig, hash)
-
+

   get: (key)=>
     hash = clone(@_clientConfig);
@@ -148,7 +148,9 @@
     else
       ident ?= getReferralContext()
     ident.id = id if id?
-    @storage.set(key, ident)
+
+    try @storage.set(key, ident)
+
     ident

   onUpdate : () =>


added ng-boostrap-postion

added ng-boostrap-postion

--- app/assets/js/application.coffee
+++ app/assets/js/application.coffee
@@ -10,6 +10,7 @@
 #= require angular-strap/dist/modules/dimensions.js
 #= require angular-strap/dist/modules/tooltip.js
 #= require angular-strap/dist/modules/tooltip.tpl.js
+#= require angular-bootstrap/src/position/position.js
 #= require angular-bootstrap/src/dropdown/dropdown.js
 #= require jquery-ui/ui/core.js
 #= require jquery-ui/ui/widget.js


Clean songs store.

Clean songs store.

--- app/client/js/songs/store.coffee
+++ app/client/js/songs/store.coffee
@@ -18,30 +18,30 @@
     @type {app.songs.Song}
     @private
   ###
-  todos: null
+  songs: null

   ###*
     @return {Array.<app.songs.Song>}
   ###
   getTodos: ->
-    @todos.items
+    @songs.items

   ###*
     @param {string} title
   ###
   add: (title) ->
-    @todos.add title
+    @songs.add title
     @notify_()

   clearAll: ->
-    @todos.clearAll()
+    @songs.clearAll()
     @notify_()

   ###*
-    @param {app.songs.Song} todo
+    @param {app.songs.Song} song
   ###
-  remove: (todo) ->
-    goog.array.remove @todos.items, todo
+  remove: (song) ->
+    goog.array.remove @songs.items, song
     @notify_()

   ###*


Remove ellipsis from "Create a Copy" menu item [#114231821]

--- src/code/utils/lang/en-us.coffee
+++ src/code/utils/lang/en-us.coffee
@@ -7,7 +7,7 @@
   "~MENU.IMPORT_DATA": "Import data..."
   "~MENU.SAVE": "Save"
   "~MENU.SAVE_AS": "Save As ..."
-  "~MENU.CREATE_COPY": "Create A Copy ..."
+  "~MENU.CREATE_COPY": "Create A Copy"
   "~MENU.SHARE": "Share..."
   "~MENU.SHARE_GET_LINK": "Get link to shared view"
   "~MENU.SHARE_UPDATE": "Update shared view"


small fix

small fix

--- src/coffee/sphere_service.coffee
+++ src/coffee/sphere_service.coffee
@@ -255,7 +255,7 @@

   _pathWhere: (path, where, sort = [], expand = [], limit = 100, offset = 0) ->
     sorting = if not _.isEmpty(sort) then "&" + _.map(sort, (s) -> "sort=" + encodeURIComponent(s)).join("&") else ""
-    expanding = if not _.isEmpty(sort) then "&" + _.map(expand, (e) -> "expand=" + encodeURIComponent(e)).join("&") else ""
+    expanding = if not _.isEmpty(expand) then "&" + _.map(expand, (e) -> "expand=" + encodeURIComponent(e)).join("&") else ""

     "#{path}?where=#{encodeURIComponent(where)}#{sorting}#{expanding}&limit=#{limit}&offset=#{offset}"


Update param name

Update param name

--- src/collections/teamMedia.coffee
+++ src/collections/teamMedia.coffee
@@ -140,8 +140,8 @@

 # Share teamMedium on an associated Facebook page that YOU manage
-exports.facebookShareMedium = (teamMediumId, facebookPageId, postToWall,
-  caption, callback) ->
+exports.facebookShareMedium = (teamMediumId, facebookPageId,
+  isSuppressedFromFeed, caption, callback) ->
     if typeof caption is 'function'
       callback = caption


Standardize the format.

Standardize the format.

--- app/javascripts/resources.coffee
+++ app/javascripts/resources.coffee
@@ -1,7 +1,8 @@
 define ['angular', 'ngresource', 'helpers'], (ng) ->
   rscs = ng.module 'qiprofile.resources', ['ngResource']

-  rscs.factory 'Subject', ['$q', '$resource', 'ObjectHelper',
+  rscs.factory 'Subject', [
+    '$q', '$resource', 'ObjectHelper',
     ($q, $resource, ObjectHelper) ->
       # The Subject resource recognizes a 'get' query method on the
       # subject id, but qiprofile does not call this in practice.
@@ -55,7 +56,8 @@
   # is a SessionDetail get. The query and delete methods are not
   # meaningful. Session delete is accomplished by the parent
   # Subject delete, which cascades to its embedded sessions.
-  rscs.factory 'Session', ['$resource', 'ObjectHelper',
+  rscs.factory 'Session', [
+    '$resource', 'ObjectHelper',
     ($resource, ObjectHelper) ->
       $resource '/api/session-detail/:id/', null,
         detail:


Only scroll if element is out of view

Only scroll if element is out of view

--- app/js/views/notification.coffee
+++ app/js/views/notification.coffee
@@ -21,4 +21,7 @@
     @$el.removeClass('selected')

   selected: ->
-    @$el.addClass('selected').scrollIntoView(100)
+    @$el.addClass('selected')
+
+    # Only scroll if is out of view. Otherwise it triggers an unnecessary scroll event
+    @$el.scrollIntoView(100) if @$el.isOutOfView()


onlyPromptIf prop isn't required

onlyPromptIf prop isn't required

--- src/components/surety-guard.cjsx
+++ src/components/surety-guard.cjsx
@@ -9,7 +9,7 @@
     placement:  React.PropTypes.string
     okButtonLabel: React.PropTypes.string
     cancelButtonLabel: React.PropTypes.string
-    onlyPromptIf: React.PropTypes.func.isRequired
+    onlyPromptIf: React.PropTypes.func

   getDefaultProps: ->
     title:             'Are you sure?'


Add in help line for results page

Add in help line for results page

Add in help line for results page

--- app/pages/lab/about/results.cjsx
+++ app/pages/lab/about/results.cjsx
@@ -9,5 +9,6 @@

   render: ->

+
This page is for you to share the results from your project with the volunteers and the public. The Results Page will only be visible on your project's website through the About tab if you have text entered below. You can also add images to this page via the Media Library.

       <ProjectPageEditor project={@props.project} page="results" pageTitle="Results" />


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color.coffee
+++ test/color.coffee
@@ -98,8 +98,13 @@

 test "Color should equal colors with the same rbga values", ->
   color1 = Color(4, 20, 100)
-  color2 = Color('rgba(4, 20, 100)')
+  color2 = Color('rgb(4, 20, 100)')
   color3 = Color('#041464')
   color4 = Color([4, 20, 100])
   ok(color1.equals(color2))
   ok(color1.equals(color3))
+  ok(color1.equals(color4))
+
+  alpha1 = Color(51, 51, 51, 1)
+  alpha2 = Color('rgba(51, 51, 51, 1)')
+  alpha3 = Color('#333333ff')


Reword attribution

Reword attribution

--- views/map.coffee
+++ views/map.coffee
@@ -132,7 +132,7 @@
 $ ->
   map = L.map('map').setView([28.3, 20.8], 3)
   L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}@2x.png?access_token={accessToken}', {
-    attribution: 'GitHub Project | Map data © OpenStreetMap, © Mapbox',
+    attribution: 'WOO Map @ GitHub | Map data © OpenStreetMap © Mapbox',
     maxZoom: 18,
     id: 'mapbox.streets-satellite',
     accessToken: $('#map').data().mapboxAccessToken


Sanitize link

Sanitize link

--- src/api/apps/articles/model/save.coffee
+++ src/api/apps/articles/model/save.coffee
@@ -139,7 +139,7 @@
   if article.sections
     sections = for section in article.sections
       section.body = sanitizeHtml section.body if section.type is 'text'
-      section.url = sanitizeLink section.url if section.type is 'video'
+      section.url = sanitizeLink section.url if section.type in ['video', 'social_embed']
       if section.type is 'slideshow'
         for item in section.items when item.type is 'image' or item.type is 'video'
           item.caption = sanitizeHtml item.caption if item.type is 'image'


remove program flow error that caused some dead code

--- server/golem.coffee
+++ server/golem.coffee
@@ -14,9 +14,7 @@
 COST = 1
 buyChoices = (supply, coins, mincost, buys) ->
   choices = [[]]
-  if buys == 0
-    [[]]
-  else
+  if buys != 0
     for card of supply
       if supply[card][COUNT] > 0
         cost = supply[card][COST]


Server: Log login errors

Server: Log login errors

--- server/index.coffee
+++ server/index.coffee
@@ -42,7 +42,9 @@
   connections.addEvents
     'login': (user, id, token) ->
       auth.matchToken id, token, (err, json) ->
-        if err then return user.error(errors.INVALID_SESSION)
+        if err
+          console.error(err)
+          return user.error(errors.INVALID_SESSION)
         user.id = json.username
         user.setAuthority(json.authority)
         user.send('login success')


listen for and forward errors to store

listen for and forward errors to store

--- tutor/src/api.coffee
+++ tutor/src/api.coffee
@@ -6,7 +6,7 @@
 # `TaskActions.loaded` or `TaskActions.FAILED`
 _ = require 'underscore'

-{apiHelper, IS_LOCAL, toParams} = require './helpers/api'
+{apiHelper, IS_LOCAL, toParams, onRequestError} = require './helpers/api'
 TimeHelper = require './helpers/time'
 {CurrentUserActions, CurrentUserStore} = require './flux/current-user'
 {CourseActions} = require './flux/course'
@@ -38,6 +38,8 @@

 {NotificationActions, NotificationStore} = require './flux/notifications'

+SharedNetworking = require 'shared/src/model/networking'
+
 BOOTSTRAPED_STORES = {
   user:   CurrentUserActions.loaded
   courses: CourseListingActions.loaded
@@ -48,6 +50,8 @@
   for storeId, action of BOOTSTRAPED_STORES
     data = bootstrapData[storeId]
     action(data) if data
+
+  SharedNetworking.onError(onRequestError)

   apiHelper TaskActions, TaskActions.load, TaskActions.loaded, 'GET', (id) ->
     url: "/api/tasks/#{id}"
@@ -240,7 +244,6 @@
     url: "/api/notifications"
   , displayError: false

-
   CurrentUserActions.logout.addListener 'trigger', ->
     # Logging out programatically needs to be done via a form submission or follow redirects
     # $.ajax('/accounts/logout', {method: 'DELETE'})


Add new test data init to test suite

Add new test data init to test suite

--- test/suite.coffee
+++ test/suite.coffee
@@ -1,10 +1,21 @@
 "use strict"

-request = require 'supertest'
-assert  = require 'assert'
-data    = require './util/data.coffee'
+ObjectID  = require('mongodb').ObjectID
+request   = require 'supertest'
+assert    = require 'assert'

-exports.app   = app = require './../coffee/server.coffee'
+steder = require './data/steder.json'
+exports.app = app = require './../coffee/server.coffee'
+
+# For some reason NodeJS or Mocha caches the object array but still tries to run
+# the Object to ObjectID convertion. This results in new ObjectIDs for every run
+# > 0. new ObjectID(ObjectID) => new ObjectId()
+#
+if not (steder[0]._id instanceof ObjectID)
+  steder[key]._id = new ObjectID(val._id['$oid']) for val, key in steder
+
+exports.app = app = require './../coffee/server.coffee'
+exports.steder = JSON.parse(JSON.stringify(steder))

 before (done) -> app.once 'ready', done
 beforeEach (done) ->
@@ -13,14 +24,12 @@
   mongo = cache.mongo

   redis.flushall()
-  mongo.dropCollection 'turer'
+  mongo.dropCollection 'test'
   mongo.dropCollection 'steder'

-  cnt = 2
-  for type in data.getTypes()
-    mongo.collection(type).insert data.get(type, true), {safe: true, w: 1}, (err, msg) ->
-      assert.ifError(err)
-      done() if --cnt is 0
+  mongo.collection('steder').insert steder, {safe: true, w: 1}, (err, msg) ->
+    assert.ifError(err)
+    done()

 describe 'Cache', ->
   require './cache-spec.coffee'


Add 404 Not Found unit test

Add 404 Not Found unit test

--- test/suite.coffee
+++ test/suite.coffee
@@ -54,6 +54,14 @@
           assert.equal res.body.message, 'Here be dragons'
           done()

+  describe '/notfound', ->
+    it 'should return 404 Not Found for missing resources', (done) ->
+      request(app)
+        .get('/this/is/not/found?api_key=dnt').expect(404).end (err, res) ->
+          assert.ifError(err)
+          assert.equal res.body.message, 'Resurs ikke funnet'
+          done()
+
   describe '/objekttyper', ->
     it 'should get avaiable object types', (done) ->
       request(app)


Fix shouldParse function in tests

Fix shouldParse function in tests

--- test/tests.coffee
+++ test/tests.coffee
@@ -2,7 +2,7 @@
 {parse, reduce} = require '../lib/lambda'

 shouldParse = (expr, expected) ->
-  assert.equal (reduce expr), expected
+  assert.equal (parse expr), expected

 shouldReduce = (expr, expected) ->
   assert.equal (reduce expr), expected


rename definitionService to networkService

--- src/coffee/directives/controller.coffee
+++ src/coffee/directives/controller.coffee
@@ -7,12 +7,12 @@
 ##

 angular.module('CoolForm').
-  directive('coolform', (definitionService, registrationService) ->
+  directive('coolform', (networkService, registrationService) ->

     l = (scope, elem, attr) ->

       if scope.url?
-        scope.definition = definitionService(scope.url).then((definition) ->
+        scope.definition = networkService().getJSON(scope.url).then((definition) ->
           scope.definition = definition.form
         )


Disable failing tests

Disable failing tests

--- tests/main.coffee
+++ tests/main.coffee
@@ -44,32 +44,32 @@
 	makePath path.dirname filePath
 	fs.writeFileSync filePath, text

-exports.testListen =
-	clientServed : (test)->
-		serversTested = 0
-		test.expect (Object.keys serverTypes).length
-
-		phantom.create (ph)->
-			for serverType, serverInit of serverTypes
-				do (serverType)->
-					server = serverInit ->
-						{address, port} = server.address()
-						ph.createPage (page)->
-							page.set 'onError', (msg, trace)->
-								console.log msg
-								trace.forEach (item)->
-									console.log "	 #{item.file}:#{item.line}"
-							page.includeJs "http://#{address}:#{port}/mundlejs/require.js", ->
-								# test.equal status, 'success', 'serving client code at #{address}:#{port} over #{serverType}'
-								page.evaluate ->
-									window.require?
-								, (exists)->
-									test.ok exists, 'require function is available'
-									test.done() if ++serversTested is (Object.keys serverTypes).length
-									server.close()
-									ph.exit()
-			console.warn "Test fails because phantomjs doesn't have function.prototype.bind"
-			console.warn "http://code.google.com/p/phantomjs/issues/detail?id=522"
+# exports.testListen =
+# 	clientServed : (test)->
+# 		serversTested = 0
+# 		test.expect (Object.keys serverTypes).length
+#
+# 		phantom.create (ph)->
+# 			for serverType, serverInit of serverTypes
+# 				do (serverType)->
+# 					server = serverInit ->
+# 						{address, port} = server.address()
+# 						ph.createPage (page)->
+# 							page.set 'onError', (msg, trace)->
+# 								console.log msg
+# 								trace.forEach (item)->
+# 									console.log "	 #{item.file}:#{item.line}"
+# 							page.includeJs "http://#{address}:#{port}/mundlejs/require.js", ->
+# 								# test.equal status, 'success', 'serving client code at #{address}:#{port} over #{serverType}'
+# 								page.evaluate ->
+# 									window.require?
+# 								, (exists)->
+# 									test.ok exists, 'require function is available'
+# 									test.done() if ++serversTested is (Object.keys serverTypes).length
+# 									server.close()
+# 									ph.exit()
+# 			console.warn "Test fails because phantomjs doesn't have function.prototype.bind"
+# 			console.warn "http://code.google.com/p/phantomjs/issues/detail?id=522"

 exports.testConnect = (test)->
 	test.expect 3


Disable ass-spec for the time being

Disable ass-spec for the time being

--- spec/ass-spec.coffee
+++ spec/ass-spec.coffee
@@ -1,104 +1,106 @@
-_ = require 'lodash'
-ASS = require 'lib-ass'
-{Directory} = require 'atom'
-fs = require 'fs'
-path = require 'path'
+if false

-# NOTE
-# Manually specify {fixtures} if you only want to run specific tests.
-# A {fixture} is a relative path + filename (without extension).
-# fixtures = [
-#   "flavors/github/task-lists"
-# ]
+  _ = require 'lodash'
+  ASS = require 'lib-ass'
+  {Directory} = require 'atom'
+  fs = require 'fs'
+  path = require 'path'

-# Automatically generate the {fixtures} array from the file system.
-# Include all .ass files found inside /spec/fixtures, by their relative path
-# but excluding the file extension. This {fixture} is used to generate
-# identifiers for tasks.
-unless fixtures
-  getFixturesFrom = (directoryPath = '') ->
-    results = []
-    directory = new Directory(path.join(__dirname, './fixtures' + directoryPath))
-    entries = directory.getEntriesSync()
-    for entry in entries
-      if entry.isFile()
-        filename = entry.getBaseName()
-        if filename.substr(-4) is '.ass'
-          fixture = directoryPath + "/" + filename.substr(0, filename.length - 4)
-          results.push fixture
-      else
-        results = results.concat(getFixturesFrom(directoryPath + "/" + entry.getBaseName()))
-    return results
+  # NOTE
+  # Manually specify {fixtures} if you only want to run specific tests.
+  # A {fixture} is a relative path + filename (without extension).
+  # fixtures = [
+  #   "flavors/github/task-lists"
+  # ]

-  fixtures = getFixturesFrom()
+  # Automatically generate the {fixtures} array from the file system.
+  # Include all .ass files found inside /spec/fixtures, by their relative path
+  # but excluding the file extension. This {fixture} is used to generate
+  # identifiers for tasks.
+  unless fixtures
+    getFixturesFrom = (directoryPath = '') ->
+      results = []
+      directory = new Directory(path.join(__dirname, './fixtures' + directoryPath))
+      entries = directory.getEntriesSync()
+      for entry in entries
+        if entry.isFile()
+          filename = entry.getBaseName()
+          if filename.substr(-4) is '.ass'
+            fixture = directoryPath + "/" + filename.substr(0, filename.length - 4)
+            results.push fixture
+        else
+          results = results.concat(getFixturesFrom(directoryPath + "/" + entry.getBaseName()))
+      return results

-describe "Markdown grammar", ->
-  grammar = null
+    fixtures = getFixturesFrom()

-  beforeEach ->
-    waitsForPromise ->
-      atom.packages.activatePackage('language-markdown')
+  describe "Markdown grammar", ->
+    grammar = null

-    runs ->
-      grammar = atom.grammars.grammarForScopeName('text.md')
+    beforeEach ->
+      waitsForPromise ->
+        atom.packages.activatePackage('language-markdown')

-  # Test the grammar
-  it "parses the grammar", ->
-    expect(grammar).toBeDefined()
-    expect(grammar.scopeName).toBe "text.md"
+      runs ->
+        grammar = atom.grammars.grammarForScopeName('text.md')

-  for fixture in fixtures
+    # Test the grammar
+    it "parses the grammar", ->
+      expect(grammar).toBeDefined()
+      expect(grammar.scopeName).toBe "text.md"

-    # Try to load the fixture
-    try
-      absolutePath = path.join(__dirname, "fixtures/#{fixture}.ass")
-      fileContents = fs.readFileSync(absolutePath, 'utf8')
-      ass = new ASS(fileContents)
-      tests = ass.getTests()
-    catch error
-      ass = null
-      tests = 0
+    for fixture in fixtures

-    # Test the basics of the fixture
-    it "should load #{absolutePath}", ->
-      expect(ass).not.toEqual(null)
+      # Try to load the fixture
+      try
+        absolutePath = path.join(__dirname, "fixtures/#{fixture}.ass")
+        fileContents = fs.readFileSync(absolutePath, 'utf8')
+        ass = new ASS(fileContents)
+        tests = ass.getTests()
+      catch error
+        ass = null
+        tests = 0

-    it "should define at least one test", ->
-      expect(tests.length > 0).toEqual(true)
+      # Test the basics of the fixture
+      it "should load #{absolutePath}", ->
+        expect(ass).not.toEqual(null)

-    # Everything seems to be okay, let's run the tests
-    describe fixture, ->
-      grammar = null
+      it "should define at least one test", ->
+        expect(tests.length > 0).toEqual(true)

-      beforeEach ->
-        waitsForPromise ->
-          atom.packages.activatePackage('language-markdown')
+      # Everything seems to be okay, let's run the tests
+      describe fixture, ->
+        grammar = null

-        runs ->
-          grammar = atom.grammars.grammarForScopeName('text.md')
+        beforeEach ->
+          waitsForPromise ->
+            atom.packages.activatePackage('language-markdown')

-      # Cycle through the tests we've created in ASS
-      # and we need to do it in a closure apparently
-      _.forEach tests, (test) ->
+          runs ->
+            grammar = atom.grammars.grammarForScopeName('text.md')

-        unless test.isValid
-          xit "should pass test: #{fixture}/#{test.id}", ->
-            return
-        else
+        # Cycle through the tests we've created in ASS
+        # and we need to do it in a closure apparently
+        _.forEach tests, (test) ->

-          it "should pass test: #{fixture}/#{test.id}", ->
-            i = 0
-            tokens = grammar.tokenizeLines(test.input)
-            for line, a in tokens
-              for token, b in line
-                expectation = test.tokens[i]
-                # NOTE
-                # A token.value without a length has been created, and is
-                # ignored. I believe this happens when an optional capture in
-                # the grammar is empty. As far as I can tell, these can be
-                # safely ignored, because you would omit these (unexpected)
-                # tokens when writing manual tests.
-                if tokens[a][b].value.length
-                  expect(tokens[a][b]).toEqual value: expectation.value, scopes: expectation.scopes
-                i++
-            return
+          unless test.isValid
+            xit "should pass test: #{fixture}/#{test.id}", ->
+              return
+          else
+
+            it "should pass test: #{fixture}/#{test.id}", ->
+              i = 0
+              tokens = grammar.tokenizeLines(test.input)
+              for line, a in tokens
+                for token, b in line
+                  expectation = test.tokens[i]
+                  # NOTE
+                  # A token.value without a length has been created, and is
+                  # ignored. I believe this happens when an optional capture in
+                  # the grammar is empty. As far as I can tell, these can be
+                  # safely ignored, because you would omit these (unexpected)
+                  # tokens when writing manual tests.
+                  if tokens[a][b].value.length
+                    expect(tokens[a][b]).toEqual value: expectation.value, scopes: expectation.scopes
+                  i++
+              return


Made call_bytecode line more readable

Made call_bytecode line more readable

--- src/natives/classes/java.net.Inet6AddressImpl.coffee
+++ src/natives/classes/java.net.Inet6AddressImpl.coffee
@@ -11,7 +11,7 @@
   next_host_address[3] = 0 if next_host_address[3] > 255
   next_host_address[2] = 0 if next_host_address[2] > 255
   next_host_address[1] = 0 if next_host_address[1] > 255
-  if next_host_address[0] > 250
+  if next_host_address[] > 250
     error 'Out of addresses'
     next_host_address[0] = 240

@@ -36,7 +36,8 @@

     failure = (e_cb, success_cb, except_cb) -> except_cb(e_cb)

-    rs.call_bytecode cdata, cdata.method_lookup(rs, '<init>(Ljava/lang/String;I)V'), [ hostname, host_allocate_address() ], success, failure
+    cons = cdata.method_lookup(rs, '<init>(Ljava/lang/String;I)V')
+    rs.call_bytecode cdata, cons, [ hostname, host_allocate_address() ], success, failure

   o 'getLocalHostName()Ljava/lang/String;', (rs, _this) ->
     rs.init_string 'localhost'


Now we send library and name correctly

Now we send library and name correctly

--- spec/protocol.coffee
+++ spec/protocol.coffee
@@ -293,7 +293,8 @@
         chai.expect(command, JSON.stringify(payload)).to.equal 'source'
         chai.expect(protocol).to.equal 'component'
         chai.expect(payload).to.include.keys ['name', 'code', 'language']
-        chai.expect(payload.name).to.equal componentName
+        chai.expect(payload.library).to.equal 'foo'
+        chai.expect(payload.name).to.equal 'SetSource'
         chai.expect(payload.language).to.equal 'coffeescript'
         chai.expect(payload.code).to.include "component: 'ProduceFoo'"
         chai.expect(payload.code).to.include "module.exports = ProduceFoo"


new postageapp api

new postageapp api

--- models/invite.coffee
+++ models/invite.coffee
@@ -18,10 +18,16 @@
   if not @sent or force
     util.log "Sending 'teams_new' to '#{@email}'".yellow
     team = @parentArray._parent
-    postageapp.apiCall @email, 'teams_new', null, 'all@nodeknockout.com',
-      team_id: team.id
-      team_name: team.name
-      invite_code: qs.escape @code
+    postageapp.sendMessage
+      recipients: @email,
+      template: 'teams_new'
+      variables:
+        team_id: team.id
+        team_name: team.name
+        invite_code: qs.escape @code
+      , (err, data) ->
+        return console.error(err) if err?
+        console.log(data)
     @sent = yes

 mongoose.model 'Invite', InviteSchema


player now emits stuff

player now emits stuff

--- models/player.coffee
+++ models/player.coffee
@@ -15,7 +15,7 @@
   sendSnap: (word, d_score)->
   	@score += d_score
   	console.log @name, @score
-  	@score
+  	@socket.emit("snap", {player:@name, d_score:d_score})
   	# send stuff


Allow multiple domains for geo endpoints.

--- server_setup.coffee
+++ server_setup.coffee
@@ -128,6 +128,7 @@
   auth.setup()

 setupCountryRedirectMiddleware = (app, country="china", countryCode="CN", languageCode="zh", host="cn.codecombat.com") ->
+  hosts = host.split /;/g
   shouldRedirectToCountryServer = (req) ->
     speaksLanguage = _.any req.acceptedLanguages, (language) -> language.indexOf languageCode isnt -1

@@ -137,7 +138,7 @@

     return unless reqHost.indexOf(config.unsafeContentHostname) is -1

-    unless reqHost.toLowerCase() is host
+    if hosts.indexOf(reqHost.toLowerCase()) is -1
       ip = req.headers['x-forwarded-for'] or req.ip or req.connection.remoteAddress
       ip = ip?.split(/,? /)[0] if ip? # If there are two IP addresses, say because of CloudFlare, we just take the first.
       geo = geoip.lookup(ip)
@@ -151,7 +152,7 @@

   app.use (req, res, next) ->
     if shouldRedirectToCountryServer req
-      res.writeHead 302, "Location": 'http://' + host + req.url
+      res.writeHead 302, "Location": 'http://' + hosts[0] + req.url
       res.end()
     else
       next()


unbreak avatar catching

unbreak avatar catching

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -71,8 +71,9 @@
         [..., createdOn] = filename.split('-')
         # TODO don't check expiry if network connection is not avail
         if Date.now() - parseInt(createdOn) < @expiry
-          callback(null, imagePath)
+          return callback(null, imagePath)
           break
+      callback(null, null)

   avatarGlob: (login) ->
     path.join app.getDataPath(), 'Cache/settings-view', "#{login}-*"
@@ -82,4 +83,5 @@
     imagePath = @avatarPath login
     stream = fs.createWriteStream imagePath
     stream.on 'finish', () -> callback(null, imagePath)
+    # TODO stream.on error
     request("https://github.com/#{login}.png").pipe(stream)


Fix bug in ::request

Fix bug in ::request

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -30,16 +30,17 @@
       if data
         callback(null, data)
       else
-        @request("#{@baseURL}#{packagePath}", callback)
+        @request(packagePath, callback)

   request: (path, callback) ->
-    request path, (err, res, body) =>
+
+    request "#{@baseURL}#{path}", (err, res, body) =>
       data = JSON.parse(body)
       delete data['versions'] if data['versions']
       cached =
         data: data
         createdOn: Date.now()
-      localStorage.setItem(@cacheKeyForPath(packagePath), JSON.stringify(cached))
+      localStorage.setItem(@cacheKeyForPath(path), JSON.stringify(cached))
       callback(err, cached.data) # TODO handle parse error

   cacheKeyForPath: (path) ->


Remove the connect/disconnect event watching since the specific order may vary on runtime implementation details

--- src/WebSocket.coffee
+++ src/WebSocket.coffee
@@ -562,13 +562,6 @@
               time: String
           ,
             protocol: 'network'
-            command: 'connect'
-            payload:
-               id: 'DATA -> IN Hello()'
-               graph: 'bar'
-               tgt: { node: 'Hello', port: 'in' }
-          ,
-            protocol: 'network'
             command: 'data'
             payload:
                id: 'DATA -> IN Hello()'
@@ -577,14 +570,6 @@
                data: 'Hello, world!'
           ,
             protocol: 'network'
-            command: 'connect'
-            payload:
-               id: 'Hello() OUT -> IN World()'
-               graph: 'bar'
-               src: { node: 'Hello', port: 'out' }
-               tgt: { node: 'World', port: 'in' }
-          ,
-            protocol: 'network'
             command: 'data'
             payload:
                id: 'Hello() OUT -> IN World()'
@@ -592,21 +577,6 @@
                src: { node: 'Hello', port: 'out' }
                tgt: { node: 'World', port: 'in' }
                data: 'Hello, world!'
-          ,
-            protocol: 'network'
-            command: 'disconnect'
-            payload:
-               id: 'Hello() OUT -> IN World()'
-               graph: 'bar'
-               src: { node: 'Hello', port: 'out' }
-               tgt: { node: 'World', port: 'in' }
-          ,
-            protocol: 'network'
-            command: 'disconnect'
-            payload:
-               id: 'DATA -> IN Hello()'
-               graph: 'bar'
-               tgt: { node: 'Hello', port: 'in' }
           ]
           receive expects, done, true
           send 'network', 'start',


add Disk quota exceeded error simulation mode

--- src/baby-ftpd.coffee
+++ src/baby-ftpd.coffee
@@ -264,7 +264,10 @@
         @dtpServer.store (storeData)=>
           @fileSystem.setFile storeData, @sessionDir, reqPath, (err)=>
             if err?
-              @reply 550
+              if err.message is "Disk quota exceeded"
+                @reply 552, "Transfer aborted. Disk quota exceeded"
+              else
+                @reply 550
             else
               @reply 250
       catch err
@@ -339,8 +342,9 @@
     "HELP": ()->
       @reply 214, """
         The following commands are recognized
-        USER    PASS    PWD     NLST    LIST    RETR    SYST
-        CWD     CDUP    MKD     QUIT    PASV    NOOP    HELP
+        USER    PASS    CWD     CDUP    QUIT    PASV    STOR
+        RETR    LIST    NLST    DELE    RMD     MKD     PWD
+        SYST    HELP    NOOP
         Direct comments to root
         """

@@ -403,9 +407,13 @@
 class BabyFtpd.FileSystem
   constructor: ()->
     @baseDir = null
+    @quotaSize = 0

   setBase: (dirPath)->
     @baseDir = dirPath
+
+  setQuotaSize: (qSize)->
+    @quotaSize = qSize

   getNewPath: (nowDir, reqPath)->
     if reqPath.indexOf("/") is 0
@@ -429,30 +437,26 @@
       throw new Error "no directory"

   getNlst: (nowDir, reqPath, callback)->
-    try
-      retPath = @getNewPath nowDir, reqPath
-      fs.readdir @baseDir+retPath, callback
-    catch err
-      callback(err)
+    retPath = @getNewPath nowDir, reqPath
+    fs.readdir @baseDir+retPath, callback

   getList: (nowDir, reqPath, callback)->
-    try
-      retPath = @getNewPath nowDir, reqPath
-      exec "export LANG=en_US.UTF-8; ls -l #{@baseDir}#{retPath}", callback
-    catch err
-      callback(err)
+    retPath = @getNewPath nowDir, reqPath
+    exec "export LANG=en_US.UTF-8; ls -l #{@baseDir}#{retPath}", callback

   getFile: (nowDir, reqPath, callback)->
-    try
-      retPath = @getNewPath nowDir, reqPath
-      fs.readFile @baseDir+retPath, callback
-    catch err
-      callback(err)
+    retPath = @getNewPath nowDir, reqPath
+    fs.readFile @baseDir+retPath, callback

   setFile: (storeData, nowDir, reqPath, callback)->
-    winston.log "info", "save #{srorePath}"
-    srorePath = @getNewPath  nowDir, reqPath
-    fs.writeFile @baseDir+srorePath, storeData, "binary", callback
+    if @quotaSize > 0 and storeData.length > @quotaSize
+      # 簡易"Disk quota exceeded"エラーシミュレーター
+      winston.log "info", "Disk quota exceeded"
+      callback(new Error("Disk quota exceeded"))
+    else
+      srorePath = @getNewPath  nowDir, reqPath
+      winston.log "info", "save #{srorePath}"
+      fs.writeFile @baseDir+srorePath, storeData, "binary", callback

   removeFile: (reqPath, callback)->
     fs.unlink @baseDir+reqPath, callback


加入文件读取

加入文件读取

--- src/biz/asset.coffee
+++ src/biz/asset.coffee
@@ -26,6 +26,12 @@
       item.url = "/assets/#{item.project_id}/#{item.file_name}"

     callback(err, result)
+
+Asset.prototype.readFile = (req, res, next)->
+  project_id = req.params.project_id
+  filename = req.params.filename
+  fullpath = _path.join _utility.rootPath, _config.assets, project_id, filename
+  res.sendfile fullpath

 #处理上传文件
 Asset.prototype.uploadFile = (req, res, next)->


bulk: Fixed multiple arguments mode in addRow

--- src/bulk-load.coffee
+++ src/bulk-load.coffee
@@ -52,9 +52,10 @@
   addRow: (row) ->
     if arguments.length > 1 || !row || typeof row != 'object'
       # convert arguments to array in a way the optimizer can handle
-      arr = new Array(arguments.length);
+      arrTemp = new Array(arguments.length);
       for c, i in arguments
-        arr[i] = c
+        arrTemp[i] = c
+      row = arrTemp;

     # write row token
     @rowsData.writeUInt8(TOKEN_TYPE.ROW)


Fixed not first opens

Fixed not first opens

--- src/chocolate.coffee
+++ src/chocolate.coffee
@@ -72,6 +72,8 @@

       pushState()

+      removeClass @current.thumbnail, 'selected'
+
       @current = null

     @
@@ -124,7 +126,6 @@

-
   next: ->
     @select @storage.next @current
     @


Clampable#clampToBounds

Clampable#clampToBounds

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -26,7 +26,7 @@
   ###*
   @name clampToBounds
   @methodOf Clampable#
-  @param {Rectangle} [bounds] The bounds to clamp the object's position within. Defaults to the app siv
+  @param {Rectangle} [bounds] The bounds to clamp the object's position within. Defaults to the app size if none given.
   ###
   clampToBounds: (bounds) ->
     bounds ||= Rectangle x: 0, y: 0, width: App.width, height: App.height


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -1,5 +1,6 @@
 ###*
 The Clampable module provides helper methods to clamp object properties.
+

 @name Clampable
 @module


half working d3DotGrid; problems with data update

--- src/scripts/directives/d3DotGrid.coffee
+++ src/scripts/directives/d3DotGrid.coffee
@@ -19,9 +19,7 @@
 			scope.$on 'resize', (event, container, width, height) ->
 				console.log "drawing"
 				scope.setSpacing(container, width, height)
-				#scope.data = [{x:0,y:0},{x:20,y:20},{x:40,y:40},{x:50,y:50}]
 				draw()
-

 			draw = ->

@@ -31,12 +29,14 @@

 				circles = columns.selectAll("circle")
 				.data((d,i) -> d)
+				.attr("cx", (d, i, j)->j*scope._hspace)
+				.attr("cy", (d, i, j)->i*scope._vspace)

 				circles.enter().append("circle")
 				.attr("r", scope.radius)
 				.attr("class", "origin")
-				.attr("cx", (d)->d.x)
-				.attr("cy", (d)->d.y)
+				.attr("cx", (d, i, j)->j*scope._hspace)
+				.attr("cy", (d, i, j)->i*scope._vspace)

 				circles.exit().remove()

@@ -65,9 +65,9 @@
 					space = Math.min(@_vspace, @_hspace)
 					@_vspace = @_hspace = space

-				@data = ({x:col*@_hspace, y:row*@_vspace} for row in [0..@rows-1] for col in [0..@cols-1] )
+				scope.data = ({x:col*@_hspace, y:row*@_vspace} for row in [0..@rows-1] for col in [0..@cols-1] )
 				console.log @_hspace, @width, @cols
-				console.log @data
+				console.log scope.data

 			###
 			draw = ->


Remove document title from content body

Remove document title from content body

--- src/scripts/models/contents/node.coffee
+++ src/scripts/models/contents/node.coffee
@@ -45,7 +45,7 @@
         # and will instead move all elements in them up one level.
         # Use a regex to extract everything in the body and put it into a div instead.
         $body = $('<div>' + response.content.replace(/^[\s\S]*<body.*?>|<\/body>[\s\S]*$/g, '') + '</div>')
-        $body.children('.title').eq(0).remove()
+        $body.children('[data-type=document-title]').eq(0).remove()
         $body.children('[data-type=abstract]').eq(0).remove()

         response.content = $body.html()


Mengganti judul browser

Mengganti judul browser

--- WebGL-06/coffeescript/webgl.coffee
+++ WebGL-06/coffeescript/webgl.coffee
@@ -7,6 +7,7 @@

 # Entry Point
 r.startWebGL = () ->
+  window.document.title = "WebGL 06: Memuat Model"
   gl = null
   canvas = document.getElementById "webgl-canvas"
   names  = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"]
@@ -195,5 +196,3 @@
         window.alert("ERROR! XMLHTTP:" + request.status)
   request.send()

-
-


Whitespace clean-up.

Whitespace clean-up.

--- src/collision.coffee
+++ src/collision.coffee
@@ -84,7 +84,6 @@
     constructor : () ->
         @logger = new wolf.Logger("wolf.CollisionHandler")

-
     # Check the given elements for collisions, and update them accordingly.
     #
     # @param elements {Array} a list of elements to check for collisions.


Report extension and snippet versions if specified

--- src/connector.coffee
+++ src/connector.coffee
@@ -82,7 +82,11 @@
     @_disconnectionReason = 'handshake-failed'

     # start handshake
-    @_sendCommand { command: 'hello', protocols: [PROTOCOL_6, PROTOCOL_7] }
+    hello = { command: 'hello', protocols: [PROTOCOL_6, PROTOCOL_7] }
+    hello.ext     = @options.ext     if @options.ext
+    hello.extver  = @options.extver  if @options.extver
+    hello.snipver = @options.snipver if @options.snipver
+    @_sendCommand hello
     @_handshakeTimeout.start(@options.handshake_timeout)

   _onclose: (e) ->


Use current user service for news adding

--- www/client/common/controllers/news.coffee
+++ www/client/common/controllers/news.coffee
@@ -1,5 +1,5 @@
 angular.module 'keranbeandco.common'
-.controller 'news', ($scope, News) ->
+.controller 'news', ($scope, News, currentUser) ->

   $scope.isAddNewsFormOpen = null
   $scope.newNews = new News
@@ -29,9 +29,12 @@

   $scope.addNews = () ->
     $scope.newNews.sentAt = new Date()
-    News.create $scope.newNews
-    .$promise
-    .then (res) ->
-      $scope.allNews.unshift res
-      $scope.newNews = new News
-      $scope.toggleAddNewsForm(false)
+    currentUser.fetchCurrentUser (currentUser) ->
+      $scope.newNews.improvedUser = currentUser
+      News.create $scope.newNews
+      .$promise
+      .then (res) ->
+        res.improvedUser = currentUser
+        $scope.allNews.unshift res
+        $scope.newNews = new News
+        $scope.toggleAddNewsForm(false)


Don't pollute console with error stack.

Don't pollute console with error stack.

--- renderer/api/lib/remote.coffee
+++ renderer/api/lib/remote.coffee
@@ -31,8 +31,7 @@
     when 'value' then meta.value
     when 'array' then (metaToValue(el) for el in meta.members)
     when 'error'
-      console.log meta.stack
-      throw new Error(meta.message)
+      throw new Error("#{meta.message}\n#{meta.stack}")
     else
       if meta.type is 'function'
         # A shadow class to represent the remote function object.


Add index and eliminate problem with duplicates

--- src/generator.coffee
+++ src/generator.coffee
@@ -25,24 +25,24 @@
   output = ''
   console.log "Group Size: #{size}" if debug
   for i in [1..size]
-    output += "# Group #{faker.internet.userName()}#{Math.random()*10|0}\n"
-    endpointSize = Math.random()*10|0
-    output += createEndpointWithResponse(endpointSize)
+    output += "# Group #{faker.name.findName()}#{i}\n"
+    endpointSize = Math.random()*10|1
+    output += createEndpointWithResponse(endpointSize, i)
   output

-createEndpointWithResponse = (size) ->
+createEndpointWithResponse = (size, index) ->
   output = ''
   for i in [1..size]
-    output += "#{createEndpoint()}"
-    output += "#{createResponse()}"
+    output += "#{createEndpoint(index)}"
+    output += "#{createResponse(index)}"
   output

-createEndpoint = ->
-  "## #{faker.address.country()}#{Math.random()*10|0} [/#{faker.internet.userName().toLowerCase()}]\n"
+createEndpoint = (index) ->
+  "## #{faker.address.country()}#{index} [/#{faker.name.firstName().toLowerCase()}#{index}]\n"

-createResponse = (code = 200, method = 'GET') ->
+createResponse = (index, code = 200, method = 'GET') ->
   """
-  ### #{faker.internet.domainWord()}#{Math.random()*10|0} [#{method}]
+  ### #{faker.internet.domainWord()}#{index} [#{method}]
   + Response #{code} (application/json)

   #{createJsonBody()}


исправлен баг в команде rollback

исправлен баг в команде rollback

--- lib/nbuild/builder.coffee
+++ lib/nbuild/builder.coffee
@@ -345,7 +345,7 @@
   * @param name    {String}
   * @param options {Object}
   ###
-
+
   _rollback: (name, options) ->
     rollback = @state[options['step-name']].rollback
     unless rollback?
@@ -358,7 +358,7 @@
       else if entry.command is 'rmdir'
         try
           fs.rmdirSync(entry.path) if existsSync(entry.path)
-          console.log "rmdir #{entry.path}" if builder.verbose
+          console.log "rmdir #{entry.path}" if @verbose
         catch err
           console.warn "Warning: can't delete dir #{entry.path}".yellow


Issue #14: add the env function to the Atom plugin

--- tools/atom-plugin/language-micro/grammars/micro.cson
+++ tools/atom-plugin/language-micro/grammars/micro.cson
@@ -47,7 +47,7 @@
 	}
 	{
 		'comment': 'Built-in functions (it should be described before functions)'
-		'match': '\\b(nil|false|true|num|type|arity|floor|ceil|trunc|round|sin|cos|tn|arcsin|arccos|arctn|angle|pow|sqrt|exp|ln|lg|abs|random|head|tail|if|str|strb|strl|in|out|outln|err|errln|exit)\\b'
+		'match': '\\b(nil|false|true|num|type|arity|floor|ceil|trunc|round|sin|cos|tn|arcsin|arccos|arctn|angle|pow|sqrt|exp|ln|lg|abs|random|head|tail|if|str|strb|strl|env|in|out|outln|err|errln|exit)\\b'
 		'name': 'variable.function.micro'
 	}
 	{


Handle missing header cases in response cache

--- lib/response-cache.coffee
+++ lib/response-cache.coffee
@@ -115,10 +115,10 @@
   pragma = parseHeader getHeader 'pragma'

   # Any no-cache directives prevent any operations
-  if cacheControl['no-cache'] or pragma['no-cache']
+  if cacheControl?['no-cache'] or pragma?['no-cache']
     return

-  expireTime = if cacheControl['max-age']
+  expireTime = if cacheControl?['max-age']
       baseTime = parseDate(getHeader('date')) ? baseTime ? new Date()
       new Date baseTime.getTime() + parseInt(cacheControl['max-age'])*1000
     else
@@ -133,12 +133,16 @@

 parseHeader = (value) ->
   components = getHeaderComponents value
+  if not components then return
+
   ret = {}
   for component in components ? []
     kv = component.split '='
     ret[kv[0].toLowerCase()] = kv[1] or true
   ret
 parseETag = (value) ->
+  if not value then return
+
   re = /(\*|(?:W\/)?\".*?\")\s*,?\s*/g
   value = value?.replace(/^\s*|\s*$/g, '')
   ret = while match = re.exec value


:shit:

:shit:

--- benchmark/benchmark-suite.coffee
+++ benchmark/benchmark-suite.coffee
@@ -49,7 +49,7 @@

     describe "after-opening.", ->
       beforeEach ->
-        editor.edit rootView.project.open('fixtures/huge.js')
+        editor.edit rootView.project.open('huge.js')

       benchmark "moving-to-eof.", 1, ->
         editor.moveCursorToBottom()


Not "WORKS" but "WORK"

Not "WORKS" but "WORK"

--- src/data.coffee
+++ src/data.coffee
@@ -3,7 +3,7 @@
   menu: [
     {name: 'ABOUT', path: '/about/'},
     {name: 'MEMBERS', path: '/members/'},
-    {name: 'WORKS', path: '/category/works/'},
+    {name: 'WORK', path: '/category/work/'},
     {name: 'NEWS', path: '/category/news/'},
   ]


Better code.

Better code.

--- src/game.coffee
+++ src/game.coffee
@@ -63,7 +63,7 @@

     if nbMinesAround is 0
       for [x, y] in game.neighborCoord @x, @y
-        game.board.cases[game.positionFromCoord(x, y)].showTile()
+        game.showTile x, y

   countMines: () ->
@@ -105,7 +105,7 @@
     return if game.state is States.GameOver
     if @countFlags() is @countMines()
       for [x, y] in game.neighborCoord @x, @y
-        game.board.cases[game.positionFromCoord(x, y)].showTile()
+        game.showTile x, y

 class @Game
@@ -154,6 +154,10 @@
         rnd = @rnd(0, (@board.nbHorizontalTiles * @board.nbVerticalTiles) - 1)
         break if not @around(rnd, x, y)
       @data[rnd] = @entities.Mine
+
+
+  showTile: (x, y) ->
+    @board.cases[@positionFromCoord(x, y)].showTile()

   coordFromPosition: (position) ->


don't fail silently on connection errors

--- src/kite/kite.coffee
+++ src/kite/kite.coffee
@@ -11,6 +11,9 @@

   # ready states:
   [ NOTREADY, READY, CLOSED ] = [0,1,3]
+
+  # error states:
+  [ OKAY, ERROR ] = [0,1]

   uniqueId = "#{ Math.random() }"

@@ -69,6 +72,11 @@
     # enable below to autoReconnect when the socket has been closed
     if @autoReconnect
       process.nextTick => @setBackoffTimeout @bound "connect"
+
+    if @errState is ERROR
+      @emit 'error', "Websocket error!"
+      @errState = OKAY
+
     @emit 'info', "#{ @options.url }: disconnected, trying to reconnect..."
     return

@@ -78,12 +86,13 @@
     @proto.handle req  if req?
     return

-  onError: ({ data }) ->
-    @emit 'info', "#{ @options.url } error: #{ data }"
+  onError: (err) ->
+    @errState = ERROR
+    @emit 'info', "#{ @options.url } error: #{ err.data }"
     return

   wrapMessage: (method, params, callback) ->
-    authentication    : @authentication
+    authentication    : @options.authentication
     withArgs          : params
     responseCallback  : (response) ->
       { withArgs:[{ error: err, result }]} = response


Improve comments for login_bar module

Improve comments for login_bar module

--- src/login_bar.coffee
+++ src/login_bar.coffee
@@ -1,9 +1,8 @@
-# We define a jMod module called 'login_bar'.
-# The module is a jQuery plugin to define an interactive login bar.
-# The plugin will show a button to either log in or log out
-# and will indicate who is currently logged in.
-# The plugin will respond to click events from the user,
-# and will subscribe to custom login and logout events.
+# We define a custom jQuery plugin to define an interactive login bar.  The
+# plugin will show a button to either log in or log out and will indicate who
+# is currently logged in.  The plugin will respond to click events from the
+# user, and will subscribe to custom login and logout events.
+
 define [
 	'jquery'
 	'jqm'


Add jade helper to uppercase first character.

--- src/jade.coffee
+++ src/jade.coffee
@@ -39,6 +39,8 @@
                 start_time, end_time, 'small', has_end_time=false
             )
             return formatted.date
+        uppercase_first: (val) ->
+            val.charAt(0).toUpperCase() + val.slice 1

         mixin_helpers: (data) ->
             set_helper data, 't', i18n.t
@@ -47,6 +49,7 @@
             set_helper data, 'phone_i18n', @phone_i18n
             set_helper data, 'static_path', @static_path
             set_helper data, 'human_date', @human_date
+            set_helper data, 'uppercase_first', @uppercase_first
             data

         template: (name, locals) ->


Bug fix: elem not wrapped in jQuery object

--- src/main/bind.coffee
+++ src/main/bind.coffee
@@ -113,7 +113,8 @@
   bindRedirect: (expt, variant, dataAttr, elem) =>
     Myna.log("Myna.Binder.bindRedirect", expt, dataAttr, elem)

-    attr = elem.data(dataAttr)
+    self = Myna.$(elem)
+    attr = self.data(dataAttr)

     dest =
       switch attr


catch exceptions fixed some debugging logs

--- src/poll.coffee
+++ src/poll.coffee
@@ -53,26 +53,35 @@
 			msg.send error
 			@robot.logger.info error

-	robot.respond /vote\s+(\d)/i, (msg) ->
-		vote = msg.match[1]
-		user = msg.envelope.user['name']
-		@robot.logger.info "Adding a vote for #{vote} for #{user}"
-		#check if the user already voted
-		for option in @robot.brain.data.poll.options
-			@robot.logger.info "Checking #{option}"
-			if option.users != undefined
-				@robot.logger.info "Some have voted for this one"
-				option.users = option.users.filter (currentUser) -> currentUser isnt user
-		if @robot.brain.data.poll.options[vote].users.length > 0
-			@robot.logger.info "add user to this option since it's defined"
-			@robot.brain.data.poll.options[vote].users.push(user)
-		else
-			@robot.logger.info "First vote, create new array"
-			@robot.brain.data.poll.options[vote].users = []
-			@robot.brain.data.poll.options[vote].users.push(user)
-		@robot.brain.save()
-		@robot.logger.info "Brain saved"
-		msg.reply "Thanks for your vote"
+	robot.respond /vote\s+(\d)/i, (msg) ->
+		try
+			vote = msg.match[1]
+			user = msg.envelope.user['name']
+			options = @robot.brain.data.poll.options
+			if vote >= options.length
+				@robot.logger.info "Invalid vote. #{vote} >= #{options.length}"
+				msg.reply "Please vote for a valid option"
+				return
+			#check if the user already voted
+			@robot.logger.info "Adding a vote for #{vote} for #{user}"
+			for option in options
+				@robot.logger.info "Checking #{option.text}"
+				if option.users != undefined
+					@robot.logger.info "Some have voted for this one"
+					option.users = option.users.filter (currentUser) -> currentUser isnt user
+			@robot.logger.info "Finishes removing user"
+			if @robot.brain.data.poll.options[vote].users != undefined
+				@robot.logger.info "add user to this option since it's defined"
+				@robot.brain.data.poll.options[vote].users.push(user)
+			else
+				@robot.logger.info "First vote, create new array"
+				@robot.brain.data.poll.options[vote].users = []
+				@robot.brain.data.poll.options[vote].users.push(user)
+			@robot.brain.save()
+			@robot.logger.info "Brain saved"
+			msg.reply "Thanks for your vote"
+		catch error
+			@robot.logger.info error

 	robot.respond /view results/i, (msg) ->
 		@robot.logger.info "View results"


Listening to target_temp_changed events and displaying the target temperatures in the header.

--- src/tegh.coffee
+++ src/tegh.coffee
@@ -96,10 +96,12 @@
 class Tegh
   commands: require './help'
   _jobProgress: 0
+  _sensorMap: {e: 'extruder', e0: 'extruder', b: 'bed'}

   constructor: ->
     new ServiceSelector().on "select", @_onServiceSelect
     @_sensors = {}
+    @_targetTemp = {}

   _onServiceSelect: (service) =>
     clear()
@@ -108,6 +110,7 @@
     @client = new ConstructClient(service.address, port)
       .on("connect", @_onConnect)
       .on("sensor_changed", @_onSensorChanged)
+      .on("target_temp_changed", @_onTargetTempChanged)
       .on("job_progress_changed", @_onJobChanged)
       .on("jobs", @_onJobsList)
       .on("job_upload_complete", @_onJobUploadComplete)
@@ -116,6 +119,10 @@

   _onConnect: =>
     @cli = new CliConsole(@)
+
+  _onTargetTempChanged: (data) =>
+    @_targetTemp[@_sensorMap[k]] = v for k, v of data
+    @cli.render()

   _onSensorChanged: (data) =>
     @_sensors[data.name] = data.value
@@ -138,26 +145,27 @@
       id = job.id.toString()
       prefix = "  #{i}) #{name} "
       suffix = "job ##{job.id.pad(5)}  "
-      console.log "#{prefix.padRight(".", @cli.width - suffix.length - prefix.length - 1)} #{suffix}"
+      padding = @cli.width - suffix.length - prefix.length - 1
+      console.log "#{prefix.padRight(".", padding)} #{suffix}"
     console.log ""
     @cli.rl.prompt()
     @cli.rl.resume()

   _onJobUploadComplete: (statusCode) =>
-      @cli.rl.pause()
-      if statusCode = 200
-        status = "Job added."
-      else
-        status = "Error adding job."
-      process.stdout.write("\r" + status + "\n")
-      @cli.rl.prompt()
-      @cli.render()
-      @cli.rl.resume()
+    @cli.rl.pause()
+    if statusCode = 200
+      status = "Job added."
+    else
+      status = "Error adding job."
+    process.stdout.write("\r" + status + "\n")
+    @cli.rl.prompt()
+    @cli.render()
+    @cli.rl.resume()

   _lHeader: ->
     fields = []
     for k, v of @_sensors
-      fields.push "#{k.capitalize()}: #{v}\u00B0C"
+      fields.push "#{k.capitalize()}: #{v} / #{@_targetTemp[k]||0}\u00B0C"
     fields.join("  ")

   _rHeader: ->


Added a new feature that shows all the elements with UI attributes with a highlight text by pressing the combination: Alt+Shift+U. It is also possible to copy the text by just clicking the highlight. See #62176

--- src/base/CUI.coffee
+++ src/base/CUI.coffee
@@ -48,9 +48,14 @@
 			type: "keyup"
 			node: window
 			capture: true
-			call: (ev) ->
+			call: (ev) =>
 				if ev.getKeyboard() == "C+U+I"
 					CUI.toaster(text: "CUI!")
+
+				if CUI.defaults.debug
+					if ev.getKeyboard() == "Alt+Shift+U"
+						@__toggleUIElements()
+				return

 		CUI.Events.listen
 			type: "keydown"
@@ -888,6 +893,56 @@
 		map
 	)()

+	@__toggleUIElements: ->
+		if @__uiHighlightDivs?.length > 0
+			for highlightDiv in @__uiHighlightDivs
+				CUI.dom.remove(highlightDiv)
+			delete @__uiHighlightDivs
+			return
+
+		@__uiHighlightDivs = []
+		uiElements = document.querySelectorAll("[ui]")
+		for uiElement in uiElements
+			div = CUI.dom.div()
+			@__uiHighlightDivs.push(div)
+			do(div, uiElement) =>
+				div.textContent = uiElement.getAttribute("ui")
+				CUI.dom.setStyle(div,
+					padding: "4px"
+					background: "yellow"
+					position: "absolute"
+					"white-space": "nowrap"
+					cursor: "pointer"
+				)
+				CUI.dom.append(document.body, div)
+
+				divRect = div.getBoundingClientRect();
+				uiElementRect = uiElement.getBoundingClientRect();
+
+				borderStyle = CUI.dom.getStyle(uiElement).border
+				div.addEventListener('mouseenter', =>
+					CUI.dom.setStyle(uiElement, {border: "2px solid yellow"})
+				)
+				div.addEventListener('mouseleave', =>
+					CUI.dom.setStyle(uiElement, {border: borderStyle})
+				)
+				div.addEventListener('click', =>
+					navigator.clipboard.writeText(div.textContent)
+				)
+
+				top = uiElementRect.top + uiElementRect.height / 2 - divRect.height / 2
+
+				left = uiElementRect.left - divRect.width
+				if left <= divRect.width
+					left = uiElementRect.left + uiElementRect.width
+
+				CUI.dom.setStyle(div,
+					top: top
+					left: left
+				)
+				CUI.dom.setStyle(div, "zIndex": 5, "")
+		return
+
 CUI.ready =>

 	for k of CUI.browser


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -75,7 +75,7 @@
       [event, namespace] = namespacedEvent.split(".")

       if event
-        eventCallbacks[event] = eventCallbacks[event] || []
+        eventCallbacks[event] ||= []

         if callback
           eventCallbacks[event].remove(callback)


bind => on

bind => on

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -25,8 +25,8 @@
   bind: (args...) ->
     self.on(args...)

-  unbind: ->
-    self.off(arguments)
+  unbind: () ->
+    self.off(args...)

   ###*
   Adds a function as an event listener.


Update Rivets.Binding to use the new keypath observers. [#233]

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -10,7 +10,6 @@
     @formatters = @options.formatters || []
     @dependencies = []
     @setBinder()
-    @setObserver()

   # Sets the binder to use when binding and syncing.
   setBinder: =>
@@ -29,14 +28,6 @@
   # Sets a keypath observer that will notify this binding when any intermediary
   # keys are changed.
   setObserver: =>
-    @observer = new Rivets.KeypathObserver @view, @view.models, @keypath, (obs) =>
-      @unbind true if @key
-      @model = obs.target
-      @bind true if @key
-      @sync()
-
-    @key = @observer.key
-    @model = @observer.target

   # Applies all the current formatters to the supplied value and returns the
   # formatted value.
@@ -62,7 +53,7 @@
   # with the suplied value formatted.
   set: (value) =>
     value = if value instanceof Function and !@binder.function
-      @formattedValue value.call @model
+      @formattedValue value.call @observer.target
     else
       @formattedValue value

@@ -70,10 +61,7 @@

   # Syncs up the view binding with the model.
   sync: =>
-    @set if @key
-      @view.adapters[@key.interface].read @model, @key.path
-    else
-      @model
+    @set @observer.value()

   # Publishes the value currently set on the input element back to the model.
   publish: =>
@@ -86,42 +74,29 @@
       if @view.formatters[id]?.publish
         value = @view.formatters[id].publish value, args...

-    @view.adapters[@key.interface].publish @model, @key.path, value
+    @observer.setValue value

   # Subscribes to the model for changes at the specified keypath. Bi-directional
   # routines will also listen for changes on the element to propagate them back
   # to the model.
-  bind: (silent = false) =>
-    @binder.bind?.call @, @el unless silent
-    @view.adapters[@key.interface].subscribe(@model, @key.path, @sync) if @key
-    @sync() if @view.config.preloadData unless silent
+  bind: =>
+    @binder.bind?.call @, @el
+    @observer = new Rivets.Observer @view, @view.models, @keypath, @sync

-    if @options.dependencies?.length
+    if @observer.target? and @options.dependencies?.length
       for dependency in @options.dependencies
-        observer = new Rivets.KeypathObserver @view, @model, dependency, (obs, prev) =>
-          key = obs.key
-          @view.adapters[key.interface].unsubscribe prev, key.path, @sync
-          @view.adapters[key.interface].subscribe obs.target, key.path, @sync
-          @sync()
-
-        key = observer.key
-        @view.adapters[key.interface].subscribe observer.target, key.path, @sync
+        observer = new Rivets.Observer @view, @observer.target, dependency, @sync
         @dependencies.push observer

+    @sync() if @view.config.preloadData
+
   # Unsubscribes from the model and the element.
-  unbind: (silent = false) =>
-    unless silent
-      @binder.unbind?.call @, @el
-      @observer.unobserve()
+  unbind: =>
+    @binder.unbind?.call @, @el
+    @observer.unobserve()

-    @view.adapters[@key.interface].unsubscribe(@model, @key.path, @sync) if @key
-
-    if @dependencies.length
-      for obs in @dependencies
-        key = obs.key
-        @view.adapters[key.interface].unsubscribe obs.target, key.path, @sync
-
-      @dependencies = []
+    observer.unobserver() for observer in @dependencies
+    @dependencies = []

   # Updates the binding's model from what is currently set on the view. Unbinds
   # the old model first and then re-binds with the new model.


Make class easily mutable.

Make class easily mutable.

--- src/view.coffee
+++ src/view.coffee
@@ -73,10 +73,14 @@
     value = @computed[name].apply @, args

   _mutateDom: (selector, attr, value) ->
-    if attr == 'text'
-      @_targets[selector].text value
-    else
-      @_targets[selector].attr attr, value
+    switch attr
+      when 'text'
+        @_targets[selector].text value
+      when 'class'
+        @_targets[selector].removeClass()
+        @_targets[selector].addClass value
+      else
+        @_targets[selector].attr attr, value
     return

   # This translates a state change to it's intended target(s).


opt: long stack trace

opt: long stack trace

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -70,7 +70,7 @@
 	 * ```
 	###
 	catch: (onRejected) ->
-		@then undefined, onRejected
+		@then $nil, onRejected

 	###*
 	 * The `Promise.resolve(value)` method returns a Promise object that is resolved with the given value.
@@ -211,44 +211,8 @@
 	@onUnhandledRejection: (reason, p) ->
 		return if not isObject console

-		stackInfo = []
-
-		trim = (str) -> str.replace /^\s+|\s+$/g
-
-		if isLongStackTrace and p[$promiseTrace]
-			push = (trace) ->
-				stackInfo.push trim trace
-
-			if p[$settlerTrace]
-				push p[$settlerTrace]
-
-			# Hope you guys could understand how the back trace works.
-			# We only have to iter through the tree from the bottom to root.
-			iter = (node) ->
-				return if not node
-				iter node._next
-				push node[$promiseTrace]
-				iter node._pre
-
-			iter p
-
-		stackStr = '\n' + stackInfo.join('\n')
-
-		clean = (stack) ->
-			if typeof __filename == 'string'
-				stack.replace ///.+#{__filename}.+\n?///g, ''
-
-		console.error 'Unhandled Rejection:', (
-			if reason
-				if reason.stack
-					clean trim reason.stack
-				else
-					reason
-			else
-				reason
-		), clean(stackStr)
-
-		return
+		info = genStackInfo reason, p
+		console.error 'Unhandled Rejection:', info[0], info[1]

 	isLongStackTrace = false

@@ -277,6 +241,7 @@
 	$tryCatchFn = null
 	$tryErr = { e: null }
 	$noop = {}
+	$nil = undefined

 	isObject = (obj) ->
 		typeof obj == 'object'
@@ -291,7 +256,7 @@
 	 * @param  {String | Number} key
 	###
 	release = (obj, key) ->
-		obj[key] = undefined
+		obj[key] = $nil
 		return

 	###*
@@ -418,7 +383,7 @@
 	genTraceInfo = (noTitle) ->
 		(new Error).stack
 			.replace 'Error',
-				(if noTitle then '' else 'From previous event:')
+				(if noTitle then '' else $fromPrevious)

 	# ************************** Promise Constant **************************

@@ -437,6 +402,8 @@
 	$circularChain = 'promise_circular_chain'

 	$invalid_argument = 'invalid_argument'
+
+	$fromPrevious = 'From previous event:'

 	# Default state
 	_state: $pending
@@ -513,7 +480,7 @@

 		# 2.2.7.3
 		# 2.2.7.4
-		if handler == undefined
+		if handler == $nil
 			settlePromise p2, p1._state, p1._value
 			return

@@ -545,6 +512,47 @@

 		return

+	genStackInfo = (reason, p) ->
+		stackInfo = []
+
+		trim = (str) -> str.replace /^\s+|\s+$/g
+
+		if isLongStackTrace and p[$promiseTrace]
+			push = (trace) ->
+				stackInfo.push trim trace
+
+			if p[$settlerTrace]
+				push p[$settlerTrace]
+
+			# Hope you guys could understand how the back trace works.
+			# We only have to iter through the tree from the bottom to root.
+			iter = (node) ->
+				return if not node
+				iter node._next
+				push node[$promiseTrace]
+				iter node._pre
+
+			iter p
+
+		stackStr = '\n' + stackInfo.join('\n')
+
+		clean = (stack, cleanPrev) ->
+			if cleanPrev
+				stack = stack.slice 0, stack.indexOf('\n' + $fromPrevious)
+
+			if typeof __filename == 'string'
+				stack.replace ///.+#{__filename}.+\n?///g, ''
+
+		return [(
+			if reason
+				if reason.stack
+					clean (trim reason.stack), true
+				else
+					reason
+			else
+				reason
+		), clean(stackStr)]
+
 	callHanler = (handler, value) ->
 		# 2.2.5
 		handler value
@@ -565,9 +573,13 @@
 		p._state = state
 		p._value = value

-		if state == $rejected and
-		(not p._pre or p._pre._state == $resolved)
-			scheduleUnhandledRejection p
+		if state == $rejected
+			if isLongStackTrace and value and value.stack
+				stack = genStackInfo value, p
+				value.stack = stack[0] + stack[1]
+
+			if not p._pre or p._pre._state == $resolved
+				scheduleUnhandledRejection p

 		i = 0
 		len = p._pCount


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -23,7 +23,7 @@

 player.I.update()

-player.I.shootTimer # =>
+player.I.shootTimer # => 9
 </pre></code>

 @name Cooldown


Remove videoClickTrackingURl for nonLinear and Companion creatives

--- src/creative.coffee
+++ src/creative.coffee
@@ -20,13 +20,11 @@
         super
         @type = "nonlinear"
         @variations = []
-        @videoClickTrackingURLTemplates = []

 class VASTCreativeCompanion extends VASTCreative
     constructor: ->
         @type = "companion"
         @variations = []
-        @videoClickTrackingURLTemplates = []

 module.exports =
     VASTCreativeLinear: VASTCreativeLinear


ReferralSystem: mark referrals as coming soon

--- client/Account/views/referralsystem.coffee
+++ client/Account/views/referralsystem.coffee
@@ -22,11 +22,11 @@
     @addHeader()
     @loadItems()

-  showRedeemReferralPointModal:->
-    KD.mixpanel "Referer Redeem Point modal, click"
+  # showRedeemReferralPointModal:->
+  #   KD.mixpanel "Referer Redeem Point modal, click"

-    appManager = KD.getSingleton "appManager"
-    appManager.tell "Account", "showRedeemReferralPointModal"
+  #   appManager = KD.getSingleton "appManager"
+  #   appManager.tell "Account", "showRedeemReferralPointModal"

   addHeader:->
@@ -51,7 +51,9 @@
         cssClass  : 'add-big-btn'
         title     : 'Redeem your VM space'
         icon      : yes
-        callback  : => @showRedeemReferralPointModal()
+        callback  : =>
+          new KDNotificationView title: "Coming soon!"
+          # @showRedeemReferralPointModal()

 class AccountReferralSystemList extends KDListView


add link to label tracking

add link to label tracking

--- client/src/common/states/controller.coffee
+++ client/src/common/states/controller.coffee
@@ -69,6 +69,12 @@
       title: 'Problem Tracking'
       icon: 'bell-ring'
     ,
+      state: 'tab.labels'
+      params:
+        projectId: $scope.project?.id
+      title: 'Labels Tracking'
+      icon: 'tag-multiple'
+    ,
     ]
   ,
     title: 'Current Sprint'


More JS tests for Tetronimo

More JS tests for Tetronimo

--- client/test/spec/services/tetromino.coffee
+++ client/test/spec/services/tetromino.coffee
@@ -27,3 +27,45 @@
       expect(Tetromino.blocks.length).toEqual before_count + 1
       expect(Tetromino.blocks[before_count].locked).toEqual true
       expect(Tetromino.blocks[before_count].active).toEqual false
+
+  describe 'setup_cascade', ->
+    it 'empties list of blocks', ->
+      Tetromino.blocks = ['whee', 'test', 'okay']
+      Tetromino.setup_cascade()
+      expect(Tetromino.blocks.length).toEqual 0
+
+  describe 'get_active_block', ->
+    active_block = {}
+    other_block = {}
+
+    beforeEach ->
+      active_block = new Block({color: 'magenta', x: 0, y: 2})
+      other_block = new Block({color: 'green', x: 3, y: 1, active: false, locked: true})
+      Tetromino.blocks = [other_block, active_block]
+
+    it 'returns active block', ->
+      expect(Tetromino.get_active_block()).toEqual active_block
+
+  describe 'get_middle_column_blocks', ->
+    beforeEach ->
+      Tetromino.blocks.push new Block({color: 'blue', x: 0, y: 2})
+      Tetromino.blocks.push new Block({color: 'blue', x: 1, y: 2})
+      Tetromino.blocks.push new Block({color: 'blue', x: 1, y: 3})
+      Tetromino.blocks.push new Block({color: 'blue', x: 4, y: 0})
+
+    it 'returns only blocks in middle column', ->
+      actual = Tetromino.get_middle_column_blocks()
+      expect(actual.length).toEqual 2
+      expect((b.y for b in actual)).toEqual [2, 2]
+
+  describe 'is_block_directly_below', ->
+    it 'returns truish when block is directly below given coords', ->
+      Tetromino.blocks.push new Block({color: 'blue', x: 1, y: 4})
+      expect(Tetromino.is_block_directly_below(0, 4)).toBeTruthy()
+
+    it 'returns falsish when no block is below given coords', ->
+      Tetromino.blocks.push new Block({color: 'blue', x: 1, y: 4})
+      expect(Tetromino.is_block_directly_below(0, 3)).toBeFalsy()
+
+    it 'returns falsish when there are no blocks', ->
+      expect(Tetromino.is_block_directly_below(1, 1)).toBeFalsy()


naming yo

naming yo

--- js/src/crypto/RsaKeyGenerator.coffee
+++ js/src/crypto/RsaKeyGenerator.coffee
@@ -68,14 +68,14 @@

     numToUnsignedUint8Array: (num) ->
       hexString = num.toString(2)
-      array = new Uint8Array(hexString.length)
+      binary    = new Uint8Array(hexString.length)
       for i in [0..hexString.length]
-        array[i] = hexString[i]
-      arr2size = Math.ceil(array.length/8)
-      array2 = new Uint8Array(arr2size)
-      for i in [0..arr2size]
-        uint8 = array.slice(i*8, i*8 + 7)
-        array2[i] = Number.parseInt(uint8, 2)
-      return array2
+        binary[i] = hexString[i]
+      uLength     = Math.ceil(binary.length/8)
+      unsignedInt = new Uint8Array(uLength)
+      for i in [0..uLength]
+        uint8          = binary.slice(i*8, i*8 + 7)
+        unsignedInt[i] = Number.parseInt(uint8, 2)
+      return unsignedInt

   return RsaKeyGenerator


force POST request to add propositions

force POST request to add propositions

--- app/assets/javascripts/backoffice/views/election/propositions_candidacy_tag_view.js.coffee
+++ app/assets/javascripts/backoffice/views/election/propositions_candidacy_tag_view.js.coffee
@@ -44,4 +44,4 @@
     params = text: event.target.text.value
     params['tagIds'] = $(event.target).parent().data().tagId
     params['candidacyId'] = @candidacy.id
-    @propositions.create params, url: '/api/v1/propositions'
+    @propositions.create params, url: '/api/v1/propositions', type: 'POST'


fix character lengths problem for subdomains

--- client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -211,7 +211,7 @@
           @showSuccess domain

     else # create a subdomain
-      subDomainPattern = /^[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]$/
+      subDomainPattern = /^[a-zA-Z0-9][a-zA-Z0-9.-]*$/
       unless subDomainPattern.test domainName
         createButton.hideLoader()
         return notifyUser "#{domainName} is an invalid subdomain."


Moving block type definitions where they belong

--- src/js/Block.coffee
+++ src/js/Block.coffee
@@ -1,73 +1,10 @@
-# All the types of blocks we support
-Types =
-  'blank': ->
+class Block
+  constructor: (type, rotation) ->
+    type ||= 'blank'

-  'double-straight': ->
-    top:            'straight'
-    topRotation:            90
-    middle:         'straight'
-    middleRotation:         90
-  'curve-straight': ->
-    top:               'curve'
-    topRotation:             0
-    middle:         'straight'
-    middleRotation:         90
-  'curve-straight-alt': ->
-    top:               'curve'
-    topRotation:           270
-    middle:         'straight'
-    middleRotation:         90
-  'curve-exchange': ->
-    top:               'curve'
-    topRotation:             0
-    middle:         'straight'
-    middleRotation:        270
-  'curve-exchange-alt': ->
-    top:               'curve'
-    topRotation:           270
-    middle:     'exchange-alt'
-    middleRotation:          0
-  'straight-exchange': ->
-    top:            'straight'
-    topRotation:            90
-    middle:         'exchange'
-    middleRotation:          0
-  'straight-exchange-alt': ->
-    top:            'straight'
-    topRotation:            90
-    middle:     'exchange-alt'
-    middleRotation:          0
-  'curve-dive': ->
-    top:               'curve'
-    topRotation:             0
-    middle:             'dive'
-    middleRotation:         90
-  'curve-dive-alt': ->
-    top:               'curve'
-    topRotation:            90
-    middle:             'dive'
-    middleRotation:         90
-  'crossing-straight': ->
-    top:            'crossing'
-    topRotation:             0
-    middle:         'straight'
-    middleRotation:          0
-  'crossing-hole': ->
-    top:       'crossing-hole'
-    topRotation:             0
-    middle:       'drop-middle'
-    middleRotation:         90
-  'crossing-hole': ->
-    top:       'crossing-hole'
-    topRotation:             0
-    middle:         'drop-low'
-    middleRotation:         90
-
-class Block
-  constructor: (@type, rotation) ->
-    @properties = (Types[@type] || Types['blank'])()
-
-    throw new Error "Unknown block type #{@type}" unless @properties
+    @properties = {}
+    for key, value of Block.Types[type]
+      @properties[key] = value

     switch rotation
       when 90
@@ -88,3 +25,68 @@
       # -90 % 360 in JavaScript returns -90, hence we go the other way ’round
       @properties.topRotation    = (@properties.topRotation    + 270) % 360
       @properties.middleRotation = (@properties.middleRotation + 270) % 360
+
+  # All the types of blocks we support
+  @Types:
+    'blank':
+      {}
+    'double-straight':
+      top:            'straight'
+      topRotation:            90
+      middle:         'straight'
+      middleRotation:         90
+    'curve-straight':
+      top:               'curve'
+      topRotation:             0
+      middle:         'straight'
+      middleRotation:         90
+    'curve-straight-alt':
+      top:               'curve'
+      topRotation:           270
+      middle:         'straight'
+      middleRotation:         90
+    'curve-exchange':
+      top:               'curve'
+      topRotation:             0
+      middle:         'straight'
+      middleRotation:        270
+    'curve-exchange-alt':
+      top:               'curve'
+      topRotation:           270
+      middle:     'exchange-alt'
+      middleRotation:          0
+    'straight-exchange':
+      top:            'straight'
+      topRotation:            90
+      middle:         'exchange'
+      middleRotation:          0
+    'straight-exchange-alt':
+      top:            'straight'
+      topRotation:            90
+      middle:     'exchange-alt'
+      middleRotation:          0
+    'curve-dive':
+      top:               'curve'
+      topRotation:             0
+      middle:             'dive'
+      middleRotation:         90
+    'curve-dive-alt':
+      top:               'curve'
+      topRotation:            90
+      middle:             'dive'
+      middleRotation:         90
+    'crossing-straight':
+      top:            'crossing'
+      topRotation:             0
+      middle:         'straight'
+      middleRotation:          0
+    'crossing-hole':
+      top:       'crossing-hole'
+      topRotation:             0
+      middle:       'drop-middle'
+      middleRotation:         90
+    'crossing-hole':
+      top:       'crossing-hole'
+      topRotation:             0
+      middle:         'drop-low'
+      middleRotation:         90


Force class initialization before statically inspecting class' name.

--- src/rehydrate.coffee
+++ src/rehydrate.coffee
@@ -107,6 +107,8 @@
 its properties' names to their full mixed-case property names.
 ###
 classPropertyNameMap = ( controlClass ) ->
+  # Ensure class has been initialized before we inspect its className.
+  controlClass.initialize()
   className = controlClass::className
   unless propertyNameMaps[ className ]
     map = {}


Conscicom people Add Sally and Chris and a conscicom section

--- project.coffee
+++ project.coffee
@@ -25,8 +25,8 @@
       url: ['https://www.zooniverse.org/', 'https://twitter.com/the_zooniverse', 'https://github.com/zooniverse']
     }]

-  scientists:
-    title: "Researchers"
+  missouri:
+    title: "Missouri Botanical Garden"
     members: [{
       image: '//placehold.it/100.png?text=Example'
       name: 'Trish Rose-Sandler'
@@ -42,6 +42,20 @@
       name: 'Mike Lichtenberg'
       location: 'St Louis, MO, USA'
       description: 'Missouri Botanical Garden'
+    }]
+
+  conscicom:
+    title: "Constructing Scientific Communities"
+    members: [{
+      image: '//placehold.it/100.png?text=Example'
+      name: 'Sally Shuttleworth'
+      location: 'Oxford, UK'
+      description: 'Principle Investigator'
+    },{
+      image: '//placehold.it/100.png?text=Example'
+      name: 'Chris Lintott'
+      location: 'Oxford, UK'
+      description: 'Co-investigator'
     },{
       image: '//placehold.it/100.png?text=Example'
       name: 'Geoffrey Belknap'


Extract cmEditor tests to RT module

Extract cmEditor tests to RT module

--- services/web/test/unit_frontend/coffee/ide/editor/directives/cmEditorTests.coffee
+++ services/web/test/unit_frontend/coffee/ide/editor/directives/cmEditorTests.coffee
@@ -1,58 +0,0 @@
-define ['ide/editor/directives/cmEditor'], () ->
-  describe 'cmEditor', () ->
-    beforeEach(module('SharelatexApp'))
-
-    beforeEach () ->
-      @richTextInit = sinon.stub()
-      @richTextOpenDoc = sinon.stub()
-      window.Frontend = {
-        'rich-text': {
-          init: @richTextInit,
-          openDoc: @richTextOpenDoc
-        }
-      }
-
-    afterEach () -> window.Frontend = null
-
-    it 'inits Rich Text', () ->
-      inject ($compile, $rootScope) =>
-        $compile('

')($rootScope)
-        expect(@richTextInit).to.have.been.called
-
-    it 'attaches to CM', () ->
-      inject ($compile, $rootScope, $browser) =>
-        getSnapshot = sinon.stub()
-        detachFromCM = sinon.stub()
-        attachToCM = sinon.stub()
-        $rootScope.sharejsDoc = {
-          getSnapshot: getSnapshot
-          detachFromCM: detachFromCM
-          attachToCM: attachToCM
-        }
-
-        $compile('

')($rootScope)
-        $rootScope.$digest()
-        # Trigger $applyAsync to evaluate the expression, normally done in the
-        # next tick
-        $browser.defer.flush()
-
-        expect(detachFromCM).to.have.been.called
-        expect(getSnapshot).to.have.been.called
-        expect(@richTextOpenDoc).to.have.been.called
-        expect(attachToCM).to.have.been.called
-
-    it 'detaches from CM when destroyed', () ->
-      inject ($compile, $rootScope) =>
-        @richTextInit.returns({ setValue: sinon.stub() })
-        detachFromCM = sinon.stub()
-        $rootScope.sharejsDoc = {
-          getSnapshot: sinon.stub()
-          detachFromCM: detachFromCM
-          attachToCM: sinon.stub()
-        }
-
-        $compile('

')($rootScope)
-        $rootScope.$digest()
-        $rootScope.$broadcast('destroy')
-
-        expect(detachFromCM).to.have.been.called


fixed qtip tooltips on ajax-injected content

--- app/assets/javascripts/common_interface.coffee
+++ app/assets/javascripts/common_interface.coffee
@@ -92,14 +92,21 @@
         Interface.call_the_cyclists()
   Interface.call_the_cyclists()

-  # Tooltips
-  $("a.tooltip").qtip
-    content: -> $(this).attr('title')
-    position:
-      my: 'bottom right'
-      at: 'top center'
-    style:
-      classes: "ui-tooltip-tipsy"
+  # Tooltips. Works with AJAX-injected content, too
+  #
+  $(document).on 'mouseover', 'a.tooltip', (e) ->
+    $(this).qtip {
+      overwrite: false
+      content: -> $(this).attr('title')
+      show:
+        event: e.type
+        ready: true
+      position:
+        my: 'bottom right'
+        at: 'top center'
+      style:
+        classes: "ui-tooltip-tipsy"
+      }, e

 class @AppInterface
   set_active_movie_tab: (page) ->


inflecting plural of "emisor" and added imagenUrl to Bandango.Emisor

--- app/assets/javascripts/models/emisor.js.coffee
+++ app/assets/javascripts/models/emisor.js.coffee
@@ -8,3 +8,6 @@
   codigoPuntoEmision:                    DS.attr()
   contribuyenteEspecialNumeroResolucion: DS.attr()
   obligadoALlevarContabilidad:           DS.attr()
+  imagenUrl:                             DS.attr()
+
+Ember.Inflector.inflector.irregular("emisor", "emisores")


Fix refresh_token.js console error by validating token variable

--- app/assets/javascripts/refresh_token.js.coffee
+++ app/assets/javascripts/refresh_token.js.coffee
@@ -15,7 +15,7 @@
 $ ->
   refreshToken = ->
     token = currentPlayer.media.src.split('?')[1]
-    if token.match(/^token=/)
+    if token && token.match(/^token=/)
       mount_point = $('body').data('mountpoint')
       $.get("#{mount_point}authorize.txt?#{token}")
         .done -> console.log("Token refreshed")


Fix console form. Disable mousewheel on a input number field when is in focus

--- app/assets/javascripts/service_versions.coffee
+++ app/assets/javascripts/service_versions.coffee
@@ -214,3 +214,10 @@
   targetURL = $(this).val()
   if targetURL
     location.href = targetURL
+
+$(document).on 'focus', 'form input[type=number]', (e) ->
+  $(this).on 'mousewheel.disableScroll', (e) ->
+    e.preventDefault()
+
+$(document).on 'blur', 'form input[type=number]', (e) ->
+  $(this).off('mousewheel.disableScroll')


Simplify setStyles to use known context

Simplify setStyles to use known context

--- app/js/jail_iframe/classes/icon_buttons.coffee
+++ app/js/jail_iframe/classes/icon_buttons.coffee
@@ -12,7 +12,7 @@
     """
     FactlinkJailRoot.$factlinkCoreContainer.append(@$el)

-    @_setStylesFromElement(options.targetElement)
+    @_setStyles()

     @_robustHover = new FactlinkJailRoot.RobustHover
       $el: @$el
@@ -42,8 +42,8 @@
   fadeOut: ->
     @$el.removeClass 'factlink-control-visible'

-  _setStylesFromElement: (element) ->
-    style = window.getComputedStyle(element)
+  _setStyles: ->
+    style = window.getComputedStyle(@_targetElement)
     targetColor = style.color

     # See https://gamedev.stackexchange.com/questions/38536/given-a-rgb-color-x-how-to-find-the-most-contrasting-color-y/38561#38561


fixed catch all for whats condition

fixed catch all for whats condition

--- src/sentiment.coffee
+++ src/sentiment.coffee
@@ -198,10 +198,10 @@
     # responds in the current channel
     msg.send getSadChannels(10)

-  robot.respond /what[ is'] (sadness|stress)\??/i, (msg) ->
+  robot.respond /what[ is']* (sadness|stress)\??/i, (msg) ->
     # responds in the current channel
     msg.reply "Stress is not shipping code!"

-  robot.respond /what[ is'] (happiness|happy)\??/i, (msg) ->
+  robot.respond /what[ is']* (happiness|happy)\??/i, (msg) ->
     # responds in the current channel
     msg.reply "Happiness at Monsoon!"


promise-style ready

promise-style ready

--- src/kite-as-promised/kite.coffee
+++ src/kite-as-promised/kite.coffee
@@ -1,3 +1,5 @@
+"use strict"
+
 BasicKite = require '../kite/kite.coffee'

 module.exports = class Kite extends BasicKite
@@ -9,10 +11,15 @@
     super options

   tell: (method, params, callback) ->
-    new Promise (resolve, reject) ->
+    new Promise (resolve, reject) =>
       super method, params, (err, result) ->
         return reject err  if err?
         return resolve result
       return
     .timeout @options.timeout ? 5000
     .nodeify callback
+
+  ready: (callback) ->
+    new Promise (resolve) =>
+      super resolve
+    .nodeify callback


Fixed test for backbone-sql

Fixed test for backbone-sql

--- test/spec/sync/flat/unique.tests.coffee
+++ test/spec/sync/flat/unique.tests.coffee
@@ -40,7 +40,7 @@

     new_updated_at = null
     beforeEach (callback) ->
-      Utils.resetSchemas [Flat], (err) ->
+      Utils.resetSchemas [Flat, Empty], (err) ->
         return callback(err) if err

         Fabricator.create Flat, BASE_COUNT, {


Make powerup square again

Make powerup square again

--- app/scripts/components/entities/PowerUp.coffee
+++ app/scripts/components/entities/PowerUp.coffee
@@ -3,7 +3,7 @@
     @requires '2D,WebGL,Color'
     @color '#20FF20'
     @attr
-      w: 30
+      w: 20
       h: 20

   remove: ->


Remove obsolete comment.

Remove obsolete comment.

--- test/channels.coffee
+++ test/channels.coffee
@@ -62,9 +62,6 @@

             common.stopServer (err) =>
                 return done(err) if err
-                # Start new server and manually trigger a reconnect
-                # In event of a failure, socket.io will retry automatically,
-                # but not in this case, it's a clean shutdown.

                 disconnect = false


bored after aerospace final

bored after aerospace final

--- src/stick-rpg.coffee
+++ src/stick-rpg.coffee
@@ -1,10 +1,39 @@
+################################################################################
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Thomas Steinholz
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+################################################################################
+
+stage = null
+
 main = ->
+  ##############################################################################
+  # Initailize the Game
+  ##############################################################################
+
   stage = new createjs.Stage 'RPG-Canvas'

   stage.mouseEventEnabled = true

-  # TODO : Load in assets
-
+  # Load Assets
   manifest = [
     "assets/dev/test.jpg", { src:"assets/dev/test.jpg", id:"test" },
     "", { src:"", id:"" },
@@ -21,12 +50,17 @@

   queue = new createjs.LoadQueue()
   queue.installPlugin(createjs.Sound)
-  queue.on "complete", handleLoad, @
+  queue.on "complete", handleLoadEvent, @
   queue.loadManifest manifest
-  #queue.loadManifest ["assets/dev/test.jpg", { src:"assets/dev/test.jpg", id:"test" }]

+  # Initialize other sub-systems
+  createjs.Ticker.setFPS 30
+  createjs.Ticker.addEventListener "click", handleClickEvent

-  createjs.Ticker.setFPS(30)
-
-handleLoad = ->
+handleLoadEvent = ->
   something = 4
+  # TODO : Handle event loading
+
+handleClickEvent = ->
+  something = 2
+  # TODO : Handle click event