Make documentation section responsive

Make documentation section responsive

--- examples/docs-site/pages/docs/_template.cjsx
+++ examples/docs-site/pages/docs/_template.cjsx
@@ -1,6 +1,6 @@
 React = require 'react'
 Router = require 'react-router'
-{RouteHandler, Link, State} = Router
+{RouteHandler, Link, State, Navigation} = Router
 {Container, Grid, Breakpoint, Span} = require 'react-responsive-grid'
 Typography = require 'typography'
 sortBy = require 'lodash/collection/sortBy'
@@ -9,7 +9,12 @@
 {rhythm, fontSizeToMS} = typography

 module.exports = React.createClass
-  mixins: [State]
+  mixins: [State, Navigation]
+
+  handleTopicChange: (e) ->
+    # Transition to new topic that was selected.
+    @transitionTo e.target.value
+
   render: ->
     {rhythm} = @props.typography
     childPages = @props.childPages.map (child) ->
@@ -20,6 +25,9 @@
       }

     childPages = sortBy childPages, (child) -> child.order
+
+    docOptions = childPages.map (child) ->
+      <option value={child.path}>{child.title}</option>

     docPages = childPages.map (child) =>
       isActive = @isActive(child.path)
@@ -38,31 +46,46 @@
         </Link>

-

-

-        <ul
+    <Breakpoint minWidth=700>
+

+

-          {docPages}
-

+

+            {docPages}
+

+

+

+          <RouteHandler typography={typography} {...@props}/>
+

-      <div
-        style={{
-          padding: "0 #{rhythm(1)}"
-          paddingLeft: "calc(#{rhythm(8)} + #{rhythm(1)})"
-        }}
+    </Breakpoint>
+    <Breakpoint maxWidth=700>
+      Topics:
+      {' '}
+      <select
+        defaultValue={@props.state.path}
+        onChange={@handleTopicChange}
       >
-        <RouteHandler typography={typography} {...@props}/>
-

-

+        {docOptions}
+      </select>
+

+

+      <RouteHandler typography={typography} {...@props}/>
+    </Breakpoint>


Need the new and old doc so we can compare the old and new values to know if Rock cares about the changes that occured

--- _source/server/lib/observe.coffee
+++ _source/server/lib/observe.coffee
@@ -40,9 +40,11 @@
       createdDate: 0
       updatedDate: 0
       updatedBy: 0
-  ).observeChanges
+  ).observe

-    added: (id) ->
+    added: (newDoc) ->
+
+      id = newDoc._id

       if initializing
         return
@@ -57,11 +59,12 @@
       platformAddedBy = doc.updatedBy?.toUpperCase()

       for platform, handle of Apollos[collection].added
-        if platformAddedBy and (platformAddedBy isnt platform.toUpperCase())
+        if platformAddedBy isnt platform.toUpperCase()
           handle doc

-    changed: (id) ->
+    changed: (newDoc, oldDoc) ->

+      id = newDoc._id
       doc = Apollos[collection].findOne id
       if changeWasAlreadyHandled doc, collection
         Apollos.debug "#{collection} #{id} change already handled"
@@ -72,15 +75,15 @@
       platformChangedBy = doc.updatedBy?.toUpperCase()

       for platform, handle of Apollos[collection].changed
-        if platformChangedBy and (platformChangedBy isnt platform.toUpperCase())
-          handle doc
+        if platformChangedBy isnt platform.toUpperCase()
+          handle doc, oldDoc

-    removed: (id) ->
+    removed: (oldDoc) ->

       Apollos[collection].deleted or= {}

       for platform, handle of Apollos[collection].deleted
-        handle id
+        handle oldDoc

   initializing = false


fix extra margin when device doesn't support hand

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -366,7 +366,7 @@

 		paddingOffset = @_device?.paddingOffset or 0

-		paddingOffset = 36 if @selectedHand && @_orientation == 0
+		paddingOffset = 36 if @selectedHand and @_orientation == 0 and @_device.hands[@selectedHand]?

 		phoneScale = _.min([
 			(window.innerWidth  - ((@padding + paddingOffset) * 2)) / width,


Fix scrollX / scrollY bug

Fix scrollX / scrollY bug

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -151,11 +151,11 @@

 	@define "scrollX",
 		get: -> -@content.x
-		set: (value) -> @content.x = @_pointInConstraints({x:-value, y:0}).x
+		set: (value) -> @content.x = -@_pointInConstraints({x:value, y:0}).x

 	@define "scrollY",
 		get: -> -@content.y
-		set: (value) -> @content.y = @_pointInConstraints({x:0, y:-value}).y
+		set: (value) -> @content.y = -@_pointInConstraints({x:0, y:value}).y

 	@define "scrollPoint",
 		get: ->


META-59: When there is not github assignee return a resolved promise

--- src/utils.coffee
+++ src/utils.coffee
@@ -55,7 +55,7 @@
     return null

   @lookupUserWithGithub: (github) ->
-    return if not github
+    return Promise.resolve() if not github

     github.fetch().then (user) ->
       name = user.name or github.login


Remove dead code.

Remove dead code.

--- src/views.coffee
+++ src/views.coffee
@@ -93,18 +93,12 @@
             else if data.object_type == 'service'
                 model = new models.Service(data)
             @show_search_result(model, null)
-                # @show_details new models.Unit(data),
-                #     zoom: true
-                #     draw_marker: true

         show_search_result: (model, mode) ->
             if model == null
                 return
             if model.get('object_type') == 'unit'
                 app.commands.execute 'selectUnit', model
-                # @show_details model,
-                #     zoom: true
-                #     draw_marker: true
             else if model.get('object_type') == 'service'
                 app.commands.execute 'addService', model


set logger.notificationsTitle from config or default

in anticipation of publish of updated loggy package

--- src/watch.coffee
+++ src/watch.coffee
@@ -335,6 +335,7 @@
       return logger.error error if error?
       {config, watcher, fileList, compilers, linters, compile, reload} = result
       logger.notifications = config.notifications
+      logger.notificationsTitle = config.notificationsTitle or 'Brunch'
       bindWatcherEvents config, fileList, compilers, linters, watcher, reload, @_startCompilation
       fileList.on 'ready', => compile @_endCompilation()
       @config = config


fix(thread-list-store): Preserve selection through moveThreads

Summary: Small change that resets focus and keyboard selection after moving threads

Test Plan: Run existing tests, plan to overhaul this whole class so no additioanl tests atm

Reviewers: dillon, evan

Reviewed By: evan

Maniphest Tasks: T3382

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

--- internal_packages/thread-list/lib/thread-list-store.coffee
+++ internal_packages/thread-list/lib/thread-list-store.coffee
@@ -37,8 +37,6 @@
     @listenTo AccountStore, @_onAccountChanged
     @listenTo FocusedMailViewStore, @_onMailViewChanged

-    atom.config.observe 'core.workspace.mode', => @_autofocusForLayoutMode()
-
     # We can't create a @view on construction because the CategoryStore
     # has hot yet been populated from the database with the list of
     # categories and their corresponding ids. Once that is ready, the
@@ -58,7 +56,6 @@

     @_viewUnlisten = view.listen ->
       @trigger(@)
-      @_autofocusForLayoutMode()
     ,@

     @trigger(@)
@@ -86,6 +83,14 @@
           for message in messages
             messagesByThread[message.threadId].push message
           messagesByThread
+
+      if WorkspaceStore.layoutMode() is 'split'
+          # Set up a one-time listener to focus an item in the new view
+        unlisten = view.listen ->
+          if view.loaded()
+            Actions.setFocus(collection: 'thread', item: view.get(0))
+            unlisten()
+
       @setView(view)

     Actions.setFocus(collection: 'thread', item: null)
@@ -156,27 +161,41 @@
     task = ArchiveThreadHelper.getArchiveTask([focused])
     @_moveAndShiftBy(offset, task)

-  _onMoveThread: (thread, task) ->
-    @_moveAndShiftBy('auto', task)
-
-  _onMoveThreads: (threads, task) ->
-    selectedThreadIds = threads.map (thread) -> thread.id
-    focusedId = FocusedContentStore.focusedId('thread')
-    keyboardId = FocusedContentStore.keyboardCursorId('thread')
-
-    TaskQueueStatusStore.waitForPerformLocal(task).then =>
-      if focusedId in selectedThreadIds
-        Actions.setFocus(collection: 'thread', item: null)
-      if keyboardId in selectedThreadIds
-        Actions.setCursorPosition(collection: 'thread', item: null)
-
-    Actions.queueTask(task)
-    @_view.selection.clear()
-
   _onArchiveSelection: ->
     selectedThreads = @_view.selection.items()
     task = ArchiveThreadHelper.getArchiveTask(selectedThreads)
     @_onMoveThreads(selectedThreads, task)
+
+  _onMoveThread: (thread, task) ->
+    @_moveAndShiftBy('auto', task)
+
+  _onMoveThreads: (threads, task) ->
+    threadIds = threads.map (thread) -> thread.id
+    focusedId = FocusedContentStore.focusedId('thread')
+    keyboardId = FocusedContentStore.keyboardCursorId('thread')
+
+    if focusedId in threadIds
+      changeFocused = true
+    if keyboardId in threadIds
+      changeKeyboardCursor = true
+
+    if changeFocused or changeKeyboardCursor
+      newFocusIndex = Number.MAX_VALUE
+      for thread in threads
+        newFocusIndex = Math.min(newFocusIndex, @_view.indexOfId(thread.id))
+
+    TaskQueueStatusStore.waitForPerformLocal(task).then =>
+      layoutMode = WorkspaceStore.layoutMode()
+      if changeFocused
+        item = @_view.get(newFocusIndex)
+        Actions.setFocus(collection: 'thread', item: item)
+      if changeKeyboardCursor
+        item = @_view.get(newFocusIndex)
+        Actions.setCursorPosition(collection: 'thread', item: item)
+        Actions.setFocus(collection: 'thread', item: item) if layoutMode is 'split'
+
+    Actions.queueTask(task)
+    @_view.selection.clear()

   _moveAndShiftBy: (offset, task) ->
     layoutMode = WorkspaceStore.layoutMode()
@@ -196,7 +215,7 @@
       else
         offset = 1

-    index = Math.min(Math.max(index + offset, 0), @_view.count() - 1)
+    index = Math.min(Math.max(index + offset, 0), @_view.count() - 2)
     nextKeyboard = nextFocus = @_view.get(index)

     # Remove the current thread from selection
@@ -214,13 +233,4 @@
       Actions.setCursorPosition(collection: 'thread', item: nextKeyboard)
     Actions.queueTask(task)

-  _autofocusForLayoutMode: ->
-    return unless @_view
-    layoutMode = WorkspaceStore.layoutMode()
-    focused = FocusedContentStore.focused('thread')
-    if layoutMode is 'split' and not focused and @_view.selection.count() is 0
-      item = @_view.get(0)
-      _.defer =>
-        Actions.setFocus({collection: 'thread', item: item})
-
 module.exports = new ThreadListStore()


Remove PARANOID flag.

Remove PARANOID flag.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -20,11 +20,6 @@
 NO      = -> no
 THIS    = -> this
 NEGATE  = -> @negated = not @negated; this
-
-# If PARANOID is true, then certain extra internal consistency checks are done during the compile.
-# This is used for checks that would slow down the compiler. This is really only useful if you're
-# doing compiler work.
-PARANOID = false

 #### CodeFragment

@@ -43,27 +38,7 @@

 # Convert an array of CodeFragments into a string.
 fragmentsToText = (fragments) ->
-  checkFragments fragments
   (fragment.code for fragment in fragments).join('')
-
-# Validates that `fragments` is an array of CodeFragment objects.
-# If PARANOID is false, then this does nothing.
-checkFragments = (fragments, node=null) ->
-  if not PARANOID then return fragments
-
-  nodeName = if node then " from #{node.constructor.name}" else ""
-  if not fragments
-    throw new Error("Fragments is null#{nodeName}: #{fragments}\n" )
-  if fragments instanceof CodeFragment
-    throw new Error("Expected array of fragments but found fragment#{nodeName}: #{fragments}\n" )
-  for fragment, i in fragments
-    if not (fragment instanceof CodeFragment)
-      # This is not a fragment.
-      inspected = (require 'util').inspect fragments
-      nodeStr = if node then "node: #{node.toString()}\n" else ""
-      throw new Error("Expected fragment: #{i} of #{fragments.length}#{nodeName}: #{fragment}\nFragments: #{inspected}\n#{nodeStr}")
-
-  return fragments

 #### Base

@@ -93,11 +68,9 @@
     node     = @unfoldSoak(o) or this
     node.tab = o.indent
     if o.level is LEVEL_TOP or not node.isStatement(o)
-      fragments = node.compileNode o
-    else
-      fragments = node.compileClosure o
-    checkFragments fragments, node
-    fragments
+      node.compileNode o
+    else
+      node.compileClosure o

   # Statements converted into expressions via closure-wrapping share a scope
   # object with their parent closure, to preserve the expected lexical scope.
@@ -226,7 +199,6 @@
   joinFragmentArrays: (fragmentsList, joinStr) ->
     answer = []
     for fragments,i in fragmentsList
-      checkFragments fragments, @
       if i then answer.push @makeCode joinStr
       answer = answer.concat fragments
     answer


Comment out untested methods

Comment out untested methods

--- src/world.coffee
+++ src/world.coffee
@@ -34,7 +34,17 @@
     if isFunction(value)
       return value(this,{})
     value
+###
+  reset: (key, delta) ->
+    @put(key, @get(key) + delta)

+  call: (key, args) ->
+    closure = @get_raw(key)
+    closure(this, args)
+
+  toString: ->
+    "World:#{@label}"
+###
   world_from_value: (value) ->
     label = "#{value}"
     world = new World(@, label)
@@ -64,19 +74,6 @@
       result.push callback(value)
     result

-  reset: (key, delta) ->
-    @put(key, @get(key) + delta)
-
-  call: (key, args) ->
-    closure = @get_raw(key)
-    closure(this, args)
-
-  bind: (exp) ->
-    @rx.bind exp
-
-  toString: ->
-    "World:#{@label}"
-
 exports.world = (up, rx, doc) ->
   root = new World(up, "root", rx)
   root.put(RX, rx)


rtmp: Add link to spec

rtmp: Add link to spec

--- rtmp.coffee
+++ rtmp.coffee
@@ -1,4 +1,7 @@
 # RTMP server
+#
+# RTMP specification is available at:
+# http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf

 net           = require 'net'
 crypto        = require 'crypto'


:penguin: Use ctrl-g for linux. Join selectors with the same keybinding.

--- keymaps/go-to-line.cson
+++ keymaps/go-to-line.cson
@@ -1,11 +1,5 @@
-'.platform-darwin':
+'.platform-darwin, .platform-win32, .platform-linux':
   'ctrl-g': 'go-to-line:toggle'
-
-'.platform-win32':
-  'ctrl-g': 'go-to-line:toggle'
-
-'.platform-linux':
-  'ctrl-i': 'go-to-line:toggle'

 '.go-to-line .mini.editor input':
   'enter': 'core:confirm',


Add binding for moving left and right with tab

--- keymaps/table-edit.cson
+++ keymaps/table-edit.cson
@@ -8,6 +8,10 @@
 # For more detailed documentation see
 # https://atom.io/docs/latest/advanced/keymaps

+'.table-edit':
+  'tab': 'table-edit:move-right'
+  'shift-tab': 'table-edit:move-left'
+
 '.table-edit atom-text-editor':
   'enter': 'core:confirm'
   'ctrl-enter': 'editor:newline'


Fixed an error which prevented the script from running

--- util/migrateBoxToServer.coffee
+++ util/migrateBoxToServer.coffee
@@ -122,5 +122,5 @@
     migratePasswdEntry box, user, ->
       transferBoxData box, user, ->
         transferCrontab box, user, ->
-          transferSSHKeys box, user ->
+          transferSSHKeys box, user, ->
             process.exit()


Add markers for embedded objects.

Add markers for embedded objects.

This partially fixes issue #92.

In click-to-play mode, <embed> objects can be activated via context menu: Alt, c.

--- extension/packages/marker.coffee
+++ extension/packages/marker.coffee
@@ -26,6 +26,8 @@
   "button"
   "select"
   "input[not(@type='hidden' or @disabled or @readonly)]"
+  "embed"
+  "object"
 ]


Unit tests fixed

Unit tests fixed

--- test.spec.coffee
+++ test.spec.coffee
@@ -1,5 +1,4 @@
-[utils] = ['utils'].map require
-
+utils = require 'utils'
 List = require './index'

 describe 'ctor', ->
@@ -11,13 +10,13 @@
 describe 'length', ->

 	it 'returns amount of items', ->
-		list = List 1, 2
+		list = List [1, 2]
 		expect(list.length).toBe 2

 describe 'get()', ->

 	it 'return an item at given position', ->
-		list = List 1, 2
+		list = List [1, 2]
 		expect(list.get 0).toBe 1
 		expect(list.get 1).toBe 2
 		expect(list.get 2).toBe undefined
@@ -25,14 +24,14 @@
 describe 'set()', ->

 	it 'change existed item value', ->
-		list = List 1
+		list = List [1]
 		list.set 0, 2
 		expect(list.get 0).toBe 2

 describe 'items()', ->

 	it 'return array of items', ->
-		list = List 1, 2
+		list = List [1, 2]
 		expect(list.items()).toEqual {0: 1, 1: 2}

 	it 'return array of items for ctor-array syntax', ->
@@ -42,14 +41,14 @@
 describe 'append()', ->

 	it 'add an item on the end', ->
-		list = List 1
+		list = List [1]
 		list.append 2
 		expect(list.items()).toEqual 0: 1, 1: 2

 describe 'insert()', ->

 	it 'insert an item at given position', ->
-		list = List 1, 2
+		list = List [1, 2]
 		list.insert 1, 0
 		list.insert 0, 2
 		expect(list.items()).toEqual 0: 2, 1: 1, 2: 0, 3: 2
@@ -57,19 +56,19 @@
 describe 'remove()', ->

 	it 'remove the first element equal given value', ->
-		list = List 0, 1, 1, 2
+		list = List [0, 1, 1, 2]
 		list.remove 1
 		expect(list.items()).toEqual {0: 0, 1: 1, 2: 2}

 describe 'pop()', ->

 	it 'remove the last item', ->
-		list = List 1, 2
+		list = List [1, 2]
 		list.pop()
 		expect(list.items()).toEqual 0: 1

 	it 'remove the item at given position', ->
-		list = List 1, 2, 3
+		list = List [1, 2, 3]
 		list.pop 1
 		expect(list.items()).toEqual {0: 1, 1: 3}
 		list.pop 0
@@ -78,25 +77,25 @@
 describe 'clear()', ->

 	it 'remove all items', ->
-		list = List 1, 2
+		list = List [1, 2]
 		list.clear()
 		expect(list.items()).toEqual {}

 describe 'index()', ->

 	it 'return the index of the first item equal given value', ->
-		list = List 1, 2, 2
+		list = List [1, 2, 2]
 		expect(list.index 2).toBe 1
 		expect(list.index 3).toBe -1

 describe 'has()', ->

 	it 'return true if given value exists in a list', ->
-		list = List 1, 2, 2
+		list = List [1, 2, 2]
 		expect(list.has 2).toBe true
 		expect(list.has 3).toBe false

-describe 'onChanged signal', ->
+describe 'onChange signal', ->

 	list = listener = null
 	ok = false
@@ -113,18 +112,18 @@

 	afterEach ->
 		expect(ok).toBeTruthy()
-		list.onChanged.disconnect listener
+		list.onChange.disconnect listener

 	it 'works with set()', ->
-		list = List 1, 2, 3
-		list.onChanged listener
+		list = List [1, 2, 3]
+		list.onChange listener

 		list.set 1, 'a'

 		expect(args).toEqual [[2, 1]]
 		expect(copy).toEqual [1, 'a', 3]

-describe 'onInserted signal', ->
+describe 'onInsert signal', ->

 	list = listener = null
 	ok = false
@@ -141,11 +140,11 @@

 	afterEach ->
 		expect(ok).toBeTruthy()
-		list.onInserted.disconnect listener
+		list.onInsert.disconnect listener

 	it 'works with append()', ->
-		list = List 1, 2
-		list.onInserted listener
+		list = List [1, 2]
+		list.onInsert listener

 		list.append 'a'

@@ -153,15 +152,15 @@
 		expect(copy).toEqual [1, 2, 'a']

 	it 'works with insert()', ->
-		list = List 1, 2
-		list.onInserted listener
+		list = List [1, 2]
+		list.onInsert listener

 		list.insert 1, 'a'

 		expect(args).toEqual [['a', 1]]
 		expect(copy).toEqual [1, 'a', 2]

-describe 'onPopped signal', ->
+describe 'onPop signal', ->

 	list = listener = null
 	ok = false
@@ -178,11 +177,10 @@

 	afterEach ->
 		expect(ok).toBeTruthy()
-		list.onPopped.disconnect listener

 	it 'works with pop()', ->
-		list = List 1, 2
-		list.onPopped listener
+		list = List [1, 2]
+		list.onPop listener

 		list.pop()

@@ -190,8 +188,8 @@
 		expect(copy).toEqual [1]

 	it 'works with pop(i)', ->
-		list = List 1, 2
-		list.onPopped listener
+		list = List [1, 2]
+		list.onPop listener

 		list.pop 0

@@ -199,8 +197,8 @@
 		expect(copy).toEqual [2]

 	it 'works with remove()', ->
-		list = List 1, 2
-		list.onPopped listener
+		list = List [1, 2]
+		list.onPop listener

 		list.remove 2

@@ -208,8 +206,8 @@
 		expect(copy).toEqual [1]

 	it 'works with clear()', ->
-		list = List 1, 2
-		list.onPopped listener
+		list = List [1, 2]
+		list.onPop listener

 		list.clear()


Fix a logical error

Fix a logical error

clear additional data when receiver setting data

Signed-off-by: Manson Li <f2a598d7717cc6757bef28b84a9ab02e5efc6048@matchstick.tv>

--- lib/application/Application.coffee
+++ lib/application/Application.coffee
@@ -40,6 +40,7 @@
     setAdditionalDataWithJson: (data) ->
         if not data
             return
+        @additionalData = ""
         @additionalData += "<additionalData>\n"
         @additionalData += jsontoxml(data)
         @additionalData += "\n"
@@ -48,6 +49,7 @@
     setAdditionalData: (data) ->
         if not data
             return
+        @additionalData = ""
         @additionalData += "<additionalData>\n"
         @additionalData += data
         @additionalData += "\n"


create empty editor if there are no paths given

--- lib/atom-ternjs-config-view.coffee
+++ lib/atom-ternjs-config-view.coffee
@@ -33,6 +33,8 @@
     wrapper.appendChild(doc)
     for path in obj
       wrapper.appendChild(@createInputWrapper(path, section))
+    if obj.length is 0
+      wrapper.appendChild(@createInputWrapper(null, section))
     wrapper

   createInputWrapper: (path, section) ->


Use ComponentLoader to get instance

Use ComponentLoader to get instance

--- spec/ReadFile.coffee
+++ spec/ReadFile.coffee
@@ -1,23 +1,32 @@
 noflo = require 'noflo'
 unless noflo.isBrowser()
-  chai = require 'chai' unless chai
-  ReadFile = require '../components/ReadFile.coffee'
+  chai = require 'chai'
+  path = require 'path'
+  baseDir = path.resolve __dirname, '../'
 else
-  ReadFile = require 'noflo-browserfile/components/ReadFile.js'
+  baseDir = 'noflo-browserfile'

 describe 'ReadFile component', ->
   c = null
   ins = null
   out = null
+  before (done) ->
+    @timeout 4000
+    loader = new noflo.ComponentLoader baseDir
+    loader.load 'browserfile/ReadFile', (err, instance) ->
+      return done err if err
+      c = instance
+      ins = noflo.internalSocket.createSocket()
+      c.inPorts.file.attach ins
+      done()
   beforeEach ->
-    c = ReadFile.getComponent()
-    ins = noflo.internalSocket.createSocket()
     out = noflo.internalSocket.createSocket()
-    c.inPorts.in.attach ins
     c.outPorts.out.attach out
+  afterEach ->
+    c.outPorts.out.detach out

   describe 'when instantiated', ->
-    it 'should have an input port', ->
-      chai.expect(c.inPorts.in).to.be.an 'object'
+    it 'should have an file port', ->
+      chai.expect(c.inPorts.file).to.be.an 'object'
     it 'should have an output port', ->
       chai.expect(c.outPorts.out).to.be.an 'object'


removing unnecessary code - we don't want to change the color

--- plugins/image.coffee
+++ plugins/image.coffee
@@ -354,7 +354,6 @@
                     delay = ->
                         window.timeoutTrash = clearTimeout(window.timeoutTrash)  if window.timeoutTrash
                         # todo: handle with a class
-                        $(ui.helper).css "border", "3px solid white"
                         $(ui.helper).find('.trashcan').remove()

                         editable.append overlay.big


first GCP 0.4 proposal

first GCP 0.4 proposal

--- spec/protocol.coffee
+++ spec/protocol.coffee
@@ -2,10 +2,11 @@
 expect = require('chai').expect
 sinon = require 'sinon'
 io = require 'socket.io-client'
-GameInstanceID = ""
+BeginOfTest = 0
+MatchId = ""

 #   GCP PROTOCOL SPECIFICATION   *StudioLounge Multiplayer Game*
-#                         v0.3                           (final)
+#                         v0.4                           (draft)
 #   (in)formal description of digital message formats and rules,
 #   for exchanging of theese messages between computing systems,
 #   defines syntax, semantics, synchronization of communication;
@@ -19,7 +20,12 @@
   it "should make developers smile", -> expect(":-)").to.be.ok

+
+
+
   describe "LOGIN", ->
+
+    BeginOfTest = Date.now

     it "should allow any player to login with any name", (done) ->
       (@anyplayer = server.gcp()).on "connect", () ->
@@ -35,27 +41,23 @@
           expect(msg).to.equal "Try again later"
           done()

-    it "should notify everyone about players that log in", (done) ->
-      (@anotherplayer = server.gcp()).on "connect", () -> @emit 'login', "I am Ananda"
-      @anyplayer.on 'login', (msg) => this.happens()
-      (@lukas = server.gcp()).on "connect", () -> @emit 'login', "I am Lukas"
-      @troll.on 'login', (msg) -> expect("this").to.not.be.ok
-      setTimeout ( () =>
-        expect(@happens.calledTwice).to.be.ok
-        done() ), 21 # ms responsiveness !!!
+

   describe "CHATTING", ->

-    it "must allow players to chat in the public chatroom", (done) ->
+    (@anotherplayer = server.gcp()).on "connect", () -> @emit 'login', "I am Ananda"
+    (@lukas = server.gcp()).on "connect", () -> @emit 'login', "I am Lukas"
+
+    it "should allow players to chat in the public chatroom", (done) ->
       @lukas.send "happy again :-)"
       @anyplayer.on 'message', (msg) ->
         expect(msg).to.equal "Lukas:   happy again :-)"
       @anotherplayer.on 'message', (msg) ->
         expect(msg).to.equal "Lukas:   happy again :-)"
         done()
-
+
     it "must not let players chat who are not logged in", (done) ->
       server.gcp().on 'connect', () ->
         @send "I did not log in but I chat anayway"
@@ -64,119 +66,86 @@

-  describe "HOSTING GAMES", () ->
+
+
+  describe "MATCHING (players with instances of games)", () ->

     it "should allow any logged in player to host games", (done) ->
       @anyplayer.emit 'host', { game: "my.game", min: 2, max: 3}
-      @lukas.on 'host', (msg) ->
-        expect(msg.game).to.match /my\.game-.+/
-        expect(msg.host).to.equal "Anyname"
-        expect(msg.min).to.equal 2
-        expect(msg.max).to.equal 3
-      @anyplayer.on 'host', (msg) ->   # host gets the msg too
-        expect(msg.game).to.match /my\.game-.+/
-        expect(msg.host).to.equal "Anyname"
-        expect(msg.min).to.equal 2
-        expect(msg.max).to.equal 3
-        GameInstanceID = msg.game
+      @lukas.on 'host', (match) ->
+        expect(match.host).to.equal "Anyname"
+        expect(match.min).to.equal 2
+        expect(match.max).to.equal 3
+        MatchId = match.id
         done()

     it "should deny anyone else to host a new game", (done) ->
-      @troll.emit 'host', { game: "a.random.game"}
+      @troll.emit 'host', { game: "a.random.game", min: 43, max: 55 }
       @anotherplayer.on 'host', (msg) -> expect("that").to.be.not.ok
       setTimeout done, 58

-    it "should inform about everything upon request", (done) ->
-      @lukas.emit 'state'
-      @lukas.on 'state', (msg) ->
-        expect(msg).to.deep.equal( {
-          players: [
-            {
-              name:  "Anyname"
-              game: { # hosted
-                id: GameInstanceID
-                min:     2
-                max:     3
-                joined:  1
-              }
-            },
-            { name: "Lukas" },
-            { name: "Ananda"  }
-          ],
-          chat: [ { player: "Lukas", msg: "happy again :-)" } ],
-          games_played: 1,
-          msges_sent: 1
-        })
+    it "should broadcast that another player joins", (done) ->
+      @anotherplayer.on 'join', (msg) =>
+      @anyplayer.on 'join', (msg) =>
+        expect(msg.player).to.equal "Ananda"
+        expect(msg.id).to.equal MatchId
         done()

-    it "should tell all players when another player joins", (done) ->
-      @anotherplayer.emit 'join', { game: GameInstanceID }
-      @anotherplayer.on 'join', (msg) =>
-        this.happens()
-        expect(msg.game).to.equal GameInstanceID
-      @anyplayer.on 'join', (msg) =>
-        this.happens()
-        expect(msg.game).to.equal GameInstanceID
-      setTimeout ( () =>
-        expect(@happens.calledTwice).to.be.ok
-        done() ), 42 # ms responsiveness !!!
+
+

-    it "should allow a server to unhost games (e.g. when full)" , (done) ->
-      @lukas.emit 'join', { game: GameInstanceID }
-      @anyplayer.on "unhost", (msg) =>
-        expect(msg.host).to.equal "Anyname"
-        expect(msg.game).to.equal GameInstanceID
-      @anotherplayer.on "unhost", (msg) =>
-        expect(msg.host).to.equal "Anyname"
-        expect(msg.game).to.equal GameInstanceID
-        @anotherplayer.emit 'state'
-        @anotherplayer.on 'state', (msg) -> # not hosted anymore because it is already 'full'
-          expect(msg.players[0].game).to.equal undefined
-          done()

-    it "may inform about active games (for debugging)", (done) ->
-      @lukas.emit 'games'
-      @lukas.on 'games', (msg) ->
-        expect(msg).to.deep.equal( [
-          {
-            game: GameInstanceID,
-            players: ["Anyname", "Ananda", "Lukas"]
-          }
-        ] )
-        done()
+  describe "HISTORY (catch up)", ->
+
+    it "should tell (the diff) what happened in some meanwhile", (done) ->
+      @anyplayer.emit 'history', { since: Date.now }
+      @anyplayer.on 'history', (diffs) ->
+        expect(diffs).to.deep.equal( {
+          players: [],
+          games: [],
+          match: [],
+          chat: []
+          stats: { player_online: 3, games_played: 1}
+        } # not much happened since now :-)
+
+      @anyplayer.emit 'history', { since: BeginOfTest }
+      @anyplayer.on 'history', (diffs) ->
+        expect(diffs).to.deep.equal( {
+          players: [],
+          games: [],
+          match: [ {
+            id: MatchID
+            min: 2
+            max: 3
+            host: "Anyname"
+            players: ["Ananda"]
+            } ],
+          chat: ["Lukas:   happy again :-)"]
+          stats: { player_online: 3, games_played: 1}
+        }
+
+

   describe "CUSTOM MESSAGING", () ->

-    it "should send custom game move messages to everyone in the game", (done) ->
-      @anyplayer.emit 'move', {abc: "my", data: 42}
-      @lukas.on 'move', (msg) => this.happens()
+    it "should send custom game messages to everyone in the game", (done) ->
+      @anyplayer.emit 'move', {foo: "my", data: 42}
       @anotherplayer.on 'move', (msg) =>
-        expect(msg.abc).to.equal "my"
-        expect(msg.data).to.equal 42
-        this.happens()
-      setTimeout ( () =>
-        expect(@happens.calledTwice).to.be.ok
-        done() ), 42 # ms
+        expect(msg).to.deep.equal( {
+          foo: "my"
+          data: 42
+        }

-  describe "LOGOUT", () ->
-
-    it "should notify about players that log out", (done) ->
-      @anyplayer.emit 'logout'
-      @anotherplayer.on 'logout', (msg) ->
-        expect(msg).to.equal "Anyname"
-        done()

 # it "should allow players to send private messages", (done) ->
-
 # it "should bring multiple game players together ", (done) ->
-
 # it "should empower players to plant happy* trees", (done) ->

   after (test) -> server.stop test


Remove zooAPI from Window (#5714)

Remove zooAPI from Window (#5714)

--- app/main.cjsx
+++ app/main.cjsx
@@ -57,7 +57,4 @@
 # Are we connected to the latest back end?
 require('./lib/log-deployed-commit')()

-# Just for console access:
-window?.zooAPI = require 'panoptes-client/lib/api-client'
-window?.talkAPI = require 'panoptes-client/lib/talk-client'
 require('./lib/split-config')


Update administrationview.coffee

Update administrationview.coffee

--- client/Dashboard/views/administrationview.coffee
+++ client/Dashboard/views/administrationview.coffee
@@ -27,7 +27,7 @@
                   new KDNotificationView {title : "Select a user first"}
           fields            :
             Username        :
-              label         : "Dear User"
+              label         : "Search for user:"
               type          : "hidden"
               nextElement   :
                 userWrapper :


Cambio sin importancia

Cambio sin importancia

--- source/organisms/menu.coffee
+++ source/organisms/menu.coffee
@@ -5,7 +5,7 @@

   # -- Children bubble events --------------------------------------------------
   onButtonTouch: (event, dispatcher, hierarchy...) ->
-    # Your code....................Jesus
+    # Your code...

   onInicioPressed: (event, dispatcher, hierarchy...) ->
   	Atoms.Url.path "inicio/personas"


Jesus cambia esto

Jesus cambia esto

--- source/organisms/menu.coffee
+++ source/organisms/menu.coffee
@@ -5,7 +5,7 @@

   # -- Children bubble events --------------------------------------------------
   onButtonTouch: (event, dispatcher, hierarchy...) ->
-    # Your code...
+    # Your code....................Jesus

   onInicioPressed: (event, dispatcher, hierarchy...) ->
   	Atoms.Url.path "inicio/personas"


Test:Logs: skip papertrail tests for now

--- servers/lib/server/handlers/api/logs.test.coffee
+++ servers/lib/server/handlers/api/logs.test.coffee
@@ -147,7 +147,7 @@
       done()

-  it 'should send HTTP 200 if provided token and request domain is valid', (done) ->
+  it.skip 'should send HTTP 200 if provided token and request domain is valid', (done) ->

     { client, apiToken, group } = TESTUSERS.team

@@ -163,7 +163,7 @@
       done()

-  it 'should send HTTP 200 if token not provided but there is a valid session', (done) ->
+  it.skip 'should send HTTP 200 if token not provided but there is a valid session', (done) ->

     { client } = TESTUSERS.admin


Remove direct aesCryptoService call from sesarchCredentialService

--- js/src/auth/SearchCredentialService.coffee
+++ js/src/auth/SearchCredentialService.coffee
@@ -8,7 +8,6 @@
   'kryptnostic.crypto-key-storage-api'
   'kryptnostic.credential-loader'
   'kryptnostic.crypto-service-loader'
-  'kryptnostic.aes-crypto-service'
   'kryptnostic.block-ciphertext'
 ], (require) ->


Fancy button styles

Fancy button styles

--- client.coffee
+++ client.coffee
@@ -47,11 +47,15 @@
 		ranking = Db.personal.ref('rounds', round_no)
 		my_vote = Db.shared.get('votes', Plugin.userId()) || null

-		Ui.bigButton (if my_vote == 1 then "Hello" else "") + 'I have!', ->
-			Server.call 'registerVote', Plugin.userId(), 1
+		Ui.bigButton ->
+			Dom.style marginTop: '14px', marginLeft: '0px', height: '90px', backgroundColor: (if my_vote == 2 then "#aaa" else ""), textAlign: 'center', fontSize: '250%', paddingTop: '50px'
+			Dom.text 'I have!'
+		, -> Server.call 'registerVote', Plugin.userId(), 1

-		Ui.bigButton (if my_vote == 2 then "Hello" else "") + 'I have not!', ->
-			Server.call 'registerVote', Plugin.userId(), 2
+		Ui.bigButton ->
+			Dom.style marginTop: '14px', marginRight: '0px', height: '90px', backgroundColor: (if my_vote == 1 then "#aaa" else ""), textAlign: 'center', fontSize: '250%', paddingTop: '50px'
+			Dom.text 'I have not!'
+		, -> Server.call 'registerVote', Plugin.userId(), 2

 renderRoundList = ->
 	renderRoundItem = (round) ->


Added more details to APIOffline error

Added more details to APIOffline error

--- web/coffee/dependencies.coffee
+++ web/coffee/dependencies.coffee
@@ -4,7 +4,7 @@

   $.ajax {url: url, type: type, data: data, cache: false}
   .fail (jqXHR, text) ->
-    ga('send', 'event', 'Error', 'APIOffline')
+    ga('send', 'event', 'Error', 'APIOffline', url)
     $.notify "The picoCTF server is currently down. We will work to fix this error right away.", "error"

 @redirectIfNotLoggedIn = ->


Keed the cursor position on visual mode

Keed the cursor position on visual mode

ScrollKeepingCursor subclasses was not keeping the correct cursor
position

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -387,12 +387,14 @@
     super(@editor, @vimState)

   select: (count, options) ->
-    @scrollScreen(count)
+    final_destination = @scrollScreen(count)
     super(count, options)
+    @editor.setScrollTop(final_destination)

   execute: (count) ->
-    @scrollScreen(count)
+    final_destination = @scrollScreen(count)
     super(count)
+    @editor.setScrollTop(final_destination)

   moveCursor: (cursor, count=1) ->
     cursor.setScreenPosition([@getDestinationRow(count), 0])
@@ -403,12 +405,15 @@

   scrollScreen: (count = 1) ->
     @previousFirstScreenRow = @editor.getFirstVisibleScreenRow()
-    @editor.setScrollTop(@scrollDestination(count))
+    destination = @scrollDestination(count)
+    @editor.setScrollTop(destination)
     @currentFirstScreenRow = @editor.getFirstVisibleScreenRow()
+    destination

 class ScrollHalfUpKeepCursor extends ScrollKeepingCursor
   scrollDestination: (count) ->
-    @editor.getScrollTop() - (count * Math.floor(@editor.getHeight() / 2))
+    half = (Math.floor(@editor.getRowsPerPage() / 2) * @editor.getLineHeightInPixels())
+    @editor.getScrollTop() - count * half

 class ScrollFullUpKeepCursor extends ScrollKeepingCursor
   scrollDestination: (count) ->
@@ -416,7 +421,8 @@

 class ScrollHalfDownKeepCursor extends ScrollKeepingCursor
   scrollDestination: (count) ->
-    @editor.getScrollTop() + (count * Math.floor(@editor.getHeight() / 2))
+    half = (Math.floor(@editor.getRowsPerPage() / 2) * @editor.getLineHeightInPixels())
+    @editor.getScrollTop() + count * half

 class ScrollFullDownKeepCursor extends ScrollKeepingCursor
   scrollDestination: (count) ->


fix MoveToPreviousParagraph inclusivity

fix MoveToPreviousParagraph inclusivity

--- lib/motions/general-motions.coffee
+++ lib/motions/general-motions.coffee
@@ -267,6 +267,8 @@
       cursor.moveToBeginningOfNextParagraph()

 class MoveToPreviousParagraph extends Motion
+  operatesInclusively: false
+
   moveCursor: (cursor, count=1) ->
     _.times count, ->
       cursor.moveToBeginningOfPreviousParagraph()


Removed unneeded @

Removed unneeded @

--- core/app/backbone/views/top_channels_view.coffee
+++ core/app/backbone/views/top_channels_view.coffee
@@ -31,7 +31,7 @@
     showMoreLessButtons: '.js-show-more-less-buttons'

   initialize: ->
-    @listenTo @collection, "add remove reset", @toggleShowMore, @
+    @listenTo @collection, "add remove reset", @toggleShowMore

   templateHelpers: =>
     topics: Factlink.Global.t.topics.capitalize()


Uncomment hubot examples

Uncomment hubot examples

--- scripts/example.coffee
+++ scripts/example.coffee
@@ -10,97 +10,97 @@

 module.exports = (robot) ->

-  # robot.hear /badger/i, (res) ->
-  #   res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"
-  #
-  # robot.respond /open the (.*) doors/i, (res) ->
-  #   doorType = res.match[1]
-  #   if doorType is "pod bay"
-  #     res.reply "I'm afraid I can't let you do that."
-  #   else
-  #     res.reply "Opening #{doorType} doors"
-  #
-  # robot.hear /I like pie/i, (res) ->
-  #   res.emote "makes a freshly baked pie"
-  #
-  # lulz = ['lol', 'rofl', 'lmao']
-  #
-  # robot.respond /lulz/i, (res) ->
-  #   res.send res.random lulz
-  #
-  # robot.topic (res) ->
-  #   res.send "#{res.message.text}? That's a Paddlin'"
-  #
-  #
-  # enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
-  # leaveReplies = ['Are you still there?', 'Target lost', 'Searching']
-  #
-  # robot.enter (res) ->
-  #   res.send res.random enterReplies
-  # robot.leave (res) ->
-  #   res.send res.random leaveReplies
-  #
-  # answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING
-  #
-  # robot.respond /what is the answer to the ultimate question of life/, (res) ->
-  #   unless answer?
-  #     res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
-  #     return
-  #   res.send "#{answer}, but what is the question?"
-  #
-  # robot.respond /you are a little slow/, (res) ->
-  #   setTimeout () ->
-  #     res.send "Who you calling 'slow'?"
-  #   , 60 * 1000
-  #
-  # annoyIntervalId = null
-  #
-  # robot.respond /annoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #     return
-  #
-  #   res.send "Hey, want to hear the most annoying sound in the world?"
-  #   annoyIntervalId = setInterval () ->
-  #     res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
-  #   , 1000
-  #
-  # robot.respond /unannoy me/, (res) ->
-  #   if annoyIntervalId
-  #     res.send "GUYS, GUYS, GUYS!"
-  #     clearInterval(annoyIntervalId)
-  #     annoyIntervalId = null
-  #   else
-  #     res.send "Not annoying you right now, am I?"
-  #
-  #
-  # robot.router.post '/hubot/chatsecrets/:room', (req, res) ->
-  #   room   = req.params.room
-  #   data   = JSON.parse req.body.payload
-  #   secret = data.secret
-  #
-  #   robot.messageRoom room, "I have a secret: #{secret}"
-  #
-  #   res.send 'OK'
-  #
-  # robot.error (err, res) ->
-  #   robot.logger.error "DOES NOT COMPUTE"
-  #
-  #   if res?
-  #     res.reply "DOES NOT COMPUTE"
-  #
-  # robot.respond /have a soda/i, (res) ->
-  #   # Get number of sodas had (coerced to a number).
-  #   sodasHad = robot.brain.get('totalSodas') * 1 or 0
-  #
-  #   if sodasHad > 4
-  #     res.reply "I'm too fizzy.."
-  #
-  #   else
-  #     res.reply 'Sure!'
-  #
-  #     robot.brain.set 'totalSodas', sodasHad+1
-  #
-  # robot.respond /sleep it off/i, (res) ->
-  #   robot.brain.set 'totalSodas', 0
-  #   res.reply 'zzzzz'
+  robot.hear /badger/i, (res) ->
+    res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"
+
+  robot.respond /open the (.*) doors/i, (res) ->
+    doorType = res.match[1]
+    if doorType is "pod bay"
+      res.reply "I'm afraid I can't let you do that."
+    else
+      res.reply "Opening #{doorType} doors"
+
+  robot.hear /I like pie/i, (res) ->
+    res.emote "makes a freshly baked pie"
+
+  lulz = ['lol', 'rofl', 'lmao']
+
+  robot.respond /lulz/i, (res) ->
+    res.send res.random lulz
+
+  robot.topic (res) ->
+    res.send "#{res.message.text}? That's a Paddlin'"
+
+
+  enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you']
+  leaveReplies = ['Are you still there?', 'Target lost', 'Searching']
+
+  robot.enter (res) ->
+    res.send res.random enterReplies
+  robot.leave (res) ->
+    res.send res.random leaveReplies
+
+  answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING
+
+  robot.respond /what is the answer to the ultimate question of life/, (res) ->
+    unless answer?
+      res.send "Missing HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING in environment: please set and try again"
+      return
+    res.send "#{answer}, but what is the question?"
+
+  robot.respond /you are a little slow/, (res) ->
+    setTimeout () ->
+      res.send "Who you calling 'slow'?"
+    , 60 * 1000
+
+  annoyIntervalId = null
+
+  robot.respond /annoy me/, (res) ->
+    if annoyIntervalId
+      res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
+      return
+
+    res.send "Hey, want to hear the most annoying sound in the world?"
+    annoyIntervalId = setInterval () ->
+      res.send "AAAAAAAAAAAEEEEEEEEEEEEEEEEEEEEEEEEIIIIIIIIHHHHHHHHHH"
+    , 1000
+
+  robot.respond /unannoy me/, (res) ->
+    if annoyIntervalId
+      res.send "GUYS, GUYS, GUYS!"
+      clearInterval(annoyIntervalId)
+      annoyIntervalId = null
+    else
+      res.send "Not annoying you right now, am I?"
+
+
+  robot.router.post '/hubot/chatsecrets/:room', (req, res) ->
+    room   = req.params.room
+    data   = JSON.parse req.body.payload
+    secret = data.secret
+
+    robot.messageRoom room, "I have a secret: #{secret}"
+
+    res.send 'OK'
+
+  robot.error (err, res) ->
+    robot.logger.error "DOES NOT COMPUTE"
+
+    if res?
+      res.reply "DOES NOT COMPUTE"
+
+  robot.respond /have a soda/i, (res) ->
+    # Get number of sodas had (coerced to a number).
+    sodasHad = robot.brain.get('totalSodas') * 1 or 0
+
+    if sodasHad > 4
+      res.reply "I'm too fizzy.."
+
+    else
+      res.reply 'Sure!'
+
+      robot.brain.set 'totalSodas', sodasHad+1
+
+  robot.respond /sleep it off/i, (res) ->
+    robot.brain.set 'totalSodas', 0
+    res.reply 'zzzzz'


Remove jQuery from up.rails; Support new jquery-less rails-ujs adapater

--- lib/assets/javascripts/unpoly/rails.coffee
+++ lib/assets/javascripts/unpoly/rails.coffee
@@ -3,23 +3,22 @@
 ========================
 ###

-up.rails = (($) ->
+up.rails = do ->

   u = up.util
+  e = up.element

   isRails = ->
-    !!$.rails
+    !!(window.Rails || (window.jQuery?.rails))

   u.each ['method', 'confirm'], (feature) ->

     dataAttribute = "data-#{feature}"
     upAttribute = "up-#{feature}"

-    up.$macro "[#{dataAttribute}]", ($element) ->
-      if isRails() && up.link.isFollowable($element)
+    up.macro "[#{dataAttribute}]", (element) ->
+      if isRails() && up.link.isFollowable(element)
         replacement = {}
-        replacement[upAttribute] = $element.attr(dataAttribute)
-        u.setMissingAttrs($element, replacement)
-        $element.removeAttr(dataAttribute)
-
-)(jQuery)
+        replacement[upAttribute] = element.getAttribute(dataAttribute)
+        u.setMissingAttrs(element, replacement)
+        element.removeAttribute(dataAttribute)


Rails package works with modern versions of rails-ujs

--- lib/assets/javascripts/unpoly/rails.coffee
+++ lib/assets/javascripts/unpoly/rails.coffee
@@ -9,7 +9,9 @@
   e = up.element

   isRails = ->
-    !!(window.Rails || (window.jQuery?.rails))
+    window._rails_loaded || # current rails-ujs integrated with Rails 5.2+
+      window.Rails ||       # legacy rails/rails-ujs gem
+      window.jQuery?.rails  # legacy rails/jquery-ujs gem

   u.each ['method', 'confirm'], (feature) ->


Replace OK by '--'

Replace OK by '--'

--- lib/commands/run.coffee
+++ lib/commands/run.coffee
@@ -66,7 +66,7 @@
         line += pad "#{styles.fqdn ctx.config.host}", hostlength
         line += pad "#{styles.label ctx.middleware.name}", 40
         line += pad "#{styles.status_start 'WORKING'}", 20
-        rl.write line
+        rl.write line
       .on 'middleware_stop', (err, status) ->
         return unless ctx.middleware.name?
         time = Date.now() - times[ctx.config.host]
@@ -74,9 +74,9 @@
         line += pad "#{styles.fqdn ctx.config.host}", hostlength
         line += pad "#{styles.label ctx.middleware.name}", 40
         statusmsg = if err then "#{styles.status_error ctx.middleware.label_error or 'ERROR'}"
-        else if typeof status is 'string' then status
+        else if typeof status is 'string' then status
         else if status then "#{styles.status_true ctx.middleware.label_true or 'MODIFIED'}"
-        else "#{styles.status_false ctx.middleware.label_false or 'OK'}"
+        else "#{styles.status_false ctx.middleware.label_false or '--'}"
         line += pad "#{statusmsg}", 20
         line += "#{styles.time print_time time}"
         rl.cursor = 0
@@ -124,7 +124,3 @@
       else
         rl.write "#{styles.final_status_error err.stack?.trim() or err.message}\n"
       rl.close()
-
-
-
-


Notifications are now accessed through the vehicles controller rather than through the individual vehicle

--- lib/controllers/vehicles_controller.coffee
+++ lib/controllers/vehicles_controller.coffee
@@ -74,15 +74,9 @@
   processNotifications: (notifications) ->
     console.time "processing notifications"
     console.log notifications
-    @get('content').forEach (vehicle) ->
-      # TODO: this can probably be optimised by removing the notifications from the data array
-      # so that after the messages have been added to a vehicle, we don't need to cycle
-      # through those messages again on the next iteration
-      vehicleId = vehicle.get('id')
-      notificationsForVehicle = notifications.filter (n) -> n.message.unit_id == vehicleId
-      notificationObjects = notificationsForVehicle.map (n) ->
-        Sensor.Message.create(n)
-      vehicle.updateWithNotifications(notificationObjects)
+    notificationObjects = notifications.map (n) ->
+      Sensor.Notification.create(n)
+    @set('notifications', SC.ArrayProxy.create(content: notificationObjects))
     console.timeEnd "processing notifications"
     @set('loadedNotifications', true)
   subscribeToWebsockets: ->


adding error handling to product form

adding error handling to product form

--- app/assets/javascripts/admin/stores.js.coffee
+++ app/assets/javascripts/admin/stores.js.coffee
@@ -5,6 +5,32 @@

     #config
     product_photos_gallery_displayed = 'product-photos-gallery-displayed'
+
+    product_form_submission_error_callback = (self, response) ->
+        if response.errors?
+            errors = response.errors
+        else if response.status is 500 and response.statusText?
+            errors = [{error: "#{response.statusText} - Please review your information and try submitting the form again."}]
+        else
+            errors = null
+
+        # this is criminal, but it'll have to do for now...
+        # inputs are populated with CSVs when the form is submitted, but
+        # form-label-buttons already exist in the dom,
+        # so we clear the inputs to prevent double entry
+        ($ '#product_instagram_tag').val('')
+        ($ '#product_colors').val('')
+        ($ '#product_sizes').val('')
+
+        ($ self).removeClass(hide)
+
+        ($ '.form-errors-wrapper').html(
+            if errors?
+                Mustache.render(
+                    templates.form_error_template, {
+                        errors: errors }))
+        $window.scrollTop(($ '.form-errors-wrapper'))
+

     reset_product_form = (data) ->
         $product_form_wrapper.find('form').replaceWith($('<form />'))
@@ -310,10 +336,13 @@

                 verb = if @id is 'new_product' then 'created' else 'updated'

+
                 $.ajax
                     url: ($ @).prop('action')
                     type: 'post'
                     data: ($ @).serialize()
+                    error: (response) =>
+                        product_form_submission_error_callback(@, response)
                     success: (response) =>
                         if response.status isnt 'error'
                             expire_alert_and_notice_in(6000)
@@ -322,9 +351,4 @@
                             reset_product_form()
                             fetch_and_render_product_widgets()
                         else
-                            ($ @).removeClass(hide)
-                            ($ '.form-errors-wrapper').html(
-                                Mustache.render(
-                                    templates.form_error_template, {
-                                        errors: response.errors}))
-                            $window.scrollTop(($ '.form-errors-wrapper'))
+                            product_form_submission_error_callback(@, response)


Improve offset for course navigation affix

--- app/assets/javascripts/components/course.cjsx
+++ app/assets/javascripts/components/course.cjsx
@@ -207,7 +207,7 @@

-        <Affix className="course_navigation" offset=55>
+        <Affix className="course_navigation" offset=57>

             {courseLink}


part4

--- app/assets/javascripts/oxalis/view/gui.coffee
+++ app/assets/javascripts/oxalis/view/gui.coffee
@@ -68,10 +68,6 @@
     @addCheckbox(@fViewportcontrols, @user.getSettings(), "dynamicSpaceDirection", "d/f-Switching")

     @folders.push( @fColors = @gui.addFolder("Colors") )
-    if @model.getSegmentationBinary()
-      @segmentationOpacityController =
-        @addSlider(@fColors, @user.getSettings(), "segmentationOpacity",
-          0, 100, 1, "Segment. Opacity")
     @brightnessControllers = []
     @contrastControllers = []
     @colorControllers = []
@@ -88,6 +84,10 @@
         @addSlider(@fColors, @settingsGeneral.brightnessContrastColor[binary.name], "contrast",
           0.5, 5, 0.1, "Contrast " + (i+1), @setColorSettings)
       )
+    if @model.getSegmentationBinary()
+      @segmentationOpacityController =
+        @addSlider(@fColors, @user.getSettings(), "segmentationOpacity",
+          0, 100, 1, "Segment. Opacity")
     @addFunction(@fColors, @settingsGeneral, "resetColorSettings",
       "Reset")


some fixes

some fixes

--- idea-details/idea-details.coffee
+++ idea-details/idea-details.coffee
@@ -49,6 +49,8 @@
             changedIdeas = Session.get 'changedIdeas'
             changedIdeas[@_id] = newValue
             Session.set 'changedIdeas', changedIdeas
+
+            simSearch.setExcludes [@_id]
             simSearch.find newValue
         'submit form': (e) ->
             e.preventDefault()


Remove excess code from coffee script

Remove excess code from coffee script

--- app/assets/javascripts/static_pages.js.coffee
+++ app/assets/javascripts/static_pages.js.coffee
@@ -5,76 +5,58 @@
 # Responsive Action
 ResponsiveWindowWidth = 767

-#Menu Characteristics
-edgeLocation = "0%"
-mobileMenuWidth = "80%"
-negMobileMenuWidth = "-85%"
-desktopMenuWidth = "20%"
-mobileSiteWidth = "85%"
-
 #Turn on or off touch based menu sliding ("on"/"off")
 touchToggle = "on"

+#Swipe Variables
 maxTime = 1000
 maxDistance = 50
 startX = 0
 startTime = 0
-speed = 200
-animationState = "done"
-closed = "closed"
-open = "open"
-fixed = "fixed"
-relative = "relative"
+
 touch = "ontouchend" of document
 startEvent = (if (touch) then "touchstart" else "mousedown")
 moveEvent = (if (touch) then "touchmove" else "mousemove")
 endEvent = (if (touch) then "touchend" else "mouseup")

-changeMenuWidth = (e) ->
-	$(".menu").removeClass("menu-open-mobile");
-	$('.siteContainer').removeClass("site-open-mobile");
-	$(".menu").removeClass("menu-open-browser");
-	$('.siteContainer').removeClass("site-open-browser");
+clearMenu = (e) ->
+	$(".menu").removeClass("menu-open-mobile")
+	$('.siteContainer').removeClass("site-open-mobile")
+	$(".menu").removeClass("menu-open-browser")
+	$('.siteContainer').removeClass("site-open-browser")
+
+closeMenu = (e) ->
+	if ($(window).width() < ResponsiveWindowWidth)
+		$(".menu").toggleClass("menu-open-mobile")
+		$('.siteContainer').toggleClass("site-open-mobile")
+		$('body').css "position", "relative"
+	else
+		$(".menu").toggleClass("menu-open-browser")
+		$('.siteContainer').toggleClass("site-open-browser")
+	$(".menu").data "state", "closed"
+
+openMenu = (e) ->
+	if ($(window).width() < ResponsiveWindowWidth)
+		$(".menu").toggleClass("menu-open-mobile")
+		$('.siteContainer').toggleClass("site-open-mobile")
+		$('body').css "position", "fixed"
+	else
+		$(".menu").toggleClass("menu-open-browser");
+		$('.siteContainer').toggleClass("site-open-browser")
+	$(".menu").data "state", "open"

 jQuery ->
-
 	#Closes the menu when resizing in the browser
-	$(window).bind("resize", changeMenuWidth)
+	$(window).bind("resize", clearMenu)

 	# Menu Open/Close Button
 	$('.btn-menu').click ->
+		if $(".menu").data("state") == "open"
+			closeMenu ->
+		else if $(".menu").data("state") == "closed"
+			openMenu ->

-		if $(".menu").data("state") == "open"
-			if ($(window).width() < ResponsiveWindowWidth)
-				$(".menu").toggleClass("menu-open-mobile");
-				$('.siteContainer').toggleClass("site-open-mobile");
-				$('body').css "position", relative
-			else
-				$(".menu").toggleClass("menu-open-browser");
-				$('.siteContainer').toggleClass("site-open-browser");
-			$(".menu").data "state", closed
-
-		else if $(".menu").data("state") == "closed"
-			if ($(window).width() < ResponsiveWindowWidth)
-				$(".menu").toggleClass("menu-open-mobile");
-				$('.siteContainer').toggleClass("site-open-mobile");
-				$('body').css "position", fixed
-			else
-				$(".menu").toggleClass("menu-open-browser");
-				$('.siteContainer').toggleClass("site-open-browser");
-			$(".menu").data "state", open
-
-	# This closes the menu when a link is pressed before loading the next page
-	$(".menuLink").click (e) ->
-		e.preventDefault()
-		$('.siteContainer').animate(
-			left: edgeLocation
-		, $('.menu').data('closespeed'))
-		$(".menu").animate
-			left: "-" + $('.menu').data('menuwidth')
-		, $('.menu').data('closespeed'), ->
-			document.location.href = $(".menuLink").attr('href')
-
+	#Turn on or off touch based menu sliding ("on"/"off")
 	if touchToggle == "on"
 		$(".siteContainer").bind startEvent, (e) ->
 			startTime = e.timeStamp
@@ -90,22 +72,8 @@
 			currentTime = e.timeStamp
 			if startTime != 0 && currentTime - startTime < maxTime && currentDistance > maxDistance
 				if currentX < startX
-					if $(".menu").data("state") == open
-						if ($(window).width() < ResponsiveWindowWidth)
-							$(".menu").toggleClass("menu-open-mobile");
-							$('.siteContainer').toggleClass("site-open-mobile");
-							$('body').css "position", relative
-						else
-							$(".menu").toggleClass("menu-open-browser");
-							$('.siteContainer').toggleClass("site-open-browser");
-						$(".menu").data "state", closed
+					if $(".menu").data("state") == "open"
+						closeMenu ->
 				if currentX > startX
-					if $(".menu").data("state") == closed
-						if ($(window).width() < ResponsiveWindowWidth)
-							$(".menu").toggleClass("menu-open-mobile");
-							$('.siteContainer').toggleClass("site-open-mobile");
-							$('body').css "position", fixed
-						else
-							$(".menu").toggleClass("menu-open-browser");
-							$('.siteContainer').toggleClass("site-open-browser");
-						$(".menu").data "state", open+					if $(".menu").data("state") == "closed"
+						openMenu ->


add referrer to remote_url

add referrer to remote_url

--- src/api/xdm.coffee
+++ src/api/xdm.coffee
@@ -8,6 +8,7 @@
     remoteUrl += "&uid=#{config.uid}"   if config.uid
     remoteUrl += "&access_token=#{config.appSecret}" if config.appSecret
     remoteUrl += "&user_hash=#{config.userHash}" if config.userHash != undefined
+    remoteUrl += "&r=#{document.referrer}"
     remoteUrl

   (config, emitter)->


remove old code

remove old code

--- src/atrackt.coffee
+++ src/atrackt.coffee
@@ -195,218 +195,3 @@

 Atrackt.refresh()
 Atrackt._debugConsole() if Atrackt._debug()
-
-
-# (($) ->
-#   defaults =
-#     event: 'click'
-#     selectors: [ 'a', 'button' ]
-#     delimiters:
-#       linkName: '/'
-#       category: '|'
-#       elementId: '-'
-
-#   init = ->
-#     initGlobal() unless Atrackt?
-
-#     $ ->
-#       selectors = $(defaults.selectors.join(','))
-
-#       if $(document).livequery?
-#         selectors.livequery ->
-#           initEl $(@)
-#       else
-#         selectors.each ->
-#           initEl $(@)
-
-#   initGlobal = ->
-#     window.Atrackt =
-#       globalData: {}
-#       debug: urlParams('debugTracking') == 'true'
-#       location: buildLocation()
-#       refresh: ->
-#         init()
-#       send: (trackObject)->
-#         $.extend trackObject, buildGlobalData()
-
-#         # siteCatalyst
-#         if s? and s.tl
-#           buildSObject trackObject
-#           s.tl true, 'o', buildLinkName trackObject
-#         else
-#           console.log 'SiteCatalyst script not loaded!'
-#           console.log trackObject
-
-#     if Atrackt.debug
-#       $ ->
-#         debugGlobal()
-
-#   initEl = (el) ->
-#     el.data 'track-object', buildElData el
-
-#     el.on el.data('track-data').event, (e) ->
-#       trackEl $(@)
-
-#     if Atrackt.debug
-#       debugEl el
-
-#   trackEl = (el) ->
-#     Atrackt.send el.data('track-object')
-
-#   urlParams = (key = null) ->
-#     params = {}
-#     paramString = window.location.search.substring(1)
-#     $.each paramString.split('&'), (i, param) ->
-#       paramObject = param.split('=')
-#       params[paramObject[0]] = paramObject[1]
-#     if key
-#       params[key]
-#     else
-#       params
-
-#   debugGlobal = ->
-#     $('body').addClass('tracking-debug')
-#     Atrackt.debugConsole = $('
').append(
-#       '
' +
-#       '
Location: ' + Atrackt.location + '
' +
-#       '
' +
-#       '

' +
-#       'Categories	' +
-#       'CTA	' +
-#       'Event Type	' +
-#       '

' +
-#       '

' +
-#       '
' +
-#       '
' +
-#       '
'
-#     ).prependTo('body')
-
-#   debugEl = (el) ->
-#     _elId = buildElementId(el)
-#     _elsDiv = Atrackt.debugConsole.find('#tracking-elements tbody')
-#     _elDiv = _elsDiv.find('#' + _elId)
-#     _elContent = $('
')
-
-#     el.addClass(_elId)
-#     _elContent.append(
-#       '' + el.data('track-data').categories + '	' +
-#       '' + el.data('track-data').ctaValue + '	' +
-#       '' + el.data('track-data').event + '	' +
-#       '
'
-#     )
-
-#     if _elDiv.length == 0
-#       _elsDiv.append(_elContent)
-#     else
-#       _elDiv.replaceWith(_elContent)
-
-#     _elsDiv.find('#' + _elId).hover ->
-#       el.addClass 'tracking-highlight'
-#       $('html, body').stop().animate
-#         scrollTop: el.offset().top - $('#tracking-debug').height() - 20
-#       , 500
-#     , ->
-#       el.removeClass 'tracking-highlight'
-
-#   keyLookup = (key) ->
-#     return key if defaults.siteCatalystVer >= 15
-
-#     _newKey = defaults.propMap[key]
-#     console.log 'No mapping for "' + key + '" found.' unless _newKey
-#     _newKey || key
-
-
-#   # Build methods
-#   #
-#   buildGlobalData = ->
-#     _globalData = {}
-#     $.each Atrackt.globalData, (k,v) ->
-#       _globalData[keyLookup k] = v
-#     _globalData
-
-#   buildElData = (el) ->
-#     el.data 'track-data',
-#       categories: buildCategories el
-#       ctaValue: buildCtaValue el
-#       event: buildEvent el
-
-#     _trackObject = {}
-#     _trackObject[keyLookup('location')]   = Atrackt.location
-#     _trackObject[keyLookup('categories')] = el.data('track-data').categories
-#     _trackObject[keyLookup('ctaValue')]   = el.data('track-data').ctaValue
-#     _trackObject[keyLookup('event')]      = el.data('track-data').event
-#     _trackObject
-
-#   buildLocation = ->
-#     console.log 'The  element does not have the data-track-location attribute set.' unless $('body').data('track-location')
-#     $('body').data('track-location') || $(document).attr('title') || document.URL
-
-#   buildCategories = (el) ->
-#     catArray = []
-
-#     # add this element's data-trackkey/value
-#     catArray.unshift el.data('track-cat') if el.data('track-cat')
-
-#     # add this element's parents data-trackkey/value
-#     el.parents('[data-track-cat]').each ->
-#       catArray.unshift $(@).data('track-cat')
-
-#     catArray.join defaults.delimiters.category
-
-#   buildCtaValue = (el) ->
-#     el.attr('title') || el.attr('name') || el.text() || el.val() || el.attr('id') || el.attr('class')
-
-#   buildEvent = (el) ->
-#     el.data('track-event') || defaults.event
-
-#   buildElementId = (el) ->
-#     _categories = el.data('track-data').categories
-#     _ctaValue = el.data('track-data').ctaValue
-#     _event = el.data('track-data').event
-
-#     idArray = []
-#     idArray.push _categories if _categories
-#     idArray.push _ctaValue if _ctaValue
-#     idArray.push _event if _event
-
-#     idArray.join(defaults.delimiters.elementId).toLowerCase().replace(/[^\w]/g, '')
-
-#   # site catalyst specific
-#   buildSObject = (trackObject) ->
-#     switch defaults.siteCatalystVer
-#       when 14
-#         varsArray = ['products', 'events']
-#         eventsArray = []
-#         for i in Array(defaults.propLimit)
-#           varsArray.push('prop' + (_i + 1))
-#           varsArray.push('eVar' + (_i + 1))
-#           eventsArray.push('event' + (_i + 1))
-#         s.linkTrackVars = varsArray.join(',')
-#         s.linkTrackEvents = eventsArray.join(',')
-
-#         $.extend s, trackObject
-#       when 15
-#         s.contextData = trackObject
-#     s
-
-#   buildLinkName = (trackObject) ->
-#     linkName = []
-#     switch defaults.siteCatalystVer
-#       when 14
-#         linkName = [
-#           trackObject.prop1,
-#           trackObject.prop2,
-#           trackObject.prop3,
-#         ]
-#       when 15
-#         linkName = [
-#           trackObject.location,
-#           trackObject.categories,
-#           trackObject.ctaValue,
-#         ]
-
-#     linkName.join(defaults.delimiters.linkName)
-
-#   init()
-
-# )(jQuery)


Rename for clarity.

Rename for clarity.

--- src/app.coffee
+++ src/app.coffee
@@ -44,16 +44,16 @@
             @services.set []
             @units.reset [unit]
         selectUnit: (unit) ->
-            perform = (unit) =>
+            select = (unit) =>
                 @selected_units.reset [unit]
             if unit.has('department') and unit.has('municipality')
-                perform(unit)
+                select(unit)
             else
                 unit.fetch
                     data:
                         include: 'department,municipality'
                     success: =>
-                        perform(unit)
+                        select(unit)
         clearSelectedUnit: (unit) ->
             @selected_units.reset []
         removeUnit: (unit) ->


Updated url

Updated url

--- src/app.coffee
+++ src/app.coffee
@@ -45,15 +45,13 @@
             output += '### ' + innerKey + '\n'
             if innerValue[0].title?
                 output += innerValue[0].title + '\n\n'
-            if innerValue[0].type?
+            if innerValue[0].type? and innerValue[0].url?
                 switch _.toLower innerValue[0].type
-                    when 'get'    then output += '![](https://img.shields.io/badge/rest-GET-00C000.png?maxAge=3600)\n\n'
-                    when 'post'   then output += '![](https://img.shields.io/badge/rest-POST-4070EC.png?maxAge=3600)\n\n'
-                    when 'put'    then output += '![](https://img.shields.io/badge/rest-PUT-E5C500.png?maxAge=3600)\n\n'
-                    when 'delete' then output += '![](https://img.shields.io/badge/rest-DELETE-ED0039.png?maxAge=3600)\n\n'
+                    when 'get'    then output += '![](https://img.shields.io/badge/GET-' + innerValue[0].url + '-00C000.png?maxAge=3600)\n\n'
+                    when 'post'   then output += '![](https://img.shields.io/badge/POST-' + innerValue[0].url + '-4070EC.png?maxAge=3600)\n\n'
+                    when 'put'    then output += '![](https://img.shields.io/badge/PUT-' + innerValue[0].url + '-E5C500.png?maxAge=3600)\n\n'
+                    when 'delete' then output += '![](https://img.shields.io/badge/DELETE-' + innerValue[0].url + '-ED0039.png?maxAge=3600)\n\n'
                     else output += _.toUpper innerValue[0].type
-            if innerValue[0].url?
-                output += '`' + innerValue[0].url + '`\n\n'
             if innerValue[0].header?
                 if innerValue[0].header.fields?
                     output += '#### Headers\n'


Atom don't close empty panes

Atom don't close empty panes

Signed-off-by: Caddy Dz <cbc6199cd1cb93e9d3589fb9aa7ecaece7b37833@gmail.com>

--- src/atom/config.cson
+++ src/atom/config.cson
@@ -5,9 +5,9 @@
   autosave:
     enabled: true
   core:
-    allowPendingPaneItems: false
     automaticallyUpdate: false
     closeDeletedFileTabs: true
+    closeEmptyWindows: false
     disabledPackages: [
       "wrap-guide"
       "semantic-ui"
@@ -17,9 +17,14 @@
     projectHome: "/Users/mac/Documents"
     restorePreviousWindowsOnStart: "no"
     telemetryConsent: "no"
+    themes: [
+      "one-light-ui"
+      "one-light-syntax"
+    ]
     titleBar: "custom"
     useProxySettingsWhenCallingApm: false
   editor:
+    fontFamily: "Fira Code"
     scrollPastEnd: true
     softWrap: true
     zoomFontWhenCtrlScrolling: true
@@ -29,6 +34,7 @@
     lintInlineJavaScript: true
   "linter-ui-default":
     panelHeight: 69
+    showPanel: true
   "selector-to-tag":
     activeForFileTypes: [
       "htm"
@@ -53,3 +59,7 @@
   editor:
     commentEnd: " --}}"
     commentStart: "{{-- "
+".blade.html.php.text":
+  editor:
+    commentEnd: " --}}"
+    commentStart: "{{-- "


The timestamp is now owned by the strip

The timestamp is now owned by the strip

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -139,7 +139,7 @@
         return msg.reply "SOMETHING TERRIBLE HAPPENED: #{err}" if err

         # Save locally, upload, cleanup
-        now = Date.now()
+        now = strip.info.when
         name = "ayp-#{now}.jpg"
         outPath = path.resolve("/tmp", name)
         image.saveJpeg outPath, 95, (err) ->
@@ -187,6 +187,7 @@
   # and passes flow to the builder.
   constructor: (@script, @ready) ->
     @info =
+      when: Date.now()
       script: @script
       image: null
       image_url: null


issue with lookup for ratings

issue with lookup for ratings

--- src/book-list.coffee
+++ src/book-list.coffee
@@ -205,13 +205,18 @@
       content: payload

   formatBookInfo = (book, action, cb) ->
+    currentAverage = 0
+
+    if book[BOOK.RATING] and book[BOOK.REVIEWCOUNT]
+      currentAverage = book[BOOK.RATING].value
+
     payload =
       title: action + book[BOOK.TITLE].value
       thumb_url: book[BOOK.IMAGE].value
       fields: [
         { short: true, title: "Author", value: book[BOOK.AUTHOR].value }
         { short: true, title: "Category", value: book[BOOK.CATEGORY].value }
-        { short: true, title: "Average Rating", value: if book[BOOK.RATING] then book[BOOK.RATING].value else "0"}
+        { short: true, title: "Average Rating", value: if currentAverage.toString() }
       ]

     cb(payload, null)


fix bug where after using scrollTo, scroll position is retained even on a different page/route

--- vue/src/routes.coffee
+++ vue/src/routes.coffee
@@ -51,7 +51,9 @@

 export default new Router
-  mode: 'history',
+  mode: 'history'
+  scrollBehavior: (to, from, savedPosition) ->
+    { x: 0, y: 0 }
   routes: [
     {path: '/dashboard', component: DashboardPage},
     {path: '/dashboard/:filter', component: DashboardPage},


bounded

bounded

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -48,6 +48,7 @@
   Redefining the center method will change the relative position.

   @name position
+  @methodOf Bounded#
   @returns The position of this object
   @type Point
   ###
@@ -70,6 +71,7 @@
   </pre></code>

   @name collides
+  @methodOf Bounded#
   @returns The position of this object
   @type Point
   ###


More explicit window context in src/browser.coffee.

--- src/browser.coffee
+++ src/browser.coffee
@@ -35,7 +35,7 @@
   xhr = if window.ActiveXObject
     new window.ActiveXObject('Microsoft.XMLHTTP')
   else
-    new XMLHttpRequest()
+    new window.XMLHttpRequest()
   xhr.open 'GET', url, true
   xhr.overrideMimeType 'text/plain' if 'overrideMimeType' of xhr
   xhr.onreadystatechange = ->
@@ -51,7 +51,7 @@
 # all script tags with a content-type of `text/coffeescript`.
 # This happens on page load.
 runScripts = ->
-  scripts = document.getElementsByTagName 'script'
+  scripts = window.document.getElementsByTagName 'script'
   coffeetypes = ['text/coffeescript', 'text/literate-coffeescript']
   coffees = (s for s in scripts when s.type in coffeetypes)
   index = 0
@@ -71,6 +71,6 @@

 # Listen for window load, both in decent browsers and in IE.
 if window.addEventListener
-  addEventListener 'DOMContentLoaded', runScripts, no
+  window.addEventListener 'DOMContentLoaded', runScripts, no
 else
-  attachEvent 'onload', runScripts
+  window.attachEvent 'onload', runScripts


Enable campaign-game-dev campaigns in verifier and hide web-dev campaigns

--- app/views/editor/verifier/VerifierView.coffee
+++ app/views/editor/verifier/VerifierView.coffee
@@ -41,7 +41,7 @@
       @supermodel.trackRequest @campaigns.fetch(data: {project: 'slug,type,levels'})
       @campaigns.comparator = (m) ->
         ['intro', 'course-2', 'course-3', 'course-4', 'course-5', 'course-6', 'course-8',
-         'dungeon', 'forest', 'desert', 'mountain', 'glacier', 'volcano'].indexOf(m.get('slug'))
+         'dungeon', 'forest', 'desert', 'mountain', 'glacier', 'volcano', 'campaign-game-dev-1', 'campaign-game-dev-2', 'campaign-game-dev-3'].indexOf(m.get('slug'))

   onLoaded: ->
     super()
@@ -52,10 +52,10 @@

   filterCampaigns: ->
     @levelsByCampaign = {}
-    for campaign in @campaigns.models when campaign.get('type') in ['course', 'hero'] and campaign.get('slug') isnt 'picoctf'
+    for campaign in @campaigns.models when campaign.get('type') in ['course', 'hero'] and campaign.get('slug') not in ['picoctf', 'game-dev-1', 'game-dev-2', 'game-dev-3', 'web-dev-1', 'web-dev-2', 'web-dev-3', 'campaign-web-dev-1', 'campaign-web-dev-2', 'campaign-web-dev-3']
       @levelsByCampaign[campaign.get('slug')] ?= {levels: [], checked: campaign.get('slug') in ['intro']}
       campaignInfo = @levelsByCampaign[campaign.get('slug')]
-      for levelID, level of campaign.get('levels') when level.type not in ['hero-ladder', 'course-ladder', 'game-dev', 'web-dev']  # Would use isType, but it's not a Level model
+      for levelID, level of campaign.get('levels') when level.type not in ['hero-ladder', 'course-ladder', 'web-dev']  # Would use isType, but it's not a Level model
         campaignInfo.levels.push level.slug

   filterCodeLanguages: ->


fixed 3.0 + snippets not working

fixed 3.0 + snippets not working

--- snippets/ssf_3-0_.cson
+++ snippets/ssf_3-0_.cson
@@ -164,7 +164,7 @@
 #
 ######

-'.text.html.php.ss_3-0_':
+'.text.html.php.ssf_3-0_':
   #  Fields
   'setAttribute':
     'prefix': 'setattribute'


Updating sys_info resource snippet

Updating sys_info resource snippet

--- snippets/sys_info.cson
+++ snippets/sys_info.cson
@@ -1,4 +1,4 @@
 '.source.inspec':
   'Describe sys_info':
     'prefix': 'sys_info'
-    'body': "# http://inspec.io/docs/reference/resources/sys_info/\ndescribe sys_info('${1:name}') do\n  it { should${2:_not} ${3:exist} }${4}\nend"
+    'body': "# http://inspec.io/docs/reference/resources/sys_info/\ndescribe sys_info do\n  its('${1:setting}') { should${2:_not} eq '${3:value}' }${4}\nend"


Cleanup test

Cleanup test

--- desktop/components/article/test/templates.coffee
+++ desktop/components/article/test/templates.coffee
@@ -92,7 +92,7 @@
   it 'super articles have markdown-supported footers', ->
     html = render('index')
       article: new Article
-        title: 'hi'
+        title: 'Super Article Title'
         sections: []
         contributing_authors: []
         is_super_article: true
@@ -103,9 +103,9 @@
           partner_logo_link: 'http://logo.com'
           partner_fullscreen_header_logo: 'http://fullscreen-logo.jpg'
           footer_blurb: 'Article Test [Link](http://artsy.net)'
-      superSubArticles: {models: []}
-      crop: (url) -> url
-      resize: (u) -> u
+      superSubArticles: new Articles
+      crop: ->
+      resize: ->
       moment: moment
       sd: {}
       asset: ->


slight performance improvement

slight performance improvement

--- src/coffee/controllers/player/player-ctrl.coffee
+++ src/coffee/controllers/player/player-ctrl.coffee
@@ -74,8 +74,5 @@
         initializing = no
       , 0

-    $scope.$watch (->$state.current.data.selectedTab), (newVal) ->
-      $timeout ->
-        $scope.selectedIndex = newVal
-      , 0
+    $scope.selectedIndex = $state.current.data.selectedTab
 ]


Fix workaround for new Preact 10 release

There is VNode class anymore.

--- app/assets/components/core.coffee
+++ app/assets/components/core.coffee
@@ -1,9 +1,7 @@
 import * as Preact from 'preact'

-VNode = Preact.h('a', null).constructor
-
 export createElement = (element, props, children...) ->
-  if props? && (Array.isArray(props) || typeof props != 'object' || props instanceof VNode)
+  if props? && (Array.isArray(props) || typeof props != 'object' || props.props?)
     children.unshift props
     props = {}


move rendering func to enmlRenderer

move rendering func to enmlRenderer

--- app/assets/javascripts/app.coffee
+++ app/assets/javascripts/app.coffee
@@ -60,15 +60,6 @@
     p = noteManager.get_all_notes().then (notes) ->
       vm.set_note_list(notes)
     p.catch (error) -> alert('reload_local_note_list() failed')
-
-  vm.get_enml_and_title_from_md = (md) ->
-    html_div_hidden = $('#md_html_div_hidden')
-    # return a promise
-    return enmlRenderer.get_enml_and_title_promise(
-      html_div_hidden,
-      vm.render_html,
-      md
-    )

   # ------------------------------------------------------------------------------------------------------------------
   # Debugging methods
@@ -121,12 +112,14 @@
         console.log('no valid previous current note found. create new note')
         noteManager.make_new_note().then(
           (note) =>
-            vm.change_current_note(guid=note.guid, title=note.title, md=note.md)
+            vm.change_current_note(note.guid, note.title, note.md)
             console.log('New note made: ' + JSON.stringify(note))
           (error) =>
-            alert('make_new_note() failed!')
-        ).catch (error) => alert('make new note failed!')
-    p.catch (error) => alert('load previous current note failed')
+            alert('make_new_note() failed: ' + error)
+        ).catch (error) =>
+          trace = printStackTrace({e: error})
+          alert('Error, make new note failed!\n' + 'Message: ' + error.message + '\nStack trace:\n' + trace.join('\n'))
+    p.catch (error) => alert('load previous current note failed: ' + error)

     # get note list
     vm.refresh_all_notes()
@@ -152,47 +145,7 @@
     # set markdown (single direction updating)
     vm.set_md(vm.ace_editor.getValue())
     # TODO optimize performance
-    vm.render_html($('#md_html_div'))
-
-  # ------------------------------------------------------------------------------------------------------------------
-  # markdown render functions
-  # ------------------------------------------------------------------------------------------------------------------
-  vm.render_html = (jq_html_div) ->
-    processed_md = vm.md_pre_process(vm.get_md())
-    _html_dom = $('

')
-    _html_dom.html($window.marked(processed_md, {sanitize: true}))
-    return vm.html_post_process(_html_dom).then () ->
-      jq_html_div.empty()
-      jq_html_div.append(_html_dom)
-
-  vm.md_pre_process = (md) ->
-    # TODO more handling
-    processed_md = md
-    return processed_md
-
-  # return promise
-  vm.html_post_process = (jq_tmp_div) ->
-    # code highlighting
-    jq_tmp_div.find('pre code').each (i, block) ->
-      hljs.highlightBlock(block)
-    # render Latex
-    MathJax.Hub.Queue(['Typeset', MathJax.Hub, jq_tmp_div.get(0)])
-    # change img src to real data url
-    must_finish_promise_list = []
-    jq_tmp_div.find('img[src]').each (index) ->
-      $img = $(this)
-      uuid = $img.attr('src')
-      p = imageManager.find_image_by_uuid(uuid).then(
-        (image) =>
-          if image?
-            console.log('change img src from ' + uuid + ' to its base64 content')
-            $img.attr('longdesc', uuid)
-            $img.attr('src', image.content)
-        (error) =>
-          alert('vm.html_post_process() failed due to failure in imageManager.find_image_by_uuid(' + uuid + ')')
-      )
-      must_finish_promise_list.push(p)
-    return Promise.all(must_finish_promise_list).then () -> return jq_tmp_div
+    enmlRenderer.render_html($('#md_html_div'), vm.get_md()).catch (error) => alert('render error: ' + error)

   # ------------------------------------------------------------------------------------------------------------------
   # Operations for notes
@@ -203,7 +156,7 @@
     vm.set_title(title) if title?
     vm.set_md_and_update_editor(md) if md?
     # render html
-    vm.render_html($('#md_html_div'))
+    enmlRenderer.render_html($('#md_html_div'), vm.get_md())
     # save current note guid to local storage
     localStorageService.set(vm.SETTINGS_KEY.CURRENT_NOTE_GUID, guid)

@@ -229,7 +182,7 @@
           note.status == noteManager.NOTE_STATUS.MODIFIED)
             # note in db -> current note
             console.log('loading note ' + note.guid + ' with status ' + note.status + ' from local DB')
-            vm.change_current_note(guid=note.guid, title=note.title, md=note.md)
+            vm.change_current_note(note.guid, note.title, note.md)
             vm.close_loading_modal()
             console.log('loading note ' + note.guid + ' finished')
         if (note? == false) or (note.status == noteManager.NOTE_STATUS.SYNCED_META)
@@ -237,7 +190,7 @@
           noteManager.fetch_remote_note(guid).then(
             (note) ->
               console.log('loading note ' + note.guid + ' with status ' + note.status + ' from remote')
-              vm.change_current_note(guid=note.guid, title=note.title, md=note.md)
+              vm.change_current_note(note.guid, note.title, note.md)
               vm.close_loading_modal()
               console.log('loading note ' + note.guid + ' finished')
               # updating note list
@@ -258,7 +211,7 @@
   vm.save_current_note_to_db = () ->
     if vm.is_note_dirty()
       console.log('current note is dirty, saving to db...')
-      enmlRenderer.get_title_promise($('#md_html_div_hidden'), vm.render_html).then (title) =>
+      enmlRenderer.get_title_promise($('#md_html_div_hidden'), vm.get_md()).then (title) =>
         note_info =
           guid: vm.get_guid()
           title: title
@@ -483,10 +436,51 @@
 # ----------------------------------------------------------------------------------------------------------------------
 # Service: enmlRenderer
 # ----------------------------------------------------------------------------------------------------------------------
-markever.factory 'enmlRenderer', ['imageManager', (imageManager) ->
-  get_enml_and_title_promise = (jq_html_div, html_render_func, markdown) ->
-    # html_render_func has to be a promise
-    return html_render_func(jq_html_div).then () =>
+markever.factory 'enmlRenderer', ['$window', 'imageManager', ($window, imageManager) ->
+  render_html = (jq_html_div, md) ->
+    processed_md = _md_pre_process(md)
+    _html_dom = $('

')
+    _html_dom.html($window.marked(processed_md, {sanitize: true}))
+    return _html_post_process(_html_dom).then(
+      () ->
+        jq_html_div.empty()
+        jq_html_div.append(_html_dom)
+      (error) ->
+        alert('render_html() error: ' + error)
+    )
+
+  _md_pre_process = (md) ->
+    # TODO more handling
+    processed_md = md
+    return processed_md
+
+  # return promise
+  _html_post_process = (jq_tmp_div) ->
+    # code highlighting
+    jq_tmp_div.find('pre code').each (i, block) ->
+      hljs.highlightBlock(block)
+    # render Latex
+    $window.MathJax.Hub.Queue(['Typeset', $window.MathJax.Hub, jq_tmp_div.get(0)])
+    # change img src to real data url
+    must_finish_promise_list = []
+    jq_tmp_div.find('img[src]').each (index) ->
+      $img = $(this)
+      uuid = $img.attr('src')
+      p = imageManager.find_image_by_uuid(uuid).then(
+        (image) =>
+          if image?
+            console.log('change img src from ' + uuid + ' to its base64 content')
+            $img.attr('longdesc', uuid)
+            $img.attr('src', image.content)
+        (error) =>
+          alert('_html_post_process() failed due to failure in imageManager.find_image_by_uuid(' + uuid + '): ' + error)
+      )
+      must_finish_promise_list.push(p.catch (error) -> alert('image replace failed: ' + error))
+    return Promise.all(must_finish_promise_list).then () -> return jq_tmp_div
+
+
+  get_enml_and_title_promise = (jq_html_div, markdown) ->
+    return render_html(jq_html_div, markdown).then () =>
       # further post process
       # remove all script tags
       jq_html_div.find('script').remove()
@@ -578,8 +572,8 @@
       return {enml: final_note_xml, title: title}

-  get_title_promise = (jq_html_div, html_render_func) ->
-    return html_render_func(jq_html_div).then () =>
+  get_title_promise = (jq_html_div, markdown) ->
+    return render_html(jq_html_div, markdown).then () =>
       title = 'New Note - Markever'
       if jq_html_div.find('h1').size() > 0
         text = jq_html_div.find('h1').text()
@@ -599,6 +593,7 @@
   return {
     get_enml_and_title_promise : get_enml_and_title_promise
     get_title_promise: get_title_promise
+    render_html: render_html
   }
 ]


Apps: AppController: remove forgotten code

--- client/Social/Apps/AppController.coffee
+++ client/Social/Apps/AppController.coffee
@@ -188,7 +188,6 @@
   handleRoute:(route)->

     getAppInstance route, (err, app)=>
-      if not err and app then @showContentDisplay app
       if not err and app
       then @showContentDisplay app


activity: fix feed enabled route conditions

--- client/activity/lib/routehandler.coffee
+++ client/activity/lib/routehandler.coffee
@@ -34,7 +34,7 @@
   # FIXME: Remove this call before public release. ~Umut
   kd.singletons.mainController.ready ->
     if type in reactivityRouteTypes
-      if isFeedEnabled()
+      if not isKoding() or isFeedEnabled()
       then handleReactivity info, ctx
       # unless reactivity is enabled redirect reactivity routes to `Public`
       else ctx.handleRoute '/Activity/Public'


added linkSelector option added options to suppress pages- and maxima links

--- modules/gui/lib/widgets/ListBrowser.coffee
+++ modules/gui/lib/widgets/ListBrowser.coffee
@@ -11,11 +11,16 @@
                 data:
                     color: 'white'

+                    linkSelector: '> a'
+
                     text:
                         first: '|<'
                         left: '<'
                         right: '>'
                         last: '>|'
+
+                    pages: true
+                    maxima: true

                 template: ->
                     div class:'browse'
@@ -34,10 +39,8 @@
         ##
         ##
         ##
-        _wireLinks: (data, done)->
-
-
-            @find('a').each (i, link)=>
+        _wireLinks: (data, done)->
+            @find(@data.linkSelector).each (i, link)=>
                 link = $(link).click ()=>
                     href = link.attr('href')

@@ -92,7 +95,7 @@
         ##
         _display: (items, data, done)->
             @__root.html ''
-
+
             if !data.limit || (data?.size || 0) / data.limit <= 1
                 done?()

@@ -103,12 +106,13 @@
                 if rest is 0
                     last -= 1

-                @__root.append @_createLink 0, 'first', @data.text.first, 'first', @data.imageLinks
+                if @data.maxima
+                    @__root.append @_createLink 0, 'first', @data.text.first, 'first', @data.imageLinks

                 offset = if curr > 0 then curr - 1 else last
                 @__root.append @_createLink offset, 'left', @data.text.left, 'prev', @data.imageLinks

-                @_process [0..last],
+                @_process (if @data.pages then [0..last] else []),

                     each: (i, next)=>

@@ -127,7 +131,8 @@
                         offset = if curr < last then curr + 1 else 0
                         @__root.append @_createLink offset, 'right', @data.text.right, 'next', @data.imageLinks

-                        @__root.append @_createLink last, 'last', @data.text.last, 'last', @data.imageLinks
+                        if @data.maxima
+                            @__root.append @_createLink last, 'last', @data.text.last, 'last', @data.imageLinks

                         if floyd.system.platform is 'remote'
                             @_wireLinks data, done


Add ctrl-C keybind to tree-view:copy-full-path

--- keymaps/tree-view.cson
+++ keymaps/tree-view.cson
@@ -16,6 +16,7 @@
   'left': 'tree-view:collapse-directory'
   'ctrl-[': 'tree-view:collapse-directory'
   'enter': 'tree-view:open-selected-entry'
+  'ctrl-C': 'tree-view:copy-full-path'
   'm': 'tree-view:move'
   'a': 'tree-view:add'
   'delete': 'tree-view:remove'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -631,7 +631,8 @@
   @returns {Point}
   ###
   Point.random = ->
-    Point.fromAngle(Random.angle)
+    Point.fromAngle(Random.angle())
+
   ###*
   @name ZERO
   @fieldOf Point


:memo: Mark `exclusive` option in Range methods as optional

[ci skip]

--- src/range.coffee
+++ src/range.coffee
@@ -263,7 +263,7 @@
   # range.
   #
   # * `otherRange` A {Range} or range-compatible {Array}
-  # * `exclusive` A boolean value including that the containment should be exclusive of
+  # * `exclusive` (optional) {Boolean} including that the containment should be exclusive of
   #   endpoints. Defaults to false.
   containsRange: (otherRange, exclusive) ->
     {start, end} = @constructor.fromObject(otherRange)
@@ -273,7 +273,7 @@
   # point.
   #
   # * `point` A {Point} or point-compatible {Array}
-  # * `exclusive` A boolean value including that the containment should be exclusive of
+  # * `exclusive` (optional) {Boolean} including that the containment should be exclusive of
   #   endpoints. Defaults to false.
   containsPoint: (point, exclusive) ->
     point = Point.fromObject(point)


fix another deprecation warning

fix another deprecation warning

--- src/robot.coffee
+++ src/robot.coffee
@@ -460,7 +460,7 @@
     app.use express.query()

     app.use bodyParser.json       verify: verifyHub
-    app.use bodyParser.urlencoded verify: verifyHub
+    app.use bodyParser.urlencoded {verify: verifyHub, extended: true}
     # replacement for deprecated express.multipart/connect.multipart
     # limit to 100mb, as per the old behavior
     app.use multipart(maxFilesSize: 100 * 1024 * 1024)


Default group for tiles if none provided

--- src/tiles.coffee
+++ src/tiles.coffee
@@ -2,6 +2,7 @@
   @tileSize = 60

   constructor: (opts={}) ->
+    opts.group ||= 'tiles'
     super(opts)
     @size = Kona.Tile.tileSize
     @box =


Document our type handling.

Document our type handling.

--- src/types.coffee
+++ src/types.coffee
@@ -1,3 +1,17 @@
+# We have to deal with three different string representations of types. First,
+# there's the 'external' type representation that we usually use with when
+# writing in Java -- 'byte', 'char', etc. Then there's the 'internal' type
+# representation which is more compact: all primitive types get shortened into a
+# single character, and array types are denoted by a '[' prefixed to their
+# component types. Non-array classes have their types are denoted by an 'L'
+# prefix and a ';' suffix. This format allows multiple type strings to be
+# concatenated together and still give an unambiguous parse.
+#
+# Finally, there are what we call 'class strings'. For ordinary (non-array)
+# classes, this is just their class name, sans prefix and suffix. For array
+# classes, this is identical to their 'internal' type representation.
+#
+# Rather confusing, I know.

 root = exports ? this.types = {}


Fetch part of a collection by giving a filter object as options.data for the fetch call

--- src/views.coffee
+++ src/views.coffee
@@ -34,10 +34,11 @@
         render_units_with_filter: (params)->
             querys = params.split('&')
             paramsArray = querys[0].split '=', 2
-            apiUrl = sm_settings.backend_url + '/unit/?' + paramsArray[0] + '=' + paramsArray[1]
-            UnitCollection = models.UnitList.extend url: apiUrl
-            unitCollection = new UnitCollection()
+            unitCollection = new models.UnitList()
+            dataFilter = {}
+            dataFilter[paramsArray[0]] = paramsArray[1]
             unitCollection.fetch(
+                data: dataFilter
                 success: (collection)=>
                     @draw_units collection, zoom: true, drawMarker: true
                 error: ->


Removed support for NO_AMD_PREFIX. It's no longer needed since module loader is now inside Muffin.

--- src/watch.coffee
+++ src/watch.coffee
@@ -194,22 +194,16 @@
           sourceData = _.template(fs.readFileSync(source).toString(), {settings})
           filename = sysPath.basename(source, sysPath.extname(source)) + '.js'
           path = sysPath.join destDir, filename
-
-          if sourceData.split('\n')[0].match('NO_AMD_PREFIX')
-            js = CoffeeScript.compile(sourceData)
-            fs.writeFileSync path, js
-            logging.info "compiled #{source}"
-            reload(path)
-          else
-            # Wrap the file into AMD module format
-            js = CoffeeScript.compile(sourceData, {bare: true})
-            modulePath = sysPath.relative(publicDir, path)[...-3]
-            deps = parseDeps(js)
-            js = "define('#{modulePath}', #{JSON.stringify(deps)}, function(require, exports, module) {#{js}});"
-            fs.writeFileSync path, js
-            logging.info "compiled #{source}"
-            reload(path)
-
+
+          # Wrap the file into AMD module format
+          js = CoffeeScript.compile(sourceData, {bare: true})
+          modulePath = sysPath.relative(publicDir, path)[...-3]
+          deps = parseDeps(js)
+          js = "define('#{modulePath}', #{JSON.stringify(deps)}, function(require, exports, module) {#{js}});"
+          fs.writeFileSync path, js
+          logging.info "compiled #{source}"
+          reload(path)
+
         when '.jade'
           # Run the source file through template engine
           sourceData = _.template(fs.readFileSync(source).toString(), _.extend({}, {settings}, jadeHelpers))


don't start the watcher until the server is started

--- src/watch.coffee
+++ src/watch.coffee
@@ -394,24 +394,27 @@
     callCompileCallbacks = (generatedFiles) ->
       callbacks.forEach (callback) ->
         callback generatedFiles
-    fileList   = new fs_utils.FileList config
+    fileList = new fs_utils.FileList config

     if worker.isWorker
       return callback null, {config, fileList, compilers, linters}

-    if config.persistent and config.server.run
-      server   = startServer config
-
-    # Initialise file watcher.
-    initWatcher config, (error, watcher) ->
-      return callback error if error?
-      # Get compile and reload functions.
-      compile = getCompileFn config, joinConfig, fileList, optimizers, watcher, callCompileCallbacks
-      reload = getReloadFn config, options, onCompile, watcher, server, plugins
-      includes = getPluginIncludes(plugins)
-      callback error, {
-        config, watcher, server, fileList, compilers, linters, compile, reload, includes
-      }
+    serverIsLaunched = ->
+      # Initialise file watcher.
+      initWatcher config, (error, watcher) ->
+        return callback error if error?
+        # Get compile and reload functions.
+        compile = getCompileFn config, joinConfig, fileList, optimizers, watcher, callCompileCallbacks
+        reload = getReloadFn config, options, onCompile, watcher, server, plugins
+        includes = getPluginIncludes(plugins)
+        callback error, {
+          config, watcher, server, fileList, compilers, linters, compile, reload, includes
+        }
+
+    if config.persistent and config.server.run
+      server = startServer config, serverIsLaunched
+    else
+      serverIsLaunched()

 isConfigFile = (basename, configPath) ->
   files = Object.keys(require.extensions).map (_) -> configPath + _


revert to K1 data

revert to K1 data

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -24,7 +24,7 @@
 K1_SUBJECT_GROUP   = "5417014a3ae7400bda000001"
 K2_0_SUBJECT_GROUP = "547d05ce415ac13139000001"
 K2_1_SUBJECT_GROUP = "54f4c5ab8f165b6e85000001"
-MAIN_SUBJECT_GROUP = K2_1_SUBJECT_GROUP
+MAIN_SUBJECT_GROUP = K1_SUBJECT_GROUP
 SIMULATION_GROUP   = "5417014b3ae7400bda000002"

 USERS_OPT_IN = ['a', 'b', 'c', 'g', 'h', 'i']