Persist reminders across restarts

Persist reminders across restarts

--- src/scripts/remind.coffee
+++ src/scripts/remind.coffee
@@ -5,27 +5,41 @@
 #                                      Time segments are optional, as are commas

 class Reminders
-  constructor: ->
+  constructor: (@robot) ->
     @cache = []
     @current_timeout = null
+
+    loadReminders = =>
+      if @robot.brain.data.reminders
+        @cache = @robot.brain.data.reminders
+        @queue()
+    setTimeout loadReminders, 5000 # Hacky until there's some kind of 'loaded' notification

   add: (reminder) ->
     @cache.push reminder
     @cache.sort (a, b) -> a.due - b.due
+    @robot.brain.data.reminders = @cache
     @queue()
+
+  removeFirst: ->
+    reminder = @cache.shift()
+    @robot.brain.data.reminders = @cache
+    return reminder

   queue: ->
     clearTimeout @current_timeout if @current_timeout
     if @cache.length > 0
       now = new Date().getTime()
-      trigger = =>
-        reminder = @cache.shift()
-        reminder.msg.send reminder.msg.message.user.name + ', you asked me to remind you to ' + reminder.action
-        @queue()
-      @current_timeout = setTimeout trigger, @cache[0].due - now
+      @removeFirst() until @cache.length is 0 or @cache[0].due > now
+      if @cache.length > 0
+        trigger = =>
+          reminder = @removeFirst()
+          @robot.send reminder.for, reminder.for.name + ', you asked me to remind you to ' + reminder.action
+          @queue()
+        @current_timeout = setTimeout trigger, @cache[0].due - now

 class Reminder
-  constructor: (@msg, @time, @action) ->
+  constructor: (@for, @time, @action) ->
     @time.replace(/^\s+|\s+$/g, '')

     periods =
@@ -59,12 +73,12 @@

 module.exports = (robot) ->

-  reminders = new Reminders
+  reminders = new Reminders robot

   robot.respond /remind me in ((?:(?:\d+) (?:weeks?|days?|hours?|hrs?|minutes?|mins?|seconds?|secs?)[ ,]*(?:and)? +)+)to (.*)/i, (msg) ->
     time = msg.match[1]
     action = msg.match[2]
-    reminder = new Reminder msg, time, action
+    reminder = new Reminder msg.message.user, time, action
     reminders.add reminder
     msg.send 'I\'ll remind you to ' + action + ' on ' + reminder.dueDate()


Chrome has a different default charWidth

--- spec/atom/editor-spec.coffee
+++ spec/atom/editor-spec.coffee
@@ -52,13 +52,13 @@

     describe "when soft-wrap is enabled", ->
       beforeEach ->
-        editor.width(9 * 50)
+        editor.width(11 * 50)
         editor.setSoftWrap(true)
         editor.attachToDom()
         # this verifies the assumption made above in setting the editor's width
         # charWidth isn't be calculated until it's the editor is on the DOM, but
         # we need to ensure that the maxLineLength is recalculated when we attach.
-        expect(editor.charWidth).toBe 9
+        expect(editor.charWidth).toBe 11

       it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", ->
         expect(editor.lines.find('.line').length).toBe 16


Allow dataType in multipleSelect

Allow dataType in multipleSelect

--- controls/MultiSelect.jsx.coffee
+++ controls/MultiSelect.jsx.coffee
@@ -5,6 +5,7 @@
 StandardErrorDisplayMixin = require('./mixins/StandardErrorDisplay')
 DisableOnSubmitMixin = require('./mixins/DisableOnSubmit')
 HelpMixin = require('./mixins/Help')
+DataTypeConversionMixin = require("./mixins/DataTypeConversion")

 MultiSelectField = React.createClass(
   mixins: [
@@ -12,6 +13,7 @@
     StandardErrorDisplayMixin
     DisableOnSubmitMixin
     HelpMixin
+    DataTypeConversionMixin
   ]

   propTypes:
@@ -22,8 +24,8 @@
     dataKey: React.PropTypes.string.isRequired

   onChange: (event) ->
-    value = _.map event.target.selectedOptions, (option) ->
-      option.value
+    value = _.map event.target.selectedOptions, (option) =>
+      @convertDataType(option.value)

     @props.onDataChanged(@props.dataKey, value)


Fixed abort method to bind this correctly

--- src/server/session.coffee
+++ src/server/session.coffee
@@ -56,7 +56,10 @@
     docState = {}

     abort = ->
-      (session.stop ? session.close)()
+      if session.stop
+        session.stop()
+      else
+        session.close()

     # We'll only handle one message from each client at a time.
     handleMessage = (query) ->


Drop suffix from handler path

Drop suffix from handler path

--- spec/stdlib/task-spec.coffee
+++ spec/stdlib/task-spec.coffee
@@ -8,7 +8,7 @@
         spyOn(console, 'error')
         spyOn(console, 'warn')

-        jqueryTask = new Task('fixtures/jquery-task-handler.coffee')
+        jqueryTask = new Task('fixtures/jquery-task-handler')
         jqueryLoaded = false
         jqueryTask.start (loaded) -> jqueryLoaded = loaded


DE18339 - Fixed RoadmapScrollable.js to handle end dates correctly

--- roadmapplanningboard/plugin/RoadmapScrollableSpec.coffee
+++ roadmapplanningboard/plugin/RoadmapScrollableSpec.coffee
@@ -13,12 +13,12 @@
     createBacklogColumn: (id) ->
       xtype: 'backlogplanningcolumn', testId: "#{id}"

-    createColumn: (id, date = new Date()) ->
+    createColumn: (id, date = new Date(), offset = 0) ->
       timeframeRecord = Ext.create Rally.apps.roadmapplanningboard.AppModelFactory.getTimeframeModel(),
         id: "#{id}"
         name: "#{id}"
-        start: date
-        end: date
+        start: Ext.Date.add(date, Ext.Date.MONTH, offset-1)
+        end: Ext.Date.add(Ext.Date.add(date, Ext.Date.MONTH, offset), Ext.Date.DAY, -1)
       planRecord = Ext.create Rally.apps.roadmapplanningboard.AppModelFactory.getPlanModel(),
         id: "#{id}"
         name: "#{id}"
@@ -43,16 +43,15 @@
       , config

       id = 0
-      pastDate = new Date '2012-01-01'
-      presentDate = Ext.Date.add(new Date(), Ext.Date.DAY, 1)
+      date = config.date || new Date(Ext.Date.format(new Date(), 'Y-m-d'))

       columns = [
         @createBacklogColumn(id++)
       ]

-      columns = columns.concat (@createColumn(num, pastDate) for num in [id...id+(config.pastColumnCount or 0)])
+      columns = columns.concat (@createColumn(num, date, num-config.pastColumnCount) for num in [id...id+(config.pastColumnCount or 0)])
       id += config.pastColumnCount
-      columns = columns.concat (@createColumn(num, presentDate) for num in [id...id+(config.presentColumnCount or 0)])
+      columns = columns.concat (@createColumn(num, date, num-config.pastColumnCount) for num in [id...id+(config.presentColumnCount or 0)])

       @cardboard = Ext.create 'Rally.apps.roadmapplanningboard.PlanningBoard',
         _.extend
@@ -134,6 +133,11 @@
       @createCardboard(pastColumnCount: 4, presentColumnCount: 4, timeframeColumnCount: 4).then =>
         expect(@plugin.getFirstVisibleScrollableColumn().testId).toEqual '5'

+    it 'should show current timeframe if the timeframe ends today', ->
+      tomorrow = Ext.Date.add(new Date(), Ext.Date.DAY, 1) # This will actually move a past column to the present columns
+      @createCardboard(pastColumnCount: 4, presentColumnCount: 4, date: tomorrow).then =>
+        expect(@plugin.getFirstVisibleScrollableColumn().testId).toEqual '4'
+
     it 'should show a left scroll arrow for past timeframes', ->
       @createCardboard(pastColumnCount: 1, presentColumnCount: 4, timeframeColumnCount: 4).then =>
         expect(@plugin.backwardsButton.hidden).toBe false


Preload Doppio when the page is loaded

Preload Doppio when the page is loaded

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -25,14 +25,12 @@
     root.doppioReady = false
     root.doppioPreloaded = false
     root.doppioAPI = new DoppioApi null, root.log
-    return
-
-root.preloadDoppio = (backEnd) ->
+    root.preloadDoppio()
+    return
+
+root.preloadDoppio = ->
     if root.doppioPreloaded == false
-        if backEnd == 'doppio'
-            root.doppioAPI.preload root.currentQuest.commandBeanshell, root.wrapperCompiled
-        else
-            root.doppioAPI.preload "", -> return
+        root.doppioAPI.preload root.currentQuest.commandBeanshell, root.wrapperCompiled
         root.doppioPreloaded = true
     return

@@ -93,7 +91,6 @@
     #Todo FADE IN

     description = root.getGameDescriptions()[game]
-    root.preloadDoppio description.backEnd
     env = {
         key: game
         description : description


gameMap now updates on a game won

gameMap now updates on a game won

--- web/scripts/codeland.coffee
+++ web/scripts/codeland.coffee
@@ -30,6 +30,7 @@
 root.storeGameCompletionData = (key, data) ->
     throw new Error("Cannot be null") unless key? && data?
     root.updatePlayer( (p)-> p.games[key] = data )
+    root.drawGameMap()
     return

 root.getGame = ->
@@ -167,7 +168,6 @@

 root.canPlay = (game) ->
-
     player = root.getPlayer()
     #If already completed then no need to check dependencies
     return true if player?.games[game]?.passed
@@ -185,6 +185,7 @@
  # FRONTEND UI
 root.drawGameMap = ->
     mapDiv = $('#mapdiv')
+    mapDiv.empty()
     gameSequence = root.getGameSequence()
     player = root.getPlayer()
     descriptions = root.getGameDescriptions()
@@ -203,6 +204,7 @@
         else
             entry.css('background-color','gray')
         entry.appendTo(mapDiv)
+        return

     mapDiv.empty()
     addGameToMap game for game in gameSequence


pagination control will refresh on render

--- src/modules/paginatable.coffee
+++ src/modules/paginatable.coffee
@@ -22,6 +22,8 @@
     @on "after:refresh", (models, query, options)=>
       _.defer ()=>
         @updatePagination.call(@, models, query, options)
+
+    @defer("refresh").until("after:render")

     if old = @getQueryOptions
       @getQueryOptions = ()->


ComputeUtils: no need a fat arrow here

ComputeUtils: no need a fat arrow here

--- workers/social/lib/social/models/computeproviders/computeutils.coffee
+++ workers/social/lib/social/models/computeproviders/computeutils.coffee
@@ -27,7 +27,7 @@
   { connection: { delegate:account }, context: { group } } = client

   JGroup = require '../group'
-  JGroup.one { slug: group }, (err, groupObj)=>
+  JGroup.one { slug: group }, (err, groupObj)->

     return callback err  if err
     return callback new KodingError "Group not found"  unless groupObj


is cursor is out of sync with selected item row move to selected item

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -589,15 +589,23 @@
       if @isNormalItemRow(row) and @items[row].filePath isnt selectedItem.filePath
         return @items[row]

+  isCursorOutOfSyncWithSelectedItem: ->
+    @editor.getCursorBufferPosition().row isnt @getRowForSelectedItem()
+
   moveToNextFileItem: ->
+    @moveToDifferentFileItem('next')
+
+  moveToPreviousFileItem: ->
+    @moveToDifferentFileItem('previous')
+
+  moveToDifferentFileItem: (direction) ->
+    if @isCursorOutOfSyncWithSelectedItem()
+      @moveToSelectedItem(ignoreCursorMove: false)
+      return
+
     # Fallback to selected item incase there is only single filePath in all items
     # But whant to move to that item from query-prompt.
-    if item = @findDifferentFileItem('next') ? @getSelectedItem()
-      @selectItem(item)
-      @moveToSelectedItem(ignoreCursorMove: false)
-
-  moveToPreviousFileItem: ->
-    if item = @findDifferentFileItem('previous') ? @getSelectedItem()
+    if item = @findDifferentFileItem(direction) ? @getSelectedItem()
       @selectItem(item)
       @moveToSelectedItem(ignoreCursorMove: false)


syncToEditor take editor argument again for explicitness.

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -262,7 +262,7 @@

     @refresh().then =>
       if @provider.needAutoReveal()
-        if @syncToEditor()
+        if @syncToEditor(@provider.editor)
           @moveToBeginningOfSelectedItem()
           @preview()
           previewd = true
@@ -532,9 +532,8 @@
         @preview() if @autoPreview

   # Return success or fail
-  syncToEditor: ->
+  syncToEditor: (editor) ->
     return false if @preventSyncToEditor
-    editor = @provider.editor

     point = editor.getCursorBufferPosition()
     if @provider.boundToSingleFile
@@ -657,18 +656,17 @@
     newFilePath = editor.getPath()

     @provider.bindEditor(editor)
-    @syncToEditor()
+    @syncToEditor(editor)

     ignoreColumnChange = @provider.ignoreSideMovementOnSyncToEditor

     @syncSubcriptions.add editor.onDidChangeCursorPosition (event) =>
-      return unless @provider.isActive()
       return if event.textChanged
       return if ignoreColumnChange and (event.oldBufferPosition.row is event.newBufferPosition.row)
-      @syncToEditor()
+      @syncToEditor(editor) if isActiveEditor(editor)

     @syncSubcriptions.add @onDidRefresh =>
-      @syncToEditor()
+      @syncToEditor(editor) if isActiveEditor(editor)

     # Suppress refresh while ui is active.
     # Important to update-real-file don't cause auto-refresh.


Add spec for disabling spell check of a grammar

--- spec/spell-check-spec.coffee
+++ spec/spell-check-spec.coffee
@@ -45,3 +45,17 @@
       editor.insertText('a')
       advanceClock(editor.getBuffer().stoppedChangingDelay)
       expect(editor.find('.misspelling')).toBeHidden()
+
+  describe "when spell checking for a grammar is removed", ->
+    it "removes all current decorations", ->
+      editor.setText('notaword')
+      advanceClock(editor.getBuffer().stoppedChangingDelay)
+      config.set('spell-check.grammars', ['source.js'])
+
+      waitsFor ->
+        editor.find('.misspelling').length > 0
+
+      runs ->
+        expect(editor.find('.misspelling').length).toBe 1
+        config.set('spell-check.grammars', [])
+        expect(editor.find('.misspelling').length).toBe 0


Don't assign a screen extent to void tokens

This plays nicely with all the point translation math, and makes the following
specs pass with an arbitrary number of operations (e.g. fold, change, etc.) per
iteration:

1. Verify rightmost screen position
2. Verify position translation
3. Verify incrementally changed text

--- src/display-layer.coffee
+++ src/display-layer.coffee
@@ -310,7 +310,7 @@

       if eolInvisibleReplacement = @eolInvisibles[@buffer.lineEndingForRow(bufferRow)]
         tokens.push({
-          screenExtent: eolInvisibleReplacement.length,
+          screenExtent: 0,
           bufferExtent: Point(0, 0),
           metadata: {
             eol: eolInvisibleReplacement,
@@ -319,21 +319,17 @@
             void: true
           }
         })
-        screenColumn += eolInvisibleReplacement.length
-        tokensScreenExtent = screenColumn

       while @showIndentGuides and indentGuidesCount > 0 and not previousPositionWasFold
         distanceToNextTabStop = @tabLength - (tokensScreenExtent % @tabLength)
         tokens.push({
-          screenExtent: distanceToNextTabStop,
+          screenExtent: 0,
           bufferExtent: Point(0, 0),
           metadata: {
             showIndentGuide: (tokensScreenExtent % @tabLength is 0),
             void: true
           }
         })
-        screenColumn += distanceToNextTabStop
-        tokensScreenExtent = screenColumn
         indentGuidesCount--

       if tokens.length is 0
@@ -640,10 +636,7 @@

     metadata = @spatialTokenIterator.getMetadata()
     if metadata?.void
-      if options?.clipDirection is 'forward'
-        throw new Error('TODO: Not implemented. Support void tokens followed by valid screen positions, such as soft wrap indents.')
-      else
-        screenPosition = @spatialTokenIterator.getScreenStart()
+      throw new Error('TODO: Not implemented. Support void tokens followed by valid screen positions, such as soft wrap indents.')
     else if comparePoints(screenPosition, @spatialTokenIterator.getScreenEnd()) <= 0
       if (metadata?.atomic and
           comparePoints(screenPosition, @spatialTokenIterator.getScreenStart()) > 0 and
@@ -664,15 +657,7 @@
     @screenLineIndex.getScreenLineCount()

   getRightmostScreenPosition: ->
-    position = @screenLineIndex.getScreenPositionWithMaxLineLength()
-    if position?
-      @clipScreenPosition(position)
-    else
-      {row: 0, column: 0}
+    @screenLineIndex.getScreenPositionWithMaxLineLength() or {row: 0, column: 0}

   lineLengthForScreenRow: (screenRow) ->
-    length = @screenLineIndex.lineLengthForScreenRow(screenRow)
-    if length?
-      @clipScreenPosition({row: screenRow, column: length}).column
-    else
-      0
+    @screenLineIndex.lineLengthForScreenRow(screenRow) or 0


chore(request): Fix deprecated warning message

--- modules/request/main/index.coffee
+++ modules/request/main/index.coffee
@@ -1,4 +1,4 @@
-deprecatedWarning = (fn) => hx.deprecatedWarning(fn, 'is deprecated and will be removed in a later release')
+deprecatedWarning = (fn) => hx.deprecatedWarning(fn, 'fetch (native browser function) - https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API')

 respondToRequest = (request, url, data, callback, options, index) ->
   status = request.status


Remove alternate backup file before running spec if it exists

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -1759,13 +1759,16 @@

         describe "if a file already exists at the default backup path", ->
           it "chooses a different backup file name to avoid overwriting the existing file", ->
+            alternateBackupFilePath = backupFilePath + '~'
+            fs.removeSync(alternateBackupFilePath) if fs.existsSync(alternateBackupFilePath)
+
             fs.writeFileSync(backupFilePath, 'Contents of file at default backup path')

             saveAsBuffer.saveAs(filePath, backup: true)
             expect(fs.readFileSync(filePath, 'utf8')).toBe 'Buffer contents'
             expect(fs.readFileSync(backupFilePath, 'utf8')).toBe 'Contents of file at default backup path'
             expect(fs.existsSync(backupFilePath)).toBe true
-            expect(fs.existsSync(backupFilePath + '~')).toBe false
+            expect(fs.existsSync(alternateBackupFilePath)).toBe false

       describe "if no file exists at the path", ->


Fix adding of new list values

Fix adding of new list values

--- app/assets/javascripts/admin/views/home/add_integration.js.coffee
+++ app/assets/javascripts/admin/views/home/add_integration.js.coffee
@@ -58,7 +58,7 @@
       false

     @$el.on 'click', '.add-new-value', (e) =>
-      @$el.find('.list-item:last').after(@listTemplate())
+      $(e.currentTarget).parent().after(@listTemplate())
       false

   parametersByConsumer: ->


Fix Author View hover class toggle

Fix Author View hover class toggle

--- app/assets/javascripts/components/author_view_component.js.coffee
+++ app/assets/javascripts/components/author_view_component.js.coffee
@@ -7,11 +7,10 @@
   deleteState: false

   attachHoverEvent: (->
-    self = this
-    toggleHoverClass = (e) ->
-      self.toggleProperty 'hoverState'
+    toggleHoverClass = (e) =>
+      @toggleProperty 'hoverState'

-    @$().hover(toggleHoverClass, toggleHoverClass)
+    @$().hover toggleHoverClass, toggleHoverClass
   ).on('didInsertElement')

   teardownHoverEvent: (->


add preload to questions.js.coffee

add preload to questions.js.coffee

--- app/assets/javascripts/simon_asks/application/questions.js.coffee
+++ app/assets/javascripts/simon_asks/application/questions.js.coffee
@@ -1,4 +1,8 @@
 window.SimonAsks ||= {}
+
+SimonAsks.preload = (arrayOfImages) ->
+  $(arrayOfImages).each ->
+    (new Image()).src = this

 SimonAsks.prepareCommentsLinks = (context) ->
   context = ((if context is `undefined` then '' else context+' '))


slow down the polling, for now

slow down the polling, for now

--- frontend/app/js/player-ctrl.coffee
+++ frontend/app/js/player-ctrl.coffee
@@ -27,7 +27,7 @@
     [chan, rate, freq, bitr].join(', ')

   startMonitoring = ->
-    poller = $interval(checkPlayerStatus, 1000)
+    poller = $interval(ctrl.checkPlayerStatus, 5000)

   stopMonitoring = ->
     if angular.isDefined(poller)


Clean up coffeescript of lineman configuration

--- frontend/config/application.coffee
+++ frontend/config/application.coffee
@@ -32,21 +32,28 @@

   watch:
     scripts:
-      files: ['generated/**'],
+      files: ['generated/**']
       tasks: ['copy:dev']

   copy:
     dev:
-      files: [expand: true, cwd: 'generated', src: ['js/**', '!**/spec.js',
-              '!**/*.coffee*', '!**/spec.js.map'], dest: '../public' ]
+      files: [
+        expand: true
+        cwd: 'generated'
+        src: ['js/**', '!**/spec.js', '!**/*.coffee*', '!**/spec.js.map']
+        dest: '../public'
+      ]
     dist:
-      files: [expand: true, cwd: 'dist', src: ['js/**'], dest: '../public']
+      files: [
+        expand: true
+        cwd: 'dist'
+        src: ['js/**']
+        dest: '../public'
+      ]

   # configuration for grunt-ngmin, this happens _after_ concat once, which is the ngmin ideal :)
-  ngmin: {
-    js: {
-      src: '<%= files.js.concatenated %>',
+  ngmin:
+    js:
+      src: '<%= files.js.concatenated %>'
       dest: '<%= files.js.concatenated %>'
-    }
-  },
 })


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_levels.coffee
+++ src/engine_levels.coffee
@@ -51,7 +51,7 @@
   engine.goToLevel 'bossFight'
   </pre></code>

-  @name nextLevel
+  @name goToLevel
   @methodOf Engine#
   ###
   goToLevel: (level) ->


fix ajax nav menu

fix ajax nav menu

--- BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
+++ BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
@@ -198,6 +198,6 @@
   smartConfirm('fa-refresh', $(e.currentTarget).data('message-title'), $(e.currentTarget).data('message-text'), callbacks)

 #SMARTADMIN:JARVISMENU PREVENT LOAD PAGE ON EXTENDS ACCORDION MENU CALLS
-$("#left-panel nav").on("click",".collapse-sign em[class*='fa-'][class*='-square-o']", (event) ->
+$("#left-panel").on("click","nav .collapse-sign em[class*='fa-'][class*='-square-o']", (event) ->
    event.preventDefault()
 )


Use unverified_positions instead of current_line

--- player.coffee
+++ player.coffee
@@ -22,6 +22,7 @@
     constructor: (@name, pos, @canvas, @game) ->
       @positions = []
       @positions.push(pos)
+      @unverified_positions = []
       @current_line = new fabric.Polyline([], {
         stroke: 'blue',
         strokeWidth: 5,


Fix typo in versionCheck options

Fix typo in versionCheck options

--- src/leap.rigged-hand.coffee
+++ src/leap.rigged-hand.coffee
@@ -282,7 +282,7 @@
 Leap.plugin 'riggedHand', (scope = {})->
   @use('handHold')
   @use('handEntry')
-  @use('versionCheck', {requiredProtocolVerion: 6})
+  @use('versionCheck', {requiredProtocolVersion: 6})

   scope.offset ||= new THREE.Vector3(0,-10,0)
   scope.scale ||= 1


Functionalise

Functionalise

--- app/assets/javascripts/admin/order_cycle.js.erb.coffee
+++ app/assets/javascripts/admin/order_cycle.js.erb.coffee
@@ -96,14 +96,12 @@
       variant_ids = [product.master_id].concat(product_variant_ids)
       incomingExchangesVariants = this.incomingExchangesVariants()

-      # TODO: This would be much nicer functional (set intersection)
-      found = false
-      for variant_id in variant_ids
-        if incomingExchangesVariants.indexOf(variant_id) != -1
-          found = true
-          break
-
-      found
+      # TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
+      # Use a better algorithm if needed.
+      # Also, incomingExchangesVariants is called every time, when it only needs to be
+      # called once per change to incoming variants. Some sort of caching?
+      ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
+      ids.length > 0

     incomingExchangesVariants: ->
       variant_ids = []


resurrection.account: fix errors in email notification settings

--- client/Account/views/emailnotifications.coffee
+++ client/Account/views/emailnotifications.coffee
@@ -28,11 +28,11 @@
       groupLeft        :
         title          : 'When someone leaves your group'

-    globalValue = if user.getAt("emailFrequency.global") is on then 'ON' else 'OFF'
+    globalValue = user.getAt "emailFrequency.global"

     turnedOffHint = new KDCustomHTMLView
       partial : "Email notifications are turned off. You won't receive any emails about anything."
-      cssClass: "no-item-found #{if globalValue is 'ON' then 'hidden'}"
+      cssClass: "no-item-found #{if globalValue then 'hidden'}"

     @addSubView turnedOffHint

@@ -40,9 +40,7 @@
       cssClass      : "dark in-account-header"
       defaultValue  : globalValue
       callback      : (state)=>
-        stateValue = if state is 'ON' then on else off
-
-        account.setEmailPreferences global: stateValue, (err)=>
+        account.setEmailPreferences global: state, (err)=>
           if err
             global.oldValue = globalValue
             global.fallBackToOldState()
@@ -62,9 +60,8 @@

       field.formView.addSubView field.switch = new KodingSwitch
         cssClass      : 'dark'
-        defaultValue  : if field.current is on then 'ON' else 'OFF'
+        defaultValue  : field.current
         callback      : (state)->
-          state = if state is 'ON' then on else off
           prefs = {}

           prefs[@getData()] = state
@@ -87,7 +84,7 @@

     toggleFieldStates = (state)->
       for own flag, field of fields
-        if state is 'OFF'
+        if state is off
           field.formView.hide()
         else
           field.formView.show()
@@ -96,6 +93,6 @@

     @on 'GlobalStateChanged', (state)=>
       toggleFieldStates state
-      if state is 'OFF'
+      if state is off
       then turnedOffHint.show()
       else turnedOffHint.hide()


style changes

style changes

--- client/Dashboard/views/badge/badgerules.coffee
+++ client/Dashboard/views/badge/badgerules.coffee
@@ -11,7 +11,7 @@

     @badgeListView = @badgeRulesListController.getListView()

-    @addRule          = new KDButtonView
+    @addRuleButton    = new KDButtonView
       name            : 'addrule'
       style           : 'add-new-rule solid green'
       title           : 'Add new rule'
@@ -60,6 +60,8 @@
     @once "BadgeCreated" , =>
       @giveBadgeButton.loader.hide()
       @giveBadgeButton.hide()
+      @addRuleButton.hide()
+      @doneButton.hide()
       new KDNotificationView
         title      : "Badge created"
         duration   : "2000"
@@ -112,7 +114,7 @@

   pistachio:->
     """
-    {{> @addRule}}
+    {{> @addRuleButton}}
     {{> @badgeListView}}
     {{> @doneButton}}
     {{> @userList}}


ComputeStorage: get/query: faster and simpler getters

--- client/app/lib/providers/computestorage.coffee
+++ client/app/lib/providers/computestorage.coffee
@@ -108,17 +108,14 @@
     debug 'query requested', type, key, value

     if items = @storage[type]
-      if typeof key is 'string'
-        res = items
-          .map (item) ->
-            return item  if item and (item.getAt?(key) ? item[key]) is value
-          .filter Boolean
-
-    res ?= []
+      res = items
+        .map (item) ->
+          return item  if item and (item.getAt?(key) ? item[key]) is value
+        .filter Boolean

     debug 'query result', res

-    return res
+    return res ? []

   get: (type, key, value) ->
@@ -126,8 +123,11 @@
     if not key and not value
       return @storage[type] ? []

-    [ item ] = @query type, key, value
-    return item
+    if items = @storage[type]
+      for item in items when (item and (item.getAt?(key) ? item[key]) is value)
+        return item
+
+    return null

   push: (type, value) ->


Return object as response to projectionRebuild.rebuild

--- source/server/infrastructure/projection-rebuilder.coffee
+++ source/server/infrastructure/projection-rebuilder.coffee
@@ -63,9 +63,9 @@
       for projection in queue
         projection.exitRebuildMode()
     duration = Math.round(process.hrtime(startHrTime)[1]/1000000)
-    responseMessage = "Finished rebuilding #{projections} in #{duration}ms"
-    @log.info(@_logMsg(responseMessage))
-    return responseMessage
+    response = { message: "Finished rebuilding #{projections} in #{duration}ms", duration: duration }
+    @log.info(@_logMsg(response.message))
+    return response

   _getCollectionIdsOfProjection: (projection) ->
     collectionIds = []


model bypass test

model bypass test

--- coffee/test/model.spec.coffee
+++ coffee/test/model.spec.coffee
@@ -136,7 +136,7 @@
       expect(model.shellData()).toEqual {shellid:'acorn.EmptyShell'}

-  describe 'acorn.Model.sync', ->
+  xdescribe 'acorn.Model.sync', ->

     #TODO expand this into comprehensive tests.
     #TODO find a way to mock the server in the future.


Added more docs to the detectEncoding config option

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -780,6 +780,8 @@

 		# Detect Encoding
 		# Should we attempt to auto detect the encoding of our files?
+		# Useful when you are using foreign encoding (e.g. GBK) for your files
+		# Only works on unix systems currently (limit of iconv module)
 		detectEncoding: false

 		# Render Single Extensions


the rectangle can now be formed by pulling in any direction away from the initial click

--- app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
@@ -34,7 +34,7 @@
     initMove: (cursor, mark) ->
       if cursor.x > @initCoords.x
         width = cursor.x - mark.x
-        x = marks.x
+        x = mark.x
       else
         width = @initCoords.x - cursor.x
         x = cursor.x


Relative path conflict detection now supports virtual documents

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -1121,8 +1121,8 @@
 					existingModel = @database.get(existingModelId)
 					if existingModel
 						# We have a conflict, let the user know
-						modelPath = model.get('fullPath')
-						existingModelPath = existingModel.get('fullPath')
+						modelPath = model.get('fullPath') or (model.get('relativePath')+':'+model.id)
+						existingModelPath = existingModel.get('fullPath') or (existingModel.get('relativePath')+':'+existingModel.id)
 						message =  util.format(docpad.getLocale().outPathConflict, outPath, modelPath, existingModelPath)
 						docpad.warn(message)
 					else


Fix DOM context reference bugs with token field refactor

--- app/assets/javascripts/exo/views/token_field.js.coffee
+++ app/assets/javascripts/exo/views/token_field.js.coffee
@@ -45,15 +45,12 @@

       @disabled ||= false

-      # Geh....
       if @originalInput = options.input
         @$originalInput = $(@originalInput)
         @$originalInput.wrap(@el)
         @$originalInput.attr("type", "hidden")
-        @placeholderText = @$originalInput.attr("placeholder")
-        @el = @$originalInput.parents()[0]
-        @$el = $(@el)
-        @delegateEvents()
+        @placeholderText ||= @$originalInput.attr("placeholder")
+        @setElement @$originalInput.parents()[0]

       @placeholderText ||= ""

@@ -70,7 +67,11 @@

       @keyboardManager ||= new Exo.KeyboardManager(@el)

-      # Use templates instead?
+      @render()
+
+    render: ->
+      return this if @_renderCount
+
       @input = document.createElement("input")
       @input.setAttribute("autocorrect", "off")
       @input.setAttribute("autocapitalize", "off")
@@ -96,10 +97,6 @@
       @tokenContainer = document.createElement("ul")
       @tokenContainer.className = @tokenContainerClassName

-    render: ->
-      # In case this is a re-render, remove the token-input
-      $(@tokenInput).remove() if @tokenInput
-
       @_updatePlaceholder()

       @_toggleInputVisibility()
@@ -112,6 +109,7 @@

       @el.appendChild(@tokenContainer)

+      @_renderCount = 1
       this

     insertToken: (tokens, options = {}) ->
@@ -400,7 +398,7 @@

     _updatePlaceholder: ->
       if @inputPlaceholder
-        @inputPlaceholder?.innerHTML = @placeholderText
+        @inputPlaceholder.innerHTML = @placeholderText

     _toggleInputVisibility: ->
       exceeded = @_maxTokensExceeded()


added config.ignorePatterns option

added config.ignorePatterns option

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -526,6 +526,10 @@
 			'node_modules',
 			'plugins'
 		]
+
+		# Ignored files patterns during directory parsing
+		# Default to balUtil.commonIgnorePatterns
+		ignorePatterns: true

 		# -----------------------------
@@ -1531,7 +1535,7 @@
 	# next(err)
 	parseDirectory: (opts={},next) ->
 		# Prepare
-		me = @
+		docpad = @
 		locale = @getLocale()

 		# Extract
@@ -1541,21 +1545,21 @@
 		# Check if the directory exists
 		unless balUtil.existsSync(path)
 			# Log
-			me.log 'debug', util.format(locale.renderDirectoryNonexistant, path)
+			docpad.log 'debug', util.format(locale.renderDirectoryNonexistant, path)

 			# Forward
 			return next()

 		# Log
-		me.log 'debug', util.format(locale.renderDirectoryParsing, path)
+		docpad.log 'debug', util.format(locale.renderDirectoryParsing, path)

 		# Files
 		balUtil.scandir(
 			# Path
 			path: path

-			# Ignore common patterns
-			ignorePatterns: true
+			# Ignore patterns
+			ignorePatterns: docpad.config.ignorePatterns

 			# File Action
 			fileAction: (fileFullPath,fileRelativePath,nextFile,fileStat) ->
@@ -1579,10 +1583,10 @@
 				return next(err)  if err

 				# Log
-				me.log 'debug', util.format(locale.renderDirectoryParsed, path)
+				docpad.log 'debug', util.format(locale.renderDirectoryParsed, path)

 				# Load the files
-				me.loadFiles {collection:filesToLoad}, (err) ->
+				docpad.loadFiles {collection:filesToLoad}, (err) ->
 					# Forward
 					return next(err)
 		)
@@ -1751,8 +1755,8 @@
 			# Path
 			path: pluginsPath

-			# Ignore common patterns
-			ignorePatterns: true
+			# Ignore patterns
+			ignorePatterns: docpad.config.ignorePatterns

 			# Skip files
 			fileAction: false
@@ -2508,6 +2512,9 @@
 		locale = @getLocale()
 		database = @getDatabase()
 		watchrs = []
+		ignorePatterns = @config.ignorePatterns
+		if @config.ignorePatterns == true
+			ignorePatterns = balUtil.commonIgnorePatterns

 		# Close our watchers
 		closeWatchers = ->
@@ -2541,7 +2548,7 @@
 				path: docpad.config.srcPath
 				listener: changeHandler
 				next: tasks.completer()
-				ignorePatterns: true
+				ignorePatterns: docpad.config.ignorePatterns
 			)

 		# Timer
@@ -2572,7 +2579,7 @@
 			docpad.log 'debug', util.format(locale.watchChange, new Date().toLocaleTimeString()), eventName, filePath

 			# Check if we are a file we don't care about
-			if ( balUtil.commonIgnorePatterns.test(pathUtil.basename(filePath)) )
+			if ( ignorePatterns and ignorePatterns.test(pathUtil.basename(filePath)) )
 				docpad.log 'debug', util.format(locale.watchIgnoredChange, new Date().toLocaleTimeString()), filePath
 				return


Reindent operators

Reindent operators

--- src/liquid/condition.coffee
+++ src/liquid/condition.coffee
@@ -11,15 +11,15 @@
 #
 module.exports = class Condition
   @operators =
-    '==':   (cond, left, right) ->  cond.equalVariables(left, right)
-    'is':   (cond, left, right) ->  cond.equalVariables(left, right)
-    '!=':   (cond, left, right) -> !cond.equalVariables(left, right)
-    '<>':   (cond, left, right) -> !cond.equalVariables(left, right)
-    'isnt': (cond, left, right) ->  cond.equalVariables(left, right)
-    '<':    (cond, left, right) -> left < right
-    '>':    (cond, left, right) -> left > right
-    '<=':   (cond, left, right) -> left <= right
-    '>=':   (cond, left, right) -> left >= right
+    '==':       (cond, left, right) ->  cond.equalVariables(left, right)
+    'is':       (cond, left, right) ->  cond.equalVariables(left, right)
+    '!=':       (cond, left, right) -> !cond.equalVariables(left, right)
+    '<>':       (cond, left, right) -> !cond.equalVariables(left, right)
+    'isnt':     (cond, left, right) ->  cond.equalVariables(left, right)
+    '<':        (cond, left, right) -> left < right
+    '>':        (cond, left, right) -> left > right
+    '<=':       (cond, left, right) -> left <= right
+    '>=':       (cond, left, right) -> left >= right
     'contains': (cond, left, right) -> left?.indexOf?(right) >= 0

   operators: ->


#235 removed unneeded dependancy

#235 removed unneeded dependancy

--- app/assets/javascripts/main/non_backbone_router.coffee
+++ app/assets/javascripts/main/non_backbone_router.coffee
@@ -3,7 +3,6 @@
 underscore : _
 backbone : Backbone
 oxalis/constants : constants
-./paginator : Paginator
 ###

 class Router extends Backbone.Router


Update cards-es.coffee

Update cards-es.coffee

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -907,7 +907,6 @@
         "Ion Cannon Battery":
             text: """Attack (energy): Spend 2 energy from this card to perform this attack.  If this attack hits, the defender suffers 1 critical damage and receives 1 ion token.  Then cancel all dice results."""

-
     modification_translations =
         "Stealth Device":
             name: "Dispositivo de Sigilo"


Lets add some logging

Lets add some logging

--- src/adapters/campfire.coffee
+++ src/adapters/campfire.coffee
@@ -30,6 +30,9 @@
     bot = new CampfireStreaming(options, @robot)

     withAuthor = (callback) -> (id, created, room, user, body) ->
+      if user is null
+        console.log "Null user!"
+
       bot.User user, (err, userData) ->
         if userData.user
           author = self.userForId(userData.user.id, userData.user)
@@ -49,6 +52,10 @@
     bot.on "LeaveMessage", withAuthor (id, created, room, user, body, author) ->
       unless bot.info.id == author.id
         self.receive new Robot.LeaveMessage(author)
+
+    bot.on "TimestampMessage", withAuthor (id, created, room, user , author) ->
+      console.log "Timestamp"
+      console.log author

     bot.Me (err, data) ->
       bot.info = data.user


bugfix — don't clear the group name from the route

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -123,6 +123,9 @@
       else
         @loadContent name, section, slug, route, query

+  clear:(route="/#{KD.config.groupEntryPoint}", replaceState=yes)->
+    super route, replaceState
+
   createLinks =(names, fn)->
     names = names.split ' '  if names.split?
     names
@@ -286,7 +289,7 @@

         nameHandler =(routeInfo, state, route)->
           return  if @landingPageLoading
-
+
           {params} = routeInfo
           status_404 = @handleNotFound.bind this, params.name


#1394 Add Buckets to garbage collection only when thez are pulled

--- app/assets/javascripts/oxalis/model/binary/cube.coffee
+++ app/assets/javascripts/oxalis/model/binary/cube.coffee
@@ -177,8 +177,9 @@

     bucket = new Bucket(@BIT_DEPTH, address, @temporalBucketManager)
     bucket.on
-      bucketLoaded : => @trigger("bucketLoaded", address)
-    @addBucketToGarbageCollection(bucket)
+      bucketLoaded : =>
+        @trigger("bucketLoaded", address)
+        @addBucketToGarbageCollection(bucket)
     return bucket


Reset graphs collection before re-rerendering graphs

--- app/assets/javascripts/pgdatagraph/datagraph.js.coffee
+++ app/assets/javascripts/pgdatagraph/datagraph.js.coffee
@@ -234,8 +234,9 @@
       success: (data, status, xhr) =>
         @element.removeClass "#{@options.className}_loading"
         series = @getSeries(data)
+        @graphs = []
         @renderDetailGraph(series)
         @renderOverviewGraph(series)
+        @updateSeries()
         @updateLegend() if @options.legend
-        @updateSeries()
-
+


added feed routes

added feed routes

--- src/main/coffee/main.coffee
+++ src/main/coffee/main.coffee
@@ -1,6 +1,38 @@
 class NRApplication extends SimpleMVC.Controller
+    _processFeedPosts: (data) ->
+        for i in data
+            post = new NR.Models.NewsFeedArticleInfo
+            for k,v of i
+                post[k] = v
+            this.articleList.add post
+
+    _apiError: (type, desc) ->
+        # TODO
+        alert "Error: " + type + " " + desc
+
+    @route "/news/:uid/feeds/:fid", (uid, fid) ->
+        # Specific feed listing.
+        this.newsArticleView.show()
+        this.welcomeView.hide()
+
+        # Get posts from server
+        NR.API.GetPostsForFeed fid, 0, "", true, this._processFeedPosts, this._apiError
+
+    @route "/news/:uid/feeds", (uid) ->
+        # "All Feeds" listing.
+        this.newsArticleView.show()
+        this.welcomeView.hide()
+
+        # Get posts from server
+        NR.API.GetAllPosts 0, "", true, this._processFeedPosts, this._apiError
+
     @route "/news/:uid", (uid) ->
-        # empty for now
+        this._uid = uid
+
+        # Hide articles.
+        this.newsArticleView.hide()
+        this.articleList.reset()
+        this.welcomeView.show()

     constructor: (bootstrappedFeeds) ->
         super()


Prevent weird wrapping issues with the message

--- app/assets/javascripts/views/paper_edit_view.js.coffee
+++ app/assets/javascripts/views/paper_edit_view.js.coffee
@@ -45,6 +45,7 @@
     $('.edit-paper').scrollToFixed
       marginTop: marginTop + 5
       zIndex: 1010
+      dontSetWidth: true
       preFixed: ->
         $(this).css('marginTop', '5')


[client][mainApp][avatararea] added split view to add "Back to Koding" link next to "See All Groups"

--- client/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
+++ client/app/MainApp/avatararea/avatarareagroupswitcherpopup.coffee
@@ -47,13 +47,30 @@

     @avatarPopupContent.addSubView @listController.getView()

-    @avatarPopupContent.addSubView new KDView
-      height   : "auto"
-      cssClass : "sublink"
+
+    seeAllView = new KDView
+      height   : "auto"
+      cssClass : "split sublink"
       partial  : "See all groups..."
       click    : =>
         KD.getSingleton("appManager").open "Groups"
         @hide()
+
+    backToKodingView = new KDView
+      height   : "auto"
+      cssClass : "split sublink right"
+      partial  : "Back to Koding"
+      click    : =>
+        KD.getSingleton('router').handleRoute '/Activity'
+        @hide()
+
+    split = new SplitView
+      domId     : "avatararea-bottom-split-view"
+      height    : "37px"
+      sizes     : [130,null]
+      views     : [seeAllView,backToKodingView]
+      resizable : no
+    @avatarPopupContent.addSubView split

   accountChanged:->
     @listController.removeAllItems()


In LanguageMode, unsubscribe from old grammar when switching grammars

--- src/app/language-mode.coffee
+++ src/app/language-mode.coffee
@@ -32,6 +32,7 @@

   setGrammar: (grammar, score) ->
     return if grammar is @grammar
+    @unsubscribe(@grammar) if @grammar
     @grammar = grammar
     @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText())
     @subscribe @grammar, 'grammar-updated', => @trigger 'grammar-updated'


fix to prevent coffeescript from misinterpreting

--- app/assets/javascripts/active_admin/lib/checkbox-toggler.js.coffee
+++ app/assets/javascripts/active_admin/lib/checkbox-toggler.js.coffee
@@ -1,7 +1,7 @@
 class ActiveAdmin.CheckboxToggler
   constructor: (@options, @container)->
     defaults = {}
-    @options = $.extend defaults, options
+    @options = $.extend defaults, @options
     @_init()
     @_bind()


correct indentation, remove redundant comments

--- app/assets/javascripts/comparative_genome.js.coffee
+++ app/assets/javascripts/comparative_genome.js.coffee
@@ -3,7 +3,8 @@
 ###global d3, CodeMirror, d3sparql ###
 ###jshint multistr: true ###

-ENDPOINT = 'http://dev.togogenome.org/sparql-test'
+ENDPOINT = 'http://dev.togogenome.org/sparql-test' # TODO use URL from endpoint.yml
+
 DEFAULT =
   aspect: 'pathway'
   species: [
@@ -11,19 +12,96 @@
     10090
     10116
   ]
+
 DURATION =
   scroll: 250
   css: 200
+
 TREE =
   svgHeight: 320
   lineHeight: 32
   width: 200
   nodeSize: 8
   nodeStrokeWidth: 2
+
 SPARQLS =
-  step1: 'DEFINE sql:select-option "order"\n\nPREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\nPREFIX up: <http://purl.uniprot.org/core/>\nPREFIX tax: <http://purl.uniprot.org/taxonomy/>\nPREFIX db: <http://purl.uniprot.org/database/>\n\nSELECT ?orgs (COUNT(?orgs) AS ?count)\nWHERE {\nSELECT ?label (GROUP_CONCAT(REPLACE(STR(?tax), tax:, "") ;separator=", ") AS ?orgs)\nWHERE {\nSELECT DISTINCT ?label ?tax\nFROM <http://togogenome.org/graph/uniprot>\nWHERE {\nVALUES ?tax { @@taxvalues@@ }\n?up up:organism ?tax .\n@@aspect@@\n}\nORDER BY ?tax\n}\n}\nORDER BY DESC(?count)'
-  step2: 'DEFINE sql:select-option "order"\n\nPREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\nPREFIX up: <http://purl.uniprot.org/core/>\nPREFIX tax: <http://purl.uniprot.org/taxonomy/>\nPREFIX db: <http://purl.uniprot.org/database/>\n\nSELECT DISTINCT ?label ?sum ?orgs\nWHERE {\n{\nSELECT ?label (SUM(?count) AS ?sum) (GROUP_CONCAT(REPLACE(STR(?tax), tax:, "") ;separator=", ") AS ?orgs)\nWHERE {\nSELECT ?label ?tax (COUNT(?up) AS ?count)\nWHERE {\nSELECT DISTINCT ?label ?tax ?up\nFROM <http://togogenome.org/graph/uniprot>\nWHERE {\nVALUES ?tax { @@taxvalues@@ }\n?up up:organism ?tax .\n@@aspect@@\n}\n}\nORDER BY ?tax\n}\n}\nFILTER (?orgs = "@@taxfilter@@")\n}\nORDER BY DESC(?sum)'
-  step3: 'DEFINE sql:select-option "order"\n\nPREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\nPREFIX up: <http://purl.uniprot.org/core/>\nPREFIX tax: <http://purl.uniprot.org/taxonomy/>\nPREFIX db: <http://purl.uniprot.org/database/>\n\n#SELECT DISTINCT ?tg_id ?up_id ?tax_id ?up ?tax\nSELECT DISTINCT ?tg_id ?up_id ?tax_id\nWHERE {\nGRAPH <http://togogenome.org/graph/uniprot> {\nVALUES ?label { @@category@@ }\nVALUES ?tax { @@taxvalues@@ }\n?up up:organism ?tax .\n@@aspect@@\n}\nGRAPH <http://togogenome.org/graph/tgup> {\n?up_id rdfs:seeAlso ?up .\n?tg_id rdfs:seeAlso ?up_id .\n?tg_id rdfs:seeAlso ?tax_id .\n?tax_id a <http://identifiers.org/taxonomy> .\n}\n}\nORDER BY (?tax_id)'
+  step1: '''
+    DEFINE sql:select-option "order"
+    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+    PREFIX up: <http://purl.uniprot.org/core/>
+    PREFIX tax: <http://purl.uniprot.org/taxonomy/>
+    PREFIX db: <http://purl.uniprot.org/database/>
+    SELECT ?orgs (COUNT(?orgs) AS ?count)
+    WHERE {
+      SELECT ?label (GROUP_CONCAT(REPLACE(STR(?tax), tax:, "") ;separator=", ") AS ?orgs)
+      WHERE {
+        SELECT DISTINCT ?label ?tax
+        FROM <http://togogenome.org/graph/uniprot>
+        WHERE {
+          VALUES ?tax { @@taxvalues@@ }
+          ?up up:organism ?tax .
+          @@aspect@@
+        }
+        ORDER BY ?tax
+      }
+    }
+    ORDER BY DESC(?count)
+  '''
+  step2: '''
+    DEFINE sql:select-option "order"
+    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+    PREFIX up: <http://purl.uniprot.org/core/>
+    PREFIX tax: <http://purl.uniprot.org/taxonomy/>
+    PREFIX db: <http://purl.uniprot.org/database/>
+    SELECT DISTINCT ?label ?sum ?orgs
+    WHERE {
+      {
+        SELECT ?label (SUM(?count) AS ?sum) (GROUP_CONCAT(REPLACE(STR(?tax), tax:, "") ;separator=", ") AS ?orgs)
+        WHERE {
+          SELECT ?label ?tax (COUNT(?up) AS ?count)
+          WHERE {
+            SELECT DISTINCT ?label ?tax ?up
+            FROM <http://togogenome.org/graph/uniprot>
+            WHERE {
+              VALUES ?tax { @@taxvalues@@ }
+              ?up up:organism ?tax .
+              @@aspect@@
+            }
+          }
+          ORDER BY ?tax
+        }
+      }
+      FILTER (?orgs = "@@taxfilter@@")
+    }
+    ORDER BY DESC(?sum)
+  '''
+  step3: '''
+    DEFINE sql:select-option "order"
+    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+    PREFIX up: <http://purl.uniprot.org/core/>
+    PREFIX tax: <http://purl.uniprot.org/taxonomy/>
+    PREFIX db: <http://purl.uniprot.org/database/>
+    SELECT DISTINCT ?tg_id ?up_id ?tax_id
+    WHERE {
+      GRAPH <http://togogenome.org/graph/uniprot> {
+        VALUES ?label { @@category@@ }
+        VALUES ?tax { @@taxvalues@@ }
+        ?up up:organism ?tax .
+        @@aspect@@
+      }
+      GRAPH <http://togogenome.org/graph/tgup> {
+        ?up_id rdfs:seeAlso ?up .
+        ?tg_id rdfs:seeAlso ?up_id .
+        ?tg_id rdfs:seeAlso ?tax_id .
+        ?tax_id a <http://identifiers.org/taxonomy> .
+      }
+    }
+    ORDER BY (?tax_id)
+  '''
+
 LINKS_TO_HEADER_LABEL_MAPPING =
   tg_id: 'TogoGenome ID'
   up_id: 'UniProt ID'
@@ -96,7 +174,6 @@
 ###

 Tree = (data) ->
-#window.console.log(data);
   deepest = 0
   species = undefined
   clades = undefined
@@ -205,7 +282,17 @@
   # htmlの生成
   if !@$container
     @$container = $('#section-species').children('.species-container')
-  html = '
\u0009\u0009\u0009

\u0009\u0009\u0009
@@common_name@@
\u0009\u0009\u0009
@@scientific_name@@
\u0009\u0009\u0009

\u0009\u0009
'.replace(/@@index@@/, @index + '').replace(/@@common_name@@/, data.common_name + '').replace(/@@scientific_name@@/, data.scientific_name)
+  html = '''
+

+

+
@@common_name@@

+
@@scientific_name@@

+

+

+  '''.
+    replace(/@@index@@/, @index + '').
+    replace(/@@common_name@@/, data.common_name + '').
+    replace(/@@scientific_name@@/, data.scientific_name)
   @$container.append html
   @$ = @$container.children('.species').last()
   @$.find('.close-button').on 'click', @clickCloseButton.bind(this)
@@ -308,26 +395,26 @@
     ).on('click', (d) ->
       if d.isLeaf
         if d.isSelected
-# 選択を解除
+          # 選択を解除
           d.isSelected = false
           d3.select(this).classed selected: false
           application.deleteSpecies d, this
         else
-# 種を選択
+          # 種を選択
           if application.setSpecies(d, this)
-# 選択に成功したら、選択状態に
+            # 選択に成功したら、選択状態に
             d.isSelected = true
             d3.select(this).classed selected: true
       else
         if d._children
-# 閉じてる
+          # 閉じてる
           d3.select(this).classed
             collapsed: false
             expanded: true
           d.children = d._children
           d._children = null
         else
-# 開いてる
+          # 開いてる
           d3.select(this).classed
             collapsed: true
             expanded: false
@@ -377,7 +464,6 @@
     # link
     # Update the links…
     link = @treeGroup.selectAll('path.link').data(links, (d) ->
-#window.console.log(d.target.id);
       d.target.id
     )
     # Enter any new links at the parent's previous position.
@@ -411,6 +497,7 @@
   resize: ->
     @$speciesSelector.width 'calc(100% - 24px)'
     return
+
 Species.prototype =
   $container: undefined
   clickCloseButton: ->
@@ -419,13 +506,13 @@
     return
   updateByDeleteTaxId: (taxId, index) ->
     if taxId == @data.taxid
-      window.console.log '消す'
       @$.remove()
       true
     else
       @index = index
       @$.attr 'data-index', index
       false
+
 VennDiagram.prototype =
   MARGIN_TOP: 80
   MARGIN_BOTTOM: 80
@@ -440,6 +527,7 @@
         position: ''
         top: ''
     return
+
 HeadingNavigation.prototype =
   scroll: ->
     scrollTop = @$window.scrollTop()
@@ -476,13 +564,13 @@
   scrollTo: (index) ->
     @$scroll.animate { scrollTop: @sections[index].$section.offset().top - (@sections[index].top) + 2 }, duration: DURATION.scroll
     return
+
 Application.prototype =
   MAX_OF_TAXON: 5
+
   initialize: (data) ->
-#window.console.log(data);
     self = this
     treeLeafNode = undefined
-    #this.species = []; // TODO: デフォルトを定義
     # reference
     @$aspectH2 = $('#section-aspect h2 strong')
     @$speciesH2 = $('#section-species h2 strong')
@@ -530,6 +618,7 @@
       $g.get(0).dispatchEvent event
       return
     return
+
   getValueWithKeyValue: (key1, key2, value) ->
     value = value + ''
     i = 0
@@ -538,11 +627,13 @@
         return @data[i][key1]
       i++
     return
+
   setAspect: (label, value) ->
     @$aspectH2.text label
     @cache.aspect = value
     @step1()
     return
+
   setSpecies: (data, targetNode) ->
     species = undefined
     if @cache.species.length >= @MAX_OF_TAXON
@@ -553,6 +644,7 @@
       @cache.species.push species
       @updateSpecies()
       true
+
   deleteSpecies: (data, targetNode) ->
     newSpecies = []
     isLDead = undefined
@@ -564,6 +656,7 @@
     @cache.species = newSpecies
     @updateSpecies()
     return
+
   updateSpecies: ->
     html = if @cache.species.length == 0 then '--' else ''
     # data
@@ -571,11 +664,14 @@
     @$sectionCombination.attr 'data-number-of-species', @cache.species.length
     # heading
     @cache.species.forEach (species, index, array) ->
-      html += '@@name@@'.replace(/@@index@@/, species.index).replace(/@@name@@/, species.data.common_name + (if index < array.length - 1 then ',' else ''))
+      html += '@@name@@'.
+        replace(/@@index@@/, species.index).
+        replace(/@@name@@/, species.data.common_name + (if index < array.length - 1 then ',' else ''))
       return
     @$speciesH2.html html
     @step1()
     return
+
   aspectSparql: (aspect) ->
     sparql = ''
     mapping =
@@ -602,20 +698,44 @@
       pride: 'PRIDE'
     switch aspect
       when 'pathway'
-        sparql = '?up up:annotation ?annotation .\n\t?annotation rdf:type up:Pathway_Annotation .\n\t?annotation rdfs:seeAlso ?pathway .\n\t?pathway rdfs:label ?label .'
+        sparql = '''
+          ?up up:annotation ?annotation .
+          ?annotation rdf:type up:Pathway_Annotation .
+          ?annotation rdfs:seeAlso ?pathway .
+          ?pathway rdfs:label ?label .
+        '''
       when 'location'
-        sparql = '?up up:annotation ?annotation .\n\u0009?annotation a up:Subcellular_Location_Annotation .\n\u0009?annotation up:locatedIn/up:cellularComponent ?location .\n\u0009?location up:alias ?label .'
+        sparql = '''
+          ?up up:annotation ?annotation .
+          ?annotation a up:Subcellular_Location_Annotation .
+          ?annotation up:locatedIn/up:cellularComponent ?location .
+          ?location up:alias ?label .
+        '''
       when 'geneontology'
-        sparql = '?up up:classifiedWith ?go .\n\u0009?go up:database db:go .\n\u0009?go rdfs:label ?label .'
+        sparql = '''
+          ?up up:classifiedWith ?go .
+          ?go up:database db:go .
+          ?go rdfs:label ?label .
+        '''
       when 'interpro', 'pfam', 'supfam', 'prosite', 'reactome', 'cazy'
         sparql = @dbsparql(mapping[aspect])
       when 'ctd', 'brenda', 'eggnog', 'genetree', 'hogenom', 'hovergen', 'inparanoid', 'ko', 'oma', 'orthodb', 'phylomedb', 'treefam', 'nextbio', 'paxdb', 'pride'
         sparql = @dbsparql_link(mapping[aspect])
     sparql
+
   dbsparql: (db) ->
-    '\u0009\u0009\u0009?up rdfs:seeAlso ?link .\n\u0009\u0009\u0009?link up:database db:@@database@@ .\n\u0009\u0009\u0009?link rdfs:comment ?label .'.replace /@@database@@/, db
+    '''
+      ?up rdfs:seeAlso ?link .
+      ?link up:database db:@@database@@ .
+      ?link rdfs:comment ?label .
+    '''.replace(/@@database@@/, db)
+
   dbsparql_link: (db) ->
-    '\u0009\u0009\u0009?up rdfs:seeAlso ?label .\n\u0009\u0009\u0009?label up:database db:@@database@@ .'.replace /@@database@@/, db
+    '''
+      ?up rdfs:seeAlso ?label .
+      ?label up:database db:@@database@@ .
+    '''.replace(/@@database@@/, db)
+
   step1: ->
     self = this
     taxIds = undefined
@@ -640,7 +760,9 @@
     commonNames = @cache.species.map((taxon) ->
       taxon.data.common_name
     )
-    sparql = SPARQLS.step1.replace(/@@taxvalues@@/, 'tax:' + taxIds.join(' tax:')).replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
+    sparql = SPARQLS.step1.
+      replace(/@@taxvalues@@/, 'tax:' + taxIds.join(' tax:')).
+      replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
     d3sparql.query ENDPOINT, sparql, ((response) ->
       html = ''
       results = response.results.bindings
@@ -675,13 +797,21 @@
         0
       # 結果の描画
       results.forEach (d) ->
-# 結果の描画：図表
+        # 結果の描画：図表
         commonNames2 = d.taxIdIndices.map((taxIdIndex) ->
           commonNames[taxIdIndex]
         )
         setValue = setPrefix + d.taxIdIndices.join('_')
         barWidth = parseInt(d.count.value) / max * 100
-        html += '\u0009\u0009\u0009\u0009\u0009
\u0009\u0009\u0009\u0009\u0009\u0009
' + commonNames2.join('∩') + '
\u0009\u0009\u0009\u0009\u0009\u0009

\u0009\u0009\u0009\u0009\u0009\u0009
= 50 then ' over-half' else '') + '" style="width: ' + barWidth + '%;">' + d.count.value + '
\u0009\u0009\u0009\u0009\u0009
'
+        html += """
+

+
#{commonNames2.join('∩')}

+

+
= 50 then ' over-half' else ''}" style="width: #{barWidth}%">
+              #{d.count.value}
+

+
'
+        """
         # 結果の描画：ベン図
         d3.select('#venn-text-' + setPrefix + d.taxIdIndices.join('_')).text d.count.value
         return
@@ -724,6 +854,7 @@
       return
     ).bind(this)
     return
+
   step2: (taxIds) ->
     self = this
     sparql = undefined
@@ -749,7 +880,10 @@
     @$linkToContainer.empty()
     @$sectionCategory.addClass 'loading'
     # クエリ
-    sparql = SPARQLS.step2.replace(/@@taxvalues@@/, @taxIdsWithString(@cache.taxIds)).replace(/@@taxfilter@@/, @cache.taxIds).replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
+    sparql = SPARQLS.step2.
+      replace(/@@taxvalues@@/, @taxIdsWithString(@cache.taxIds)).
+      replace(/@@taxfilter@@/, @cache.taxIds).
+      replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
     d3sparql.query ENDPOINT, sparql, (response) ->
       results = response.results.bindings
       # 有効化
@@ -762,7 +896,14 @@
       # グラフの描画
       results.forEach (d) ->
         barWidth = parseInt(d.sum.value) / max * 100
-        html += '\u0009\u0009\u0009\u0009\u0009
\u0009\u0009\u0009\u0009\u0009\u0009
' + d.label.value + '
\u0009\u0009\u0009\u0009\u0009\u0009
= 50 then ' over-half' else '') + '" style="width: ' + barWidth + '%;">' + d.sum.value + '
\u0009\u0009\u0009\u0009\u0009
'
+        html += """
+

+
#{d.label.value}

+
= 50 then ' over-half' else ''}" style="width: #{barWidth}%">
+              #{d.sum.value}
+

+

+        """
         return
       self.$categoryContainer.html wrapInner(html)
       # イベント
@@ -772,6 +913,7 @@
         return
       return
     return
+
   step3: (category) ->
     self = this
     sparql = undefined
@@ -788,31 +930,41 @@
       @category = '"' + category + '"'
     else
       @category = '<' + category + '>'
-    sparql = SPARQLS.step3.replace(/@@taxvalues@@/, @taxIdsWithString(@cache.taxIds)).replace(/@@category@@/, @category).replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
+
+    sparql = SPARQLS.step3.
+      replace(/@@taxvalues@@/, @taxIdsWithString(@cache.taxIds)).
+      replace(/@@category@@/, @category).
+      replace(/@@aspect@@/, @aspectSparql(@cache.aspect))
+
     d3sparql.query ENDPOINT, sparql, (response) ->
-# 有効化
+      # 有効化
       self.$sectionLinkTo.removeClass 'disabled loading'
       header = response.head.vars
       results = response.results.bindings
-      html = '

@@thead@@

@@tbody@@

'.replace(/@@thead@@/, header.map((value) ->
-        '' + LINKS_TO_HEADER_LABEL_MAPPING[value] + '	'
-      ).join('')).replace(/@@tbody@@/, results.map((value) ->
-        tr = header.map((headerEml) ->
-          '@@uri@@	'.replace /@@uri@@/g, value[headerEml].value
-        ).join('')
-        '
' + tr + '
'
-      ).join(''))
+      html = '

@@thead@@

@@tbody@@

'.
+        replace(/@@thead@@/, header.map((value) ->
+          '' + LINKS_TO_HEADER_LABEL_MAPPING[value] + '	'
+        ).join('')).
+        replace(/@@tbody@@/, results.map((value) ->
+          tr = header.map((headerEml) ->
+            '@@uri@@	'.replace(/@@uri@@/g, value[headerEml].value)
+          ).join('')
+          '
' + tr + '
'
+        ).join(''))
       self.$linkToContainer.html wrapInner(html)
       return
     return
+
   taxIdsWithArray: (array) ->
     array.map((d) ->
       'tax:' + d
     ).join ' '
+
   taxIdsWithString: (string) ->
     string.split(', ').map((d) ->
       'tax:' + d
     ).join ' '
+
 $ ->
   application = new Application
   # TogoGenome 埋め込みにあたっての処置


Regex for adding id to fields

Regex for adding id to fields

--- app/assets/javascripts/incident_reports/edit.coffee
+++ app/assets/javascripts/incident_reports/edit.coffee
@@ -17,7 +17,12 @@

 addFields = (fieldsSelector) ->
   fields = $(fieldsSelector).last()
-  fields.clone().insertAfter fields
+  field = fields.clone()
+  for attrs in field.find('input')
+    num = parseInt(attrs.id.match /\d+/) + 1
+    attrs.id = attrs.id.replace /\d+/, num
+    attrs.name = attrs.name.replace /\d+/, num
+  field.insertAfter fields
   $(fieldsSelector).last().find('input').val('').prop 'checked', false

 addWitnessFields = (event) ->


stop impersonating when value is cleared

--- app/assets/javascripts/main/views/nav_bar.js.coffee
+++ app/assets/javascripts/main/views/nav_bar.js.coffee
@@ -29,6 +29,10 @@
       "change select#pretend": (e) ->
         view = @
         user_id = $(e.target).val() # new user id
+
+        if !user_id? || user_id == ""
+          user_id = @true_user.id # impersonation was cleared
+
         new Bixby.model.User().impersonate user_id, (data, status, xhr) ->
           view.$("li.dropdown").removeClass('open')
           view.current_user = Bixby.app.current_user = new Bixby.model.User(data)


code readability fix

code readability fix

--- client/landing/app/Applications/Ace.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Ace.kdapplication/AppController.coffee
@@ -27,8 +27,8 @@
       { title     : "Find and Replace",    eventName : "findAndReplace" }
       { title     : "Goto line",           eventName : "gotoLine" }
       { type      : "separator" }
-      { title     : "Compile and Run",     eventName : "compileAndRun", condition: compileCondition}
-      { type      : "separator",                                        condition: compileCondition}
+      { title     : "Compile and Run",     eventName : "compileAndRun", condition: canCompile}
+      { type      : "separator",                                        condition: canCompile}
       { title     : "Preview",             eventName : "preview" }
       { type      : "separator" }
       { title     : "Advanced Settings",   id        : "advancedSettings" }


Load preview modules with actual preview elements

--- cms/static/coffee/src/views/module_edit.coffee
+++ cms/static/coffee/src/views/module_edit.coffee
@@ -10,7 +10,7 @@
   initialize: ->
     @$el.load @model.editUrl(), =>
       @model.loadModule(@el)
-      @$el.find('.preview').each (idx, previewEl) =>
+      @$el.find('.preview :first-child').each (idx, previewEl) =>
           @model.loadPreview(previewEl)

   save: (event) ->


Atualizando chamada aos dados

Atualizando chamada aos dados

--- src/assets/coffee/app.coffee
+++ src/assets/coffee/app.coffee
@@ -5,7 +5,8 @@

 @App.controller 'PageController', ($scope, $http) ->
   $http.get('https://raw.githubusercontent.com/pedrorocha-net/' +
-      'eleicoes2016-rj-comparadoacoes/master/data/candidatos.json'
+      'eleicoes2016-rj-comparadoacoes/master/data/' +
+      'candidatos_dados_processados.json'
   ).then (result) ->
     $scope.candidatos = result.data
     $scope.selecao1 = null


fix(web): fixed user/group unbind;

fix(web): fixed user/group unbind;

--- apps/actor-web/app/components/app/appController.coffee
+++ apps/actor-web/app/components/app/appController.coffee
@@ -9,18 +9,16 @@
     @$scope.$on 'onConversationOpen', (event, peer) =>
       switch peer.type
         when 'user'
-          @actorService.bindUser peer.id, (info) => @renderPeerInfo info
+          @actorService.bindUser peer.id, @renderPeerInfo
         when 'group'
-          @actorService.bindGroup peer.id, (info) => @renderPeerInfo info
+          @actorService.bindGroup peer.id, @renderPeerInfo

     @$scope.$on 'onConversationClosed', (event, peer) =>
       switch peer.type
         when 'user'
-          @actorService.unbindUser peer.id, =>
-            console.log '[AW]AppController unbindUser: unbinded'
+          @actorService.unbindUser peer.id, @renderPeerInfo
         when 'group'
-          @actorService.unbindGroup peer.id, =>
-            console.log '[AW]AppController unbindGroup: unbinded'
+          @actorService.unbindGroup peer.id, @renderPeerInfo

   renderMyInfo: (info) =>
     console.log '[AW]AppController renderMyInfo', info


Added comparator.

Added comparator.

--- src/lib/strata.coffee
+++ src/lib/strata.coffee
@@ -24,10 +24,14 @@
   compare: (left, right) ->
     if left < right then -1 else if left > right then 1 else 0

+comparator = (a, b) ->
+  if a < b then -1 else if a > b then 1 else 0
+
 class module.exports.Strata
   constructor: (options) ->
     @leafSize = options.leafSize or 12
     @branchSize = options.branchSize or 12
+    @comparator or= comparator
     throw new Error("I/O Strategy is required.") unless @io = options.io
     throw new Error("Root address is required.") unless @rootAddress = options.rootAddress
     @locks = { shared: {}, exclusive: {} }
@@ -48,6 +52,8 @@
     mutation.levels.push mutation.parentLevel
     parentLevel.lock mutation, @_initialTest

+  # Perform the root decision with only a read lock obtained. If it decides to
+  # mutate the root, we will
   _initialTest: (mutation) ->
     mutation.childLevel = new Level(false)
     mutation.levels.push mutation.childLevel
@@ -56,8 +62,6 @@
       mutation.parentLevel.upgrade mutation.childLevel, @_initialTestAgain

   _initialTestAgain: (mutation) ->
-    parentLevel = mutation.levels[0]
-    parent = @io.root @rootAddress
     if not mutation.initial.call this, mutation
       mutation.rewind 0
     @_tierDecision mutation


Only resize the chart if it can determine the new container dimensions

--- app/assets/javascripts/bz_highcharts/chart_helpers.js.coffee
+++ app/assets/javascripts/bz_highcharts/chart_helpers.js.coffee
@@ -37,9 +37,10 @@
         if chart?
           chart_el = $(chart.container)
           chart_container = chart_el.closest(".js-highcharts").parent()
-          chart_el.hide()
-          chart.setSize(chart_container.width(), chart.containerHeight, false)
-          chart_el.show()
+          if chart_container.width() && chart.containerHeight
+            chart_el.hide()
+            chart.setSize(chart_container.width(), chart.containerHeight, false)
+            chart_el.show()
     ), 500

   # resize charts on window resized


[MatrixView] Enabling "allowsDragSelection" will force "allowsMultipleSelection" to be enabled.

--- app/assets/javascripts/cartilage/views/matrix_view.js.coffee
+++ app/assets/javascripts/cartilage/views/matrix_view.js.coffee
@@ -15,10 +15,11 @@

   initialize: (options = {}) ->

+    super(options)
+
     # Apply options
     @allowsDragSelection = options["allowsDragSelection"] || (@allowsDragSelection ?= false)
-
-    super(options)
+    @allowsMultipleSelection = options["allowsDragSelection"] || (@allowsMultipleSelection ?= @allowsDragSelection)

   render: =>
     super()


Fix scheduling once schedule

Fix scheduling once schedule

--- _source/client/templates/components/controls/calendar.coffee
+++ _source/client/templates/components/controls/calendar.coffee
@@ -139,7 +139,7 @@

     switch frequency
       when "Once"
-        return null
+        return moment(today).recur(today)
       when "Weekly"
         return moment(today).recur().every(1).weeks()
       when "Biweekly"


Removed parents-esis

Removed parents-esis

--- core/app/backbone/views/facts/auto_complete_fact_relations_view.coffee
+++ core/app/backbone/views/facts/auto_complete_fact_relations_view.coffee
@@ -136,7 +136,7 @@
     content = @$('.content').val()
     comment = new Comment(content: content, created_by: currentUser)

-    @comments.add(comment)
+    @comments.add comment
     comment.save {},
       error: =>
         @comments.remove comment


Incorrect variable

Incorrect variable

Signed-off-by: Steven Hoffman <fe20d0b9f22d8cad9ff6cecdb579c0c0dbedec8a@me.com>

--- vendor/assets/javascripts/fustrate/components/alert_box.coffee
+++ vendor/assets/javascripts/fustrate/components/alert_box.coffee
@@ -2,9 +2,11 @@
   @fadeSpeed: 300

   @initialize: ->
-    $('.alert-box').on 'click', '.close', (event) ->
+    $('.alert-box').on 'click', '.close', (e) ->
       event.preventDefault()

       alert_box = $(e.currentTarget).closest('.alert-box')

       alert_box.fadeOut @constructor.fadeSpeed, -> alert_box.remove()
+
+      false


Fix pull when receiving a single file

Fix pull when receiving a single file

--- components/BlobToEntry.coffee
+++ components/BlobToEntry.coffee
@@ -2,6 +2,7 @@
 uuid = require 'uuid'
 collections = require '../src/collections'
 projects = require '../src/projects'
+_ = require 'underscore'

 handleGraph = (sha, content, entry, project, callback) ->
   # Start by loading the graph object
@@ -110,6 +111,9 @@
     unless operation.pull?.length
       output.done new Error 'Operation does not provide any pull entries'
       return
+    unless _.isArray blobs
+      # Only one received
+      blobs = [blobs]

     entities = []
     errors = []


CreateModel: remove useless require

CreateModel: remove useless require

--- components/CreateModel.coffee
+++ components/CreateModel.coffee
@@ -1,4 +1,3 @@
-schemagen = require 'mongoose-schemagen'
 noflo = require 'noflo'

 class CreateModel extends noflo.Component


add port`s type for MakeRequest

add port`s type for MakeRequest

--- components/MakeRequest.coffee
+++ components/MakeRequest.coffee
@@ -10,10 +10,10 @@
     @method = 'GET'

     @inPorts =
-      url:    new Port
-      method: new Port
+      url:    new Port 'string'
+      method: new Port 'string'
     @outPorts =
-      out: new Port
+      out: new Port 'object'

     @inPorts.method.on 'data', (@method) =>


burlywood

burlywood

--- src/load/kinds.coffee
+++ src/load/kinds.coffee
@@ -30,9 +30,9 @@
       editable: false
       obstruction: true
       position: [7,0]
-      fill: 'brown'
+      fill: 'burlywood'
       paths: [
-        "M#{OFFSET+ INSET/2},#{OFFSET} h#{INSET/2} v#{INSET} h-#{INSET/2} v-#{INSET}"
+        "M#{OFFSET+ INSET/4},#{OFFSET} h#{INSET/2} v#{INSET} h-#{INSET/2} v-#{INSET}"
       ]
     }
     egg: {


default name of gtae is exit

default name of gtae is exit

--- src/load/kinds.coffee
+++ src/load/kinds.coffee
@@ -46,7 +46,7 @@
       ]
     }
     gate: {
-      name: 'gate'
+      name: 'exit'
       editable: false
       obstruction: false
       position: [7,7]


upload dir tmp fix

upload dir tmp fix

--- components/backend/modules/files/server.coffee
+++ components/backend/modules/files/server.coffee
@@ -52,7 +52,8 @@

   app.post "/uploadFile", auth, (req,res)->
-    form = new multiparty.Form
+    form = new multiparty.Form
+      uploadDir: dir
     form.parse req, (err, fields, files)->
       if err then return console.log 'formparse error', err
       for srcFile in files['files[]']


'fastRender' breaks spiderable (but only on this route, on modulus?), take out for now.

--- router.coffee
+++ router.coffee
@@ -110,8 +110,6 @@
       Meteor.subscribe 'authors'
     ]

-    fastRender: true
-
     data: ->
       Post.first slug: @params.slug


Remove conflicting isAnimating property

Remove conflicting isAnimating property

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -56,7 +56,6 @@
 	@define "speedX", @proxyProperty("content.draggable.speedX", true)
 	@define "speedY", @proxyProperty("content.draggable.speedY", true)
 	@define "isDragging", @proxyProperty("content.draggable.isDragging", true)
-	@define "isAnimating", @proxyProperty("content.draggable.isAnimating", true)

 	# We throw an error here, because you almost never would like the enclosing
 	# scroll component to be draggable, but it's an easy mistake to make. If you


Strip whitespace from api url input

Strip whitespace from api url input

--- app/assets/javascripts/admin/views/connections/new.js.coffee
+++ app/assets/javascripts/admin/views/connections/new.js.coffee
@@ -45,7 +45,7 @@
             .anythingBut(' ')
             .then('/api')
             .endOfLine()
-          regex.test val
+          regex.test val.trim()
       messages:
         apiurl: 'This value should be a valid API url.'


Added task auto resize.

Added task auto resize.

--- app/assets/javascripts/app/controllers/task_widget.js.coffee
+++ app/assets/javascripts/app/controllers/task_widget.js.coffee
@@ -94,6 +94,8 @@
       taskBarActions: @_getTaskActions()
     )

+    @resizeTasks()
+
     dndOptions =
       tolerance:            'pointer'
       distance:             15
@@ -140,6 +142,8 @@
     # remove task
     App.TaskManager.remove( key )

+    @resizeTasks()
+
     # navigate to next task if needed
     tasks = App.TaskManager.all()
     if active_is_closed && !_.isEmpty( tasks )
@@ -153,6 +157,17 @@
         return
     if _.isEmpty( tasks )
       @navigate '#'
+
+  resizeTasks: ->
+    width = $('#task .taskbar').width() - 280
+    task_count = App.TaskManager.all().length
+    task_size  = ( width / task_count ) - ( task_count * 1.3 )
+    if task_size < 40
+      $('#task .task').css('max-width', '40px')
+    else if task_size < 130
+      $('#task .task').css('max-width', task_size + 'px')
+    else
+      $('#task .task').css('max-width', '120px')

   _getTaskActions: ->
     roles  = App.Session.get( 'roles' )


Disable pistachios

Disable pistachios

--- config/main.rc.coffee
+++ config/main.rc.coffee
@@ -85,7 +85,7 @@
   presence      :
     exchange    : 'services-presence'
   client        :
-    pistachios  : yes
+    pistachios  : no
     version     : version
     minify      : yes
     watch       : no


added edge types to graph

added edge types to graph

--- src/gg/util/graph.coffee
+++ src/gg/util/graph.coffee
@@ -1,3 +1,5 @@
+#<< gg/util/log
+
 _ = require 'underscore'

 class gg.util.Graph
@@ -6,6 +8,7 @@
     @id2node = {}
     @pid2cid = {}     # parent id -> child ids
     @cid2pid = {}     # child id -> parent ids
+    @log = gg.util.Log.logger "graph", gg.util.Log.DEBUG

   id: (@idFunc) ->

@@ -15,6 +18,7 @@
     @id2node[id] = node
     @pid2cid[id] = {}  # store it as an associative map
     @cid2pid[id] = {}
+    @log "add\t#{node}"
     @

   rm: (node) ->
@@ -22,19 +26,20 @@
     return @ unless id of @id2node

     # delete all pointers
-    _.each @cid2pid[id], (md, pid) => delete @pid2cid[pid][id]
-    _.each @pid2cid[id], (md, cid) => delete @cid2pid[cid][id]
+    _.each @cid2pid[id], (edges, pid) => delete @pid2cid[pid][id]
+    _.each @pid2cid[id], (edges, cid) => delete @cid2pid[cid][id]

     # delete node and edge entries
     delete @pid2cid[id]
     delete @cid2pid[id]
     delete @id2node[id]
+    @log "rm\t#{node}"
     @

   # add an edge
   # subsequent calls will OVERWRITE existing metadata
-  connect: (from, to, metadata=null) ->
+  connect: (from, to, type, metadata=null) ->
     if _.isArray from
       _.each from, (args) => @connect args...
       return @
@@ -44,46 +49,71 @@
     fid = @idFunc from
     tid = @idFunc to

-    @pid2cid[fid][tid] = metadata
-    @cid2pid[tid][fid] = metadata
+    @pid2cid[fid][tid] = {} unless @pid2cid[fid][tid]?
+    @cid2pid[tid][fid] = {} unless @cid2pid[tid][fid]?
+    @pid2cid[fid][tid][type] = metadata
+    @cid2pid[tid][fid][type] = metadata
+    @log "connect: #{from.toString()} -> #{to.toString()}\t#{type}\t#{JSON.stringify metadata}"
     @

-  edgeExists: (from, to) ->
+  # @param type null to match all edges, or set to a value to check if edge type exists
+  edgeExists: (from, to, type) ->
     fid = @idFunc from
     tid = @idFunc to
-    fid of @pid2cid and tid of @pid2cid[fid]
+    return false unless fid of @pid2cid and tid of @pid2cid[fid]
+    edges = @pid2cid[fid][tid]
+    if type? then  type of edges else true

   # retrieve metadata for edge
-  metadata: (from, to) ->
+  # @param type null to fetch all metadatas, or set to value to retrive specific metadata
+  metadata: (from, to, type) ->
     fid = @idFunc from
     tid = @idFunc to
     return null unless fid of @id2node
-    @pid2cid[fid][tid]
+    edges = @pid2cid[fid][tid]
+    return null unless edges?
+    if type?
+      edges[type]
+    else
+      _.map edges, (md, t) -> md

   nodes: (filter=(node)->true) ->
     _.filter _.values(@id2node), filter

   # return a list of all edges in the graph
-  edges: (filter=(metadata)->true) ->
-    edges = []
+  # that are of type @param type AND pass the filter
+  #
+  # @param type null to return all filtered edges, otherwise filter for specified edges
+  # @param filter boolean function
+  edges: (type, filter=(metadata)->true) ->
+    ret = []
     _.each @pid2cid, (cmap, pid) =>
-      subEdges = _.map cmap, (metadata, cid) =>
-        [@id2node[pid], @id2node[cid], metadata] if filter metadata
-      edges.push.apply edges, subEdges
-    edges
+      _.map cmap, (edges, cid) =>
+        _.map edges, (metadata, t) =>
+          if (not type? or t == type) and filter metadata
+            ret.push [@id2node[pid], @id2node[cid], t, metadata]
+    ret

   # extract children whose edges pass the filter parameter
+  # @param type null to check all edges, otherwise filter for specified edges
   # @param filter takes edge metadata as input
-  children: (node, filter=((metadata)->true)) ->
+  #
+  children: (node, type, filter=((metadata)->true)) ->
     id = @idFunc node
-    _.compact _.map @pid2cid[id], (metadata, id) =>
-      @id2node[id] if filter metadata
+    children = []
+    _.each @pid2cid[id], (edges, id) =>
+      if _.any(edges, (metadata, t)-> (not type? or type == t) and filter(metadata))
+        children.push @id2node[id]
+    children

-  parents: (node, filter=(metadata)->true) ->
+  parents: (node, type, filter=(metadata)->true) ->
     id = @idFunc node
-    _.compact _.map @cid2pid[id], (metadata, id) =>
-      @id2node[id] if filter metadata
+    parents = []
+    _.each @cid2pid[id], (edges, id) =>
+      if _.any(edges, (metadata, t)-> (not type? or type == t) and filter(metadata))
+        parents.push @id2node[id]
+    parents

   sources: ->
     ids = _.filter _.keys(@id2node), (id) =>


Moved opinion animation stuff to hints methods

--- core/app/backbone/client/facts_new_view.coffee
+++ core/app/backbone/client/facts_new_view.coffee
@@ -5,9 +5,6 @@

   showTooltip: ->
     return if ( ! @_shouldShowTooltip )
-
-    if FactlinkApp.guided
-      @$('.js-opinion-animation').show();

     else
       @$('.fact-wheel').tooltip(
@@ -20,7 +17,6 @@
     $(window).off 'resize.whatsyouropinion'
     @$('.fact-wheel').off 'click.whatsyouropinion'
     @$('.fact-wheel').tooltip('destroy')
-    @$('.js-opinion-animation').hide()

   render: ->
     $(window).on 'resize.whatsyouropinion', => @showTooltip();
@@ -105,8 +101,13 @@
   renderHints: ->
     @the_tooltip.render()

+    if FactlinkApp.guided
+      @$('.js-opinion-animation').show();
+
   closeHints: ->
     @the_tooltip.close()
+
+    @$('.js-opinion-animation').hide()

   renderShareNewFact: ->
     @factSharingOptions = new FactSharingOptions


Link to shipments when clicking to error

--- app/assets/javascripts/components/validation_error.js.coffee
+++ app/assets/javascripts/components/validation_error.js.coffee
@@ -18,7 +18,7 @@
       div(
         { className: 'error-message' }
         a(
-          { href: '#' }
+          { href: '/shipments/' }
           @state.data.error_message
         )
       )


When a feed refresh job finishes successfully, its unread entries count is updated in the sidebar automatically.

Or, if the feed being refreshed is not in the sidebar right now, it is inserted there.

--- app/assets/javascripts/ng-services/ng-JobStatusSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-JobStatusSvc.js.coffee
@@ -49,6 +49,7 @@
             timerFlagSvc.start 'error_refreshing_feed'
           else if data.status=="SUCCESS"
             timerFlagSvc.start 'success_refresh_feed'
+            feedsFoldersSvc.load_feed job.feed_id
         .error (data, status)->
           # if HTTP call has been prematurely cancelled, do nothing
           timerFlagSvc.start 'error_loading_job_statuses' if status!=0 && status!=404


Fixed direction of "plural" function in util.coffee.  (Sigh.)

--- drivers/javascript/util.coffee
+++ drivers/javascript/util.coffee
@@ -1,6 +1,6 @@
 err = require('./errors')

-plural = (number) -> if number == 1 then "s" else ""
+plural = (number) -> if number == 1 then "" else "s"

 # Function wrapper that enforces that the function is
 # called with the correct number of arguments


* Do not try to remove model on destroy method because it has already been removed by release

--- app/assets/javascripts/backbone.rails_store.js.coffee
+++ app/assets/javascripts/backbone.rails_store.js.coffee
@@ -1292,7 +1292,6 @@
       # Ignore destroy request on read only models
       return false
     @_store.registerDestroyRequest(@)
-    @collection.remove(@)
     @trigger('destroy', @)

   ###


fix issues with basic_edit when multiple editors exist on one page

--- app/assets/javascripts/transit/frontend/basic_edit.js.coffee
+++ app/assets/javascripts/transit/frontend/basic_edit.js.coffee
@@ -5,43 +5,35 @@

 editInstance = null

-@Transit.basicEdit = (node)->
-  Transit.basicEditor ||= new Editor()
-  $(node).each (i, el)->
-    field = $(el)
-    return true if field.hasClass('transit-editor')
+class Editable
+  constructor:(@field)->
+    @field.addClass('transit-editor')
+      .wrap('

')
+    @editor = @field.parent('div.transit-editor')
+    @editor.prepend("

")
+    @content = @editor.find("> div.editor-content")
+    @content.html( @field.val() )
+      .css( 'min-height', @field.height() )
+    @field.addClass('transit-editor')
+    @field.closest('form').on 'submit', @save

-    field.wrap('

')
-    editor = field.parent('div.transit-editor')
-    editor.prepend("

")
-    content = editor.find("> div.editor-content")
-    content.html( field.val() )
-      .css( 'min-height', field.height() )
-    field.addClass('transit-editor')
-    field.closest('form').on 'submit', (event)->
-      $('div.transit-editor div.editor-content').trigger('blur')
-    true
-
-
-$(document).on 'mouseenter mouseleave', 'div.transit-editor', (event)->
-  if event.type is 'mouseenter' then $(event.currentTarget).addClass('current')
-  else $(event.currentTarget).removeClass('current')
-
-$(document).on 'click', 'div.transit-editor div.editor-content', (event)->
-  editor = $(event.currentTarget)
-  return true if editor == editInstance
-  Transit.basicEditor.activate(editor,
-    placeholder: "
Enter your text
",
-    controlsTarget: editor.parent('div.transit-editor')
-  )
-  editInstance = editor
-
-
-$(document).on 'blur', 'div.transit-editor div.editor-content', (event)->
-  editor = $(event.currentTarget)
-  unless editor.parent('div.transit-editor').hasClass('current')
-    editor.trigger('changed.editor')
-    field  = editor.next('textarea.transit-editor')
+    @content.on 'blur.editor', @save
+
+    @editor.on 'mouseenter mouseleave', (event)=>
+      if event.type is 'mouseenter' then @editor.addClass('current')
+      else @editor.removeClass('current')
+
+    @content.on 'click', (event)=>
+      return true if editInstance is @
+      Transit.basicEditor.activate(@content,
+        placeholder: "
Enter your text
",
+        controlsTarget: @editor
+      )
+      editInstance = @
+
+  save:(event)=>
+    return true unless editInstance is @
+    @content.trigger('changed.editor')
     wrap   = $("

")
     wrap.html(Transit.basicEditor.content())
     wrap.find('p').each (i, el)->
@@ -49,8 +41,61 @@
       if $.trim(para.text()) == ''
         para.remove()
     wrap.find('[style]').each (i, el)-> $(el).removeAttr('style')
-    editor.html( wrap.html() )
-    field.val( wrap.html() )
+    @content.html( wrap.html() )
+    @field.val( wrap.html() )
+    Transit.basicEditor.deactivate()
+    editInstance = null
+    return false

-    Transit.basicEditor.deactivate()
-    editInstance = null+
+@Transit.basicEdit = (node)->
+  Transit.basicEditor ||= new Editor()
+  $(node).each (i, el)->
+    field = $(el)
+    return true if field.data('transit_editor') isnt undefined
+    field.data('transit_editor', new Editable( field ))
+    # return true if field.hasClass('transit-editor')
+    #
+    # field.wrap('

')
+    # editor = field.parent('div.transit-editor')
+    # editor.prepend("

")
+    # content = editor.find("> div.editor-content")
+    # content.html( field.val() )
+    #   .css( 'min-height', field.height() )
+    # field.addClass('transit-editor')
+    # field.closest('form').on 'submit', (event)->
+    #   $('div.transit-editor div.editor-content').trigger('blur')
+    # true
+
+
+# $(document).on 'mouseenter mouseleave', 'div.transit-editor', (event)->
+#   if event.type is 'mouseenter' then $(event.currentTarget).addClass('current')
+#   else $(event.currentTarget).removeClass('current')
+#
+# $(document).on 'click', 'div.transit-editor div.editor-content', (event)->
+#   editor = $(event.currentTarget)
+#   return true if editor == editInstance
+#   Transit.basicEditor.activate(editor,
+#     placeholder: "
Enter your text
",
+#     controlsTarget: editor.parent('div.transit-editor')
+#   )
+#   editInstance = editor
+#
+#
+# $(document).on 'blur', 'div.transit-editor div.editor-content', (event)->
+#   editor = $(event.currentTarget)
+#   unless editor.parent('div.transit-editor').hasClass('current')
+#     editor.trigger('changed.editor')
+#     field  = editor.next('textarea.transit-editor')
+#     wrap   = $("

")
+#     wrap.html(Transit.basicEditor.content())
+#     wrap.find('p').each (i, el)->
+#       para = $(el)
+#       if $.trim(para.text()) == ''
+#         para.remove()
+#     wrap.find('[style]').each (i, el)-> $(el).removeAttr('style')
+#     editor.html( wrap.html() )
+#     field.val( wrap.html() )
+#
+#     Transit.basicEditor.deactivate()
+#     editInstance = null


Extract methods.

Extract methods.

--- app/scripts/models/one_dimensional_automaton.coffee
+++ app/scripts/models/one_dimensional_automaton.coffee
@@ -3,6 +3,19 @@
     constructor: (params) ->
       seed = params.seed.split ''
       rule = params.rule
+
+      binary = (numberOrString) ->
+        parseInt(numberOrString).toString 2
+
+      binaryArray = (numberOrString, length) ->
+        pad(binary(numberOrString), length).split ''
+
+      computeBit = (index) ->
+        left = if index == 0 then '0' else seed[index - 1]
+        center = seed[index]
+        right = seed[index + 1] || '0'
+        triplet = [left, center, right]
+        ruleBits()[triplet]

       pad = (string, minLength) ->
         if string.length >= minLength
@@ -10,17 +23,17 @@
         else
           pad "0#{string}", minLength

+      ruleBits = ->
+        result = {}
+        for bit, index in binaryArray(rule, 8).reverse()
+          result[binaryArray(index, 3)] = bit
+        result
+
       @compute = ->
-        ruleBits = {}
         result = []
-        for bit, index in pad(parseInt(params.rule).toString('2'), 8).split('').reverse()
-          ruleBits[pad(index.toString('2'), 3).split('')] = bit

-        for center, index in seed
-          left = if index == 0 then '0' else seed[index - 1]
-          right = seed[index + 1] || '0'
-          triplet = [left, center, right]
-          result.push ruleBits[triplet]
+        for _, index in seed
+          result.push computeBit(index)

         result.join ''


fix tool selection from marking task

fix tool selection from marking task

--- app/assets/javascripts/components/subject-viewer.cjsx
+++ app/assets/javascripts/components/subject-viewer.cjsx
@@ -84,9 +84,10 @@

   # Handle initial mousedown:
   handleInitStart: (e) ->
-    console.log "hIS", @props
+    console.log 'SUBJECT-VIEWER::handleInitStart(): ', @props
+
     return null if ! @props.subToolIndex?
-    subTool = @props.task.tool_config.tools[@props.subToolIndex]
+    subTool = @props.task.tool_config.tools[@props.annotation.subToolIndex]
     return null if ! subTool?

     # If there's a current, uncommitted mark, commit it:


use file info from URL uploading status

use file info from URL uploading status

--- app/assets/javascripts/uploadcare/files/url.js.coffee
+++ app/assets/javascripts/uploadcare/files/url.js.coffee
@@ -90,7 +90,7 @@
         .on 'success', (e, data) =>
           $(e.target).trigger('progress', data)
           @fileId = data.uuid
-          @fileName = data.original_filename
+          @__handleFileData(data)
           df.resolve()

         .on('error fail', df.reject)


config: fix health check and post collisions

--- config/generateRunFile.coffee
+++ config/generateRunFile.coffee
@@ -339,9 +339,9 @@

     function check_connectivity_countly() {
       local HOST=$KONFIG_COUNTLY_HOST:$KONFIG_COUNTLY_APIPORT
-      local RESPONSE_CODE=$(curl --silent --output /dev/null --write-out '%{http_code}' --user $USER http://$HOST)
-
-      if [[ $? != 0 || $RESPONSE_CODE != 200 ]]; then
+      local RESPONSE_CODE=$(curl --silent --output /dev/null --write-out '%{http_code}' http://$HOST)
+
+      if [[ $? != 0 || $RESPONSE_CODE != 302 ]]; then
         echo "error: countly service check failed on $HOST"
         return 1
       fi
@@ -414,7 +414,7 @@
     }

     function runCountlyDocker () {
-        docker run -d -p $KONFIG_SERVICEHOST:5672:5672 -p $KONFIG_COUNTLY_APIPORT:80 --name=countly countly/countly-server:16.06
+        docker run -d -p $KONFIG_COUNTLY_APIPORT:80 --name=countly countly/countly-server:16.06
         check_connectivity countly
     }


fix last step of bouygues konnector import

--- server/konnectors/bouyguestelecom.coffee
+++ server/konnectors/bouyguestelecom.coffee
@@ -60,7 +60,8 @@
             .use(filterExisting log, PhoneBill)
             .use(saveDataAndFile log, PhoneBill, 'bouygues', ['facture'])
             .args(requiredFields, {}, {})
-            .fetch (err) ->
+            .fetch (err, fields, entries) ->
+                return callback err if err
                 log.info "Import finished"

                 # TODO move this in a procedure.
@@ -70,8 +71,7 @@
                     options = smart_count: entries.filtered.length
                     notifContent = localization.t localizationKey, options

-                callback err, notifContent
-                callback()
+                callback null, notifContent

 # Procedure to login to Bouygues website.


Now fix the build.

Now fix the build.

--- test/delete.coffee
+++ test/delete.coffee
@@ -94,7 +94,7 @@
                 topic: (obj) -> obj.where("field1=2")
                 'then when toString() is called': tu.contextAssertStringEqual 'DELETE FROM test WHERE (field1=2)'
                 'then when where("field2=\'3\'") is called':
-                    topic: (obj) -> obj.where("field22='3'")
+                    topic: (obj) -> obj.where("field2='3'")
                     'then when toString() is called': tu.contextAssertStringEqual 'DELETE FROM test WHERE (field1=2) AND (field2=\'3\')'
     'when from("test2") is called':
         topic: -> inst().from("test2")


validate function

validate function

--- src/hit-validator.coffee
+++ src/hit-validator.coffee
@@ -86,6 +86,11 @@

     return schemaGenerator.generate()

+validate = (hit) ->
+  hitValidator = new HitValidator hit
+  return hitValidator.validate()
+
 module.exports = {
-  HitValidator
+  HitValidator,
+  validate
 }


fix header test

fix header test

--- test/enttec.coffee
+++ test/enttec.coffee
@@ -30,7 +30,7 @@
     host.commit()

-  it "headers match the spec":  ->
+  it "headers match the spec",  ->
     assert.deepEqual data.slice(0, dmxHeaders.length), dmxHeaders

   it "ending must be 0xe7",  ->


relative path support

relative path support

--- console/runner.coffee
+++ console/runner.coffee
@@ -7,7 +7,8 @@
 read_binary_file = (filename) ->
   util.bytestr_to_array fs.readFileSync(filename, 'binary')

-read_classfile = (cls) -> read_binary_file "third_party/#{cls}.class"
+relpath = process.argv[1].replace(/\/[^\/]*$/, '')
+read_classfile = (cls) -> read_binary_file "#{relpath}/../third_party/#{cls}.class"

 # first two are 'coffee', 'scriptname.coffee'
 fname = if process.argv.length > 2 then process.argv[2] else '/dev/stdin'


Added restrictions to admin commands

Added restrictions to admin commands

--- src/scripts/diagnostics.coffee
+++ src/scripts/diagnostics.coffee
@@ -18,6 +18,8 @@

 constants = require '../helpers/constants'
 HEADERS = constants.HEADERS
+# TODO: FIX THIS FOR WIDE RELEASE
+ADMINS = ['aaronsky', 'reid', 'ryan']

 Organization = require('../models/organization').get()

@@ -26,10 +28,7 @@
   Logger = require('../helpers/logger')(robot)

   isAdminUser = (user) ->
-    return user? and user.is_admin
-
-  isLogChannel = (channel) ->
-    return channel is 'ibizan-diagnostics'
+    return user? and user in ADMINS

   # Org statistics
   robot.router.post '/ibizan/diagnostics/info', (req, res) ->
@@ -52,7 +51,8 @@

   robot.router.post '/ibizan/diagnostics/users', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_USERS_TOKEN
+    if body.token is process.env.SLASH_USERS_TOKEN and
+       isAdminUser body.user_name
       res.status 200
       response = ''
       for user in Organization.users
@@ -66,7 +66,8 @@

   robot.router.post '/ibizan/diagnostics/projects', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_PROJECTS_TOKEN
+    if body.token is process.env.SLASH_PROJECTS_TOKEN and
+       isAdminUser body.user_name
       res.status 200
       response = ''
       for project in Organization.projects
@@ -80,7 +81,8 @@

   robot.router.post '/ibizan/diagnostics/calendar', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_CALENDAR_TOKEN
+    if body.token is process.env.SLASH_CALENDAR_TOKEN and
+       isAdminUser body.user_name
       res.status 200
       response = Organization.calendar.description()
     else
@@ -92,7 +94,8 @@

   robot.router.post '/ibizan/diagnostics/resethounding', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_RESETHOUNDING_TOKEN
+    if body.token is process.env.SLASH_RESETHOUNDING_TOKEN and
+       isAdminUser body.user_name
       res.status 200
       count = Organization.resetHounding()
       response = "Reset #{count}
@@ -107,7 +110,8 @@

   robot.router.post '/ibizan/diagnostics/payroll', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_SYNC_TOKEN
+    if body.token is process.env.SLASH_SYNC_TOKEN and
+       isAdminUser body.user_name
       comps = body.text || []
       start = if comps[0] then moment comps[0] else moment().subtract 2, 'weeks'
       end = if comps[1] then moment comps[1] else moment()


Now-marker in payroll diagnostic command is dysfunctional, creating new Moment objects to compensate

--- src/scripts/diagnostics.coffee
+++ src/scripts/diagnostics.coffee
@@ -111,9 +111,8 @@
       response_url = body.response_url
       if response_url
         comps = body.text || []
-        now = moment()
-        end = if comps[1] then moment(comps[1]) else now
-        start = if comps[0] then moment(comps[0]) else now.subtract(2, 'weeks')
+        start = if comps[0] then moment(comps[0]) else moment().subtract(2, 'weeks')
+        end = if comps[1] then moment(comps[1]) else moment()
         Organization.generateReport(start, end)
         .catch(
           (err) ->


Fix a bug with Frame.merge size calculations

This change fixes a bug with nested frames, where the size of a parent is calculated incorrectly and set to the width/height of the largest child, not to the total sum of widths.

--- src/primitives/frame.coffee
+++ src/primitives/frame.coffee
@@ -69,8 +69,9 @@
 		frame =
 			x: Math.min(r1.x, r2.x)
 			y: Math.min(r1.y, r2.y)
-			width: Math.max(r1.width, r2.width)
-			height: Math.max(r1.height, r2.height)
+			width: Math.max(r1.width, (r2.x - r1.x) + r2.width),
+		        height: Math.max(r1.height, (r2.y - r1.y) + r2.height)
+
 		return new Frame frame

 Frame.Properties =
@@ -88,4 +89,4 @@
 	midY: null
 	maxY: null

-exports.Frame = Frame+exports.Frame = Frame


It ain't love but it ain't bad: Reverse DNS Functional

--- src/scripts/reverse-dns.coffee
+++ src/scripts/reverse-dns.coffee
@@ -15,4 +15,8 @@

 module.exports = (robot) ->
   robot.respond /reverse dns (.*)/i, (msg) ->
-    # Get reverse dns
+    ip = msg.match[1].toLowerCase()
+    robot.http("http://api.hackertarget.com/reversedns/?q=#{ip}")
+      .get() (err, res, body) ->
+
+        msg.send "Reverse DNS: #{body}"


compileJoin should happen once after all files are read

--- command.coffee
+++ command.coffee
@@ -74,25 +74,38 @@
 # compile them. If a directory is passed, recursively compile all
 # '.coffee' extension source files in it and all subdirectories.
 compileScripts = ->
+  unprocessed = []
+  for source in sources
+    unprocessed[sources.indexOf(source)]=1
   for source in sources
     base = path.join(source)
     compile = (source, sourceIndex, topLevel) ->
+      remaining_files = ->
+        total = 0
+        total += x for x in unprocessed
+        total
       path.exists source, (exists) ->
         throw new Error "File not found: #{source}" if topLevel and not exists
         fs.stat source, (err, stats) ->
           throw err if err
           if stats.isDirectory()
             fs.readdir source, (err, files) ->
+              unprocessed[sourceIndex] += files.length
               for file in files
                 compile path.join(source, file), sourceIndex
+              unprocessed[sourceIndex] -= 1
           else if topLevel or path.extname(source) is '.coffee'
             fs.readFile source, (err, code) ->
+              unprocessed[sourceIndex] -= 1
               if opts.join
                 contents[sourceIndex] = helpers.compact([contents[sourceIndex], code.toString()]).join('\n')
-                compileJoin() if helpers.compact(contents).length > 0
+                if remaining_files() == 0
+                  compileJoin() if helpers.compact(contents).length > 0
               else
                 compileScript(source, code.toString(), base)
             watch source, base if opts.watch and not opts.join
+          else
+            unprocessed[sourceIndex] -= 1
     compile source, sources.indexOf(source), true

 # Compile a single source script, containing the given code, according to the


Environments: Update Stack view for new data structure

--- client/Environments/views/environmentsmainscene.coffee
+++ client/Environments/views/environmentsmainscene.coffee
@@ -163,27 +163,10 @@
       @_inProgress = no
       @updateView yes

-  dumpStack:->

-    {containers, connections} = @scene
+  updateView:(updateData = no)->

-    dump = {}
-
-    for i, container of containers
-      name = EnvironmentScene.containerMap[container.constructor.name]
-      dump[name] = []
-      for j, dia of container.dias
-        dump[name].push \
-          if name is 'domains'
-            title   : dia.data.title
-            aliases : dia.data.aliases
-          else dia.data
-
-    log jsyaml.dump dump
-
-  updateView:(dataUpdated = no)->
-
-    @scene.updateConnections()  if dataUpdated
+    @scene.updateConnections()  if updateData

     if @getHeight() > 50
       @setHeight @getProperHeight()