Renamed runOptions to scriptOptions in Runner

--- lib/runner.coffee
+++ lib/runner.coffee
@@ -6,9 +6,9 @@

   # Public: Creates a Runner instance
   #
-  # * `runOptions` a {ScriptOptions} object instance
+  # * `scriptOptions` a {ScriptOptions} object instance
   # * `emitter` Atom's {Emitter} instance. You probably don't need to overwrite it
-  constructor: (@runOptions, @emitter = new Emitter) ->
+  constructor: (@scriptOptions, @emitter = new Emitter) ->

   run: (command, extraArgs, codeContext, inputString = null) ->
     @startTime = new Date()
@@ -44,7 +44,7 @@
     @emitter.dispose()

   getCwd: ->
-    cwd = @runOptions.workingDirectory
+    cwd = @scriptOptions.workingDirectory

     workingDirectoryProvided = cwd? and cwd isnt ''
     paths = atom.project.getPaths()
@@ -80,10 +80,10 @@

   options: ->
     cwd: @getCwd()
-    env: @runOptions.mergedEnv(process.env)
+    env: @scriptOptions.mergedEnv(process.env)

   args: (codeContext, extraArgs) ->
-    args = (@runOptions.cmdArgs.concat extraArgs).concat @runOptions.scriptArgs
-    if not @runOptions.cmd? or @runOptions.cmd is ''
+    args = (@scriptOptions.cmdArgs.concat extraArgs).concat @scriptOptions.scriptArgs
+    if not @scriptOptions.cmd? or @scriptOptions.cmd is ''
       args = codeContext.shebangCommandArgs().concat args
     args


use same destruction style as the rest of package

--- lib/scroll.coffee
+++ lib/scroll.coffee
@@ -10,13 +10,13 @@
     super(@editorView, @editor)

   execute: (count=1) ->
+    {row, column} = @editor.getCursorBufferPosition()
     lastRow = @editorView.getLastVisibleScreenRow() - @scrolloff
     firstRow = @editorView.getFirstVisibleScreenRow() + @scrolloff
-    [cursorRow, cursorColumn] = @editor.getCursorBufferPosition().toArray()

-    if cursorRow - count <= firstRow
-      @editor.setCursorBufferPosition([firstRow + count, cursorColumn])
-    @editorView.scrollToBufferPosition([lastRow + count, cursorColumn])
+    if row - count <= firstRow
+      @editor.setCursorBufferPosition([firstRow + count, column])
+    @editorView.scrollToBufferPosition([lastRow + count, column])

 class ScrollUp extends Scroll
@@ -24,12 +24,12 @@
     super(@editorView, @editor)

   execute: (count=1) ->
+    {row, column} = @editor.getCursorBufferPosition()
     lastRow = @editorView.getLastVisibleScreenRow() - @scrolloff
     firstRow = @editorView.getFirstVisibleScreenRow() + @scrolloff
-    [cursorRow, cursorColumn] = @editor.getCursorBufferPosition().toArray()

-    if cursorRow + count >= lastRow
-      @editor.setCursorBufferPosition([lastRow - count, cursorColumn])
-    @editorView.scrollToBufferPosition([firstRow - count, cursorColumn])
+    if row + count >= lastRow
+      @editor.setCursorBufferPosition([lastRow - count, column])
+    @editorView.scrollToBufferPosition([firstRow - count, column])

 module.exports = { ScrollDown, ScrollUp }


Fixed lint errors

Fixed lint errors

--- lib/sentry.coffee
+++ lib/sentry.coffee
@@ -30,7 +30,7 @@
       hostname: os.hostname()
       enable_env: ['production']

-  error: (err, logger, culprit, extra={}) =>
+  error: (err, logger, culprit, extra = {}) =>
     unless err instanceof Error
       console.error 'error must be an instance of Error', err
       err = new Error "CONVERT_TO_ERROR: #{JSON.stringify(err, null, 2)}"


only read cson files for schema - watch out for swap files

--- lib/server.coffee
+++ lib/server.coffee
@@ -75,10 +75,13 @@
         return next err if err
         files = (path.join(root, dir, file) for file in files)
         async.each files, ((file, next) ->
-          key = path.basename file, '.cson'
-          readCSON file, (err, model) ->
-            schema[key] = model
-            next err
+          if path.extname file == '.cson'
+            key = path.basename file, '.cson'
+            readCSON file, (err, model) ->
+              schema[key] = model
+              next err
+          else
+            next()
         ), (err) ->
           return next err if err
           next()


typo fix (damn testing on remote)

typo fix (damn testing on remote)

--- lib/server.coffee
+++ lib/server.coffee
@@ -21,7 +21,7 @@
     if req.user?
       req.session.auth = null
       req.session.destroy(->)
-    resp.redirect("/login/")
+    resp.redirect("/login/"))

 server.get('/slideshow/',
   (req, resp) ->


Update customizations.coffee

Update customizations.coffee

Updated Czech translations based on comments from Martin at D21

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -273,7 +273,7 @@
       pros: 'pro'
       con: 'proti'
       cons: 'proti'
-      your_header: "Dejte --valences--"
+      your_header: "Názor --valences--"
       other_header: "Jiný' --valences--"
       top_header: "Top --valences--"
     slider_pole_labels:


update the default timezone for hx.preferences when not using moment.js (resolves #254)

--- modules/preferences/main/index.coffee
+++ modules/preferences/main/index.coffee
@@ -230,7 +230,13 @@
         -(moment.tz.zone(timezone).offset(timestamp) / 60)
       @timezone guessedMomentTimezone
     else
-      @timezone 'UTC+00:00'
+      zeroPad = hx.format.zeroPad(2)
+      offset = (new Date()).getTimezoneOffset()
+      modifier = if offset > 1 then '-' else '+'
+      absOffset = Math.abs(offset)
+      minutes = absOffset % 60
+      hours = (absOffset - minutes) / 60
+      @timezone "UTC#{modifier}#{zeroPad(hours)}:#{zeroPad(minutes)}"

   timezone: (timezone) ->
     if arguments.length > 0


Rename AutoComplete -> Autocomplete in preferences

--- modules/preferences/main/index.coffee
+++ modules/preferences/main/index.coffee
@@ -3,7 +3,7 @@
 import { select, detached, div } from 'selection/main'
 import { isString } from 'utils/main'
 import { Modal } from 'modal/main'
-import { AutoComplete } from 'autocomplete/main'
+import { Autocomplete } from 'autocomplete/main'
 import { notifyNegative, notifyPositive } from 'notify/main'
 import { format } from 'format/main'
 import logger from 'logger/main'
@@ -179,7 +179,7 @@
           disabled: not (l.value in @_.supportedLocales)
         }

-      new AutoComplete(localeAutocompleteElement.node(), supportedLocales, {
+      new Autocomplete(localeAutocompleteElement.node(), supportedLocales, {
         renderer: (element, datum) -> select(element).text(datum.full)
         inputMap: (item) -> item.full
         showOtherResults: true
@@ -194,7 +194,7 @@
       timezoneAutocompleteElement = detached('input')
       timezoneAutocompleteElement.value(timezone)

-      new AutoComplete(timezoneAutocompleteElement.node(), @_.supportedTimezones, {
+      new Autocomplete(timezoneAutocompleteElement.node(), @_.supportedTimezones, {
         showOtherResults: true
         mustMatch: true
       }).on 'change', (value) -> timezone = value


vm/listitem: read from expandedmachinelabel store for vm name

--- client/home/lib/virtualmachines/components/machineslist/listitem.coffee
+++ client/home/lib/virtualmachines/components/machineslist/listitem.coffee
@@ -45,6 +45,7 @@

     return {
       selectedMachine: VirtualMachinesSelectedMachineFlux.getters.selectedMachine
+      expandedMachineLabel: EnvironmentFlux.getters.expandedMachineLabelStore
     }

@@ -137,7 +138,7 @@
         <div
           className="MachinesListItem-machineLabel #{@props.machine.getIn ['status', 'state']}"
           onClick={@bound 'toggle'}>
-          {@props.machine.get 'label'}
+          {@state.expandedMachineLabel or @props.machine.get 'label'}
           {@renderProgressbar()}
         </div>
         {@renderIpAddress()}


fullwidth adjust fixed

fullwidth adjust fixed

--- client/landing/app/MainApp/maincontroller/mainviews/contentpanel.coffee
+++ client/landing/app/MainApp/maincontroller/mainviews/contentpanel.coffee
@@ -66,7 +66,8 @@
   adjustForFullWidth:->

     @setX 0
-    @setWidth @windowController.winWidth - @chatMargin
+    @getElement().style.width = "100%"
+    # @setWidth @windowController.winWidth - @chatMargin
     @setClass 'full'
     @state = 'full'


Minor cleanup.

Minor cleanup.

--- src/kache.coffee
+++ src/kache.coffee
@@ -256,7 +256,7 @@
     @

   set: (key, value, timeout) ->
-    @instance.set(key, value, timeout)
+    @instance.set key, value, timeout

 root.Kache = (namespace, timeout, atts) ->
   new _Kache(namespace, timeout, atts)


Make fetch Classroom by code case insensitive

--- server/classrooms/classroom_handler.coffee
+++ server/classrooms/classroom_handler.coffee
@@ -124,6 +124,7 @@
         return @sendDatabaseError(res, err) if err
         return @sendSuccess(res, (@formatEntity(req, classroom) for classroom in classrooms))
     else if code = req.query.code.toLowerCase()
+      code = code.toLowerCase()
       Classroom.findOne {code: code}, (err, classroom) =>
         return @sendDatabaseError(res, err) if err
         return @sendNotFoundError(res) unless classroom


Delay

Delay


Fetch comments 10 by 10 asynchronously

Fetch comments 10 by 10 asynchronously

--- client/app/CommonViews/comments/commentlistviewcontroller.coffee
+++ client/app/CommonViews/comments/commentlistviewcontroller.coffee
@@ -3,26 +3,28 @@
     super
     @startListeners()

-  instantiateListItems:(items)->
+  instantiateListItems:(items, keepDeletedComments = no)->

     newItems = []

     for comment, i in items
-
+
       nextComment = items[i+1]

       skipComment = no
       if nextComment? and comment.deletedAt
         if Date.parse(nextComment.meta.createdAt) > Date.parse(comment.deletedAt)
           skipComment = yes
-
+
       if not nextComment and comment.deletedAt
         skipComment = yes
+
+      skipComment = no if keepDeletedComments

       unless skipComment
         commentView = @getListView().addItem comment
         newItems.push commentView
-
+
     return newItems

   startListeners:->
@@ -31,15 +33,38 @@
     listView.on 'ItemWasAdded', (view, index)=>
       view.on 'CommentIsDeleted', ->
         listView.emit "CommentIsDeleted"
-
+
     listView.on "AllCommentsLinkWasClicked", (commentHeader)=>

       # some problems when logged out server doesnt responds
       @utils.wait 5000, -> listView.emit "BackgroundActivityFinished"
-
-      @fetchAllComments 0, (err, comments)=>
-        @removeAllItems()
-        @instantiateListItems comments
+
+      {repliesCount} = listView.getData()
+
+      log "Total comment: ", repliesCount
+      stack = []
+      removedAlready = no
+
+      stack.push (callback) =>
+        listView.emit "BackgroundActivityStarted"
+        callback()
+
+      for xto in [0..repliesCount] by 10
+        do (xto) =>
+          stack.push (callback) =>
+            @fetchCommentsByRange xto, xto+10, (err, comments) =>
+              if not removedAlready
+                @removeAllItems()
+                removedAlready = yes
+              @instantiateListItems comments, yes
+              callback err, comments
+
+      async.parallel stack, (error, result) =>
+        listView.emit "BackgroundActivityFinished"
+        if not error
+          listView.emit "AllCommentsWereAdded"
+        else
+          log "Failed to get comments..."

     listView.registerListener
       KDEventTypes  : "CommentSubmitted"
@@ -60,9 +85,9 @@
     message.commentsByRange query,(err,comments)=>
       @getListView().emit "BackgroundActivityFinished"
       callback err,comments
-
+
   fetchAllComments:(skipCount=3, callback = noop)=>
-
+
     listView = @getListView()
     listView.emit "BackgroundActivityStarted"
     message = @getListView().getData()
@@ -70,7 +95,7 @@
       listView.emit "BackgroundActivityFinished"
       listView.emit "AllCommentsWereAdded"
       callback err, comments
-
+
   replaceAllComments:(comments)->
     @removeAllItems()
     @instantiateListItems comments


+ unit test

+ unit test

--- test/get-contributors-test.coffee
+++ test/get-contributors-test.coffee
@@ -8,5 +8,72 @@

 setImmediate    = setImmediate || process.nextTick

-describe "get-contributors", ->

+contributors  = require('git-contributors').GitContributors
+
+fakeResults = []
+fakeError = null
+# mock this method
+contributors.constructor::list = sinon.spy (options, callback)->
+  options = options || {}
+  if fakeError?
+    callback(fakeError)
+  else
+    callback(null, fakeResults)
+  return
+
+getContributors = require '../src/'
+
+describe 'get-contributors', ->
+
+  it 'should sort by percentage', (done)->
+    fakeResults = [
+      {
+        commits: 33
+        name: 'Riceball LEE'
+        email: 'snowyu.lee@gmail.com'
+        percent: 37.1 }
+      {
+        commits: 25
+        name: 'zhuangbiaowei'
+        email: 'zhuangbiaowei@gmail.com'
+        percent: 28.1 }
+      {
+        commits: 16
+        name: 'zhuangbiaowei'
+        email: 'zhuangbiaowei@huawei.com'
+        percent: 18 }
+      {
+        commits: 13
+        name: '庄 表伟'
+        email: 'zhuangbiaowei@gmail.com'
+        percent: 14.6 }
+      {
+        commits: 3
+        name: '庄表伟'
+        email: 'zhuangbiaowei@gmail.com'
+        percent: 3.4 }
+    ]
+    options =
+      dirname: '.'
+      users:
+        zhuangbiaowei:
+          name: "BiaoWei Zhuang"
+          email: [
+            "zhuangbiaowei@gmail.com"
+            "zhuangbiaowei@huawei.com"
+          ]
+        riceball:
+          name: "Riceball LEE"
+          email: "snowyu.lee@gmail.com"
+      fields: [
+        'name'
+      ]
+    getContributors options
+    .then (users)->
+      users.should.be.deep.equal [
+        {name: 'BiaoWei Zhuang'}
+        {name: 'Riceball LEE'}
+      ]
+      users
+    .nodeify(done)


updated show view with new actions (edit, deleted, etc)

--- app/assets/javascripts/ember/views/projects/show.js.coffee
+++ app/assets/javascripts/ember/views/projects/show.js.coffee
@@ -1,4 +1,20 @@
 DashIt.ShowProjectView = Ember.View.extend
-  templateName: 'app/templates/projects/show',
-  classNames:   ['show-project'],
-  tagName:      'tr'
+  templateName: 'ember/templates/projects/show'
+  classNames: ['show-project']
+  tagName: 'tr'
+
+  doubleClick: ->
+    @showEdit()
+
+  showEdit: ->
+    @set "isEditing", true
+
+  hideEdit: ->
+    @set "isEditing", false
+
+  destroyRecord: ->
+    project = @get("project")
+    project.destroyResource().fail((e) ->
+      DashIt.displayError e
+    ).done ->
+      DashIt.projectsController.removeObject projects


new look of upload file in modal view #65

--- app/assets/javascripts/oxalis/view/merge_modal_view.coffee
+++ app/assets/javascripts/oxalis/view/merge_modal_view.coffee
@@ -60,13 +60,18 @@
                     enctype="multipart/form-data"
                     id="upload-and-explore-form"
                     class="form-inline inline-block">
-
-                    <input type="file" name="nmlFile" accept=".nml">
-
-
-                      Upload NML
-                    </input>
-
+

+
+
+                          <input type="file" name="nmlFile" accept=".nml">
+
+
+                          Upload NML
+                        </input>
+
+
+                      <input type="text" class="file-info form-control" readonly="">
+

                 </form>

@@ -111,11 +116,11 @@
     "uploadAndExploreForm" : "#upload-and-explore-form"
     "formSpinnerIcon"      : "#form-spinner-icon"
     "formUploadIcon"       : "#form-upload-icon"
-
+    "fileInfo"             : ".file-info"

   initialize : (options) ->
-
     @_model = options._model
+    debugger
     @nml = ""

@@ -219,6 +224,7 @@

     if event.target.files.length
       @ui.uploadAndExploreForm.submit()
+      @ui.fileInfo.val(event.target.files[0].name)

   toggleIcon : ->


change modal footer #65

change modal footer #65

--- app/assets/javascripts/oxalis/view/merge_modal_view.coffee
+++ app/assets/javascripts/oxalis/view/merge_modal_view.coffee
@@ -90,9 +90,10 @@

-
If you want product of merging to be read-only then left checkbox selected

-

-              <input type="checkbox" id="checkbox-read-only" checked="checked"/>
+

+
+              <input type="checkbox" id="checkbox-read-only" checked="checked">
+              If you want product of merging to be read-only then left checkbox selected

@@ -132,6 +133,7 @@

   show : ->

+    debugger
     @$el.modal("show")

     $.ajax(url : "/api/user").done((user) =>


cleanup

cleanup

--- app/assets/javascripts/states/main/views/nav_bar.js.coffee
+++ app/assets/javascripts/states/main/views/nav_bar.js.coffee
@@ -5,7 +5,7 @@
     reuse: true
     template: "main/nav_bar"

-    links: {
+    links:
       "a.brand": [ "inventory" ]
       ".tab.inventory a": [ "inventory" ]
       ".tab.monitoring a": [ "monitoring" ]
@@ -13,9 +13,8 @@

       # user menu
       "a#profile": [ "profile" ]
-    }

-    events: {
+    events:
       "click a#logout": (e) ->
         v = @
         $.ajax("/users/sign_out", {
@@ -33,16 +32,12 @@
       "click a#stop_impersonating": (e) ->
         @impersonate()

-    }
-
-    app_events: {
+    app_events:
       "state:activate": (state) ->
         if state.tab? and state.tab != @current_tab
           $("ul.nav li.tab").removeClass("active")
           if @current_tab = state.tab
             $("ul.nav li.tab.#{@current_tab}").addClass("active")
-
-    }

     impersonate: (user_id) ->
       return if !@true_user.can("impersonate_users")


log ops in filestore error

log ops in filestore error

GitOrigin-RevId: 008722ba2ff335ebedb2e17e9413737d0244f78e

--- services/web/app/coffee/Features/FileStore/FileStoreHandler.coffee
+++ services/web/app/coffee/Features/FileStore/FileStoreHandler.coffee
@@ -75,7 +75,7 @@
 				opts.headers['range'] = "bytes=#{query['range']}"
 		readStream = request(opts)
 		readStream.on "error", (err) ->
-			logger.err {err, project_id, file_id, query}, "error in file stream"
+			logger.err {err, project_id, file_id, query, opts}, "error in file stream"
 		callback(null, readStream)

 	deleteFile: (project_id, file_id, callback)->


replace console.logs with error callbacks

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -49,11 +49,11 @@

 			getPdfViewport: (pageNum, scale) ->
 				scale ?= @scale
-				@document.then (pdfDocument) ->
+				@document.then (pdfDocument) =>
 					pdfDocument.getPage(pageNum).then (page) ->
 						viewport = page.getViewport scale
-					, (error) ->
-						console.log 'ERROR', error
+					, (error) =>
+						@errorCallback?(error)

 			getDestinations: () ->
 				@document.then (pdfDocument) ->
@@ -68,8 +68,8 @@
 					pdfDocument.getDestinations()
 				return @destinations.then (all) ->
 					all[dest]
-				, (error) ->
-					console.log 'ERROR', error
+				, (error) =>
+					@errorCallback?(error)
 				# When we upgrade we can switch to using the following direct
 				# code.
 				# @document.then (pdfDocument) ->
@@ -78,11 +78,11 @@
 				# 	console.log 'ERROR', error

 			getPageIndex: (ref) ->
-				@document.then (pdfDocument) ->
+				@document.then (pdfDocument) =>
 					pdfDocument.getPageIndex(ref).then (idx) ->
 						idx
-					, (error) ->
-						console.log 'ERROR', error
+					, (error) =>
+						@errorCallback?(error)

 			getScale: () ->
 				@scale
@@ -152,7 +152,7 @@
 					@spinner.stop(element.canvas)
 					# @jobs = @jobs - 1
 					# @triggerRenderQueue(0)
-					this.errorCallback?('timeout')
+					@errorCallback?('timeout')
 				, @PAGE_LOAD_TIMEOUT

 				@pageLoad[pagenum] = @getPage(pagenum)
@@ -259,11 +259,11 @@
 					page.getTextContent().then (textContent) ->
 						textLayer.setTextContent textContent
 					, (error) ->
-						console.log 'ERROR', error
+						self.errorCallback?(error)
 					page.getAnnotations().then (annotations) ->
 						annotationsLayer.setAnnotations annotations
 					, (error) ->
-						console.log 'ERROR', error
+						self.errorCallback?(error)
 				.catch (error) ->
 					# console.log 'page render failed', pagenum, error
 					$timeout.cancel(timer)


Fixed for format row with spaces

Fixed for format row with spaces


Forgot to add in to the extensions list

Forgot to add in to the extensions list

--- src/sloc.coffee
+++ src/sloc.coffee
@@ -220,6 +220,7 @@
   "hx"
   "hy"
   "ino"
+  "jade"
   "java"
   "jl"
   "js"


Client: Activity: Added handleMouseClickOutside event handler to fix multiple modals closing problem

--- client/app/lib/components/modal/index.coffee
+++ client/app/lib/components/modal/index.coffee
@@ -1,6 +1,7 @@
 kd     = require 'kd'
 React  = require 'kd-react'
 Portal = require 'react-portal'
+isNodeInRoot = require 'app/util/isnodeinroot'

 class ModalOverlay extends React.Component
@@ -17,16 +18,41 @@
   @defaultProps =
     onClose             : kd.noop
     closeOnEsc          : yes
-    closeOnOutsideClick : yes
     hasOverlay          : yes
     isOpen              : no
     closeIcon           : yes
+
+  constructor: (options = {}, data) ->
+
+    super options, data
+
+    @handleMouseClickOutside = @handleMouseClickOutside.bind this
+

   getPortalProps: ->
     isOpened            : @props.isOpen
     onClose             : @props.onClose
     closeOnEsc          : @props.closeOnEsc
-    closeOnOutsideClick : @props.closeOnOutsideClick
+
+
+  componentDidMount: ->
+
+    document.addEventListener('mousedown', @handleMouseClickOutside);
+
+
+  componentWillUnmount: ->
+
+    document.removeEventListener('mousedown', @handleMouseClickOutside);
+
+
+  handleMouseClickOutside: (event) ->
+
+    return if isNodeInRoot event.target, React.findDOMNode @refs.ModalWrapper
+
+    return @props.handleMouseClickOutside(event)  if @props.handleMouseClickOutside
+
+    event.stopPropagation()
+    @props.onClose()

   renderModalCloseIcon: ->
@@ -40,7 +66,7 @@
     return null  unless @props.isOpen

     <Portal {...@getPortalProps()} ref="modal">
-

+

         {@renderModalCloseIcon()}
         {@props.children}


Update jquery.observer.coffee

Update jquery.observer.coffee

--- javascripts/jquery.observer.coffee
+++ javascripts/jquery.observer.coffee
@@ -21,9 +21,7 @@

   observe: ->
     _this = this
-    $(@form.elements).change ->
-      _this.modified = new Date()
-    $(@form.elements).keypress ->
+    $(@form.elements).on 'change keypress', ->
       _this.modified = new Date()

     @every @settings.interval, =>


[node_modules_koding][jraphical] wrapped save and delete methods of Relationship collection

--- node_modules_koding/jraphical/lib/relationship.coffee
+++ node_modules_koding/jraphical/lib/relationship.coffee
@@ -110,30 +110,23 @@
   save_ = @::save
   save:(callback)->
     save_.call this, (err, model)=>
-      console.log "save called"
-      console.log @
-#      console.log arguments
       return callback err if err
-      #      @constructor.emit 'RelationshipRemoved', this
-      callback model
+      @constructor.emit 'RelationshipSaved', this
+      callback err, model

   remove_ = @::remove
-  remove:(selector, callback)->
-    remove_.call this, selector, (err)=>
-      console.log "instance remove called"
-#      console.log @
+  remove:(callback)->
+    remove_.call this, (err)=>
       return callback err if err
       @constructor.emit 'RelationshipRemoved', this
       callback err

-
   @remove_ = @remove
   @remove:(selector, callback)->
-    console.log selector
-    @remove_.call null, selector, (err)->
-      console.log "static remove called"
-#      console.log @
-      return callback err if err
-
-#      @emit 'hebele', selector
-      callback err
+    Relationship.all selector, (err, res)=>
+      @remove_ selector, (err)->
+        return callback err if err
+        if res.length > 0
+          for rel in res
+            Relationship.emit "RelationshipRemoved", rel
+        callback err


dunno

dunno

--- .atom/config.cson
+++ .atom/config.cson
@@ -12,11 +12,11 @@
     ]
     telemetryConsent: "limited"
     themes: [
-      "solarized-dark-ui"
-      "solarized-dark-syntax"
+      "solarized-light-ui"
+      "solarized-light-syntax"
     ]
   editor:
-    fontSize: 17
+    fontSize: 16
     preferredLineLength: 120
   "exception-reporting":
     userId: "9363c2a1-ce13-c340-7351-14b4c86035b6"


Atom tweaks

Atom tweaks

Signed-off-by: Tom Skarbek Wazynski <35d26e5bea0c1d3a1f8f55c8aef571f50f39f963@lancaster.ac.uk>

--- .atom/config.cson
+++ .atom/config.cson
@@ -31,9 +31,8 @@
     fonts: {}
     tabs: {}
     treeView:
-      compactList: true
-    ui:
-      panelContrast: true
+      compactList: false
+    ui: {}
   "auto-update-packages": {}
   "autocomplete-plus": {}
   "autocomplete-ruby":


Fix typo

Fix typo

--- test/spec.coffee
+++ test/spec.coffee
@@ -7,7 +7,7 @@
       headings = document.querySelectorAll('h1')
     )

-    it('should keep a uniform vertical rythm respecting line spacing', ->
+    it('should keep a uniform vertical rhythm respecting line spacing', ->
       lineHeight = headings[0].getBoundingClientRect().top
       firstHeadingRect = headings[1].getBoundingClientRect()
       secondHeadingRect = headings[2].getBoundingClientRect()


fix url generation

fix url generation

--- client/skr/models/mixins/PrintSupport.coffee
+++ client/skr/models/mixins/PrintSupport.coffee
@@ -1,6 +1,6 @@
 Skr.Models.Mixins.PrintSupport = {

     pdfDownloadUrl: ->
-        '/print-skr-form' + @url()
+        "#{Lanes.config.api_path}/skr/print/#{@modelTypeIdentifier()}/#{@hash_code}.pdf"

 }


Updated coffee test

Updated coffee test

--- test/test.coffee
+++ test/test.coffee
@@ -1,6 +1,6 @@
 scraper = require '../'

-baseUrl = 'http://thepiratebay.am'
+baseUrl = 'http://thepiratebay.mn'
 scraper.setUrl(baseUrl)

 describe 'scraper', ->


Fix for older browsers.

Fix for older browsers.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -992,9 +992,6 @@

   init:

-    faviconColors: ->
-      feedbin.faviconColors($(".feeds-column"))
-
     baseFontSize: ->
       element = document.createElement('div')
       content = document.createTextNode('content')
@@ -1012,6 +1009,9 @@

       $("html").css
         "font-size": "#{base}px"
+
+    faviconColors: ->
+      feedbin.faviconColors($(".feeds-column"))

     hasScrollBars: ->
       if feedbin.scrollBars()


form js update

form js update

--- source/components/form/form.jquery.coffee
+++ source/components/form/form.jquery.coffee
@@ -1,14 +1,69 @@

-$('.b-form').on 'change', '.b-form__radio input', =>
-    it = $(@)
-    propName = it.attr('name')
-    $('[name="' + propName + '"]').parent().removeClass 'b-form__radio_checked'
-    it.parent().addClass 'b-form__radio_checked'
+mhForm = do ->
+    # private API
+    version = '0.0.1'

-$('.b-form').on 'change', '.b-form__checkbox input', =>
-    it = $(@)
-    parent = it.parent()
-    if it.prop('checked')
-        parent.addClass 'b-form__checkbox_checked'
-    else
-        parent.removeClass 'b-form__checkbox_checked'
+    init = ->
+        $('input').each ->
+            it = $(@)
+            inputType = checkType(it)
+            if inputType == false then return
+            parent = it.parent()
+            if it.prop('checked')
+                parent.addClass 'b-form__' + inputType + '_checked'
+                return
+            else
+                parent.removeClass 'b-form__' + inputType + '_checked'
+                return
+
+        $('body').on 'click', '.b-form__radio input', toggleChecked
+        $('body').on 'click', '.b-form__checkbox input', toggleChecked
+        return
+
+    checkType = (it) ->
+        parent = it.parent()
+        if parent.hasClass('b-form__radio')
+            type = 'radio'
+        else if parent.hasClass('b-form__checkbox')
+            type = 'checkbox'
+        else
+            type = false
+
+        return type
+
+    toggleChecked = ->
+        it = $(@)
+        inputType = checkType(it)
+        if it.prop('checked')
+            addChecked(it, inputType)
+        else
+            unChecked(it, inputType)
+
+    addChecked = (it, inputType) ->
+        it = $(it) if typeof it == 'string'
+        inputType = checkType(it) unless inputType?
+        propName = it.attr('name')
+        if inputType == 'radio'
+            $('[name="' + propName + '"]').parent()
+                .removeClass 'b-form__radio_checked'
+
+        it.parent().addClass 'b-form__' + inputType + '_checked'
+
+    unChecked = (it, inputType) ->
+        it = $(it) if typeof it == 'string'
+        inputType = checkType(it) unless inputType?
+        if inputType == 'radio' then return
+        it.parent().removeClass 'b-form__' + inputType + '_checked'
+
+    #  public API
+    {
+        init: init # Start initialize module
+        addChecked: addChecked  # for checkbox and radio
+        unChecked: unChecked # only for checkbox
+    }
+
+mhForm.init()
+
+# mhForm.addChecked 'input#checkboxOn'
+# mhForm.addChecked 'input#radioOn'
+# mhForm.inChecked 'input#checkboxOff'


DRY up getting the previous token

DRY up getting the previous token

--- src/lexer.coffee
+++ src/lexer.coffee
@@ -132,7 +132,7 @@
       @token 'DEFAULT', id
       return id.length

-    [..., prev] = @tokens
+    prev = @prev()

     tag =
       if colon or prev? and
@@ -242,6 +242,8 @@
     [quote] = STRING_START.exec(@chunk) || []
     return 0 unless quote

+    prev = @prev()
+
     # If the preceding token is `from` and this is an import or export statement,
     # properly tag the `from`.
     if @tokens.length and @value() is 'from' and (@seenImport or @seenExport)
@@ -325,7 +327,7 @@
         [regex, body, closed] = match
         @validateEscapes body, isRegex: yes, offsetInChunk: 1
         index = regex.length
-        [..., prev] = @tokens
+        prev = @prev()
         if prev
           if prev.spaced and prev[0] in CALLABLE
             return 0 if not closed or POSSIBLY_DIVISION.test regex
@@ -450,7 +452,7 @@
   whitespaceToken: ->
     return 0 unless (match = WHITESPACE.exec @chunk) or
                     (nline = @chunk.charAt(0) is '\n')
-    [..., prev] = @tokens
+    prev = @prev()
     prev[if match then 'spaced' else 'newLine'] = true if prev
     if match then match[0].length else 0

@@ -478,7 +480,7 @@
     else
       value = @chunk.charAt 0
     tag  = value
-    [..., prev] = @tokens
+    prev = @prev()

     if prev and value in ['=', COMPOUND_ASSIGN...]
       skipToken = false
@@ -768,6 +770,11 @@
     [..., token] = @tokens
     token?[1]

+  # Get the previous token in the token stream.
+  prev: ->
+    [..., token] = @tokens
+    token if token?
+
   # Are we in the midst of an unfinished expression?
   unfinished: ->
     LINE_CONTINUER.test(@chunk) or


modifies coffeescript so that clicking on 'add tag' button doesn't cause popularity to appear

--- app/assets/javascripts/games.js.coffee
+++ app/assets/javascripts/games.js.coffee
@@ -4,10 +4,10 @@

 jQuery ->

-  $('#game_tag_ids').chosen()
-  $('#game_relation_ids').chosen()
+  $(document).ready ->

-  $(document).ready ->
+    $('#game_tag_ids').chosen()
+    $('#game_relation_ids').chosen()

     $("#game_is_an_exercise").iphoneStyle
       checkedLabel: "EXERCISE"
@@ -37,8 +37,9 @@
         time = new Date().getTime()
         regexp = new RegExp($(this).data('id'), 'g')
         $(this).before($(this).data('fields').replace(regexp, time))
-        $('#game_names_attributes_0_popularity').show()
-        event.preventDefault()
+        if($(this).text() == "Add Name")
+          $('#game_names_attributes_0_popularity').show()
+          event.preventDefault()
       else if $('.remove_fields').filter(':visible').length >= 3
         time = new Date().getTime()
         regexp = new RegExp($(this).data('id'), 'g')


minor fixes in uploadcareFile mock object

--- test/dummy/spec/javascripts/mock-objects/uploadcare-file.js.coffee
+++ test/dummy/spec/javascripts/mock-objects/uploadcare-file.js.coffee
@@ -15,12 +15,12 @@

   class FakeFile

-    constructor: (@settings, fixture=kitty) ->
+    constructor: (@settings, @type, @data, fixture=kitty) ->
       @df = $.Deferred()
       @pr = @df.promise()
       @pr.cancel = ->

-      $.extend this, utils.toFileProperties kitty
+      $.extend this, utils.toFileProperties fixture
       @progressState = 'uploading'
       @progress = 0

@@ -65,7 +65,7 @@

   fakeRawFilesFrom = (type, data, settings = {}) ->
     settings = uploadcare.settings.build settings
-    file = transformer new FakeFile(settings), type, data, settings
+    file = transformer new FakeFile(settings, type, data)
     [file]

   origRawFilesFrom = uploadcare.rawFilesFrom


Add saveForWeb option

Add saveForWeb option

--- test/main.coffee
+++ test/main.coffee
@@ -34,6 +34,7 @@
       stream = sketch
         export: 'slices'
         formats: 'png'
+        saveForWeb: true
       stream.on 'data', (dist) ->
         should.exist dist
         should.exist dist.path
@@ -51,6 +52,7 @@
       stream = sketch
         export: 'slices'
         formats: 'png'
+        saveForWeb: true
       stream.on 'data', (dist) ->
         should.exist dist
         should.exist dist.path


Add startGame method

Add startGame method

--- private/coffee/index.coffee
+++ private/coffee/index.coffee
@@ -1,31 +1,48 @@
 $ = require 'jquery'
+debug = true

 $ ->
+  # Global variables
   $page = {}
+  data = {}
   # mode = { 'easy', 'hard' }
   mode = ''

+  # Sets up the website for the first time
   setup = ->
     $page =
       intro: $ '.page.intro'
       game: $ '.page.game'
       results: $ '.page.results'

-    # $.getJSON '/api', (response) ->
-    #   data = response
-    #   console.log data
     setPage 'intro', false

     $easyButton = $('.easy.button')
     $hardButton = $('.hard.button')

-    $easyButton.click ->
-      mode = 'easy'
-      setPage('game')
-    $hardButton.click ->
-      mode = 'hard'
-      setPage('game')
+    # Load the data then enable intro button clicks
+    loadData ->
+      $easyButton.click ->
+        startGame('easy')
+      $hardButton.click ->
+        startGame('hard')

+  # Starts a new game
+  startGame = (type) ->
+    mode = type
+    setPage('game')
+
+  # Loads the data for the game
+  loadData = (cb) ->
+    if debug
+      data = require '../../public/data/data'
+      cb()
+    else
+      $.getJSON '/api', (response) ->
+        data = response
+        cb()
+
+  # Sets the state of the pages
   setPage = (newPageName, transition = true) ->
     for pageName of $page
       if pageName is newPageName


prevent saving if you don't own

prevent saving if you don't own

--- app/assets/javascripts/lists.js.coffee
+++ app/assets/javascripts/lists.js.coffee
@@ -1,6 +1,6 @@
 class App.backbone.List extends Backbone.Model
   change: ->
-    @save()
+    @save() if App.data.listShares
     @propertiesView.render()

   updateItems: ->


minor refactoring

minor refactoring

--- src/tail.coffee
+++ src/tail.coffee
@@ -1,5 +1,5 @@
-events= require("events")
-fs =require('fs')
+events = require("events")
+fs = require('fs')

 environment = process.env['NODE_ENV'] || 'development'

@@ -11,7 +11,7 @@
       if block.end > block.start
         stream = fs.createReadStream(@filename, {start:block.start, end:block.end-1, encoding:"utf-8"})
         stream.on 'error',(error) =>
-          console.log("Tail error:#{error}")
+          console.error("Tail error:#{error}")
           @emit('error', error)
         stream.on 'end',=>
           @internalDispatcher.emit("next") if @queue.length >= 1
@@ -22,7 +22,7 @@
           @buffer = parts.pop()
           @emit("line", chunk) for chunk in parts

-  constructor:(@filename, @separator=/[\r]{0,1}\n/, @fsWatchOptions = {}, @frombeginning=false) ->
+  constructor:(@filename, @separator=/[\r]{0,1}\n/, @fsWatchOptions = {}, @fromBeginning=false) ->
     @buffer = ''
     @internalDispatcher = new events.EventEmitter()
     @queue = []
@@ -30,7 +30,7 @@
     stats =  fs.statSync(@filename)
     @internalDispatcher.on 'next',=>
       @readBlock()
-    @pos = if @frombeginning then 0 else stats.size
+    @pos = if @fromBeginning then 0 else stats.size
     @watch()

@@ -66,4 +66,4 @@
     @queue = []

-exports.Tail = Tail
+exports.Tail = Tail


:memo: doc parameters in task.coffee

:memo: doc parameters in task.coffee

--- src/task.coffee
+++ src/task.coffee
@@ -24,11 +24,9 @@

   # Public: A helper method to easily launch and run a task once.
   #
-  # * taskPath:
-  #   The path to the Coffeescript/Javascript file which exports a single
-  #   function to execute.
-  # * args:
-  #   The Array of arguments to pass to the exported function.
+  # taskPath - The {String} path to the CoffeeScript/JavaScript file which
+  #            exports a single {Function} to execute.
+  # args     - The arguments to pass to the exported function.
   @once: (taskPath, args...) ->
     task = new Task(taskPath)
     task.once 'task:completed', -> task.terminate()
@@ -45,9 +43,8 @@

   # Public: Creates a task.
   #
-  # * taskPath:
-  #   The path to the Coffeescript/Javascript file that exports a single
-  #   function to execute.
+  # taskPath - The {String} path to the CoffeeScript/JavaScript file that
+  #            exports a single {Function} to execute.
   constructor: (taskPath) ->
     coffeeCacheRequire = "require('#{require.resolve('./coffee-cache')}').register();"
     coffeeScriptRequire = "require('#{require.resolve('coffee-script')}').register();"
@@ -81,21 +78,21 @@

   # Public: Starts the task.
   #
-  # * args:
-  #   The Array of arguments to pass to the function exported by the script. If
-  #   the last argument is a function, its removed from the array and called
-  #   upon completion (and replaces the complete function on the task instance).
-  start: (args...) ->
+  # args - The arguments to pass to the function exported by this task's script.
+  # callback - An optional {Function} to call when the task completes.
+  start: (args..., callback) ->
     throw new Error("Cannot start terminated process") unless @childProcess?

     @handleEvents()
-    @callback = args.pop() if _.isFunction(args[args.length - 1])
+    if _.isFunction(callback)
+      @callback = callback
+    else
+      args = arguments
     @send({event: 'start', args})

   # Public: Send message to the task.
   #
-  # * message:
-  #   The message to send
+  # message - The message to send to the task.
   send: (message) ->
     throw new Error("Cannot send message to terminated process") unless @childProcess?
     @childProcess.send(message)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text.coffee
+++ src/text.coffee
@@ -9,7 +9,7 @@

   self.bind "update", ->
     I.y -= 1
-    log
+    log I.age /
     I.alpha = 1 - (I.age / I.duration)

   self.unbind "draw"


More function conversion

More function conversion

--- resources/assets/coffee/react/beatmapset-page/score.coffee
+++ resources/assets/coffee/react/beatmapset-page/score.coffee
@@ -19,40 +19,37 @@
 {div, a} = React.DOM
 el = React.createElement

-BeatmapsetPage.Score = React.createClass
-  mixins: [React.addons.PureRenderMixin]
+BeatmapsetPage.Score = (props) ->
+  hits = Hits.generate score: props.score, playmode: props.playmode

-  render: ->
-    hits = Hits.generate score: @props.score, playmode: @props.playmode
+  div className: 'beatmapset-score',
+    div className: 'beatmapset-score__element beatmapset-score__element--position',
+      "##{props.position}"

-    div className: 'beatmapset-score',
-      div className: 'beatmapset-score__element beatmapset-score__element--position',
-        "##{@props.position}"
+    div className: 'beatmapset-score__element beatmapset-score__element--flag',
+      if props.score.user.country_code
+        el FlagCountry,
+          country: props.countries[props.score.user.country_code]
+          classModifiers: ['scoreboard']

-      div className: 'beatmapset-score__element beatmapset-score__element--flag',
-        if @props.score.user.country_code
-          el FlagCountry,
-            country: @props.countries[@props.score.user.country_code]
-            classModifiers: ['scoreboard']
+    div className: 'beatmapset-score__element beatmapset-score__element--player',
+      a
+        href: laroute.route 'users.show', user: props.score.user.id
+        props.score.user.username

-      div className: 'beatmapset-score__element beatmapset-score__element--player',
-        a
-          href: laroute.route 'users.show', user: @props.score.user.id
-          @props.score.user.username
+    div className: 'beatmapset-score__element beatmapset-score__element--mods',
+      el Mods,
+        mods: props.score.mods
+        modifiers: ['small', 'reversed']

-      div className: 'beatmapset-score__element beatmapset-score__element--mods',
-        el Mods,
-          mods: @props.score.mods
-          modifiers: ['small', 'reversed']
+    div className: 'beatmapset-score__element beatmapset-score__element--rank',
+      div className: "badge-rank badge-rank--#{props.score.rank}"

-      div className: 'beatmapset-score__element beatmapset-score__element--rank',
-        div className: "badge-rank badge-rank--#{@props.score.rank}"
+    div className: 'beatmapset-score__stat beatmapset-score__stat--score',
+      props.score.score.toLocaleString()

-      div className: 'beatmapset-score__stat beatmapset-score__stat--score',
-        @props.score.score.toLocaleString()
+    div className: 'beatmapset-score__stat beatmapset-score__stat--accuracy hidden-xs',
+      "#{_.round props.score.accuracy * 100, 2}%"

-      div className: 'beatmapset-score__stat beatmapset-score__stat--accuracy hidden-xs',
-        "#{_.round @props.score.accuracy * 100, 2}%"
-
-      div className: 'beatmapset-score__stat beatmapset-score__stat--hits hidden-xs',
-        hits.values
+    div className: 'beatmapset-score__stat beatmapset-score__stat--hits hidden-xs',
+      hits.values


Environments: Added missing scheme to createStack method of JStack.

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -27,7 +27,7 @@
         getStack      :
           (signature Function)
         createStack   :
-          (signature Function)
+          (signature Object, Function)
         getStacks     :
           (signature Function)
       instance        :


JStack: config type is Object

JStack: config type is Object

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -72,7 +72,7 @@
       machines           : [ ObjectId ]
       extras             : [ ObjectId ]

-      config             : String
+      config             : Object

       meta               : require 'bongo/bundles/meta'


Smooth shading in engine sprite.

Smooth shading in engine sprite.

--- packages/landsofillusions-assets/engine/sprite/sprite.coffee
+++ packages/landsofillusions-assets/engine/sprite/sprite.coffee
@@ -271,49 +271,8 @@

               shadedColor.add _lightColor

-            if palette
-              # Find the nearest color from the palette to represent the shaded color.
-              bestColor = null
-              secondBestColor = null
-              bestColorDistance = Number.POSITIVE_INFINITY
-              secondBestColorDistance = Number.POSITIVE_INFINITY
-
-              # If we got this color from a palette ramp, we should shade only withing it.
-              # If it was done as a direct color, we'll match it to the whole palette space.
-              ramps = if paletteColor then [palette.ramps[paletteColor.ramp]] else palette.ramps
-
-              for ramp in ramps
-                for shade in ramp.shades
-                  distance = Math.pow(shade.r - shadedColor.r, 2) + Math.pow(shade.g - shadedColor.g, 2) + Math.pow(shade.b - shadedColor.b, 2)
-
-                  if distance < bestColorDistance
-                    secondBestColor = bestColor
-                    secondBestColorDistance = bestColorDistance
-                    bestColor = shade
-                    bestColorDistance = distance
-
-                  else if distance < secondBestColorDistance
-                    secondBestColor = shade
-                    secondBestColorDistance = distance
-
-              destinationColor = bestColor
-
-              # Apply dithering.
-
-              ditherPercentage = 2 * bestColorDistance / (bestColorDistance + secondBestColorDistance)
-
-              if ditherPercentage > 1 - (paletteColor?.dither or 0)
-                if Math.abs(pixel.x % 2) + Math.abs(pixel.y % 2) is 1
-                  destinationColor = secondBestColor
-
-              ### Smooth shading routine
-              #closest = THREE.Color.fromObject bestColor
-              #farthest = THREE.Color.fromObject secondBestColor
-
-              #blendFactor = bestColorDistance / (bestColorDistance + secondBestColorDistance)
-
-              #destinationColor = closest.lerp(farthest, blendFactor)
-              ###
+            if palette and paletteColor
+              destinationColor = @_boundColorToPaletteRamp pixel, shadedColor, palette.ramps[paletteColor.ramp], paletteColor.dither, false

             else
               destinationColor = shadedColor
@@ -331,3 +290,67 @@
           @_imageData.data[pixelIndex + 3] = (destinationColor.a or 1) * 255

     @_canvas.putFullImageData @_imageData
+
+  _boundColorToPaletteRamp: (pixel, color, ramp, dither, smoothShading) ->
+    # Find the nearest color from the palette to represent the shaded color.
+    bestColor = null
+    bestColorDistance = Number.POSITIVE_INFINITY
+
+    passedZero = false
+    earlierColor = null
+    laterColor = null
+    blendFactor = 0
+
+    previousColor = null
+    previousSignedDistance = 0
+
+    for shade, shadeIndex in ramp.shades
+      # Measure distance to color.
+      difference =
+        r: shade.r - color.r
+        g: shade.g - color.g
+        b: shade.b - color.b
+
+      signedDistance = difference.r + difference.g + difference.b
+      distance = Math.abs(difference.r) + Math.abs(difference.g) + Math.abs(difference.b)
+
+      unless shadeIndex
+        # Set initial values in first loop iteration.
+        bestColor = shade
+        bestColorDistance = distance
+
+      else
+        # See if we've crossed zero distance, which means our target
+        # shaded color is between the previous and current shade.
+        if previousSignedDistance < 0 and signedDistance >= 0 or previousSignedDistance >= 0 and signedDistance < 0
+          passedZero = true
+          earlierColor = previousColor
+          laterColor = shade
+          blendFactor = Math.abs(previousSignedDistance) / Math.abs(signedDistance - previousSignedDistance)
+
+        if distance < bestColorDistance
+          bestColor = shade
+          bestColorDistance = distance
+
+        # Note: We have to make sure the distance increased since there could be two of the same colors in the palette.
+        else if distance > bestColorDistance
+          # We have increased the distance, which means we're moving away from the best color and can safely quit.
+          break
+
+      previousSignedDistance = signedDistance
+      previousColor = shade
+
+    boundColor = bestColor
+
+    # Apply dithering.
+    if Math.abs(0.5 - blendFactor) < dither / 2.0
+      if Math.abs(pixel.x % 2) + Math.abs(pixel.y % 2) is 1
+        boundColor = laterColor
+
+      else
+        boundColor = earlierColor
+
+    else if smoothShading and passedZero
+      boundColor = _color.copy(earlierColor).lerp(laterColor, blendFactor)
+
+    boundColor


Support for erase material.

Support for erase material.

--- packages/landsofillusions-assets/engine/sprite/sprite.coffee
+++ packages/landsofillusions-assets/engine/sprite/sprite.coffee
@@ -74,9 +74,16 @@
         depthPixelIndex = x + y * @_canvas.width
         pixelIndex = depthPixelIndex * 4

-        # Cull by depth.
-        z = layerOrigin.z + (pixel.z or 0)
-        continue if z < @_depthBuffer[depthPixelIndex]
+        # Allow a special material called 'erase' to delete pixels.
+        erase = spriteData.materials?[pixel.materialIndex]?.name is 'erase'
+
+        if erase
+          z = Number.NEGATIVE_INFINITY
+
+        else
+          # Cull by depth.
+          z = layerOrigin.z + (pixel.z or 0)
+          continue if z < @_depthBuffer[depthPixelIndex]

         # Update depth buffer.
         @_depthBuffer[depthPixelIndex] = z
@@ -240,9 +247,13 @@
           else
             destinationColor = sourceColor

-        @_imageData.data[pixelIndex] = destinationColor.r * 255
-        @_imageData.data[pixelIndex + 1] = destinationColor.g * 255
-        @_imageData.data[pixelIndex + 2] = destinationColor.b * 255
-        @_imageData.data[pixelIndex + 3] = (destinationColor.a or 1) * 255
+        if erase
+          @_imageData.data[pixelIndex + 3] = 0
+
+        else
+          @_imageData.data[pixelIndex] = destinationColor.r * 255
+          @_imageData.data[pixelIndex + 1] = destinationColor.g * 255
+          @_imageData.data[pixelIndex + 2] = destinationColor.b * 255
+          @_imageData.data[pixelIndex + 3] = (destinationColor.a or 1) * 255

     @_context.putImageData @_imageData, 0, 0


Slider: Fix indentation

Slider: Fix indentation

--- spec/javascripts/slider_spec.coffee
+++ spec/javascripts/slider_spec.coffee
@@ -128,85 +128,85 @@

       (expect @slider.handle_arrow_event).not.toHaveBeenCalledWith 'right'

-   it 'should enable both buttons if we can scroll more', ->
-     ($ '#slider .right_arrow').click()
-
-     (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
-     (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
-
-   it 'should disable the right button and enable the left one if the right was clicked', ->
-     ($ '#slider .right_arrow').click()
-     ($ '#slider .right_arrow').click()
-
-     (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
-     (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
-
-   describe "with different display/step", ->
-     beforeEach ->
-       jasmine.getFixtures().cleanUp()
-       loadFixtures 'slider.html'
-
-     describe "display: 1, step: 1", ->
-       beforeEach ->
-         @options.display = 1
-         @options.step = 1
-
-         @slider = new Slider @options
-
-       it 'should enable both buttons if we can scroll more', ->
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-
-         (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
-         (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
-
-       it 'should disable the right button and enable the left one if the right was clicked', ->
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-
-         (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
-         (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
-
-     describe "display: 2, step: 1", ->
-       beforeEach ->
-         @options.display = 2
-         @options.step = 1
-
-         @slider = new Slider @options
-
-       it 'should enable both buttons if we can scroll more', ->
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-
-         (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
-         (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
-
-       it 'should disable the right button and enable the left one if the right was clicked', ->
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-         ($ '#slider .right_arrow').click()
-
-         (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
-         (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+    it 'should enable both buttons if we can scroll more', ->
+      ($ '#slider .right_arrow').click()
+
+      (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
+      (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+
+    it 'should disable the right button and enable the left one if the right was clicked', ->
+      ($ '#slider .right_arrow').click()
+      ($ '#slider .right_arrow').click()
+
+      (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
+      (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+
+    describe "with different display/step", ->
+      beforeEach ->
+        jasmine.getFixtures().cleanUp()
+        loadFixtures 'slider.html'
+
+      describe "display: 1, step: 1", ->
+        beforeEach ->
+          @options.display = 1
+          @options.step = 1
+
+          @slider = new Slider @options
+
+        it 'should enable both buttons if we can scroll more', ->
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+
+          (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
+          (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+
+        it 'should disable the right button and enable the left one if the right was clicked', ->
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+
+          (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
+          (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+
+      describe "display: 2, step: 1", ->
+        beforeEach ->
+          @options.display = 2
+          @options.step = 1
+
+          @slider = new Slider @options
+
+        it 'should enable both buttons if we can scroll more', ->
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+
+          (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeFalsy()
+          (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()
+
+        it 'should disable the right button and enable the left one if the right was clicked', ->
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+          ($ '#slider .right_arrow').click()
+
+          (expect ($ '#slider .right_arrow').hasClass 'disabled').toBeTruthy()
+          (expect ($ '#slider .left_arrow').hasClass 'disabled').toBeFalsy()

   describe "Step", ->
     beforeEach ->


Fixed the bug where ResponsiveRestorableDisplay appeared out of position sometimes

--- scripts/coffee/lib/film/ResponsiveRestorableDisplay.coffee
+++ scripts/coffee/lib/film/ResponsiveRestorableDisplay.coffee
@@ -25,7 +25,13 @@
 		@view = El '.film-display-view.responsive'
 		.inside @el

-		window.addEventListener 'resize', => do @_layout
+		@_relayoutTimeout = -1
+
+		window.addEventListener 'resize', => do @_scheduleToRelayout
+
+		window.addEventListener 'load', => do @_scheduleToRelayout
+
+		do @_scheduleToRelayout

 		@restore no

@@ -135,4 +141,18 @@

 		else

-			@fullscreen animated+			@fullscreen animated
+
+	_scheduleToRelayout: ->
+
+		if @_relayoutTimeout is -1
+
+			clearTimeout @_relayoutTimeout
+
+			@_relayoutTimeout = -1
+
+		@_relayoutTimeout = setTimeout =>
+
+			do @_layout
+
+		, 200


新增从group中移除元素

新增从group中移除元素

--- core/model/User.coffee
+++ core/model/User.coffee
@@ -32,7 +32,7 @@
   # @group 可以是数组，也可以是字符串，但是必须在['admin','user','trial']中
   # @callback 第一个参数是err,第二个参数是添加分组后的model
   # 用法：
-  #     user.addToGroup ['admin','users'],(err,result)->
+  #     user.addToGroup ['admin','user'],(err,result)->
   #       console.log result
   # 或
   #     user.addToGroup 'admin',(err,result)->
@@ -40,8 +40,20 @@
   addToGroup: (group,callback) ->
     group = [].push group if not _.isArray group
     for i in group
-      throw 'bad group' if i not in @constructor.validateData['group']
+      throw 'unknown group' if i not in @constructor.validateData['group']
     @update $addToSet:
       group:
         $each:group
+    ,callback
+  #从分组中移除
+  # @group [string]，必须在['admin','user','trial']中
+  # @callback 第一个参数是err,第二个参数是移除分组后的model
+  # 用法：
+  #     user.removeFromGroup 'user',(err,result)->
+  #       console.log result
+  removeFromGroup: (group,callback) ->
+    throw 'group must be string' if not _.isString group
+    throw 'unknown group' if group not in @constructor.validateData['group']
+    @update $pull:
+      group: group
     ,callback


Fixed not checking integer correctly.

Fixed not checking integer correctly.

--- src/util.coffee
+++ src/util.coffee
@@ -87,7 +87,7 @@
       if output[key] in ["true", "false"]
         output[key] = output[key] is "true"
       # Convert to integer or numbers from strings
-      else if +output[key] isnt NaN
+      else if not isNaN +output[key]
         output[key] = +output[key]

     return output


use heredocs in tests for html fragments

--- test/test.coffee
+++ test/test.coffee
@@ -8,22 +8,31 @@
   equal "ws://localhost:28080", Cable.boundConsumer.url,

 test "it should create subscritions for data-cable-subscribe attributes in the dom", ->
-  withContent("

")
+  withContent """
+

+  """

   equal Cable.boundConsumer.subscriptions.findAll("{\"channel\":\"ChatChannel\"}").length, 1

-  withContent("")
+  withContent ""
   equal Cable.boundConsumer.subscriptions.findAll("{\"channel\":\"ChatChannel\"}").length, 0

 test "it should trigger events on the elements for messages received through the channel", ->
-  withContent("

")
+  withContent """
+

+

+  """
   $('#event-test').on "cable:received", (e, data)->
     deepEqual data, {"message": "Hello"}

   Cable.boundConsumer.subscriptions.notify("{\"channel\":\"ChatChannel\"}","received", {"message": "Hello"})

 test "it should perform actions on the elements for messages received through the channel", ->
-  withContent("
Action
")
+  withContent """
+

+      Action
+

+  """

   subscription = Cable.boundConsumer.subscriptions.findAll("{\"channel\":\"ChatChannel\"}")[0]
   subscription.perform = (action, data)->


Fix test suite after project structure refactor

--- test/test.coffee
+++ test/test.coffee
@@ -6,7 +6,7 @@
 requirejs.config nodeRequire: require
 brain = new (requirejs 'dist/lib/geekywalletlib.js')

-parser = peg.buildParser fs.readFileSync 'grammar.peg', 'utf8'
+parser = peg.buildParser fs.readFileSync 'lib/syntax/grammar.peg', 'utf8'
 wallet = fs.readFileSync 'examples/plain.wallet', 'utf8'
 lines = null


making collision detection default + avoiding larger functions in the default conf file

--- app/scripts/alchemy/defaultConf.coffee
+++ app/scripts/alchemy/defaultConf.coffee
@@ -33,15 +33,11 @@
                                 "#f800df", "#ff8d8f",
                                 "#ffcd00", "#184fff",
                                 "#ff7e00"])
+    collisionDetection: true
     fixNodes: false
     fixRootNodes: false
     forceLocked: true
-    linkDistance: (edge, k) ->
-        if alchemy.conf.cluster
-            if (edge.source.node_type or edge.target.node_type) is 'root' then 300
-            if edge.source.cluster is edge.target.cluster then 10 else 600
-        else
-            10 / (k * 5)
+    linkDistance: alchemy.layout.linkDistancefn
     nodePositions: null # not currently implemented

     # Editing


Add a help link to the account-merge email in case of error

--- services/web/app/coffee/Features/Email/EmailBuilder.coffee
+++ services/web/app/coffee/Features/Email/EmailBuilder.coffee
@@ -43,7 +43,12 @@
 templates.accountMergeToOverleafAddress = CTAEmailTemplate({
 	subject: () -> "Confirm Account Merge - #{settings.appName}"
 	title: () -> "Confirm Account Merge"
-	message: () -> "To merge your ShareLaTeX and Overleaf accounts, click the link below."
+	message: () ->
+		"""
+			To merge your ShareLaTeX and Overleaf accounts, click the link below.
+			If you think you have received this message in error,
+			please contact us at https://www.overleaf.com/contact
+		"""
 	ctaText: () -> "Confirm Account Merge"
 	ctaURL: (opts) -> opts.tokenLinkUrl
 })


added custom footer in email option

added custom footer in email option

--- services/web/app/coffee/Features/Email/EmailBuilder.coffee
+++ services/web/app/coffee/Features/Email/EmailBuilder.coffee
@@ -2,6 +2,8 @@
 PersonalEmailLayout = require("./Layouts/PersonalEmailLayout")
 NotificationEmailLayout = require("./Layouts/NotificationEmailLayout")
 settings = require("settings-sharelatex")
+
+

 templates = {}

@@ -114,6 +116,8 @@
 		template = templates[templateName]
 		opts.siteUrl = settings.siteUrl
 		opts.body = template.compiledTemplate(opts)
+		if settings.email?.templates?.customFooter?
+			opts.body += settings.email?.templates?.customFooter
 		return {
 			subject : template.subject(opts)
 			html: template.layout(opts)


[Unusable] add a few categories and changed a few api

--- views/filter_selector.cjsx
+++ views/filter_selector.cjsx
@@ -1,6 +1,16 @@
 {React, ReactDOM} = window
 {Input, Button, Table, Well, Panel, ListGroup, ListGroupItem, Alert} = ReactBootstrap

+AlertDismissable = React.createClass
+  render: ->
+    if @props.show
+      options = @props.options
+      <Alert onDismiss={@handleAlertDismiss} {...options}>
+        @props.children
+      </Alert>
+
+  handleAlertDismiss: ->
+    @props.onDismiss?()

 FilterSelector = React.createClass
   getInitialState: ->
@@ -35,34 +45,69 @@
                     title: 'The map has not been cleared'
                     value: true
               '_id':
+                title: 'Map number'
                 func: (path, value, record) ->
                   record.map?.id == value
-                title: 'Map number'
                 textFunc: (value) ->
                   "On map #{value}"
                 options:
                   placeholder: 'Enter the map number here (e.g. 2-3, 32-5)'
+              '_rank':
+                title: 'Map rank'
+                func: (path, value, record) ->
+                  if value == 0
+                    !record.map?.rank?
+                  else
+                    record.map?.rank == value
+                textFunc: (value) ->
+                  if value == 0
+                    "On a map with no rank"
+                  else
+                    "On a map of rank "+['', 'Easy', 'Medium', 'Hard'][value]
+                sub:
+                  '_0':
+                    title: 'No rank'
+                    value: 0
+                  '_1':
+                    title: 'Easy'
+                    value: 1
+                  '_2':
+                    title: 'Medium'
+                    value: 2
+                  '_3':
+                    title: 'Hard'
+                    value: 3
           '_ship':
             title: 'Ship'
-            preprocess: (path, value) ->
-              isWith: path[path.length-1] == '_with'
-              shipId: value
-            func: (path, value, record) ->
-              (record.fleet.concat(record.fleet2 || []).filter(
-                (sh) -> sh.shipId.toString() == value.shipId.toString())
-              .length != 0) == value.isWith
-            textFunc: (value) ->
-              _out = if value.isWith then '' else 'out'
-              "With#{_out} ship #{value.shipId}"
             sub:
-              '_with':
-                title: 'With ship'
+              '_name':
+                title: 'By ship name'
+                postprocess: (path, value) ->
+                  text: value
+                  regex: new RegExp(value)
+                func: (path, value, record) ->
+                  console.log 'shipname value', value
+                  record.fleet.concat(record.fleet2 || []).filter(
+                    (sh) -> value.regex.test $ships[sh.shipId]?.api_name)
+                  .length != 0
+                textFunc: (value) ->
+                  "With ship #{value.text}"
                 options:
-                  placeholder: 'Enter the ship id here'
-              '_without':
-                title: 'Without ship'
+                  placeholder: 'Enter the ship name here. (Javascript regex is supported.)'
+              '_id':
+                title: 'By ship id'
+                testError: (path, value) ->
+                  if !_ships[value]?
+                    'You have no ship with id #{value}'
+                func: (path, value, record) ->
+                  record.fleet.concat(record.fleet2 || []).filter(
+                    (sh) -> sh.id?.toString() == value.toString())
+                  .length != 0
+                textFunc: (value) ->
+                  name = _ships[value].api_name
+                  "With ship #{name} (##{value})"
                 options:
-                  placeholder: 'Enter the ship id here'
+                  placeholder: 'Enter the ship id here. You can find it in Ship Girls Info at the first column.'

   generateFilterFunc_: (filterList) ->
     if filterList.length == 0


HACK: disabled too-large-cache-prevention 1) it was triggering when it shouldn't 2) it caused errors rather than just not-caching

--- source/Art/Engine/Core/Element.coffee
+++ source/Art/Engine/Core/Element.coffee
@@ -939,7 +939,8 @@
     elementSpaceCacheArea = drawArea.mul(pixelsPerPoint)

     # don't cache if too big
-    return unless @getNeedsStagingBitmap() || elementSpaceCacheArea.size.area <= 2048 * 1536
+    # TODO: this doesn't work; it causes errors to abort caching at this point
+    # return if elementSpaceCacheArea.size.area >= 2048 * 1536 && !@getNeedsStagingBitmap()

     # re-use existing bitmap, if possible
     d2eMatrix = Matrix.translateXY(-drawArea.x, -drawArea.y).scale(pixelsPerPoint).inv


add a mailbox field to attachments

add a mailbox field to attachments

--- db/schema.coffee
+++ db/schema.coffee
@@ -43,6 +43,7 @@
     property 'length', Number
     property 'checksum'
     property 'content', Text
+    property 'mailbox',

 Mail.hasMany(Attachment, {as: 'attachments',foreignKey: 'mail_id'})


Added SizeMe.pluginVersion to SizeMe-object and sending code, and added some preliminary functionality to read the value and possibly block illegal versions from making requests.

Removed some strange, totally unnecessary block logging tracking status

--- src/sizeme-api.coffee
+++ src/sizeme-api.coffee
@@ -12,6 +12,7 @@
   ###
   @contextAddress = "https://www.sizeme.com"
   @gaTrackingID = "UA-40735596-1"
+  @pluginVersion = "UNKNOWN"

   gaEnabled = false
   ga ->
@@ -62,11 +63,15 @@
       xhr.setRequestHeader(
         "X-Analytics-Enabled", "true"
       ) if gaEnabled
+      xhr.setRequestHeader(
+        "X-Sizeme-Pluginversion", SizeMe.pluginVersion
+      )
     else if XDomainRequest?
       xhr = new XDomainRequest()
       url = "#{url}?_tm=#{new Date().getTime()}"
       url = "#{url}&authToken=#{_authToken}" if _authToken?
       url = "#{url}&analyticsEnabled=true" if gaEnabled
+      url = "#{url}&pluginVersion=#{SizeMe.pluginVersion}"
       xhr.onload = -> callback(xhr)
       xhr.onerror = -> errorCallback(xhr)
       xhr.open(method, url, true)


Fix for routing to account from URL.

Fix for routing to account from URL.

--- packages/landsofillusions/components/account/account.coffee
+++ packages/landsofillusions/components/account/account.coffee
@@ -38,7 +38,7 @@

     LOI.Adventure.registerDirectRoute "#{@constructor.url()}/*", =>
       # Show the dialog if we need to.
-      @show() if @ in LOI.adventure.modalDialogs()
+      @show() unless _.find LOI.adventure.modalDialogs(), (modalDialog) => modalDialog.dialog is @

       return unless pageUrl = FlowRouter.getParam 'parameter2'


Fix sound of new room

Fix sound of new room

--- packages/rocketchat-ui-sidenav/side-nav/chatRoomItem.coffee
+++ packages/rocketchat-ui-sidenav/side-nav/chatRoomItem.coffee
@@ -35,7 +35,7 @@
 		return RocketChat.roomTypes.getRouteLink @t, @

 Template.chatRoomItem.rendered = ->
-	if not (FlowRouter.getParam('_id')? and FlowRouter.getParam('_id') is this.data.rid) and not this.data.ls
+	if not (FlowRouter.getParam('_id')? and FlowRouter.getParam('_id') is this.data.rid) and not this.data.ls and this.data.alert is true
 		KonchatNotification.newRoom(this.data.rid)

 Template.chatRoomItem.events
@@ -54,8 +54,8 @@
 			when this.t == 'c' then 'Hide_Room_Warning'
 			when this.t == 'p' then 'Hide_Group_Warning'
 			when this.t == 'd' then 'Hide_Private_Warning'
-
-
+
+
 		swal {
 			title: t('Are_you_sure')
 			text: t(warnText, name)


fixed skin url

fixed skin url

--- source/javascripts/application.coffee
+++ source/javascripts/application.coffee
@@ -3,54 +3,54 @@
 getStylesheet = ->
   currentTime = (new Date).getHours()
   if 0 <= currentTime and currentTime < 1
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c00.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c00.css\' type=\'text/css\'>'
   if 1 <= currentTime and currentTime < 2
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c01.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c01.css\' type=\'text/css\'>'
   if 2 <= currentTime and currentTime < 3
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c02.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c02.css\' type=\'text/css\'>'
   if 3 <= currentTime and currentTime < 4
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c03.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c03.css\' type=\'text/css\'>'
   if 4 <= currentTime and currentTime < 5
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c04.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c04.css\' type=\'text/css\'>'
   if 5 <= currentTime and currentTime < 6
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c05.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c05.css\' type=\'text/css\'>'
   if 6 <= currentTime and currentTime < 7
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c06.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c06.css\' type=\'text/css\'>'
   if 7 <= currentTime and currentTime < 8
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c07.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c07.css\' type=\'text/css\'>'
   if 8 <= currentTime and currentTime < 9
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c08.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c08.css\' type=\'text/css\'>'
   if 9 <= currentTime and currentTime < 10
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c09.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c09.css\' type=\'text/css\'>'
   if 10 <= currentTime and currentTime < 11
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c10.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c10.css\' type=\'text/css\'>'
   if 11 <= currentTime and currentTime < 12
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c11.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c11.css\' type=\'text/css\'>'
   if 12 <= currentTime and currentTime < 13
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c12.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c12.css\' type=\'text/css\'>'
   if 13 <= currentTime and currentTime < 14
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c13.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c13.css\' type=\'text/css\'>'
   if 14 <= currentTime and currentTime < 15
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c14.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c14.css\' type=\'text/css\'>'
   if 15 <= currentTime and currentTime < 16
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c15.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c15.css\' type=\'text/css\'>'
   if 16 <= currentTime and currentTime < 17
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c16.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c16.css\' type=\'text/css\'>'
   if 17 <= currentTime and currentTime < 18
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c17.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c17.css\' type=\'text/css\'>'
   if 18 <= currentTime and currentTime < 19
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c18.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c18.css\' type=\'text/css\'>'
   if 19 <= currentTime and currentTime < 20
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c19.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c19.css\' type=\'text/css\'>'
   if 20 <= currentTime and currentTime < 21
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c20.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c20.css\' type=\'text/css\'>'
   if 21 <= currentTime and currentTime < 22
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c21.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c21.css\' type=\'text/css\'>'
   if 22 <= currentTime and currentTime < 23
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c22.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c22.css\' type=\'text/css\'>'
   if 23 <= currentTime and currentTime < 24
-    document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c23.css\' type=\'text/css\'>'
+    document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c23.css\' type=\'text/css\'>'
   # if 24 <= currentTime and currentTime < 25
-  #   document.write '<link rel=\'stylesheet\' href=\'http://localhost:4567/stylesheets/c24.css\' type=\'text/css\'>'
+  #   document.write '<link rel=\'stylesheet\' href=\'http://mariusnedelcu.com/stylesheets/c24.css\' type=\'text/css\'>'
   return
 getStylesheet()


Update names.coffee

Update names.coffee

--- app/lib/world/names.coffee
+++ app/lib/world/names.coffee
@@ -107,6 +107,9 @@
   "Archer M": [
     "Brian"
     "Cole"
+    "Roman"
+    "Hunter"
+    "Simon"
   ]
   "Ogre Munchkin M": [
     "Brack"


added klondike as name for Polar Bear Pet #53

--- app/lib/world/names.coffee
+++ app/lib/world/names.coffee
@@ -155,7 +155,7 @@
   'Frog Pet': ['Hypnotoad']
   'Griffin Pet': ['']
   'Pugicorn Pet': ['']
-  'Polar Bear Pet': ['']
+  'Polar Bear Pet': ['Klondike']
   'Wolf Pet': ['']
   'Horse': [
     # Animal


+ Created function for generating a reproducable seed out of user code - as using JSON.stringify is not garanteed to be the same on every browser for objects.

--- app/lib/world/world.coffee
+++ app/lib/world/world.coffee
@@ -31,15 +31,13 @@
     @systemMap = {}
     @scriptNotes = []
     # We want a seed thats not always 0 yet reproducable.
-    console.log "Seed: ", @getSeed()
     @rand = new Rand @getSeed()
     @frames = [new WorldFrame(@, 0)]

   getSeed: ->
-    return 0 unless @userCodeMap #TODO: When does this happen?
     #Packs all methods in all thangs in one big array, then sorts and joins them. Then returns a hash value of the result.
     @hashString((methods for thangID, methods of @userCodeMap).reduce(((ret, methods) ->
-      (method for methodID, method of methods).reduce(((ret, method) -> ret.push(method)), ret)
+      ret.push method for methodID, method of methods
     ), []).sort().join())

   getFrame: (frameIndex) ->
@@ -497,4 +495,4 @@

   #djb2 algorithm
   hashString: (str) ->
-    (str.charCodeAt i for i in [0...str]).reduce(((hash, char) -> ((hash << 5) + hash) + char), 5381) # hash * 33 + c
+    (str.charCodeAt i for i in [0...str.length]).reduce(((hash, char) -> ((hash << 5) + hash) + char), 5381) # hash * 33 + c


More style cleanup.

More style cleanup.

--- src/stashboard.coffee
+++ src/stashboard.coffee
@@ -30,66 +30,66 @@
   constructor: (@robot, cb) ->
     if process.env.HUBOT_STASHBOARD_URL? and process.env.HUBOT_STASHBOARD_TOKEN? and process.env.HUBOT_STASHBOARD_SECRET?
       @state =
-        oauthConsumerKey: 'anonymous'
-        oauthConsumerSecret: 'anonymous'
-        oauthToken: process.env.HUBOT_STASHBOARD_TOKEN
-        oauthTokenSecret: process.env.HUBOT_STASHBOARD_SECRET
-        @baseUrl = process.env.HUBOT_STASHBOARD_URL
+        oauth_consumer_key: 'anonymous'
+        oauth_consumer_secret: 'anonymous'
+        oauth_token: process.env.HUBOT_STASHBOARD_TOKEN
+        oauth_token_secret: process.env.HUBOT_STASHBOARD_SECRET
+      @baseUrl = process.env.HUBOT_STASHBOARD_URL
     else
       cb 'Please set environment variables.'

-    getStatusAll: (cb) ->
-      request.get @baseUrl + '/services', (error, data, response) =>
-        if err?
-          return cb('Unable to retrieve status. ERROR: ' + err)
+  getStatusAll: (cb) ->
+    request.get @baseUrl + '/services', (error, data, response) =>
+      if err?
+        return cb('Unable to retrieve status. ERROR: ' + err)

-        if data.statusCode != 200
-          return cb('Unable to retrieve status. HTTP: ' + data.statusCode)
+      if data.statusCode != 200
+        return cb('Unable to retrieve status. HTTP: ' + data.statusCode)

-        status = JSON.parse response
-        for service in status.services
-          serviceMsg = ''
-          switch service['current-event'].status.id
-            when 'down' then serviceMsg += @statusDown
-            when 'up' then serviceMsg += @statusUp
-            when 'warn' then serviceMsg += @statusWarn
-              serviceMsg += ' ' + service.name
-              cb(null, serviceMsg)
+      status = JSON.parse response
+      for service in status.services
+        serviceMsg = ''
+        switch service['current-event'].status.id
+          when 'down' then serviceMsg += @statusDown
+          when 'up' then serviceMsg += @statusUp
+          when 'warn' then serviceMsg += @statusWarn
+        serviceMsg += ' ' + service.name
+        cb(null, serviceMsg)

-    setStatus: (search_string, status, message, cb) ->
-      found = false
-      request.get @baseUrl + "/services", (error, data, response) =>
-        if response?
-          services = JSON.parse response
-          for service in services['services']
-            do (service) =>
-              if (service.id.search search_string.toLowerCase()) > -1
-                found = true
-                form = status: status, message: message
-                options = urllib.parse(@baseUrl + '/services/' + service.id + '/events')
-                options.url = options
-                options.method = 'POST'
-                headers = 'Authorization': oauth.makeAuthorizationHeader(@state, options)
-                request.post url: options.url, form: form, headers: headers, (error, response, body) =>
-                  if response.statusCode != 200
-                    return cb('Setting service failed! ' + response.statusCode + ' ' + response.body)
-                  return cb('Okay, service ' + service.name + ' marked as ' + status + ' due to ' + message)
-                unless found
-                  cb('Unable to find service service called: ' + search_string)
+  setStatus: (search_string, status, message, cb) ->
+    found = false
+    request.get @baseUrl + "/services", (error, data, response) ->
+      if response?
+        services = JSON.parse response
+        for service in services['services']
+          do (service) =>
+            if (service.id.search search_string.toLowerCase()) > -1
+              found = true
+              form = status: status, message: message
+              options = urllib.parse(@baseUrl + '/services/' + service.id + '/events')
+              options.url = options
+              options.method = 'POST'
+              headers = 'Authorization': oauth.makeAuthorizationHeader(@state, options)
+              request.post url: options.url, form: form, headers: headers, (error, response, body) ->
+                if response.statusCode != 200
+                  return cb('Setting service failed! ' + response.statusCode + ' ' + response.body)
+                return cb('Okay, service ' + service.name + ' marked as ' + status + ' due to ' + message)
+        unless found
+          cb('Unable to find service service called: ' + search_string)

 module.exports = (robot) ->

-    stashbot = new Stashbot robot, (err) ->
-      robot.send null, 'Stashbot init error: ' + err
+  stashbot = new Stashbot robot, (err) ->
+    robot.send null, 'Stashbot init error: ' + err

-    robot.respond /stashboard (status|sup|\?)/i, (msg) =>
-      msg.send 'Checking stashboard status ...'
-      stashbot.getStatusAll (err, status_msg) ->
-        unless err?
-          msg.send status_msg
-        else
-          msg.send err
+  robot.respond /stashboard (status|sup|\?)/i, (msg) ->
+    msg.send 'Checking stashboard status ...'
+    stashbot.getStatusAll (err, status_msg) ->
+      unless err?
+        msg.send status_msg
+      else
+        msg.send err

-    robot.respond /stashboard set (.*?) (.*?) (.*)/i, (msg) =>
-      stashbot.setStatus msg.match[1], msg.match[2], msg.match[3], (data) ->
-        msg.send data
+  robot.respond /stashboard set (.*?) (.*?) (.*)/i, (msg) ->
+    stashbot.setStatus msg.match[1], msg.match[2], msg.match[3], (data) ->
+      msg.send data


adds better error messages for some undefined situations

--- drivers/javascript/src/ast.coffee
+++ drivers/javascript/src/ast.coffee
@@ -124,7 +124,7 @@
                     datum.setType Datum.DatumType.R_STR
                     datum.setRStr @data
                 else
-                    throw new RqlDriverError "Unknown datum value `#{@data}`, did you forget a `return`?"
+                    throw new RqlDriverError "Cannot convert `#{@data}` to Datum."
         term = new Term
         term.setType Term.TermType.DATUM
         term.setDatum datum
@@ -167,7 +167,12 @@
 class RDBOp extends RDBVal
     constructor: (optargs, args...) ->
         self = super()
-        self.args = (rethinkdb.expr arg for arg in args)
+        self.args =
+            for arg,i in args
+                if arg isnt undefined
+                    rethinkdb.expr arg
+                else
+                    throw new RqlDriverError "Argument #{i} to #{@st || @mt} may not be `undefined`."
         self.optargs = translateOptargs(optargs)
         return self

@@ -560,6 +565,9 @@
             i++

         body = func(args...)
+        if body is undefined
+            throw new RqlDriverError "Annonymous function returned `undefined`. Did you forget a `return`?"
+
         argsArr = new MakeArray({}, argNums...)
         return super(optargs, argsArr, body)


pass file basename instead of full path or url

--- www/js/file.coffee
+++ www/js/file.coffee
@@ -1,4 +1,5 @@
 _ = require 'lodash'
+path = require 'path'

 angular
 	.module 'util.file', [
@@ -91,7 +92,7 @@
 						data = new FormData()
 						_.each opts.data, (value, key) ->
 							data.append key, value
-						data.append 'file', entry, entry.name
+						data.append 'file', entry, path.basename entry.name

 						# define header
 						xhr = new XMLHttpRequest()


Update hapi.coffee

Update hapi.coffee

--- demo/hapi.coffee
+++ demo/hapi.coffee
@@ -1,6 +1,6 @@
 #example server using hapi and prescript
 default const
-require :: hapi, redis, inert, consoleLogger as log
+require :: hapi, redis, inert, log

 server = new hapi.server(host: 'localhost', port: 8000)
 await server.register(inert) catch (err.registrationFailure)


Remove unneeded require

Remove unneeded require

--- src/packages/command-logger/src/command-logger.coffee
+++ src/packages/command-logger/src/command-logger.coffee
@@ -1,4 +1,4 @@
-{View, $$, $$$} = require 'space-pen'
+{View, $$$} = require 'space-pen'
 ScrollView = require 'scroll-view'
 $ = require 'jquery'
 _ = require 'underscore'


Workspace: Fixed terminal instantiation delay.

--- client/app/CommonViews/workspace/panes/terminalpane.coffee
+++ client/app/CommonViews/workspace/panes/terminalpane.coffee
@@ -3,7 +3,7 @@
   constructor: (options = {}, data) ->

     options.cssClass  = "terminal-pane"
-    options.delay    ?= if location.hostname is "localhost" then 10000 else 10000
+    options.delay    ?= if location.hostname is "localhost" then 100 else 10000

     super options, data


delete old cookie keys

delete old cookie keys

--- services/web/public/coffee/analytics/AbTestingManager.coffee
+++ services/web/public/coffee/analytics/AbTestingManager.coffee
@@ -2,8 +2,23 @@
 	"base"
 	"libs/md5"
 ], (App) ->
-
+	oldKeys = [
+		"sl_abt_multi_currency_editor_eu-eu"
+		"sl_abt_multi_currency_eu-eu"
+		"sl_abt_multi_currency_editor_eu-usd"
+		"sl_abt_multi_currency_eu-usd"
+		"sl_abt_trial_len_14d"
+		"sl_abt_trial_len_7d"
+		"sl_abt_trial_len_30d"
+		"sl_utt"
+		"sl_utt_trial_len"
+		"sl_utt_multi_currency"
+	]
+
 	App.factory "abTestManager", ($http, ipCookie) ->
+
+		_.each oldKeys, (oldKey)->
+			ipCookie.remove(oldKey)

 		_buildCookieKey = (testName, bucket)->
 			key = "sl_abt_#{testName}_#{bucket}"


Fix typo in error message.

Fix typo in error message.

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -92,7 +92,7 @@
       @emitter.emit("did-change-scroll-left", arguments...)

   initialize: (model, {@views, @config, @themes, @workspace, @assert, @styles, @grammars}, @autoHeight = true, @scrollPastEnd = true) ->
-    throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @views?
+    throw new Error("Must pass a views parameter when initializing TextEditorElements") unless @views?
     throw new Error("Must pass a config parameter when initializing TextEditorElements") unless @config?
     throw new Error("Must pass a themes parameter when initializing TextEditorElements") unless @themes?
     throw new Error("Must pass a workspace parameter when initializing TextEditorElements") unless @workspace?


open new links in new tabs

open new links in new tabs

--- www/src/app.coffee
+++ www/src/app.coffee
@@ -44,3 +44,9 @@
             url = $(event.currentTarget).attr("href").replace(/^\//, "")
             Backbone.history.navigate url,
                 trigger: true
+
+    $(document).on "click", "a[href^='h']", (event) ->
+        if not event.altKey and not event.ctrlKey and not event.metaKey and not event.shiftKey
+            event.preventDefault()
+            url = $(event.currentTarget).attr("href")
+            window.open url, '_blank'


Add flag that can disable plans page

Add flag that can disable plans page

--- services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee
+++ services/web/app/coffee/Features/Subscription/SubscriptionRouter.coffee
@@ -9,7 +9,8 @@
 	apply: (webRouter, privateApiRouter, publicApiRouter) ->
 		return unless Settings.enableSubscriptions

-		webRouter.get  '/user/subscription/plans',      SubscriptionController.plansPage
+		if !Settings.disablePlansPage
+			webRouter.get  '/user/subscription/plans', SubscriptionController.plansPage

 		webRouter.get  '/user/subscription',            AuthenticationController.requireLogin(), SubscriptionController.userSubscriptionPage


Remove old artifacts

Remove old artifacts

--- src/model.coffee
+++ src/model.coffee
@@ -1,9 +1,6 @@
 idCounter = 1

 class Model
-  @belongsTo: -> @prototype.belongsTo(arguments...)
-  @hasMany: -> @prototype.hasMany(arguments...)
-
   @identityMap: true

   @find: (id) -> Cache.get(this, id) or new this(id: id)


Fix bug

Fix bug

--- modules/ui.coffee
+++ modules/ui.coffee
@@ -66,6 +66,7 @@
       showNotification "Poi", "#{$("#kdock-name-#{i}").text()}建造完成" if missionTimer[i] == 1
     else
       $("#kdock-timer-#{i}").text ''
+      $("#kdock-#{i}-resttime").text ''

 exports.initConfig = ->
   # Update tab state
@@ -320,7 +321,7 @@
         $("#kdock-#{kdock.api_id}-open").text kdock.api_id
         $("#kdock-#{kdock.api_id}-name").text ships[kdock.api_created_ship_id].api_name
         dateNow = new Date(kdock.api_complete_time)
-
+
         month = dateNow.getMonth() + 1
         day = dateNow.getDate()
         hours = dateNow.getHours()


Cleanup

Cleanup

--- src/api/apps/users/test/model.test.coffee
+++ src/api/apps/users/test/model.test.coffee
@@ -7,7 +7,6 @@
 gravity = require('antigravity').server
 gravityFabricate = require('antigravity').fabricate
 express = require 'express'
-request = require 'superagent'

 app = express()
 app.get '/__gravity/api/v1/user/563d08e6275b247014000026', (req, res, next) ->


Clicking outside the inspector panel should close it. It should not automatically reopen.

[#106499762]

https://www.pivotaltracker.com/story/show/106499762

--- src/code/views/inspector-panel-view.coffee
+++ src/code/views/inspector-panel-view.coffee
@@ -107,6 +107,11 @@
     else if @props.link
       (LinkRelationInspectorView {link:@props.link, graphStore: @props.graphStore})

+  # 2015-12-09 NP: Deselection makes inpector panel hide http://bit.ly/1ORBBp2
+  componentWillReceiveProps: (nextProps) ->
+    unless (nextProps.node or nextProps.link)
+      @setState
+        nowShowing: null

   renderInspectorPanel: ->
     view = switch @state.nowShowing


Test comment.

Test comment.

--- tests.coffee
+++ tests.coffee
@@ -214,8 +214,7 @@
     Tracker.afterFlush ->
       afterFlushHasExecuted = true

-    # Create a new computation in this fiber. This will cause the global outstandingComputations
-    # to be incremented.
+    # Create a new computation in this fiber.
     Tracker.autorun ->
       # Inside the computation, we yield so other fibers may run. This will cause the
       # deferred flush to execute.


Removed TODOs from tests.

Removed TODOs from tests.

--- tests.coffee
+++ tests.coffee
@@ -1874,8 +1874,7 @@
   test.equal Second.Meta.fields.first.sourceCollection, Posts
   test.isUndefined Second.Meta.fields.first.targetCollection # Currently target collection is still undefined
   test.equal Second.Meta.fields.first.sourceDocument.Meta.collection, Posts
-  # TODO: Why this gets defined here?
-  test.equal Second.Meta.fields.first.targetDocument.Meta.collection, Posts # Now it gets defined
+  test.equal Second.Meta.fields.first.targetDocument.Meta.collection, Posts # Now it gets defined because First gets defined
   test.equal Second.Meta.fields.first.fields, []
   test.instanceOf Second.Meta.fields.second, Second._ReferenceField
   test.isFalse Second.Meta.fields.second.isArray
@@ -2013,8 +2012,7 @@
   test.equal Third.Meta.fields.first.sourceDocument, Third
   test.equal Third.Meta.fields.first.targetDocument, firstReferenceA
   test.equal Third.Meta.fields.first.sourceCollection, Posts
-  # TODO: Why this gets defined here?
-  test.equal Third.Meta.fields.first.targetCollection, Posts # Now it gets defined
+  test.equal Third.Meta.fields.first.targetCollection, Posts # Here it is already defined because First was defined at the time when Third got defined (after a delay)
   test.equal Third.Meta.fields.first.sourceDocument.Meta.collection, Posts
   test.equal Third.Meta.fields.first.targetDocument.Meta.collection, Posts
   test.equal Third.Meta.fields.first.fields, []


activity feed: add refresh button; removed commented out logs/methods

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -49,11 +49,10 @@
     # if @mainController.appIsReady then @putListeners()
     # else @mainController.on 'AppIsReady', => @putListeners()

-    #@status = KD.getSingleton "status"
-    #@status.on "reconnected", (conn)=>
-      #if conn && conn.reason is "internetDownForLongTime"
-      #then @refresh()
-      #else @fetchSomeActivities()
+    @status = KD.getSingleton "status"
+    @status.on "reconnected", (conn)=>
+      if conn && conn.reason is "internetDownForLongTime"
+      then @refresh()

   loadView:->
     @getView().feedWrapper.ready (controller)=>
@@ -119,11 +118,8 @@
     eventSuffix = "#{@getFeedFilter()}_#{@getActivityFilter()}"
     @off "activitiesFetched_#{eventSuffix}"
     @off "cacheFetched_#{eventSuffix}"
-    # log ">>>>>>>>>>>>>>>>>    clearPopulateActivityBindings", @_e
-
-
-  populateActivity:(options = {})->
-
+
+  populateActivity:(options = {}, callback)->
     return  if @isLoading

     @listController.showLazyLoader no
@@ -159,12 +155,16 @@
           reset()
           @extractCacheTimeStamps cache
           @listController.listActivitiesFromCache cache
+          callback? cache
+
         @fetchPublicActivities options
       else
         @once "followingFeedFetched_#{eventSuffix}", (activities)=>
           reset()
           @extractTeasersTimeStamps activities
           @listController.listActivities activities
+          callback? activities
+
         @fetchFollowingActivities options

       # log "------------------    populateActivity", @getActivityFilter(), @lastFrom
@@ -311,70 +311,17 @@
     return  if @isLoading

     @resetAll()
-
-  #populateActivityWithTimeout:->
-    #@populateActivity {},\
-      #KD.utils.getTimedOutCallbackOne
-        #name      : "populateActivity",
-        #onSuccess : -> KD.logToMixpanel "refresh activity feed success"
-        #onTimeout : @recover.bind this
-
-  #recover:->
-
-    #KD.logToMixpanel "activity feed render failed; recovering"
-
-    #@isLoading = no
-    #@status.reconnect()
-
-  ## Fetches activities that occured after the first entry in user feed,
-  ## used for minor disruptions.
-  #fetchSomeActivities:(options = {}) ->
-    #return if @isLoading
-    #@isLoading = yes
-
-    #lastItemCreatedAt = @listController.getLastItemTimeStamp()
-    #unless lastItemCreatedAt? or lastItemCreatedAt is ""
-      #@isLoading = no
-      #log "lastItemCreatedAt is empty"
-      ## if lastItemCreatedAt is null, we assume there are no entries
-      ## and refresh the entire feed
-      #@refresh()
-      #return
-
-    #selector       =
-      #createdAt    :
-        #$gt        : options.createdAt or lastItemCreatedAt
-      #type         : { $in : options.facets or @getFilter() }
-      #isLowQuality : { $ne : options.exempt or no }
-
-    #options       =
-      #limit       : 20
-      #sort        :
-        #createdAt : -1
-
-    #KD.remote.api.CActivity.some selector, options,\
-      #KD.utils.getTimedOutCallback (err, activities) =>
-        #if err then warn err
-        #else
-
-          #KD.logToMixpanel "refresh activity feed success"
-
-          ## FIXME: SY
-          ## if it is exact 20 there may be other items
-          ## put a separator and check for new items in between
-          #if activities.length is 20
-            #warn "put a separator in between new and old activities"
-
-          #@activitiesArrived activities.reverse()
-          #@isLoading = no
-      #, =>
-        #@isLoading = no
-        #log "fetchSomeActivities timeout reached"
-
-  #isExempt:(callback)->
-
-    #@appStorage.fetchStorage (storage) =>
-      #flags  = KD.whoami().globalFlags
-      #exempt = flags?.indexOf 'exempt'
-      #exempt = (exempt? and exempt > -1) or storage.getAt 'bucket.showLowQualityContent'
-      #callback exempt+    @populateActivityWithTimeout()
+
+  populateActivityWithTimeout:->
+    @populateActivity {},\
+      KD.utils.getTimedOutCallbackOne
+        name      : "populateActivity",
+        onSuccess : -> KD.logToMixpanel "refresh activity feed success"
+        onTimeout : @recover.bind this
+
+  recover:->
+    KD.logToMixpanel "activity feed render failed; recovering"
+
+    @isLoading = no
+    @status.reconnect()


CollaborativeWorkspace: scroll to bottom of chat view after after it is shown

--- client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
+++ client/landing/CollaborativeWorkspace/collaborativeworkspace.coffee
@@ -63,7 +63,9 @@
       @userRef.child("status").set "online"

       @loader.destroy()
-      @chatView?.show()
+      if @chatView
+        @chatView.show()
+        @utils.defer => @chatView.scrollToBottom()

       @emit "WorkspaceSyncedWithRemote"