finished refactoring drmValidation

finished refactoring drmValidation

--- app/coffee/drm-validation/drm-validation.coffee
+++ app/coffee/drm-validation/drm-validation.coffee
@@ -38,21 +38,81 @@
                 validate = self.validateURL value
                 validateField.call @, value, validate

-            # body.on 'keyup', ':input.drm-valid-phone', self.validatePhone
-            # body.on 'keyup', ':input.drm-valid-email', self.validateEmail
-            # body.on 'keyup', ':input.drm-valid-full-name', self.validateFullName
-            # body.on 'keyup', ':input.drm-valid-alpha', self.validateAlpha
-            # body.on 'keyup', ':input.drm-valid-alphanum', self.validateAlphaNum
-            # body.on 'keyup', ':input.drm-valid-alphadash', self.validateAlphaNumDash
-            # body.on 'keyup', ':input.drm-valid-alpha-num-underscore', self.validateAlphaNumUnderscore
-            # body.on 'keyup', ':input.drm-valid-no-spaces', self.validateNoSpaces
-            # body.on 'keyup', ':input.drm-valid-no-tags', self.validateNoTags
-            # body.on 'keyup', ':input[data-max-value]:not([data-min-value])', self.validateMaxValue
-            # body.on 'keyup', ':input[data-min-value]:not([data-max-value])', self.validateMinValue
-            # body.on 'keyup', ':input[data-max-length]:not([data-min-length])', self.validateMaxLength
-            # body.on 'keyup', ':input[data-min-length]:not([data-max-length])', self.validateMinLength
-            # body.on 'keyup', ':input[data-min-value][data-max-value]', self.validateBetweenValue
-            # body.on 'keyup', ':input[data-min-length][data-max-length]', self.validateBetweenLength
+            body.on 'keyup', ':input.drm-valid-phone', ->
+                value = self.getValue.call @
+                validate = self.validatePhone value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-email', ->
+                value = self.getValue.call @
+                validate = self.validateEmail value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-full-name', ->
+                value = self.getValue.call @
+                validate = self.validateFullName value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-alpha', ->
+                value = self.getValue.call @
+                validate = self.validateAlpha value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-alphanum', ->
+                value = self.getValue.call @
+                validate = self.validateAlphaNum value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-alphadash', ->
+                value = self.getValue.call @
+                validate = self.validateAlphaNumDash value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-alpha-num-underscore', ->
+                value = self.getValue.call @
+                validate = self.validateAlphaNumUnderscore value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-no-spaces', ->
+                value = self.getValue.call @
+                validate = self.validateNoSpaces value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input.drm-valid-no-tags', ->
+                value = self.getValue.call @
+                validate = self.validateNoTags value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-max-value]:not([data-min-value])', ->
+                value = self.getValue.call @
+                validate = self.validateMaxValue value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-min-value]:not([data-max-value])', ->
+                value = self.getValue.call @
+                validate = self.validateMinValue value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-max-length]:not([data-min-length])', ->
+                value = self.getValue.call @
+                validate = self.validateMaxLength value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-min-length]:not([data-max-length])', ->
+                value = self.getValue.call @
+                validate = self.validateMinLength value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-min-value][data-max-value]', ->
+                value = self.getValue.call @
+                validate = self.validateBetweenValue value
+                validateField.call @, value, validate
+
+            body.on 'keyup', ':input[data-min-length][data-max-length]', ->
+                value = self.getValue.call @
+                validate = self.validateBetweenLength value
+                validateField.call @, value, validate
+
             body.on 'keyup', ':input', self.trackLength

             body.on 'keyup', '[required]', ->
@@ -229,22 +289,20 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'email'
             re = new RegExp '^[a-z][a-z\\-\\_\\.\\d]*@[a-z\\-\\_\\.\\d]*\\.[a-z]{2,6}$','gi'

             evaluate = (result, value) ->
-                message = 'please enter a valid email address'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please enter a valid email address'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -252,22 +310,21 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'phone'
+            # validates United States phone number patterns
             re = new RegExp '^\\(?\\d{3}[\\)\\-\\.]?\\d{3}[\\-\\.]?\\d{4}(?:[xX]\\d+)?$','gi'

             evaluate = (result, value) ->
-                message = 'please enter a valid phone number'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please enter a valid phone number'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -275,22 +332,21 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'fullName'
+            # allows alpha . -
             re = new RegExp '^[a-z]+ [a-z\\.\\- ]+$','gi'

             evaluate = (result, value) ->
-                message = 'please enter your first and last name'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please enter your first and last name'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -298,22 +354,20 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'alpha'
             re = new RegExp '^[a-z ]*','gi'

             evaluate = (result, value) ->
-                message = 'please use alpha characters only'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please use alpha characters only'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -321,22 +375,20 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'alphanum'
             re = new RegExp '^[a-z\\d ]*$','gi'

             evaluate = (result, value) ->
-                message = 'please use alphanumeric characters only'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please use alphanumeric characters only'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -344,22 +396,20 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'noSpaces'
             re = new RegExp '^\\S*$','gi'

             evaluate = (result, value) ->
-                message = 'no spaces'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'no spaces'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -367,22 +417,20 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
+                issuer: 'alphaNumDash'
             re = new RegExp '^[a-z\\d- ]*$','gi'

             evaluate = (result, value) ->
-                message = 'please use alphanumeric and dashes only'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please use alphanumeric and dash characters only'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -390,23 +438,21 @@
             validate =
                 status: null
                 message: null
-                issuer: 'number'
-            # allows alphanumeric characters and underscores; no spaces
+                issuer: 'alphaNumUnderscore'
+            # allows alphanumeric characters and underscores; no spaces; recommended for usernames
             re = new RegExp '^[a-z\\d_]*$','gi'

             evaluate = (result, value) ->
-                message = 'please use alphanumeric and underscores only. no spaces'
-                if result and value is result
-                    status = 'success'
-                    @removeNotice 'danger', message
-                else
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                if result and value is result
+                    validate.status = 'success'
+                else
+                    validate.status = 'danger'
+                    validate.message = 'please use alphanumeric and underscores only. no spaces'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -417,19 +463,17 @@
                 issuer: 'number'
             re = new RegExp '<[a-z]+.*>.*<\/[a-z]+>','i'

-            evaluate = (result) ->
-                message = 'no html tags allowed'
+            evaluate = (result) ->
                 if result
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                result = $.trim re.exec value
-                status = evaluate result, value
+                    validate.status = 'danger'
+                    validate.message = 'no html tags allowed'
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                result = $.trim re.exec value
+                validate.status = evaluate result, value

             validate

@@ -439,17 +483,15 @@
             value = $.trim that.val()

             evaluate = (max, value) ->
-                message = "please enter a value that is less than #{max + 1}"
                 if value > max
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate max, value
+                    validate.status = 'danger'
+                    validate.message = "please enter a value that is less than #{max + 1}"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate max, value

             validate

@@ -458,18 +500,16 @@
             min = that.data 'min-value'
             value = $.trim that.val()

-            evaluate = (min, value) ->
-                message = "please enter a value of at least #{min}"
+            evaluate = (min, value) ->
                 if value < min
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate(min, value)
+                    validate.status = 'danger'
+                    validate.message = "please enter a value of at least #{min}"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate(min, value)

             validate

@@ -479,18 +519,16 @@
             max = that.data 'max-value'
             value = $.trim that.val()

-            evaluate = (min, max, value) ->
-                message = "please enter a value that is between #{min - 1} and #{max + 1}"
+            evaluate = (min, max, value) ->
                 if (value < min) or (value > max)
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate min, max, value
+                    validate.status = 'danger'
+                    validate.message = "please enter a value that is between #{min - 1} and #{max + 1}"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate min, max, value

             validate

@@ -500,18 +538,16 @@
             value = $.trim that.val()
             length = value.length

-            evaluate = (max, length) ->
-                message = "please enter less than #{max + 1} characters"
+            evaluate = (max, length) ->
                 if length > max
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate max, length
+                    validate.status = 'danger'
+                    validate.message = "please enter less than #{max + 1} characters"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate max, length

             validate

@@ -521,18 +557,16 @@
             value = $.trim that.val()
             length = value.length

-            evaluate = (min, length) ->
-                message = "please enter at least #{min} characters"
+            evaluate = (min, length) ->
                 if length < min
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate min, length
+                    validate.status = 'danger'
+                    validate.message = "please enter at least #{min} characters"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate min, length

             validate

@@ -543,18 +577,16 @@
             value = $.trim that.val()
             length = value.length

-            evaluate = (min, max, length) ->
-                message = "please enter a value that is between #{min - 1} and #{max + 1} characters"
+            evaluate = (min, max, length) ->
                 if (length < min) or (length > max)
-                    status = 'danger'
-                    @issueNotice.call that, status, message
-                else
-                    status = 'success'
-                    @removeNotice 'danger', message
-                status
-
-            if value?
-                status = evaluate min, max, length
+                    validate.status = 'danger'
+                    validate.message = "please enter a value that is between #{min - 1} and #{max + 1} characters"
+                else
+                    validate.status = 'success'
+                validate.status
+
+            if value?
+                validate.status = evaluate min, max, length

             validate


Zmq error handling

Zmq error handling

--- client/src/App.coffee
+++ client/src/App.coffee
@@ -60,10 +60,9 @@
             str_topic = topic.toString()
             str_message = message.toString()

-            if(topic.toString() == 'update')
+            if(str_topic == 'update')
                 model = JSON.parse str_message
                 #console.info "Model: ", model
-                #console.log "translator: ", translator
                 translator.parseGestures(model)
         catch e
             console.log "error", e


Add username to log output

Add username to log output

--- handlers/log.coffee
+++ handlers/log.coffee
@@ -19,7 +19,7 @@
 debug = (require 'debug')('Chatbot:log')

 handleMessage = (message, callback) ->
-	debug message.message
+	debug "[#{message.username}] #{message.message}"
 	callback?()

 module.exports =


Fix sync bugs

Fix sync bugs

--- app/lib/sync.coffee
+++ app/lib/sync.coffee
@@ -33,10 +33,9 @@

   # Go through each item in the queue and send it to the server
   sync: ->
-    # Don't run if the queue is empty
-    return if @queue.length is 0
+    console.log "Going to run sync"
     # Send queue to server
-    @emit 'sync', @queue, (records) ->
+    @emit 'sync', @queue, (records) =>
       # Update records
       console.log records
       @queue = []
@@ -205,8 +204,8 @@
   socket.on event, Sync.goOffline

 # Sync unsunk changes
-socket.on 'connect', ->
-  Sync.sync()
+# socket.on 'connect', ->
+#   Sync.sync()

 # Just in case you need any default values
 class Base


lead markdown needs to handle component functions

--- app/markdown.coffee
+++ app/markdown.coffee
@@ -44,7 +44,7 @@
         fn ctx
     React.DOM.div {className: 'inline-example'},
       React.DOM.div {className: 'example'}, example_component
-      React.DOM.div {className: 'output'}, nested_context.component
+      React.DOM.div {className: 'output'}, nested_context.component()

 LeadMarkdownComponent = React.createClass
   displayName: 'LeadMarkdownComponent'


extend javascript to have array methods where and dup

--- lib/assets/javascripts/extensions.coffee
+++ lib/assets/javascripts/extensions.coffee
@@ -65,3 +65,18 @@
   return parseFloat(this)
 String.prototype.to_i = ->
   return parseInt(this)
+Array.prototype.dup = ->
+  return @.slice(0)
+Array.prototype.where = (obj) ->
+  equiv = (first,second) ->
+    return true if first == second
+    if !isNaN(first) && !isNaN(second)
+      return true if parseFloat(first) == parseFloat(second)
+    return false
+  result = []
+  for entry in @
+    addEntry = true
+    for key,value of obj
+      addEntry = addEntry && equiv(entry[key], value)
+    result.push(entry) if addEntry
+  result


Delay displaying the progress bar for (a default of) 400ms

--- lib/assets/javascripts/turbolinks.coffee
+++ lib/assets/javascripts/turbolinks.coffee
@@ -3,6 +3,7 @@
 transitionCacheEnabled  = false
 requestCachingEnabled   = true
 progressBar             = null
+progressBarDelay        = 400

 currentState            = null
 loadedAssets            = null
@@ -36,7 +37,7 @@
     cacheCurrentPage()

   rememberReferer()
-  progressBar?.start()
+  progressBar?.start(delay: progressBarDelay)

   if transitionCacheEnabled and !options.change and cachedPage = transitionCacheFor(url.absolute)
     fetchHistory cachedPage
@@ -500,7 +501,16 @@
     @element.classList.remove(className)
     document.head.removeChild(@styleElement)

-  start: ->
+  start: ({delay} = {})->
+    clearTimeout(@displayTimeout)
+    if delay
+      @display = false
+      @displayTimeout = setTimeout =>
+        @display = true
+      , delay
+    else
+      @display = true
+
     if @value > 0
       @_reset()
       @_reflow()
@@ -581,7 +591,7 @@
       background-color: #0076ff;
       height: 3px;
       opacity: #{@opacity};
-      width: #{@value}%;
+      width: #{if @display then @value else 0}%;
       transition: width #{@speed}ms ease-out, opacity #{@speed / 2}ms ease-in;
       transform: translate3d(0,0,0);
     }
@@ -590,7 +600,8 @@
 ProgressBarAPI =
   enable: ProgressBar.enable
   disable: ProgressBar.disable
-  start: -> ProgressBar.enable().start()
+  setDelay: (value) -> progressBarDelay = value
+  start: (options) -> ProgressBar.enable().start(options)
   advanceTo: (value) -> progressBar?.advanceTo(value)
   done: -> progressBar?.done()


Optimize project_roles requests

Optimize project_roles requests

- Only request if a user is logged in
- Filter to the logged in user

--- app/classifier/index.cjsx
+++ app/classifier/index.cjsx
@@ -250,8 +250,8 @@
     </div>

   renderExpertOptions: ->
-    if @props.project?
-      getUserRoles = @props.project.get 'project_roles'
+    if @props.project? and @props.user?
+      getUserRoles = @props.project.get('project_roles', user_id: @props.user.id)
         .then (projectRoles) =>
           getProjectRoleHavers = Promise.all projectRoles.map (projectRole) =>
             projectRole.get 'owner'


enh: wider area for events

enh: wider area for events

--- coffee/application.coffee
+++ coffee/application.coffee
@@ -865,10 +865,13 @@
 E("btn-reset").addEventListener "click", doReset
 E("btn-step").addEventListener "click", doStep
 #E("canvas").addEventListener "click", doCanvasClick
-E("canvas").addEventListener "mousedown", doCanvasMouseDown
-E("canvas").addEventListener "mouseup", doCanvasMouseUp
-E("canvas").addEventListener "mousemove", doCanvasMouseMove
-E("canvas").addEventListener "mousedrag", doCanvasMouseMove
+#
+mouseMoveReceiver = E("canvas-container")
+mouseMoveReceiver.addEventListener "mousedown", doCanvasMouseDown
+mouseMoveReceiver.addEventListener "mouseup", doCanvasMouseUp
+mouseMoveReceiver.addEventListener "mousemove", doCanvasMouseMove
+mouseMoveReceiver.addEventListener "mousedrag", doCanvasMouseMove
+
 E("btn-set-rule").addEventListener "click", doSetRule
 E("btn-set-rule-generic").addEventListener "click", (e)->
   doSetRuleGeneric()


I don't believe we need this now.

I don't believe we need this now.

--- coffee/simulate-ui.coffee
+++ coffee/simulate-ui.coffee
@@ -41,10 +41,7 @@
   document.getElementById("offline-simulate-check").addEventListener "click", ->
     if @checked
       Offline.options.checks.active = 'down'
-      Offline.markDown()
     else
       Offline.options.checks.active = 'up'
-      Offline.markUp()
-
-  Offline.on "confirmed-up", ->
-    document.getElementById("offline-simulate-check").checked = false
+
+    Offline.check()


#206, #203 put note for removal

#206, #203 put note for removal

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -128,6 +128,7 @@
   column = end.column - start.column
   new Point(row, column)

+# [TODO] Remove this code once I updated minimum Atom version to >=1.7.0
 getNewTextRangeFromChanges = (changes) ->
   finalRange = null
   for change in changes when change.newRange?
@@ -150,15 +151,16 @@
   finalRange

 getNewTextRangeFromPaches = (patches) ->
-  if (change = Patch.compose(patches).getChanges().shift())?
-    newStart = Point.fromObject(change.newStart)
-    new Range(newStart, newStart.traverse(change.newExtent))
+  if (patch = Patch.compose(patches).getChanges().shift())?
+    newStart = Point.fromObject(patch.newStart)
+    new Range(newStart, newStart.traverse(patch.newExtent))
   else
     null

 Patch = null
 IsSupportPatch = semver.satisfies(atom.appVersion, '>=1.7.0-beta0')

+# [TODO] Remove version check code once I updated minimum Atom version to >=1.7.0
 getNewTextRangeFromCheckpoint = (editor, checkpoint) ->
   {history} = editor.getBuffer()
   if (index = history.getCheckpointIndex(checkpoint))?


fix bug: 0 matches _.isBblank

fix bug: 0 matches _.isBblank

--- client/lanes/models/State.coffee
+++ client/lanes/models/State.coffee
@@ -52,7 +52,11 @@

         integer:
             set: (newVal) ->
-                val = parseInt(newVal, 10) unless _.isBlank(newVal)
+                val = switch
+                    when _.isInteger(newVal) then newVal
+                    when ! _.isBlank(newVal) then parseInt(newVal, 10)
+                    else
+                        undefined
                 {val, type: 'integer'}
         file:
             set: (val) ->


Export SearchPaginator class

Export SearchPaginator class

--- src/coffee/cilantro/ui/field/controls/search.coffee
+++ src/coffee/cilantro/ui/field/controls/search.coffee
@@ -187,4 +187,4 @@
             @collection.set(value, merge: false)

-    { FieldValueSearch, ValueSearch, SearchItem, SearchPage, SearchPageRoll }
+    { FieldValueSearch, ValueSearch, SearchItem, SearchPage, SearchPageRoll, SearchPaginator }


Bugfix

Bugfix

Enable "clear" button even after game
Erase found tuples on side

--- coffee/rungame.coffee
+++ coffee/rungame.coffee
@@ -205,7 +205,7 @@
 	diagram = game.currDiagram()
 	ap = diagram.active_points
 	enableButtonIf("#check_button", ap.length > 0 and !diagram.complete)
-	enableButtonIf("#clear_button", ap.length > 0 and !diagram.complete)
+	enableButtonIf("#clear_button", ap.length > 0)
 	enableButtonIf("#done_button", (ap.length == 0) and
 		(game.currDiagram().found != 0) and !diagram.complete)
 	enableButtonIf("#next_button", game.i != game.length-1)
@@ -259,6 +259,7 @@
 	# Updates for clicking prev, next, or correct done
 	updateProgressBullets()
 	updateScores()
+	updateFoundTuples()
 	updateSidebarSoft(c)

 startGameUI = () ->


JS fixes

JS fixes

--- coffee/rungame.coffee
+++ coffee/rungame.coffee
@@ -69,7 +69,7 @@
 			p = new Point(point_array[0], px, py, i)
 			@points[point_array[0]] = p
 		@items = for item in json_array["items"]
-			 sortItem((@points[name] for name in item))
+			sortItem((@points[name] for name in item))
 		@item_hashes = (hashItem(item) for item in @items)
 		@found_item_indices = []
 		@unfound_item_indices = [0...@items.length]
@@ -233,7 +233,7 @@
 				diagram = new Diagram(data)
 				game.diagrams[i] = diagram
 				game.doneLoadingDiagram(i)
-	).error( # chain
+	).fail( # chain
 		(jqXhr, textStatus, error) ->
 			swal({
 				title: textStatus,


argh. one character off alignment

argh. one character off alignment

--- assets/js/teacher.coffee
+++ assets/js/teacher.coffee
@@ -57,7 +57,7 @@
                     , () ->
                       $("#text-container-#{closed_id}").hide("explode", 1000);)(id);
     #also, delete any ids that are still client side but have disconnected from the server
-     _.map $('.container').find('.student-box'),
+    _.map $('.container').find('.student-box'),
           (el) ->
             pair_present = _.map data.idNickPairs, (p) ->
                              id_rgx = new RegExp p.id, 'g'


updated atom package list

updated atom package list

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -27,6 +27,8 @@
   "linter-htmlhint": {}
   "linter-scss-lint": {}
   "linter-shellcheck": {}
+  "linter-stylelint":
+    useStandard: true
   "linter-tidy": {}
   "markdown-preview-plus": {}
   "one-light-ui": {}


Update Atom configuration

Update Atom configuration

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -1,6 +1,26 @@
 "*":
-  "atom-lint": {}
+  "atom-ide-ui":
+    "atom-ide-diagnostics-ui":
+      autoVisibility: true
+    use:
+      "atom-ide-diagnostics": false
+      "atom-ide-diagnostics-ui": false
   core:
+    customFileTypes:
+      "source.ini": [
+        ".buckconfig"
+        ".flowconfig"
+        ".hgrc"
+      ]
+      "source.json": [
+        "BUCK.autodeps"
+      ]
+      "source.python": [
+        "BUCK"
+      ]
+      "source.shell": [
+        "symlink"
+      ]
     disabledPackages: [
       "background-tips"
       "exception-reporting"
@@ -13,33 +33,27 @@
       "repositories"
       "tmp"
       "vendor"
+      "*.pyc"
     ]
     telemetryConsent: "limited"
-    themes: [
-      "one-dark-ui"
-      "pure-syntax"
-    ]
     titleBar: "custom-inset"
   editor:
-    fontFamily: "Source Code Pro"
-    fontSize: 13
+    fontFamily: "Hasklig"
     invisibles: {}
     preferredLineLength: 100
     showInvisibles: true
-  "file-watcher":
-    promptWhenFileHasChangedOnDisk: false
-  "find-and-replace": {}
-  "go-plus": {}
-  kite:
-    showKiteTourOnStatup: false
-  linter: {}
-  "linter-eslint":
-    disableWhenNoEslintrcFileInPath: true
-  "linter-rubocop": {}
-  "one-dark-ui": {}
-  react:
-    disableAutoClose: true
-    enabledForAllJavascriptFiles: true
+  "flow-ide": {}
+  "ide-flowtype":
+    tryFlowBin: true
+  "linter-flow":
+    executablePath: "/usr/local/bin/flow"
+  "linter-flowtype": {}
+  "linter-pylint":
+    executablePath: "%p/env/bin/pylint"
+    pythonPath: "%p/env/bin/python"
+  "linter-ui-default":
+    panelHeight: 202
+  "one-light-ui": {}
   "spell-check":
     grammars: [
       "text.plain"
@@ -47,25 +61,6 @@
       "text.git-commit"
       "source.yaml"
     ]
-  "tree-view":
-    showOnRightSide: true
+  "tree-view": {}
   welcome:
     showOnStartup: false
-".coffee.jsx.source":
-  editor:
-    preferredLineLength: 100
-".git-commit.text":
-  editor:
-    preferredLineLength: 100
-".git-config.source":
-  editor:
-    preferredLineLength: 100
-".git-rebase.text":
-  editor:
-    preferredLineLength: 100
-".ini.source":
-  editor:
-    preferredLineLength: 100
-".js.jsx.source":
-  editor:
-    preferredLineLength: 100


sidebar: remove fake notification jewel when clicked to it

--- client/Social/Activity/sidebar/topicitem.coffee
+++ client/Social/Activity/sidebar/topicitem.coffee
@@ -35,6 +35,7 @@
       # and navigate to there
       for route in visitedRoutes by -1 when route.search('/Public') isnt -1
         KD.utils.stopDOMEvent event
+        @setUnreadCount 0
         router.handleRoute route
         return no


Teamwork: Header styling for teamwork header.

--- client/Teamwork/Views/playgroundteamwork.coffee
+++ client/Teamwork/Views/playgroundteamwork.coffee
@@ -6,8 +6,7 @@

     super options, data

-    @on "PanelCreated", =>
-      @getActivePanel().header.unsetClass "teamwork"
+    @on "PanelCreated", @bound "applyHeaderStyling"

     @on "ContentIsReady", =>
       return unless @amIHost()
@@ -22,6 +21,26 @@
           warn "Unhandled prerequisite type."
       else if initialState
         @setUpInitialState initialState
+
+  applyHeaderStyling: ->
+    {header} = @getActivePanel().getPaneByName "finder"
+    options = @getOptions().headerStyling
+    return unless  options
+
+    header.updatePartial "" # header.destroySubViews didn't work
+    header.addSubView icon = new KDCustomHTMLView
+      tagName  : "span"
+      cssClass : "icon tw-ply-icon"
+
+    header.addSubView title = new KDCustomHTMLView
+      tagName  : "span"
+      partial  : "#{@getOptions().playgroundManifest.name} Teamwork"
+
+    {bgColor, bgImage, textColor} = options
+
+    header.setCss "background"        , "#{bgColor}"       if bgColor
+    header.setCss "color"             , textColor          if textColor
+    icon.setCss   "backgroundImage"   , "url(#{bgImage})"  if bgImage

   handleRun: (panel) ->
     options      = @getOptions()


Client: Activity: Added followChannel and unfollowChannel methods to channel actions.

--- client/activity/lib/flux/actions/channel.coffee
+++ client/activity/lib/flux/actions/channel.coffee
@@ -273,7 +273,48 @@
   dispatch SET_CHAT_INPUT_CHANNELS_VISIBILITY, { visible }

+###*
+ * Action to set visibility of chat input channels
+###
+followChannel = (channelId) ->
+
+  { follow } = kd.singletons.socialapi.channel
+  { FOLLOW_CHANNEL_BEGIN, FOLLOW_CHANNEL_SUCCESS, FOLLOW_CHANNEL_FAIL } = actionTypes
+
+  dispatch FOLLOW_CHANNEL_BEGIN, { channelId }
+
+  follow { channelId }, (err, lol) ->
+
+    if err
+      dispatch FOLLOW_CHANNEL_FAIL, { err, channelId }
+      return
+
+    dispatch FOLLOW_CHANNEL_SUCCESS, {channelId }
+
+
+###*
+ * Action to set visibility of chat input channels
+###
+unfollowChannel = (channelId) ->
+
+  { unfollow } = kd.singletons.socialapi.channel
+  { UNFOLLOW_CHANNEL_BEGIN, UNFOLLOW_CHANNEL_SUCCESS, UNFOLLOW_CHANNEL_FAIL } = actionTypes
+
+  dispatch UNFOLLOW_CHANNEL_BEGIN, { channelId }
+
+  unfollow { channelId }, (err, lol) ->
+
+    if err
+      dispatch UNFOLLOW_CHANNEL_FAIL, { err, channelId }
+      return
+
+    dispatch UNFOLLOW_CHANNEL_SUCCESS, {channelId }
+
+
+
 module.exports = {
+  followChannel
+  unfollowChannel
   loadChannelByName
   loadFollowedPrivateChannels
   loadFollowedPublicChannels


video: getParticipants returns participant no matter what

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -6,6 +6,11 @@
 getNick         = require 'app/util/nick'
 ProfileTextView = require 'app/commonviews/linkviews/profiletextview'

+defaultPublisher = ->
+  nick      : getNick()
+  type      : 'publisher'
+  videoData : null
+
 ###*
  * It makes a request to the backend and gets session id
  * and creates a session with that session id.
@@ -60,8 +65,20 @@
 toNickKeyedMap = (subscribers, publisher) ->

   map = {}
+
+  # we want get participants to return a publisher no matter what, it may not
+  # have a video data (e.g while camera is being asked), but views are
+  # expecting a Publisher no matter what, and it's how it should be. Video
+  # without a publisher is not permitted atm.
+
   map[subscriber.nick] = subscriber  for own cId, subscriber of subscribers when subscriber
-  map[publisher.nick] = publisher  if publisher
+
+  # if default publisher is mutated via `or=` the passed original is being
+  # mutated and affect other parts of the application. That's why copying into
+  # another variable happens here. ~Umut
+  _publisher = publisher ? defaultPublisher()
+  map[_publisher.nick] = _publisher
+
   return map


video: fix reference error

video: fix reference error

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -113,7 +113,7 @@
   options.width  = 325

   publisher = OT.initPublisher view.getElement(), options, (err) ->
-    return calback err  if err
+    return callback err  if err
     publisher.setStyle 'backgroundImageURI', uri = _getGravatarUri whoami()
     callback null, publisher


focus hack

focus hack

--- app/notebook.coffee
+++ app/notebook.coffee
@@ -240,8 +240,12 @@
   Editor.set_value cell.editor, value

 focus_cell = (cell) ->
-  cell.editor.focus()
-  cell.notebook.cell_focused.push cell
+  # hack around not understanding how this plays with react
+  # https://github.com/facebook/react/issues/1791
+  setTimeout ->
+    cell.editor.focus()
+    cell.notebook.cell_focused.push cell
+  , 0

 OutputCellComponent = React.createIdentityClass
   displayName: 'OutputCellComponent'


Update the field value after the component initialization

--- lib/components/uploader/DropzoneComponent.coffee
+++ lib/components/uploader/DropzoneComponent.coffee
@@ -65,6 +65,11 @@
     # if there's any previous data, display it
     @populatePreviousUploads(data)

+    # set the initial value
+    setTimeout(=>
+      @updateFieldValue()
+    , 1000)
+

   ###
   Getter for the wrapped DropzoneJS instance


Store position as fabric.Point

Store position as fabric.Point

--- position.coffee
+++ position.coffee
@@ -1,5 +1,6 @@
 class Position

-  constructor: (@coordinate, @direction, @time) ->
+  constructor: (pos, @direction, @time) ->
+    @pos = new fabric.Point(pos)

 window.Position = Position


Fix quotes for lint

Fix quotes for lint

--- src/utils.coffee
+++ src/utils.coffee
@@ -51,7 +51,7 @@
         for key, value of elem
           o = {}

-          if @isPojo(value) || value instanceof Array || typeof value == "string"
+          if @isPojo(value) || value instanceof Array || typeof value == 'string'
             o[key] = @wrapObjects(if value instanceof Array then value else [value])
           else
             o[key] = value


OPCE3: Updated regexp

OPCE3: Updated regexp

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -1,24 +1,7 @@
 _ = require 'underscore'
-
-splitWord = word ->
-  word.match(/(^ +|)\W+ +/g)
-
-
-buildWord = (word, prepend='', append='') ->
-  tmp = word.match /(^ +|)\W+ +/g
-  tmp2 = []
-
-  if tmp.length == 1
-    tmp2.push (new Word (prepend+tmp.pop()+append))
-  else
-    tmp2.push (new Word (prepend+tmp.pop(0)))
-    while tmp.length > 1
-      tmp2.push (new Word (tmp.pop(0) + ' '))
-
-    tmp2.push (new Word (tmp.pop() + append + ' '))
-
-  tmp2.reverse()
-
+# XRegExp = require('xregexp').XRegExp
+WordRegExp = /\S+\s*/g
+PreSpacesRegExp = /^\s+/
 # Simple class that contains a word and links to the selections pointing
 # to it
 class Word
@@ -276,11 +259,19 @@
       throw new Error "Index out of bounds"
     @_rte.words[index].word = content

-  # Split the content around ' ' and append all the words created to the object
-  #
-  _push: (content) ->
-    for w in content.split(' ')
-      @_rte.words.push(w)
+  # Append new words at the end of the word list
+  # @param [String] content the string to append
+  push: (content) ->
+    console.log '"' + content + '"'
+    preSpaces = content.match PreSpacesRegExp
+    if preSpaces isnt null
+      @_rte.words.push(new Word (preSpaces[0]))
+    words = content.match WordRegExp
+    console.log '"' + preSpaces + '"'
+    console.log words
+    if _.isArray(words)
+      for w in words
+        @_rte.words.push (new Word w)

   # Insert words at position
   #
@@ -394,8 +385,8 @@
     index = sel.startPos.word   #position to work from
     pos = sel.startPos.pos

-    wordsToInsert = content.match /\w+ +/g
-    preSpaces = content.search /^ +/
+    wordsToInsert = content.match WordRegExp
+    preSpaces = content.search PreSpacesRegExp

     currWord = @getWord index

@@ -423,12 +414,14 @@
         if index == 0
           emptyWord = new Word content.substring(0, preSpaces)
           @insertWord 0, emptyWord
+          index = 1
         else
           # update previous word
           prevWord = @getWord (index-1)
           prevWord += content.substring(0, preSpaces)
           @setWord (index-1), prevWord

+    @insertWords index, wordsToInsert

   split: (n) ->
     word = @getWord n


Make specs comply to the new cache storage contract

--- spec/native-compile-cache-spec.coffee
+++ spec/native-compile-cache-spec.coffee
@@ -4,12 +4,12 @@

   beforeEach ->
     cachedFiles = []
-    fakeCacheStorage = jasmine.createSpyObj("cache storage", ["set", "get"])
+    fakeCacheStorage = jasmine.createSpyObj("cache storage", ["set", "get", "has"])
     nativeCompileCache.setCacheStorage(fakeCacheStorage)
     nativeCompileCache.install()

   it "writes and reads from the cache storage when requiring files", ->
-    fakeCacheStorage.get.andReturn(null)
+    fakeCacheStorage.has.andReturn(false)
     fakeCacheStorage.set.andCallFake (filename, cacheBuffer) ->
       cachedFiles.push({filename, cacheBuffer})

@@ -28,6 +28,7 @@
     expect(cachedFiles[1].cacheBuffer.length).toBeGreaterThan(0)
     expect(fn2()).toBe(2)

+    fakeCacheStorage.has.andReturn(true)
     fakeCacheStorage.get.andReturn(cachedFiles[0].cacheBuffer)
     fakeCacheStorage.set.reset()


Add Davis mutex

Add Davis mutex

--- js/hocr-reader.coffee
+++ js/hocr-reader.coffee
@@ -61,52 +61,57 @@
     nav_bar.append($('<a>',class:'nav_right').attr('href',"/hocr-reader/#/read/#{user}/#{repo}/#{format_page(page + 1)}").text('Next'))
   $('.header').append(nav_bar)

+hocr_handler_mutex = 0
+
 hocr_handler = (req) ->
-  console.log(req.params)
-  $(document.body).empty()
-  container = $('<div>', class: 'container')
-  header = $('<div>', class: 'header')
-  content = $('<div>', class: 'content')
-  footer = $('<div>', class: 'footer')
-  container.append(header)
-  container.append(content)
-  container.append(footer)
-  $(document.body).append(container)
-  repo = github.getRepo(req.params['github_user'],req.params['github_repo'])
-  repo.getTree 'master', (err, tree) ->
-    book = _.filter(tree, (node) -> (node.type == 'tree' && node.path.match(/\.book$/)))[0]
-    page = format_page(req.params['page'])
-    if book
-      repo.getTree book.sha, (err, book_tree) ->
-        all_pages = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p[0-9]+\.html$")))
-        make_nav_bar(req.params['github_user'],req.params['github_repo'],parseInt(page),all_pages.length)
-        page_image = _.filter(book_tree, (node) -> node.path.match(new RegExp("^i#{page}\.jpg$")))[0]
-        page_hocr = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p#{page}\.html$")))[0]
-        console.log page_image
-        $('.content').append($('<div>').attr('id','page_image'))
-        $('.content').append($('<div>').attr('id','page_right'))
-        $.ajax page_image.url + (if get_cookie('access_token') then "?access_token=#{get_cookie('access_token')}" else ''),
-          type: 'GET'
-          dataType: 'json'
-          crossDomain: 'true'
-          error: (jqXHR, textStatus, errorThrown) ->
-            console.log "Image fetch error: #{textStatus}"
-          success: (data) ->
-            $('#page_image').append($('<img>').attr('style','width:100%').attr('src','data:image/jpeg;charset=utf-8;base64,'+data.content))
-        console.log page_hocr
-        $.ajax page_hocr.url + (if get_cookie('access_token') then "?access_token=#{get_cookie('access_token')}" else ''),
-          type: 'GET'
-          dataType: 'json'
-          crossDomain: 'true'
-          error: (jqXHR, textStatus, errorThrown) ->
-            console.log "hOCR fetch error: #{textStatus}"
-          success: (data) ->
-            hocr_html = atob(decodeURIComponent(escape(data.content.replace(/\s/g, ""))))
-            css_rewrite = if hocr_html.match('<link')
-              hocr_html.replace('http://heml.mta.ca/Rigaudon/hocr.css','{{ site.url }}/hocr.css')
-            else
-              hocr_html.replace('<head>',"<head>\n<link rel='stylesheet' type='text/css' href='{{ site.url }}/hocr.css'>")
-            $('#page_right').append($('<iframe>').attr('style','width:100%').attr('height','800').attr('frameBorder','0').attr('src','data:text/html;charset=utf-8;base64,'+btoa(css_rewrite)))
+  page = format_page(req.params['page'])
+  # mutex is needed here to prevent multiple Davis onLoad calls in e.g. Safari
+  unless hocr_handler_mutex == page
+    hocr_handler_mutex = page
+    console.log(req.params)
+    $(document.body).empty()
+    container = $('<div>', class: 'container')
+    header = $('<div>', class: 'header')
+    content = $('<div>', class: 'content')
+    footer = $('<div>', class: 'footer')
+    container.append(header)
+    container.append(content)
+    container.append(footer)
+    $(document.body).append(container)
+    repo = github.getRepo(req.params['github_user'],req.params['github_repo'])
+    repo.getTree 'master', (err, tree) ->
+      book = _.filter(tree, (node) -> (node.type == 'tree' && node.path.match(/\.book$/)))[0]
+      if book
+        repo.getTree book.sha, (err, book_tree) ->
+          all_pages = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p[0-9]+\.html$")))
+          make_nav_bar(req.params['github_user'],req.params['github_repo'],parseInt(page),all_pages.length)
+          page_image = _.filter(book_tree, (node) -> node.path.match(new RegExp("^i#{page}\.jpg$")))[0]
+          page_hocr = _.filter(book_tree, (node) -> node.path.match(new RegExp("^p#{page}\.html$")))[0]
+          console.log page_image
+          $('.content').append($('<div>').attr('id','page_image'))
+          $('.content').append($('<div>').attr('id','page_right'))
+          $.ajax page_image.url + (if get_cookie('access_token') then "?access_token=#{get_cookie('access_token')}" else ''),
+            type: 'GET'
+            dataType: 'json'
+            crossDomain: 'true'
+            error: (jqXHR, textStatus, errorThrown) ->
+              console.log "Image fetch error: #{textStatus}"
+            success: (data) ->
+              $('#page_image').append($('<img>').attr('style','width:100%').attr('src','data:image/jpeg;charset=utf-8;base64,'+data.content))
+          console.log page_hocr
+          $.ajax page_hocr.url + (if get_cookie('access_token') then "?access_token=#{get_cookie('access_token')}" else ''),
+            type: 'GET'
+            dataType: 'json'
+            crossDomain: 'true'
+            error: (jqXHR, textStatus, errorThrown) ->
+              console.log "hOCR fetch error: #{textStatus}"
+            success: (data) ->
+              hocr_html = atob(decodeURIComponent(escape(data.content.replace(/\s/g, ""))))
+              css_rewrite = if hocr_html.match('<link')
+                hocr_html.replace('http://heml.mta.ca/Rigaudon/hocr.css','{{ site.url }}/hocr.css')
+              else
+                hocr_html.replace('<head>',"<head>\n<link rel='stylesheet' type='text/css' href='{{ site.url }}/hocr.css'>")
+              $('#page_right').append($('<iframe>').attr('style','width:100%').attr('height','800').attr('frameBorder','0').attr('src','data:text/html;charset=utf-8;base64,'+btoa(css_rewrite)))

 hocr_reader = (req) ->
   check_rate_limit(hocr_handler, req)
@@ -197,6 +202,8 @@
       $(document.body).append(results_ul)

 davis_app = Davis ->
+  this.configure ->
+    this.generateRequestOnPageLoad = true
   this.get '/', no_repo
   this.get '/hocr-reader/', no_repo
   this.get '/hocr-reader/#/read/:github_user/:github_repo', (req) ->
@@ -221,4 +228,3 @@
     hocr_reader_github_oauth['code'] = query_params.split('=')[1].split('&')[0]

   davis_app.start()
-  Davis.location.assign(new Davis.Request("#{window.location.pathname}#{window.location.hash}"))


Build: Login app taken to main bundle

Build: Login app taken to main bundle

...since we have login form on the home page

--- projects.coffee
+++ projects.coffee
@@ -159,6 +159,7 @@
     projects      : [
                      'KDBackend'
                      'Core'
+                     'Login'
                      'Home'
                      'KDMainApp'
                      'Activity'


beers/last is decrepitated, changed to beers/

--- src/handlers/frontpage/IndexHandler.coffee
+++ src/handlers/frontpage/IndexHandler.coffee
@@ -23,7 +23,7 @@
 					results: body.beers

 	onRequest: (request, reply) ->
-		@api.beers.get { partial: 'last', limit: 10 }, (err, res, body) =>
+		@api.beers.get { partial: '', limit: 10 }, (err, res, body) =>
 			reply @render(err, res, body, request.user)
 		, request.token


Prepare for merge

Prepare for merge

--- app/scripts/scenes/GameplayDemo.js.coffee
+++ app/scripts/scenes/GameplayDemo.js.coffee
@@ -10,92 +10,92 @@
     title: 'Gameplay Demo'

   level.addBlock('Generic.Start')
-  #level.addBlock 'Generic.Event',
-    #enter: ->
-      #@level.setForcedSpeed(2)
+  level.addBlock 'Generic.Event',
+    enter: ->
+      @level.setForcedSpeed(2)

-  #level.addBlock('GameplayDemo.PerspectiveTest')
-  #level.addBlock('GameplayDemo.PerspectiveTest')
-  #level.addBlock 'Generic.Event',
-    #enter: ->
-      #@level.setForcedSpeed(1)
+  level.addBlock('GameplayDemo.PerspectiveTest')
+  level.addBlock('GameplayDemo.PerspectiveTest')
+  level.addBlock 'Generic.Event',
+    enter: ->
+      @level.setForcedSpeed(1)

-  #level.addBlock('Generic.Dialog', {
-    #dialog: [
-      #has: ['Player 1'],
-      #name: 'John',
-      #lines: [
-        #'Welcome to the gameplay demo!'
-        #'Let\'s do some target practice!'
-      #]
-    #,
-      #has: ['Player 1', 'Player 2'],
-      #name: 'Jim',
-      #lines: [
-        #'Yeah let\'s shoot stuff!'
-      #]
-    #,
-      #only: ['Player 2'],
-      #name: 'Jim',
-      #lines: [
-        #'Woohoo target practice!'
-        #'Kill kill kill!!!'
-      #]
-    #]
-  #})
-  #level.addBlock('GameplayDemo.Asteroids')
-  #level.addBlock('Generic.Dialog', {
-    #dialog: [
-      #only: ['Player 1'],
-      #name: 'John',
-      #lines: [
-        #'Entering the tunnel!'
-      #]
-    #,
-      #has: ['Player 2'],
-      #name: 'Jim',
-      #lines: [
-        #'Prepare for darkness!!!'
-      #]
-    #]
-  #})
-  #level.addBlock('GameplayDemo.TunnelStart')
-  #level.addBlock('GameplayDemo.Tunnel')
-  #level.addBlock('Generic.Dialog', {
-    #triggerOn: 'enter'
-    #dialog: [
-      #has: ['Player 1'],
-      #name: 'John',
-      #lines: [
-        #'Slow down! Lasers!!'
-      #]
-    #,
-      #only: ['Player 2'],
-      #name: 'Jim',
-      #lines: [
-        #'Uhoh! Danger!'
-      #]
-    #]
-  #})
-  #level.addBlock 'Generic.Event', enter: ->
-    #@level.setForcedSpeed(0)
+  level.addBlock('Generic.Dialog', {
+    dialog: [
+      has: ['Player 1'],
+      name: 'John',
+      lines: [
+        'Welcome to the gameplay demo!'
+        'Let\'s do some target practice!'
+      ]
+    ,
+      has: ['Player 1', 'Player 2'],
+      name: 'Jim',
+      lines: [
+        'Yeah let\'s shoot stuff!'
+      ]
+    ,
+      only: ['Player 2'],
+      name: 'Jim',
+      lines: [
+        'Woohoo target practice!'
+        'Kill kill kill!!!'
+      ]
+    ]
+  })
+  level.addBlock('GameplayDemo.Asteroids')
+  level.addBlock('Generic.Dialog', {
+    dialog: [
+      only: ['Player 1'],
+      name: 'John',
+      lines: [
+        'Entering the tunnel!'
+      ]
+    ,
+      has: ['Player 2'],
+      name: 'Jim',
+      lines: [
+        'Prepare for darkness!!!'
+      ]
+    ]
+  })
+  level.addBlock('GameplayDemo.TunnelStart')
+  level.addBlock('GameplayDemo.Tunnel')
+  level.addBlock('Generic.Dialog', {
+    triggerOn: 'enter'
+    dialog: [
+      has: ['Player 1'],
+      name: 'John',
+      lines: [
+        'Slow down! Lasers!!'
+      ]
+    ,
+      only: ['Player 2'],
+      name: 'Jim',
+      lines: [
+        'Uhoh! Danger!'
+      ]
+    ]
+  })
+  level.addBlock 'Generic.Event', enter: ->
+    @level.setForcedSpeed(0)

-  #level.addBlock('GameplayDemo.Lasers')
-  #level.generateBlocks(amount: 1)
-  #level.addBlock('Generic.Dialog', {
-    #dialog: [
-      #has: ['Player 1'],
-      #name: 'John',
-      #lines: [
-        #'Let\'s get out of here!'
-      #]
-    #]
-  #})
-  #level.addBlock 'Generic.Event', inScreen: ->
-    #@level.setForcedSpeed(4)
-  #level.addBlock('GameplayDemo.Tunnel', only: ['cleared'])
-  #level.generateBlocks(amount: 2, only: ['cleared'])
-  #level.generateBlocks(stopBefore: 'GameplayDemo.TunnelEnd')
+  level.addBlock('GameplayDemo.Lasers')
+  level.generateBlocks(amount: 1)
+  level.addBlock('Generic.Dialog', {
+    dialog: [
+      has: ['Player 1'],
+      name: 'John',
+      lines: [
+        'Let\'s get out of here!'
+      ]
+    ]
+  })
+  level.addBlock 'Generic.Event', inScreen: ->
+    @level.setForcedSpeed(4)
+  level.addBlock('GameplayDemo.Tunnel', only: ['cleared'])
+  level.generateBlocks(amount: 2, only: ['cleared'])
+  level.generateBlocks(stopBefore: 'GameplayDemo.TunnelEnd')
   level.addBlock('GameplayDemo.TunnelEnd', only: ['cleared'])
   level.addBlock 'Generic.Event',
     enter: ->


Add explicitness for clarity.

Add explicitness for clarity.

--- src/views.coffee
+++ src/views.coffee
@@ -278,24 +278,22 @@
             'click .close-button': 'close'
         initialize: (options) ->
             @navigation_layout = options.layout
-            @active = null
         onShow: ->
             @search.show new SearchInputView()
             @browse.show new BrowseButtonView()
         open: (event) ->
             action_type = $(event.currentTarget).data('type')
-            @active = action_type
-
             @update_classes action_type
             if action_type is 'search'
                 @$el.find('input').select()
-                action_type = null
-            @navigation_layout.change action_type
+                @navigation_layout.change null
+            else
+                @navigation_layout.change action_type
         close: (event) ->
             event.preventDefault()
             event.stopPropagation()
             header_type = $(event.target).closest('.header').data('type')
-            @update_classes()
+            @update_classes null

             # Clear search query if search is closed.
             if header_type is 'search'


Show ThangType original in Thang editor for easy reference

--- app/views/editor/thang/ThangTypeEditView.coffee
+++ app/views/editor/thang/ThangTypeEditView.coffee
@@ -568,6 +568,8 @@
     data = @getThangData()
     schema = _.cloneDeep ThangType.schema
     schema.properties = _.pick schema.properties, (value, key) => not (key in ['components'])
+    delete schema.properties.original.format  # Don't hide this, we often want to see it here
+    schema.properties.original.readOnly = true
     options =
       data: data
       schema: schema


cleanup

cleanup

--- src/linter/used_module_finder/index.coffee
+++ src/linter/used_module_finder/index.coffee
@@ -22,7 +22,7 @@
   normalizeModules: (modules...) ->
     result = {}
     for {name, file, script} in _.flattenDeep(modules)
-      result[name] or= {name, files: [], scripts: []}
+      result[name] = {name, files: [], scripts: []} unless result[name]
       result[name].files.push file if file
       result[name].scripts.push script if script
     _.values result


fix(web): fixed undefined peer in onConversationOpen

--- apps/actor-web/app/services/actorService.coffee
+++ apps/actor-web/app/services/actorService.coffee
@@ -154,7 +154,7 @@
   onConversationOpen: (peer) ->
     console.log '[AW]ActorService onConversationOpen' #, peer
     @messenger.onConversationOpen peer
-    @$rootScope.$broadcast 'onConversationOpen' #, peer
+    @$rootScope.$broadcast 'onConversationOpen', peer
   onDialogsClosed: ->
     console.log '[AW]ActorService onDialogsClosed'
   onDialogsEnd: ->


better error handling

better error handling

--- src/youku.coffee
+++ src/youku.coffee
@@ -113,6 +113,10 @@
       try
         data = JSON.parse data
       catch parse_err
+        err = {
+          code: resp.statusCode
+          message: resp.statusMessage
+        }
         return callback err, data, resp

       if typeof data.error isnt 'undefined'


Made wait time configurable through environment variable.

--- tests.coffee
+++ tests.coffee
@@ -1,3 +1,8 @@
+if Meteor.isServer
+  __meteor_runtime_config__.WAIT_TIME = WAIT_TIME = process?.env?.WAIT_TIME or 500
+else
+  WAIT_TIME = __meteor_runtime_config__?.WAIT_TIME or 500
+
 Persons = new Meteor.Collection 'Persons', transform: (doc) => new Person doc
 Posts = new Meteor.Collection 'Posts', transform: (doc) => new Post doc
 UserLinks = new Meteor.Collection 'UserLinks', transform: (doc) => new UserLink doc
@@ -471,7 +476,7 @@
     # (triggered by person inserts, but pending) run afterwards, then they can patch things which
     # should in fact be done by source observers (on post), like setting usernames in post's
     # references to persons
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person1 = Persons.findOne @person1Id
@@ -525,7 +530,7 @@
         @postId = postId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -601,7 +606,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged together to better
     # test the code for multiple updates
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     Persons.update @person3Id,
@@ -628,7 +633,7 @@
     test.equal @person3.displayName, 'Person 3'

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -688,7 +693,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -737,7 +742,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -769,7 +774,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -828,7 +833,7 @@
         @circularSecondId = circularSecondId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -869,7 +874,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -898,7 +903,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -928,7 +933,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -958,7 +963,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -984,7 +989,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -1020,7 +1025,7 @@
         @circularFirstId = circularFirstId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -1054,7 +1059,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -1080,7 +1085,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update document
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @circularFirst = CircularFirsts.findOne @circularFirstId,
@@ -1118,7 +1123,7 @@
         @recursive2Id = recursive2Id

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1145,7 +1150,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1174,7 +1179,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1204,7 +1209,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1234,7 +1239,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1260,7 +1265,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive1 = Recursives.findOne @recursive1Id,
@@ -1288,7 +1293,7 @@
         @recursiveId = recursiveId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1309,7 +1314,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1331,7 +1336,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1349,7 +1354,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @recursive = Recursives.findOne @recursiveId,
@@ -1367,7 +1372,7 @@
         _id: 'nonexistent'

     # Sleep so that observers have time to update documents
-    Meteor._sleepForMs(1000)
+    Meteor._sleepForMs(WAIT_TIME)

     intercepted = Log._intercepted()

@@ -1384,7 +1389,7 @@
       subscribers: 'foobar'

     # Sleep so that observers have time to update documents
-    Meteor._sleepForMs(1000)
+    Meteor._sleepForMs(WAIT_TIME)

     intercepted = Log._intercepted()

@@ -1401,7 +1406,7 @@
       author: null

     # Sleep so that observers have time to update documents
-    Meteor._sleepForMs(1000)
+    Meteor._sleepForMs(WAIT_TIME)

     intercepted = Log._intercepted()

@@ -1418,7 +1423,7 @@
       user: null

     # Sleep so that observers have time to update documents
-    Meteor._sleepForMs(1000)
+    Meteor._sleepForMs(WAIT_TIME)

     intercepted = Log._intercepted()

@@ -1537,7 +1542,7 @@
         @postId = postId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -1600,7 +1605,7 @@
         @postLinkId = postLinkId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @postLink = PostLinks.findOne @postLinkId,
@@ -1631,7 +1636,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person2 = Persons.findOne @person2Id
@@ -1664,7 +1669,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @postLink = PostLinks.findOne @postLinkId,
@@ -1686,7 +1691,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @postLink = PostLinks.findOne @postLinkId,
@@ -1776,7 +1781,7 @@
         @postId = postId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -1840,7 +1845,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged together to better
     # test the code for multiple updates
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -1906,7 +1911,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged together to better
     # test the code for multiple updates
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
    (test, expect) ->
     @post = Posts.findOne @postId,
@@ -1972,7 +1977,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged together to better
     # test the code for multiple updates
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
    (test, expect) ->
     @post = Posts.findOne @postId,
@@ -2036,7 +2041,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -2095,7 +2100,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -2561,7 +2566,7 @@
         @identityGeneratorId = identityGeneratorId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -2597,7 +2602,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     if Meteor.isServer
@@ -2784,7 +2789,7 @@
         @postId = postId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -2917,7 +2922,7 @@
     # Sleep so that observers have time to update documents
     # so that persons updates are not merged together to better
     # test the code for multiple updates
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     Persons.update @person3Id,
@@ -2944,7 +2949,7 @@
     test.equal @person3.displayName, 'Person 3'

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -3067,7 +3072,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person1 = Persons.findOne @person1Id
@@ -3195,7 +3200,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person2 = Persons.findOne @person2Id
@@ -3313,7 +3318,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person3 = Persons.findOne @person3Id
@@ -3421,7 +3426,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person1 = Persons.findOne @person1Id
@@ -3530,7 +3535,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person2 = Persons.findOne @person2Id
@@ -3649,7 +3654,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @person3 = Persons.findOne @person3Id
@@ -3778,7 +3783,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -3901,7 +3906,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4024,7 +4029,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4147,7 +4152,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4269,7 +4274,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4388,7 +4393,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4512,7 +4517,7 @@
         test.isTrue res

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4637,7 +4642,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4703,7 +4708,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4733,7 +4738,7 @@
         test.isFalse error, error?.toString?() or error

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     @post = Posts.findOne @postId,
@@ -4755,7 +4760,7 @@
         @identityGeneratorId = identityGeneratorId

     # Sleep so that observers have time to update documents
-    Meteor.setTimeout expect(), 1000
+    Meteor.setTimeout expect(), WAIT_TIME
 ,
   (test, expect) ->
     if Meteor.isServer


Refacto builder

Refacto builder

--- assets/javascripts/builder/builder.coffee
+++ assets/javascripts/builder/builder.coffee
@@ -2,15 +2,26 @@
 Operators = require '../descriptors/operators'
 Roots = require '../descriptors/roots'

-buildArg = R.curryN 2, (operatorInspector, arg) ->
+buildObservable = ({root, operators}) ->
+  (operatorInspector) ->
+    buildRoot(operatorInspector)(root) +
+      R.join('', R.map(buildOperator(operatorInspector))(operators))
+
+buildRoot = R.curry (operatorInspector, {id, type, args}) ->
+  rootEvaluators[type](args)(id, operatorInspector)
+
+buildOperator = R.curry (operatorInspector, {id, type, args, observable}) ->
+  operatorEvaluators[type](args)(id, operatorInspector)
+
+buildArg = R.curry (operatorInspector, arg) ->
   if arg.functionDeclaration && arg.observable
-    arg.functionDeclaration + " " + buildCode(arg.observable)(operatorInspector) + "}"
+    arg.functionDeclaration + " " + buildObservable(arg.observable)(operatorInspector) + "}"
   else if arg.observable
-    buildCode(arg.observable)(operatorInspector)
+    buildObservable(arg.observable)(operatorInspector)
   else
     arg

-buildArgs = R.curryN 2, (operatorInspector, args) ->
+buildArgs = R.curry (operatorInspector, args) ->
   R.mapIndexed( (argType, index) ->
     if args[index]
       buildArg(operatorInspector)(args[index])
@@ -40,15 +51,5 @@
       })")
   )(Operators)

-evalRoot = R.curryN 2, (operatorInspector, {id, type, args}) ->
-  rootEvaluators[type](args)(id, operatorInspector)

-evalOperator = R.curryN 2, (operatorInspector, {id, type, args, observable}) ->
-  operatorEvaluators[type](args)(id, operatorInspector)
-
-buildCode = ({root, operators}) ->
-  (operatorInspector) ->
-    evalRoot(operatorInspector)(root) +
-      R.join('', R.map(evalOperator(operatorInspector))(operators))
-
-module.exports = buildCode
+module.exports = buildObservable


Social: disable follow button

Social: disable follow button

--- client/Social/Activity/sidebar/topicitem.coffee
+++ client/Social/Activity/sidebar/topicitem.coffee
@@ -7,16 +7,16 @@
     {name, typeConstant, participantCount}    = data

     # rewrite grop channel as topic?
-    typeConstant            = "topic" if typeConstant is "group"
+    firstRoute              = if typeConstant is "group" then "topic" else typeConstant
     # uppercase first letter of the type constant for route
-    typeConstant            = "#{typeConstant.charAt(0).toUpperCase()}#{typeConstant.slice 1}"
-    # build route with typeConstant and name
-    options.route           = "#{typeConstant}/#{name}"
+    firstRoute              = "#{firstRoute.charAt(0).toUpperCase()}#{firstRoute.slice 1}"
+    # build route with firstRoute and name
+    options.route           = "#{firstRoute}/#{name}"
     options.cssClass        = 'clearfix'

     super options, data

-    @followButton = if name is 'public'
+    @followButton = if typeConstant in ['group', 'announcement']
     then new KDCustomHTMLView tagName : 'span'
     else new TopicFollowButton {}, @getData()


resurrection.activity: convert newlines with break-line elements on status editing

--- client/Social/Activity/views/inputwidget.coffee
+++ client/Social/Activity/views/inputwidget.coffee
@@ -86,9 +86,10 @@

   edit: (activity) ->
     @setData activity
-    @input.setContent activity.body, activity
     if activity.link
       @embedBox.loadEmbed activity.link.link_url
+    content = activity.body.replace /\n/g, "<br>"
+    @input.setContent content, activity
     @submit.setTitle "Update"

   reset: ->


Activity: code styling minor

Activity: code styling minor

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -15,11 +15,10 @@
       self          : 0
       body          : 0

-    {itemClass, type, lastToFirst, wrapper} = @getOptions()
-    {typeConstant}    = @getData()
-
-    {channelId} = options
+    {itemClass, type, lastToFirst, wrapper, channelId} = @getOptions()
+    {typeConstant} = @getData()
     viewOptions = {itemOptions: {channelId}}
+
     @listController = new ActivityListController { wrapper, itemClass, type: typeConstant, viewOptions, lastToFirst}

     @createInputWidget()
@@ -28,12 +27,10 @@
     @once 'ChannelReady', @bound 'bindChannelEvents'
     socialapi.onChannelReady data, @lazyBound 'emit', 'ChannelReady'

-    @on 'LazyLoadThresholdReached', @bound 'lazyLoad'  if typeConstant in ['group', 'topic']
-
-    {windowController} = KD.singletons
-    windowController.addFocusListener (focused) =>
-
-      @glance()  if focused and @active
+    if typeConstant in ['group', 'topic']
+      @on 'LazyLoadThresholdReached', @bound 'lazyLoad'
+
+    KD.singletons.windowController.addFocusListener @bound 'handleFocus'

     switch typeConstant
       when 'post'
@@ -43,6 +40,10 @@
       else
         @listController.getListView().on 'ItemWasAdded', @bound 'scrollUp'

+
+  handleFocus: (focused) -> @glance()  if focused and @active
+
+
   scrollDown: (item) ->

     return  unless @active


activity: fix channel post regression

activity: fix channel post regression

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -107,7 +107,7 @@

   putMessage: (message, index = 0) ->
-    @mostRecent.listController.addItem message, index
+    @listController.addItem message, index

   createFakeItemView: (value, clientRequestId) ->


Handle scrolling past the section

Handle scrolling past the section

--- client/apps/edit/components/layout/index.coffee
+++ client/apps/edit/components/layout/index.coffee
@@ -66,10 +66,13 @@
     $section = @$('.edit-section-container[data-state-editing=true]')
     return unless $section.length
     $controls = $section.find('.edit-section-controls')
-    scrolledPast = @$window.scrollTop() + @$('#edit-header').outerHeight() >
+    insideComponent = @$window.scrollTop() + @$('#edit-header').outerHeight() >
       ($section.offset().top or 0) - $controls.height()
+    if (@$window.scrollTop() + $controls.outerHeight() >
+        $section.offset().top + $section.height())
+      insideComponent = false
     left = ($controls.width() / 2) - ($('#layout-sidebar').width() / 2)
     $controls.css(
-      width: if scrolledPast then $controls.width() else ''
-      left: if scrolledPast then "calc(50% - #{left}px)" else ''
-    ).attr('data-fixed', scrolledPast)+      width: if insideComponent then $controls.width() else ''
+      left: if insideComponent then "calc(50% - #{left}px)" else ''
+    ).attr('data-fixed', insideComponent)


disabling the page for later usage

disabling the page for later usage

--- client/landing/app/MainApp/book/bookdata.coffee
+++ client/landing/app/MainApp/book/bookdata.coffee
@@ -297,7 +297,7 @@

Best of all, you get referral rewards for sharing Koding with others. So what do you say, share Koding today!

                 """
     section   : -1
-    version   : 1.1
+    # version   : 1.1 # enable later
   ,
     title     : "Enjoy!"
     content   : """book and illustrations by @petorial


fixed one translation

fixed one translation

--- t9n/pt_PT.coffee
+++ t9n/pt_PT.coffee
@@ -70,7 +70,7 @@
       "Email already exists.": "O e-mail já existe."
       "Email doesn't match the criteria.": "E-mail inválido."
       "Invalid login token": "Token de início de sessão inválido"
-      "Login forbidden": "Início de sessão proibido"
+      "Login forbidden": "Início de sessão impedido"
     #"Service " + options.service + " already configured"
       "Service unknown": "Serviço desconhecido"
       "Unrecognized options for login request": "Pedido de início de sessão com opções não reconhecidas"


Fixed langiage code

Fixed langiage code

all other similar cases are using `_` instead of `-`.
So it should be `zn_ch` and not `zn-ch`.

--- t9n/zh_cn.coffee
+++ t9n/zh_cn.coffee
@@ -111,4 +111,4 @@
       "Unknown error": "未知错误"

-T9n.map "zh-cn", zh_cn
+T9n.map "zh_cn", zh_cn


Removed console.log

Removed console.log

--- test/args.coffee
+++ test/args.coffee
@@ -62,7 +62,6 @@
             args = generate_args aliace, type
             webp = (new Webp filename)[name] args...
             write(webp, 'out.json').then (data) ->
-              console.log data
               should(data._[0]).be.equal filename
               if type is 'boolean'
                 should(data[key]).be.equal true


Blender internal: Prevent variable 'v' leaking into global scope.

Possible performance improvement by doing that.

--- engine/material_shaders/blender_internal.coffee
+++ engine/material_shaders/blender_internal.coffee
@@ -261,9 +261,9 @@
                         u.type>>16, u.type&0xffff, "of data type", \
                         ['0','1i','1f','2f','3f','4f',
                         'm3','m4','4ub'][u.datatype]
-        preamble = 'var locations=shader.uniform_locations, lamps=shader.lamps,
-            material=shader.material, inputs=material._input_list,
-            tex_list=material._texture_list;\n'
+        preamble = 'var v, locations=shader.uniform_locations,
+            lamps=shader.lamps, material=shader.material,
+            inputs=material._input_list, tex_list=material._texture_list;\n'
         func = new Function 'gl', 'shader', 'ob', 'render', 'mat4',
             preamble+code.join '\n'
         {uniform_assign_func: func, uniform_locations: locations, lamps}


cleanup button test

cleanup button test

--- src/test/test_render.coffee
+++ src/test/test_render.coffee
@@ -105,16 +105,10 @@
   test "render controls", (t) ->
     t.ok controls, 'controls'
     body = render_mock(controls).body
-
-    t.ok col_tags = body.body, 'extract cols'
-
-    t.ok col = col_tags[0], "first col"
-    t.equal col.tag, 'span', 'col tag'
-
     t.equal body.tag, 'div', 'body.tag'
     t.equal get_label(body), controls._LABEL, 'controls label'
+
     button = body.body[0]
     t.equal button.tag, 'span', 'button span'
-    console.log 'button', button
     t.end()


Use TextBuffer::getEndPosition

Use TextBuffer::getEndPosition

--- src/tokenized-buffer.coffee
+++ src/tokenized-buffer.coffee
@@ -286,7 +286,7 @@
     position

   findClosingBracket: (startBufferPosition) ->
-    range = [startBufferPosition, @buffer.getEofPosition()]
+    range = [startBufferPosition, @buffer.getEndPosition()]
     position = null
     depth = 0
     @iterateTokensInBufferRange range, (token, startPosition, { stop }) ->


Update uk.coffee (#5573)

Update uk.coffee (#5573)


Client: Activity: Added leaveChannel functionality to channel thread pane context menu 'leave channel' link

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -88,6 +88,11 @@

   leaveChannel: ->

+    { unfollowChannel } = ActivityFlux.actions.channel
+    channelId   = @state.channelThread.get 'channelId'
+
+    unfollowChannel channelId
+

   updatePurpose: ->


closes #337

closes #337

--- src/viz/types/helpers/graph/includes/svg.coffee
+++ src/viz/types/helpers/graph/includes/svg.coffee
@@ -162,10 +162,10 @@
         if vars[axis].ticks.visible.indexOf(d) >= 0 then y2 else y2/2

     groups.select("text")
-        .attr "dy", ""
         .style "text-anchor", if rotated and axis is "x" then "end" else if rotated then "start" else "middle"
         .call tickFont, axis
         .each (d) ->
+          d3.select(this).attr "dy", "0px"
           d = +d if d.constructor is Date
           if !vars[axis].ticks.hidden and vars[axis].ticks.visible.indexOf(d) >= 0
             textwrap()


Remove non-functional component

Remove non-functional component

--- components/Timeline.coffee
+++ components/Timeline.coffee
@@ -1,88 +0,0 @@
-noflo = require 'noflo'
-ease = null
-ease = require 'ease-component'
-
-class Timeline extends noflo.Component
-  description: 'Timeline with multiple tracks and keyframes'
-  icon: 'clock-o'
-  constructor: ->
-    @inPorts = new noflo.InPorts
-      # Tracks array of objects with name, group, keyframes
-      # keyframes array of objects with time, value, easing
-      tracks:
-        datatype: 'array'
-        description: 'configuration array of tracks and keyframes'
-      play:
-        datatype: 'bang'
-      pause:
-        datatype: 'bang'
-      stop:
-        datatype: 'bang'
-      length:
-        datatype: 'number'
-        description: 'length in ms of timeline'
-        value: 3000
-      loop:
-        datatype: 'boolean'
-      time:
-        datatype: 'number'
-        description: 'skip to time'
-      percent:
-        datatype: 'number'
-        description: 'skip to percent, 0-1'
-      speed:
-        datatype: 'number'
-        description: '1.0 = normal speed'
-        default: 1
-      fps:
-        datatype: 'number'
-        description: 'maximum fps of animation loop'
-        default: 60
-
-    @outPorts = new noflo.OutPorts
-      out:
-        datatype: 'number'
-        addressable: true
-      play:
-        datatype: 'bang'
-      pause:
-        datatype: 'bang'
-      stop:
-        datatype: 'bang'
-      end:
-        datatype: 'bang'
-        description: 'fired when end is reached (or loops)'
-      time:
-        datatype: 'number'
-
-    playing = false
-    now = null
-    percent = null
-    time = null
-    lastTime = null
-    fps = 60
-    frame = 1000/fps
-    raf = null
-
-    animate = () ->
-      raf = requestAnimationFrame animate
-      now = Date.now()
-      if now-lastTime >= frame
-        lastTime = now
-
-    @inPorts.play.on 'data', () ->
-      raf = requestAnimationFrame animate
-
-    @inPorts.pause.on 'data', () ->
-      cancelAnimationFrame raf
-      playing = false
-
-    @inPorts.stop.on 'data', () ->
-      cancelAnimationFrame raf
-      playing = false
-      time = 0
-
-  getEasing: (name) ->
-    return ease[name]
-
-exports.getComponent = -> new Timeline


Fixed typo

Fixed typo

--- components/form/index.cjsx
+++ components/form/index.cjsx
@@ -2,7 +2,7 @@
 @todo
 ###

-require './style'
+require "./style"
 Autocomplete  = require "../autocomplete"
 Button        = require "../button"
 Input         = require "../input"
@@ -26,6 +26,10 @@
   getInitialState: ->
     attributes        : if @props.storage then do @storage else @props.attributes

+  # -- Lifecycle
+  componentWillReceiveProps: (next_props) ->
+    @setValue next_props.attributes if next_props.attributes
+
   # -- Events
   onSubmit: (event) ->
     event.preventDefault()
@@ -34,7 +38,7 @@
   onChange: (event) ->
     is_valid = true
     value = @getValue()
-    for attr in @props.attributes when attr.required and value[attr.ref]?.trim() is ""
+    for attr in @state.attributes when attr.required and value[attr.ref]?.trim() is ""
       is_valid = false
       @refs[attr.ref].setError? "Required field"
       break
@@ -53,7 +57,7 @@
     <form data-component-form className={@props.className}
           onSubmit={@onSubmit} onChange={@onChange}>
       {
-        for attribute, index in @props.attributes
+        for attribute, index in @state.attributes
           if attribute.type is "submit"
             <Button {...attribute} type="square" ref="submit" onClick={@onSubmit}/>
           else if attribute.type is "autocomplete"
@@ -81,5 +85,5 @@
     value[ref] = el.getValue() for ref, el of @refs when el.getValue?
     value

-  setValue: (data) ->
-    @setState attributes: data
+  setValue: (data = {}) ->
+    @refs[field.ref].setValue? field.value for field in data


Add the channel id to the _id property of the messages

--- packages/rocketchat-importer-slack/server.coffee
+++ packages/rocketchat-importer-slack/server.coffee
@@ -204,7 +204,7 @@
 								@updateRecord { 'messagesstatus': "#{channel}/#{date}.#{msgs.messages.length}" }
 								for message in msgs.messages
 									msgDataDefaults =
-										_id: "S#{message.ts}"
+										_id: "#{slackChannel.id}.S#{message.ts}"
 										ts: new Date(parseInt(message.ts.split('.')[0]) * 1000)

 									if message.type is 'message'


Added RangeSet#foreach

Added RangeSet#foreach

--- rangeset.coffee
+++ rangeset.coffee
@@ -6,6 +6,11 @@

   add: (x) ->
     @arr[x] = 1
+
+  foreach: (func) ->
+    for val, i in @arr
+      if val
+        func(i)

   toString: ->
     closeRange = ->


Remove redundant 's' binding

Remove redundant 's' binding

--- keymaps/vim-mode.cson
+++ keymaps/vim-mode.cson
@@ -42,7 +42,6 @@
   'D': 'vim-mode:delete-to-last-character-of-line'
   'c': 'vim-mode:change'
   'C': 'vim-mode:change-to-last-character-of-line'
-  's': 'vim-mode:change'
   'S': 'vim-mode:substitute-line'
   'r': 'vim-mode:replace'
   'I': 'vim-mode:insert-at-beginning-of-line'


components: ListenProperty: robustify

components: ListenProperty: robustify

--- components/ListenProperty.coffee
+++ components/ListenProperty.coffee
@@ -28,6 +28,7 @@
         required: no

     @inPorts.readback.on 'data', (@readback) =>
+      @sendOutputs() if @readback
     @inPorts.object.on 'data', (object) =>
       @updateListener object, @property
     @inPorts.property.on 'data', (property) =>
@@ -37,11 +38,13 @@
     @disconnectListener()
     @object = object
     @property = property
+    return unless @object? and @property?
     @listener = @object.connect "notify::#{@property}", () =>
       @sendOutputs()
     @sendOutputs() if @readback

   sendOutputs: () ->
+    return unless @object? and @property?
     @outPorts.object.send @object
     @outPorts.value.send @object[@property]
     @outPorts.object.disconnect()


fix layout template naming

fix layout template naming

--- app/templates/app/scripts/app.coffee
+++ app/templates/app/scripts/app.coffee
@@ -18,13 +18,13 @@
     # Helper for using layouts.
     useLayout: (name) ->
       # If already using this Layout, then don't re-inject into the DOM.
-      return @layout  if @layout and @layout.options.template is name
+      return @layout  if @layout and @layout.options.template is "layouts/#{name}"
       # If a layout already exists, remove it from the DOM.
       @layout.remove()  if @layout
       # Create a new Layout.
       layout = new Backbone.Layout
-        template: name
-        className: "layout menu-container " + name
+        template: "layouts/#{name}"
+        className: "layout #{name}"
         id: "layout"
       # Insert into the DOM.
       $("#main").empty().append layout.el


Remove fix for a weird browserify issue in Chrome Canary

--- app/resources/main.coffee
+++ app/resources/main.coffee
@@ -1,9 +1,3 @@
-# WTF: Why aren't these being resolved?
-require 'zooniverse/views/zooniverse-logo-svg'
-require 'zooniverse/views/group-icon-svg'
-require 'zooniverse/views/language-icon-svg'
-require 'zooniverse/views/mail-icon-svg'
-
 window.jQuery = require 'jquery'

 Project = require '../../src/project'


Server: Tests: replace daisy in confirm handler test file

--- servers/lib/server/handlers/confirm.test.coffee
+++ servers/lib/server/handlers/confirm.test.coffee
@@ -1,4 +1,4 @@
-{ daisy
+{ async
   expect
   request
   generateRandomString
@@ -72,7 +72,7 @@

       queue = [

-        ->
+        (next) ->
           confirmRequestParams = generateConfirmRequestParams
             qs : { token : jwtToken }

@@ -80,21 +80,18 @@
             expect(err).to.not.exist
             expect(res.statusCode).to.be.equal 200
             expect(body).not.to.be.empty
-            queue.next()
+            next()

-
-        ->
+        (next) ->
           # expecting a session to be created
           JSession.one { username, groupName }, (err, session) ->
             expect(err).to.not.exist
             expect(session).to.exist
-            queue.next()
-
-        -> done()
+            next()

       ]

-      daisy queue
+      async.series queue, done

 beforeTests()


Use a more significative size change in tests

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -132,8 +132,8 @@
       describe 'when the editor is resized to a greater size', ->
         beforeEach ->
           height = editor.getHeight()
-          editorElement.style.width = '300px'
-          editorElement.style.height = '300px'
+          editorElement.style.width = '100%'
+          editorElement.style.height = '500px'

           waitsFor -> editor.getHeight() isnt height


Update string scoring to be case insensitive

--- assets/javascripts/string_score.js.coffee
+++ assets/javascripts/string_score.js.coffee
@@ -1,5 +1,6 @@
 String::score = (abbreviation) ->
-  string = @
+  string = @toLowerCase()
+  abbreviation = abbreviation.toLowerCase()

   return 1 if string == abbreviation


SocialWorker: Tests: remove unused require statement

--- workers/social/lib/social/models/computeproviders/koding.test.coffee
+++ workers/social/lib/social/models/computeproviders/koding.test.coffee
@@ -7,8 +7,7 @@
   generateRandomString
   checkBongoConnectivity }  = require '../../../../testhelper'
 { fetchUserPlan }           = require './computeutils'
-{ createUserAndMachine
-  fetchMachinesByUsername } = require \
+{ fetchMachinesByUsername } = require \
   '../../../../testhelper/models/computeproviders/machinehelper'
 { createSnapshot }          = require \
   '../../../../testhelper/models/computeproviders/snapshothelper'


put t method in window

put t method in window

--- app/src/utils/i18n.coffee
+++ app/src/utils/i18n.coffee
@@ -3,5 +3,5 @@
 # @param [String] messageId Unique identifier of the message
 # @return [String] localized message
 #
-t = (messageId) ->
+@t = (messageId) ->
   chrome.i18n.getMessage(messageId.replace('.', '_'))


LOOKS: Switch to small BS theme for angular xeditable theme

--- src/app/config/runtime/runtime.coffee
+++ src/app/config/runtime/runtime.coffee
@@ -3,8 +3,11 @@
 #
 angular.module('doubtfire.config.runtime', [])

-.run(($rootScope, $state, $filter, $location, auth, editableOptions) ->
+.run(($rootScope, $state, $filter, $location, auth, editableOptions, editableThemes) ->
+  # Angular xeditable
   editableOptions.theme = 'bs3'
+  editableThemes.bs3.inputClass = 'input-sm'
+  editableThemes.bs3.buttonsClass = 'btn-sm'

   serialize = (obj, prefix) ->
     str = []