Fixed spaces. Ugh.

Fixed spaces. Ugh.

--- bsModalBuilder.coffee
+++ bsModalBuilder.coffee
@@ -1,260 +1,260 @@
 # --- Bootstrap 3.2.0 Modal Builder ---------------------------------------------------------------
 # --- constructor ---
 ModalBuilder = (defaultParams) ->
-	@__version = '0.1.0'
-
-	@defaultParams =
-		title: null
-		hasTitle: true
-		hasFooter: true
-		content: null
-		isClosable: true
-		buttons: [
-			{
-				text: 'Close'
-				type: 'dismiss'
-				style: 'btn-default'
-			}
-			{
-				text: 'Ok'
-				type: 'submit'
-				style: 'btn-primary'
-			}
-		]
-	return
+  @__version = '0.1.0'
+
+  @defaultParams =
+    title: null
+    hasTitle: true
+    hasFooter: true
+    content: null
+    isClosable: true
+    buttons: [
+      {
+        text: 'Close'
+        type: 'dismiss'
+        style: 'btn-default'
+      }
+      {
+        text: 'Ok'
+        type: 'submit'
+        style: 'btn-primary'
+      }
+    ]
+  return

 # --- generalized modal builder ---
 ModalBuilder::buildModal = (title, content, callback, params = false) ->
-	modalParams = @defaultParams
-	if params isnt false
-		currentParamsMap = Object.getOwnPropertyNames(params)
-		currentParamsMap.forEach (item) ->
-			modalParams[item] = params[item]
-			return
-
-	modalId = @__getModalId()
-	modalTitle = title ? @defaultParams.title
-	modalContent = content ? @defaultParams.content
-	modalCallback = if typeof callback is 'function' then callback else null
-
-	modalCloseBtn = '<button type="button" class="close" data-dismiss="modal">×Close</button>'
-	modalCloseBtn = ''  if modalParams.noClose? and modalParams.noClose is true
-
-	modalDOMHeader = """
-

-			#{modalCloseBtn}
-
#{modalTitle}

-

-	"""
-	modalDOMHeader = if modalParams.hasTitle? and modalParams.hasTitle is true then modalDOMHeader else ''
-
-	modalDOMContent = """
-

-			#{modalContent}
-

-	"""
-	modalDOMContent = if content? then modalDOMContent else ''
-
-	modalDOMFooter = '
'
-	buttonHandlers = []
-	modalParams.buttons.forEach (button) ->
-		buttonDataType = ' data-'+button.type+'="modal"'  if button.type?
-		buttonStyle = if button.style? then button.style else 'btn-default'
-		buttonBind = if button.bind? then ' data-bind="'+button.bind+'"' else ''
-		modalDOMFooter += '<button type="button" class="btn '+buttonStyle+'"'+buttonDataType+buttonBind+'>'+button.text+'</button>'
-		buttonHandlers.push button.type  if button.type? and button.type isnt 'dismiss' and button.type isnt 'submit'
-		return
-	modalDOMFooter += '
'
-	modalDOMFooter += '
'
-
-	modalDOMFooter = if modalParams.hasFooter? and modalParams.hasFooter is true then modalDOMFooter else ''
-	modalKbClose = if modalParams.noClose? and modalParams.noClose is true then '' else ' keyboard'
-
-	modalDom = """
-

-

-

-				#{modalDOMHeader}
-				#{modalDOMContent}
-				#{modalDOMFooter}
-

-

-

-	"""
-
-	modalContainer = @__getContainer()
-	modalContainer.append modalDom
-
-	$('#modal_'+modalId+' [data-submit="modal"]').click ->
-		$('#modal_'+modalId).attr 'data-interaction', 'submit'
-		$('#modal_'+modalId).modal 'toggle'
-		if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
-			if $('#modal_'+modalId+' .form-input').length is 1
-				output = $('#modal_'+modalId+' .form-input').val()
-			else
-				ci = 0
-				max = $('#modal_'+modalId+' .form-input').length
-				output = {}
-				while ci < max
-					element = $('#modal_'+modalId+' .form-input').eq(ci)
-					output[element.attr('name')] = element.val()
-					ci++
-
-			callback(output)  if typeof callback is 'function'
-			callback.submit(output)  if typeof callback is 'object' and callback.submit?
-		return
-
-	$('#modal_'+modalId+' [data-dismiss="modal"]').click ->
-		$('#modal_'+modalId).attr 'data-interaction', 'dismiss'
-		$('#modal_'+modalId).modal 'toggle'
-		if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
-			callback.dismiss()  if typeof callback is 'object' and callback.dismiss?
-		return
-
-	buttonHandlers.forEach (handler) ->
-		$('#modal_'+modalId+' [data-'+handler+'="modal"]').click ->
-			$('#modal_'+modalId).attr 'data-interaction', handler
-			$('#modal_'+modalId).modal 'toggle'
-			if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
-				callback[handler]()  if typeof callback is 'object' and callback[handler]?
-		return
-
-	'#modal_' + modalId
+  modalParams = @defaultParams
+  if params isnt false
+    currentParamsMap = Object.getOwnPropertyNames(params)
+    currentParamsMap.forEach (item) ->
+      modalParams[item] = params[item]
+      return
+
+  modalId = @__getModalId()
+  modalTitle = title ? @defaultParams.title
+  modalContent = content ? @defaultParams.content
+  modalCallback = if typeof callback is 'function' then callback else null
+
+  modalCloseBtn = '<button type="button" class="close" data-dismiss="modal">×Close</button>'
+  modalCloseBtn = ''  if modalParams.noClose? and modalParams.noClose is true
+
+  modalDOMHeader = """
+

+      #{modalCloseBtn}
+
#{modalTitle}

+

+  """
+  modalDOMHeader = if modalParams.hasTitle? and modalParams.hasTitle is true then modalDOMHeader else ''
+
+  modalDOMContent = """
+

+      #{modalContent}
+

+  """
+  modalDOMContent = if content? then modalDOMContent else ''
+
+  modalDOMFooter = '
'
+  buttonHandlers = []
+  modalParams.buttons.forEach (button) ->
+    buttonDataType = ' data-'+button.type+'="modal"'  if button.type?
+    buttonStyle = if button.style? then button.style else 'btn-default'
+    buttonBind = if button.bind? then ' data-bind="'+button.bind+'"' else ''
+    modalDOMFooter += '<button type="button" class="btn '+buttonStyle+'"'+buttonDataType+buttonBind+'>'+button.text+'</button>'
+    buttonHandlers.push button.type  if button.type? and button.type isnt 'dismiss' and button.type isnt 'submit'
+    return
+  modalDOMFooter += '
'
+  modalDOMFooter += '
'
+
+  modalDOMFooter = if modalParams.hasFooter? and modalParams.hasFooter is true then modalDOMFooter else ''
+  modalKbClose = if modalParams.noClose? and modalParams.noClose is true then '' else ' keyboard'
+
+  modalDom = """
+

+

+

+        #{modalDOMHeader}
+        #{modalDOMContent}
+        #{modalDOMFooter}
+

+

+

+  """
+
+  modalContainer = @__getContainer()
+  modalContainer.append modalDom
+
+  $('#modal_'+modalId+' [data-submit="modal"]').click ->
+    $('#modal_'+modalId).attr 'data-interaction', 'submit'
+    $('#modal_'+modalId).modal 'toggle'
+    if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
+      if $('#modal_'+modalId+' .form-input').length is 1
+        output = $('#modal_'+modalId+' .form-input').val()
+      else
+        ci = 0
+        max = $('#modal_'+modalId+' .form-input').length
+        output = {}
+        while ci < max
+          element = $('#modal_'+modalId+' .form-input').eq(ci)
+          output[element.attr('name')] = element.val()
+          ci++
+
+      callback(output)  if typeof callback is 'function'
+      callback.submit(output)  if typeof callback is 'object' and callback.submit?
+    return
+
+  $('#modal_'+modalId+' [data-dismiss="modal"]').click ->
+    $('#modal_'+modalId).attr 'data-interaction', 'dismiss'
+    $('#modal_'+modalId).modal 'toggle'
+    if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
+      callback.dismiss()  if typeof callback is 'object' and callback.dismiss?
+    return
+
+  buttonHandlers.forEach (handler) ->
+    $('#modal_'+modalId+' [data-'+handler+'="modal"]').click ->
+      $('#modal_'+modalId).attr 'data-interaction', handler
+      $('#modal_'+modalId).modal 'toggle'
+      if callback isnt null and typeof callback isnt 'string' and typeof callback isnt 'number' and typeof callback isnt 'boolean'
+        callback[handler]()  if typeof callback is 'object' and callback[handler]?
+    return
+
+  '#modal_' + modalId

 # --- modal displayer ---
 ModalBuilder::show = (modalId, onloadCallback = false, oncloseCallback = false) ->
-	$(modalId).modal('show').on 'shown.bs.modal', (e) ->
-		$(modalId).find('input').eq(0).focus()
-		if onloadCallback isnt false
-			onloadCallback modalId
-		return
-
-	$(modalId).on 'hide.bs.modal', (e) ->
-		$(modalId).find('input').eq(0).focus()
-		if oncloseCallback isnt false
-			oncloseCallback modalId, e
-		return
-
-	return
+  $(modalId).modal('show').on 'shown.bs.modal', (e) ->
+    $(modalId).find('input').eq(0).focus()
+    if onloadCallback isnt false
+      onloadCallback modalId
+    return
+
+  $(modalId).on 'hide.bs.modal', (e) ->
+    $(modalId).find('input').eq(0).focus()
+    if oncloseCallback isnt false
+      oncloseCallback modalId, e
+    return
+
+  return

 # --- modal hider ---
 ModalBuilder::hide = (modalId) ->
-	$(modalId).modal 'hide'
-	return
+  $(modalId).modal 'hide'
+  return

 # --- combined modal builder with displayer ---
 ModalBuilder::modal = (title, content, callback, params = false) ->
-	modalId = @buildModal title, content, callback, params
-	if typeof params.onloadCallback is 'function' and typeof params.oncloseCallback is 'function'
-		@show modalId, params.onloadCallback, params.oncloseCallback
-	else if typeof params.onloadCallback is 'function'
-		@show modalId, params.onloadCallback
-	else
-		@show modalId
-	modalId
+  modalId = @buildModal title, content, callback, params
+  if typeof params.onloadCallback is 'function' and typeof params.oncloseCallback is 'function'
+    @show modalId, params.onloadCallback, params.oncloseCallback
+  else if typeof params.onloadCallback is 'function'
+    @show modalId, params.onloadCallback
+  else
+    @show modalId
+  modalId

 # --- modal-powered alert box ---
 ModalBuilder::alert = (title, content) ->
-	modalId = @modal title, content, null,
-		hasTitle: title?
-		buttons: [
-			{
-				text: 'Ok'
-				type: 'submit'
-				style: 'btn-primary'
-			}
-		]
-	modalId
+  modalId = @modal title, content, null,
+    hasTitle: title?
+    buttons: [
+      {
+        text: 'Ok'
+        type: 'submit'
+        style: 'btn-primary'
+      }
+    ]
+  modalId

 # --- modal-powered confirm box ---
 ModalBuilder::confirm = (title, content, callback) ->
-	modalId = @modal title, content, callback,
-		hasTitle: title?
-		buttons: [
-			{
-				text: 'Cancel'
-				type: 'dismiss'
-				style: 'btn-standard'
-			}
-			{
-				text: 'Ok'
-				type: 'submit'
-				style: 'btn-primary'
-			}
-		]
-	modalId
+  modalId = @modal title, content, callback,
+    hasTitle: title?
+    buttons: [
+      {
+        text: 'Cancel'
+        type: 'dismiss'
+        style: 'btn-standard'
+      }
+      {
+        text: 'Ok'
+        type: 'submit'
+        style: 'btn-primary'
+      }
+    ]
+  modalId

 # --- modal-powered prompt box ---
 ModalBuilder::prompt = (title, content, callback, params = false) ->
-	placeholderText = if params.placeholder? then params.placeholder else 'Text input'
-	inputBind = if params.inputBind? then ' data-bind="'+params.inputBind+'"' else ''
-	promptContent = """
-

-
#{content}

-			<input type="text" class="form-control form-input" placeholder="#{placeholderText}" autofocus="autofocus"#{inputBind}>
-

-	"""
-	modalId = @modal title, promptContent, callback,
-		hasTitle: title?
-		buttons: [
-			{
-				text: 'Cancel'
-				type: 'dismiss'
-				style: 'btn-standard'
-				bind: if params.cancelBind? then params.cancelBind else null
-			}
-			{
-				text: 'Ok'
-				type: 'submit'
-				style: 'btn-primary'
-				bind: if params.confirmBind? then params.confirmBind else null
-			}
-		]
-	modalId
+  placeholderText = if params.placeholder? then params.placeholder else 'Text input'
+  inputBind = if params.inputBind? then ' data-bind="'+params.inputBind+'"' else ''
+  promptContent = """
+

+
#{content}

+      <input type="text" class="form-control form-input" placeholder="#{placeholderText}" autofocus="autofocus"#{inputBind}>
+

+  """
+  modalId = @modal title, promptContent, callback,
+    hasTitle: title?
+    buttons: [
+      {
+        text: 'Cancel'
+        type: 'dismiss'
+        style: 'btn-standard'
+        bind: if params.cancelBind? then params.cancelBind else null
+      }
+      {
+        text: 'Ok'
+        type: 'submit'
+        style: 'btn-primary'
+        bind: if params.confirmBind? then params.confirmBind else null
+      }
+    ]
+  modalId

 # --- html-filled modal ---
 ModalBuilder::html = (title, content, callback, params = false) ->
-	modalContent = content
-	modalButtons = if params.buttons? then params.buttons else [
-		{
-			text: 'Cancel'
-			type: 'dismiss'
-			style: 'btn-standard'
-		}
-		{
-			text: 'Ok'
-			type: 'submit'
-			style: 'btn-primary'
-		}
-	]
-	modalId = @modal title, modalContent, callback,
-		hasTitle: title?
-		buttons: modalButtons
-		onloadCallback: (if params.onloadCallback? then params.onloadCallback else null)
-	modalId
+  modalContent = content
+  modalButtons = if params.buttons? then params.buttons else [
+    {
+      text: 'Cancel'
+      type: 'dismiss'
+      style: 'btn-standard'
+    }
+    {
+      text: 'Ok'
+      type: 'submit'
+      style: 'btn-primary'
+    }
+  ]
+  modalId = @modal title, modalContent, callback,
+    hasTitle: title?
+    buttons: modalButtons
+    onloadCallback: (if params.onloadCallback? then params.onloadCallback else null)
+  modalId

 # --- internal stuff ---
 # --- pseudo-random id generator ---
 ModalBuilder::__getModalId = ->
-	Math.floor(Math.random() * 101) + Math.floor(Math.random() * 101)
+  Math.floor(Math.random() * 101) + Math.floor(Math.random() * 101)

 # --- container fetcher ---
 # this one could be tweaked a bit, though - e.g. could check for container visibility
 ModalBuilder::__getContainer = ->
-	container = if $('.container:visible').length isnt 0 then $('.container:visible') else $('.container-fluid:visible')
-	if container.length is 0
-		container = $('body')
-
-	if container.length is 1
-		return container
-	else
-		heights = []
-		container.each (count) ->
-			item =
-				order: count
-				height: $(@).height()
-			heights.push item
-			return
-		sortedHeights = heights.sort (a, b) ->
-			a.height
+      item =
+        order: count
+        height: $(@).height()
+      heights.push item
+      return
+    sortedHeights = heights.sort (a, b) ->
+      a.height < b.height
+    return container.eq sortedHeights[0].order


Check if RemoteDrives exists in nodes before trying to refresh, if its not first refresh the root folder

--- client/app/CommonViews/remotesmodal.coffee
+++ client/app/CommonViews/remotesmodal.coffee
@@ -240,16 +240,24 @@
         cssClass : "success"

   refreshRemoteDrives:(finalPath = '', destroy = no)=>
-    {nickname} = KD.whoami().profile
-    log finalPath
+    {nickname}  = KD.whoami().profile
+    remotesPath = "/Users/#{nickname}/RemoteDrives"
+
     tc = KD.getSingleton("finderController").treeController
-    tc.navigateTo "/Users/#{nickname}/RemoteDrives", =>
-      if finalPath
-        tc.refreshFolder tc.nodes["/Users/#{nickname}/RemoteDrives/#{finalPath}"], =>
-          tc.selectNode tc.nodes["/Users/#{nickname}/RemoteDrives/#{finalPath}"]
+    navTo = =>
+      tc.navigateTo remotesPath, =>
+        if finalPath
+          tc.refreshFolder tc.nodes["/Users/#{nickname}/RemoteDrives/#{finalPath}"], =>
+            tc.selectNode tc.nodes["/Users/#{nickname}/RemoteDrives/#{finalPath}"]
+            @destroy() if destroy
+        else
           @destroy() if destroy
-      else
-        @destroy() if destroy
+
+    unless remotesPath of tc.nodes
+      tc.refreshFolder tc.nodes["/Users/#{nickname}"], =>
+        navTo()
+    else
+      navTo()

 class RemoteListItem extends KDListItemView


Extend 'experimental' setting description

--- lib/haskell-ghc-mod.coffee
+++ lib/haskell-ghc-mod.coffee
@@ -104,7 +104,8 @@
       type: 'boolean'
       default: false
       description: 'Enable experimentai features, which are expected to land in
-                    next release of ghc-mod'
+                    next release of ghc-mod. ENABLE ONLY IF YOU KNOW WHAT YOU
+                    ARE DOING'

   activate: (state) ->
     GhcModiProcess = require './ghc-mod/ghc-modi-process'


client/util: depend on positive states on isGroupDisabled

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/util/isGroupDisabled.coffee
+++ client/app/lib/util/isGroupDisabled.coffee
@@ -2,14 +2,16 @@

 { Status } = require 'app/redux/modules/payment/constants'

-expiredStatuses = [
-  Status.EXPIRED
-  Status.PAST_DUE
-  Status.CANCELED
+allowedStatuses = [
+  Status.TRIALING
+  Status.EXPIRING
+  Status.ACTIVE
 ]

 module.exports = isGroupDisabled = (group) ->

   status = getGroupStatus group

-  status and status in expiredStatuses
+  isAllowed = status and status in allowedStatuses
+
+  return not isAllowed


Fix comment links Navigation function

Fix comment links Navigation function

- To prevent having to click the back button twice when navigating the
  browser to single comments

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -60,7 +60,7 @@

         if page isnt @props.query.page
           @props.query.page = page
-          @transitionTo @getPath(), @props.params, @props.query
+          @replaceWith @getPath(), @props.params, @props.query

       @setComments(@props.query.page ? 1)


Handle updates after landing on page

Handle updates after landing on page

--- app/talk/discussion.cjsx
+++ app/talk/discussion.cjsx
@@ -34,14 +34,12 @@
     commentValidationErrors: []

   componentDidMount: ->
+    @shouldScrollToBottom = true if @props.query?.scrollToLastComment
     @handleAuthChange()
     authClient.listen @handleAuthChange

   componentWillUnmount: ->
     authClient.stopListening @handleAuthChange
-
-  componentDidUpdate: ->
-    @scrollToBottomOfDiscussion() if @props.query?.scrollToLastComment

   handleAuthChange: ->
     @promiseToSetState user: authClient.checkCurrent()
@@ -71,6 +69,9 @@
       .then (comments) =>
         commentsMeta = comments[0]?.getMeta()
         @setState {comments, commentsMeta}, =>
+          if @shouldScrollToBottom and comments.length
+            @scrollToBottomOfDiscussion()
+            @shouldScrollToBottom = false
           callback?()

   scrollToBottomOfDiscussion: ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

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


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -50,7 +50,8 @@
       @copy().clamp()

     clamp$: (n) ->
-      @norm$(n) if @magnitude() > n
+
+      @norm$(n)

     ###*
     Creates a copy of this point.


Add helper methods.

Add helper methods.

--- src/utils.coffee
+++ src/utils.coffee
@@ -1,4 +1,4 @@
-exports.logger =
+exports.logger = logger =
   _pad: (n) ->
     n = n + ''
     if n.length >= 2 then n else new Array(2 - n.length + 1).join('0') + n
@@ -23,3 +23,8 @@
     console.error "#{@timestamp()} [#{level}] #{message}", metadata

     require('postmortem').prettyPrint err if err?
+
+for level in ['info', 'debug', 'warn', 'error']
+  do (level) ->
+    logger[level] = (message, metadata) ->
+      logger.log level, message, metadata


Distinguish betweek - permanent TCP check disable and pausing tcp check

--- src/utils.coffee
+++ src/utils.coffee
@@ -66,15 +66,19 @@

 exports.blink = blink

-exports.pauseCheck = pauseCheck = false
+pauseConnectivityCheck = false
+disableConnectivityCheck = false
 # options: An object of net.connect options, with the addition of:
 #	timeout: 10s
 checkHost = (options) ->
 	new Promise (resolve, reject) ->
-		if exports.pauseCheck
+		if disableConnectivityCheck
 			resolve()
 		else
-			reject()
+			if pauseConnectivityCheck
+				resolve()
+			else
+				reject()
 	.then ->
 		return true
 	.catch ->
@@ -83,8 +87,13 @@
 customMonitor = (options, fn) ->
 	networkCheck.monitor(checkHost, options, fn)

+# pause: A Boolean to pause the connectivity checks
+exports.pauseCheck = (pause) ->
+	pauseConnectivityCheck = pause
+
+# disable: A Boolean to disable the connectivity checks
 exports.disableCheck = (disable) ->
-	exports.pauseCheck=disable
+	disableConnectivityCheck = disable

 exports.connectivityCheck = _.once ->
 	parsedUrl = url.parse(config.apiEndpoint)


Code cleanup.

Code cleanup.

This is not needed anymore.

--- client/publication.coffee
+++ client/publication.coffee
@@ -1033,29 +1033,6 @@

     return # Make sure CoffeeScript does not return anything

-  # We do conversion of local annotation already in mousedown so that
-  # we are before mousedown on document which deselects highlights
-  'mousedown': (e, template) ->
-    return
-
-    ###
-    TODO: Get rid of this. Annotation invites are being replaced.
-
-    return unless @local
-
-    LocalAnnotation.documents.update @_id,
-      $unset:
-        local: ''
-
-    Meteor.Router.toNew Meteor.Router.annotationPath Session.get('currentPublicationId'), Session.get('currentPublicationSlug'), @_id
-
-    # On click to convert local annotation we are for sure inside the annotation, so we can
-    # immediatelly send a mouse enter event to make sure related highlight has a hovered state
-    $('.viewer .display-wrapper .highlights-layer .highlights-layer-highlight').trigger 'annotationMouseenter', [@_id]
-
-    return # Make sure CoffeeScript does not return anything
-    ###
-
   'click': (e, template) ->
     # We do not select and even deselect an annotation on clicks inside a meta menu.
     # We do the former so that when user click "delete" button, an annotation below


Add canEdit to annotationMetaMenu

Add canEdit to annotationMetaMenu

--- client/publication.coffee
+++ client/publication.coffee
@@ -690,3 +690,5 @@
     Meteor.Router.toNew Meteor.Router.publicationPath Session.get('currentPublicationId'), Session.get('currentPublicationSlug')

     return # Make sure CoffeeScript does not return anything
+
+Template.annotationMetaMenu.canEdit = Template.highlightsControl.canEdit


open app list in url hash (if found)

open app list in url hash (if found)

--- app/assets/javascripts/apps.js.coffee
+++ app/assets/javascripts/apps.js.coffee
@@ -15,16 +15,19 @@
   constructor: ->
     @toggleAppsGroups()
     @appTitles = $(".app-title")
+    hash = window.location.hash
+    activeTitle = $(".app-title##{hash}") if hash
+    activeTitle.click() if activeTitle

   toggleAppsGroups: ->
     $(".app-title").on 'click', ->
       if $(this).hasClass('show')
         $(this).removeClass('show')
         $(this).next('.app-list').removeClass('show')
-        window.location.hash = $(this).attr('id')
       else
         $(this).addClass('show')
         $(this).next('.app-list').addClass('show')
+        window.location.hash = $(this).attr('id')

   ajaxVersion: (urn, master, elem)->
     url = "//#{urn}.herokuapp.com/g5_ops/health"


minor changes

minor changes

--- app/assets/javascripts/lessons.coffee
+++ app/assets/javascripts/lessons.coffee
@@ -63,10 +63,10 @@
     str.replace /\w\S*/g, (txt) ->
       txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()

-  numMainHeadSize = $('.heading').size()
-  toRemove = []
+  numOfH1 = $('.heading').size()
+  arrowsToRemove = []
   n = 0
-  while n < numMainHeadSize
+  while n < numOfH1
     mainHeading = $('h1.heading').get(n)
     headingClass = $(mainHeading).text()
     headToc = '.' + headingClass.toLowerCase() + '-toc'
@@ -93,10 +93,10 @@
       aLink = aLink.replace('/', '-')
       $(headToc).append ' ' + aTextFinal + '  >  '
       a++
-    toRemove.push('div' + headToc + ' span:last-child')
+    arrowsToRemove.push('div' + headToc + ' span:last-child')
     r = 0
-    while r < toRemove.length
-      $(toRemove[r]).remove()
+    while r < arrowsToRemove.length
+      $(arrowsToRemove[r]).remove()
       r++
     n++


Only update the sidebar stats once every 5 seconds

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -5,7 +5,7 @@
 $ ->
   setInterval(->
     $("#status-counts").load "/status_counts"
-  , 1000)
+  , 5000)

 $ ->
   $("#reputation").load("/reputation")


Fixed survey checkbox button change events

--- app/assets/javascripts/surveys.coffee
+++ app/assets/javascripts/surveys.coffee
@@ -143,14 +143,14 @@
     if this.checked then revealNextInput($(this).data('target'))
   )

-# Attach change event handler to everything but radio button inputs. Radio button inputs are changed by JS, so each time
-# the :checked property is changed, handleChangedValue has to be called.
-$(".survey-container input").not(":radio").change((event) ->
-  target = event.target or event.srcElement
-  handleChangedValue($(target))
-)
-
-$(".survey-container select").change((event) ->
-  target = event.target or event.srcElement
-  handleChangedValue($(target))
-)
+  # Attach change event handler to everything but radio button inputs. Radio button inputs are changed by JS, so each time
+  # the :checked property is changed, handleChangedValue has to be called.
+  .on('change', ".survey-container input:not(:radio)", (event) ->
+    console.log '.survey-container input : change'
+    target = event.target or event.srcElement
+    handleChangedValue($(target))
+  )
+  .on('change', ".survey-container select", (event) ->
+    target = event.target or event.srcElement
+    handleChangedValue($(target))
+  )


Update Atom config file.

Update Atom config file.

--- roles/fedora_laptop/files/atom/config.cson
+++ roles/fedora_laptop/files/atom/config.cson
@@ -10,14 +10,12 @@
     ]
     telemetryConsent: "limited"
   editor:
-    fontSize: 14
     showIndentGuide: true
   "exception-reporting":
     userId: "a2c07b84-577e-2e1f-6364-19a81188bf7f"
   "git-plus":
     general:
       _analyticsUserId: "7c7df9ea-c0e9-44e9-8f76-384e0f077622"
-  linter:
-    showErrorPanel: false
+  linter: {}
   welcome:
     showOnStartup: false


Forgot to add a module dependency

Forgot to add a module dependency

--- client/src/app/app.coffee
+++ client/src/app/app.coffee
@@ -8,6 +8,7 @@
   'ui.ace'
   'gamEvolve.model.games'
   'gamEvolve.model.users'
+  'gamEvolve.util.eventBus'
   'gamEvolve.util.logger'
   'gamEvolve.util.gameConverter'
   'gamEvolve.game.assets'


Don't ignore xreflists.

Don't ignore xreflists.

--- client/views-table.coffee
+++ client/views-table.coffee
@@ -4,6 +4,8 @@
   if is_record_type field.type
     td().append render_field(),
                 measure()
+  else
+    root[field.type]()

 record = ->
   (tr().addClass "record").append root.record_selector(),


ensure only logged out users see sign in prompt; fixes #92

--- app/controllers/subject_viewer.coffee
+++ app/controllers/subject_viewer.coffee
@@ -106,7 +106,7 @@
     @handleLoginPrompt()

   handleLoginPrompt: =>
-    return if +localStorage.cwwClassifyCount > 2
+    return if +localStorage.cwwClassifyCount > 2 or !!User.current
     if +localStorage.cwwClassifyCount is 2
       Notify.message """
         Sign in or


Ignore err

Ignore err

--- plugin/ssh/service.coffee
+++ plugin/ssh/service.coffee
@@ -14,10 +14,12 @@
   delete: (account, callback) ->
     async.series [
       (callback) ->
-        child_process.exec "sudo pkill -u #{account.username}", callback
+        child_process.exec "sudo pkill -u #{account.username}", ->
+          callback()

       (callback) ->
-        child_process.exec "sudo userdel -rf #{account.username}", callback
+        child_process.exec "sudo userdel -rf #{account.username}", ->
+          callback()
     ], (err) ->
       throw err if err
       callback()


Ditch animating hints for now

Ditch animating hints for now

--- framer/Layer.coffee
+++ framer/Layer.coffee
@@ -1196,32 +1196,13 @@
 			borderRadius: @borderRadius * Utils.average([@canvasScaleX(), @canvasScaleY()])
 			borderWidth: 1

-		if @_draggable and @_draggable.enabled
-
-			offset = 16
-
-			layer.x += offset if (@_draggable.horizontal and @_draggable.speedX)
-			layer.y += offset if (@_draggable.vertical and @_draggable.speedY)
-
-			properties = {}
-			properties.x = frame.x - offset if (@_draggable.horizontal and @_draggable.speedX)
-			properties.y = frame.y - offset if (@_draggable.vertical and @_draggable.speedY)
-
-			animation = layer.animate
-				properties: properties
-				time: 0.4
-
-			animation.onAnimationEnd ->
-				layer.destroy()
-
-		else
-			animation = layer.animate
-				properties:
-					opacity: 0
-				time: 0.4
-
-			animation.onAnimationEnd ->
-				layer.destroy()
+		animation = layer.animate
+			properties:
+				opacity: 0
+			time: 0.4
+
+		animation.onAnimationEnd ->
+			layer.destroy()

 	##############################################################
 	## DESCRIPTOR


Clean up, point utils

Clean up, point utils

--- framer/Utils.coffee
+++ framer/Utils.coffee
@@ -262,16 +262,9 @@
 	Utils.isWebKit() and not Utils.isChrome()

 Utils.deviceType = ->
-
-	# Taken from
 	# https://github.com/jeffmcmahan/device-detective/blob/master/bin/device-detect.js
-
-	if /(mobi)/i.test(navigator.userAgent)
-		return "phone"
-
-	if /(tablet)|(iPad)/i.test(navigator.userAgent)
-		return "tablet"
-
+	return "phone" if /(mobi)/i.test(navigator.userAgent)
+	return "tablet" if /(tablet)|(iPad)/i.test(navigator.userAgent)
 	return "desktop"

 Utils.pathJoin = ->
@@ -562,6 +555,29 @@
 		y: frame.y + inset.top
 		width: frame.width - inset.left - inset.right
 		height: frame.height - inset.top - inset.bottom
+
+Utils.pointInPolygon = (point, vs) ->
+	# ray-casting algorithm based on
+	# http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+	x = point[0]
+	y = point[1]
+	inside = false
+	i = 0
+	j = vs.length - 1
+	while i < vs.length
+		xi = vs[i][0]
+		yi = vs[i][1]
+		xj = vs[j][0]
+		yj = vs[j][1]
+		intersect = yi > y != yj > y and x < (xj - xi) * (y - yi) / (yj - yi) + xi
+		if intersect
+			inside = !inside
+		j = i++
+	inside
+
+Utils.pointAngle = (p1, p2) ->
+	Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
+

 # Coordinate system


Fixed bug with request

Fixed bug with request

--- src/plugins/ubs-grab.coffee
+++ src/plugins/ubs-grab.coffee
@@ -41,13 +41,14 @@
     promise = deferred.promise
     responseCode = null

-    request command
-    .pipe fs.createWriteStream path.join resolvedPath, path.basename command
-    .on 'error', (error) ->
-      deferred.reject error
-    .on 'end', (response) ->
-      responseCode = response.status
-    .on 'finish', ->
-      deferred.resolve responseCode
+    request
+      .get command
+      .pipe(fs.createWriteStream path.join resolvedPath, path.basename command)
+      .on 'error', (error) ->
+        deferred.reject error
+      .on 'end', (response) ->
+        responseCode = response.status
+      .on 'finish', ->
+        deferred.resolve responseCode

     promise


QML parsing data to JSON fixed

QML parsing data to JSON fixed

--- impl/qml/index.coffee
+++ impl/qml/index.coffee
@@ -47,4 +47,8 @@
 				data: response
 				cookies: cookies

-		xhr.send()+		if utils.isObject(req.data)
+			data = utils.tryFunction JSON.stringify, null, [req.data], req.data
+		else
+			data = req.data
+		xhr.send data


Do not break out of list when shift-tabbing

--- vendor/assets/javascripts/mercury/regions/editable.js.coffee
+++ vendor/assets/javascripts/mercury/regions/editable.js.coffee
@@ -148,7 +148,7 @@
           return

         when 13 # enter
-          if jQuery.browser.webkit && @selection().commonAncestor().closest('li, ul', @element).length == 0
+          if jQuery.browser.webkit && @selection().commonAncestor().closest('li, ul, ol', @element).length == 0
             event.preventDefault()
             @document.execCommand('insertParagraph', false, null)
           else if @specialContainer || jQuery.browser.opera
@@ -164,7 +164,11 @@
           # indent when inside of an li
           if container.closest('li', @element).length
             handled = true
-            if event.shiftKey then @execCommand('outdent') else @execCommand('indent')
+            if ! event.shiftKey
+              @execCommand('indent')
+            # do not outdent on last ul/ol parent, or we break out of the list
+            else if container.parents('ul, ol').length > 1
+              @execCommand('outdent')

           @execCommand('insertHTML', {value: '  '}) unless handled


stub send

stub send

--- src/world.coffee
+++ src/world.coffee
@@ -50,6 +50,9 @@
   owner: (key) ->
     return @ if @get_local(key)?
     @up.owner(key)
+
+  send: (path, sender) ->
+    assert receiver = @find_path(path), "No path #{path}"

   update: (key, delta, max) ->
     result = @get(key) + delta


Moved zappa_used to its proper position

Moved zappa_used to its proper position

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -262,6 +262,7 @@
       static: (p = path.join(context.real_root, '/public')) ->
         express.static(p)
       zappa: ->
+        zappa_used = yes
         (req, res, next) ->
           send = (code) ->
             res.contentType 'js'
@@ -275,8 +276,6 @@
               else next()

     use = (name, arg = null) ->
-      zappa_used = yes if name is 'zappa'
-
       if zappa_middleware[name]
         app.use zappa_middleware[name](arg)
       else if typeof express[name] is 'function'


Re-introduced callback arguments.

Re-introduced callback arguments.

My main concern about these had always been the potential performance drop. (And before that, we couldn't do this because of the databag.)

This makes our callbacks' API closer to Express' and Socket.IO's.

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -392,7 +392,7 @@
           next: next
           param: p
         apply_helpers ctx
-        callback.apply ctx
+        callback.call ctx, req, res, next, p

     for k, v of obj
       @app.param k, build v
@@ -426,7 +426,7 @@
           ctx.data = {}
           copy_data_to ctx.data, [req.query, req.params, req.body]

-        f.apply ctx, [req, res, next]
+        f.call ctx, req, res, next

     if typeof r.handler is 'string'
       app[r.verb] r.path, r.middleware, (req, res) ->
@@ -509,7 +509,7 @@
           ctx.data = {}
           copy_data_to ctx.data, [req.query, req.params, req.body]

-        result = r.handler.apply ctx
+        result = r.handler.call ctx, req, res, next

         res.contentType(r.contentType) if r.contentType?
         if typeof result is 'string' then res.send result
@@ -574,7 +574,7 @@
             ctx = build_ctx()
             ctx.data = data
             ctx.ack = ack
-            h.apply ctx
+            h.call ctx, data, ack
         return
     return


Removed test stuff

Removed test stuff

--- src/coffee/order_processing.coffee
+++ src/coffee/order_processing.coffee
@@ -20,7 +20,7 @@
   .describe('orderEmailTemplate', 'The pathe to the email template: http://underscorejs.org/#template')
   .default('retryAttempts', 10)
   .default('shippedStateKey', 'Shipped')
-.messageCriteria 'resource(typeId="order" and id in ("e240ecba-2a4e-4843-bbf9-e43bc4f6e4eb","8dfd05cf-7631-4edb-a27b-2b1bfb41f4a9","434c6e63-3af7-4a6d-93fa-7216ad52624a","3f6671bb-5d48-4fed-b9db-38687c002d05","d8593455-2fda-4683-a8d2-de879dbd9322"))'
+.messageCriteria 'resource(typeId="order")'
 .build()
 .run (argv, stats, requestQueue) ->
   Q.spread [util.loadFile(argv.transitionConfig), util.loadFile(argv.smtpConfig), util.loadFile(argv.orderEmailTemplate)], (transitionConfigText, smtpConfig, orderEmailTemplateText) ->


handle ship3

handle ship3

--- views/components/main/parts/miniship-parts/minipanebody.cjsx
+++ views/components/main/parts/miniship-parts/minipanebody.cjsx
@@ -123,7 +123,7 @@
     {label} = @state
     updateflag = false
     switch path
-      when '/kcsapi/api_port/port', '/kcsapi/api_req_hensei/change', '/kcsapi/api_req_hokyu/charge', '/kcsapi/api_req_map/next', '/kcsapi/api_req_kaisou/remodeling', '/kcsapi/api_req_nyukyo/speedchange'
+      when '/kcsapi/api_port/port', '/kcsapi/api_req_hensei/change', '/kcsapi/api_req_hokyu/charge', '/kcsapi/api_req_map/next', '/kcsapi/api_get_member/ship3', '/kcsapi/api_req_nyukyo/speedchange'
         updateflag = true
         label = @updateLabels()
       when '/kcsapi/api_req_nyukyo/start'


Call constructor to initialize @cwd

Call constructor to initialize @cwd

Fixes similar error to https://github.com/AtomLinter/linter-pep8/issues/2

--- lib/linter-pyflakes.coffee
+++ lib/linter-pyflakes.coffee
@@ -18,7 +18,8 @@
   # regex: ""/path/to/python/file.py:28: redefinition of unused 'models' from line 5"
   regex: ':(?<line>\\d+): (?<message>.*?)\n'

-  constructor: (editorView)->
+  constructor: (editor)->
+    super editor
     @executablePath = atom.config.get 'linter-pyflakes.pyflakesExecutablePath'

 module.exports = LinterPyflakes


For now, don't run the datatable unit tests, which still require some work

--- src/resources/apps/xforms-sandbox/samples/unit-tests.coffee
+++ src/resources/apps/xforms-sandbox/samples/unit-tests.coffee
@@ -30,9 +30,9 @@
     "xbl/orbeon/autocomplete/autocomplete-unittest"
     "xbl/orbeon/button/button-unittest"
     "xbl/orbeon/currency/currency-unittest"
-    "xbl/orbeon/datatable/datatable-dynamic-unittest"
-    "xbl/orbeon/datatable/datatable-structure-unittest"
-    "xbl/orbeon/datatable/datatable-unittest"
+    #"xbl/orbeon/datatable/datatable-dynamic-unittest"
+    #"xbl/orbeon/datatable/datatable-structure-unittest"
+    #"xbl/orbeon/datatable/datatable-unittest"
     "xbl/orbeon/date-picker/date-picker-unittest"
 ]


ensure block collection uses block model

--- collections/blocks.coffee
+++ collections/blocks.coffee
@@ -4,11 +4,12 @@
 Block = require '../models/block.coffee'

 module.exports = class Blocks extends BaseArenaCollection
+  model: Block

-  url: -> "#{sd.ARENA_API_URL}/channels/#{@id}/contents"
+  url: -> "#{sd.ARENA_API_URL}/channel/#{@id}/search"

   initialize: ( models, { @id } )->
     super

   parse: (data) ->
-    data.contents+    data.blocks


Slightly more intention revealing line cleaning

--- src/robot.coffee
+++ src/robot.coffee
@@ -260,8 +260,8 @@
       for line in body.split "\n"
         break unless line[0] is '#' or line.substr(0, 2) is '//'

-        # remove leading '# '
-        cleanedLine = line[2..line.length].replace("\n", "").trim()
+        # remove leading comment
+        cleanedLine = line.replace(/^(#|\/\/)\s?/, "").trim()
         @logger.debug "parseHelp(#{scriptName}): read #{cleanedLine}"

         continue if cleanedLine.length is 0
@@ -279,6 +279,7 @@
             scriptDocumentation[currentSection].push cleanedLine.trim()
             if currentSection == 'commands'
               @commands.push cleanedLine.trim()
+
       # no current section? probably using old style documentation
       if currentSection is null
         @logger.info "#{path} is using deprecated documentation syntax"


allow configurable save interval for the brain

--- src/robot.coffee
+++ src/robot.coffee
@@ -190,9 +190,7 @@
     @data =
       users: { }

-    @saveInterval = setInterval =>
-      @save()
-    , 5000
+    @resetSaveInterval 5

   save: ->
     @emit 'save', @data
@@ -201,6 +199,12 @@
     clearInterval @saveInterval
     @save()
     @emit 'close'
+
+  resetSaveInterval: (seconds) ->
+    clearInterval @saveInterval if @saveInterval
+    @saveInterval = setInterval =>
+      @save()
+    , seconds * 1000

   # Merge keys loaded from a DB against the in memory representation
   #


Remove unused tests

Remove unused tests

--- angular/test/protractor/group_spec.coffee
+++ angular/test/protractor/group_spec.coffee
@@ -176,20 +176,6 @@
         page.expectNoElement '.group-form__parent-members-can-see-discussions'
         page.expectNoElement '.group-form__allow-public-threads'

-  describe 'editing group description from description card', ->
-    it 'allows coordinators to edit description inline', ->
-      page.loadPath 'setup_group'
-      page.expectElement '.description-card__placeholder'
-      page.click '.action-dock__button--edit_group'
-      page.fillIn '.description-card__textarea', "Brand spankin' new group description"
-      page.click '.description-card__save'
-      page.expectNoElement '.description-card__placeholder'
-      page.expectText '.description-card__text', "Brand spankin' new group description"
-
-    it 'prevents non-coordinators from editing description inline', ->
-      page.loadPath 'setup_group_as_member'
-      page.expectNoElement '.action-dock__button--edit_group'
-
   describe 'editing group settings via group form', ->
     beforeEach ->
       page.loadPath('setup_group')


Make sure to dispose config on next deactivation

--- lib/operator-config.coffee
+++ lib/operator-config.coffee
@@ -32,7 +32,7 @@
     if @settings[id]?
       # TODO: Investigate why settings is not getting removed properly
       console.error("#{id} has already been activated")
-      return new Disposable()
+      return new Disposable(@remove.bind(this, id))

     allConfigs    = extend {}, provider.config, provider.privateConfig
     @settings[id] = @convertAtomConfig allConfigs


:fire: blank

:fire: blank

--- lib/operator-insert.coffee
+++ lib/operator-insert.coffee
@@ -269,8 +269,6 @@
   trackChange: true
   supportInsertionCount: false

-
-
   canContinueOnEmptySelection: ->
     not @isOccurrence()


:lipstick:

:lipstick:

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -56,7 +56,7 @@
       atom.activatePackage(name) if activateOnSuccess
       callback()
     else
-      actom.activatePackage(name) if activateOnFailure
+      atom.activatePackage(name) if activateOnFailure
       callback(new Error("Installing '#{name}' failed."))

   new BufferedNodeProcess({command, args, exit})


Events an document binden um sie auch für später nachgeladene Events zu nutzen

--- app/assets/javascripts/calendar.js.coffee
+++ app/assets/javascripts/calendar.js.coffee
@@ -1,15 +1,15 @@
 jQuery ->
   if $('.calendars_show').length > 0
-    $('.calendar-line').on 'mouseenter', ->
+    $(document).on 'mouseenter', '.calendar-line', ->
       $(this).css('background-color', $(this).data('hlcolor')).addClass('calendar-line-highlighted')

-    $('.calendar-line').on 'mouseleave', ->
+    $(document).on 'mouseleave', '.calendar-line', ->
       $(this).css('background-color', '#000').removeClass('calendar-line-highlighted')

-    $('.calendar-categories-tabs li a').on 'click', CalendarPreset.switchPreset ;
+    $('.calendar-categories-tabs li a').on 'click', CalendarPreset.switchPreset

     # Laden wir mal die DIY Kategorie
-    CalendarPreset.selectCategoriesFromPreset('diy');
+    CalendarPreset.selectCategoriesFromPreset('diy')

     ###
     # Und dann noch das infinite scroll


Use the js-cookie.

Use the js-cookie.

--- app/assets/javascripts/application.coffee
+++ app/assets/javascripts/application.coffee
@@ -4,7 +4,7 @@
 #= require prefix
 #= require jquery
 #= require jquery_ujs
-#= require jquery-cookie
+#= require js-cookie
 #= require jquery-ui/autocomplete
 #= require compat.jquery
 #= require mousetrap


change the max upload file size to 10M

change the max upload file size to 10M

--- app/assets/javascripts/checkins.js.coffee
+++ app/assets/javascripts/checkins.js.coffee
@@ -4,7 +4,7 @@
     browse_button: 'pickfiles',
     container: 'upload-area',
     drop_element: 'upload-area',
-    max_file_size: '4mb',
+    max_file_size: '10mb',
     flash_swf_url: '/plupload/Moxie.swf',
     dragdrop: true,
     chunk_size: '4mb',


Remove Turbolinks

Remove Turbolinks

--- app/assets/javascripts/easy_app.js.coffee
+++ app/assets/javascripts/easy_app.js.coffee
@@ -11,7 +11,6 @@
 # about supported directives.
 #
 #= require jquery
-#= require jquery.turbolinks
 #= require jquery_ujs
 #= require jquery-ui/autocomplete
 #= require jquery-ui/datepicker
@@ -33,14 +32,10 @@
 #= require zeroclipboard
 #= require_tree .

-beforeChange = ->
-  ZeroClipboard.destroy()
-
 onLoad = ->
   setAlertDismiss()
   contextMenuUnselectAll()

-$(document).on('ready page:load', onLoad)
+$(document).on('ready', onLoad)
 $(document).on('click', contextMenuClick)
 $(document).on('contextmenu', contextMenuRightClick)
-$(document).on('page:before-change', beforeChange)


Do less work for calculating next item in entries column.

--- app/assets/javascripts/keyboard.js.coffee
+++ app/assets/javascripts/keyboard.js.coffee
@@ -350,27 +350,33 @@
     selectedItem

   previousItem: ->
-    @drawer = @selected.prevAll().not(":hidden").first().find('.drawer')
-    if @inDrawer()
-      prev = @selected.prevAll().not(":hidden").first();
-      if prev.length == 0
-        prev = @selected.parents('li[data-tag-id]');
-    else if @hasDrawer()
-      prev = $('ul li', @drawer).not(":hidden").last();
-    else
-      prev = @selected.prevAll().not(":hidden").first();
+    if @selectedColumnName() == 'feeds'
+      @drawer = @selected.prevAll().not(":hidden").first().find('.drawer')
+      if @inDrawer()
+        prev = @selected.prevAll().not(":hidden").first();
+        if prev.length == 0
+          prev = @selected.parents('li[data-tag-id]');
+      else if @hasDrawer()
+        prev = $('ul li', @drawer).not(":hidden").last();
+      else
+        prev = @selected.prevAll().not(":hidden").first();
+    else
+      prev = @selectedItem().prev()
     prev

   nextItem: ->
-    @drawer = $('.drawer', @selectedItem())
-    if @inDrawer()
-      next = @selected.nextAll().not(":hidden").first();
-      if next.length == 0
-        next = @selected.parents('li[data-tag-id]').nextAll().not(":hidden").first();
-    else if @hasDrawer()
-      next = $('ul li', @drawer).not(":hidden").first();
-    else
-      next = @selected.nextAll().not(":hidden").first();
+    if @selectedColumnName() == 'feeds'
+      @drawer = $('.drawer', @selectedItem())
+      if @inDrawer()
+        next = @selected.nextAll().not(":hidden").first();
+        if next.length == 0
+          next = @selected.parents('li[data-tag-id]').nextAll().not(":hidden").first();
+      else if @hasDrawer()
+        next = $('ul li', @drawer).not(":hidden").first();
+      else
+        next = @selected.nextAll().not(":hidden").first();
+    else
+      next = @selectedItem().next()
     next

   inDrawer: ->


Pass data from .form-field to .input-date

--- app/assets/javascripts/date_picker.coffee
+++ app/assets/javascripts/date_picker.coffee
@@ -24,6 +24,7 @@
   insert_date_input: ->
     @el.each ->
       $input = date_input()
+      $input.data($(@).data())
       $input.appendTo(@)
       $selects = $input.siblings('select')
       $input.val($selects.map(-> @value).get().reverse().join('/'))


deselect by clicking in open space

deselect by clicking in open space

--- app/assets/javascripts/graph_ui.js.coffee
+++ app/assets/javascripts/graph_ui.js.coffee
@@ -1,5 +1,6 @@
 class window.GraphUI
   ARROWHEAD_MARGIN = 15
+  RELATIONSHIP_SELECT_DIST = 50

   newID = 0

@@ -307,10 +308,11 @@
         ending_pos = ending

     window.GraphUI.deselectAll()
-    $('body').append('

')
-    switch ending_pos.side
-      when "top"    then offsets = [-20, -40]
-      when "bottom" then offsets = [-20,   0]
-      when "left"   then offsets = [-40, -20]
-      when "right"  then offsets = [  0, -20]
-    $('#relationship_ending_highlight').css {left: (ending_pos.x + offsets[0]); top: (ending_pos.y + offsets[1])}
+    if min_dist < RELATIONSHIP_SELECT_DIST
+      $('body').append('

')
+      switch ending_pos.side
+        when "top"    then offsets = [-20, -40]
+        when "bottom" then offsets = [-20,   0]
+        when "left"   then offsets = [-40, -20]
+        when "right"  then offsets = [  0, -20]
+      $('#relationship_ending_highlight').css {left: (ending_pos.x + offsets[0]); top: (ending_pos.y + offsets[1])}


Rename to tracks for clarity

Rename to tracks for clarity

--- components/Pulsate.coffee
+++ components/Pulsate.coffee
@@ -6,7 +6,7 @@

   constructor: ->
     @value = 0
-    @steps = [0, 70, 50, 15]
+    @tracks = [0, 70, 50, 15]
     @maxSteps = 100
     @colors = [255, 255, 255]
     @inPorts = new noflo.InPorts
@@ -28,7 +28,7 @@
     @inPorts.colors.on 'data', (@colors) =>
     @inPorts.step.on 'data', =>
       colors = []
-      for step, idx in @steps
+      for step, idx in @tracks
         ledColors = []
         for color in @colors
           unless color
@@ -36,10 +36,9 @@
             continue
           ledColors.push @step step
         colors.push ledColors
-        @steps[idx]++
-        if @steps[idx] > @maxSteps
-          @steps[idx] = 0
-
+        @tracks[idx]++
+        if @tracks[idx] > @maxSteps
+          @tracks[idx] = 0
       @outPorts.colors.send colors
     @inPorts.step.on 'disconnect', =>
       @outPorts.colors.disconnect()


Better fetching & checking of message users

--- src/slack.coffee
+++ src/slack.coffee
@@ -42,6 +42,9 @@
   loggedIn: (self, team) =>
     @robot.logger.info "Logged in as #{self.name} of #{team.name}, but not yet connected"

+    # store a copy of our own user data
+    @self = self
+
     # Provide our name to Hubot
     @robot.name = self.name

@@ -76,14 +79,13 @@
     # Ignore message subtypes that don't have a top level user property
     return if not msg.user

+    # Ignore our own messages
+    return if msg.user == @self.id
+
     channel = @client.getChannelGroupOrDMByID msg.channel
-    slackUser = @client.getUserByID msg.user
-
-    # Ignore our own messages
-    return if slackUser.name == @robot.name

     # Process the user into a full hubot user
-    user = @robot.brain.userForId slackUser.id
+    user = @robot.brain.userForId msg.user
     user.room = channel.name

     # Test for enter/leave messages


Use same order as pause menu

Use same order as pause menu

--- app/scripts/components/player/PlayerInfo.coffee
+++ app/scripts/components/player/PlayerInfo.coffee
@@ -38,19 +38,19 @@

   createBoostsVisuals: (x) ->
     playerColor = @player.color()
+    @boosts['speedb'] = Crafty.e('2D, WebGL, speedBoost, ColorEffects, HUD')
+      .attr(w: 16, h: 16)
+      .positionHud(x: x + 50, y: 29, z: 2)
+      .colorOverride(playerColor, 'partial')
     @boosts['rapidb'] = Crafty.e('2D, WebGL, rapidFireBoost, ColorEffects, HUD')
-      .attr(w: 16, h: 16)
-      .positionHud(x: x + 50, y: 28, z: 2)
-      .colorOverride(playerColor, 'partial')
-    @boosts['damageb'] = Crafty.e('2D, WebGL, damageBoost, ColorEffects, HUD')
       .attr(w: 16, h: 16)
       .positionHud(x: x + 70, y: 28, z: 2)
       .colorOverride(playerColor, 'partial')
-    @boosts['speedb'] = Crafty.e('2D, WebGL, speedBoost, ColorEffects, HUD')
+    @boosts['aimb'] = Crafty.e('2D, WebGL, aimBoost, ColorEffects, HUD')
       .attr(w: 16, h: 16)
-      .positionHud(x: x + 90, y: 29, z: 2)
+      .positionHud(x: x + 90, y: 28, z: 2)
       .colorOverride(playerColor, 'partial')
-    @boosts['aimb'] = Crafty.e('2D, WebGL, aimBoost, ColorEffects, HUD')
+    @boosts['damageb'] = Crafty.e('2D, WebGL, damageBoost, ColorEffects, HUD')
       .attr(w: 16, h: 16)
       .positionHud(x: x + 110, y: 28, z: 2)
       .colorOverride(playerColor, 'partial')


move hightlight detail into footer

move hightlight detail into footer

--- manager/app/assets/javascripts/monitoring/monitoring_views.js.coffee
+++ manager/app/assets/javascripts/monitoring/monitoring_views.js.coffee
@@ -20,9 +20,21 @@
       # display graphs
       @resources.each (res) ->
         metrics = res.get("metrics");
+
         _.each metrics, (metric, key) ->
           s = ".resource[resource_id=" + res.id + "] .metric[metric='" + key + "']"
           el = $(s + " .graph")[0]
+
+          # draw footer
+          footer = $(s + " .footer")
+          unit = ""
+          if metric.unit?
+            if metric.unit != "%"
+              unit = " " + metric.unit
+            else
+              unit = "%"
+          footer_text = sprintf("Last Value: %0.2f%s", metric.vals[metric.vals.length-1].y, unit)
+          footer.text(footer_text)

           vals = _.map metric.vals, (v) ->
             [ new Date(v.x * 1000), v.y ]
@@ -30,6 +42,8 @@
           opts = {
             labels: [ "Date/Time", "v" ]
             strokeWidth: 2
+            showLabelsOnHighlight: false
+            legend: "never"
           }

           if metric.unit == "%"
@@ -39,14 +53,18 @@
           # draw
           g = new Dygraph(el, vals, opts)

-          unit = ""
-          if metric.unit?
-            if metric.unit != "%"
-              unit = " " + metric.unit
-            else
-              unit = "%"
+          # set callbacks
+          xOptView = g.optionsViewForAxis_('x');
+          xvf = xOptView('valueFormatter');
+          opts = {
+            highlightCallback: (e, x, pts, row) ->
+              date = xvf(x, xOptView, "", g) + ", " + sprintf("val = %0.2f%s", pts[0].yval, unit)
+              footer.text(date)

-          $(s + " .footer").text(sprintf("Last Value: %0.2f%s", metric.vals[metric.vals.length-1].y, unit))
+            unhighlightCallback: (e) ->
+              footer.text(footer_text)
+          }
+          g.updateOptions(opts);

     render_with_rickshaw: ->
       render()


Fix margin for data repartition

Fix margin for data repartition

--- admin/static/coffee/namespaces/namespace.coffee
+++ admin/static/coffee/namespaces/namespace.coffee
@@ -137,7 +137,7 @@

         render: =>
             $('.tooltip').remove()
-
+            @.$el.html @template @json
             data_in_memory = 0
             data_total = 0
             data_is_ready = false
@@ -197,114 +197,108 @@
                 @json = json
                 @.$el.html @template @json

-
-                # Draw pie chart
-                if data_in_memory isnt 0 and data_total isnt 0
-                    r = 100
-                    width = 270
-                    height = 270
-                    color = (i) ->
-                        if i is 0
-                            return '#f00'
-                        else
-                            return '#1f77b4'
-
-                    data_pie = [data_in_memory, data_total-data_in_memory]
-
-                    # Remove transition for the time being. We have to use transition with opacity only the first time
-                    # For update, we should just move/extend pieces, too much work for now
-                    #@.$('.loading_text-svg').fadeOut '600', -> $(@).remove()
-                    @.$('.pie_chart-data_in_memory > g').remove()
-                    @.$('.loading_text-pie_chart').remove()
-
-
-
-                    arc = d3.svg.arc().innerRadius(0).outerRadius(r);
-                    svg = d3.select('.pie_chart-data_in_memory').attr('width', width).attr('height', height).append('svg:g').attr('transform', 'translate('+width/2+', '+height/2+')')
-                    arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc)
-
-                    # No transition for now
-                    #arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc).style('opacity', 0).transition().duration(600).style('opacity', 1)
-
-
-                # Draw histogram
-                if json.max_keys? and shards.length isnt 0
-                    @.$('.data_repartition-diagram > g').remove()
-                    @.$('.loading_text-diagram').remove()
-
-                    if json.numerous_shards? and json.numerous_shards
-                        svg_width = 700
-                        svg_height = 350
+            # Draw pie chart
+            if data_in_memory isnt 0 and data_total isnt 0
+                r = 100
+                width = 270
+                height = 270
+                color = (i) ->
+                    if i is 0
+                        return '#f00'
                     else
-                        svg_width = 350
-                        svg_height = 270
-                    margin_width = 20
-                    margin_height = 20
-
-                    width = Math.floor(svg_width/shards.length*0.7)
-                    x = d3.scale.linear().domain([0, shards.length]).range([margin_width+Math.floor(width/2), svg_width-margin_width*2])
-                    y = d3.scale.linear().domain([0, json.max_keys]).range([0, svg_height-margin_height*2.5])
-
-
-                    svg = d3.select('.data_repartition-diagram').attr('width', svg_width).attr('height', svg_height).append('svg:g')
-                    svg.selectAll('rect').data(shards)
-                        .enter()
-                        .append('rect')
-                        .attr('x', (d, i) -> return x(i))
-                        .attr('y', (d) -> return svg_height-y(d.num_keys)-margin_height)
-                        .attr('width', width)
-                        .attr( 'height', (d) -> return y(d.num_keys))
-                        .attr( 'title', (d) -> return 'Shard:'+d.boundaries+'
'+d.num_keys+' keys')
-                        ###
-                        .attr('data-num_keys', (d) -> return d.num_keys)
-                        .attr('data-shard', (d) -> return d.boundaries)
-                        ###
-
-
-                    arrow_width = 4
-                    arrow_length = 7
-                    extra_data = []
-                    extra_data.push
-                        x1: margin_width
-                        x2: margin_width
-                        y1: margin_height
-                        y2: svg_height-margin_height
-
-                    extra_data.push
-                        x1: margin_width
-                        x2: svg_width-margin_width
-                        y1: svg_height-margin_height
-                        y2: svg_height-margin_height
-
-                    svg = d3.select('.data_repartition-diagram').attr('width', svg_width).attr('height', svg_height).append('svg:g')
-                    svg.selectAll('line').data(extra_data).enter().append('line')
-                        .attr('x1', (d) -> return d.x1)
-                        .attr('x2', (d) -> return d.x2)
-                        .attr('y1', (d) -> return d.y1)
-                        .attr('y2', (d) -> return d.y2)
-                        .style('stroke', '#000')
-
-                    axe_legend = []
-                    axe_legend.push
-                        x: margin_width
-                        y: Math.floor(margin_height/2)
-                        string: 'Keys'
-                        anchor: 'middle'
-                    axe_legend.push
-                        x: Math.floor(svg_width/2)
-                        y: svg_height
-                        string: 'Shards'
-                        anchor: 'middle'
-
-                    svg.selectAll('.legend')
-                        .data(axe_legend).enter().append('text')
-                        .attr('x', (d) -> return d.x)
-                        .attr('y', (d) -> return d.y)
-                        .attr('text-anchor', (d) -> return d.anchor)
-                        .text((d) -> return d.string)
-
-                    @.$('rect').tooltip
-                        trigger: 'hover'
+                        return '#1f77b4'
+
+                data_pie = [data_in_memory, data_total-data_in_memory]
+
+                # Remove transition for the time being. We have to use transition with opacity only the first time
+                # For update, we should just move/extend pieces, too much work for now
+                #@.$('.loading_text-svg').fadeOut '600', -> $(@).remove()
+                @.$('.pie_chart-data_in_memory > g').remove()
+                @.$('.loading_text-pie_chart').remove()
+
+                arc = d3.svg.arc().innerRadius(0).outerRadius(r);
+                svg = d3.select('.pie_chart-data_in_memory').attr('width', width).attr('height', height).append('svg:g').attr('transform', 'translate('+width/2+', '+height/2+')')
+                arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc)
+
+                # No transition for now
+                #arcs = svg.selectAll('path').data(d3.layout.pie().sort(null)(data_pie)).enter().append('svg:path').attr('fill', (d,i) -> color(i)).attr('d', arc).style('opacity', 0).transition().duration(600).style('opacity', 1)
+
+            # Draw histogram
+            if json.max_keys? and shards.length isnt 0
+                @.$('.data_repartition-diagram > g').remove()
+                @.$('.loading_text-diagram').remove()
+
+                if json.numerous_shards? and json.numerous_shards
+                    svg_width = 700
+                    svg_height = 350
+                else
+                    svg_width = 350
+                    svg_height = 270
+                margin_width = 20
+                margin_height = 20
+
+                width = Math.floor((svg_width-margin_width*2)/shards.length*0.7)
+                x = d3.scale.linear().domain([0, shards.length]).range([margin_width+Math.floor(width/2), svg_width-margin_width*2])
+                y = d3.scale.linear().domain([0, json.max_keys]).range([0, svg_height-margin_height*2.5])
+
+                svg = d3.select('.data_repartition-diagram').attr('width', svg_width).attr('height', svg_height).append('svg:g')
+                svg.selectAll('rect').data(shards)
+                    .enter()
+                    .append('rect')
+                    .attr('x', (d, i) -> return x(i))
+                    .attr('y', (d) -> return svg_height-y(d.num_keys)-margin_height)
+                    .attr('width', width)
+                    .attr( 'height', (d) -> return y(d.num_keys))
+                    .attr( 'title', (d) -> return 'Shard:'+d.boundaries+'
'+d.num_keys+' keys')
+                    ###
+                    .attr('data-num_keys', (d) -> return d.num_keys)
+                    .attr('data-shard', (d) -> return d.boundaries)
+                    ###
+
+                arrow_width = 4
+                arrow_length = 7
+                extra_data = []
+                extra_data.push
+                    x1: margin_width
+                    x2: margin_width
+                    y1: margin_height
+                    y2: svg_height-margin_height
+
+                extra_data.push
+                    x1: margin_width
+                    x2: svg_width-margin_width
+                    y1: svg_height-margin_height
+                    y2: svg_height-margin_height
+
+                svg = d3.select('.data_repartition-diagram').attr('width', svg_width).attr('height', svg_height).append('svg:g')
+                svg.selectAll('line').data(extra_data).enter().append('line')
+                    .attr('x1', (d) -> return d.x1)
+                    .attr('x2', (d) -> return d.x2)
+                    .attr('y1', (d) -> return d.y1)
+                    .attr('y2', (d) -> return d.y2)
+                    .style('stroke', '#000')
+
+                axe_legend = []
+                axe_legend.push
+                    x: margin_width
+                    y: Math.floor(margin_height/2)
+                    string: 'Keys'
+                    anchor: 'middle'
+                axe_legend.push
+                    x: Math.floor(svg_width/2)
+                    y: svg_height
+                    string: 'Shards'
+                    anchor: 'middle'
+
+                svg.selectAll('.legend')
+                    .data(axe_legend).enter().append('text')
+                    .attr('x', (d) -> return d.x)
+                    .attr('y', (d) -> return d.y)
+                    .attr('text-anchor', (d) -> return d.anchor)
+                    .text((d) -> return d.string)
+
+                @.$('rect').tooltip
+                    trigger: 'hover'

             @delegateEvents()
             return @


Fixed month selection bug.

Fixed month selection bug.

--- app/assets/javascripts/planning.js.coffee
+++ app/assets/javascripts/planning.js.coffee
@@ -4,7 +4,7 @@
   $('.other-planned-hours').tooltip({ html: true, placement: 'bottom' })

   $('#month-select').on 'change', (e) ->
-    if unsavedData && confirm("You have unsaved data, do you want to leave this page and discard your changes?")
+    if !unsavedData || (unsavedData && confirm("You have unsaved data, do you want to leave this page and discard your changes?"))
       window.location = e.currentTarget.value

   $("input[name*='project[planned_hours]']").on 'focus', (e) ->


return response data from auth.user failure

--- lib/route.coffee
+++ lib/route.coffee
@@ -144,7 +144,9 @@
   ###
   _callEndpoint: (endpointContext, endpoint) ->
     # Call the endpoint if authentication doesn't fail
-    if @_authAccepted endpointContext, endpoint
+    authResult = @_authAccepted endpointContext, endpoint
+    return authResult if typeof authResult is 'object'
+    if authResult
       if @_roleAccepted endpointContext, endpoint
         endpoint.action.call endpointContext
       else
@@ -193,7 +195,8 @@
       endpointContext.user = auth.user
       endpointContext.userId = auth.user._id
       true
-    else false
+    else
+      if typeof auth is 'object' then auth else false

   ###


if nothing is entered in console, don't error out

--- src/sailthru-console.coffee
+++ src/sailthru-console.coffee
@@ -41,6 +41,11 @@

     parseCommand: (cmd, callback) ->
         cmd = cmd.trim()
+
+        if cmd.length == 0
+            @prompt()
+            return
+
         splits = cmd.split(" ")
         verb = splits[0]
         apiCall = if splits[1] then splits[1] else ""


Replace put with patch

Replace put with patch

--- app/assets/javascripts/models/base.coffee
+++ app/assets/javascripts/models/base.coffee
@@ -28,7 +28,7 @@
       method = 'PUT'
     else
       path = @route + "/save_all.json"
-      method = 'PATCH'
+      method = 'PUT'

     $.ajax
       type: method


fix for ugly error message

fix for ugly error message

--- modules/http/platforms/node/lib/LibLoader.coffee
+++ modules/http/platforms/node/lib/LibLoader.coffee
@@ -222,7 +222,7 @@

                     catch e
                         if !e.message.match 'Cannot find module' # e.code isnt 'MODULE_NOT_FOUND' ## > node 0.7
-                            console.error 'lib build error', e
+                            console.error 'lib build error', e.toString()

                 ##
                 package: _module


Update canvases when a bulb is pressed

Update canvases when a bulb is pressed

--- app/assets/javascripts/lights.coffee
+++ app/assets/javascripts/lights.coffee
@@ -21,6 +21,8 @@

     $('.bulb').on 'click', (e) ->
       setSliders(parseInt(e.target.dataset.lightId))
+      draw_hue_canvas()
+      draw_sat_canvas()

     $('#change_btn').on 'click', (e) ->
         e.preventDefault()


IDE: Change editor's mapping logic when tab is saving.

--- client/ace/lib/ace.coffee
+++ client/ace/lib/ace.coffee
@@ -679,14 +679,14 @@
       kd.warn 'possible race condition, shadowing the error! @acet'
       return

-    {tabView}    = aceView.delegate
+    { tabView }     = aceView.delegate
+    activeTabHandle = tabView.getActivePane().getHandle()
     return  unless tabView

-    {name}       = aceView.ace.data
-    {handles}    = tabView
+    { handles }  = tabView
     targetHandle = null

-    for handle in handles when handle.getOptions().title is name
+    for handle in handles when activeTabHandle.id is handle.id
       targetHandle = handle
       targetHandle.setClass 'saved'


Drop scheme in autolink

Drop scheme in autolink

--- assets/javascripts/helpers/formatting.js.coffee
+++ assets/javascripts/helpers/formatting.js.coffee
@@ -41,7 +41,7 @@
       start_index = i.indices[0] + offset
       end_index = i.indices[1] + offset
       original_text = text.substring(start_index, end_index)
-      replace_text = "#{original_text}"
+      replace_text = "#{TentStatus.Helpers.formatUrl original_text}"
       delta = replace_text.length - original_text.length
       offset += delta
       text = TentStatus.Helpers.replaceIndexRange(start_index, end_index, text, replace_text)


implements global amount calculation

implements global amount calculation

--- app/assets/javascripts/backend/budget.js.coffee
+++ app/assets/javascripts/backend/budget.js.coffee
@@ -31,6 +31,12 @@
       $("[data-budget-global-amount=#{direction}]").each ->
         sum = sum + parseFloat($(this).text())
       $(this).text(sum)
+    # global amount
+    $("[data-balance='global']").each ->
+      revenue = parseFloat($("[data-budgets-global-amount='revenue']").text())
+      expense = parseFloat($("[data-budgets-global-amount='expense']").text())
+      sum = revenue - expense
+      $(this).text(sum)
     #balance per support
   # adds a budget item when adding a support
   $(document).on 'click keyup', "a[data-association='support']", ->


added buton print and removed some button's

--- app/assets/javascripts/buttons_helper.js.coffee
+++ app/assets/javascripts/buttons_helper.js.coffee
@@ -20,11 +20,8 @@
     $(".atras_back:not(.looks_like_button)").each ->
       $(this).addClass("looks_like_button btn btn-default").html " " + $(this).html()

-    $(".cancel_button:not(.looks_like_button)").each ->
-      $(this).addClass("looks_like_button btn btn-inverse").html " " + $(this).html()
-
-    $(".pencil_button:not(.looks_like_button)").each ->
-      $(this).addClass("looks_like_button btn").html " " + $(this).html()
+    $(".print_button:not(.looks_like_button)").each ->
+      $(this).addClass("looks_like_button btn btn-info").html " " + $(this).html()
 }

 jQuery window.Helpers.ButtonsHelper.init


fix analytics tracking

fix analytics tracking

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -6,7 +6,8 @@

 { ARTSY_URL, NODE_ENV, ARTSY_ID, ARTSY_SECRET, SESSION_SECRET, PORT, ASSET_PATH,
   FACEBOOK_APP_NAMESPACE, MOBILE_MEDIA_QUERY, MOBILE_URL, APP_URL, REDIS_URL, DEFAULT_CACHE_TIME,
-  SECURE_APP_URL, CANONICAL_MOBILE_URL, IMAGES_URL_PREFIX, SECURE_IMAGES_URL } = config = require "../config"
+  SECURE_APP_URL, CANONICAL_MOBILE_URL, IMAGES_URL_PREFIX, SECURE_IMAGES_URL,
+  GOOGLE_ANALYTICS_ID, MIXPANEL_ID } = config = require "../config"
 { parse, format } = require 'url'
 _ = require 'underscore'
 express = require "express"
@@ -59,6 +60,8 @@
     SECURE_APP_URL: SECURE_APP_URL
     SECURE_IMAGES_URL: SECURE_IMAGES_URL
     IMAGES_URL_PREFIX: IMAGES_URL_PREFIX
+    GOOGLE_ANALYTICS_ID: GOOGLE_ANALYTICS_ID
+    MIXPANEL_ID: MIXPANEL_ID

   # Inject the current path into Sharify
   app.use (req, res, next) ->


Support syntax highlight for long lines

Support syntax highlight for long lines

Note: Still matches only 100 tokens for now

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -3,6 +3,7 @@
 ]
 firstLineMatch: "^\\\\documentclass(?!.*\\{beamer\\})"
 name: "LaTeX"
+limitLineLength: false
 patterns: [
   {
     comment: "This scope identifies partially typed commands such as `\\tab`. We use this to trigger “Command Completion” only when it makes sense."


don't parse json when remote server 404s

fix for issue #257

--- server/express/lib/server.coffee
+++ server/express/lib/server.coffee
@@ -152,7 +152,9 @@
         cb(e, 'Page not found', 404)
       )
       resp.on('end', ->
-        if responsedata
+        if resp.statusCode == 404
+          cb(null, 'Page not found', 404)
+        else if responsedata
           cb(null, JSON.parse(responsedata), resp.statusCode)
         else
           cb(null, 'Page not found', 404)


Make finalizedquantity optional in the ofn-on-hand directive and extract avaiable quantity to a separate method for clarity

--- app/assets/javascripts/darkswarm/directives/on_hand.js.coffee
+++ app/assets/javascripts/darkswarm/directives/on_hand.js.coffee
@@ -13,9 +13,7 @@
       ngModel.$setDirty = setDirty

     ngModel.$parsers.push (viewValue) ->
-      on_hand = parseInt(attr.ofnOnHand)
-      finalized_quantity = parseInt(attr.finalizedquantity)
-      available_quantity = on_hand + finalized_quantity
+      available_quantity = scope.available_quantity()
       if parseInt(viewValue) > available_quantity
         alert t("js.insufficient_stock", {on_hand: available_quantity})
         viewValue = available_quantity
@@ -23,3 +21,8 @@
         ngModel.$render()

       viewValue
+
+    scope.available_quantity = ->
+      on_hand = parseInt(attr.ofnOnHand)
+      finalized_quantity = parseInt(attr.finalizedquantity) || 0 # finalizedquantity is optional
+      on_hand + finalized_quantity


use more general selector for image questions

This makes them work for 'image' interactives as well

#54415592
https://www.pivotaltracker.com/story/show/54415592

--- app/assets/javascripts/image_question.js.coffee
+++ app/assets/javascripts/image_question.js.coffee
@@ -17,7 +17,7 @@
       modal: true
     })

-    @shutterbug       = new Shutterbug("iframe", null,(image_tag)=>
+    @shutterbug       = new Shutterbug(".interactive-mod > *", null,(image_tag)=>
       @set_image(image_tag)
     ,@image_question_id)


Include authenticity token in reorder

Include authenticity token in reorder

--- app/assets/javascripts/effective_datatables/reorder.js.coffee
+++ app/assets/javascripts/effective_datatables/reorder.js.coffee
@@ -8,7 +8,14 @@
   return unless oldNode? && newNode?

   url = @context[0].ajax.url.replace('.json', '/reorder.json')
-  data = {'reorder[id]': oldNode.data('reorder-resource'), 'reorder[old]': oldNode.val(), 'reorder[new]': newNode.val(), attributes: $table.data('attributes') }
+
+  data = {
+    'authenticity_token': $('head').find("meta[name='csrf-token']").attr('content'),
+    'reorder[id]': oldNode.data('reorder-resource'),
+    'reorder[old]': oldNode.val(),
+    'reorder[new]': newNode.val(),
+    'attributes': $table.data('attributes')
+  }

   @context[0].rowreorder.c.enable = false

@@ -40,4 +47,3 @@
     $table.addClass('reordering')

   column.visible(!column.visible())
-


correctly handle scalar or vectorized line widths

--- bokehjs/src/coffee/models/glyphs/segment.coffee
+++ bokehjs/src/coffee/models/glyphs/segment.coffee
@@ -36,8 +36,6 @@
     x = @renderer.xmapper.map_from_target(vx, true)
     y = @renderer.ymapper.map_from_target(vy, true)

-    threshold = Math.max(2, @visuals.line.line_width.value() / 2)
-
     point =
       x: this.renderer.plot_view.canvas.vx_to_sx(vx)
       y: this.renderer.plot_view.canvas.vy_to_sy(vy)
@@ -46,6 +44,7 @@

     candidates = @index.indices({minX: x, minY: y, maxX: x, maxY: y})
     for i in candidates
+      threshold = Math.max(2, @visuals.line.cache_select('line_width', i) / 2)
       [p0, p1] = [{x: @sx0[i], y: @sy0[i]}, {x: @sx1[i], y: @sy1[i]}]
       dist = hittest.dist_to_segment(point, p0, p1)
       if dist < threshold


Return silently when closing unknown listener

--- src/server/useragent.coffee
+++ src/server/useragent.coffee
@@ -133,7 +133,7 @@
             callback? error, v

     removeListener: (docName) ->
-      throw new Error 'Document is not open' unless @listeners[docName]
+      return unless @listeners[docName]
       model.removeListener docName, @listeners[docName]
       delete @listeners[docName]


gqueries: force-converting invalid values to 0 is rarely good

--- app/assets/javascripts/lib/models/gquery.coffee
+++ app/assets/javascripts/lib/models/gquery.coffee
@@ -6,11 +6,6 @@
   result : ->
     present_value = @get('present_value')
     future_value = @get('future_value')
-
-    if !@is_acceptable_value(present_value) && !@is_acceptable_value(future_value)
-      console.warn "Gquery #{@get('key')}: #{present_value}/#{future_value}, reset to 0"
-      present_value = 0
-      future_value  = 0

     [ [@get('present_year'), present_value], [@get('future_year'), future_value] ]


metric.js: don't downscale

metric.js: don't downscale

--- app/assets/javascripts/lib/models/metric.coffee
+++ app/assets/javascripts/lib/models/metric.coffee
@@ -42,6 +42,8 @@
       value = 0
     else
       pow = @power_of_thousand(x)
+      # in future we should downscale, too
+      pow = 0 if pow < 0
       value = x / Math.pow(1000, pow)
       value = @round_number(value, precision)

@@ -124,7 +126,7 @@
   # @param unit [String] The unit - currently {currency|joules|nounit|ton}
   # Add other units on config/locales/{en|nl}.yml
   scaling_in_words: (scale, unit) ->
-    scale = 0 if _.isNaN(scale)
+    scale = 0 if _.isNaN(scale) || scale < 0
     symbol = @scale_label["#{scale}"]
     return I18n.t("units.#{unit}.#{symbol}")


some consistency in docs

some consistency in docs

--- app/builtins.coffee
+++ app/builtins.coffee
@@ -68,7 +68,7 @@
           React.DOM.td {}, command.doc
         ]

-  component_cmd 'keys', 'Shows the key bindings', (ctx) ->
+  component_cmd 'keys', 'Displays the editor key bindings', (ctx) ->
     all_keys = {}
     # TODO some commands are functions instead of names
     build_map = (map) ->
@@ -95,7 +95,7 @@
       Components.SourceComponent value: s, language: 'json'

   doc 'object',
-    'Prints an object as JSON'
+    'Displays an object as JSON'
     """
     `object` converts an object to a string using `JSON.stringify` if possible and `new String` otherwise.
     The result is displayed using syntax highlighting.
@@ -111,7 +111,7 @@
     ObjectComponent object: o

   doc 'dir',
-    'Displays a JavaScript representation of the object'
+    'Displays a JavaScript representation of an object'
     """
     `dir` displays a JavaScript object's properties.

@@ -138,20 +138,20 @@
     ObjectBrowserComponent {object}

-  component_fn 'md', 'Renders Markdown', (ctx, string, opts) ->
+  component_fn 'md', 'Displays rendered Markdown', (ctx, string, opts) ->
     Markdown.MarkdownComponent value: string, opts: opts

   HtmlComponent = React.createClass
     displayName: 'HtmlComponent'
     render: -> React.DOM.div className: 'user-html', dangerouslySetInnerHTML: __html: @props.value

-  component_fn 'text', 'Prints text', (ctx, string) ->
+  component_fn 'text', 'Displays text', (ctx, string) ->
     React.DOM.p {}, string

-  component_fn 'pre', 'Prints preformatted text', (ctx, string) ->
+  component_fn 'pre', 'Displays preformatted text', (ctx, string) ->
     React.DOM.pre null, string

-  component_fn 'html', 'Adds some HTML', (ctx, string) ->
+  component_fn 'html', 'Displays rendered HTML', (ctx, string) ->
     HtmlComponent value: string

@@ -170,10 +170,10 @@
       React.DOM.pre {className: 'error'}, message

-  component_fn 'example', 'Makes a clickable code example', (ctx, value, opts) ->
+  component_fn 'example', 'Displays a code example', (ctx, value, opts) ->
     ExampleComponent value: value, run: opts?.run ? true

-  component_fn 'source', 'Shows source code with syntax highlighting', (ctx, language, value) ->
+  component_fn 'source', 'Displays source code with syntax highlighting', (ctx, language, value) ->
     Components.SourceComponent {language, value}

   fn 'options', 'Gets or sets options', (ctx, options) ->


Github Star

Github Star

--- docs/components/docs/DocsSidebar.cjsx
+++ docs/components/docs/DocsSidebar.cjsx
@@ -13,6 +13,12 @@
     'default':
       sidebar:
         paddingTop: '20px'
+        position: 'relative'
+
+      star:
+        position: 'absolute'
+        top: '-50px'
+        left: '10px'

       li:
         paddingBottom: '8px'
@@ -47,10 +53,19 @@
     'fixed':
       sidebar:
         top: '0'
+        bottom: '0'
         position: 'fixed'
+
+      star:
+        bottom: '20px'
+        top: 'auto'

   render: ->

+
+

+
+

       { for fileName, file of @props.files
           id = /id: (.+)/.exec(file)[1]


Allow use of external collections

Allow use of external collections

--- paginate.coffee
+++ paginate.coffee
@@ -1,7 +1,7 @@
 @__Pages = class Pages
   constructor: (collection, settings = {}) ->
     @setCollection collection
-    @setId collection
+    @setId @Collection._name
     Pages.prototype.paginations[@name] = @
     for key, value of settings
       @set key, value
@@ -84,11 +84,15 @@
     @id = "pages." + name
     @name = name
   setCollection: (collection) ->
-    try
-      @Collection = new Meteor.Collection collection
-      Pages.prototype.collections[@name] = @Collection
-    catch e
-      @Collection = Pages.prototype.collections[@name]
+    if (typeof(collection) == 'object')
+      Pages.prototype.collections[collection._name] = collection
+      @Collection = collection
+    else
+      try
+        @Collection = new Meteor.Collection collection
+        Pages.prototype.collections[@name] = @Collection
+      catch e
+        @Collection = Pages.prototype.collections[@name]
   setMethods: ->
     nm = {}
     for n, f of @methods


Fix use of SINGLE_NAL_UNIT_MAX_SIZE

Fix use of SINGLE_NAL_UNIT_MAX_SIZE

--- rtsp.coffee
+++ rtsp.coffee
@@ -271,7 +271,7 @@
     return

   sendNALUnitOverRTSP: (stream, nalUnit, pts, dts, marker) ->
-    if nalUnit.length >= SINGLE_NAL_UNIT_MAX_SIZE
+    if nalUnit.length > SINGLE_NAL_UNIT_MAX_SIZE
       @sendVideoPacketWithFragment stream, nalUnit, pts, marker  # TODO what about dts?
     else
       @sendVideoPacketAsSingleNALUnit stream, nalUnit, pts, marker  # TODO what about dts?
@@ -963,13 +963,16 @@
     nalUnit = nalUnit.slice 1

     fragmentNumber = 0
-    while nalUnit.length > SINGLE_NAL_UNIT_MAX_SIZE
+    # We subtract 1 from SINGLE_NAL_UNIT_MAX_SIZE in order to
+    # prevent nalUnit from not being fragmented when the length of
+    # original nalUnit is equal to SINGLE_NAL_UNIT_MAX_SIZE
+    while nalUnit.length > SINGLE_NAL_UNIT_MAX_SIZE - 1
       if ++stream.videoSequenceNumber > 65535
         stream.videoSequenceNumber -= 65535

       fragmentNumber++
-      thisNalUnit = nalUnit.slice 0, SINGLE_NAL_UNIT_MAX_SIZE
-      nalUnit = nalUnit.slice SINGLE_NAL_UNIT_MAX_SIZE
+      thisNalUnit = nalUnit.slice 0, SINGLE_NAL_UNIT_MAX_SIZE - 1
+      nalUnit = nalUnit.slice SINGLE_NAL_UNIT_MAX_SIZE - 1

       # TODO: sequence number should start at a random number
       rtpData = rtp.createRTPHeader


Changed link to 7_files

Changed link to 7_files

--- server/materials/data/level_2A.coffee
+++ server/materials/data/level_2A.coffee
@@ -1,4 +1,5 @@
 import label from "../lib/label"
+import labelLink from "../lib/labelLink"
 import level from "../lib/level"
 import link from "../lib/link"
 import page from "../lib/page"
@@ -21,99 +22,9 @@

     """, {skipTree: true})

-module24341 = () ->
-    page("Питон: работа с файлами", """
-

Питон: работа с файлами

-
На серьезных олимпиадах, а также во многих других ситуациях вам надо читать данные не с клавиатуры, а из файла, и выводить данные в файл, а не на "экран". В таком случае вы должны знать имена этих файлов; в задачах они, как правило, указаны, на этом сайте имена файлов почти всегда — input.txt для входных данных и output.txt для выходных.

-
-
Во многих языках программирования ввод/вывод данных через файлы очень похож на ввод/вывод с клавиатуры — те же команды, только немного другие параметры. В питоне, к сожалению, это не так.

-
-
Ввод данных

-
Самый простой способ ввести данные из файла в питоне — это сразу считать их в массив строк. Это делается так:

-
data = open("input.txt", "r").readlines()
-

-
-
Здесь input.txt — файл, откуда надо считать данные, параметр "r" указывает, что вы собираетесь именно читать (read) данные, а не записывать (write, см. ниже). Команда open, как говорят, «открывает файл на чтение», а команда readlines считывает весь файл в массив строк.

-
-
Теперь data — это массив строк, каждый элемент которого — это очередная строка из входного файла. Например, если в файле было написано

-
1 2 3
-        4 5 6
-        some text
-

-
то data будет содержать массив ["1 2 3\n", "4 5 6\n", "some text\n"]. (Здесь "\n" — это символ перевода строки, он будет на конце каждой строки массива data, кроме, возможно, последней. Если вы считываете числа, то можете о нем не беспокоиться, а если считываете строки и он вам мешает, то, как правило, от него можно избавиться командой a = a.rstrip("\n"), где a — строка, у которого вы хотите его убрать.)

-
-
Каждый элемент массива data — это как результат отдельной команды input(), которой вы пользовались для чтения с клавиатуры. Поэтому дальше вы пишете программу так же, как писали и раньше, но вместо каждого очередного обращения к input() вы обращаетесь к очередному элементу массива data. В простых случаях все просто, в более сложных вам надо аккуратно подсчитывать, к какому именно элементу массива вам надо обратиться.

-
-
Пример. Пусть во входном файле два числа по одному на строке. Их считываем так (здесь и далее слева — пример чтения с клавиатуры, справа — из файла):

-

-
a = int(input())
-        b = int(input())
-

-
-
data = open("input.txt", "r").readlines()
-        a = int(data[0])
-        b = int(data[1])
-

-

-
Если же два числа в одной строке:

-

-
a, b = map(int, input().split())
-

-
-
data = open("input.txt", "r").readlines()
-        a, b = map(int, data[0].split())
-

-

-
Более сложный пример: сначала число N, а потом N строк по одному числу в каждой:

-

-
n = int(input())
-        for i in range(n):
-            x = int(input())
-            #... что-то делаем с x
-

-
-
data = open("input.txt", "r").readlines()
-        n = int(data[0])
-        for i in range(n):
-            x = int(data[i + 1])
-            #... что-то делаем с x
-

-

-
Обратите внимание на то, что здесь написано i + 1 — потому что в первой (нулевой) строке было n.
-
-

Вывод

-
Проще всего выводить данные — записав их в одну большую строку и выведя эту строку. Вывод строки в файл делается так:

-
open("output.txt", "w").write(s)
-

-
здесь s — строка, которую нужно вывести. Это должна быть именно строка, а не число и т.д. Используйте функцию str, чтобы превратить что угодно в строку, вручную добавляйте пробелы и переводы строк (перевод строки записывается так: "\n").

-
Пример: пусть надо вывести два числа в одну строку:

-
-        open("output.txt", "w").write(str(a) + " " + str(b))
-
-
Пусть надо вывести два числа на отдельных строках:

-
-        open("output.txt", "w").write(str(a) + "\n" + str(b))
-
-
Пусть надо вывести массив чисел по одному числу на строке:

-
s = ""
-        for x in a:
-            s = s + str(x) + "\n"
-        open("output.txt", "w").write(s)
-

-
или проще, используя строковую магию:

-
open("output.txt", "w").write("\n".join(a))
-

-
Имейте в виду, что такой вызов должен быть только один раз. Если вы хотите вызывать функцию write несколько раз, то надо суметь вызвать функцию open только один раз. Рекомендуемый вариант — такой:

-
with open("output.txt", "w") as f:
-            f.write("1")
-            f.write("2")
-

-

-    """, {skipTree: true})
-
 topic_module_24341 = () ->
     return topic("*Работа с файлами (без контеста)", null, [
-        module24341(),
+        labelLink("http://notes.algoprog.ru/python_basics/7_files.html", "Работа с файлами")
     ])

 topic_module_16005_0 = () ->


Missing arguments in respond404()

Missing arguments in respond404()

Fixed bug in frontend.coffee where function call were missing the
request and response parameters causing server to crash.

--- app/frontend.coffee
+++ app/frontend.coffee
@@ -43,7 +43,7 @@
 			if fs.existsSync(path.join(filename, 'index.jade'))
 				filename = path.join(filename, 'index.jade')
 			else
-				respond404()
+				respond404(request, response)
 				return

 		if filename.indexOf('.jade') isnt -1


doc get/set for editor_settings['jupyter_classic']

--- src/smc-util/message.coffee
+++ src/smc-util/message.coffee
@@ -1593,7 +1593,7 @@
 Security key may be blank.

 ```
-  curl -u sk_abcdefQWERTY090900000000: \\
+  curl -u : \\
     -d path=Public \\
     -d project_id=9a19cca3-c53d-4c7c-8c0f-e166aada7bb6 \\
     https://cocalc.com/api/v1/public_get_directory_listing
@@ -2059,7 +2059,7 @@
 A query is either _get_ (read from database), or _set_ (write to database).
 A query is _get_ if any query keys are null, otherwise the query is _set_.

-Examples of _get_ query:
+#### Examples of _get_ query:

 Get title and description for a project, given the project id.
 ```
@@ -2156,7 +2156,42 @@
        "id":"9dd3ef3f-002b-4893-b31f-ff51440c855f"}
 ```

-Examples of _set_ query.
+Get editor settings for the present user.
+
+```
+  curl -u sk_abcdefQWERTY090900000000: \\
+    -H "Content-Type: application/json" \\
+    -d '{"query":{"accounts":{"account_id":"29163de6-b5b0-496f-b75d-24be9aa2aa1d","editor_settings":null}}}' \\
+    https://cocalc.com/api/v1/query
+  ==> {"event":"query",
+       "multi_response":false,
+       "id":"9dd3ef3f-002b-4893-b31f-ff51440c855f",
+       "query": {"accounts": {"account_id": "29163de6-b5b0-496f-b75d-24be9aa2aa1d",
+                              "editor_settings": {"auto_close_brackets": True,
+                                                  "auto_close_xml_tags": True,
+                                                  "bindings": "standard",
+                                                  "code_folding": True,
+                                                  "electric_chars": True,
+                                                  "extra_button_bar": True,
+                                                  "first_line_number": 1,
+                                                  "indent_unit": 4,
+                                                  "jupyter_classic": False,
+                                                  "line_numbers": True,
+                                                  "line_wrapping": True,
+                                                  "match_brackets": True,
+                                                  "match_xml_tags": True,
+                                                  "multiple_cursors": True,
+                                                  "show_trailing_whitespace": True,
+                                                  "smart_indent": True,
+                                                  "spaces_instead_of_tabs": True,
+                                                  "strip_trailing_whitespace": False,
+                                                  "tab_size": 4,
+                                                  "theme": "default",
+                                                  "track_revisions": True,
+                                                  "undo_depth": 300}}}}
+```
+
+#### Examples of _set_ query.

 Set title and description for a project, given the project id.
 ```
@@ -2211,6 +2246,24 @@
          "id":"ec822d6f-f9fe-443d-9845-9cd5f68bac20"}
 ```

+Set present user to open Jupyter notebooks in
+"Modern Notebook" as opposed to "Classical Notebook".
+This change not usually needed, because accounts
+default to "Modern Notebook".
+
+It is not necessary to specify the entire `editor_settings` object
+if you are only setting the `jupyter_classic` attribute.
+```
+  curl -u sk_abcdefQWERTY090900000000: \\
+    -H "Content-Type: application/json" \\
+    -d '{"query":{"accounts":{"account_id":"29163de6-b5b0-496f-b75d-24be9aa2aa1d","editor_settings":{"jupyter_classic":false}}}}' \\
+    https://cocalc.com/api/v1/query
+  ==> {"event":"query",
+       "multi_response":false,
+       "id":"9dd3ef3f-002b-4893-b31f-ff51440c855f",
+       "query": {}}
+```
+

 __NOTE:__ Information on which fields are gettable and settable in the database tables
 via API message is in file 'db-schema.coffee', in CoCalc sources on GitHub at


change support button from "Cancel" to "Close", since Cancel suggests that you are CANCELLING the support request, which is confusing.

--- src/smc-webapp/support.cjsx
+++ src/smc-webapp/support.cjsx
@@ -434,7 +434,7 @@
             <Button
                 tabIndex  = 5
                 bsStyle   ='default'
-                onClick   = {@props.close}>Cancel</Button>
+                onClick   = {@props.close}>Close</Button>
         </Modal.Footer>

 SupportForm = rclass


More flexible show and hide operations

More flexible show and hide operations

--- app/assets/javascripts/ultimate/flash.js.coffee
+++ app/assets/javascripts/ultimate/flash.js.coffee
@@ -121,15 +121,21 @@
         @hide jFlash
       , timeout

+  _hide: (jFlash, slideTime) ->
+    jFlash.slideUp slideTime, =>
+      jFlash.remove()  if @_getOptionOverFlash('removeOnHide', jFlash)
+
   hide: (jFlashes = @jFlashes()) ->
     jFlashes.each (index, element) =>
       jFlash = $(element)
       clearTimeout jFlash.data('timeoutId')
-      jFlash.addClass('hide').slideUp @_getOptionOverFlash('slideTime', jFlash), =>
-        jFlash.remove()  if @_getOptionOverFlash('removeOnHide', jFlash)
+      @_hide jFlash.addClass('hide'), @_getOptionOverFlash('slideTime', jFlash)

   _append: (jFlash) ->
     jFlash.appendTo @$el
+
+  _show: (jFlash, slideTime) ->
+    jFlash.slideDown slideTime

   show: (type, text, timeout = null, perFlashOptions = null) ->
     text = @_prepareText(text, perFlashOptions)
@@ -140,9 +146,9 @@
       if excessFlashes > 0
         @hide jActiveFlashes.slice(0, excessFlashes)
     jFlash = $("
#{text}
")
-    @_append(jFlash).slideDown @_getOptionOverFlash('slideTime', perFlashOptions)
     if perFlashOptions
       jFlash.data(key, value)  for key, value of perFlashOptions
+    @_show @_append(jFlash), @_getOptionOverFlash('slideTime', perFlashOptions)
     @_setTimeout jFlash, timeout
     jFlash


Bug fix.

Bug fix.

--- app/lib/auth.coffee
+++ app/lib/auth.coffee
@@ -44,6 +44,7 @@
   me.url = -> '/auth/whoami'
   me.fetch()
   me.on 'sync', ->
+    me.url = -> "/db/user/#{me.id}"
     trackFirstArrival() if firstTime
     if me and not me.get('testGroupNumber')?
       # Assign testGroupNumber to returning visitors; new ones in server/handlers/user


AppStorage: Added notify option to setValue.

--- client/app/lib/appstorage.coffee
+++ client/app/lib/appstorage.coffee
@@ -76,7 +76,7 @@
     return if @_storage[group]?[appId]?['data']?[key]?     then @_storage[group][appId]['data'][key]

-  setValue: (key, value, callback, group = AppStorage.DEFAULT_GROUP_NAME) ->
+  setValue: (key, value, callback, group = AppStorage.DEFAULT_GROUP_NAME, notify = no) ->

     appId                                    = @_applicationID
     @_storageData[group]                   or= {}
@@ -88,7 +88,7 @@

     @fetchStorage (storage) ->
       query = { $set : pack }
-      storage?.upsert appId, { query }, ->
+      storage?.upsert appId, { query, notify }, ->
         callback?()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -20,7 +20,7 @@
         I.activeTweens[property].complete?()
         delete I.activeTweens[property]
       else
-        if data
+        if data.easing
         f = Easing[data.easing](data.start, data.end)
         I[property] = f((t - data.startTime) / data.duration)