Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -15,9 +15,7 @@
   # update the player
   player.update()

-  # x, y position is based on the center point so
-  # the position the player is set to is based on
-  # half their width and height
+  #
   player.I.x
   # => 2.5 # half the player's width


Fix weird spacing

Fix weird spacing

--- apps/editorial_features/routes.coffee
+++ apps/editorial_features/routes.coffee
@@ -11,7 +11,7 @@
   @curation = new Curation(id: sd.EOY_2016)
   @article = new Article(id: sd.EOY_2016_ARTICLE)
   Q.all([
-  	@curation.fetch(cache: true)
+    @curation.fetch(cache: true)
     @article.fetch(
       cache:  true
       headers: 'X-Access-Token': req.user?.get('accessToken') or ''


lower icebox limit to 100

lower icebox limit to 100

--- src/data/pivotal.coffee
+++ src/data/pivotal.coffee
@@ -33,8 +33,8 @@
   getIceboxCount: (projectId)-> @_getCount projectId, 'unscheduled'

   _getCount: (projectId, state)->
-    @_queryPivotal("projects/#{projectId}/stories", with_state: state, limit: 200).then (stories)->
-      if stories.length <= 200 then stories.length else '200+'
+    @_queryPivotal("projects/#{projectId}/stories", with_state: state, limit: 101).then (stories)->
+      if stories.length <= 100 then stories.length else '100+'

   _queryIterations: (projectId, scope, withOwners = false)->
     @_queryPivotal("projects/#{projectId}/iterations", scope: scope).then (iterations)=>


config.main.prod: add reverseProxy worker

--- config/main.prod.coffee
+++ config/main.prod.coffee
@@ -193,6 +193,7 @@
   kontrol             : command : "#{GOBIN}/kontrol -c #{configName} -r #{region}"
   # --port #{kontrol.port} -env #{environment} -public-key #{kontrol.publicKeyFile} -private-key #{kontrol.privateKeyFile}"
   kloud               : command : "#{GOBIN}/kloud -c #{configName} -r #{region} -port #{KONFIG.kloud.port} -public-key #{KONFIG.kloud.publicKeyFile} -private-key #{KONFIG.kloud.privateKeyFile} -kontrol-url \"http://#{KONFIG.kloud.kontrolUrl}\" -debug"
+  reverseProxy        : command : "#{GOBIN}/reverseproxy -port 1234 -env production -region #{publicHostname}PublicEnvironment -publicHost proxy-#{publicHostname}.ngrok.com -publicPort 80"

   # guestcleaner        : command : "node #{projectRoot}/workers/guestcleaner/index.js     -c #{configName}"


Fix 'create new' in private group list opening 'create channel' flex

--- packages/rocketchat-ui-sidenav/side-nav/listPrivateGroupsFlex.coffee
+++ packages/rocketchat-ui-sidenav/side-nav/listPrivateGroupsFlex.coffee
@@ -10,7 +10,7 @@
 		SideNav.closeFlex()

 	'click footer .create': ->
-		SideNav.setFlex "createChannelFlex"
+		SideNav.setFlex "privateGroupsFlex"

 	'mouseenter header': ->
 		SideNav.overArrow()


levels and default game states

levels and default game states

--- src/title_screen.coffee
+++ src/title_screen.coffee
@@ -7,6 +7,6 @@
   self.bind "overlay", (canvas) ->
     self.centerText canvas, App.name

-    self.center
+    self.centerText

   return self


When loading file from database, only call xqlint for xquery resources (otherwise it will uselessly slow down the editor)

--- lib/existdb-tree-view.coffee
+++ lib/existdb-tree-view.coffee
@@ -26,7 +26,6 @@
             atom.workspace.observeTextEditors((editor) =>
                 buffer = editor.getBuffer()
                 p = buffer.getId()
-                console.log("checking buffer %s", p)
                 match = /^((?:http|https):\/\/.*?)(\/db.*)$/.exec(p)
                 if  match and not buffer._remote?
                     server = match[1]
@@ -267,7 +266,8 @@
                         )
                         self.disposables.add(onDidSave)
                         self.disposables.add(onDidDestroy)
-                        XQUtils.xqlint(newEditor)
+                        if contentType == "application/xquery"
+                            XQUtils.xqlint(newEditor)
                         onOpen?(newEditor)
                     )
                 )
@@ -323,7 +323,7 @@
             locals =
                 $('.tree-view .selected').map(() ->
                     if this.getPath? then this.getPath() else ''
-                ).get();
+                ).get()
             if locals? and locals.length > 0
                 selected = @treeView.getSelected()
                 if selected.length != 1 or selected[0].item.type == "resource"
@@ -337,10 +337,12 @@

         reindex: (parentView) ->
             query = "xmldb:reindex('#{parentView.item.path}')"
+            @main.updateStatus("Reindexing #{parentView.item.path}...")
             @runQuery(query,
                 (error, response) ->
                     atom.notifications.addError("Failed to reindex collection #{parentView.item.path}", detail: if response? then response.statusMessage else error)
-                (body) ->
+                (body) =>
+                    @main.updateStatus("")
                     atom.notifications.addSuccess("Collection #{parentView.item.path} reindexed")
             )


delete exited containers

delete exited containers

--- src/docker-utils.coffee
+++ src/docker-utils.coffee
@@ -67,7 +67,7 @@
 			# Cleanup containers first, so that they don't block image removal.
 			docker.listContainersAsync(all: true)
 			.filter (containerInfo) ->
-				!_.contains(apps, containerInfo.Image)
+				!_.contains(apps, containerInfo.Image) or !containerInfo.Status
 			.map (containerInfo) ->
 				docker.getContainer(containerInfo.Id).removeAsync()
 				.then ->


Use absolute paths inside fuzzy-finder

Use absolute paths inside fuzzy-finder

This allows paths from outside the current project root to sanely
coexist in the buffer list.

--- lib/fuzzy-finder-view.coffee
+++ lib/fuzzy-finder-view.coffee
@@ -39,7 +39,7 @@
     $$ ->
       @li =>
         if git?
-          status = git.statuses[project.resolve(path)]
+          status = git.statuses[path]
           if git.isStatusNew(status)
             @div class: 'status new'
           else if git.isStatusModified(status)
@@ -61,7 +61,7 @@

         @span fs.base(path), class: "file label #{typeClass}"
         if folder = fs.directory(path)
-          @span " - #{folder}/", class: 'directory'
+          @span " - #{project.relativize(folder)}/", class: 'directory'

   openPath: (path) ->
     rootView.open(path, {@allowActiveEditorChange}) if path
@@ -76,7 +76,7 @@

   confirmed : (path) ->
     return unless path.length
-    if fs.isFile(project.resolve(path))
+    if fs.isFile(path)
       @cancel()
       @openPath(path)
     else
@@ -133,11 +133,10 @@
             @miniEditor.setText(currentWord)

   populateGitStatusPaths: ->
-    projectRelativePaths = []
-    for path, status of git.statuses
-      continue unless fs.isFile(path)
-      projectRelativePaths.push(project.relativize(path))
-    @setArray(projectRelativePaths)
+    paths = []
+    paths.push(path) for path, status of git.statuses when fs.isFile(path)
+
+    @setArray(paths)

   populateProjectPaths: (options = {}) ->
     if @projectPaths?.length > 0
@@ -179,17 +178,10 @@
       else
         -(editSession.lastOpened or 1)

-    @paths = _.map editSessions, (editSession) ->
-      project.relativize editSession.getPath()
+    @paths = []
+    @paths.push(editSession.getPath()) for editSession in editSessions

-    @setArray(@paths)
-
-  getOpenedPaths: ->
-    paths = {}
-    for editSession in project.getEditSessions()
-      path = editSession.getPath()
-      paths[path] = editSession.lastOpened if path?
-    paths
+    @setArray(_.uniq(@paths))

   detach: ->
     super


Hide Vomnibar in case of click on entire document instead of click on document.body.

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -262,7 +262,7 @@
       @input.focus()
       event.stopImmediatePropagation()
     # A click anywhere else hides the vomnibar.
-    document.body.addEventListener "click", => @hide()
+    document.addEventListener "click", => @hide()

 #
 # Sends requests to a Vomnibox completer on the background page.


:wrench: Updated atom config

:wrench: Updated atom config

--- config/atom/config.cson
+++ config/atom/config.cson
@@ -89,6 +89,7 @@
     fontSize: 0.95
     matchPattern: "([A-EG-Z]+([0-9a-eg-z])*)|[a-egz0-9]{2,}"
   "linter-eslint":
+    fixOnSave: true
     lintHtmlFiles: true
   minimap:
     plugins:


use alert icon

use alert icon

--- lib/notifications-log.coffee
+++ lib/notifications-log.coffee
@@ -83,4 +83,4 @@
   onDidDestroy: (callback) ->
     @emitter.on 'did-destroy', callback

-  getIconName: -> 'comment'
+  getIconName: -> 'alert'


Simplify conditional

Simplify conditional

--- lib/actions/device.coffee
+++ lib/actions/device.coffee
@@ -189,8 +189,7 @@
 				, callback

 		], (error) ->
-			return done(error) if os.platform() isnt 'win32'
-			if error? and (error.code is 'EPERM' or error.code is 'EACCES')
+			if os.platform() is 'win32' and error? and (error.code is 'EPERM' or error.code is 'EACCES')
 				windosu = require('windosu')

 				# Need to escape everypath to avoid errors


Bumped version of Express and connect-assets

I know we froze the Express 3 beta version because of a bug, but that seems to be fixed now.

--- lib/skeleton/template.coffee
+++ lib/skeleton/template.coffee
@@ -20,8 +20,8 @@
           "name": "#{@appName}"
         , "version": "0.0.1"
         , "dependencies": {
-              "express": "3.0.0beta4"
-            , "connect-assets": "2.1.x"
+              "express": "3.0.3"
+            , "connect-assets": "2.3.3"
             #{this.getTemplateModule()}
             #{this.getCssModule()}
             #{this.getJsModule()}


Use trailing if

Use trailing if

--- lib/snippet-expansion.coffee
+++ lib/snippet-expansion.coffee
@@ -33,7 +33,7 @@
     while newColumn >= 0
       break if Snippet.prefixBoundary.test line[newColumn - 1]
       newColumn--
-    if newColumn < 0 then newColumn = 0
+    newColumn = 0 if newColumn < 0
     startPoint = new Point(row, newColumn)
     endPoint = new Point(row, column)
     @editor.setSelectedBufferRange new Range(startPoint, endPoint)


fix typo

fix typo

--- client/views/pages/login/login.coffee
+++ client/views/pages/login/login.coffee
@@ -41,4 +41,4 @@
 						wrs -> FlowRouter.setParams language: language
 		else
 			submit.ladda('stop')
-			Session.set 'error', 'Mssing field'
+			Session.set 'error', 'Missing field'


Rename describe to character set support

--- spec/text-buffer-spec.coffee
+++ spec/text-buffer-spec.coffee
@@ -1819,7 +1819,7 @@
             buffer.append("hello\n1\r\n2\n")
             expect(buffer.getText()).toBe "\ninitialtexthello\n1\n2\n"

-  describe "encodings", ->
+  describe "character set encoding support", ->
     describe "when the buffer is unmodified", ->
       describe "when the encoding of the buffer is changed", ->
         beforeEach ->


Fix test-case

Fix test-case

--- tests/integration/components/password-reset-test.coffee
+++ tests/integration/components/password-reset-test.coffee
@@ -13,4 +13,4 @@

   @render hbs """{{password-reset}}"""

-  assert.equal @$().text().trim(), 'Reset your passwordSecurity fanatics at your serviceNew PasswordConfirm PasswordReset'
+  assert.equal @$().text().trim(), 'Reset your passwordSecurity fanatics at your serviceNew PasswordConfirm PasswordResetLogin?'


Update validateGenerator.coffee

Update validateGenerator.coffee

--- lib/validateGenerator.coffee
+++ lib/validateGenerator.coffee
@@ -8,16 +8,24 @@
     @_validate = {}
     this

-  @add: (param, message, conditions...)->
+  @add: (param, message, condition, value...)->
     @_validate[param] = {} unless @_validate[param]?
-    @_validate[param][condition] = message: message for condition in conditions
+
+    @_validate[param][condition] =
+      message : message
+      value   : value
     this

   @end: (cb) ->
     for param of @_validate
       for condition of @_validate[param]
         entry = @_validate[param][condition]
-        @_req.assert(param, entry.message)[condition]()
+
+        if entry.value.length isnt 0
+          fn = @_req.assert(param, entry.message)[condition]
+          fn.apply(fn, Array.prototype.slice.call(entry.value))
+        else
+          @_req.assert(param, entry.message)[condition]()

     if @_req.validationErrors()
       errors = errorify.serialize(@_req)


Removed unused module

Removed unused module

--- lib/views/status-view.coffee
+++ lib/views/status-view.coffee
@@ -1,16 +0,0 @@
-{$, View} = require 'atom-space-pen-views'
-
-module.exports =
-  class StatusView extends View
-    @content = (params) ->
-      @div class: 'git-plus', =>
-        @div class: "#{params.type} message", params.message
-
-    initialize: ->
-      @panel ?= atom.workspace.addBottomPanel(item: this)
-      setTimeout =>
-        @destroy()
-      , atom.config.get('git-plus.general.messageTimeout') * 1000
-
-    destroy: ->
-      @panel?.destroy()


<use /> now is rendered on 'component' change when all props set

--- src/document/use.coffee
+++ src/document/use.coffee
@@ -3,6 +3,7 @@
 utils = require 'src/utils'
 assert = require 'src/assert'
 log = require 'src/log'
+signal = require 'src/signal'

 assert = assert.scope 'View.Use'
 log = log.scope 'View', 'Use'
@@ -33,7 +34,7 @@

             if @isRendered
                 @revert()
-                @render()
+                signal.setImmediate @render
         return

     queue = []
@@ -60,6 +61,7 @@
         @refName = @node.props.ref
         @usedComponent = null
         @isRendered = false
+        @render = utils.bindFunctionContext @render, @

         @node.onVisibleChange visibilityChangeListener, @
         @node.onPropsChange propsChangeListener, @


Fixed instance checking

Fixed instance checking

--- lib/class-complete.coffee
+++ lib/class-complete.coffee
@@ -164,7 +164,7 @@
                     buffer += "\tif (typeof #{param.name} !== \"#{param.type}\") throw new Error(\"Parameter '#{param.name}' expects to be type '#{param.type}'\");\n"
                 if param.instance
                     checks = true
-                    buffer += "\tif (#{param.name} instanceof #{param.instance}) throw new Error(\"Parameter '#{param.name}' expects to be instance of '#{param.instance}'\");\n"
+                    buffer += "\tif (!(#{param.name} instanceof #{param.instance})) throw new Error(\"Parameter '#{param.name}' expects to be instance of '#{param.instance}'\");\n"

         if parameters.length > 0 && members


DebuggerModel: allow empty constructor.

DebuggerModel: allow empty constructor.

--- lib/debugger-model.coffee
+++ lib/debugger-model.coffee
@@ -30,7 +30,7 @@
 module.exports=
 class DebuggerModel
   constructor: (state)->
-    @breakpoints = state.breakpoints ? []
+    @breakpoints = state?.breakpoints ? []
     @api = new DebuggerApi()

   serialize: ->


Calculate favicon colors on all pages and when modals are shown.


hub -- hopefully fix local hub reconnect bug.

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2191,11 +2191,11 @@
                     if not err
                         cb(err, socket)
                     else
-                        if retries > 1
+                        if retries == 0
                             cb(err)
                         else
-                            delete @_status
-                            @new_socket(cb, retries + 1)
+                            delete @_status  # forget port and secret token.
+                            @new_socket(cb, retries - 1)

     remove_multi_response_listener: (id) =>
         delete @_multi_response[id]


hub: consistency was invalid

hub: consistency was invalid

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -729,7 +729,7 @@
                     key         : @hash_session_id
                     value       : signed_in_mesg
                     ttl         : ttl
-                    consistency : 'eachQuorum'
+                    consistency : 2
                     cb          : (err) =>
                         if err
                             winston.debug("WARNING: issue writing remember me cookie: #{err}")


hub sync: part 4 -- making it even more robust!

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -1946,6 +1946,7 @@
         # The downstream clients of this hub
         @diffsync_clients = {}

+        codemirror_sessions.by_path[@project_id + @path] = @
         @connect (err) =>
             if err
                 opts.cb(err)
@@ -1953,9 +1954,8 @@
                 opts.cb(false, @)

     _connect: (cb) =>
-        delete codemirror_sessions.by_path[@project_id + @path]
-        delete codemirror_sessions.by_uuid[@session_uuid]
-        delete @_sync_lock
+        # The downstream clients of this hub
+        @diffsync_clients = {}
         @local_hub.call
             mesg : message.codemirror_get_session(path:@path, project_id:@project_id, session_uuid:@session_uuid)
             cb   : (err, resp) =>
@@ -1967,10 +1967,23 @@
                 else
                     @session_uuid = resp.session_uuid
                     @chat         = resp.chat
+
                     codemirror_sessions.by_uuid[@session_uuid] = @
+
+                    if @_last_sync?
+                        # We have sync'd before.
+                        patch = @diffsync_server._compute_edits(@_last_sync, @diffsync_server.live)

                     # Reconnect to the upstream (local_hub) server
                     @diffsync_server = new diffsync.DiffSync(doc:resp.content)
+
+                    if @_last_sync?
+                        # applying missed patches to the new upstream version that we just got from the hub.
+                        @_apply_patch_to_live(patch)
+                    else
+                        # This initialiation is the first.
+                        @_last_sync   = resp.content
+
                     @diffsync_server.connect(new CodeMirrorDiffSyncLocalHub(@))

                     #
@@ -1978,10 +1991,12 @@
                     #
                     @set_content(resp.content)

-                    codemirror_sessions.by_path[@project_id + @path] = @
-                    codemirror_sessions.by_uuid[@session_uuid] = @
-
                     cb()
+
+    _apply_patch_to_live: (patch) =>
+        @diffsync_server._apply_edits_to_live(patch)
+        for id, ds of @diffsync_clients
+            ds.live = @diffsync_server.live

     set_content: (content) =>
         @diffsync_server.live = content
@@ -2096,6 +2111,7 @@
         before = @diffsync_server.live
         @diffsync_server.push_edits (err) =>
             @_sync_lock = false
+            after = @diffsync_server.live
             if err
                 winston.debug("codemirror session local hub sync error -- #{err}")
                 if typeof(err) == 'string' and err.indexOf('retry') != -1
@@ -2114,7 +2130,8 @@
                             cb(true)  # successful connect, but we still need to return error so sync happens
             else
                 winston.debug("codemirror session local hub sync -- pushed edits, thus completing cycle")
-                if before != @diffsync_server.live
+                @_last_sync = after # what was the last successful sync with upstream.
+                if before != after
                     @_tell_clients_to_sync()
                 cb()


Add some window properties in the sample app

--- sample/app.coffee
+++ sample/app.coffee
@@ -1,5 +1,10 @@
 Titanium.UI.backgroundColor = 'white'
 Titanium.include 'sub/sub.js'
+winargs =
+	backgroundColor: 'red'
+	fullscreen: false
+	title: 'Test window'
+	exitOnClose: true

 onOpen = (e) ->
 	alert 'window opened'


Simplify. Use an array, no need for complicated id generation.

--- src/tweenmachine.coffee
+++ src/tweenmachine.coffee
@@ -10,63 +10,44 @@

 class @TweenMachine

-  # http://byronsalau.com/blog/how-to-create-a-guid-uuid-in-javascript/
-  generateId = ->
-    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
-        r = (Math.random() * 16) | 0
-        v = if c is 'x' then r else (r & 0x3 | 0x8)
-        return v.toString(16)
-
   # http://stackoverflow.com/a/1830844/652765
   isNumber = (candidate) ->
     return !isNaN(parseFloat(candidate)) and isFinite(candidate)

   clamp = (lower, upper, value) ->
-    if value < lower
-      lower
-    else if value > upper
-      upper
-    else
-      value
+    return lower if value < lower
+    return upper if value > upper
+    return value

-  @tweens: {}
+  @tweens = []
+  @clear = -> TweenMachine.tweens.length = 0

-  @clear: ->
-    TweenMachine.tweens.length = 0
-
-  ProgressFunctions =
+  constructor: (@start, @end) ->

-    Numeric: (progress) ->
-      @start + (@end - @start) * @easingFunction(progress)
+    @type = 'Numeric' if isNumber(@start) and isNumber(@end)
+    @type = 'Array' if Array.isArray(@start) and Array.isArray(@end)

-    Array: (progress) ->
-      @interpolationFunction @end, @easingFunction(progress)
+    unless @type? throw new Error 'Must provide either numeric or Array start and end values.'

-  constructor: (@start, @end, @easingFunction, @interpolationFunction) ->
+    @easer = TweenMachine.easings.Linear.None
+    @interpolator = TweenMachine.interpolations.Linear
+    @$id = @tweens.length

-    if isNumber(@start) and isNumber(@end)
-      @type = 'Numeric'
-    else if Array.isArray(@start) and Array.isArray(@end)
-      @type = 'Array'
-    else
-      throw new Error 'Must provide either numeric or Array start and end values.'
-
-    @easingFunction or= TweenMachine.easings.Linear.None
-    @interpolationFunction or= TweenMachine.interpolations.Linear
-    @$id = generateId()
-
-    TweenMachine.tweens[@$id] = this
+    TweenMachine.tweens.push this
     return this

   easing: (name) ->
     [category, type] = name.split('.')
-    @easingFunction = TweenMachine.easings[category][type]
-    this
+    @easer = TweenMachine.easings[category][type]
+    return this

   interpolation: (name) ->
-    @interpolationFunction = TweenMachine.interpolations[name]
-    this
+    @interpolator = TweenMachine.interpolations[name]
+    return this

   at: (progress) ->
     progress = clamp(0, 1, progress)
-    ProgressFunctions[@type].bind(this)(progress)
+
+    switch @type
+      when 'Numeric' then @start + (@end - @start) * @easer(progress)
+      when 'Array' then @interpolator @end, @easer(progress)


Refactor administration control panel JavaScript

Just added some more functions and comments to improve the overall
structure of the JavaScript file used in the administration control
panel.

Signed-off-by: Konrad Reiche <4ff0213f56e2082fcd8c882d04c546881c579ce4@gmail.com>

--- app/assets/javascripts/admin.js.coffee
+++ app/assets/javascripts/admin.js.coffee
@@ -1,4 +1,10 @@
 $ ->
+
+  # Adds to each compute button a callback event
+  initializeButtons = () ->
+    for metric in gon.metrics
+      repository = gon.repository.name
+      $(".compute-metric.#{metric}").click createRequestCallback(repository, metric)

   # Create the callback used for the metric button click handler
   createRequestCallback = (repository, metric) ->
@@ -8,26 +14,29 @@
         requestStatus()
       )

+  # Request the status of issued jobs
   requestStatus = () ->
     $.getJSON('/metrics/status', processStatus)

+  # Processes the JSON-encoded job progress results
   processStatus = (response) ->
     for repository, metrics of response
       for metric, job of metrics
-        type = determineBarType(job)
-        if type?
-          progressClass = '.admin.control.progress'
-          repositoryId = repository.split('.').join('-')
-          barDiv = $("#{progressClass} .#{repositoryId}.#{metric}.#{type}.bar")
-          barDiv.css('width', "#{job.percent}%")
-          if type == 'compute'
-            barDiv = $("#{progressClass} .#{repositoryId}.#{metric}.analyze.bar")
-            barDiv.css('width', "100%")
-
+        displayJobProgress(repository, metric, job)

     if repeatRequest(response)
       setTimeout(requestStatus, 500)

+  # Visualizes the job progresses
+  displayJobProgress = (repository, metric, job) ->
+    type = determineBarType(job)
+    if type?
+      fillProgressBar(repository, metric, type, job.percent)
+      if type == 'compute'
+        fillProgressBar(repository, metric, 'analyze', '100%')
+
+  # Determines progress bar element that need change based on the
+  # current job state
   determineBarType = (job) ->
     type = null
     types = ['analyze', 'compute']
@@ -35,6 +44,15 @@
       type = job.state
     return type

+  # Fills a defined progress bar up to the given percent
+  fillProgressBar = (repository, metric, type, percent) ->
+    progressClass = '.admin.control.progress'
+    repositoryId = repository.split('.').join('-')
+    barDiv = $("#{progressClass} .#{repositoryId}.#{metric}.#{type}.bar")
+    barDiv.css('width', "#{percent}%")
+
+  # Determines whether the request for the job progress should
+  # be repeated
   repeatRequest = (response) ->
     for repository, metrics of response
       for metric, job of metrics
@@ -42,13 +60,7 @@
           return true
     return false

-  initializeButtons = () ->
-    for metric in gon.metrics
-      repository = gon.repository.name
-      $(".compute-metric.#{metric}").click createRequestCallback(repository, metric)
-
   if gon? and gon.repository? and gon.metrics?
-
     # activate repository selector
     $('.dropdown-toggle').dropdown()
     initializeButtons()


Implement jewel swapping.

Implement jewel swapping.

--- src/entity/jewel.coffee
+++ src/entity/jewel.coffee
@@ -26,10 +26,10 @@
             @field.currentJewel = @
         @events.onInputUp.add =>
             # swap, destroy, or return to initial location
-            @field.currentJewel = null
             underPointer = game.physics.arcade.getObjectsUnderPointer(
                 game.input.activePointer, @field)
             if underPointer.length is 1
+                # destroy
                 underPointer.forEach (jewel) =>
                     console.log jewel.color
                     toDestroy = []
@@ -37,11 +37,26 @@
                     @field.clearJewel jewel, toDestroy
                     toDestroy.forEach (j) ->
                         j.kill()
-            else if underPointer.length > 1
-                console.log 'swap'
-                #swap jewels
-                underPointer.forEach (jewel) ->
-                    [jewel.x, jewel.y] = [jewel.prevX, jewel.prevY]
-                    jewel.body.reset(jewel.x, jewel.y)
+            else if underPointer.length > 1 # should never be > 2
+                # swap
+                targetJewel = null
+                underPointer.forEach (jewel) =>
+                    if jewel != @field.currentJewel
+                        targetJewel = jewel
+                # drop currentJewel to this tile
+                [oldX, oldY] = [@field.currentJewel.prevX, @field.currentJewel.prevY]
+                @sendToTile(@field.currentJewel, targetJewel.tileX, targetJewel.tileY)
+                # tween targetJewel to old tile of currentJewel
+                @sendToTile(targetJewel, oldX, oldY)
+            @field.currentJewel = null
+
+    sendToTile: (jewel, x, y) ->
+        jewel.x = x * c.TILE_SIZE + c.TILE_SIZE / 2
+        jewel.y = y * c.TILE_SIZE + c.TILE_SIZE / 2
+        jewel.tileX = x
+        jewel.tileY = y
+        jewel.prevX = x
+        jewel.prevY = y
+        jewel.body.reset(jewel.x, jewel.y)

 module.exports = Jewel


Multiline labels for mekko chart

Multiline labels for mekko chart

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -88,18 +88,18 @@
       legend_columns = if @carrier_list.length > 9 then 3 else 2
       legend_rows = @carrier_list.length / legend_columns
       legend_height = legend_rows * @legend_cell_height
-      label_height = 80 # rotated labels
+      @label_height = 80 # rotated labels
       @label_margin = 25

-      @series_height  = @height - legend_height - label_height - @label_margin
+      @series_height  = @height - legend_height - @label_height - @label_margin
       @label_offset   = @series_height + @label_margin

-      @svg = @create_svg_container @width, @series_height + label_height, @margins
+      @svg = @create_svg_container @width, @series_height + @label_height, @margins

       @draw_legend
         series: @carrier_list.models
         width: @width
-        vertical_offset: @series_height + label_height
+        vertical_offset: @series_height + @label_height
         columns: legend_columns

       @x = d3.scale.linear().range([0, @width])
@@ -239,8 +239,40 @@
         )
         .attr("data-tooltip-text", (d) => @main_formatter()(d.val()))

+      @wrapLabels()
       @arrangeLabels()
       @moveArrows()
+
+    wrapLabels: =>
+      label_height = @label_height
+
+      @svg.selectAll("tspan.key").each((d) ->
+        el         = d3.select(this)
+        words      = el.text().split(/\s+/).reverse()
+        line       = []
+        lineNumber = 0
+        lineHeight = 1.1
+        y          = el.attr('y')
+
+        el.text(null)
+          .attr("x", 0)
+          .attr("y", y)
+          .attr("dy", "0em")
+
+        while word = words.pop()
+          line.push(word)
+          el.text(line.join(' '))
+
+          if el.node().getComputedTextLength() > label_height
+            line.pop()
+            el.text(line.join(' '))
+            line = [word]
+            el   = el.insert("tspan", "tspan + *")
+                     .attr("x", 0)
+                     .attr("y", y)
+                     .attr("dy", ++lineNumber * lineHeight + "em")
+                     .text(word)
+      )

     # Arranges the labels for each sector so that no labels overlap.
     #


HTML-escape messages added to floating console

--- tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/core/console.coffee
@@ -19,7 +19,8 @@
   # module exports are mutable; someone else could
   # require this module to change the default DURATION
   exports =
-    DURATION : 10 # seconds
+    DURATION: 10
+  # seconds

   try
     nativeConsole = console
@@ -30,23 +31,23 @@
     # Disable the floating console temporarily, since we have to resolve the tangle of dependencies
     # related to loading vs. the core stack, etc.

-#    unless floatingConsole
-#      floatingConsole = new Element "div", class: "t-console"
-#      $(document.body).insert top: floatingConsole
-#
-#    div = new Element "div", class: "t-console-entry #{className}"
-#    div.update(message).hide()
-#    floatingConsole.insert top:div
-#
-#    new Effect.Appear div, duration: .25
-#
-#    fade = new Effect.Fade div,
-#      delay: exports.DURATION
-#      afterFinish: -> div.remove()  # was T5.dom.remove(div)
-#
-#    div.observe "click", ->
-#      fade.cancel()
-#      div.remove() # was T5.dom.remove(div)
+    #    unless floatingConsole
+    #      floatingConsole = new Element "div", class: "t-console"
+    #      $(document.body).insert top: floatingConsole
+    #
+    #    div = new Element "div", class: "t-console-entry #{className}"
+    #    div.update(_.escape(message)).hide()
+    #    floatingConsole.insert top:div
+    #
+    #    new Effect.Appear div, duration: .25
+    #
+    #    fade = new Effect.Fade div,
+    #      delay: exports.DURATION
+    #      afterFinish: -> div.remove()  # was T5.dom.remove(div)
+    #
+    #    div.observe "click", ->
+    #      fade.cancel()
+    #      div.remove() # was T5.dom.remove(div)

   level = (className, consolefn) ->
     (message) ->


usunieto nadmiarowe console.logi

usunieto nadmiarowe console.logi

--- app/assets/javascripts/oz-piwik.coffee
+++ app/assets/javascripts/oz-piwik.coffee
@@ -63,7 +63,6 @@
         ])

      editedField: (fieldType) ->
-       console.log(@EVENTS['fields'][fieldType])
        if @getRole()
          _paq.push([
           'trackEvent',
@@ -78,7 +77,6 @@

   #  Edycja pola zabytku
   _.forEach( PiwikTracker.EDITED_FIELDS, (_class, _event) ->
-    console.log(_event, _class)
     $(document).on('click', _class, () -> PiwikTracker.editedField(_event))
   )


Format the versions in a list.

Format the versions in a list.

--- src/github-pages.coffee
+++ src/github-pages.coffee
@@ -60,6 +60,11 @@
 formatted_build_text = (build) ->
   "Page build #{build.status} @ #{build.commit.substr(0, 7)}. Triggered by #{build.pusher.login} #{timeago(build.created_at)}."

+formatted_versions = (versions) ->
+  all = for gem, version of versions
+    "#{gem} = v#{version}"
+  all.join("\n")
+
 module.exports = (robot) ->
   github = require("githubot")(robot)
   robot.respond /pages (\w+)( \w+\/[\w\.]+)?/i, (msg) ->
@@ -86,6 +91,6 @@
     else if command in VERSION_COMMANDS
       cb = (info) ->
         msg.send "No versions found. Is GitHub.com up?" unless info?
-        msg.send JSON.stringify info
+        msg.send formatted_versions(info)

     github_pages_info github, repo, command, cb


Improved Config class documentation

Improved Config class documentation

--- www/src/theoricus/config/config.coffee
+++ www/src/theoricus/config/config.coffee
@@ -10,32 +10,33 @@
 module.exports = class Config

   ###*
-    If true, execute all the animations at startup, or skip them and just render the views.
+    If true, execute the __default__ view's transitions at startup, otherwise, skip them and render the views without transitions.

     @property {Boolean} animate_at_startup
   ###
   animate_at_startup: false

   ###*
-    If true, insert automatically fadeIn/fadeOut transitions for the views.
+    If true, automatically insert __default__ fadeIn/fadeOut transitions for the views.

     @property {Boolean} enable_auto_transitions
   ###
   enable_auto_transitions: true

   ###*
-    If true, skip all the transitions, and just render the views.
+    If true, skip all the view's __default__ transitions.

     @property {Boolean} disable_transitions
   ###
   disable_transitions: null

   ###*
-  Config constructor, initializing the app's config settings.
+  Config constructor, initializing the app's config settings defined in `settings.coffee`
+
   @class Config
   @constructor
   @param the {Theoricus} Shortcut for app's instance
-  @param Settings {Object} App settings
+  @param Settings {Object} App settings defined in the `settings.coffee`.
   ###
   constructor:( @the, @Settings )->
     @disable_transitions = @Settings.disable_transitions ? false


embedly: deal with non alphanumeric chars

--- server/lib/server/image_cache.coffee
+++ server/lib/server/image_cache.coffee
@@ -37,6 +37,9 @@
   if ext.indexOf("com") > -1
     ext = "com"

+  # replace nonalphanumeric characters
+  com = com.replace(/\W+/g, "")
+
   md5      = crypto.createHash("md5").update(noExt).digest("hex")
   filename = "#{imagePath}/#{md5}.#{ext}"


hide module when no artworks

hide module when no artworks

--- components/artwork_filter/view.coffee
+++ components/artwork_filter/view.coffee
@@ -67,7 +67,7 @@
     @filter.fetchRoot
       success: (model, response, options) =>
         mediator.trigger 'artwork_filter:filter:sync', model
-        @remove() unless model.get('total')
+        @remove() unless (model.get('total').value > 0)
       error: =>
         @remove()
     @fetchArtworks()


Reload the client only when authentication changes

To keep the ETags around longer

--- scripts/gh-book/session.coffee
+++ scripts/gh-book/session.coffee
@@ -1,11 +1,13 @@
-define ['backbone', 'github'], (Backbone, Github) ->
+define ['underscore', 'backbone', 'github'], (_, Backbone, Github) ->

   class GithubSession extends Backbone.Model
     initialize: () ->
       @_reloadClient()

       @on 'change', () =>
-        @_reloadClient()
+        # If any authentication info has changed then reload the client
+        if not _.isEmpty _.pick @.changed, ['token', 'id', 'password']
+          @_reloadClient()

         # See if this user can collaborate
         @getRepo().canCollaborate().done (canCollaborate) =>


Data script: constant for frequency of progress logs.

--- scripts/normalize-data._coffee
+++ scripts/normalize-data._coffee
@@ -33,6 +33,9 @@
 # How many files to process in parallel:
 NUM_PARALLEL = 100

+# How frequently to log progress:
+LOG_FREQUENCY_SECS = 60
+
 getFilePathForId = (id) ->
     # NOTE: Since file systems are typically case-insensitive (even though
     # they're case-aware), prefix capital letters with an underscore.
@@ -63,7 +66,7 @@
 # Since we're going to be I/O bound here, we parallelize heavily.
 echo 'Processing...'
 funnel = flows.funnel NUM_PARALLEL
-nextLogTime = Date.now() + 1000 * 60
+nextLogTime = Date.now() + 1000 * LOG_FREQUENCY_SECS

 while idFileNames.length then funnel _, (_) ->
     # Grab an ID file and remove it from our array (to reduce memory usage).
@@ -110,6 +113,6 @@
         remaining = idFileNames.length
         percent = 100 * (totalNumIds - remaining) / totalNumIds
         echo "#{percent.toFixed 2}% processed; #{remaining} files remaining."
-        nextLogTime = Date.now() + 1000 * 60
+        nextLogTime = Date.now() + 1000 * LOG_FREQUENCY_SECS

 echo 'All done!'


Update tonypai.weather.coffee

Update tonypai.weather.coffee

--- scripts/tonypai.weather.coffee
+++ scripts/tonypai.weather.coffee
@@ -1,13 +1,13 @@
 module.exports = (robot) ->

-  robot.hear /[w|W]{1}eather (.*),(.*)/i, (result) ->
+  robot.hear /weather (.*),(.*)/i, (result) ->

     if result.match.length is 0
       city = "taichung"
       country = "tw"
     else
-      city = result.match[2].trim()
-      country = result.match[3].trim()
+      city = result.match[1].trim()
+      country = result.match[2].trim()

     url = "http://api.openweathermap.org/data/2.5/forecast/daily?q="+city+"," + country


use cart title from session cart object

use cart title from session cart object


Check for missing tasks when ComboEditor mounts

--- app/classifier/tasks/combo/editor.cjsx
+++ app/classifier/tasks/combo/editor.cjsx
@@ -7,6 +7,11 @@
     workflow: null
     task: null
     onChange: ->
+
+  componentWillMount: () ->
+    @props.task.tasks.map (taskKey, i) =>
+      unless @props.workflow.tasks[taskKey]
+        @removeTask(i)

   removeTask: (index) ->
     @props.task.tasks.splice index, 1


Fixing issue #1100, the popovers for point selection doesn't close properly

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -736,7 +736,7 @@
             if not $el.find(".pop-role:visible").css("left")?
                 $el.find(".pop-points-open").css("left", "110px")

-            $el.find(".pop-points-open").show()
+            $el.find(".pop-points-open").popover().open()

         renderRolesSelector = (us) ->
             # Prepare data for rendering


removed debugging comment

removed debugging comment

--- src/locaternator.coffee
+++ src/locaternator.coffee
@@ -117,7 +117,6 @@
   # defaults
   $.Locaternator.options =
     service: ->
-      # console.debug "@ is", @, @locationServices.default, @locationServices[@locationServices.default]
       @locationServices[@locationServices.default]
     locations: ""
     locationServices:


Added for scheduled tasks

Added for scheduled tasks

--- SingularityUI/app/views/request.coffee
+++ SingularityUI/app/views/request.coffee
@@ -268,17 +268,14 @@
             @refresh()

     runTask: (e) =>
-        taskModel = app.collections.tasksScheduled.get($(e.target).data('task-id'))
         $row = $(e.target).parents('tr')
         $containingTable = $row.parents('table')
-
-        vex.dialog.confirm
-            message: "
Are you sure you want to run this task immediately?

#{ taskModel.get('id') }
"
-            callback: (confirmed) =>
-                return unless confirmed
-                taskModel.run()
-                app.collections.tasksScheduled.remove(taskModel)
-                $row.remove()
-                utils.handlePotentiallyEmptyFilteredTable $containingTable, 'task'
+        taskModel = app.collections.tasksScheduled.get($(e.target).data('task-id'))
+
+        requestModel = new Request id: taskModel.get "requestId"
+        requestModel.promptRun =>
+            app.collections.tasksScheduled.remove(taskModel)
+            $row.remove()
+            utils.handlePotentiallyEmptyFilteredTable $containingTable, 'task'

 module.exports = RequestView


Fix check for showing disqualify button

Fix check for showing disqualify button

--- resources/assets/coffee/react/beatmap-discussions/nominations.coffee
+++ resources/assets/coffee/react/beatmap-discussions/nominations.coffee
@@ -156,7 +156,7 @@
                     " #{nominations.current}/#{nominations.required}"
                 @renderLights(nominations.current, nominations.required)

-              if @props.currentUser.id? && !@userIsOwner()
+              if @props.currentUser.id?
                 div className: "#{bn}__row-right",
                   if mapIsQualified && @userCanDisqualify()
                     el BigButton,
@@ -465,7 +465,7 @@

   userCanDisqualify: =>
-    !@userIsOwner() && (@props.currentUser.is_admin || @props.currentUser.can_moderate || @props.currentUser.is_full_bn)
+    @props.currentUser.is_admin || @props.currentUser.can_moderate || @props.currentUser.is_full_bn

   userIsOwner: =>


Fix worker search autocompletion

Fix worker search autocompletion

--- admin/mturkAdmin.coffee
+++ admin/mturkAdmin.coffee
@@ -200,8 +200,6 @@
             { workerId: { $regex: "^" + match.toUpperCase() } },
             { username: { $regex: match, $options: "i" } }
           ]
-        callback: (user) ->
-          Router.go("tsWorkers", {workerId: user.workerId}) if user.workerId?
       }
     ]
   }
@@ -223,6 +221,10 @@
     }, {
       sort: submitTime: -1
     })
+
+Template.tsAdminWorkers.events
+  "autocompleteselect input": (e, t, user) ->
+    Router.go("tsWorkers", {workerId: user.workerId}) if user.workerId?

 Template.tsAdminPanel.rendered = ->
   svg = d3.select(@find("svg"))


Made AuthPacket's header size statically accessible

--- src/wrathnet/expansions/wotlk/net/auth-packet.coffee
+++ src/wrathnet/expansions/wotlk/net/auth-packet.coffee
@@ -13,8 +13,7 @@
 class WrathNet.expansions.wotlk.net.AuthPacket extends WrathNet.net.Packet

   # Header size in bytes for both incoming and outgoing packets
-  @getter 'headerSize', ->
-    1
+  @HEADER_SIZE = 1

   # Finalizes this packet
   finalize: ->


removed old comment

removed old comment

--- app/scripts/directives/sub-view.coffee
+++ app/scripts/directives/sub-view.coffee
@@ -1,10 +1,8 @@
 'use strict';

 angular.module('appApp.directives')
-  .directive('subView', [()->
+  .directive 'subView', [()->
     restrict: 'E'
-    # NOTE: the templateUrl function requires angular 1.1.4 or higher
     templateUrl: (element, attrs) ->
       "views/#{attrs.template}.html"
-  ])
- +  ]


FIX: partial-draw test

FIX: partial-draw test

--- test/tests/Art/Engine/Core/Element/Drawing.coffee
+++ test/tests/Art/Engine/Core/Element/Drawing.coffee
@@ -202,8 +202,8 @@
       .then ->
         compareDownsampledRedChannel "partialRedraw_partialDraw", canvasElement, [
           0, 0, 0, 0
-          0, 8, 4, 0
-          0, 4, 4, 0
+          0, 8, 0, 0
+          0, 0, 4, 0
           0, 0, 0, 0
         ]


Freeze all routes after initialisation.

Freeze all routes after initialisation.

--- src/chaplin/lib/route.coffee
+++ src/chaplin/lib/route.coffee
@@ -45,6 +45,9 @@
         'properties as action names'

     @createRegExp()
+
+    # You’re frozen when your heart’s not open.
+    Object.freeze? this

   reverse: (params) ->
     url = @pattern


Ooops, that's a function.

Ooops, that's a function.

--- app/views/play/level/tome/spell.coffee
+++ app/views/play/level/tome/spell.coffee
@@ -27,7 +27,7 @@
     @permissions = read: p.permissions?.read ? [], readwrite: p.permissions?.readwrite ? []  # teams
     if @canWrite()
       @setLanguage options.language
-    else if @isEnemySpell
+    else if @isEnemySpell()
       @setLanguage options.otherSession.get 'submittedCodeLanguage'
     else
       @setLanguage 'javascript'
@@ -163,13 +163,13 @@
     "<Spell: #{@spellKey}>"

   isEnemySpell: ->
+    return false unless @permissions.readwrite.length
     teamSpells = @session.get('teamSpells')
     team = @session.get('team') ? 'humans'
     teamSpells and not _.contains(teamSpells[team], @spellKey)

   shouldUseTranspiledCode: ->
     # Determine whether this code has already been transpiled, or whether it's raw source needing transpilation.
-    return false unless @permissions.readwrite.length  # Only player-writable code will be stored transpiled.
     return true if @spectateView  # Use transpiled code for both teams if we're just spectating.
     return true if @isEnemySpell()  # Use transpiled for enemy spells.
     # Players without permissions can't view the raw code.


[#65638108] Reset currentError

[#65638108] Reset currentError

--- app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/sandbox_shipments_controller.js.coffee
@@ -138,6 +138,7 @@
     updateShipment: (shipment) ->
       shipment.setProperties({'_modified': true})
       @set('currentShipment', null)
+      @get("controllers.annualReportUpload").set("currentError", null)
       $('.shipment-form-modal').modal('hide')

     deleteShipment: (shipment) ->


Remove debug logs

Remove debug logs

--- app/src/controllers/wallet/send/wallet_send_index_dialog_view_controller.coffee
+++ app/src/controllers/wallet/send/wallet_send_index_dialog_view_controller.coffee
@@ -88,7 +88,6 @@
       total = total.add(output.get('value'))
     estimatedSize = ledger.bitcoin.estimateTransactionSize(utxo.length, 2).max
     fees = feePerByte.multiply(estimatedSize)
-    l total.toBtcString(), fees.toBtcString()
     amount = total.subtract(fees)
     if amount.lte(0)
       amount = ledger.Amount.fromSatoshi(0)


Teamwork: Playgrounds modal is now active.

--- client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
+++ client/landing/app/Applications/Teamwork.kdapplication/Views/teamworkapp.coffee
@@ -17,7 +17,7 @@
     else
       @teamwork.on "PanelCreated", =>
         @doCurlRequest playgroundsManifest, (err, manifest) =>
-          @populatePlaygroundsButton manifest
+          @playgroundsManifest = manifest

   createTeamwork: ->
     options               = @getOptions()
@@ -41,9 +41,9 @@
             callback      : => @showToolsModal @teamwork.getActivePanel(), @teamwork
           }
           title           : "Playgrounds"
-          itemClass       : KDButtonViewWithMenu
+          itemClass       : KDButtonView
           cssClass        : "clean-gray playgrounds-button"
-          menu            : []
+          callback        : => @showPlaygroundsModal()
         ]
         floatingPanes     : [ "chat" , "terminal", "preview" ]
         layout            :
@@ -61,21 +61,6 @@
             }
           ]
       ]
-
-  populatePlaygroundsButton: (playgrounds) ->
-    button   = @teamwork.getActivePanel().headerButtons.Playgrounds
-    menu     = []
-
-    playgrounds.forEach (playground) =>
-      item   = {}
-      {name} = playground
-      item[name] = {}
-      item[name].callback = =>
-        @handlePlaygroundSelection name, playground.manifestUrl
-
-      menu.push item
-
-    button.setOption "menu", menu

   showToolsModal: (panel, workspace) ->
     modal       = new KDModalView
@@ -128,7 +113,9 @@
     finderController.mountVm "#{defaultVmName}:#{path}"

   showPlaygroundsModal: ->
-    new TeamworkPlaygroundsModal delegate: this
+    new TeamworkPlaygroundsModal
+      delegate    : this
+      playgrounds : @playgroundsManifest

   mergePlaygroundOptions: (manifest, playground) ->
     {rawOptions}                    = @teamwork
@@ -148,6 +135,10 @@
     return rawOptions

   handlePlaygroundSelection: (playground, manifestUrl) ->
+    unless manifestUrl
+      for manifest in @playgroundsManifest when playground is manifest.name
+        {manifestUrl} = manifest
+
     @doCurlRequest manifestUrl, (err, manifest) =>
       @teamwork.startNewSession @mergePlaygroundOptions manifest, playground
       @teamwork.container.setClass playground


EDSC-122: Address duplicate definition from merge and load order issue

--- app/assets/javascripts/models/page/access_page.js.coffee
+++ app/assets/javascripts/models/page/access_page.js.coffee
@@ -7,7 +7,6 @@
 data = @edsc.models.data
 ui = @edsc.models.ui
 ns = @edsc.models.page
-ui = @edsc.models.ui

 ns.AccessPage = do (ko,
                     setCurrent = ns.setCurrent
@@ -23,7 +22,6 @@
     constructor: ->
       @query = new QueryModel()
       @project = new ProjectModel(@query)
-      @project.fromJson(accessData)
       @bindingsLoaded = ko.observable(false)
       @user = new UserModel()

@@ -32,6 +30,8 @@
         temporal: new TemporalModel(@query)
         projectList: new ProjectListModel(@project, @user)

+      @project.fromJson(accessData)
+
   setCurrent(new AccessPage())

   exports = AccessPage


Better variable name in test

Better variable name in test

It should be a subscription id, instead of an admin id. The test passes
anyway because the db calls are mocked, but the wrong variable name
can cause confusion.

--- services/web/test/unit/coffee/Subscription/SubscriptionGroupHandlerTests.coffee
+++ services/web/test/unit/coffee/Subscription/SubscriptionGroupHandlerTests.coffee
@@ -88,8 +88,8 @@
 				done()

 		it "should add the user to the group", (done)->
-			@Handler.addUserToGroup @adminUser_id, @newEmail, (err)=>
-				@SubscriptionUpdater.addUserToGroup.calledWith(@adminUser_id, @user._id).should.equal true
+			@Handler.addUserToGroup @subscription_id, @newEmail, (err)=>
+				@SubscriptionUpdater.addUserToGroup.calledWith(@subscription_id, @user._id).should.equal true
 				done()

 		it "should not add the user to the group if the limit has been reached", (done)->


Remove memoize from ability service

Remove memoize from ability service

--- angular/core/services/ability_service.coffee
+++ angular/core/services/ability_service.coffee
@@ -1,183 +1,183 @@
 angular.module('loomioApp').factory 'AbilityService', (AppConfig, Records, Session) ->
   new class AbilityService

-    isLoggedIn: Records.memoize ->
+    isLoggedIn: ->
       @isUser() and !Session.user().restricted?

-    isEmailVerified: Records.memoize ->
+    isEmailVerified: ->
       @isLoggedIn() && Session.user().emailVerified

-    isUser: Records.memoize ->
+    isUser: ->
       AppConfig.currentUserId?

-    canContactUser: Records.memoize (user) ->
+    canContactUser: (user) ->
       @isLoggedIn() &&
       Session.user().id != user.id &&
       _.intersection(Session.user().groupIds(), user.groupIds()).length

-    canAddComment: Records.memoize (thread) ->
+    canAddComment: (thread) ->
       Session.user().isMemberOf(thread.group())

-    canRespondToComment: Records.memoize (comment) ->
+    canRespondToComment: (comment) ->
       Session.user().isMemberOf(comment.group())

-    canStartPoll: Records.memoize (group) ->
+    canStartPoll: (group) ->
       group and
       (@canAdministerGroup(group) or Session.user().isMemberOf(group) and group.membersCanRaiseMotions)

-    canParticipateInPoll: Records.memoize (poll) ->
+    canParticipateInPoll: (poll) ->
       return false unless poll
       @canAdministerPoll(poll) or
       !poll.group() or
       (Session.user().isMemberOf(poll.group()) and poll.group().membersCanVote)

-    canEditStance: Records.memoize (stance) ->
+    canEditStance: (stance) ->
       Session.user() == stance.author()

-    canEditThread: Records.memoize (thread) ->
+    canEditThread: (thread) ->
       @canAdministerGroup(thread.group()) or
       Session.user().isMemberOf(thread.group()) and
       (Session.user().isAuthorOf(thread) or thread.group().membersCanEditDiscussions)

-    canPinThread: Records.memoize (thread) ->
+    canPinThread: (thread) ->
       !thread.pinned && @canAdministerGroup(thread.group())

-    canUnpinThread: Records.memoize (thread) ->
+    canUnpinThread: (thread) ->
       thread.pinned && @canAdministerGroup(thread.group())

-    canMoveThread: Records.memoize (thread) ->
+    canMoveThread: (thread) ->
       @canAdministerGroup(thread.group()) or
       Session.user().isAuthorOf(thread)

-    canDeleteThread: Records.memoize (thread) ->
+    canDeleteThread: (thread) ->
       @canAdministerGroup(thread.group()) or
       Session.user().isAuthorOf(thread)

-    canChangeThreadVolume: Records.memoize (thread) ->
+    canChangeThreadVolume: (thread) ->
       Session.user().isMemberOf(thread.group())

-    canChangeGroupVolume: Records.memoize (group) ->
+    canChangeGroupVolume: (group) ->
       Session.user().isMemberOf(group)

-    canAdministerGroup: Records.memoize (group) ->
+    canAdministerGroup: (group) ->
       Session.user().isAdminOf(group)

-    canManageGroupSubscription: Records.memoize (group) ->
+    canManageGroupSubscription: (group) ->
       group.isParent() and
       @canAdministerGroup(group) and
       group.subscriptionKind? and
       group.subscriptionKind != 'trial' and
       group.subscriptionPaymentMethod != 'manual'

-    isCreatorOf: Records.memoize (group) ->
+    isCreatorOf: (group) ->
       Session.user().id == group.creatorId

-    canStartThread: Records.memoize (group) ->
+    canStartThread: (group) ->
       @canAdministerGroup(group) or
       (Session.user().isMemberOf(group) and group.membersCanStartDiscussions)

-    canAddMembers: Records.memoize (group) ->
+    canAddMembers: (group) ->
       @canAdministerGroup(group) or
       (Session.user().isMemberOf(group) and group.membersCanAddMembers)

-    canCreateSubgroups: Records.memoize (group) ->
+    canCreateSubgroups: (group) ->
       group.isParent() and
       (@canAdministerGroup(group) or
       (Session.user().isMemberOf(group) and group.membersCanCreateSubgroups))

-    canEditGroup: Records.memoize (group) ->
+    canEditGroup: (group) ->
       @canAdministerGroup(group)

-    canArchiveGroup: Records.memoize (group) ->
+    canArchiveGroup: (group) ->
       @canAdministerGroup(group)

-    canEditComment: Records.memoize (comment) ->
+    canEditComment: (comment) ->
       Session.user().isMemberOf(comment.group()) and
       Session.user().isAuthorOf(comment) and
       (comment.isMostRecent() or comment.group().membersCanEditComments)

-    canDeleteComment: Records.memoize (comment) ->
+    canDeleteComment: (comment) ->
       (Session.user().isMemberOf(comment.group()) and
       Session.user().isAuthorOf(comment)) or
       @canAdministerGroup(comment.group())

-    canRemoveMembership: Records.memoize (membership) ->
+    canRemoveMembership: (membership) ->
       membership.group().memberIds().length > 1 and
       (!membership.admin or membership.group().adminIds().length > 1) and
       (membership.user() == Session.user() or @canAdministerGroup(membership.group()))

-    canDeactivateUser: Records.memoize ->
+    canDeactivateUser: ->
      _.all Session.user().memberships(), (membership) ->
        !membership.admin or membership.group().hasMultipleAdmins

-    canManageMembershipRequests: Records.memoize (group) ->
+    canManageMembershipRequests: (group) ->
       (group.membersCanAddMembers and Session.user().isMemberOf(group)) or @canAdministerGroup(group)

-    canViewPublicGroups: Records.memoize ->
+    canViewPublicGroups: ->
       AppConfig.features.public_groups

-    canStartGroups: Records.memoize ->
+    canStartGroups: ->
       AppConfig.features.create_group || Session.user().isAdmin

-    canViewGroup: Records.memoize (group) ->
+    canViewGroup: (group) ->
       !group.privacyIsSecret() or
       Session.user().isMemberOf(group)

-    canViewPrivateContent: Records.memoize (group) ->
+    canViewPrivateContent: (group) ->
       Session.user().isMemberOf(group)

-    canCreateContentFor: Records.memoize (group) ->
+    canCreateContentFor: (group) ->
       Session.user().isMemberOf(group)

-    canViewMemberships: Records.memoize (group) ->
+    canViewMemberships: (group) ->
       Session.user().isMemberOf(group)

-    canViewPreviousPolls: Records.memoize (group) ->
+    canViewPreviousPolls: (group) ->
       @canViewGroup(group)

-    canJoinGroup: Records.memoize (group) ->
+    canJoinGroup: (group) ->
       (group.membershipGrantedUpon == 'request') and
       @canViewGroup(group) and
       !Session.user().isMemberOf(group)

-    canRequestMembership: Records.memoize (group) ->
+    canRequestMembership: (group) ->
       (group.membershipGrantedUpon == 'approval') and
       @canViewGroup(group) and
       !Session.user().isMemberOf(group)

-    canTranslate: Records.memoize (model) ->
+    canTranslate: (model) ->
       AppConfig.inlineTranslation.isAvailable? and
       _.contains(AppConfig.inlineTranslation.supportedLangs, Session.user().locale) and
       Session.user().locale != model.author().locale

-    canSubscribeToPoll: Records.memoize (poll) ->
+    canSubscribeToPoll: (poll) ->
       if poll.group()
         @canViewGroup(poll.group())
       else
         @canAdministerPoll() || _.contains(@poll().voters(), Session.user())

-    canSharePoll: Records.memoize (poll) ->
+    canSharePoll: (poll) ->
       @canEditPoll(poll)

-    canEditPoll: Records.memoize (poll) ->
+    canEditPoll: (poll) ->
       poll.isActive() and @canAdministerPoll(poll)

-    canDeletePoll: Records.memoize (poll) ->
+    canDeletePoll: (poll) ->
       @canAdministerPoll(poll)

-    canSetPollOutcome: Records.memoize (poll) ->
+    canSetPollOutcome: (poll) ->
       poll.isClosed() and @canAdministerPoll(poll)

-    canAdministerPoll: Records.memoize (poll) ->
+    canAdministerPoll: (poll) ->
       if poll.group()
         (@canAdministerGroup(poll.group()) or (Session.user().isMemberOf(poll.group()) and Session.user().isAuthorOf(poll)))
       else
         Session.user().isAuthorOf(poll)

-    canClosePoll: Records.memoize (poll) ->
+    canClosePoll: (poll) ->
       @canEditPoll(poll)

-    requireLoginFor: Records.memoize (page) ->
+    requireLoginFor: (page) ->
       return false if @isLoggedIn()
       switch page
         when 'emailSettingsPage' then !Session.user().restricted?


removed unused methods from commentviewheader inheritance

--- client/app/CommonViews/opinions/opinionviewheader.coffee
+++ client/app/CommonViews/opinions/opinionviewheader.coffee
@@ -67,78 +67,6 @@
       click     : =>
         list.emit "AllOpinionsLinkWasClicked", @

-  ownCommentArrived:->
-
-    # Get correct number of items in list from controller
-    # I'm not sure maybe its not a good idea
-    @onListCount = @parent.commentController?.getItemCount?()
-
-    # If there are same number of comments in list with total
-    # comment size means we don't need to show new item count
-    @newItemsLink.unsetClass('in')
-
-    # If its our comments so it's not a new comment
-    if @newCount > 0 then @newCount--
-
-    @updateNewCount()
-
-  ownCommentDeleted:->
-    if @newCount > 0
-      @newCount++
-
-  render:->
-
-    # Get correct number of items in list from controller
-    # I'm not sure maybe its not a good idea
-    if @parent?.commentController?.getItemCount?()
-      @onListCount = @parent.commentController.getItemCount()
-    _newCount = @getData().repliesCount
-
-    # Show View all bla bla link if there are more comments
-    # than maxCommentToShow
-    @show() if _newCount > @maxCommentToShow and @onListCount < _newCount
-
-    # Check the oldCount before update anything
-    # if its less means someone deleted a comment
-    # otherwise it meanse we have a new comment
-    # if nothing changed it means user clicked like button
-    # so we don't need to touch anything
-    if _newCount > @oldCount
-      @newCount++
-    else if _newCount < @oldCount
-      if @newCount > 0 then @newCount--
-
-    # If the count is changed then we need to update UI
-    if _newCount isnt @oldCount
-      @oldCount = _newCount
-      @utils.wait => @updateNewCount()
-
-    super
-
-  updateNewCount:->
-
-    # If there is no comments so we can not have new comments
-    if @oldCount is 0 then @newCount = 0
-
-    # If we have comments more than 0 we should show the new item link
-    if @newCount > 0
-      # @show()
-      @newItemsLink.updatePartial "#{@newCount} new"
-      @newItemsLink.setClass('in')
-    else
-      @newItemsLink.unsetClass('in')
-
-    if @onListCount > @oldCount
-      @onListCount = @oldCount
-
-    if @onListCount is @getData().repliesCount
-      @newCount = 0
-
-    if @onListCount is @oldCount and @newCount is 0
-      @hide()
-    else
-      # @show()
-
   hide:->
     @unsetClass "in"
     super


SidebarMachineBox: Cache created workspace list items.

--- client/app/lib/activity/sidebar/sidebarmachinebox.coffee
+++ client/app/lib/activity/sidebar/sidebarmachinebox.coffee
@@ -20,6 +20,7 @@
     super options, data

     @machine = new Machine machine: remote.revive data.machine
+    @workspaceListItemsById = {}

     @machine.isCollaborationMachine = data.machine.isCollaborationMachine
     @machine.isSharedMachine = data.machine.isSharedMachine
@@ -45,6 +46,7 @@
     @listWrapper = @listController.getView()

     @listController.getListView().on 'ItemWasAdded', (item) =>
+      @workspaceListItemsById[item.getData().getId()] = item
       item.once 'WorkspaceDeleted', @bound 'handleWorkspaceDeleted'

     @addWorkspace ws  for ws in workspaces
@@ -157,6 +159,8 @@

     { workspaces } = @getData()

+    delete @workspaceListItemsById[wsId]
+
     for ws, index in workspaces when ws.getId() is wsId
       return workspaces.splice index, 1


Toggler should call #deactivate on disposal

--- app/assets/components/toggler/toggler.coffee
+++ app/assets/components/toggler/toggler.coffee
@@ -60,6 +60,7 @@
     @is_active = false

   dispose: ->
+    @deactivate()
     @el.off(@trigger)


Don't include active admin JS in main bundle

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -1,5 +1,8 @@
 #= require libraries
-#= require_tree .
+#= require albums
+#= require comments
+#= require images
+#= require sources

 $ ->
   $('.alert').delay(4000).fadeOut('slow')


fix keg_engine2.js changeDate

fix keg_engine2.js changeDate

--- app/assets/javascripts/keg_engine2.js.coffee
+++ app/assets/javascripts/keg_engine2.js.coffee
@@ -134,8 +134,8 @@
     input.on
       change: ->
         form.submit()
-      changeDate: ->
-        form.submit()
+      # changeDate: ->
+      #   form.submit()

     link.on
       click: ->


SaveOnChangePage force dirty saving on navigation.

[#60875594]
https://www.pivotaltracker.com/story/show/60875594
(cherry picked from commit 8e7d097a57fd00fa13871082018e9026e7bb8981)

--- app/assets/javascripts/save-on-change.coffee
+++ app/assets/javascripts/save-on-change.coffee
@@ -32,53 +32,90 @@

 class SaveOnChange
-  constructor: (@$form) ->
+  changeInterval: 200  # Fire change and blur events almost instantly.
+  keyUpinterval:  2000 # Fire keyup events with a larger delay
+  constructor: (@$form, @page) ->
     @scheduled_job      = null
     @previous_value     = null
-    @update_interval_s  = 2.0
-    @save_indicator     = new SaveIndicator($("#save"))
     @setupEvents()

   setupEvents: ->
-    # Fire inputs field changes on 'change' events with no scheduled_jobs
-    @$form.find('input,textarea,select').on 'change', (e) =>
-      @saveElement()
-    @$form.find('input,textarea,select').on 'blur',   (e) =>
-      @saveElement()
-    @$form.find('input,textarea,select').on 'keyup',  (e) =>
-      @schedule()

-  saveElement: ->
-      data = @$form.serialize()
-      return if @previous_value == data
-      @save_indicator.showSaving()
-      $.ajax({
-        type: "POST",
-        url: @$form.attr( 'action' ),
-        data: @$form.serialize(),
-        success: (response) =>
-          @previous_value = data
-          @save_indicator.showSaved()
-        error: (jqxhr, status, error) =>
-          @save_indicator.showSaveFailed()
-      })
+    @$form.find('input,textarea,select').on 'change blur', (e) =>
+      @schedule(@changeInterval)
+    @$form.find('input,textarea').on 'keyup',  (e) =>
+      @schedule(@keyUpinterval)
+
+  saveElement:(async = true) ->
+    data = @$form.serialize()
+    @page.saving()
+    $.ajax({
+      type: "POST",
+      async: async,
+      url: @$form.attr( 'action' ),
+      data: @$form.serialize(),
+      success: (response) =>
+        @previous_value = data
+        @page.saved(@)
+        @dirty = false
+      error: (jqxhr, status, error) =>
+        @page.failed(@)
+    })
+
+  saveNow: ->
+    @unschedule()
+    @saveElement(false)

   # remove events scheduled for elem
   unschedule: () ->
     clearTimeout(@scheduled_job) if @scheduled_job
     @scheduled_job = null

-  schedule:  () ->
+  schedule:  (interval) ->
     @unschedule() # remove any existing events
-    action = () =>
-      @saveElement()
-    @scheduled_job = setTimeout(action, @update_interval_s * 1000)
+    dirty = (@previous_value != @$form.serialize())
+    if dirty
+      @page.mark_dirty(this)
+      action = () =>
+        @saveElement()
+      @scheduled_job = setTimeout(action, interval)
+
+
+class SaveOnChangePage
+  constructor: () ->
+    @save_indicator = new SaveIndicator($("#save"))
+    @intercept_navigation()
+    @forms = []
+    if $('.live_submit').length
+      $('.live_submit').each (i,e) =>
+        @forms.push(new SaveOnChange($(e),@))
+    @dirty_forms = {}
+
+  intercept_navigation: ->
+    $("a").on 'click', (e) =>
+      @force_save_dirty()
+
+  saving: (form) ->
+    @save_indicator.showSaving()
+
+  saved: (form) ->
+    @save_indicator.showSaved()
+    @mark_clean(form)
+
+  failed: (form) ->
+    @save_indicator.showSaveFailed()
+
+  mark_dirty: (form) ->
+    @dirty_forms[form] = form;
+
+  mark_clean: (form) ->
+    delete @dirty_forms[form]
+
+  force_save_dirty: ->
+    for item, value of @dirty_forms
+      value.saveNow()

 $(document).ready ->
-  window.SaveOnChange = SaveOnChange
-  if $('.live_submit').length
-    $('.live_submit').each (i,e) =>
-      new SaveOnChange($(e))
-  else
-    # construct the indicator so it will enter its clear state
-    indicator = new SaveIndicator($("#save"))
+  window.SaveOnChangePage = SaveOnChangePage
+  window.SaveOnChange     = SaveOnChange
+  new SaveOnChangePage()


Made FactBody clickable

Made FactBody clickable

--- core/app/backbone/views/evidence/ndp_fact_relation_or_comment_view.coffee
+++ core/app/backbone/views/evidence/ndp_fact_relation_or_comment_view.coffee
@@ -26,7 +26,7 @@
     if @model instanceof Comment
       @contentRegion.show new NDPCommentView model: @model
     else if @model instanceof FactRelation
-      @contentRegion.show new FactBaseView model: @model.getFact()
+      @contentRegion.show new FactBaseView model: @model.getFact(), clickable_body: true
     else
       throw "Invalid type of model: #{@model}"


Fix map view center coords

Fix map view center coords

--- app/controllers/classify-steps/reveal.coffee
+++ app/controllers/classify-steps/reveal.coffee
@@ -181,7 +181,7 @@

       @youAreHere.bindPopup(popupContent).openPopup()

-      @map.setView @classifier.classification.subject.coords, DEFAULT_ZOOM
+      @map.setView [lat, lng], DEFAULT_ZOOM

   reset: ->
     super


template.grunt.jaded fix

template.grunt.jaded fix

--- app/services/new/template/build/grunt.coffee
+++ app/services/new/template/build/grunt.coffee
@@ -41,7 +41,9 @@
         "build/webapp/public/":           "#{app.paths.client}/index.html"

   reload: {}
-
+  jaded:
+    files: "#{app.paths.app}/views/**/*.jade"
+
   watch:
     test:
       files: "<config:test.files>"
@@ -57,7 +59,7 @@
       ]
       tasks: "copy reload"
     jaded:
-      files: "build/webapp/client/templates/*.jade"
+      files: "<config:jaded.files>"
       tasks: "jaded reload"
     #jobs:
     #  files: "#{app.paths.app}/**/jobs/**/*.coffee"
@@ -95,21 +97,20 @@
     contract = coffee.eval (grunt.file.read file), bare: true
     require('shaman').start contract, path.resolve(__dirname, '..')

-  ## grunt-jaded
-  grunt.registerTask "jaded", "compile jaded templates", ->
-  jaded = require 'jaded'
-  {basename, extname}  = require 'path'
-  src   = app.paths.client + '/templates'
-  dest  = app.paths.public + '/templates'
-  grunt.file.recurse src,
-    (absolute, root, subdir, filename) ->
-      name = basename filename, extname filename
-      template = jaded.compile grunt.file.read(absolute),
+ ## grunt-jaded
+  grunt.registerMultiTask "jaded", "compile jaded templates", ->
+    jaded = require 'jaded'
+    files = grunt.file.expandFiles(@file.src)
+    {basename, extname}  = require 'path'
+    for file in files
+      n = basename file, extname file
+      template = jaded.compile grunt.file.read(file),
         development: true
         rivets: false
         amd: true
-        filename: absolute
-      grunt.file.write "#{dest}/#{name}.js", template
+        filename: file
+      grunt.file.mkdir "#{app.paths.public}/views/"
+      grunt.file.write "#{app.paths.public}/views/"+n+".tpl.js" , template

   ## grunt-mocha-node
   grunt.registerMultiTask "test", "Run unit tests with Mocha", ->


Exact size of a binary + hex snippet

Exact size of a binary + hex snippet

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -3402,6 +3402,22 @@
         binary_to_string: (bin) =>
             # We print the size of the binary, not the size of the base 64 string
             size = bin.data.length*3/4
+            blocks_of_76 = Math.floor(bin.data.length/78) # 78 to count \r\n
+            leftover = bin.data.length-blocks_of_76*78
+
+            base64_digits = 76*blocks_of_76+leftover
+
+            blocks_of_4 = Math.floor(base64_digits/4)
+            leftover2 = base64_digits-Math.floor(base64_digits/4)
+
+            if bin.data.slice(-2) is '=='
+                number_of_equals = 2
+            else if bin.data.slice(-1) is '='
+                number_of_equals = 1
+            else
+                number_of_equals = 0
+
+            size = 3*blocks_of_4-number_of_equals

             if size >= 1073741824
                 sizeStr = (size/1073741824).toFixed(1)+'GB'
@@ -3416,13 +3432,28 @@
             else if size is 0
                 sizeStr = size+' bytes'

+
+            # Compute a snippet and return the <binary, size, snippet> result
             if size is 0
                 return "<binary, #{sizeStr}>"
             else
-                if bin.data.length > 10
-                    return "<binary, #{sizeStr}, \"#{bin.data.slice(0, 10)}...\">"
-                else
-                    return "<binary, #{sizeStr}, \"#{bin.data}\">"
+                str = atob bin.data.replace(/[ \r\n]+$/, "")
+                snippet = ''
+                for char, i  in str
+                    next = str.charCodeAt(i).toString(16)
+                    if next.length is 1
+                        next = "0" + next
+                    snippet += next
+                    if i > 4
+                        snippet += "..."
+                        break
+                    else
+                        snippet += " "
+
+            return "<binary, #{sizeStr}, \"#{snippet}\">"
+
+
+

     class @ResultView extends DataExplorerView.SharedResultView
         className: 'result_view'


Fix accidentally created closure inside of a loop

I accidentally created a closure inside a loop with the result, that the
callback was only executed with the last iteration value. I have fixed
that by creating a function which then again creates the callback
function based on given parameters.

Signed-off-by: Konrad Reiche <4ff0213f56e2082fcd8c882d04c546881c579ce4@gmail.com>

--- app/assets/javascripts/admin.js.coffee
+++ app/assets/javascripts/admin.js.coffee
@@ -1,14 +1,15 @@
 $ ->
+
+  createRequestCallback = (repository, metric) ->
+    return () ->
+      parameter = {"repository": repository, "metric": metric}
+      $.post("/metrics/compute", parameter)

   initializeButtons = () ->

     for metric in gon.metrics
       repository = gon.repository.name
-      $(".compute-metric.#{metric}").on 'click', (event) =>
-        parameter = { "repository": repository, "metric": metric }
-        $.post("/metrics/compute", parameter, (data, status) =>
-          console.log data
-        )
+      $(".compute-metric.#{metric}").bind 'click', createRequestCallback(repository, metric)

   if gon? and gon.repository? and gon.metrics?


Humanize shown UI properties in game dev levels

So things like "goldKeys" show up as "Gold Keys"

--- app/views/play/level/GameDevTrackView.coffee
+++ app/views/play/level/GameDevTrackView.coffee
@@ -46,7 +46,7 @@
     @$el.removeClass('playback-float-right')

   titleize: (name) ->
-    return _.string.titleize(name)
+    return _.string.titleize(_.string.humanize(name))

   beautify: (name, val) ->
     if typeof val is 'object' and val.x? and val.y? and val.z?


Populate track name with filename if no id3 song/artist tags

--- app/assets/javascripts/mixes.js.coffee
+++ app/assets/javascripts/mixes.js.coffee
@@ -7,8 +7,8 @@
       handle: '.fi-list'
       animation: 150

-  # populate song-artist when id3 tag is read
-  populateName = (i, tag) ->
+  # populate track name with song - artist (or filename if no id3 tags)
+  populateName = (i, tag, filename) ->
     el = document.getElementById('mix_tracks_attributes_' + i + '_name')
     if tag.tags.title && tag.tags.artist
       el.value = tag.tags.title + ' - ' + tag.tags.artist
@@ -16,6 +16,8 @@
       el.value = tag.tags.title
     else if tag.tags.artist
       el.value = tag.tags.artist
+    else
+      el.value = filename.replace(/\.[^/.]+$/, '')

   # read id3 tag data when file is chosen
   handleFileChange = (i,event) ->
@@ -25,7 +27,7 @@
       'artist'
     ]).read
       onSuccess: (tag) ->
-        populateName i,tag
+        populateName i,tag,file.name
         return
       onError: (error) ->
         console.log ':(', error.type, error.info


Fix diff comments

Fix diff comments

Signed-off-by: Dmitriy Zaporozhets <be23d75b156792e5acab51b196a2deb155d35d6a@gmail.com>

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -375,7 +375,7 @@
   ###
   addDiffNote: (e) =>
     e.preventDefault()
-    link = e.target
+    link = e.currentTarget
     form = $(".js-new-note-form")
     row = $(link).closest("tr")
     nextRow = row.next()


Unminify Google Maps style

Unminify Google Maps style

--- app/assets/javascripts/pages.js.coffee
+++ app/assets/javascripts/pages.js.coffee
@@ -7,7 +7,107 @@
         lng: -95
       zoom: 4
       scrollwheel: false
-      styles: [{"featureType":"administrative","elementType":"all","stylers":[{"hue":"#000000"},{"lightness":-100},{"visibility":"off"}]},{"featureType":"landscape","elementType":"geometry","stylers":[{"hue":"#dddddd"},{"saturation":-100},{"lightness":-3},{"visibility":"on"}]},{"featureType":"landscape","elementType":"labels","stylers":[{"hue":"#000000"},{"saturation":-100},{"lightness":-100},{"visibility":"off"}]},{"featureType":"poi","elementType":"all","stylers":[{"hue":"#000000"},{"saturation":-100},{"lightness":-100},{"visibility":"off"}]},{"featureType":"road","elementType":"geometry","stylers":[{"hue":"#bbbbbb"},{"saturation":-100},{"lightness":26},{"visibility":"on"}]},{"featureType":"road","elementType":"labels","stylers":[{"hue":"#ffffff"},{"saturation":-100},{"lightness":100},{"visibility":"off"}]},{"featureType":"road.local","elementType":"all","stylers":[{"hue":"#ffffff"},{"saturation":-100},{"lightness":100},{"visibility":"on"}]},{"featureType":"transit","elementType":"labels","stylers":[{"hue":"#000000"},{"lightness":-100},{"visibility":"off"}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffffff"},{"saturation":-100},{"lightness":100},{"visibility":"on"}]},{"featureType":"water","elementType":"labels","stylers":[{"hue":"#000000"},{"saturation":-100},{"lightness":-100},{"visibility":"off"}]}]
+      styles:
+        [
+          {
+            'featureType': 'administrative'
+            'elementType': 'all'
+            'stylers': [
+              { 'hue': '#000000' }
+              { 'lightness': -100 }
+              { 'visibility': 'off' }
+            ]
+          }
+          {
+            'featureType': 'landscape'
+            'elementType': 'geometry'
+            'stylers': [
+              { 'hue': '#dddddd' }
+              { 'saturation': -100 }
+              { 'lightness': -3 }
+              { 'visibility': 'on' }
+            ]
+          }
+          {
+            'featureType': 'landscape'
+            'elementType': 'labels'
+            'stylers': [
+              { 'hue': '#000000' }
+              { 'saturation': -100 }
+              { 'lightness': -100 }
+              { 'visibility': 'off' }
+            ]
+          }
+          {
+            'featureType': 'poi'
+            'elementType': 'all'
+            'stylers': [
+              { 'hue': '#000000' }
+              { 'saturation': -100 }
+              { 'lightness': -100 }
+              { 'visibility': 'off' }
+            ]
+          }
+          {
+            'featureType': 'road'
+            'elementType': 'geometry'
+            'stylers': [
+              { 'hue': '#bbbbbb' }
+              { 'saturation': -100 }
+              { 'lightness': 26 }
+              { 'visibility': 'on' }
+            ]
+          }
+          {
+            'featureType': 'road'
+            'elementType': 'labels'
+            'stylers': [
+              { 'hue': '#ffffff' }
+              { 'saturation': -100 }
+              { 'lightness': 100 }
+              { 'visibility': 'off' }
+            ]
+          }
+          {
+            'featureType': 'road.local'
+            'elementType': 'all'
+            'stylers': [
+              { 'hue': '#ffffff' }
+              { 'saturation': -100 }
+              { 'lightness': 100 }
+              { 'visibility': 'on' }
+            ]
+          }
+          {
+            'featureType': 'transit'
+            'elementType': 'labels'
+            'stylers': [
+              { 'hue': '#000000' }
+              { 'lightness': -100 }
+              { 'visibility': 'off' }
+            ]
+          }
+          {
+            'featureType': 'water'
+            'elementType': 'geometry'
+            'stylers': [
+              { 'hue': '#ffffff' }
+              { 'saturation': -100 }
+              { 'lightness': 100 }
+              { 'visibility': 'on' }
+            ]
+          }
+          {
+            'featureType': 'water'
+            'elementType': 'labels'
+            'stylers': [
+              { 'hue': '#000000' }
+              { 'saturation': -100 }
+              { 'lightness': -100 }
+              { 'visibility': 'off' }
+            ]
+          }
+        ]

     internal:
       id: 'home-map-jumbotron'


Removed async: false configs, moved logic that was waiting on saveUrl function into the saveUrl success callback.

--- app/assets/javascripts/s3_relay.coffee
+++ app/assets/javascripts/s3_relay.coffee
@@ -9,13 +9,12 @@
   ev.initCustomEvent name, true, false, detail
   document.dispatchEvent ev

-saveUrl = (container, uuid, filename, contentType, publicUrl) ->
+saveUrl = (container, uuid, filename, contentType, publicUrl, progressColumn, fileColumn) ->
   privateUrl = null

   $.ajax
     type: "POST"
     url: "/s3_relay/uploads"
-    async: false
     data:
       parent_type: container.data("parentType")
       parent_id: container.data("parentId")
@@ -27,6 +26,14 @@
     success: (data, status, xhr) ->
       privateUrl = data.private_url
       publishEvent "upload:success", { uuid: uuid }
+      if privateUrl == null
+        displayFailedUpload(progressColumn)
+      else
+        fileColumn.html("#{filename}")
+
+        virtualAttr = "#{container.data('parentType')}[new_#{container.data('association')}_uuids]"
+        hiddenField = "<input type='hidden' name='#{virtualAttr}[]' value='#{uuid}' />"
+        container.append(hiddenField)
     error: (xhr) ->
       console.log xhr.responseText

@@ -44,7 +51,6 @@
   $.ajax
     type: "GET"
     url: "/s3_relay/uploads/new"
-    async: false
     success: (data, status, xhr) ->
       formData = new FormData()
       xhr = new XMLHttpRequest()
@@ -87,17 +93,7 @@
           if xhr.status == 201
             contentType = file.type
             publicUrl = $("Location", xhr.responseXML).text()
-            privateUrl = saveUrl(container, uuid, fileName, contentType, publicUrl)
-
-            if privateUrl == null
-              displayFailedUpload(progressColumn)
-            else
-              fileColumn.html("#{fileName}")
-
-              virtualAttr = "#{container.data('parentType')}[new_#{container.data('association')}_uuids]"
-              hiddenField = "<input type='hidden' name='#{virtualAttr}[]' value='#{uuid}' />"
-              container.append(hiddenField)
-
+            saveUrl(container, uuid, fileName, contentType, publicUrl, progressColumn, fileColumn)
           else
             displayFailedUpload(progressColumn)
             console.log $("Message", xhr.responseXML).text()


Fix default value

Fix default value

--- app/assets/javascripts/favorite.coffee
+++ app/assets/javascripts/favorite.coffee
@@ -17,7 +17,7 @@
   $("#remaining-favs").show()

-window.Favorite = link_to_users: (users) ->
+window.Favorite = link_to_users: (users = []) ->
   return I18n.t "js.noone" if users.length == 0

   html = usersLinks users.slice(0, 6)


Fixes issue with close button not working on MR

Closes #14383

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -627,10 +627,10 @@
       if closebtn.text() isnt closetext
         closebtn.text(closetext)

-      if reopenbtn.is(':not(.btn-comment-and-reopen)')
+      if reopenbtn.is('.btn-comment-and-reopen')
         reopenbtn.removeClass('btn-comment-and-reopen')

-      if closebtn.is(':not(.btn-comment-and-close)')
+      if closebtn.is('.btn-comment-and-close')
         closebtn.removeClass('btn-comment-and-close')

       if discardbtn.is(':visible')


MemberRemovedHandler: reduce complexity by splitting helpers into small pieces

--- workers/social/lib/social/models/computeproviders/handlers/memberremoved.coffee
+++ workers/social/lib/social/models/computeproviders/handlers/memberremoved.coffee
@@ -2,6 +2,7 @@

 log = ->
   console.log '[handlers:memberremoved]', arguments...
+

 checkOwnership = (machine, user) ->

@@ -16,6 +17,32 @@
   return owner

+changeOwnershipOfMachine = (machine, newOwner) ->
+
+  # Move machine ownership to the admin who kicked the member
+  machine.addUsers {
+    targets: [ newOwner ], asOwner: yes
+  }, (err) ->
+    log 'Failed to change ownership:', err  if err
+
+
+updateMachineUsers = (machines, user, requester, reason) ->
+
+  machines.forEach (machine) ->
+    # Check if the user is owner of the machine
+    owner = checkOwnership machine, user
+
+    machine.removeUsers { targets: [ user ] }, (err) ->
+      log "Couldn't remove user from users:", err  if err
+
+      # if not owner this machine we leave it to existing owner
+      # if user is left on by own we leave the users list as is
+      return  if not owner or reason is 'leave'
+
+      # otherwise we move the ownership of the machine to the requester
+      changeOwnershipOfMachine machine, requester
+
+
 module.exports = memberRemoved = ({ group, member, requester }) ->

   # Ignore kicks for guests and koding
@@ -28,7 +55,7 @@
   requesterJUser = null
   memberMachines = []

-  queue  = [
+  queue = [

     ->
       member.fetchUser (err, user) ->
@@ -68,21 +95,9 @@
         queue.next()

     ->
-      memberMachines.forEach (machine) ->
-        # Make sure the user is owner of the machine
-        owner = checkOwnership machine, memberJUser
+      updateMachineUsers memberMachines, memberJUser, requesterJUser, reason
+      queue.next()

-        machine.removeUsers { targets: [ memberJUser ] }, (err) ->
-          log "Couldn't remove user from users:", err  if err
-          return  if not owner or reason is 'leave'
-
-          # Move machine ownership to the admin who kicked the member
-          machine.addUsers {
-            targets: [ requesterJUser ], asOwner: yes
-          }, (err) ->
-            log 'Failed to change ownership:', err  if err
-
-      queue.next()
   ]

   daisy queue


Fix typo in Gruntfile.

Fix typo in Gruntfile.

--- app/templates/website/Gruntfile.coffee
+++ app/templates/website/Gruntfile.coffee
@@ -28,7 +28,7 @@
         tasks: ['newer:copy:components']
       coffee:
         cwd: 'assets/js'
-        files: 'assets/js**/*.coffee'
+        files: 'assets/js/**/*.coffee'
         tasks: ['brerror:newer:coffee:dev']
       stylesheets:
         cwd: 'assets/css' <% if(options.css === 'stylus') { %>


Add py -> Python abbreviation

Add py -> Python abbreviation

Closes #268.

--- assets/javascripts/models/entry.coffee
+++ assets/javascripts/models/entry.coffee
@@ -55,5 +55,6 @@
     'lodash': '_'
     'markdown': 'md'
     'postgresql': 'pg'
+    'python': 'py'
     'sass': 'scss'
     'underscore.js': '_'


cache headbar

cache headbar

--- assets/scripts/controllers/task.coffee
+++ assets/scripts/controllers/task.coffee
@@ -181,6 +181,10 @@
       $(".terminals .term-#{data.from} ul.shell-body").append "
#{msg}!
"
       $(".terminals .term-#{data.from} ul.shell-body").scrollTop($(".terminals .term-#{data.from} ul.shell-body")[0].scrollHeight)

+  headBar =
+    state: document.getElementsByClassName("tstate")[0]
+    count: document.getElementsByClassName("tsourceNum")[0]
+
   socket.on 'workerIn', (data) ->
     console.log "worker in",data
     if jobInfo.workers[data.from]
@@ -194,12 +198,12 @@
       jobInfo.flushDom data.from

     nodeCnt = (w for w,x of jobInfo.workers when x.active).length
-    document.getElementsByClassName("tsourceNum")[0].innerHTML = nodeCnt
+    headBar.count.innerHTML = nodeCnt
     activeCnt = (w for w,x of jobInfo.workers when x.active).length
     if activeCnt > 0
-      document.getElementsByClassName("tstate")[0].innerHTML = 'running...'
-    else
-      document.getElementsByClassName("tstate")[0].innerHTML = 'task finished'
+      headBar.state.innerHTML = 'running'
+    else
+      headBar.state.innerHTML = 'task finished'

   socket.on 'workerOut', (data) ->
     console.log "worker lost",data
@@ -208,11 +212,11 @@
     jobInfo.workers[data.from].doms.light.getElementsByClassName('twinkle')[0].classList.remove 'twinkle-on'

     nodeCnt = (w for w,x of jobInfo.workers when x.active).length
-    document.getElementsByClassName("tsourceNum")[0].innerHTML = nodeCnt
+    headBar.count.innerHTML = nodeCnt
     activeCnt = (w for w,x of jobInfo.workers when x.active).length
     if activeCnt > 0
-      document.getElementsByClassName("tstate")[0].innerHTML = 'running...'
-    else
-      document.getElementsByClassName("tstate")[0].innerHTML = 'task finished'
-
-
+      headBar.state.innerHTML = 'running'
+    else
+      headBar.state.innerHTML = 'task finished'
+
+


prompts open file with modal for unknown file types.

--- client/app/MainApp/ApplicationManager.coffee
+++ client/app/MainApp/ApplicationManager.coffee
@@ -148,6 +148,11 @@

       callback?()

+  promptOpenFileWith:(file)->
+    finderController = KD.getSingleton "finderController"
+    {treeController} = finderController
+    view = new KDView {}, file
+    treeController.showOpenWithModal view

   openFile:(file)->

@@ -158,7 +163,9 @@
     return @openFileWithApplication defaultApp, file  if defaultApp

     switch type
-      when 'code','text','unknown'
+      when 'unknown'
+        @promptOpenFileWith file
+      when 'code','text'
         log "open with a text editor"
         @open @defaultApps.text, (appController)-> appController.openFile file
       when 'image'


Client: Activity: Tests: Updated menuButton selector for selenium tests

--- client/test/lib/teams/teamslikeunlike.coffee
+++ client/test/lib/teams/teamslikeunlike.coffee
@@ -4,7 +4,7 @@
 HUBSPOT      = no

-module.exports =
+module.exports =

   likePost: (browser) ->
@@ -57,3 +57,4 @@
       .waitForElementVisible  editedText, 20000
       .assert.containsText    editedText, editedmessage
       .end()
+


Don't auto-hide update notification

Don't auto-hide update notification

--- assets/javascripts/app/appcache.coffee
+++ assets/javascripts/app/appcache.coffee
@@ -37,6 +37,6 @@
     return

   onUpdateReady: =>
-    new app.views.Notif 'UpdateReady' unless @reloading
+    new app.views.Notif 'UpdateReady', autoHide: null unless @reloading
     @trigger 'updateready'
     return


Detect all feed types

Detect all feed types

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -76,7 +76,7 @@
                 @results = []
                 @results_offset = 0
                 @cursor = value
-                @is_feed = @cursor.toString() in ['[object Feed]', '[object AtomFeed]']
+                @is_feed = @cursor.toString().match(/\[object .*Feed\]/)
                 @missing = 0
                 @ended = false
                 @server_duration = null  # ignore server time if batched response


Add comment and remove a TODO about escaping

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -22,7 +22,6 @@

     return result

-#TODO maintain data
 module 'DataExplorerView', ->
     class @Container extends Backbone.View
         className: 'dataexplorer_container'
@@ -648,8 +647,7 @@
             window.r = rethinkdb.query
             window.R = r.R

-
-            #TODO Make this little thing prettier
+            # We escape the last function because we are building a regex on top of it.
             @unsafe_to_safe_regexstr = []
             @unsafe_to_safe_regexstr.push # This one has to be firest
                 pattern: /\\/g


Reset extra_row - Related to #3037

Reset extra_row - Related to #3037

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -2605,6 +2605,7 @@
                         if @current_results.length < @limit # We display @limit results per page, if we don't have enough, we keep requesting more
                             @current_results.push data
                             @state.cursor.next get_result_callback
+                            @extra_row = undefined # We don't know if there's an extra row yet, reset its value to undefined
                         else if @current_results.length is @limit # If we've reached the limit of results we can show, we still want to know if there are more results available
                             @extra_row = data # We requested an extra row, but won't display it on the current page, so we need to save it for later (when the user wants to show more results)
                             get_result_callback() # Display results


Remove noisey log message

Remove noisey log message

--- server/lib/gdoc_wrapper.coffee
+++ server/lib/gdoc_wrapper.coffee
@@ -8,7 +8,6 @@

   @importByKey: (key) ->
     GDocWrapper.fetchSpreadsheet(key: key).then((spreadsheet) ->
-      console.log spreadsheet
       new GDocWrapper(spreadsheet)
     )


Updated cohtml-definition

Updated cohtml-definition

--- src/cohtml-definition.coffee
+++ src/cohtml-definition.coffee
@@ -1,8 +1,7 @@

 class CohtmlNode

-  constructor: (@parent = null, @tag = null, @id = null, @classList = [], @attributeMap = {})->
-    @innerText = null
+  constructor: (@parent = null, @tag = null, @id = null, @classList = [], @attributeMap = {}, @innerText = null)->
     @childrenList = []
     return


Ignore deploys from IPs other than Github

--- server/controllers/deploy.coffee
+++ server/controllers/deploy.coffee
@@ -1,10 +1,22 @@
 CommandRunner = require('../bin/command-runner')
+range_check = require('range_check')

 getBranchFromRef = (ref) ->
   refParts = ref.split("/")
   return refParts[refParts.length-1]

+getIpFromRequest = (req) ->
+  req.connection.remoteAddress
+
+GITHUB_IP_RANGE = "192.30.252.0/22"
+
+ipIsFromGithub = (ip) ->
+  range_check.in_range(ip, GITHUB_IP_RANGE)
+
 exports.index = (req, res) ->
+  remoteIp = getIpFromRequest(req)
+  return res.send 401 unless ipIsFromGithub(remoteIp)
+
   parsedPayload = JSON.parse(req.body.payload)

   console.log "Got deploy message from #{parsedPayload.ref}"


Using destructuring assignment.

Using destructuring assignment.

--- amCoffee/assets/scripts/lib/i18n.coffee
+++ amCoffee/assets/scripts/lib/i18n.coffee
@@ -12,13 +12,13 @@
             pairs = attrs.replace(/\s/g, '').split ','

             for pair in pairs
-                parts = pair.split ':'
+                [name, key] = pair.split ':'

-                if parts[0] is '' or parts[1] is null
+                if name is '' or key is null
                     console.error 'i18n: Skiped:', pair
                     continue

-                $ele.setAttribute parts[0], chrome.i18n.getMessage parts[1]
+                $ele.setAttribute name, chrome.i18n.getMessage key

     init : ->
         C.attrNames = Object.keys C.handlers


Return new scopes array for each line tokenized

Downstream consumers may tweak the tokens returned and it shouldn't
affect future lines tokenized which would happen if the same array
was pushed each time.

--- src/null-grammar.coffee
+++ src/null-grammar.coffee
@@ -7,13 +7,10 @@
   name: 'Null Grammar'
   scopeName: 'text.plain.null-grammar'

-  constructor: ->
-    @scopes = ['null-grammar.text.plain']
-
   getScore: -> 0

   tokenizeLine: (line) ->
-    tokens: [{value: line, @scopes}]
+    tokens: [{value: line, scopes: ['null-grammar.text.plain']}]

   tokenizeLines: (text) ->
     lines = text.split('\n')


reposition banner

reposition banner

--- app/assets/javascripts/banner.js.coffee
+++ app/assets/javascripts/banner.js.coffee
@@ -43,17 +43,16 @@
     scroll_top = $(window).scrollTop()

     # banner on top !
-    if scroll_top > window.banner_position
+    if scroll_top > window.banner_position + 9
       $('#banner').css('position', 'fixed')
-      $('#banner').css('top', '0px')
+      $('#banner').css('top', '-9px')
     # banner on this normal position
     else
       $('#banner').css('position', 'absolute')
-      $('#banner').css('top', window.banner_position)
+      $('#banner').css('top', window.banner_position )

   # update banner with each user score and hidden levels string
   window.update_banner = ->
-    console.log("verifier methode")
     pack_name = $('#packs').attr('data-pack-name')
     $.get('/banner', {pack_name: pack_name}).success((data, status, xhr) ->
       # update each hidden span with successfully completed levels string


sankey: cool node hovers

sankey: cool node hovers

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -190,6 +190,8 @@

     color: => @get('color') || "steelblue"

+    connects: (id) => @get('left') == id || @get('right') == id
+
   # This is the main chart class
   #
   View: class extends D3ChartView
@@ -209,7 +211,9 @@
         data(@module.links.models, (d) -> d.cid).
         enter().
         append("svg:path").
-        attr("class", "link").
+        attr("class", (link) ->
+          "link #{link.left.get('id')} #{link.right.get('id')}"
+        ).
         style("stroke-width", (link) -> link.value()).
         style("stroke", (link, i) -> link.color()).
         style("fill", "none").
@@ -225,7 +229,8 @@
         data(@module.nodes.models, (d) -> d.get('id')).
         enter().
         append("svg").
-        attr("class", "node").
+        attr("class", (d) -> "node").
+        attr("data-id", (d) -> d.get('id')).
         attr("x", (d) => @x(@module.Node.horizontal_spacing * d.get('column') + 20)).
         attr("y", (d) => @y(d.y_offset()))

@@ -235,7 +240,9 @@
         attr("stroke", "gray").
         attr("fill", (datum, i) -> colors(i)).
         attr("width", @x @module.Node.width).
-        attr("height", (d) => @y d.value())
+        attr("height", (d) => @y d.value()).
+        on("mouseover", @node_mouseover).
+        on("mouseout", @node_mouseout)

       # And here we have the label. We use a +svg:text+ element that will then
       # hold a +tspan+ element for each line
@@ -267,6 +274,25 @@
         text(line)
         y += 10

+    # callbacks
+    #
+    node_mouseover: ->
+      klass = $(this).parent().attr('data-id')
+      d3.selectAll(".link").
+        each((d) ->
+          return if d.connects(klass)
+          d3.select(this).
+            transition().
+            duration(200).
+            style("opacity", 0.2)
+        )
+
+    node_mouseout: ->
+      d3.selectAll(".link").
+        transition().
+        duration(100).
+        style("opacity", 0.8)
+
     # this method is called every time we're updating the chart
     #
     refresh: =>


Fix EventForm default values

Fix EventForm default values

--- app/assets/javascripts/events.js.coffee
+++ app/assets/javascripts/events.js.coffee
@@ -70,14 +70,6 @@
       $.get url, (data) ->
         $('#modal-holder').html(data).find('.modal').modal()

-    toggle_period_and_frequency: (value) ->
-      if value == 'no_repeat'
-        $('#frequency').hide()
-        $('#period').html('')
-      else
-        $('#period').html(value)
-        $('#frequency').show()
-
   Form:

     display: (options) ->
@@ -97,11 +89,26 @@
     authenticity_token: ->
       $('meta[name="csrf-token"]').attr('content')

+    toggle_period_and_frequency: (value) ->
+      if value == 'no_repeat'
+        $('#frequency').hide()
+        $('#period').html('')
+      else
+        $('#period').html(value)
+        $('#frequency').show()
+
     set_form_options: (options) ->
       format    = options['datetimeFormat'] || 'DD/MM/YYYY HH:mm'
-      startTime = (options['start_time']    || moment().floor(15, 'minutes')).format(format)
-      endTime   = (options['end_time']      || moment().ceil(15, 'minutes')).format(format)
+      startTime = options['start_time']     || moment().floor(15, 'minutes')
+      endTime   = options['end_time']       || moment().ceil(15, 'minutes')
       allDay    = options['allDay']         || false
-      $('#event_start_time').val(startTime)
-      $('#event_end_time').val(endTime)
+      min_time  = options['min_time']       || '09:00:00'
+      max_time  = options['max_time']       || '18:00:00'
+
+      if allDay == true
+        startTime = moment(moment().format('YYYY-MM-DD') + 'T' + min_time)
+        endTime =   moment(moment().format('YYYY-MM-DD') + 'T' + max_time)
+
+      $('#event_start_time').val(startTime.format(format))
+      $('#event_end_time').val(endTime.format(format))
       $('#event_all_day').bootstrapSwitch('state', allDay)