msgflo: Fix not respecting MSGFLO_BROKER envvar

--- src/msgflo.coffee
+++ src/msgflo.coffee
@@ -1,5 +1,6 @@

-runtime = require '../src/runtime'
+runtime = require './runtime'
+common = require './common'

 ## Main
 program = require 'commander'
@@ -12,7 +13,7 @@
   program
     .option('--host <hostname>', 'Host', String, 'localhost')
     .option('--port <port>', 'Port', Number, 3569)
-    .option('--broker <uri>', 'Broker address', String, 'amqp://localhost')
+    .option('--broker <uri>', 'Broker address', String, '')
     .option('--ide <uri>', 'FBP IDE address', String, 'http://app.flowhub.io')
     .option('--library <FILE.json>', 'Library configuration file', String, 'package.json')
     .option('--graph <file.json>', 'Initial graph to load', String, '')
@@ -20,7 +21,8 @@
     .option('--forward stderr,stdout', "Forward these streams from child", String, 'stderr,stdout')
     .parse(process.argv)

-  r = new runtime.Runtime program
+  options = common.normalizeOptions program
+  r = new runtime.Runtime options
   r.start (err, address, liveUrl) ->
     throw err if err
     console.log "msgflo started on #{address}"


Added a debug mode that lets you see all messages coming in.

--- javascripts/dashing.coffee
+++ javascripts/dashing.coffee
@@ -81,6 +81,7 @@

 Dashing.widgets = widgets = {}
 Dashing.lastEvents = lastEvents = {}
+Dashing.debugMode = false

 source = new EventSource('/events')
 source.addEventListener 'open', (e) ->
@@ -93,6 +94,8 @@

 source.addEventListener 'message', (e) =>
   data = JSON.parse(e.data)
+  if Dashing.debugMode
+    console.log("Received data for #{data.id}", data)
   lastEvents[data.id] = data
   if widgets[data.id]?.length > 0
     for widget in widgets[data.id]


Unnecessary.

Unnecessary.

--- lib.coffee
+++ lib.coffee
@@ -649,7 +649,7 @@
   # the data context where template helpers were called. In onCreated, onRendered,
   # and onDestroyed, the same as @data(). Inside a template this is the same as this.
   @currentData: ->
-    Blaze.getData() ? undefined
+    Blaze.getData()

   # Method should never be overridden. The implementation should always be exactly the same as class method implementation.
   currentData: ->


need to figure out what to do re: channel configs

--- channel_to_JSON_schema.coffee
+++ channel_to_JSON_schema.coffee
@@ -7,15 +7,15 @@

 class ParseChannelSchemaToJSONSchema
   constructor: ->
-    @channel_infile = commander.infile
+    @channelFile = commander.infile
     @messageSchemaFile = commander.outfile
     @formFile = commander.form
     @channelConfigFile = commander.channelConfig

   channel: =>
-    JSON.parse fs.readFileSync @channel_infile
+    JSON.parse fs.readFileSync @channelFile

-  run2: =>
+  run: =>
     channel = @channel()
     messageSchema = @getMessageSchema channel.application.resources
     form = @getForm channel.application.resources
@@ -38,16 +38,16 @@
   writeChannelConfig: (channelConfig) =>
     prettyChannelConfig = JSON.stringify(channelConfig, null, 2)
     fs.writeFileSync @channelConfigFile, prettyChannelConfig
-    debug 'form:', prettyChannelConfig
+    debug 'channel config:', prettyChannelConfig

   getMessageSchema : (resources)=>
     actions = _.pluck resources, 'action'
     messageSchema =
       type: 'object'
       properties:
-          action:
-            type: "string"
-            enum : actions
+        action:
+          type: "string"
+          enum : actions

     _.each actions, (action) =>
       actionProperties = @getActionProperties resources, action
@@ -58,20 +58,38 @@
     messageSchema

   getForm: (resources) =>
-    form = _.map resources, @getFormFromResource
-    actionForm =
+    form = [
       key: 'action'
       title: 'Action'
       titleMap: @getActionTitleMap resources
+    ]

-    form.unshift actionForm
+    resourceForms = _.flatten( _.map resources, @getFormFromResource )
+
+    form.concat resourceForms
+
+  getFormFromResource: (resource) =>
+    form = [
+      key: "#{resource.action}"
+      notitle: true
+      condition: "model.action === '#{resource.action}'"
+    ]
+
+    _.each resource.params, (param) =>
+      form.push(@getFormFromParam resource.action, param)

     form

-  getFormFromResource: (resource) =>
-    key: "#{resource.action}"
-    notitle: true
-    condition: "model.action === '#{resource.action}'"
+  getFormFromParam: (action, param) =>
+    formParam =
+      key: "#{action}.#{@sanitizeParam param.name}"
+      title: "#{action}.#{@sanitizeParam param.name}"
+      condition: "model.action === '#{action}'"
+    if param.hidden?
+      formParam.type = 'hidden'
+      formParam.notitle = true
+
+    formParam

   getActionTitleMap: (resources) =>
@@ -88,54 +106,6 @@

   getChannelConfig: =>
     "I'm empty"
-  run: =>
-    newForm = []
-    newChannel =
-      type: 'object'
-      properties: {}
-    channel = @channel()
-
-    _.each channel.application.resources, (resource) =>
-      resource.method = resource.httpMethod.toLocaleUpperCase()
-
-    newChannel.properties.endpoint =
-      type: 'string'
-      enum: _.map channel.application.resources, (resource) =>
-        "#{resource.method}-#{resource.url}"
-
-    newChannel.properties.url =
-      type: 'string'
-      required: true
-      enum: _.uniq _.pluck channel.application.resources, 'url'
-
-    newChannel.properties.method =
-      type: 'string'
-      required: true
-      enum: _.uniq _.pluck channel.application.resources, 'method'
-
-    form =
-      key: 'endpoint'
-      title: 'Endpoint'
-      titleMap: []
-
-    _.each channel.application.resources, (resource) =>
-      form.titleMap.push {value: "#{resource.method}-#{resource.url}", name: resource.displayName}
-
-    newForm.push form
-
-    _.each channel.application.resources, (resource) =>
-      newForm.push
-        type: "help"
-        helpvalue: "#{resource.method.toLocaleUpperCase()} #{resource.url}"
-        condition: "model.url === '#{resource.url}' && model.method === '#{resource.method}'"
-
-      _.each resource.params, (param) =>
-        newName = "#{@sanitizeParam param.name}"
-        newForm.push @convertFormParam param, resource.url, resource.method
-        newChannel.properties[newName] = @convertParam param
-
-    @writeOutput newChannel
-    @writeForm newForm

   convertParam: (param) =>
     resourceParam =
@@ -173,14 +143,13 @@

 commander
   .version 0.1
-  .option('-i, --infile [path]',  'Path to the channel file to input')
-  .option('-o, --outfile [path]',  'Path to the channel file to output')
-  .option('-f, --form [path]',  'Path to the schema form file to output')
-  .option('-c, --channel-config [path]',  'Path to the channel-config file to output')
-  .parse(process.argv);
+  .option '-i, --infile [path]',  'Path to the channel file to input'
+  .option '-o, --outfile [path]',  'Path to the channel file to output'
+  .option '-f, --form [path]',  'Path to the schema form file to output'
+  .option '-c, --channel-config [path]',  'Path to the channel-config file to output'
+  .parse process.argv

 commander.help() unless commander.infile?

-converter = new ParseChannelSchemaToJSONSchema channel_infile: commander.infile?,
-  messageSchemaFile: commander.outfile?
-converter.run2()
+converter = new ParseChannelSchemaToJSONSchema()
+converter.run()


Update getLongTitle to fall back to basename

Update getLongTitle to fall back to basename when the relative path
resolves to a blank string.

--- src/editor.coffee
+++ src/editor.coffee
@@ -260,6 +260,7 @@
     if sessionPath = @getPath()
       fileName = path.basename(sessionPath)
       directory = atom.project.relativize(path.dirname(sessionPath))
+      directory = if directory.length > 0 then directory else path.basename(path.dirname(sessionPath))
       "#{fileName} - #{directory}"
     else
       'untitled'


Use property accessors instead of accessing state directly

--- src/editor.coffee
+++ src/editor.coffee
@@ -191,17 +191,17 @@
   # Public: Controls visiblity based on the given Boolean.
   setVisible: (visible) -> @displayBuffer.setVisible(visible)

-  # Public: FIXME: I don't understand this.
-  setScrollTop: (scrollTop) -> @state.set('scrollTop', scrollTop)
-
-  # Public: Returns the current `scrollTop` value
-  getScrollTop: -> @state.get('scrollTop') ? 0
-
-  # Public: FIXME: I don't understand this.
-  setScrollLeft: (scrollLeft) -> @state.set('scrollLeft', scrollLeft)
-
-  # Public: Returns the current `scrollLeft` value
-  getScrollLeft: -> @state.get('scrollLeft')
+  # Deprecated: Use the ::scrollTop property directly
+  setScrollTop: (@scrollTop) -> @scrollTop
+
+  # Deprecated: Use the ::scrollTop property directly
+  getScrollTop: -> @scrollTop
+
+  # Deprecated: Use the ::scrollLeft property directly
+  setScrollLeft: (@scrollLeft) -> @scrollLeft
+
+  # Deprecated: Use the ::scrollLeft property directly
+  getScrollLeft: -> @scrollLeft

   # Set the number of characters that can be displayed horizontally in the
   # editor that contains this edit session.
@@ -213,12 +213,11 @@
   # Public: Sets the column at which columsn will soft wrap
   getSoftWrapColumn: -> @displayBuffer.getSoftWrapColumn()

-  # Public: Returns whether soft tabs are enabled or not.
-  getSoftTabs: -> @state.get('softTabs')
-
-  # Public: Controls whether soft tabs are enabled or not.
-  setSoftTabs: (softTabs) ->
-    @state.set('softTabs', softTabs)
+  # Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled.
+  getSoftTabs: -> @softTabs
+
+  # Deprecated: Use the ::softTabs property directly. Indicates whether soft tabs are enabled.
+  setSoftTabs: (@softTabs) -> @softTabs

   # Public: Returns whether soft wrap is enabled or not.
   getSoftWrap: -> @displayBuffer.getSoftWrap()


Remove array joins in buildEmptyLineHtml

--- src/editor.coffee
+++ src/editor.coffee
@@ -1764,21 +1764,21 @@
     if not mini and showIndentGuide
       if indentation > 0
         tabLength = activeEditSession.getTabLength()
-        indentGuideHtml = []
+        indentGuideHtml = ''
         for level in [0...indentation]
-          indentLevelHtml = [""]
+          indentLevelHtml = ""
           for characterPosition in [0...tabLength]
             if invisible = eolInvisibles.shift()
-              indentLevelHtml.push("#{invisible}")
+              indentLevelHtml += "#{invisible}"
             else
-              indentLevelHtml.push(' ')
-          indentLevelHtml.push("")
-          indentGuideHtml.push(indentLevelHtml.join(''))
+              indentLevelHtml += ' '
+          indentLevelHtml += ""
+          indentGuideHtml += indentLevelHtml

         for invisible in eolInvisibles
-          indentGuideHtml.push("#{invisible}")
-
-        return indentGuideHtml.join('')
+          indentGuideHtml += "#{invisible}"
+
+        return indentGuideHtml

     if htmlEolInvisibles.length > 0
       htmlEolInvisibles


Emit all known events

Emit all known events

--- src/client.coffee
+++ src/client.coffee
@@ -149,17 +149,24 @@

     onMessage: (message) ->
         @emit 'raw_message', message
-
+        m = new Message @, message
         switch message.action
             when 'ping'
                 @logger.debug 'ACK ping'
                 @_lastPong = Date.now()
                 @emit 'ping'
             when 'posted'
-                m = new Message @, message
                 @emit 'message', m
-            when 'typing'
-                @emit 'typing', @getUserByID message.user_id, message
+            when 'typing', 'post_edit', 'post_deleted', 'user_added', 'user_removed'
+                # Generic hadler
+                @emit message.action, message
+            when 'channel_viewed', 'preference_changed', 'ephemeral_message'
+                # These are personal messages
+                @emit message.action, message
+            when 'new_user'
+                # Reload all users for now as, /users/profiles/{id} gives us a 403 currently
+                @_apiCall 'GET', '/users/profiles', null, @_onProfiles
+                @emit 'new_user', message
             else
                 @logger.debug 'Received unhandled message type: '+message.action
                 @logger.debug message


treat any room with an uppercase letter as a channel id

--- src/client.coffee
+++ src/client.coffee
@@ -73,7 +73,7 @@
   send: (envelope, message) ->
     message = @format.outgoing(message)
     room = envelope.room
-    if !(room.match /C0[A-Z0-9]{7}/)
+    if !(room.match /[A-Z]/) # slack rooms are always lowercase
       # try to translate room name to room id
       channelForName = @rtm.dataStore.getChannelByName(room)
       if channelForName


import updated src/coffin - new and missing resources from 12/3 cloudformation resource update

--- src/coffin.coffee
+++ src/coffin.coffee
@@ -37,6 +37,15 @@
         Application: null
         DeploymentConfig: null
         DeploymentGroup: null
+      CodePipeline:
+        Pipeline: null
+        CustomActionType: null
+      Config:
+        ConfigRule: null
+        ConfigurationRecorder: null
+        DeliveryChannel: null
+      DirectoryService:
+        SimpleAD: null
       CloudTrail:
         Trail: null
       DataPipeline:
@@ -53,6 +62,7 @@
         NetworkAcl: null
         NetworkAclEntry: null
         NetworkInterface: null
+        PlacementGroup: null
         Route: null
         RouteTable: null
         SecurityGroup: null
@@ -61,20 +71,23 @@
         Subnet: null
         SubnetNetworkAclAssociation: null
         SubnetRouteTableAssociation: null
+        SpotFleet: null
         Volume: null
         VolumeAttachment: null
         VPC: null
         VPCDHCPOptionsAssociation: null
+        VPCEndpoint: null
         VPCGatewayAttachment: null
         VPNConnection: null
         VPNGateway: null
       ECS:
-        Cluser: null
+        Cluster: null
         Service: null
         TaskDefinition: null
       ElastiCache:
         CacheCluster: null
         ParameterGroup: null
+        ReplicationGroup: null
         SecurityGroup: null
         SecurityGroupIngress: null
       ElasticBeanstalk:
@@ -91,19 +104,23 @@
         AccessKey: null
         Group: null
         InstanceProfile: null
+        ManagedPolicy: null
         Policy: null
         Role: null
         User: null
         UserToGroupAddition: null
       Kinesis:
         Stream: null
+      KMS:
+        Key: null
       Logs:
         LogGroup: null
         MetricFilter: null
+        SubscriptionFilter: null
       Lambda:
+        EventSourceMapping: null
         Function: null
         Permission: null
-        EventSourceMapping: null
       OpsWorks:
         App: null
         Instance: null
@@ -115,11 +132,15 @@
         ClusterSecurityGroup: null
         ClusterSubnetGroup: null
       RDS:
+        DBCluster: null
+        DBClusterParameterGroup: null
         DBInstance: null
         DBParameterGroup: null
         DBSubnetGroup: null
         DBSecurityGroup: null
         DBSecurityGroupIngress: null
+        EventSubscription: null
+        OptionGroup: null
       Route53:
         RecordSet: null
         RecordSetGroup: null
@@ -136,12 +157,16 @@
       SQS:
         Queue: null
         QueuePolicy: null
+      SSM:
+        Document: null
+      WorkSpaces:
+        Workspace: null
     @Param =
       String: (name, arg1, arg2) =>             @_paramByType 'String', name, arg1, arg2
       Number: (name, arg1, arg2) =>             @_paramByType 'Number', name, arg1, arg2
       CommaDelimitedList: (name, arg1, arg2) => @_paramByType 'CommaDelimitedList', name, arg1, arg2
       AWS: (type, name, arg1, arg2) => @_paramByType "AWS::#{type}", name, arg1, arg2
-      AWSList: (type, name, arg1, arg2) => @_paramByType "List<#{type}>", name, arg1, arg2
+      AWSList: (type, name, arg1, arg2) => @_paramByType "List<AWS::#{type}>", name, arg1, arg2
     @_buildCall null, null, 'AWS', @AWS

   _paramByType: (type, name, arg1, arg2) =>


Disable Y axis dropdown tests.

Disable Y axis dropdown tests.

--- test/e2e/collectionDetailSpec.coffee
+++ test/e2e/collectionDetailSpec.coffee
@@ -132,10 +132,16 @@
         expect(dropdowns.length, 'The Y-axis dropdown count is incorrect')
           .to.equal(4)

-    it 'should display the Y-axis dropdown items', ->
+    xit 'should display the Y-axis dropdown items', ->
       page.yAxisDropdowns.then (dropdowns) ->
         dropdown = _.first(dropdowns)
         # Show the dropdown.
+        # TODO - the following tests began failing with the message:
+        #    UnknownError: unknown error: Element is not clickable at point (120, 318). Other element would receive the click:
+        #
...

+        #  Enable this test case and resolve this error.
+        #
         dropdown.click()
         # The default selection of the first chart is 'delta Ktrans'.
         dropdown.find(By.css('.qi-dropbtn-label')).then (selected) ->


Update dist task

Update dist task

--- gulp/dist.coffee
+++ gulp/dist.coffee
@@ -6,5 +6,8 @@
     runSequence 'build', 'dist:copy', next

   gulp.task 'dist:copy', ->
-    gulp.src('build/dist/eventric.js')
+    gulp.src([
+      'build/dist/eventric.js'
+      'build/dist/eventric-min.js'
+    ])
       .pipe(gulp.dest('dist/'))


Transact on resetting the settings when a schema changes

--- src/config.coffee
+++ src/config.coffee
@@ -797,7 +797,7 @@
     _.extend rootSchema, schema
     @setDefaults(keyPath, @extractDefaultsFromSchema(schema))
     @setScopedDefaultsFromSchema(keyPath, schema)
-    @resetForSchemaChange()
+    @resetSettingsForSchemaChange()

   load: ->
     @initializeConfigDirectory()
@@ -999,23 +999,28 @@
       defaults[key] = @extractDefaultsFromSchema(value) for key, value of properties
       defaults

-  makeValueConformToSchema: (keyPath, value) ->
-    value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
-    value
+  makeValueConformToSchema: (keyPath, value, options) ->
+    if options?.suppressException
+      try
+        @makeValueConformToSchema(keyPath, value)
+      catch e
+        undefined
+    else
+      value = @constructor.executeSchemaEnforcers(keyPath, value, schema) if schema = @getSchema(keyPath)
+      value

   # When the schema is changed / added, there may be values set in the config
   # that do not conform to the config. This will reset make them conform.
-  resetForSchemaChange: (source=@getUserConfigPath()) ->
-    settings = @settings
-    @settings = null
-    @set(null, settings, {save: false})
-
-    priority = @priorityForSource(source)
-    selectorsAndSettings = @scopedSettingsStore.propertiesForSource(source)
-    @scopedSettingsStore.removePropertiesForSource(source)
-    for scopeSelector, settings of selectorsAndSettings
-      @set(null, settings, {scopeSelector, source, priority, save: false}) if settings?
-    return
+  resetSettingsForSchemaChange: (source=@getUserConfigPath()) ->
+    @transact =>
+      @settings = @makeValueConformToSchema(null, @settings, suppressException: true)
+      priority = @priorityForSource(source)
+      selectorsAndSettings = @scopedSettingsStore.propertiesForSource(source)
+      @scopedSettingsStore.removePropertiesForSource(source)
+      for scopeSelector, settings of selectorsAndSettings
+        settings = @makeValueConformToSchema(null, settings, suppressException: true)
+        @setRawScopedValue(null, settings, source, scopeSelector)
+      return

   ###
   Section: Private Scoped Settings
@@ -1036,13 +1041,9 @@
     @scopedSettingsStore.removePropertiesForSource(source)

     for scopeSelector, settings of newScopedSettings
+      settings = @makeValueConformToSchema(null, settings, suppressException: true)
       validatedSettings = {}
-      try
-        settings = withoutEmptyObjects(@makeValueConformToSchema(null, settings))
-        validatedSettings[scopeSelector] = settings
-      catch e
-        validatedSettings[scopeSelector] = null
-
+      validatedSettings[scopeSelector] = withoutEmptyObjects(settings)
       @scopedSettingsStore.addProperties(source, validatedSettings, {priority}) if validatedSettings[scopeSelector]?

     @emitChangeEvent()


:memo: Use ## for headings

:memo: Use ## for headings

--- src/config.coffee
+++ src/config.coffee
@@ -10,12 +10,12 @@
 #
 # An instance of this class is always available as the `atom.config` global.
 #
-# ### Best practices
+# ## Best practices
 #
 # * Create your own root keypath using your package's name.
 # * Don't depend on (or write to) configuration keys outside of your keypath.
 #
-# ### Example
+# ## Example
 #
 # ```coffeescript
 # atom.config.set('myplugin.key', 'value')


config: Some code for min/max worker calculation

--- src/config.coffee
+++ src/config.coffee
@@ -165,6 +165,22 @@

   return rates

+countMinimumWorkers = (cfg) ->
+  byType = {}
+  for role, c of cfg
+    continue if role == '*'
+    byType[c.dynosize] = 0 if not byType[c.dynosize]?
+    byType[c.dynosize] += c.minimum
+  return byType
+countMaximumWorkers = (cfg) ->
+  byType = {}
+  for role, c of cfg
+    continue if role == '*'
+    byType[c.dynosize] = 0 if not byType[c.dynosize]?
+    byType[c.dynosize] += c.maximum
+  return byType
+
+
 main = () ->
   p = process.argv[2]
   f = fs.readFileSync p, 'utf-8'
@@ -175,6 +191,16 @@
     padded = ("                                     " + role).slice(-40)
     console.log "#{padded}: #{perMinute} jobs/minute"

+  minimums = countMinimumWorkers parsed
+  console.log 'Workers minimum'
+  for type, value of minimums
+    console.log "\t#{type}: #{value} workers"
+
+  maximums = countMaximumWorkers parsed
+  console.log 'Workers maximum'
+  for type, value of maximums
+    console.log "\t#{type}: #{value} workers"
+
 main() if not module.parent

 exports.validate = validateConfig


checking if variable exists before pushing to new rive file

--- src/parser.coffee
+++ src/parser.coffee
@@ -473,7 +473,8 @@

         if t.reply
           for r in t.reply
-            output.push "#{id}- #{r.replace(/\n/mg, "\\n")}"
+            if r
+              output.push "#{id}- #{r.replace(/\n/mg, "\\n")}"

         output.push ""


prepare to implement first class attribute parsing

--- src/parser.coffee
+++ src/parser.coffee
@@ -22,7 +22,7 @@
     while (@chunk = code[i..])
       break if @activeStates.length is 0
       consumed = (
-          if @activeBranchNode().type isnt $.CJSX_EL
+          if @currentState() isnt $.CJSX_EL and @currentState() isnt $.CJSX_ATTRIBUTES
             @csComment() or
             @csHeredoc() or
             @csString() or
@@ -42,7 +42,7 @@
       i += consumed

     if @activeBranchNode()? and @activeBranchNode() isnt @parseTree
-      message = "Unexpected end of input: unclosed #{@activeBranchNode().type}"
+      message = "Unexpected end of input: unclosed #{@currentState()}"
       throwSyntaxError message, first_line: @chunkLine, first_column: @chunkColumn

     @remainder = code[i..]
@@ -103,8 +103,9 @@

     # clever js regex heuristics should go here...
     # except as we haven't actually parsed most of the code,
-    # we can't look at tokens to figure out if this is actually division
-    # maybe search backward and do a hacky sort of mini parse?
+    # we can't look at tokens to figure out if this is actually division.
+    # maybe this will be doable if we parse the code sections for
+    # symbols and whitespace at least

     return 0 unless match = REGEX.exec @chunk
     [match, regex, flags] = match
@@ -139,28 +140,52 @@
     return 0 unless selfClosing or @chunk.indexOf("</#{tagName}>", input.length) > -1

     @pushActiveBranchNode parseTreeBranchNode $.CJSX_EL, tagName
+    # @pushActiveBranchNode parseTreeBranchNode $.CJSX_ATTRIBUTES
     @addLeafNodeToActiveBranch @cjsxAttributesParse attributesText

     if selfClosing
       @popActiveBranchNode() # close cjsx tag

+    # 1+tagName.length
     input.length

+  cjsxAttribute: ->
+    return 0 unless @currentState() is $.CJSX_ATTRIBUTES
+
+    if @chunk.charAt(0) is '/'
+      if @chunk.charAt(1) is '>'
+        @popActiveBranchNode() # end attributes
+        @popActiveBranchNode() # end cjsx
+        return 2
+      else
+        throwSyntaxError \
+          "/ without immediately following > in CJSX tag #{@peekActiveState(2).value}",
+          first_line: @chunkLine, first_column: @chunkColumn
+
+    if @chunk.charAt(0) is '>'
+      @popActiveBranchNode() # end attributes
+      return 1
+
+
+
+  cjsxAttrKey: ->
+    return 0 unless @currentState() is $.CJSX_ATTRIBUTES
+
   cjsxEscape: ->
-    return 0 unless @activeBranchNode().type is $.CJSX_EL and @chunk.charAt(0) is '{'
+    return 0 unless @currentState() is $.CJSX_EL and @chunk.charAt(0) is '{'

     @pushActiveBranchNode parseTreeBranchNode $.CJSX_ESC
     return 1

   cjsxUnescape: ->
     return 0 if @opts.contained
-    return 0 unless @activeBranchNode().type is $.CJSX_ESC and @chunk.charAt(0) is '}'
+    return 0 unless @currentState() is $.CJSX_ESC and @chunk.charAt(0) is '}'

     @popActiveBranchNode() # close cjsx escape
     return 1

   cjsxEnd: ->
-    return 0 unless @activeBranchNode().type is $.CJSX_EL
+    return 0 unless @currentState() is $.CJSX_EL
     return 0 unless match = CLOSING_TAG.exec @chunk
     [input, tagName] = match

@@ -174,7 +199,7 @@
     input.length

   cjsxText: ->
-    return 0 unless @activeBranchNode().type is $.CJSX_EL
+    return 0 unless @currentState() is $.CJSX_EL

     unless @newestNode().type is $.CJSX_TEXT
       @addLeafNodeToActiveBranch parseTreeLeafNode $.CJSX_TEXT, '' # init value as string
@@ -186,7 +211,7 @@

   # fallthrough
   coffeescriptCode: ->
-    # return 0 unless @activeBranchNode().type is $.ROOT or @activeBranchNode().type is $.CJSX_ESC
+    # return 0 unless @currentState() is $.ROOT or @currentState() is $.CJSX_ESC

     unless @newestNode().type is $.CS
       @addLeafNodeToActiveBranch parseTreeLeafNode $.CS, '' # init value as string
@@ -199,6 +224,10 @@
   # parseTree helpers

   activeBranchNode: -> last(@activeStates)
+
+  peekActiveState: (depth = 1) -> @activeStates[-depth..][0]
+
+  currentState: -> @activeBranchNode().type

   newestNode: -> last(@activeBranchNode().children) or @activeBranchNode()

@@ -348,7 +377,25 @@
 # [1] tag name
 # [2] attributes text
 # [3] self closing?
-OPENING_TAG = /^<([-A-Za-z0-9_]+)((?:\s+[\w-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:{[\s\S]*?})|[^>\s]+))?)*?\s*)(\/?)>/
+OPENING_TAG = /// ^
+  <
+    ([-A-Za-z0-9_]+) # tag name (captured)
+    (
+      (?:\s+[\w-]+ # attr name
+        (?:\s*=\s* # equals and whitespace
+          (?:
+              (?:"[^"]*") # double quoted value
+            | (?:'[^']*') # single quoted value
+            | (?:{[\s\S]*?}) # cjsx escaped expression
+            | [^>\s]+ # bare value
+          )
+        )?
+      )*?
+      \s* # whitespace after attr pair
+    ) # attributes text (captured)
+    (\/?) # self closing? (captured)
+  >
+///

 # [1] tag name
 CLOSING_TAG = /^<\/([-A-Za-z0-9_]+)[^>]*>/
@@ -361,7 +408,21 @@
   # [4] {val} {cs escaped}
   # [5] val bare
   # [6] whitespace
-  TAG_ATTRIBUTES = /(?:([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|(?:{((?:\\.|[\s\S])*)})|([^>\s]+)))?)|([\s\n]+)/g
+  TAG_ATTRIBUTES = ///
+    (?:
+      ([-A-Za-z0-9_]+) # attr name (captured)
+      (?:
+        \s*=\s* # equals and whitespace
+        (?:
+            (?: " ( (?: \\. | [^"] )* ) " ) # double quoted value (captured)
+          | (?: ' ( (?: \\. | [^'] )* ) ' ) # single quoted value (captured)
+          | (?: { ( (?: \\. | [\s\S] )* ) } ) # cjsx escaped expression (captured)
+          | ( [^>\s]+ ) # bare value (captured)
+        )
+      )?
+    )
+    | ( [\s\n]+ ) # whitespace (captured)
+  ///g

 PRAGMA = /^\s*#\s*@cjsx\s+(\S*)/i


Fix IE9 - setAttribute

Fix IE9 - setAttribute

--- src/parser.coffee
+++ src/parser.coffee
@@ -164,7 +164,9 @@

     @parseAdElement: (adElement) ->
         for adTypeElement in adElement.childNodes
-            adTypeElement.id = adElement.getAttribute("id")
+            continue unless adTypeElement.nodeName in ["Wrapper", "InLine"]
+
+            adTypeElement.setAttribute "id", adElement.getAttribute("id")
             if adTypeElement.nodeName is "Wrapper"
                 return @parseWrapperElement adTypeElement
             else if adTypeElement.nodeName is "InLine"


Replace tab indentation with space indentation.

--- src/pulsar.coffee
+++ src/pulsar.coffee
@@ -14,67 +14,67 @@
 process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"

 jobChangeListener = (->
-	jobChatInfo = {}
+  jobChatInfo = {}

-	connect = (url) ->
-		sock = new SockJS(url)
-		sock.onmessage = (msg) ->
-			data = JSON.parse(msg.data)
-			updateJob(data.job) if data.event == 'job.change'
+  connect = (url) ->
+    sock = new SockJS(url)
+    sock.onmessage = (msg) ->
+      data = JSON.parse(msg.data)
+      updateJob(data.job) if data.event == 'job.change'

-	updateJob = (job)->
-		chatInfo = jobChatInfo[job.id]
-		if chatInfo.isVerbose
-			chat = chatInfo.chat
-			lastSentPos = chatInfo.lastSentPos
-			chat.send job.output.substring(lastSentPos)
-			chatInfo.lastSentPos = job.output.length - 1
-		if job.status != 'RUNNING'
-			chatInfo.chat.send "Job #{job.id} finished with status: #{job.status}. More details here #{chatInfo.jobUrl}"
-			delete jobChatInfo[job.id]
+  updateJob = (job)->
+    chatInfo = jobChatInfo[job.id]
+    if chatInfo.isVerbose
+      chat = chatInfo.chat
+      lastSentPos = chatInfo.lastSentPos
+      chat.send job.output.substring(lastSentPos)
+      chatInfo.lastSentPos = job.output.length - 1
+    if job.status != 'RUNNING'
+      chatInfo.chat.send "Job #{job.id} finished with status: #{job.status}. More details here #{chatInfo.jobUrl}"
+      delete jobChatInfo[job.id]

-	addJob = (jobId, chat, jobUrl, isVerbose) ->
-		jobChatInfo[jobId] = {chat: chat, lastSentPos: 0, jobUrl: jobUrl, isVerbose: isVerbose}
+  addJob = (jobId, chat, jobUrl, isVerbose) ->
+    jobChatInfo[jobId] = {chat: chat, lastSentPos: 0, jobUrl: jobUrl, isVerbose: isVerbose}

-	return {
-		connect: connect
-		addJob: addJob
-	}
+  return {
+    connect: connect
+    addJob: addJob
+  }
 )()

 jobChangeListener.connect(API_URL + 'websocket')

 module.exports = (robot) ->
-	robot.respond /deploy\s?(-v|) (pending|)\s?([^\s]+) ([^\s]+)$/i, (chat) ->
-		isVerbose = chat.match[1] == '-v'
-		isPending = chat.match[2] == 'pending'
-		task = (if isPending then "deploy:pending" else "deploy")
-		application = chat.match[3]
-		environment = chat.match[4]
+  robot.respond /deploy\s?(-v|) (pending|)\s?([^\s]+) ([^\s]+)$/i, (chat) ->
+    isVerbose = chat.match[1] == '-v'
+    isPending = chat.match[2] == 'pending'
+    task = (if isPending then "deploy:pending" else "deploy")
+    application = chat.match[3]
+    environment = chat.match[4]

-		command = "#{task} '#{application}' to '#{environment}'"
-		chat.send command + " started"
+    command = "#{task} '#{application}' to '#{environment}'"
+    chat.send command + " started"

-		rest.post(API_URL + application + '/' + environment,
-			data:
-				task: task
-		).on("complete", (job) ->
-			if job.id
-				jobChangeListener.addJob(job.id, chat, job.url, isVerbose)
-				chat.send "#{command} -> assigned job ID " + job.id
-				chat.send "More info here #{job.url}"
-			else
-				chat.send "Your request failed"
-		).on("error", (error) ->
-			chat.send "Error: " + JSON.stringify error
-		).on("fail", (error) ->
-			chat.send "Fail: " + JSON.stringify error
-		)
+    rest.post(API_URL + application + '/' + environment,
+      data:
+        task: task
+    ).on("complete", (job) ->
+      if job.id
+        jobChangeListener.addJob(job.id, chat, job.url, isVerbose)
+        chat.send "#{command} -> assigned job ID " + job.id
+        chat.send "More info here #{job.url}"
+      else
+        chat.send "Your request failed"
+    ).on("error", (error) ->
+      chat.send "Error: " + JSON.stringify error
+    ).on("fail", (error) ->
+      chat.send "Fail: " + JSON.stringify error
+    )

-	robot.respond /jobs/i, (chat) ->
-		rest.get(API_URL + 'jobs')
-		.on 'complete', (response) ->
-			message = 'Jobs:'
-			_.each response, (job) ->
-				message += "\n" + job.status + ' job "' + job.task + ' ' + job.app + ' ' + job.env + '" with ID ' + job.id
-			chat.send message
+  robot.respond /jobs/i, (chat) ->
+    rest.get(API_URL + 'jobs')
+    .on 'complete', (response) ->
+      message = 'Jobs:'
+      _.each response, (job) ->
+        message += "\n" + job.status + ' job "' + job.task + ' ' + job.app + ' ' + job.env + '" with ID ' + job.id
+      chat.send message


refactor

refactor

--- src/render.coffee
+++ src/render.coffee
@@ -38,12 +38,10 @@
     attr = element.attributes.filter((i) -> i.name is 'b-if')[0]
     value = get attr.value, context
     return [] unless value
-  type = element.type
-  name = element.name
-  children = element.children.slice()
-  attributes = []
+  { type, name, children, attributes } = element
+  attrs = []
   events = []
-  element.attributes.forEach (attr) ->
+  attributes.forEach (attr) ->
     switch attr.name
       when 'b-on'
         attr.value.split(/\s*,\s*/).forEach (a) ->
@@ -54,7 +52,7 @@
       when 'b-attr'
         attr.value.split(/\s*,\s*/).forEach (a) ->
           [n, v] = a.split /\s*:\s*/
-          attributes.push
+          attrs.push
             name: n
             value: get v, context
       when 'b-html'
@@ -74,17 +72,15 @@
           value: text
         ]
       else
-        attributes.push attr
+        attrs.push { name: attr.name, value: attr.value }
   children = children.reduce (c, child) ->
     c.concat renderElement child, context
   , []
-  [{ type, name, attributes, children, events }]
+  [{ type, name, attributes: attrs, children, events }]

 renderElements = (element, context) ->
-  type = element.type
-  name = element.name
-  children = element.children.slice()
-  attributes = element.attributes.filter (i) ->
+  { type, name, children, attributes } = element
+  attrs = attributes.filter (i) ->
     i.name isnt 'b-repeat'
   attr = getAttr element, 'b-repeat'
   match = attr.value.match /^\s*(\w+)\s*in\s*(\w+)\s*$/
@@ -92,7 +88,7 @@
   [_, itemName, listName] = match
   context[listName].reduce (c, item) ->
     context[itemName] = item
-    c.concat renderElement({ type, name, attributes, children }, context)
+    c.concat renderElement { type, name, attributes: attrs, children }, context
   , []

 module.exports = (elements, context) ->


Implement a comment marker and use the original element as the reference.

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -48,7 +48,6 @@
       @routine @el, value, @currentListener
       @currentListener = value
     else if @options.special is "iteration"
-      @iterated or= []
       @routine @el, value, @
     else
       value = value() if value instanceof Function
@@ -192,21 +191,23 @@

 # Returns an iteration binding routine for the specified collection.
 iterationBinding = (name) -> (el, collection, binding) ->
-  Rivets.routines.hide el, true
-
-  for iteration in binding.iterated
-    iteration.view.unbind()
-    iteration.el.parentNode.removeChild iteration.el
+  if binding.iterated?
+    for iteration in binding.iterated
+      iteration.view.unbind()
+      iteration.el.parentNode.removeChild iteration.el
+  else
+    binding.marker = document.createComment " rivets: each-#{name} "
+    el.parentNode.insertBefore binding.marker, el
+    el.parentNode.removeChild el

   binding.iterated = []

   for item in collection
     data = {}
     data[name] = item
-
     itemEl = el.cloneNode true
-    Rivets.routines.show itemEl, true
-    el.parentNode.insertBefore itemEl, el
+    previous = binding.iterated[binding.iterated.length - 1] or binding.marker
+    binding.marker.parentNode.insertBefore itemEl, previous.nextSibling

     binding.iterated.push
       el: itemEl


add disposal logic

add disposal logic

--- src/router.coffee
+++ src/router.coffee
@@ -109,11 +109,13 @@
     @_location.search = location.search

   _avoidTransitionSameRoute: ->
-    @_history.registerTransitionHook (location) =>
-      isSameLocation = _.isEqual(location.pathname, @_location.pathname) and
-        _.isEqual(location.search, @_location.search) and
-        _.isEqual(location.state, @_location.state)
-      return false if isSameLocation
+    @_history.registerTransitionHook(@_handleSameRouteTransitionCheck)
+
+  _handleSameRouteTransitionCheck: (location) =>
+    isSameLocation = _.isEqual(location.pathname, @_location.pathname) and
+      _.isEqual(location.search, @_location.search) and
+      _.isEqual(location.state, @_location.state)
+    return false if isSameLocation

   resetMemoryHistory: ->
     @_setupHistory(@_options.history, @_options.defaultRoute,
@@ -136,7 +138,7 @@
     invariant(callback, 'listen: callback must be provided.')
     invariant(@_config, 'listen: call `addRoutes` method first.')
     invariant(@_targets.length, 'listen: call `addTarget` method first.')
-    @_history.listen (location) =>
+    @_historyListenerDisposer = @_history.listen (location) =>
       @_updateLocationObj(location)
       return if @_slashResolver(location)
       @_activeComponent = @_handleRoute(@_router.match(location.pathname))
@@ -149,6 +151,12 @@
   unregisterTransitionHook: (callback) ->
     invariant(callback, 'unregisterTransitionHook: callback must be specified.')
     @_history.unregisterTransitionHook(callback) if _.isFunction(callback)
+
+  dispose: ->
+    if @_options.avoidTransitionSameRoute
+      @_history.unregisterTransitionHook(@_handleSameRouteTransitionCheck)
+
+    @_historyListenerDisposer?.call()

   setRoute: (url) ->
     # TODO: return status codes 200, 302, 404, 500


router: fix anchor tag element inspeciton

--- src/router.coffee
+++ src/router.coffee
@@ -40,13 +40,17 @@
       tagName = 'a' + tagName

     unless props.onclick
-      props.onclick = (e) =>
-        $el = e.target
-        isLocal = $el.hostname is window.location.hostname
+      props.onclick = do =>
+        router = this
+        # coffeelint: disable=missing_fat_arrows
+        (e) ->
+          $el = this
+          isLocal = $el.hostname is window.location.hostname

-        if isLocal
-          e.preventDefault()
-          @go $el.pathname
+          if isLocal
+            e.preventDefault()
+            router.go $el.pathname
+        # coffeelint: enable=missing_fat_arrows

     z tagName, props, children


仅接受int的id

仅接受int的id

--- src/router.coffee
+++ src/router.coffee
@@ -137,7 +137,7 @@
       biz: "project"
     ,
       #素材
-      path: "#{apiRoot}project/:project_id/asset"
+      path: "#{apiRoot}project/:project_id(\\d+)/asset"
       ### paths
       paths:
         post: "post path"
@@ -150,7 +150,7 @@
         put: false
     ,
       #查看素材
-      path: "/asset/:project_id/:filename",
+      path: "/asset/:project_id(\\d+)/:filename",
       biz: "asset"
       methods:
         get: "readFile"
@@ -159,15 +159,15 @@
         delete: false
     ,
       #issue
-      path: "#{apiRoot}project/:project_id/issue"
+      path: "#{apiRoot}project/:project_id(\\d+)/issue"
       biz: "issue"
     ,
       #针对issue的评论
-      path: "#{apiRoot}issue/:issue_id/comment"
+      path: "#{apiRoot}issue/:issue_id(\\d+)/comment"
       biz: "comment"
     ,
       #建立或者解除asset与issue的关系
-      path: "#{apiRoot}issue/:issue_id/asset"
+      path: "#{apiRoot}issue/:issue_id(\\d+)/asset"
       biz: "asset_issue_relation"
       methods:
         put: false
@@ -182,7 +182,7 @@
         put: "changeStatus"
     ,
       #获取项目状态，及修改项目状态的路由
-      path: "#{apiRoot}project/:project_id/status"
+      path: "#{apiRoot}project/:project_id(\\d+)/status"
       biz: "project"
       methods:
         get: "getStatus",


Stepped okSubmits date

Stepped okSubmits date

--- server/api/dashboard.coffee
+++ server/api/dashboard.coffee
@@ -59,7 +59,7 @@
 export default dashboard = () ->
     queries =
         # remember that months start from 0
-        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2017, 9, 26)}},
+        ok: {ok: 1, lastSubmitTime: {$gt: new Date(2018, 0, 1)}},
         wa: {solved: 0, ok: 0, ignored: 0, attempts: {$gt: 0}},
         ig: {ignored: 1},
         ac: {solved: 1}


Fix DHIS url and content-type

Fix DHIS url and content-type

--- src/server.coffee
+++ src/server.coffee
@@ -175,7 +175,7 @@
     return callback false

   options =
-    url: config.getConf()['ilr-to-dhis']['dhis2-url'] + '/api/metadata.xml'
+    url: config.getConf()['ilr-to-dhis']['dhis2-url'] + '/api/metadata'
     body: dxfData
     auth:
       username: config.getConf()['ilr-to-dhis']['dhis2-user']
@@ -184,6 +184,7 @@
     key: nullIfEmpty config.getConf()['sync-type']['both-trigger-client-key']
     ca: nullIfEmpty config.getConf()['sync-type']['both-trigger-ca-cert']
     timeout: 0
+    headers: { 'content-type': 'application/xml' }

   beforeTimestamp = new Date()
   request.post options, (err, res, body) ->


<model> Sensor - adding Measure creation

--- server/models/sensor.coffee
+++ server/models/sensor.coffee
@@ -6,6 +6,7 @@
 ###

 cozydb = require 'cozydb'
+Measure = require './measure'

 module.exports = SensorModel = cozydb.getModel 'Sensor',
 	@schema:
@@ -43,7 +44,7 @@
 	###
 	# updateAttributesForDBAndDriver
 	# ====
-	# Update data about the Sensor, both for the DB and Driver
+	# Updates data about the Sensor, both for the DB and Driver
 	# @param data (dictionary): 						New data
 	# @param sensorsDrivers (Driver[]): 				List of drivers supported by the system
 	# @param callback (Function(Error, Sensor):null):	Callback
@@ -75,6 +76,18 @@
 		return

+	###
+	# createMeasure
+	# ====
+	# Generates a measure for this Sensor.
+	# @param data (dictionary): 						Measure's data (value, time, type)
+	# @param callback (Function(Measure):null):			Callback
+	###
+	@createMeasure = (data, callback) ->
+		sanitize data
+		data.sensorId = @id
+		measure = Measure.create data callback
+

 ###
 # byId


modified

modified

--- src/jisilu.coffee
+++ src/jisilu.coffee
@@ -58,3 +58,6 @@
 (new JisiluData()).getSymbols 'fundmt', (err, symbols)->
   console.log symbols
 ###
+
+
+# http://www.abcfund.cn/fund/share.php?code=164401 數據也許更好


follow docs

follow docs

--- src/follow.coffee
+++ src/follow.coffee
@@ -1,7 +1,8 @@
 ###*
 The Follow module provides a simple method to set an object's
 velocity so that it will approach another object. This method
-relies on the objects having `bounds` methods. GameObject prov
+relies on the objects having `bounds` methods. GameObject
+

 player = GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/follow.coffee
+++ src/follow.coffee
@@ -1,6 +1,6 @@
 ###*
 The Follow module provides a simple method to set an object's
-so that it is
+so that it will

 # Health regeneration


:penguin: Add linux keymaps.

:penguin: Add linux keymaps.

--- keymaps/settings-view.cson
+++ keymaps/settings-view.cson
@@ -1,5 +1,5 @@
 '.platform-darwin':
   'cmd-,': 'settings-view:open'

-'.platform-win32':
+'.platform-win32, .platform-linux':
   'ctrl-,': 'settings-view:open'


Add/document deprecated methods

Add/document deprecated methods

--- src/keymap.coffee
+++ src/keymap.coffee
@@ -126,6 +126,15 @@
   bindKeys: (source, selector, keyBindings) ->
     @addKeyBindingsForSelector(source, selector, keyBindings)

-  # Deprecated: Use {::keyBindingsForCommand} with the second element argument.
+  # Deprecated: Use {::findKeyBindings} with the 'command' param.
+  keyBindingsForCommand: (command) ->
+    @findKeyBindings({command})
+
+  # Deprecated: Use {::findKeyBindings} with the 'command' and 'target'
+  # params
   keyBindingsForCommandMatchingElement: (command, target) ->
     @findKeyBindings({command, target: target[0] ? target})
+
+  # Deprecated: Use {::findKeyBindings} with the 'target' param.
+  keyBindingsMatchingElement: (target) ->
+    @findKeyBindings({target: target[0] ? target})


Fix silly call-by-value error

Fix silly call-by-value error

--- src/lambda.coffee
+++ src/lambda.coffee
@@ -81,7 +81,7 @@
         App l, r
     when Def
       cb markStep 'def', t, t.term
-      reduceCallByName t.term, cb
+      reduceCallByValue t.term, cb

 reduceApplicative = (t, cb) ->
   switch t.type


fix usage of lodash when in the browser

fix usage of lodash when in the browser

--- src/hinoki.coffee
+++ src/hinoki.coffee
@@ -13,7 +13,7 @@
     )
   # other
   else
-    root.hinoki = factory(root.Promise, root.lodash, root.helfer)
+    root.hinoki = factory(root.Promise, root._, root.helfer)
 )(this, (Promise, _, helfer, fsModule, pathModule) ->

 ################################################################################


groups: renamed dialog to addRoleDialog

groups: renamed dialog to addRoleDialog

--- client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
+++ client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
@@ -95,7 +95,7 @@
           style       : "modal-clean-gray"
           cssClass    : 'add-role'
           callback    : =>
-            @addSubView saveDialog = new KDDialogView
+            @addSubView addRoleDialog = new KDDialogView
               cssClass      : "add-role-dialog"
               duration      : 200
               topOffset     : -21
@@ -105,12 +105,12 @@
                 Save        :
                   style     : "modal-clean-gray"
                   callback  : ()=>
-                    saveDialog.hide()
+                    addRoleDialog.hide()
                 Cancel :
                   style     : "modal-cancel"
                   callback  : ()->
-                    saveDialog.hide()
-            saveDialog.addSubView wrapper = new KDView
+                    addRoleDialog.hide()
+            addRoleDialog.addSubView wrapper = new KDView
               cssClass : "kddialog-wrapper"

             wrapper.addSubView title = new KDCustomHTMLView
@@ -136,7 +136,7 @@
             form.addSubView @inputCopyPermissions = inputCopyPermissions = new KDSelectBox
               selectOptions : selectOptions
               defaultValue : null
-            saveDialog.show()
+            addRoleDialog.show()

         Save          :
           style       : "modal-clean-gray"


[Feeder] revived prefetched items on feeder level

--- client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
+++ client/landing/app/Applications/Feeder.kdapplication/FeedController.coffee
@@ -199,7 +199,7 @@
       unless prefetchedItems = KD.prefetchedFeeds[feedId]
       then @loadFeed filter
       else
-        log "burdan yukle dayi"
-        kallback null, prefetchedItems
+        log "buyur dayi feed var, burdan yukle."
+        kallback null, (KD.remote.revive item for item in prefetchedItems)
     else
       filter.dataSource selector, options, kallback


Use group vm if vm is not provided

Use group vm if vm is not provided

--- client/landing/app/Applications/WebTerm.kdapplication/AppController.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/AppController.coffee
@@ -14,8 +14,8 @@
         KD.singletons.vmController.askToTurnOn 'WebTerm', cb

   constructor:(options = {}, data)->
-
-    vmName          = options.params?.vmName or KD.nick()
+    vmName          = options.params?.vmName or \
+                      KD.singletons.vmController.getDefaultVmName()
     options.view    = new WebTermAppView {vmName}
     options.appInfo =
       title         : "Terminal on #{vmName}"


No dice

No dice

--- SingularityUI/app/views/fileBrowserSubview.coffee
+++ SingularityUI/app/views/fileBrowserSubview.coffee
@@ -14,7 +14,7 @@
         @listenTo @collection, 'error', @catchAjaxError
         @task = @model

-        @scrollAfterRender = Backbone.history.fragment?.indexOf('/files') isnt -1
+        @scrollAfterRender = Backbone.history.fragment.indexOf('/files') isnt -1

     render: ->
         # Ensure we have enough space to scroll


added "orUse" field

added "orUse" field

--- t9n/es_ES.coffee
+++ t9n/es_ES.coffee
@@ -40,6 +40,7 @@
   username: "Usuario"
   usernameOrEmail: "Usuario o email"
   with: "con"
+  orUse: "O puedes usar"

   info:
     emailSent: "Email enviado"


Use new configuration page

Use new configuration page

--- src/js/app.coffee
+++ src/js/app.coffee
@@ -8,7 +8,7 @@
 cards = require('cards')
 timeline = require('timeline')

-CONFIG_BASE_URL = 'http://traktv-forwarder.herokuapp.com/'
+CONFIG_BASE_URL = 'https://pebbleshows.herokuapp.com/pebbleConfig'

 console.log "accessToken: #{Settings.option 'accessToken'}"


Update the regex

Update the regex

--- src/lieber.coffee
+++ src/lieber.coffee
@@ -17,10 +17,10 @@
 phrases = require './data/phrases.json'

 module.exports = (robot) ->
-    robot.respond /lieber(|ism) (me|.*)/i, (msg) ->
-        name = msg.match[1].trim()
-        switch name
-            when "me"
-                msg.reply ", you are ${msg.random phrases}"
-            else
-                msg.send "#{name} is ${msg.random phrases}"
+  robot.respond /lieber(|ism) (\S*)/i, (msg) ->
+    name = msg.match[1].trim()
+    switch name
+      when 'me'
+        msg.reply ", you are ${msg.random phrases}"
+      else
+        msg.send "#{name} is ${msg.random phrases}"


Added some documentation to the security module

--- app/security.coffee
+++ app/security.coffee
@@ -8,6 +8,12 @@

 db = require './db'

+# Adds a user submission object to database
+# @param [object] query
+# @option query [object] submission
+# @option query [function] onSuccess
+# @option query [function] onError
+# @private
 _addSubmission = (query) ->
 	addSubmissionQuery =
 		submission: query.submission
@@ -15,6 +21,12 @@
 		onError: query.onError
 	db.setSubmission addSubmissionQuery

+# Verifies a user submission, i.e. checks if rate limiting should be applied
+# @param [object] query
+# @option query [object] req object from node httpserver module
+# @option query [function] onSuccess
+# @option query [function] onError
+_addSubmission
 verifySubmission = (query) ->
 	verifySubmissionQuery =
 		ip: query.req.connection.remoteAddress


DevTools: Improve toggleLiveReload

DevTools: Improve toggleLiveReload

--- client/DevTools/AppView.coffee
+++ client/DevTools/AppView.coffee
@@ -15,14 +15,8 @@
     toggleLiveReload = new KDView
       partial : "<span>#{itemLabel}</span>"
       click   : =>
-        @liveMode = !@liveMode
-        @storage.setValue 'liveMode', @liveMode
-
+        @toggleLiveReload()
         menu.contextMenu.destroy()
-
-        if @liveMode
-          KD.utils.defer =>
-            @previewApp yes; @previewCss yes

     toggleLiveReload.on "viewAppended", ->
       toggleLiveReload.parent.setClass "default"
@@ -204,7 +198,7 @@
             _.debounce (@lazyBound 'previewCss', no), 500

           CSSEditor.on "RunRequested", @lazyBound 'previewCss', yes
-          CSSEditor.on "AutoRunRequested", toggleAutoRun
+          CSSEditor.on "AutoRunRequested", @bound 'toggleLiveReload'

   previewApp:(force = no)->
@@ -279,6 +273,22 @@

     KodingAppsController.compileAppOnServer JSEditor.getData()?.path, ->
       log "COMPILE", arguments
+
+  toggleLiveReload:(state)->
+
+    if state?
+    then @liveMode = state
+    else @liveMode = !@liveMode
+
+    new KDNotificationView
+      title: if @liveMode then 'Live compile enabled' \
+                          else 'Live compile disabled'
+
+    @storage.setValue 'liveMode', @liveMode
+    return  unless @liveMode
+
+    KD.utils.defer =>
+      @previewApp yes; @previewCss yes

 class DevToolsEditorPane extends CollaborativeEditorPane


tiniest edit ever: remove a linebreak

tiniest edit ever: remove a linebreak

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -69,7 +69,6 @@
       error: (model, xhr, options) ->
         console.warn xhr

-
   view: (datasetID, viewID) ->
     dataset = Cu.Model.Dataset.findOrCreate
       user: window.user.effective.shortName


semicolon

semicolon

--- client/code/view/titles.coffee
+++ client/code/view/titles.coffee
@@ -55,7 +55,7 @@
     $input = @$el.find('input')
     @newName = $.trim($input.val())
     if @newName == '' or @newName == $label.text()
-      $label.show().next().hide();
+      $label.show().next().hide()
     else
       $input.hide()
       $label.text(@newName).show()


Highlight code block upon visual mode and upon save

--- client/views/admin/edit.coffee
+++ client/views/admin/edit.coffee
@@ -24,9 +24,11 @@
     wrap_line_length: 0
   ).replace(/\n/g, "\n\n")

+# Help return medium editor's contents
 MediumEditor.prototype.scrubbed = ->
   @serialize()['element-0'].value

+# Set up the medium editor with image upload
 makeEditor = (tpl) ->
   editor = new MediumEditor '.editable',
     placeholder: 'Start typing...'
@@ -64,6 +66,30 @@
       #embeds: {}

   editor
+
+# Toggle between visual and HTML mode
+setEditMode = (tpl, mode) ->
+  tpl.$('.editable').toggle()
+  tpl.$('.html-editor').toggle()
+  tpl.$('.edit-mode a').removeClass 'selected'
+  tpl.$(".#{mode}-toggle").addClass 'selected'
+
+# Highlight code blocks
+highlightSyntax = (tpl) ->
+  if Blog.settings.syntaxHighlighting
+    hljs.configure userBR: true
+
+    br2nl = (i, html) ->
+      html
+        # medium-editor leaves
's in
 tags, which screws up
+        # highlight.js. Replace them with actual newlines.
+        .replace(/
/g, "\n")
+        # Strip out highlight.js tags so we don't create them multiple times
+        .replace(/<[^>]+>/g, '')
+
+    # Remove 'hljs' class we don't create it multiple times
+    tpl.$('pre').removeClass('hljs').html(br2nl).each (i, block) ->
+      hljs.highlightBlock(block)

 Template.blogAdminEdit.rendered = ->
   # Tags
@@ -82,34 +108,12 @@
     this.tagsinput('input').typeahead('val', '')
   , $('input[data-role="tagsinput"]'))

-  # Syntax highlighting
-  if Blog.settings.syntaxHighlighting
-    hljs.configure({userBR: true})
-
-    br2nl = (i, html) ->
-      html
-        # medium-editor leaves
's in
 tags, which screws up
-        # highlight.js. Replace them with actual newlines.
-        .replace(/
/g, "\n")
-        # Strip out highlight.js tags so we don't create them multiple times
-        .replace(/<[^>]+>/g, '')
-
-    # Remove 'hljs' class we don't create it multiple times
-    @$('pre').removeClass('hljs').html(br2nl).each (i, block) ->
-      hljs.highlightBlock(block)
-
   # Medium editor
   makeEditor @

 Template.blogAdminEdit.helpers
   post: ->
     getPost()
-
-setEditMode = (tpl, mode) ->
-  tpl.$('.editable').toggle()
-  tpl.$('.html-editor').toggle()
-  tpl.$('.edit-mode a').removeClass 'selected'
-  tpl.$(".#{mode}-toggle").addClass 'selected'

 Template.blogAdminEdit.events
   'click .mediumInsert-action': (e, tpl) ->
@@ -126,6 +130,7 @@

     post = getPost()
     post.body = $html.val()?.trim()
+    highlightSyntax tpl
     setEditMode tpl, 'visual'

   'click .html-toggle': (e, tpl) ->
@@ -164,6 +169,9 @@
     $editable.find('p:not([data-section-id])').each ->
       $(this).addClass('commentable-section').attr('data-section-id', i)
       i++
+
+    # Highlight code blocks
+    highlightSyntax tpl

     if $editable.get(0)
       editor = makeEditor tpl


Test securerandom.

Test securerandom.

--- test/test.coffee
+++ test/test.coffee
@@ -70,3 +70,14 @@
       phrase = gen.phrase(10)
       assert.equal phrase.split(' ').length, 10

+  describe 'secure_random', ->
+    random = null
+
+    before ->
+      random = require('../lib/secure_random')
+
+    it '<= 1', repeat ->
+      assert.operator random(), '<=', 1
+
+    it '>= 0', repeat ->
+      assert.operator random(), '>=', 0


Test parsing multiple examples origin

Test parsing multiple examples origin

--- test/test.coffee
+++ test/test.coffee
@@ -2,7 +2,10 @@
 SegfaultHandler.registerHandler()

 protagonist = require '../build/Release/protagonist'
-{assert} = require 'chai'
+
+chai = require 'chai'
+chai.Assertion.includeStack = true;
+assert = chai.assert

 describe "API Blueprint parser", ->

@@ -391,3 +394,60 @@
       assert.strictEqual action.parameters.id.values.length, 0

       done()
+
+  it 'parses multiple transactions', (done) ->
+    source = """
+# /resource
+## GET
+
++ request A
+
+        A
+
++ response 200
+
+        200-A
+
++ request B
+
+        B
+
++ response 200
+
+        200-B
+
+"""
+
+    protagonist.parse source, (err, result) ->
+      assert.isNull err
+
+      assert.strictEqual result.ast.resourceGroups.length, 1
+      assert.strictEqual result.ast.resourceGroups[0].resources.length, 1
+      assert.strictEqual result.ast.resourceGroups[0].resources[0].actions.length, 1
+      assert.strictEqual result.ast.resourceGroups[0].resources[0].actions[0].examples.length, 2
+
+      example = result.ast.resourceGroups[0].resources[0].actions[0].examples[0]
+      assert.strictEqual example.requests.length, 1
+      assert.strictEqual example.responses.length, 1
+
+      request = example.requests[0]
+      assert.strictEqual request.name, 'A'
+      assert.strictEqual request.body, 'A\n'
+
+      response = example.responses[0]
+      assert.strictEqual response.name, '200'
+      assert.strictEqual response.body, '200-A\n'
+
+      example = result.ast.resourceGroups[0].resources[0].actions[0].examples[1]
+      assert.strictEqual example.requests.length, 1
+      assert.strictEqual example.responses.length, 1
+
+      request = example.requests[0]
+      assert.strictEqual request.name, 'B'
+      assert.strictEqual request.body, 'B\n'
+
+      response = example.responses[0]
+      assert.strictEqual response.name, '200'
+      assert.strictEqual response.body, '200-B\n'
+
+      done()


update for optional path

update for optional path

--- test/test.coffee
+++ test/test.coffee
@@ -20,7 +20,12 @@

 describe 'sprout', ->
   it 'inits the project properly', (done) ->
-    tgt = path.join(test_path, 'index.jade')
+    if fs.existsSync(path.join(test_path, 'views'))
+      tgt = path.join(test_path, 'views/index.jade')
+
+    else
+      tgt = path.join(test_path, 'index.jade')
+
     fs.existsSync(tgt).should.be.ok
     done()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -6,5 +6,4 @@
 test "text fades out", ->
   ;

-
 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -25,7 +25,7 @@

   equals text.I.alpha, 1

-  text.update(1)
+  text.update(0.5)
   text.trigger "afterUpdate", 1

   equals text.I.alpha, 1 - 1/40


Uppercase keys count suffix

Uppercase keys count suffix

--- resources/assets/coffee/react/beatmap-discussions/beatmap-list-item.coffee
+++ resources/assets/coffee/react/beatmap-discussions/beatmap-list-item.coffee
@@ -26,7 +26,7 @@
   topClasses += " #{bn}--large" if props.large

   version = if props.beatmap.mode == 'mania'
-    "[#{props.beatmap.difficulty_size}k] #{props.beatmap.version}"
+    "[#{props.beatmap.difficulty_size}K] #{props.beatmap.version}"
   else
     props.beatmap.version


fix empty-line

fix empty-line

--- views/components/main/parts/ndock-panel.cjsx
+++ views/components/main/parts/ndock-panel.cjsx
@@ -115,7 +115,6 @@
                     <OverlayTrigger placement='right' overlay={<Tooltip>完成时间: {timeToString @state.docks[i].completeTime}</Tooltip>}>
                       {resolveTime @state.docks[i].countdown}
                     </OverlayTrigger>
-
                   else if @state.docks[i].countdown > -1
                     {resolveTime @state.docks[i].countdown}
                   else


Don't loose label/hint editor, fixes #497

--- src/resources/forms/orbeon/builder/resources/cell/placeholders.coffee
+++ src/resources/forms/orbeon/builder/resources/cell/placeholders.coffee
@@ -71,7 +71,9 @@
     # Function dealing with editables
     anyEditableSelector = _.pluck (_.values editables), 'selector'                                                      # CSS selectors for all the editables
     editable = (element) -> _.first _.filter (_.values editables), (e) -> f$.is '*', f$.findOrIs e.selector, element    # element: editable -> editable info
-    editableEditInput = (element) -> $ (editable element).editInputSelector                                             # Input field used for editing
+    editableEditInput = do ->                                                                                # Input field used for editing
+        selectorToInput = _.memoize (selector) -> $ selector                                                            # Avoid "loosing" editors when HTML is replaced
+        (element) -> selectorToInput (editable element).editInputSelector
     editablePlaceholderOutput = (element) -> $ (editable element).placeholderOutputSelector                             # Element in which we put the placeholder
     editableInside = (selectorType, element) -> f$.findOrIs (editable element)[selectorType], element                   # Find an element inside a container element for a certain type of selector
     editablePlaceholderContainer = (element) -> editableInside 'placeholderContainerSelector', element


fixed a test in parser.coffee

fixed a test in parser.coffee

--- src/parser.coffee
+++ src/parser.coffee
@@ -96,6 +96,6 @@
     else parserError "Encountered unexpected token of type '#{token.type}'"

   result = parseBody()
-  #     'hello'} ⍝ fails 'ParserError'
+  #     'hello'} ⍝ fails
   demand 'eof'
   result


Only require colors when outputting missing docs

--- src/parser.coffee
+++ src/parser.coffee
@@ -1,5 +1,4 @@
 fs           = require 'fs'
-colors       = require 'colors'
 _            = require 'underscore'
 _.str        = require 'underscore.string'
 CoffeeScript = require 'coffee-script'
@@ -311,6 +310,7 @@
       """

     if @options.missing
+      require 'colors'
       noDocClassNames = []
       for noDocClass in noDocClasses
         noDocClassNames.push noDocClass.className.cyan


Fix typo.

Fix typo.

--- src/master.coffee
+++ src/master.coffee
@@ -88,7 +88,7 @@
   , FORCE_KILL_TIMEOUT

 module.exports =
-  run: (server)
+  run: (server) ->
     log.info "Starting openbid #{settings.version}"
     fork() for n in [1..WORKERS]


ComputeProviders: Add defensive stackTemplate check to fetchStackTemplate function

--- workers/social/lib/social/models/computeproviders/computeutils.coffee
+++ workers/social/lib/social/models/computeproviders/computeutils.coffee
@@ -133,6 +133,10 @@

     { user, group, account } = res

+    unless group.stackTemplates?.length
+      console.warn "Failed to fetch stack template for #{group.slug} group"
+      return callback new KodingError "Template not set", "NotFound"
+
     # TODO Make this works with multiple stacks ~ gg
     stackTemplateId = group.stackTemplates[0]


修复了无需验证码的一个bug

修复了无需验证码的一个bug

--- src/qqauth.coffee
+++ src/qqauth.coffee
@@ -225,7 +225,7 @@
     log.info '登录 step0 验证码检测'
     auth.check_qq qq , (result) ->
       # log.debug "验证帐号:", result
-      [need_verify,verifycode,bits] = result
+      [need_verify,verify_code,bits] = result
       if int need_verify
         log.info "登录 step0.5 获取验证码"
         auth.get_verify_code qq, opt.host, opt.port, (error) ->


Remove the @options.bindingOptions check, as we're not passing that in from the view anymore.

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -38,8 +38,6 @@

       formatter = if @model[id] instanceof Function
         @model[id]
-      else if @options?.bindingOptions?.formatters?[id] instanceof Function
-        @options.bindingOptions.formatters[id]
       else
         @view.formatters[id]


better check for existence of `rivets` variable

--- src/rivets.coffee
+++ src/rivets.coffee
@@ -2,7 +2,7 @@
 # Adds Rivets.js functionality to CartJS if Rivets.js is available.
 # -----------------------------------------------------------------

-if rivets
+if rivets?

   # Rivets.js has been loaded, so declare the CartJS.Rivets module.
   CartJS.Rivets =


Actually invoke disconnected callback

Actually invoke disconnected callback

- cleanup dead code comment
- only clear timer if timer is defined

--- src/server.coffee
+++ src/server.coffee
@@ -35,7 +35,7 @@
       @_write_pipe()

     socket.on 'connect_error', (obj) => logger.info 'connect error', obj
-    socket.on 'disconnect',          => @_disconnected
+    socket.on 'disconnect',          => @_disconnected()
     socket.on 'colorChanged', (data) => @_to_buffer(data)
     socket.on 'colorSet',     (data) => @_to_buffer(data)

@@ -45,7 +45,7 @@
   #
   _disconnected: ->
     logger.info "socket at #{@_url()} disconnected"
-    clearTimeout(@timer)
+    clearTimeout(@timer) if @timer?

   #
   # create writestream to kernel at /dev/ttyO1
@@ -73,15 +73,6 @@
           @_write_pipe()
         )
     ), 30
-
-    # setInterval (=>
-    #   if @buffer.length > 0
-    #     @ws.write(@buffer, (err, written) =>
-    #       # writestream flushed
-    #       throw err if err
-    #       @buffer = ''
-    #     )
-    # ), 15

   #
   # convert halo rgba string to a UART instruction


fix small bug

fix small bug

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -46,7 +46,7 @@
     @args  = []
     # #/abc/dsa/ => ["#", "abc", "dsa"] or ["", "abc", "dsa"]
     parts = route.replace(/\/$/, "").split("/")
-
+    parts[0] = "#" if parts[0] == "" # hack
     # mark, this route not have a length and end
     #  #/title/id/*
     # matched with #/title/id/2014 and #/title/id/2014/2020 ...
@@ -72,12 +72,12 @@
     @args = []
     parts = url.replace(/\/$/, "").split("/")

+    parts[0] = "#" if parts[0] == ""
+
     #when not end, and parts have a different length, this not the same routes
     return false if ((parts.length != @parts.length) && @end)
     #when it have a different length, but @parts len > given len
     return false if (@parts.length > 1) && parts.length < @parts.length
-    # when not end, and last parts not the equals
-    return false if @end && (parts[parts.length-1] != @parts[parts.length-1])

     is_named_part = (part) ->
       part.indexOf(":") == 0
@@ -110,6 +110,7 @@
         return false

     @args = args
+
     true

@@ -265,8 +266,7 @@
       route_array = null
       result      = false

-
-      if event.type == "hashchange"
+      if e.type == "hashchange"
         # hashchange
         route_array = array_of_routes
         current = window.location.hash
@@ -314,14 +314,15 @@
       false

-    if plain_routes.length != 0
+    if plain_routes.length != 0
       # bind all <a> element with dispatch function, but bind only when href not contain "#"
       selector  = "a:not([href^='#'])"

       Sirius.Application.adapter.bind selector, "click", dispatcher

     window.onhashchange = dispatcher
-
+#    if push_state_support
+#      Sirius.Application.adapter.bind window, "popstate", dispatcher

     fn()

@@ -438,7 +439,6 @@
     # start
     R.create @route, setting, () => @adapter.fire(document, "application:run", new Date())

-    @logger()
     if @start
       Sirius.redirect(@start)


small fix

small fix

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -404,7 +404,7 @@

     push_state_support = if history.pushState then true else false
-    @logger("History push state support: #{push_state_support}")
+    @logger("History pushState support: #{push_state_support}")

     if !push_state_support && !@use_hash_routing_for_old_browsers
       @logger("Warning! You browser not support pushState, and you disable hash routing for old browser")


sort payload

sort payload

--- ComputerSide/PressTo/Web/Server/server.coffee
+++ ComputerSide/PressTo/Web/Server/server.coffee
@@ -63,8 +63,8 @@
               info.skype_id = memberFound.profile.skype
               info.title = memberFound.profile.title
           payload.push info
-
-        deferred.resolve payload
+        sortedPayload = _(payload).sortBy('name')
+        deferred.resolve sortedPayload
   return deferred.promise

 app.get  '/users',


webapp/course: object leakage bcb8d7e15dcd3f810720d159a906d2a520676a96

--- src/smc-webapp/course/actions.coffee
+++ src/smc-webapp/course/actions.coffee
@@ -46,7 +46,6 @@
 # Manages local and sync changes
 exports.CourseActions = class CourseActions extends Actions
     constructor: (@name, @redux) ->
-        window.a = @
         if not @name?
             throw Error("@name must be defined")
         if not @redux?


more top cleanup that nobody should notice

--- src/smc-webapp/project/file-tab.cjsx
+++ src/smc-webapp/project/file-tab.cjsx
@@ -94,8 +94,6 @@
         x_button_styles =
             float      : 'right'
             whiteSpace : 'nowrap'
-            fontSize   : '12pt'
-            marginTop  : '-3px'

         if @state.x_hovered
             x_button_styles.color = 'lightblue'


Cleaning up main loop.

Cleaning up main loop.

--- src/timber.coffee
+++ src/timber.coffee
@@ -513,11 +513,11 @@
         @collisionHandler.elapse(@elements, elapsed)
         @environment.elapse(@elements, elapsed)

-        # Draw them on the screen.
+        # Update the canvas.
         @canvas.clear()
         @canvas.render(@elements)

-        # Loop again, when the stack clears.
+        # Do it again when the stack clears.
         @timestamp = now
         setTimeout () =>
             @.loop()


Fixes #531

Fixes #531

--- lib/models/git-commit.coffee
+++ lib/models/git-commit.coffee
@@ -13,7 +13,7 @@
 getStagedFiles = (repo) ->
   git.stagedFiles(repo).then (files) ->
     if files.length >= 1
-      git.cmd(['status'], cwd: repo.getWorkingDirectory())
+      git.cmd(['-c', 'color.ui=false', 'status'], cwd: repo.getWorkingDirectory())
     else
       Promise.reject "Nothing to commit."


Using .map instead of .forEach

Using .map instead of .forEach

This cleans up the code a little bit

--- lib/open-last-project.coffee
+++ lib/open-last-project.coffee
@@ -18,9 +18,8 @@
     LastProject = JSON.parse(LastProject)
     return unless LastProject.Path
     atom.project.setPaths [LastProject.Path]
-    Promises = []
-    LastProject.Files.forEach (file)->
-      Promises.push new Promise (resolve)->
+    Promises = LastProject.Files.map (file)->
+      return new Promise (resolve)->
         FS.exists file, (Status)->
           return resolve() unless Status
           atom.workspace.open(file).then(resolve)


Collaboration: Removed wrong binded event handler.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1188,10 +1188,6 @@
     @rtm.once 'FileCreated', (file) =>
       log 'file created', file

-      @rtm.once 'RTMIsReady', =>
-        @chat.emit 'CollaborationStarted'
-        @statusBar.emit 'CollaborationStarted'
-
       @loadCollaborationFile file.id

     @rtm.createFile @getRealTimeFileName()


IDE: channel callback re-adjusted

IDE: channel callback re-adjusted

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1015,7 +1015,7 @@

   createChatPane: ->

-    @startSession (err, [channel]) =>
+    @startChatSession (err, channel) =>

       return KD.showError err  if err


QUALITY: Use unit function for returning alignments

--- src/app/projects/states/dashboard/directives/task-dashboard/directives/task-outcomes-card/task-outcomes-card.coffee
+++ src/app/projects/states/dashboard/directives/task-dashboard/directives/task-outcomes-card/task-outcomes-card.coffee
@@ -10,10 +10,6 @@
   controller: ($scope, $filter, listenerService, outcomeService) ->
     listeners = listenerService.listenTo($scope)
     listeners.push $scope.$watch('task.id', ->
-      filteredAlignments = _.map($scope.task.staffAlignments(), (alignment) ->
-        alignment.label = outcomeService.alignmentLabels[alignment.rating]
-        alignment
-      )
-      $scope.alignments = filteredAlignments
+      $scope.alignments = $scope.task.unit().staffAlignmentsForTaskDefinition($scope.task.definition)
     )
 )


remove file newkitecontroller.coffee

remove file newkitecontroller.coffee

--- client/Main/includes.coffee
+++ client/Main/includes.coffee
@@ -263,7 +263,6 @@
   "kite/oskite.coffee",
   "kite/terminalkite.coffee",
   "kite/kitecontroller.coffee",
-  "kite/newkitecontroller.coffee",
   "kite/vm.coffee", # TODO: this doesn't really belong here.
   "kite/kitehelper.coffee",


Remove spacing from braces for linter.

Remove spacing from braces for linter.

--- lib/pub/pub_component.coffee
+++ lib/pub/pub_component.coffee
@@ -101,7 +101,7 @@

     picker = new Picker()
     projects = (project for project in Utils.getDartProjectPaths()).map (p) ->
-      { item: p, displayName: path.basename(p) }
+      {item: p, displayName: path.basename(p)}
     return picker.selectFrom(projects)

   # Code to prevent multiple pub processes on the same file


Add missing "count" property to MPNS tiles

--- lib/pushservices/mpns.coffee
+++ lib/pushservices/mpns.coffee
@@ -28,7 +28,7 @@

                     when "tile" # live tile under WP 7.5 or flip tile under WP 8.0+
                         map = @conf.tileMapping
-                        properties = ["id", "title", "backgroundImage", "backBackgroundImage", "backTitle", "backContent"]
+                        properties = ["id", "title", "count", "backgroundImage", "backBackgroundImage", "backTitle", "backContent"]
                         if info.version >= 8.0
                             note = new mpns.flipTile()
                             properties.push(["smallBackgroundImage", "wideBackgroundImage", "wideBackContent", "wideBackBackgroundImage"]...)


Allow custom root passing to the resource combiner

--- lib/resource-combiner.coffee
+++ lib/resource-combiner.coffee
@@ -22,8 +22,8 @@
   resource.deferred = undefined

 # TODO : Implement cache invalidation on a periodic basis here
-exports.combine = ({resources, req, separator, contentType, prefix}) ->
-  id = resources.length + (resource.href for resource in resources).join ':'
+exports.combine = ({resources, req, root, separator, contentType, prefix}) ->
+  id = resources.length + (item.href for item in resources).join ':'

   if not resource = idResourceCache[id]
     # Create a new resource and load async
@@ -41,15 +41,16 @@
     path = (prefix ? '') + hash.digest 'hex'
     pathResourceCache[path] = resource

+    root = if root then resourceRequest.relativeRoot(req, root) else resourceRequest.relativeRoot(req)
     for content in resource.content
       do (content) ->
         chunks = []
         resourceRequest.get(
           content.href,
-          req,
+          root,
           (err, response) ->
             if err
-              console.log "Unable to load combined resource #{content.href}", err
+              console.error "Unable to load combined resource #{content.href}", root, err
               content.data = ''
               content.complete = true
             else


workspace follows currently active module

--- lib/runtime/workspace.coffee
+++ lib/runtime/workspace.coffee
@@ -2,6 +2,8 @@

 {views} = require '../ui'
 {client} = require '../connection'
+
+modules = require './modules'

 {workspace, clearLazy} = client.import rpc: 'workspace', msg: 'clearLazy'

@@ -19,7 +21,7 @@
     return @ws.setItems [] unless client.isActive() and @ws.currentPane()
     clearLazy @lazyTrees
     registerLazy = (id) => @lazyTrees.push id
-    p = workspace('Main').then (ws) =>
+    p = workspace(modules.current()).then (ws) =>
       for {items} in ws
         for item in items
           item.value = views.render item.value, {registerLazy}


OMG I used "this" in CoffeeScript

OMG I used "this" in CoffeeScript

--- coffeescript/animals.coffee
+++ coffeescript/animals.coffee
@@ -2,7 +2,7 @@
   constructor: (@name) ->

   speak: ->
-    "#{@name} says #{this.sound()}"
+    "#{@name} says #{@sound()}"

 class Cow extends Animal
   sound: -> "moooo"


Skip id

Skip id

--- components/Transform.coffee
+++ components/Transform.coffee
@@ -68,7 +68,7 @@
         # Calls @foo for each of props.foo inport that has a value
         for k of props
           # Color and type are not expected methods (for color instance)
-          if k isnt 'color' and k isnt 'type'
+          if k isnt 'color' and k isnt 'type' and k isnt 'id'
             # Following DRY, we use a factory to call proper color.method
             cc = @factory(k, cc, props)
         new_colors.push(cc)


Bugfix: take into account 24hr format when trimming hours in clock

--- components/clock/index.cjsx
+++ components/clock/index.cjsx
@@ -55,6 +55,8 @@
         if hour < 12 then hour + 12 else hour
       else
         if hour == 12 then 0 else hour
+    else
+      hour

   _handleResize: ->
     @setState


Use hasOwnProperty to check for pair match

Previously the insertion would be corrupted if the inserted text matched an
Object built-in such as toString or constructor.

Closes atom/atom#1110

--- lib/bracket-matcher.coffee
+++ lib/bracket-matcher.coffee
@@ -92,7 +92,7 @@
     @invertedPairedCharacters

   isOpeningBracket: (string) ->
-    @pairedCharacters[string]?
+    @pairedCharacters.hasOwnProperty(string)

   isClosingBracket: (string) ->
-    @getInvertedPairedCharacters()[string]?
+    @getInvertedPairedCharacters().hasOwnProperty(string)


fix (index): remove unused providers

fix (index): remove unused providers

--- app/index.coffee
+++ app/index.coffee
@@ -5,12 +5,9 @@
 ]

 # Routing
-module.config ($locationProvider, $stateProvider, $urlRouterProvider) ->
+module.config ($locationProvider) ->
   # Use HTML5 mode
   $locationProvider.html5Mode true
-
-  # Redirection rules
-  $urlRouterProvider.otherwise '/'

 # Entry point
 module.run (titleService) ->


bugfix: fix infinite pagination of packages

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- coffee/controllers/main.coffee
+++ coffee/controllers/main.coffee
@@ -52,7 +52,11 @@
           fetchError("fetch nodes on page #{page}")

     $scope.fetchPackages = (page = 1)->
-      Restangular.all('packages').getList({ page: page, limit: 50 }).
+      # construct a query for pagination
+      # page == 0 means that we try to fetch without pagination (usually last attempt)
+      query = if page is 0 then {} else {page: page, limit: 50}
+      # get packages
+      Restangular.all('packages').getList(query).
         then (ns) ->
           console.log "fetch packages (page: #{page})"
           # append the new packages to the list of packages
@@ -70,7 +74,11 @@
                 $scope.package[v.id].version = v.version
           # get packages from the next page
           $scope.fetchPackages( page+1 )
-        , fetchError("fetch packages on page #{page}")
+        , ()->
+          # if we tried with pagination, try now without
+          return $scope.fetchPackages(0) if page is 1
+          # otherwise we have a fetch error
+          fetchError("fetch packages on page #{page}")

     $scope.fetchNode = (id)->
       Restangular.one('node', id).get().


DigitalWrite: fix inverted value

DigitalWrite: fix inverted value

--- components/DigitalWrite.coffee
+++ components/DigitalWrite.coffee
@@ -17,8 +17,9 @@
       @setValue(value)

   setValue: (value) ->
+    @value = value
     return unless @gpio
-    @gpio.set(if value then 0 else 1)
+    @gpio.set(if value then 1 else 0)

   stopGpio: () ->
     return unless @gpio


DigitalWrite: set value on gpio creation if already given

--- components/DigitalWrite.coffee
+++ components/DigitalWrite.coffee
@@ -30,6 +30,7 @@
   startGpio: () ->
     return unless @pinNumber
     @gpio = gpio.export(@pinNumber, { direction: 'out' })
+    @gpio.set(if @value then 1 else 0) if @value

   shutdown: () ->
     @stopGpio()


fixed Comment coffee script

fixed Comment coffee script

--- src/resources/comment.coffee
+++ src/resources/comment.coffee
@@ -10,22 +10,22 @@
 	spam: (id, callback) ->
 		callback new Error 'missing id' unless id?
 		url = @resource.queryString "#{@prefix}/#{id}/spam"
-		@resource.post url, @slug, callback
+		@resource.post url, "", callback

 	notSpam: (id, callback) ->
 		callback new Error 'missing id' unless id?
 		url = @resource.queryString "#{@prefix}/#{id}/not_spam"
-		@resource.post url, @slug, callback
+		@resource.post url, "", callback

 	approve: (id, callback) ->
 		callback new Error 'missing id' unless id?
 		url = @resource.queryString "#{@prefix}/#{id}/approve"
-		@resource.post url, @slug, callback
+		@resource.post url, "", callback

 	remove: (id, callback) ->
 		callback new Error 'missing id' unless id?
 		url = @resource.queryString "#{@prefix}/#{id}/remove"
-		@resource.post url, @slug, callback
+		@resource.post url, "", callback

 	delete: (id, callback) ->
 		@remove(id, callback)


Fix preprocessorSuggestions

Fix preprocessorSuggestions


Display arrows following an upload / reload.

--- app/assets/javascripts/ample_assets/classes/ample_assets.js.coffee
+++ app/assets/javascripts/ample_assets/classes/ample_assets.js.coffee
@@ -12,6 +12,7 @@
   set_options: (opts) ->
     @current = 0
     @keys_enabled = true
+    @reloading = false
     ref = this
     default_options =
       debug: true
@@ -89,6 +90,7 @@

   reload: (i) ->
     @log "reload(#{i})"
+    @reloading = true
     @empty(i)
     @options.pages[i]['loaded'] = false
     @options.pages[i]['pages_loaded'] = 0
@@ -224,6 +226,9 @@
         $(selector).append(li)
       ref.load_img(li.find('a'), el.sizes.tn)
     @panels(i) unless panels_loaded
+    if @reloading
+      @reloading = false
+      @controls()

   load_results: (response) ->
     @log "load_results()"
@@ -422,7 +427,7 @@
         when escape
           @toggle() unless @modal_active
       e.stopPropagation();
-
+
     $(document).keydown (e) =>
       return unless @keys_enabled
       switch e.keyCode


8756: Remove obsolete event line

8756: Remove obsolete event line

--- app/assets/javascripts/backbone/views/work/period_form_view.coffee
+++ app/assets/javascripts/backbone/views/work/period_form_view.coffee
@@ -7,7 +7,6 @@
   events:
     "change #work_period_quota_type": "quotaTypeOrPhaseChanged"
     "change #work_period_phase": "quotaTypeOrPhaseChanged"
-    "change #work_period_auto_open_time": "autoOpenTimeChanged"
     "change #work_period_pick_type": "pickTypeChanged"

   quotaTypeOrPhaseChanged: ->


better to read the badge name

better to read the badge name

--- src/scripts/coderwall.coffee
+++ src/scripts/coderwall.coffee
@@ -19,6 +19,6 @@
           resp_str += user + "'s coderwall -> http://coderwall.com/"+user + "\n"
           # Iterate all badges and continue building string
           profile.badges.forEach (badge) ->
-            resp_str += badge.name + " - " + badge.description + "\n"
+            resp_str += "[" + badge.name + "] - " + badge.description + "\n"
           # Return response
           msg.send resp_str


explicit height

explicit height


Changed: Atom should not open an empty editor on start

--- dotfiles/atom/config.cson
+++ dotfiles/atom/config.cson
@@ -20,6 +20,7 @@
       "language-sql"
       "styleguide"
     ]
+    openEmptyEditorOnStart: false
     projectHome: "/Users/MoOx/Sync/Development"
     themes: [
       "one-light-ui"


queue checks on timer

queue checks on timer

--- Modules/WorkQueue/Queue.coffee
+++ Modules/WorkQueue/Queue.coffee
@@ -46,7 +46,7 @@
     Ti.API.info 'job was error'
     @jobs = @jobs.without job #TODO: GJ: retry or set error state?
     @checking = false
-    @check()
+    @check(true)

   onJobProgress: (job, worker, progress) => #progress between 0 and 1
     Ti.API.info "Job progress #{progress}"


Ace: AppView: Instead creating file path manually use given file instance

[completes #77663968]

--- client/Ace/AppView.coffee
+++ client/Ace/AppView.coffee
@@ -177,15 +177,12 @@
       file.once 'fs.saveAs.finished',   @ace.bound 'saveAsFinished'
       @ace.emit 'AceDidSaveAs', name, parent.path

-      file.on 'fs.saveAs.finished', =>
+      file.on 'fs.saveAs.finished', (newFile) =>
+
         {tabView} = @getDelegate()
         return  if tabView.willClose
-        @getDelegate().openFile \
-          FSHelper.createFileInstance path: "#{parent.path}/#{name}"
-
-        newFile = FSHelper.createFileFromPath "#{parent.path}/#{name}", yes
-        @getDelegate().openFile newFile, contents
-
+
+        @getDelegate().openFile newFile

     , { inputDefaultValue: file.name }


Little change in test spec name

Little change in test spec name

--- test/spec/invoke.spec.coffee
+++ test/spec/invoke.spec.coffee
@@ -1,4 +1,4 @@
-describe("Overloaded functions", ->
+describe("Invoking overloaded functions", ->
   overloadableFunction = null
   spiedFunction = jasmine.createSpy()


ensure that document title is always properly set

--- client/app/views/album.coffee
+++ client/app/views/album.coffee
@@ -62,26 +62,28 @@
         res

     afterRender: ->
+
+        # Use title of album as window title
+        document.title = "#{t 'application title'} - #{@model.get 'title'}"
+
+        @title = @$ '#title'
+        @description = @$ '#description'
+
         @galery = new Galery
             el: @$ '#photos'
             editable: @options.editable
             collection: @model.photos
             beforeUpload: @beforePhotoUpload

-        @title = @$ '#title'
-        @description = @$ '#description'
-
         @galery.album = @model
         @galery.render()
-
-        # Use title of album as window title
-        document.title = "#{t 'application title'} - #{@model.get 'title'}"

         if @options.editable
             @makeEditable()
         else
             @title.addClass 'disabled'
             @description.addClass 'disabled'
+

     beforePhotoUpload: (callback) =>
         callback albumid: @model.id
@@ -93,11 +95,13 @@
         @saveModel description: @description.val().trim()

     makeEditable: =>
+        document.title = "#{t 'application title'} - #{@model.get 'title'}"
         @$el.addClass 'editing'
         @options.editable = true
         @galery.options.editable = true

     makeNonEditable: =>
+        document.title = "#{t 'application title'} - #{@model.get 'title'}"
         @$el.removeClass 'editing'
         @options.editable = false
         @galery.options.editable = false
@@ -121,6 +125,7 @@
                 event.preventDefault()
                 @model.destroy().then ->
                     app.router.navigate 'albums', true
+        true

     # Change sharing state of the album.
     changeClearance: (event) =>


minor fix in utils.jsonp

minor fix in utils.jsonp

--- app/assets/javascripts/uploadcare/utils.js.coffee
+++ app/assets/javascripts/uploadcare/utils.js.coffee
@@ -53,8 +53,8 @@
       else
         fn1 or fn2
     pr.then(
-      compose(doneFilter, df.resolve),
-      compose(failFilter, df.reject),
+      compose(doneFilter, df.resolve),
+      compose(failFilter, df.reject),
       compose(progressFilter, df.notify)
     )
     df.promise()
@@ -89,7 +89,7 @@
   ns.fullUuidRegex = new RegExp("^#{ns.uuidRegex.source}$", 'i')
   ns.cdnUrlModifiersRegex = /(?:-\/(?:[a-z0-9_,]+\/)+)+/i

-  ns.escapeRegExp = (str) ->
+  ns.escapeRegExp = (str) ->
     str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")

   ns.normalizeUrl = (url) ->
@@ -187,7 +187,7 @@
     labels = 'B KB MB GB TB PB EB ZB YB'.split ' '
     for label, i in labels
       if value < 512 or i is labels.length - 1
-        return "#{prefix}#{value} #{label}#{postfix}"
+        return "#{prefix}#{value} #{label}#{postfix}"
       value = Math.round(value / 1024)

   ns.jsonp = (url, data) ->
@@ -198,5 +198,7 @@
         $.Deferred().reject(text)
       else
         data
-    , (_, textStatus, errorThrown) ->
-      "JSONP unexpected error: #{textStatus} (#{errorThrown})"
+    , (_, textStatus, errorThrown) ->
+      text = "#{textStatus} (#{errorThrown})"
+      ns.warn("JSONP unexpected error: #{text}")
+      text


fix course people page in IE8. fixes #10700

was previously using native map, which isn't supported in
IE8. replaced call with underscore's _.map.

test plan:
  * load course people page in IE8;
  * verify that users load as expected and no javascript errors
    are thrown.

Change-Id: I00121bb9302899337c91c78a155cac3de30cc6da
Reviewed-on: https://gerrit.instructure.com/13817
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/views/courses/RosterView.coffee
+++ app/coffeescripts/views/courses/RosterView.coffee
@@ -83,7 +83,7 @@
     #
     # Return an array of section names.
     getSections: (user) ->
-      sections = user.get('enrollments').map (enrollment) =>
+      sections = _.map user.get('enrollments'), (enrollment) =>
         @collection.sections.find (section) -> enrollment.course_section_id == section.id

       _.uniq(_.map(sections, (section) -> section.get('name')))


Remove unused namespace

Remove unused namespace

subscribe was being used so a namespace is not needed.

--- lib/snippet-expansion.coffee
+++ lib/snippet-expansion.coffee
@@ -14,7 +14,7 @@
     @editor.transact =>
       [newRange] = @editor.insertText(snippet.body, autoIndent: false)
       if snippet.tabStops.length > 0
-        @subscribe @editor, 'cursor-moved.snippet-expansion', (e) => @cursorMoved(e)
+        @subscribe @editor, 'cursor-moved', (event) => @cursorMoved(event)
         @placeTabStopMarkers(startPosition, snippet.tabStops)
         @editor.snippetExpansion = this
         @editor.normalizeTabsInBufferRange(newRange)


Show the icon when using the symbol provider

--- lib/snippets-provider.coffee
+++ lib/snippets-provider.coffee
@@ -4,6 +4,9 @@
 module.exports =
 class SnippetsProvider
   selector: '*'
+
+  constructor: ->
+    @showIcon = atom.config.get('autocomplete-plus.defaultProvider') is 'Symbol'

   getSuggestions: ({scopeDescriptor, prefix}) ->
     return unless prefix?.length
@@ -14,7 +17,7 @@
     return [] unless snippets?

     for __, snippet of snippets when snippet.prefix.lastIndexOf(prefix, 0) isnt -1
-      iconHTML: '<i class="icon-move-right"></i>'
+      iconHTML: if @showIcon then undefined else false
       type: 'snippet'
       text: snippet.prefix
       replacementPrefix: prefix


removed useless file

removed useless file


fix loader

fix loader

--- src/components/dashboard-templates-selector/dashboard-templates-selector.component.coffee
+++ src/components/dashboard-templates-selector/dashboard-templates-selector.component.coffee
@@ -10,12 +10,15 @@
       ctrl.templates = []
       ctrl.selectedTemplate = {}
       ctrl.isTemplatesMode = false
+      ctrl.hideLoader = false
       ImpacDhbTemplatesSvc.index().then(
         (templates)->
           ctrl.templates = templates
         ->
+          toastr.error('Failed to retrieve dashboard templates. You can still create your dashboard from scratch.', 'Error')
+      ).finally(
+        ->
           ctrl.hideLoader = true
-          toastr.error('Failed to retrieve dashboard templates. You can still create your dashboard from scratch.', 'Error')
       )

     ctrl.hasTemplates = ->


Actually adds the functionality that was attempted with commit 5f8a7bd7b9881307dd6c428106d597af68aebb83.  The problem with that code is that it is comparing two strings as if they are numbers, which does not work.  For instance, if your jsdom.version is '0.10.1' then it will evaluate in the code as 'less than' '0.7.0', make an incorrect call to jsdom.env and nothing will work.  I've added a quick and dirty, basic function to compare version number strings.  It can certainly be improved upon, but should work well enough.

--- src/scripts/http-info.coffee
+++ src/scripts/http-info.coffee
@@ -54,8 +54,21 @@
         else if title
           msg.send "#{title}"

+    versionCompare = (v1, v2, comparison) ->
+      v1parts = v1.split('.')
+      v2parts = v2.split('.')
+
+      for value1, i in v1parts
+        value1 = parseInt(value1, 10)
+        value2 = parseInt(v2parts[i], 10)
+        if comparison == '<' and value1 < value2
+          return 1
+        if comparison == '>' and value1 > value2
+          return 1
+      return 0
+
     unless ignore
-      if jsdom.version < '0.7.0'
+      if versionCompare jsdom.version, '0.7.0', '<'
         jsdom.env
           html: url
           scripts: [ jquery ]


added more trollicons

added more trollicons

--- src/scripts/trollicon.coffee
+++ src/scripts/trollicon.coffee
@@ -21,7 +21,12 @@
   'nothing'    : 'http://i.imgur.com/Nwos9.png',
   'pokerface'  : 'http://i.imgur.com/dDjvG.png',
   'ok'         : 'http://i.imgur.com/QRCoI.png',
-  'sadtroll'   : 'http://i.imgur.com/gYsxd.png'
+  'sadtroll'   : 'http://i.imgur.com/gYsxd.png',
+  'yuno'       : 'http://i.imgur.com/sZMnV.png',
+  'true'       : 'http://i.imgur.com/oealL.png',
+  'freddie'    : 'http://i.imgur.com/zszUl.png',
+  'forever'    : 'http://i.imgur.com/5MBi2.png'
+
 }

 module.exports = (robot)->


Handle backspaces in input mode

Handle backspaces in input mode

--- lib/operators/input.coffee
+++ lib/operators/input.coffee
@@ -58,15 +58,18 @@
   constructor: (@transaction) ->

   buildInsertText: ->
-    return undefined unless @isJustTyping()
-    typedCharacters = (patch.newText for patch in @transaction.patches)
-    typedCharacters.join("")
+    chars = []
+    for patch in @transaction.patches
+      switch
+        when @isTypedChar(patch) then chars.push(@isTypedChar(patch))
+        when @isBackspacedChar(patch) then chars.pop()
+    chars.join("")

-  isJustTyping: ->
-    return undefined unless @transaction
-    return true # required for subclasses like Change
-    window.trans = @transaction
-    typedSingleChars = (patch.oldText == "" && patch.newText != "" for patch in @transaction.patches)
-    _.every(typedSingleChars)
+  isTypedChar: (patch) ->
+    return false unless patch.newText?.length >= 1 and patch.oldText?.length == 0
+    patch.newText
+
+  isBackspacedChar: (patch) ->
+    patch.newText == "" and patch.oldText?.length == 1

 module.exports = {Input, Change}


Now loading stored highlights into the page from Readmill


Fix labeling labels and consts

Fix labeling labels and consts


started fixing a bug

started fixing a bug

--- app/helper/imageHelper.coffee
+++ app/helper/imageHelper.coffee
@@ -97,6 +97,7 @@

   @saveImageJson: (image,process) ->
     base64Data = image.replace(/^data:image\/png;base64,/, "")
+    console.log 'name: ' + process.image.name + ' - ext: ' + process.image.extension
     fs.writeFileSync(process.outputFolder + '/' + process.image.name + '.' + process.image.extension,base64Data, 'base64')
   # ---
   # **saveImageUrl**</br>
@@ -189,7 +190,7 @@
           rootFolder: path.join(path.dirname(imagePath),'..')
           folder: path.dirname(imagePath)
           name: filename
-          extension: extension
+          extension: extension.substring(1)
           path: imagePath
           md5: md5
       images.push image


fix copy paste error

fix copy paste error

--- src/blueprints/browser.coffee
+++ src/blueprints/browser.coffee
@@ -69,13 +69,13 @@
       expects: statusOk
       useCustomerToken: true

-    confirm:
+    updateOrder:
       url:     (x) -> "/account/order/#{x.orderId ? x.id ? x}"
       method:  'PATCH'
       expects: statusOk
       useCustomerToken: true

-    updateOrder:
+    confirm:
       url:     (x) -> "/account/confirm/#{x.tokenId ? x}"
       method:  'POST'
       expects: statusOk


Solve overly large padding at the bottom, for #507

--- src/resources/forms/orbeon/builder/resources/cell/control-resources-editor/actions.coffee
+++ src/resources/forms/orbeon/builder/resources/cell/control-resources-editor/actions.coffee
@@ -79,6 +79,7 @@
         mceConfig = _.clone(YAHOO.xbl.fr.Tinymce.DefaultConfig)
         mceConfig.plugins += ',autoresize'
         mceConfig.autoresize_min_height = 100
+        mceConfig.autoresize_bottom_margin = 16 # Default padding for autoresize adds too much empty space at the bottom

         tinymceObject = new window.tinymce.Editor(tinymceAnchor.attr('id'), mceConfig)
         tinymceObject.render()


remove unbalanced bracket in zootorial steps

--- app/lib/tutorial_steps.coffee
+++ app/lib/tutorial_steps.coffee
@@ -37,7 +37,7 @@
     className: 'arrow right'

     onEnter: ->
-      @toHighlight = $('button[value="like"][name="characteristic"], [button[value="likeAntelopeDeer"][name="characteristic-value"]')
+      @toHighlight = $('button[value="like"][name="characteristic"], button[value="likeAntelopeDeer"][name="characteristic-value"]')
       @toHighlight.css 'background-color': '#490'

     onExit: ->