Delay first alert/invasion/news check

Delay first alert/invasion/news check

--- src/notifier.coffee
+++ src/notifier.coffee
@@ -23,7 +23,7 @@
 module.exports = (robot) ->
   userDB = new Users(mongoURL)
   setInterval check, NOTIFICATION_INTERVAL, robot, userDB
-  check robot, userDB
+  setTimeout check, 5000, robot, userDB

 ###*
 # Check for new alerts/invasions/news


resize works, clicking leads to edit

resize works, clicking leads to edit

--- app/assets/javascripts/angular_files/components/calendar.directive.coffee
+++ app/assets/javascripts/angular_files/components/calendar.directive.coffee
@@ -1,6 +1,6 @@
 angular.module('iamin')
   #.directive 'calendar', ($compile, $state, $interval, $timeout, Api, calendarConfig) ->
-  .directive 'calendar', ($compile, Api) ->
+  .directive 'calendar', ($compile, $location, Api) ->
     templateUrl: "/views/directives/calendar.html"
     restrict: 'A'
     link: (scope, element, attrs) ->
@@ -57,15 +57,18 @@
               # only one of these should be necessary when watching is fixed
               scope.needs.push need
               scope.fetch()
-          # eventResize: (need, delta, revertFunc) ->
-          #   Api.Schedule.updateBlock(need).then (need) ->
-          #     console.debug 'event resize'
-          #     scope.reloadSchedule()
-          #     # TODO: update needs with the new block without fetch?
+          eventResize: (need, delta, revertFunc) ->
+            Api.Calendar.updateNeed(need).then (need) ->
+              scope.fetch()
+              # TODO: update needs with the new block without fetch?
           eventDrop: (need, delta, revertFunc) ->
             Api.Calendar.updateNeed(need).then (need) ->
               scope.fetch()
           #     # TODO: update needs with the new block without fetch?
+
+          eventClick: (calEvent, jsEvent, view) ->
+            window.location.href = '/needs/' + calEvent.id + '/edit'
+            #$location.path '/needs/1/edit'

           # eventDestroy: (need, element) ->
           #   # remove scope explicitly


Allow include in standalone

Allow include in standalone

--- src/nodes/directive.coffee
+++ src/nodes/directive.coffee
@@ -37,7 +37,7 @@
         when 'global' then "#{ @namespace }['#{ name }'].apply(#{ context })"
         when 'amd' then "require('#{ name }').apply(#{ context })"
         when 'standalone'
-          if browser.process
+          if browser? && browser.process
             throw new Error("Include directive not available in the Browser when placement is standalone.")
           else
             # A standalone template cannot depend on another template so


Support switching side of tabs

Support switching side of tabs

--- lib/nord-atom-ui-vertical-tabs.coffee
+++ lib/nord-atom-ui-vertical-tabs.coffee
@@ -1,4 +1,28 @@
+selfName = 'nord-atom-ui-vertical-tabs'
+
+setDirection = (isLeft) ->
+  panes = document.querySelectorAll('atom-pane')
+  for pane, i in panes
+    pane.style.flexDirection = if isLeft then 'row' else 'row-reverse'
+
+atom.config.onDidChange(selfName + '.isLeftTab', (isLeft) ->
+  setDirection(isLeft['newValue']);
+)
+
 module.exports = NordAtomUiVerticalTabs =
+
+  config:
+    isLeftTab:
+      title: 'Left tabs'
+      description: 'Show tabs in "left side" of editor'
+      type: 'boolean'
+      default: true
+
   activate: (state) ->
+    isLeft = atom.config.get(this.selfName + '.isLeftTab')
+    setDirection(isLeft)

   deactivate: ->
+    panes = document.querySelectorAll('atom-pane')
+    for pane, i in panes
+      pane.style.flexDirection = 'column'


removing unused param in "methodCall"

removing unused param in "methodCall"

--- fizzylogo_Objects.coffee
+++ fizzylogo_Objects.coffee
@@ -157,7 +157,7 @@
   # this could be native, in which case it's a JS call,
   # or non-native, in which case it will result into
   # evaluation of the message that makes up the body.
-  methodCall: (methodBody, theContext, newSelf = @) ->
+  methodCall: (methodBody, theContext) ->
     #yield
     # note that this doesn't consume from the calling
     # context, because from the caller perspective it only matters
@@ -189,10 +189,10 @@
       if methodBody == repeatFunctionContinuation
         console.log "REPEAT FUNCTION"
         # yield from
-        theContext.returned = methodBody.call newSelf, theContext
+        theContext.returned = methodBody.call @, theContext
       else
         # native method, i.e. coffeescript/javascript code
-        theContext.returned = methodBody.call newSelf, theContext
+        theContext.returned = methodBody.call @, theContext

     contextToBeReturned = theContext
     return contextToBeReturned


Keep a list of "awaiting" state keys

Keep a list of "awaiting" state keys

--- app/lib/promise-to-set-state.coffee
+++ app/lib/promise-to-set-state.coffee
@@ -1,17 +1,23 @@
 module.exports =
   getInitialState: ->
-    awaiting: false
+    awaiting: []
+
+  componentWillMount: ->
+    @_statePromises = {}

   promiseToSetState: (keysAndPromises, callback) ->
-    @_statePromises ?= {}
-
     for key, promise of keysAndPromises
       promiseHandler = @_handlePromisedState.bind this, key, promise
       promise.then promiseHandler.bind this, false
       promise.catch promiseHandler.bind this, true
       @_statePromises[key] = promise

-    @setState awaiting: true, callback
+    {awaiting} = @state
+    if key in awaiting
+      callback()
+    else
+      awaiting.push key
+      @setState {awaiting}, callback

   _handlePromisedState: (key, promise, caught, value) ->
     # Only change the state if its current value is the same promise that's resolving.
@@ -28,8 +34,8 @@

       newState[key] = value

-      finished = Object.keys(@_statePromises).length is 0
-      if finished
-        newState.awaiting = false
+      {awaiting} = @state
+      awaiting.splice awaiting.indexOf(key), 1
+      newState.awaiting = awaiting

       @setState newState


Fix nav mesh debug display

Fix nav mesh debug display

--- app/lib/surface/Surface.coco.coffee
+++ app/lib/surface/Surface.coco.coffee
@@ -750,8 +750,9 @@
     if @showingPathFinding then @showPathFinding() else @hidePathFinding()

   hidePathFinding: ->
-    @surfaceLayer.removeChild @navRectangles if @navRectangles
-    @surfaceLayer.removeChild @navPaths if @navPaths
+    surfaceLayer = @gridLayer
+    surfaceLayer.removeChild @navRectangles if @navRectangles
+    surfaceLayer.removeChild @navPaths if @navPaths
     @navRectangles = @navPaths = null

   showPathFinding: ->
@@ -759,19 +760,18 @@

     mesh = _.values(@world.navMeshes or {})[0]
     return unless mesh
-    @navRectangles = new createjs.Container()
-    @navRectangles.layerPriority = -1
+    surfaceLayer = @gridLayer
+    @navRectangles = new createjs.Container(surfaceLayer.spriteSheet)
     @addMeshRectanglesToContainer mesh, @navRectangles
-    @surfaceLayer.addChild @navRectangles
-    @surfaceLayer.updateLayerOrder()
+    surfaceLayer.addChild @navRectangles
+    surfaceLayer.updateLayerOrder()

     graph = _.values(@world.graphs or {})[0]
-    return @surfaceLayer.updateLayerOrder() unless graph
-    @navPaths = new createjs.Container()
-    @navPaths.layerPriority = -1
+    return surfaceLayer.updateLayerOrder() unless graph
+    @navPaths = new createjs.Container(surfaceLayer.spriteSheet)
     @addNavPathsToContainer graph, @navPaths
-    @surfaceLayer.addChild @navPaths
-    @surfaceLayer.updateLayerOrder()
+    surfaceLayer.addChild @navPaths
+    surfaceLayer.updateLayerOrder()

   addMeshRectanglesToContainer: (mesh, container) ->
     for rect in mesh
@@ -796,10 +796,12 @@
     v2 = @camera.worldToSurface v2
     shape.graphics
     .setStrokeStyle(1)
-    .moveTo(v1.x, v1.y)
     .beginStroke('rgba(128,0,0,0.4)')
-    .lineTo(v2.x, v2.y)
+    .moveTo(0, 0)
+    .lineTo(v2.x - v1.x, v2.y - v1.y)
     .endStroke()
+    shape.x = v1.x
+    shape.y = v1.y
     container.addChild shape


home/sharedvm: remove unused code block

home/sharedvm: remove unused code block

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

--- client/home/lib/virtualmachines/components/sharedmachineslist/view.coffee
+++ client/home/lib/virtualmachines/components/sharedmachineslist/view.coffee
@@ -6,13 +6,6 @@

 module.exports = class SharedMachinesListView extends React.Component
-
-  # render: ->
-
-  #   <List
-  #     stacks={stacks}
-  #     shouldRenderDetails={no} />
-

   numberOfSections: -> 1


Hardcode group VM limits and make UI more user friendly.

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -556,6 +556,11 @@
                   { title : "Visible in group listings",    value : "visible" }
                   { title : "Hidden in group listings",     value : "hidden" }
                 ]
+              "Group VM"             :
+                label                : "Create virtual machines for the group"
+                itemClass            : KDOnOffSwitch
+                name                 : "group-vm"
+                defaultValue         : yes
           "VM Settings"              :
             title                    : 'VM Settings'
             buttons                  :
@@ -569,102 +574,42 @@
                 style                : "modal-cancel"
                 callback             : -> modal.modalTabs.showPreviousPane()
             fields                   :
-              # Group VM should be there for every group
-              "Group VM"             :
-                label                : "Create a shared server for the group"
-                itemClass            : KDOnOffSwitch
-                name                 : "group-vm"
-                defaultValue         : yes
-              "User Limit"           :
-                label                : "User Limit"
+              "VM Host"               :
+                label                : "VM Host"
                 itemClass            : KDSelectBox
                 type                 : "select"
-                name                 : "vm-user"
-                defaultValue         : "10"
+                name                 : "vm-host"
+                defaultValue         : "1"
                 selectOptions        : [
-                  { title : "10 users",    value : "10" }
-                  { title : "25 users",    value : "25" }
-                  { title : "50 users",    value : "50" }
-                  { title : "100 user",    value : "100" }
-                  { title : "200 users",   value : "200" }
+                  { title : "2GHz, 4GB RAM, 20GB Disk",      value : "1" }
+                  { title : "4GHz, 4GB RAM, 40GB Disk",      value : "2" }
+                  { title : "8GHz, 8GB RAM, 60GB Disk",      value : "3" }
+                  { title : "8GHz, 16GB RAM, 60GB Disk",     value : "4" }
+                  { title : "16GHz, 32GB RAM, 100GB Disk",   value : "5" }
                 ]
-              "CPU Limit"            :
-                label                : "CPU Limit"
+              "Usage Policy"         :
+                label                : "Usage Policy"
                 itemClass            : KDSelectBox
                 type                 : "select"
-                name                 : "vm-cpu"
-                defaultValue         : "10"
+                name                 : "vm-policy"
+                defaultValue         : "single"
                 selectOptions        : [
-                  { title : "10 CPU units",    value : "10" }
-                  { title : "25 CPU units",    value : "25" }
-                  { title : "50 CPU units",    value : "50" }
-                  { title : "100 CPU units",   value : "100" }
-                  { title : "200 CPU units",   value : "200" }
+                  { title : "All users share same VM host.",                  value : "single" }
+                  { title : "Limit users per VM host.",                       value : "limited" }
+                  { title : "Limit users per VM, create new when necessary.", value : "multiple" }
                 ]
-              "RAM Limit"            :
-                label                : "RAM Limit"
+              "Users per VM"         :
+                label                : "Users per VM"
                 itemClass            : KDSelectBox
                 type                 : "select"
-                name                 : "vm-ram"
-                defaultValue         : "5"
+                name                 : "vm-users"
+                defaultValue         : "25"
                 selectOptions        : [
-                  { title : "5 GBs",     value : "5" }
-                  { title : "10 GBs",    value : "10" }
-                  { title : "20 GBs",    value : "20" }
-                  { title : "30 GBs",    value : "30" }
-                  { title : "40 GBs",    value : "40" }
-                ]
-              "Disk Limit"           :
-                label                : "Disk Limit"
-                itemClass            : KDSelectBox
-                type                 : "select"
-                name                 : "vm-disk"
-                defaultValue         : "10"
-                selectOptions        : [
-                  { title : "10 GBs",    value : "10" }
-                  { title : "25 GBs",    value : "25" }
-                  { title : "50 GBs",    value : "50" }
-                  { title : "100 GBs",   value : "100" }
-                ]
-              "Member VM"            :
-                label                : "Create server(s) for each group member"
-                itemClass            : KDOnOffSwitch
-                name                 : "member-vm"
-                defaultValue         : yes
-              "Member CPU Limit"     :
-                label                : "CPU Limit"
-                itemClass            : KDSelectBox
-                type                 : "select"
-                name                 : "vm-cpu-member"
-                defaultValue         : "10"
-                selectOptions        : [
-                  { title : "10 CPU units",    value : "10" }
-                  { title : "20 CPU units",    value : "20" }
-                  { title : "30 CPU units",    value : "30" }
-                  { title : "40 CPU units",    value : "40" }
-                  { title : "50 CPU units",    value : "50" }
-                ]
-              "Member RAM Limit"     :
-                label                : "RAM Limit"
-                itemClass            : KDSelectBox
-                type                 : "select"
-                name                 : "vm-ram-member"
-                defaultValue         : "10"
-                selectOptions        : [
-                  { title : "10 GBs",    value : "10" }
-                  { title : "20 GBs",    value : "20" }
-                  { title : "30 GBs",    value : "30" }
-                ]
-              "Member Disk Limit"    :
-                label                : "Disk Limit"
-                itemClass            : KDSelectBox
-                type                 : "select"
-                name                 : "vm-disk-member"
-                defaultValue         : "10"
-                selectOptions        : [
-                  { title : "10 GBs",    value : "10" }
-                  { title : "20 GBs",    value : "20" }
-                  { title : "30 GBs",    value : "30" }
+                  { title : "5",     value : "5" }
+                  { title : "10",     value : "10" }
+                  { title : "25",     value : "25" }
+                  { title : "50",     value : "50" }
+                  { title : "100",     value : "100" }
                 ]

     modal = new KDModalViewWithForms modalOptions


working on the oauth loop-de-loop

working on the oauth loop-de-loop

--- src/device/device-mailer.coffee
+++ src/device/device-mailer.coffee
@@ -8,7 +8,7 @@
 CredentialManager = require '../models/credential-manager'

 class MailerService
-
+
   @onCreate: ({metadata, data}, callback) ->
     {auth} = metadata
     {owner} = data
@@ -40,7 +40,7 @@
     meshblu = new MeshbluHttp auth
     meshblu.register deviceData, callback

-  @getUserDeviceData({auth, owner}) =>
+  @getUserDeviceData: ({auth, owner}) =>
     deviceData = _.cloneDeep defaultUserDevice
     deviceData.owner = owner


Fix geocache service reference

Fix geocache service reference

--- db-test/rest.coffee
+++ db-test/rest.coffee
@@ -5,7 +5,7 @@

 module.exports = (services) ->
     accessService = services.access
-    geocachingService = services.geocaching
+    geocacheService = services.geocache

     app = express()


Label

Label

- WIP fix for manage_overflow.

--- src/elements/Label/Label.coffee
+++ src/elements/Label/Label.coffee
@@ -70,6 +70,42 @@
 			@addClass("cui-label-appearance-"+@_appearance)
 		else if not CUI.__ng__
 			@addClass("cui-label-appearance-auto cui-label-appearance-normal")
+
+		if @_manage_overflow
+
+			if @_manage_overflow == true
+				btn_opts = copyObject(CUI.defaults.class.Label.defaults.manage_overflow, true)
+			else
+				btn_opts = @_manage_overflow
+
+			for k, v of {
+				class: "cui-label-manage-overflow-button"
+				appearance: "flat"
+				switch: true
+				hidden: true
+				onActivate: =>
+					@__label.addClass("cui-label--show-overflow")
+				onDeactivate: =>
+					@__label.removeClass("cui-label--show-overflow")
+			}
+				if not btn_opts.hasOwnProperty(k) or
+					k not in ["class"]
+						btn_opts[k] = v
+						continue
+
+				btn_opts[k] += " "+v
+
+			@__overflow_button = new CUI.defaults.class.Button(btn_opts)
+
+			# push in global markup
+			@append(@__overflow_button)
+
+			Events.listen
+				node: @DOM
+				type: "viewport-resize"
+				call: =>
+					@checkOverflowSize()
+		return

 	initOpts: ->
 		super()
@@ -148,110 +184,68 @@
 		@__currentText

 	setContent: (content) ->
-		if @_manage_overflow
-			@__overflow_content_div = $div("cui-label-manage-overflow-content-container").append(content)
-			@replace(@__overflow_content_div, "content")
-
-			@__overflow_button_div = $div("cui-label-manage-overflow-button-container", style: "display: none;")
-
-			if @_manage_overflow == true
-				btn_opts = copyObject(CUI.defaults.class.Label.defaults.manage_overflow, true)
-			else
-				btn_opts = @_manage_overflow
-
-			for k, v of {
-				class: "cui-label-manage-overflow-button"
-				appearance: "flat"
-				switch: true
-				activate_initial: false
-				onActivate: =>
-					@setOverflowSize(true)
-				onDeactivate: =>
-					@setOverflowSize(false)
-			}
-				if not btn_opts.hasOwnProperty(k) or
-					k not in ["class"]
-						btn_opts[k] = v
-						continue
-
-				btn_opts[k] += " "+v
-
-			@__overflow_button = new CUI.defaults.class.Button(btn_opts)
-
-			@__overflow_button_div.append(@__overflow_button.DOM)
-			@append(@__overflow_button_div, "content")
-
-			CUI.DOM.waitForDOMInsert(node: @DOM[0])
-			.done =>
-				@initOverflowSize()
-
-			Events.listen
-				node: @DOM
-				type: "viewport-resize"
-				call: =>
-					@initOverflowSize()
-
-		else
-			@replace(content, "content")
-
-
-	initOverflowSize: ->
-		# reset style
-		CUI.DOM.setStyle(@__overflow_content_div,
-			height: ""
-		)
-		CUI.DOM.setStyle(@__label.map.content,
-			height: ""
-			maxHeight: ""
-		)
-		DOM.hideElement(@__overflow_button_div)
+		@replace(content, "content")
+
+		if not @_manage_overflow
+			return
+
+		# append overflow button to the whole thing
+		@append(@__overflow_button)
+
+		CUI.DOM.waitForDOMInsert(node: @DOM)
+		.done =>
+			@checkOverflowSize()
+
+		return
+
+
+	checkOverflowSize: ->
+		if not @__overflow_button
+			return
+
+		# show full content and see if it fits
+		@__label.removeClass("cui-label--show-overflow")
+		@__label.addClass("cui-label--measure-overflow")
+		@__overflow_button.hide()
+
 		dim_div = DOM.getDimensions(@__label.map.content)

-		if not (dim_div.scrollHeight > dim_div.clientHeight)
-			return
-
-		DOM.showElement(@__overflow_button_div)
-
-		dim_content = DOM.getDimensions(@__overflow_content_div)
-		dim_btn = DOM.getDimensions(@__overflow_button_div)
-
-		@__overflow_heights =
-			content_hide: dim_div.clientHeight - dim_btn.marginBoxHeight
-			content_show: dim_content.scrollHeight
-			button: dim_btn.marginBoxHeight
-
-		@setOverflowSize()
-		@
-
-	setOverflowSize: (showOverflow) ->
-
-		if showOverflow == undefined
-			showOverflow = @__overflow_button.isActive()
-
-		if showOverflow
-			content_height = @__overflow_heights.content_show
-		else
-			content_height = @__overflow_heights.content_hide
-
-		# we need to show the button by making the content smaller
-		CUI.DOM.setDimensions(@__overflow_content_div[0],
-			marginBoxHeight: content_height
-		)
-		CUI.DOM.setDimensions(@__label.map.content[0],
-			height: content_height + @__overflow_heights.button
-		)
-		CUI.DOM.setStyle(@__label.map.content[0],
-			maxHeight: content_height + @__overflow_heights.button
-		)
-		Events.wait
-			type: "transitionend"
-			node: @__label.map.content
-		.always =>
-			Events.trigger
-				type: "content-resize"
+		max_height = parseInt(dim_div.computedStyle.maxHeight)
+		if not (max_height > 0)
+			max_height = dim_div.clientHeight
+
+		if dim_div.scrollHeight > max_height
+			# really to big, show button
+			@__overflow_button.show()
+			if @__overflow_button.isActive()
+				@__label.addClass("cui-label--show-overflow")
+		else
+			@__overflow_button.hide()
+
+		@__label.removeClass("cui-label--measure-overflow")
+		@
+
+	setOverflowSize: (showOverflow, trigger = true) ->
+
+		if trigger
+			Events.wait
+				type: "transitionend"
 				node: @__label.map.content
-		@__label.DOM.classList.toggle('cui-label--overflow-hidden', !showOverflow)
-		@
+			.always =>
+				Events.trigger
+					type: "content-resize"
+					node: @__label.map.content
+
+		if !showOverflow
+			CUI.DOM.setDimensions(@__overflow_content_div, marginBoxHeight: @__overflow_content_hidden_height)
+			CUI.DOM.setStyle(@__label.map.content, maxHeight: "")
+		else
+			CUI.DOM.setStyle(@__overflow_content_div, height: "")
+			CUI.DOM.setStyle(@__label.map.content, maxHeight: "none")
+
+		@
+
+

 	getGroup: ->
 		@_group


Add isSupported method

Add isSupported method

--- app/scripts/lib/bounce/index.coffee
+++ app/scripts/lib/bounce/index.coffee
@@ -51,6 +51,20 @@

     prefixes

+  isSupported: ->
+    style = document.createElement("dummy").style
+
+    propertyLists = [
+      ["transform", "webkitTransform"],
+      ["animation", "webkitAnimation"]
+    ]
+    for propertyList in propertyLists
+      propertyIsSupported = false
+      for property in propertyList
+        propertyIsSupported ||= property of style
+
+      return false unless propertyIsSupported
+
   getKeyframeCSS: (options = {}) ->
     @name = options.name or Bounce.generateName()


Set number of keyframes based on number of bounces

--- app/scripts/lib/bounce/index.coffee
+++ app/scripts/lib/bounce/index.coffee
@@ -82,12 +82,15 @@
     "@#{prefixes.animation}keyframes #{@name} { \n  #{keyframeList.join("\n  ")} \n}"

   getKeyframes: ->
+    # TODO: Clean this up, move logic to components
     keys = [0]
     for component in @components
       startKey = (component.delay / @duration) * 100
       endKey = ((component.delay + component.duration) / @duration) * 100

-      keys.push(startKey + (i / 25) * (endKey - startKey)) for i in [0..25]
+      numKeyframes = component.easingObject.bounces * 8
+
+      keys.push(startKey + (i / numKeyframes) * (endKey - startKey)) for i in [0..numKeyframes]
       keys.push(startKey - 0.01) unless startKey is 0

     keys = keys.sort((a, b) -> a - b).map((i) -> i / 100)


Small fix

Small fix

--- coffeescript/main.coffee
+++ coffeescript/main.coffee
@@ -3,4 +3,4 @@
   $('#wrapper').you2bular
     videoId: 'mcixldqDIEQ'
     start: 9
-    loopBefore: 295000
+    loopBefore: 1000


Map moveTabToNewWindow to `W`.

Map moveTabToNewWindow to `W`.

We should reserve easily-pressed keys for commonly-used actions.

--- background_scripts/commands.coffee
+++ background_scripts/commands.coffee
@@ -163,7 +163,7 @@
   "g0": "firstTab"
   "g$": "lastTab"

-  "w": "moveTabToNewWindow"
+  "W": "moveTabToNewWindow"
   "t": "createTab"
   "yt": "duplicateTab"
   "x": "removeTab"


Teamwork: Code refactor.

Teamwork: Code refactor.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkmarkdownmodal.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkmarkdownmodal.coffee
@@ -18,17 +18,10 @@

   destroy: ->
     @setClass "scale"
+
     {targetEl} = @getOptions()
     targetEl.setClass "opacity"

-    top     = targetEl.getY()
-    left    = targetEl.getX()
-    width   = @getWidth()
-    height  = @getHeight()
-
-    newTop  = top  + 12 - (height / 2) # 11 is scaled width
-    newLeft = left - (width / 2)
-
     @setStyle
-      left   : newLeft
-      top    : newTop
+      left   : targetEl.getX() - (@getWidth()  / 2)
+      top    : targetEl.getY() - (@getHeight() / 2) + 12 # 12 is scaled width


adding doctype to iframe also fixes left right width expansion

--- src/renderer.coffee
+++ src/renderer.coffee
@@ -111,17 +111,17 @@
     @iframe.frameBorder = '0'
     @iframe.height = @iframe.width = '100%'
     @container.appendChild(@iframe)
-    @root = this.getDocument().createElement('div')
+    doc = this.getDocument()
+    doc.open()
+    doc.write('<!DOCTYPE html>')
+    doc.close()
+    @root = doc.createElement('div')
     ScribeDOM.addClass(@root, 'editor')
     @root.id = @options.id
     @root.innerHTML = ScribeNormalizer.normalizeHtml(html) if @options.keepHTML
     this.runWhenLoaded( =>
       doc = this.getDocument()
       doc.body.appendChild(@root)
-      ScribeDOM.addEventListener(doc.body, 'click', (event) =>
-        # Since IE does not properly expand width when left and right is set
-        @root.focus() if event.target?.tagName == 'BODY'
-      )
       ScribeDOM.addEventListener(@container, 'focus', =>
         @root.focus()
       )


fix typo

fix typo

--- components/Server.coffee
+++ components/Server.coffee
@@ -1,5 +1,5 @@
 noflo = require 'noflo'
-express = reqreui 'express'
+express = require 'express'
 #http = require "http"
 #uuid = require "node-uuid"


Fire collection change event to reset form after successfully setting access token cookie.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -295,7 +295,7 @@
         console.log "Access Token Validation Error: #{textStatus}"
       success: (data) ->
         set_cookie('access_token',params['access_token'],params['expires_in'])
-        $('#oauth_access_warning').remove()
+        $('#collection_select').change()

 clippy = (id) ->
   console.log "Clippy: #{id}"


Store language name information on nodes

--- src/scripts/models/content/node.coffee
+++ src/scripts/models/content/node.coffee
@@ -8,7 +8,7 @@
   require('backbone-associations')

   CONTENT_URI = "#{location.protocol}//#{settings.cnxarchive.host}:#{settings.cnxarchive.port}/contents"
-
+
   LICENSES = {
     'by': 'Attribution License CC BY'
     'by-sa': 'Attribution-ShareAlike CC BY-SA'
@@ -23,8 +23,7 @@

     parse: (response, options) ->
       # Don't overwrite the title from the book's table of contents
-      if @get('title')
-        delete response.title
+      if @get('title') then delete response.title

       # Determine the license name, version, and url
       license = response.license.match(/^http:\/\/creativecommons\.org\/licenses\/(.+)\/(.+)\//)
@@ -38,4 +37,7 @@

       response.license = license

+      # Add languageName property to nodes for faster references in views
+      response.languageName = settings.languages[response.language]
+
       return response


Improve failCases report in ESM test

Improve failCases report in ESM test

--- src/test/shared/process-results.coffee
+++ src/test/shared/process-results.coffee
@@ -4,7 +4,7 @@
 	total = 0
 	success = 0
 	fail = 0
-	failCases = []
+	failCasesList = []

 	printMsg('summary')

@@ -13,12 +13,18 @@
 		success += result.success
 		fail += result.fail
 		if result.failCases.length > 0
-			failCases.push result.failCases
+			failCasesList.push {
+				jobDesc: result.jobDesc
+				failCases: result.failCases
+			}
 		console.log "#{result.jobDesc}: #{result.summary}"

-	if failCases.length > 0
+	if failCasesList.length > 0
 		printMsg('failResults')
-		console.log failCases
+		for eachFailCases in failCasesList
+			console.log "-> #{eachFailCases.jobDesc}"
+			console.log eachFailCases.failCases
+			console.log '\n'

 	printMsg('testDone')


remove double PreView definition

remove double PreView definition

--- assets/scripts/app/views/log.coffee
+++ assets/scripts/app/views/log.coffee
@@ -16,8 +16,6 @@
     willDestroyElement: ->
       job = @get('job')
       job.unsubscribe() if job
-
-  PreView: Em.View.extend

   PreView: Em.View.extend
     templateName: 'jobs/pre'


Updated 'Quit' menu item

Updated 'Quit' menu item

--- views/app.cjsx
+++ views/app.cjsx
@@ -72,9 +72,14 @@
         },
         { type: 'separator' },
         {
-          label: 'Quit'
+          label: 'Quit Poi'
           accelerator: 'CmdOrCtrl+Q'
-          selector: 'terminate:'
+          click: ->
+            # The terminate selector will ignore the 'poi.confirm.quit' setting
+            # and try to close any (plugin) window it can close first.
+            # So here we should only try to close the main window and let it handle all the rest.
+            remote.getCurrentWindow().focus()
+            window.close()
         }
       ]
     },


Fix overriding varable

Fix overriding varable

--- views/app.cjsx
+++ views/app.cjsx
@@ -279,8 +279,9 @@
     nextSpot: null
     mapspot: mapspot
   handleResponse: (e) ->
-    {path, body} = e.detail
-    switch path
+    reqPath = e.detail.path
+    {body} = e.detail
+    switch reqPath
       when '/kcsapi/api_port/port'
         @setState
           status: null


Editor: ListItem class for listing current vms.

--- client/Finder/AppController.coffee
+++ client/Finder/AppController.coffee
@@ -93,3 +93,18 @@
         @controller.unmountVm info.hostnameAlias

+class VMListItem extends KDListItemView
+
+  constructor:(options={}, data)->
+    super options, data
+    {hostnameAlias} = @getData()
+    KD.singletons.vmController.info hostnameAlias, (err, name, info)=>
+      return KD.showError err if err
+      @addSubView vmLabel  = new KDLabelView title: hostnameAlias
+      @addSubView vmSwitch = new KodingSwitch
+        cssClass     : 'dark'
+        defaultValue : if info.state is "RUNNING" then true else false
+        callback     : (state)=>
+          @getDelegate().emit "VmStateChanged", {state, hostnameAlias}
+
+  viewAppended:JView::viewAppended


Test/Teams Check all links in the footer of the team homepage

--- client/test/lib/teams/teams.coffee
+++ client/test/lib/teams/teams.coffee
@@ -4,6 +4,8 @@
 user = utils.getUser()
 createLink = "#{helpers.getUrl()}/Teams/Create"
 inviteLink = "#{helpers.getUrl()}/Teams/Create?email=#{user.email}"
+linkSelector = '.content-page.Team section p'
+resetPasswordSelector = '.TeamsModal-button--green'

 module.exports =

@@ -59,5 +61,21 @@
       browser.url url
       browser.waitForElementVisible '.content-page.Team section a.previous-team', 20000

+  checkAllLinkInTheFooter: (browser) ->
+    url = helpers.getUrl(yes)
+    browser.maximizeWindow()
+    browser.url url
+    browser
+      .waitForElementVisible linkSelector + ' p:nth-of-type(1)', 20000
+      .click linkSelector + ' p:nth-of-type(1) a'
+      .waitForElementVisible '.TeamsModal.TeamsModal--login', 20000
+      .click linkSelector + ':nth-of-type(2) a'
+      .waitForElementVisible '.content-page.Team .TeamsModal--create', 20000
+      .url url
+      .click linkSelector + ':nth-of-type(3) a'
+      .waitForElementVisible resetPasswordSelector, 20000
+      .assert.containsText resetPasswordSelector, 'RECOVER PASSWORD'
+
+
   after: (browser) ->
     browser.end()


Tests: Create 10 user.

Tests: Create 10 user.

--- client/test/lib/utils/utils.coffee
+++ client/test/lib/utils/utils.coffee
@@ -7,7 +7,7 @@

     users = []

-    for i in [1..2]
+    for i in [1..10]

       name     = faker.Name.findName()
       username = faker.Helpers.slugify(faker.Internet.userName()).toLowerCase().replace(/\./g, '').replace(/_/g, '')


Add support for retained messages

Add support for retained messages

--- watcher.coffee
+++ watcher.coffee
@@ -22,12 +22,17 @@
   .options "t",
     describe: "topic to publish messages on"
     demand: true
+  .options "r",
+    describe: "retain last message in broker"
+    default: false
+    boolean: true
   .argv

 file = argv.f
 topic = argv.t
 port = argv.p
 host = argv.h
+retain = argv.r

 unless fs.existsSync file
   console.error "mqtt-log-monitor: File #{file} does not exist"
@@ -52,6 +57,7 @@
       client.publish
         topic: topic
         payload: line
+        retain: retain

   client.on 'connack', (packet) ->
     if packet.returnCode isnt 0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -21,7 +21,7 @@

   equals player.I.x, 8

-test "should move "
+test "should move player right while"

 module()


test signout with unregistered post_logout_redirect_uri; invalid id_token_hint

--- test/oidc/signout.coffee
+++ test/oidc/signout.coffee
@@ -14,6 +14,7 @@
 settings = require '../../boot/settings'
 Client = require '../../models/Client'
 IDToken = require '../../models/IDToken'
+InvalidTokenError = require '../../errors/InvalidTokenError'
 {signout} = require('../../oidc')

@@ -105,23 +106,93 @@
           res.send.should.not.have.been.called

-      describe 'with unknown uri', ->
-
-        it 'should logout'
-        it 'should update OP browser state'
-        it 'should respond 204'
-
-
-      describe 'with valid client', ->
-
-        it 'should logout'
-        it 'should update OP browser state'
-        it 'should redirect'
+      describe 'and unknown uri', ->
+
+        before ->
+          client = new Client
+            post_logout_redirect_uris: []
+          sinon.stub(Client, 'get').callsArgWith(1, null, client)
+          opbs = 'b3f0r3'
+          req =
+            query:
+              post_logout_redirect_uri: 'http://wrong.com'
+              id_token_hint: validIDToken
+            session:
+              opbs: opbs
+            logout: sinon.spy()
+          res =
+            set: sinon.spy()
+            send: sinon.spy()
+            redirect: sinon.spy()
+          next = sinon.spy()
+          signout(req, res, next)
+
+        after ->
+          Client.get.restore()
+
+        it 'should logout', ->
+          req.logout.should.have.been.called
+
+        it 'should update OP browser state', ->
+          req.session.opbs.should.not.equal opbs
+
+        it 'should respond 204', ->
+          res.send.should.have.been.calledWith 204
+
+
+      describe 'and valid uri', ->
+
+        before ->
+          client = new Client
+            post_logout_redirect_uris: ['http://example.com']
+          sinon.stub(Client, 'get').callsArgWith(1, null, client)
+          opbs = 'b3f0r3'
+          req =
+            query:
+              post_logout_redirect_uri: 'http://example.com'
+              id_token_hint: validIDToken
+            session:
+              opbs: opbs
+            logout: sinon.spy()
+          res =
+            set: sinon.spy()
+            send: sinon.spy()
+            redirect: sinon.spy()
+          next = sinon.spy()
+          signout(req, res, next)
+
+        after ->
+          Client.get.restore()
+
+        it 'should logout', ->
+          req.logout.should.have.been.called
+
+        it 'should update OP browser state', ->
+          req.session.opbs.should.not.equal opbs
+
+        it 'should not respond 204', ->
+          res.send.should.not.have.been.calledWith 204
+
+        it 'should redirect', ->
+          res.redirect.should.have.been.calledWith req.query.post_logout_redirect_uri

     describe 'with invalid token', ->

-      it 'should provide an InvalidTokenError'
+      invalidIDToken = 'WRONG'
+
+
+      before ->
+        req =
+          query:
+            post_logout_redirect_uri: 'http://example.com'
+            id_token_hint: invalidIDToken
+        res = {}
+        next = sinon.spy()
+        signout(req, res, next)
+
+      it 'should provide an InvalidTokenError', ->
+        next.should.have.been.calledWith sinon.match.instanceOf InvalidTokenError


tests for the graphics constructor

tests for the graphics constructor

--- test/src/graphics.coffee
+++ test/src/graphics.coffee
@@ -1,3 +1,30 @@
 describe "love.graphics", ->
   it 'exists', ->
     expect(Love.Graphics).to.be.a("function")
+
+  describe 'constructor', ->
+    beforeEach ->
+      # Is the need for this indicative of something bad?
+      Love.element = null
+
+    describe 'when called without arguments', ->
+      it 'uses default values for width and height', ->
+        graphics = new Love.Graphics()
+        expect(graphics.getWidth()).to.equal(800)
+        expect(graphics.getHeight()).to.equal(600)
+
+    describe 'when called with arguments', ->
+      it 'uses those arguments for the width and height of the graphics context', ->
+        [width, height] = [300, 400]
+        graphics = new Love.Graphics(width, height)
+        expect(graphics.getWidth()).to.equal(width)
+        expect(graphics.getHeight()).to.equal(height)
+
+    it 'creates a default canvas rendering context', ->
+      graphics = new Love.Graphics()
+      expect(graphics.default_canvas).to.be.an.instanceOf(Love.Graphics.Canvas2D)
+
+    it 'sets the default background and foreground colors', ->
+      graphics = new Love.Graphics()
+      expect(graphics.getColor()).to.eql(new Love.Color(255, 255, 255).unpack())
+      expect(graphics.getBackgroundColor()).to.eql(new Love.Color(0, 0, 0).unpack())


Adding a test for Issue #669

Adding a test for Issue #669

--- test/test_classes.coffee
+++ test/test_classes.coffee
@@ -48,7 +48,11 @@

 class TwoClass extends OneClass

-ok (new TwoClass('three')).name is 'three'
+Function.prototype.new = -> new this arguments...
+
+ok (TwoClass.new('three')).name is 'three'
+
+delete Function.prototype.new

 # And now the same tests, but written in the manual style:


fix typos in test server

fix typos in test server

--- test_server/index.coffee
+++ test_server/index.coffee
@@ -64,8 +64,8 @@
     res.send 400, {error: "invalid label"}
     return next()

-  record[id].label = req.params.label.trim()
-  res.send record[id]
+  records[id].label = req.params.label.trim()
+  res.send records[id]
   return next()

 server.del '/my-record/:id', (req,res,next) ->
@@ -79,7 +79,7 @@
     res.send 404, {error: "no such record"}
     return next()

-  delete record[id]
+  delete records[id]
   res.send {}
   return next()


remove console debug

remove console debug

--- src/opensong-parser.coffee
+++ src/opensong-parser.coffee
@@ -107,8 +107,6 @@
       else
         if /^[ 0-9]/.test(line)
           dataObject.lines.push {lyrics: [line.substr(1)]}
-        else
-          console?.log "no suppport for '#{line}'"

   dataModel


Add propTypes

Add propTypes

--- src/progress/exercise.cjsx
+++ src/progress/exercise.cjsx
@@ -3,6 +3,12 @@

 ExerciseProgress = React.createClass
   displayName: 'ExerciseProgress'
+  propTypes:
+    className: React.PropTypes.string
+    exercise: React.PropTypes.shape(
+      is_completed: React.PropTypes.bool
+      is_correct: React.PropTypes.bool
+    )
   getDefaultProps: ->
     exercise: {}
   render: ->


Fixing Typo

Fixing Typo

--- src/quo.environment.coffee
+++ src/quo.environment.coffee
@@ -31,7 +31,7 @@
         environment = {}
         environment.browser = _detectBrowser(user_agent)
         environment.os = _detectOS(user_agent)
-        environment.isMobile = (if (environment.os) then true else false)
+        environment.isMobile = !!environment.os
         environment.screen = _detectScreen()
         environment

@@ -40,7 +40,7 @@
         if is_webkit then is_webkit[0] else user_agent

     _detectOS = (user_agent) ->
-        detected_os = undefined
+        detected_os = null
         for os of SUPPORTED_OS
             supported = user_agent.match(SUPPORTED_OS[os])
             if supported


Logging message when auth fails

Logging message when auth fails

--- src/resource_client.coffee
+++ src/resource_client.coffee
@@ -85,7 +85,9 @@
       success = (result) ->
         callback(result)
       error = (res, err) =>
-        return @sync.didFailAuth() if res.status == 401
+        res.status == 401
+          console.log "failed with error 401 #{err}"
+          return @sync.didFailAuth()
         console.log "Fetch failed #{res} #{err}", res, err
       options =
         type: method


Updating conditionals

Updating conditionals

--- src/scanner/resolve.coffee
+++ src/scanner/resolve.coffee
@@ -133,8 +133,8 @@
     return file if (file = resolve_index main)?

     # keep searching on parent node_module's folders
-    if filepath is not '/' and non_recurse is not true
-      resolve_module path.join(filepath, '..'), di
+    if filepath is not '/' and non_recurse isnt true
+      resolve_module path.join(filepath, '..'), dir

   # if there's no json, move on with other searches
   idpath = (path.join nmods, id)
@@ -146,7 +146,7 @@
   return file if (file = resolve_index idpath)?

   # keep searching on parent node_module's folders
-  if filepath isnt '/' and non_recurse is not true
+  if filepath isnt '/' and non_recurse isnt true
     resolve_module path.join(filepath, '..'), id


Adding another geordie helper

Adding another geordie helper

--- src/scripts/geordie.coffee
+++ src/scripts/geordie.coffee
@@ -23,3 +23,6 @@

   robot.respond /(whey-aye man|fog on the tyne)/i, (msg) ->
     msg.send msg.random geordies
+
+  robot.respond /(what is ryan saying?)/i, (msg) ->
+    msg.send "We need to get some subtitles for Ryan"


tighten up hangout regex to only match hangout and not hangout2 etc

--- src/scripts/hangout.coffee
+++ src/scripts/hangout.coffee
@@ -15,7 +15,7 @@
 #   nicoritschel

 module.exports = (robot) ->
-  robot.respond /hangout\s?(.*)?/i, (msg) ->
+  robot.respond /hangout\s.*/i, (msg) ->
     if process.env.HUBOT_HANGOUT_URL
       msg.send process.env.HUBOT_HANGOUT_URL
     else


remove unused parameter

remove unused parameter

--- src/scripts/jupiter.coffee
+++ src/scripts/jupiter.coffee
@@ -63,7 +63,7 @@
 randomColor = ->
   colors[randomNumber colors.length]

-beginTriangle = ({a, b, c, color}, fill = false) ->
+beginTriangle = ({a, b, c, color}) ->
   jupiter.fillStyle = color
   jupiter.beginPath()
   jupiter.moveTo a.x, a.y


Local scope is enough

Local scope is enough

--- src/scripts/servant.coffee
+++ src/scripts/servant.coffee
@@ -1,7 +1,7 @@
 # Hubot is very attentive

 module.exports = (robot) ->
-  @name_regex = new RegExp("#{robot.name}\\?$", "i")
+  name_regex = new RegExp("#{robot.name}\\?$", "i")

-  robot.hear @name_regex, (msg) ->
+  robot.hear name_regex, (msg) ->
     msg.reply "Yes, master?"


update comment and use removeProject to update project

--- coffee/services/dataAdapter.coffee
+++ coffee/services/dataAdapter.coffee
@@ -52,11 +52,15 @@

     ###*
     # all data.
-    # @param {DataModel}  data's url
+    # @type {object}
+    # @prop {DataModel} url of server - DataModel
     ###
     _data: {}

+    ###
     # filtered data.
+    # @type Array of AccountModel
+    ###
     _filteredData: []
     @property 'accounts',
       get: -> @_filteredData
@@ -209,13 +213,12 @@
     ###
     addProjects: (projects) ->
       if not projects? or projects.length is 0 then return
-      for p in projects
-        @_data[projects[0].url].projects.remove((n) -> return n.equals(p))
+      @removeProjects(projects)
       @_data[projects[0].url].projects.add(projects)
-      if not @selectedProject then @selectedProject = projects[0]
       for a in @_filteredData when a.url is projects[0].url
         a.projects = a.projects or []
         a.projects.add(projects)
+      if not @selectedProject then @selectedProject = projects[0]

     ###*
     # remove project from account.
@@ -225,7 +228,7 @@
       if not projects? or projects.length is 0 then return
       for p in projects
         @_data[projects[0].url].projects.remove((n) -> return n.equals(p))
-      for a in @_filteredData when a.url is projects[0].url
+      for a in @_filteredData when a.projects and a.url is projects[0].url
         a.projects.remove((n) -> return n.equals(p))

     ###*


Add capability checks, refs #202

Add capability checks, refs #202

--- components/ContextToRuntime.coffee
+++ components/ContextToRuntime.coffee
@@ -20,8 +20,10 @@

   # Check for platform-specific components
   runtimeType = component.code.match /@runtime ([a-z\-]+)/
-  if runtimeType and runtimeType[1] isnt runtime.definition.type
-    return
+  if runtimeType
+    return unless runtimeType[1] in ['all', runtime.definition.type]
+
+  return unless runtime.canDo 'component:setsource'

   runtime.sendComponent 'source',
     name: component.name
@@ -32,8 +34,9 @@

 sendGraph = (graph, runtime, project) ->
   if graph.properties.environment?.type
-    if graph.properties.environment.type isnt 'all' and graph.properties.environment.type isnt runtime.definition.type
-      return
+    return unless graph.properties.environment.type in ['all', runtime.definition.type]
+
+  return unless runtime.canDo 'protocol:graph'

   graphId = graph.name or graph.properties.id
   runtime.sendGraph 'clear',


backport syncstring maintenance delay code

--- src/smc-hub/rethink.coffee
+++ src/smc-hub/rethink.coffee
@@ -3152,6 +3152,7 @@
             map_limit         : 1     # how much parallelism to use
             limit             : 1000 # do only this many
             repeat_until_done : true
+            delay             : 0
             cb                : undefined
         dbg = @dbg("syncstring_maintenance")
         dbg(opts)
@@ -3173,7 +3174,11 @@
                     console.log("*** #{i}/#{syncstrings.length}: archiving string #{string_id} ***")
                     @archive_patches
                         string_id : string_id
-                        cb        : cb
+                        cb        : (err) ->
+                           if err or not opts.delay
+                               cb(err)
+                           else
+                               setTimeout(cb, opts.delay)
                 async.mapLimit(syncstrings, opts.map_limit, f, cb)
         ], (err) =>
             if err


Support for .css files inlined into <style> tags (#38)

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -494,6 +494,19 @@
     else
       throw new SVGTilerException "Unrecognized extension in filename #{filename}"

+class Style extends Input
+  load: (@css) ->
+
+class CSSStyle extends Style
+  @title: "CSS style file"
+  parse: (filedata) ->
+    @load filedata
+
+class Styles
+  constructor: (@styles = []) ->
+  push: (map) ->
+    @styles.push map
+
 class Mapping extends Input
   load: (data) ->
     @map = {}
@@ -648,7 +661,7 @@
               row.pop()
           j--
     @data = data
-  writeSVG: (mappings, filename) ->
+  writeSVG: (mappings, styles, filename) ->
     ## Default filename is the input filename with extension replaced by .svg
     unless filename?
       filename = path.parse @filename
@@ -658,9 +671,9 @@
         filename.base = filename.base[...-filename.ext.length] + '.svg'
       filename = path.format filename
     console.log '->', filename
-    fs.writeFileSync filename, @renderSVG mappings
+    fs.writeFileSync filename, @renderSVG mappings, styles
     filename
-  renderSVGDOM: (mappings) ->
+  renderSVGDOM: (mappings, styles) ->
     ###
     Main rendering engine, returning an xmldom object for the whole document.
     Also saves the table of symbols in `@symbols`, the corresponding
@@ -673,6 +686,10 @@
     svg.setAttribute 'xmlns:xlink', XLINKNS
     svg.setAttribute 'version', '1.1'
     #svg.appendChild defs = doc.createElementNS SVGNS, 'defs'
+    ## <style> tags for CSS
+    for style in styles.styles
+      svg.appendChild styleTag = doc.createElementNS SVGNS, 'style'
+      styleTag.textContent = style.css
     ## Look up all symbols in the drawing.
     missing = {}
     @symbols =
@@ -809,8 +826,8 @@
     svg.setAttributeNS SVGNS, 'height', @height = viewBox[3]
     svg.setAttributeNS SVGNS, 'preserveAspectRatio', 'xMinYMin meet'
     doc
-  renderSVG: (mappings) ->
-    out = new XMLSerializer().serializeToString @renderSVGDOM mappings
+  renderSVG: (mappings, styles) ->
+    out = new XMLSerializer().serializeToString @renderSVGDOM mappings, styles
     ## Parsing xlink:href in user's SVG fragments, and then serializing,
     ## can lead to these null namespace definitions.  Remove.
     out = out.replace /\sxmlns:xlink=""/g, ''
@@ -975,9 +992,9 @@
       filename2.base += @constructor.filenameSeparator + drawing.subname
     filename2.base += extension
     path.format filename2
-  writeSVG: (mappings, filename) ->
+  writeSVG: (mappings, styles, filename) ->
     for drawing in @drawings
-      drawing.writeSVG mappings, @subfilename '.svg', drawing
+      drawing.writeSVG mappings, styles, @subfilename '.svg', drawing
   writeTeX: (filename) ->
     for drawing in @drawings
       drawing.writeTeX @subfilename '.svg_tex', drawing
@@ -1027,11 +1044,15 @@
       new Context @drawing, i, j

 extensionMap =
+  # Mappings
   '.txt': ASCIIMapping
   '.js': JSMapping
   '.jsx': JSMapping
   '.coffee': CoffeeMapping
   '.cjsx': CoffeeMapping
+  # Styles
+  '.css': CSSStyle
+  # Drawings
   '.asc': ASCIIDrawing
   '.ssv': SSVDrawing
   '.csv': CSVDrawing
@@ -1168,6 +1189,7 @@

 main = ->
   mappings = new Mappings
+  styles = new Styles
   args = process.argv[2..]
   files = skip = 0
   formats = []
@@ -1224,8 +1246,10 @@
         input = Input.recognize arg
         if input instanceof Mapping
           mappings.push input
+        else if input instanceof Style
+          styles.push input
         else if input instanceof Drawing or input instanceof Drawings
-          filenames = input.writeSVG mappings
+          filenames = input.writeSVG mappings, styles
           input.writeTeX() if Symbol.texText
           for format in formats
             if typeof filenames == 'string'


fix of callback already called error

fix of callback already called error

--- src/uploader.coffee
+++ src/uploader.coffee
@@ -146,10 +146,18 @@
           chunk.progress = false
           chunk.finished = if err then false else true

+          if chunk.callbackCalled
+            console.error 'This callback was already called, WTF; chunk', chunk
+            return
+
+          chunk.callbackCalled = true
+
           if err
             if err.code is 'RequestTimeout'
               # new client will be created in next run
               chunk.client = undefined
+
+              console.info "timeout for chunk no.: #{chunk.partNumber}"
               # do not propagate err as that whould kill rest of uploads
               return next null
             else


Fix progress bar when compensating only one item.

--- kirppu/static_src/js/checkout/vendorcompensation.coffee
+++ kirppu/static_src/js/checkout/vendorcompensation.coffee
@@ -87,7 +87,6 @@

   _createProgress: (max) ->
-    max--  # Last completing index is length - 1.
     @buttonForm.empty().append(Templates.render("progress_bar", {
       max: max
     }))
@@ -118,7 +117,7 @@

     cb = (glyph, color) => () =>
       status.html("")
-      @_setProgress(index)
+      @_setProgress(index + 1)
       if index + 1 < list.length
         @_loopBack(list, index + 1)
       else


Add 'add language' button (non-functional so far)

--- app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_search_inputs.jsx.coffee
+++ app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_search_inputs.jsx.coffee
@@ -46,21 +46,24 @@
     @state.statechart.handleAction('showRegex')

   languageSelect: ->
-    if corpusNs.isMultilingual(@props.corpus)
-      `<LanguageSelect corpus={this.props.corpus} />`
-    else
-      null
+    `<LanguageSelect corpus={this.props.corpus} />`
+
+  languageAddButton: ->
+    `<button className="btn" style={{marginLeft: 100}}>Add language</button>`

   render: ->
     {corpus, searchQuery, handleQueryChanged, handleSearch} = @props
+    isMultilingual = corpusNs.isMultilingual(@props.corpus)
+
     if @state.statechart.pathContains('simple')
       `

           Simple |
           Extended |
           Regexp
+          {isMultilingual ? this.languageAddButton() : null}

-        {this.languageSelect()}
+        {isMultilingual ? this.languageSelect() : null}
         <CwbSimpleInput searchQuery={searchQuery} handleQueryChanged={handleQueryChanged} handleSearch={handleSearch} />
       `

@@ -70,8 +73,9 @@
           Simple |
           Extended |
           Regexp
+          {isMultilingual ? this.languageAddButton() : null}

-        {this.languageSelect()}
+        {isMultilingual ? this.languageSelect() : null}
         <CwbMultiwordInput
           searchQuery={searchQuery}
           corpus={corpus}
@@ -84,7 +88,8 @@
           Simple |
           Extended |
           Regexp
+          {isMultilingual ? this.languageAddButton() : null}

-        {this.languageSelect()}
+        {isMultilingual ? this.languageSelect() : null}
         <CwbRegexInput searchQuery={searchQuery} handleQueryChanged={handleQueryChanged} handleSearch={handleSearch} />
       `


Make noideadog a little less aggressive

Make noideadog a little less aggressive

--- couchbot/scripts/dogatcomputer.coffee
+++ couchbot/scripts/dogatcomputer.coffee
@@ -21,7 +21,7 @@
 module.exports = (robot) ->
   robot.hear /(dunno|I don\'t know|beats me|no idea)/i, (msg)->
     r = Math.random()
-    if r <= 0.5
+    if r <= 0.2
       msg.send noidea

   robot.respond /dunno|I don\'t know|beats me|no idea/i, (msg) ->


Don't convert clicks to up:click more than once after calling up.Layer.Root#sync()

--- lib/assets/javascripts/unpoly/classes/layer/root.coffee
+++ lib/assets/javascripts/unpoly/classes/layer/root.coffee
@@ -10,13 +10,7 @@
   constructor: (options) ->
     super(options)
     @history = true
-
-    # When we reset the framework during tests, we might re-initialize this
-    # layer with the same <html> element. In this case we do not want to
-    # setup handlers more than once.
-    unless @element.upHandlersApplied
-      @element.upHandlersApplied = true
-      @setupHandlers()
+    @setupHandlers()

   # Always return the current <body> instead of caching it,
   # since the developer might replace it with a new version.
@@ -44,6 +38,14 @@
   @selector: ->
     'html'

+  setupHandlers: ->
+    # When we reset the framework during tests, we might re-initialize this
+    # layer with the same <html> element. In this case we do not want to
+    # setup handlers more than once.
+    unless @element.upHandlersApplied
+      @element.upHandlersApplied = true
+      super()
+
   sync: ->
     # In case a fragment update has swapped the <html> element we need to re-apply
     # event handlers to the new <html> element.


fixed jump to top on browse buttons

fixed jump to top on browse buttons

--- lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
+++ lms/static/coffee/src/discussion/views/discussion_thread_list_view.coffee
@@ -142,6 +142,7 @@
       setTimeout (-> @$(".post-search-field").focus()), 200

     toggleTopicDrop: (event) =>
+      event.preventDefault()
       event.stopPropagation()
       @$(".search").removeClass('is-open')
       @$(".browse").addClass('is-open')


added transparent http and https support for end user

--- node_modules_koding/kite-amqp/lib/kite-amqp/kite.coffee
+++ node_modules_koding/kite-amqp/lib/kite-amqp/kite.coffee
@@ -1,5 +1,4 @@
 {EventEmitter} = require 'microemitter'
-http = require 'http'
 Kite = require './index'

 module.exports = class KiteBase extends EventEmitter
@@ -24,11 +23,18 @@

     return config

-  constructor:(config, functions)->
+  constructor:(config, functions, roundRobin = false)->

+    schema = config.apiAdress.split(":")[0]
+
+    if schema == "https"
+      client = require 'https'
+    else
+      client = require 'http'
+
     url = "#{config.apiAdress}/-/kite/login?key=#{config.key}&name=#{config.name}"

-    http.get url, (res) =>
+    client.get url, (res) =>
       unless res.statusCode == 200
         console.log 'You can not start this Kite'
         return


DRY more and allow store data in trigger for outgoing scripting

--- packages/rocketchat-integrations/server/triggers.coffee
+++ packages/rocketchat-integrations/server/triggers.coffee
@@ -1,6 +1,38 @@
 vm = Npm.require('vm')

 triggers = {}
+
+executeScript = (scriptContent, name, sandbox) ->
+	script = undefined
+	vmScript = undefined
+	try
+		script = "result = (function() {\n"+scriptContent+"\n}());"
+		vmScript = vm.createScript script, 'script.js'
+		logger.outgoing.info 'will execute script ' + name
+		logger.outgoing.debug script
+		logger.outgoing.debug 'with context', sandbox
+	catch e
+		logger.outgoing.error "[Error evaluating Script:]"
+		logger.outgoing.error script.replace(/^/gm, '  ')
+		logger.outgoing.error "\n[Stack:]"
+		logger.outgoing.error e.stack.replace(/^/gm, '  ')
+		return
+
+	try
+		sandbox._ = _
+		sandbox.s = s
+		sandbox.console = console
+
+		vmScript.runInNewContext sandbox
+		logger.outgoing.debug 'result', sandbox.result
+		return sandbox.result
+	catch e
+		logger.outgoing.error "[Error running Script:]"
+		logger.outgoing.error script.replace(/^/gm, '  ')
+		logger.outgoing.error "\n[Stack:]"
+		logger.outgoing.error e.stack.replace(/^/gm, '  ')
+		return
+

 RocketChat.models.Integrations.find({type: 'webhook-outgoing'}).observe
 	added: (record) ->
@@ -74,43 +106,18 @@
 			'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'

 	if trigger.prepareOutgoingRequestScript? and trigger.prepareOutgoingRequestScript.trim() isnt ''
+		trigger.store ?= {}
 		sandbox =
 			request: opts
-
-		script = undefined
-		vmScript = undefined
-		try
-			script = "result = (function() {\n"+trigger.prepareOutgoingRequestScript+"\n}());"
-			vmScript = vm.createScript script, 'script.js'
-			logger.outgoing.info 'will execute script prepareOutgoingRequestScript'
-			logger.outgoing.debug script
-			logger.outgoing.debug 'with context', sandbox
-		catch e
-			logger.outgoing.error "[Error evaluating Script:]"
-			logger.outgoing.error script.replace(/^/gm, '  ')
-			logger.outgoing.error "\n[Stack:]"
-			logger.outgoing.error e.stack.replace(/^/gm, '  ')
-			return
-
-		try
-			sandbox._ = _
-			sandbox.s = s
-			sandbox.console = console
-			vmScript.runInNewContext sandbox
-			opts = sandbox.result
-			logger.outgoing.debug 'result', opts
-			if opts?.message?
-				return sendMessage opts.message
-		catch e
-			logger.outgoing.error "[Error running Script:]"
-			logger.outgoing.error script.replace(/^/gm, '  ')
-			logger.outgoing.error "\n[Stack:]"
-			logger.outgoing.error e.stack.replace(/^/gm, '  ')
-			return
+			store: trigger.store
+
+		opts = executeScript trigger.prepareOutgoingRequestScript, 'prepareOutgoingRequestScript', sandbox

 	if not opts?
 		return

+	if opts.message?
+		return sendMessage opts.message

 	HTTP.call opts.method, opts.url, opts, (error, result) ->
 		if not result? or result.statusCode isnt 200
@@ -144,37 +151,11 @@
 						content: result.data
 						content_raw: result.content
 						headers: result.headers
-
-				script = undefined
-				vmScript = undefined
-				try
-					script = "result = (function() {\n"+trigger.processOutgoingResponseScript+"\n}());"
-					vmScript = vm.createScript script, 'script.js'
-					logger.outgoing.info 'will execute script processOutgoingResponseScript'
-					logger.outgoing.debug script
-					logger.outgoing.debug 'with context', sandbox
-				catch e
-					logger.outgoing.error "[Error evaluating Script:]"
-					logger.outgoing.error script.replace(/^/gm, '  ')
-					logger.outgoing.error "\n[Stack:]"
-					logger.outgoing.error e.stack.replace(/^/gm, '  ')
-					return
-
-				try
-					sandbox._ = _
-					sandbox.s = s
-					sandbox.console = console
-					vmScript.runInNewContext sandbox
-					result = sandbox.result?.content
-					if result?
-						result = data: result
-					logger.outgoing.debug 'result', result
-				catch e
-					logger.outgoing.error "[Error running Script:]"
-					logger.outgoing.error script.replace(/^/gm, '  ')
-					logger.outgoing.error "\n[Stack:]"
-					logger.outgoing.error e.stack.replace(/^/gm, '  ')
-					return
+					store: trigger.store
+
+				scriptResult = executeScript trigger.processOutgoingResponseScript, 'processOutgoingResponseScript', sandbox
+				result =
+					data: scriptResult?.content

 			if result?.data?.text? or result?.data?.attachments?
 				sendMessage result.data


fix(grunt): missing circleci config

fix(grunt): missing circleci config

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -479,6 +479,15 @@
               "!TODOS.md"
             ]
             dest: "./"
+          }
+          {
+            expand: true
+            dot: true
+            cwd: "<%= amsf.core %>/.circleci/"
+            src: [
+              "config.example.yml"
+            ]
+            dest: "<%= config.app %>/.circleci/"
           }
           {
             expand: true


style: make watch task's name more meaningful

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -46,8 +46,8 @@
     watch:
       options:
         livereload: true
-      # Build and run test case
-      develop:
+      # Run test case
+      test:
         files: ["#{sourceDir}/**/*"]
         tasks: ['karma:unit:run']

@@ -74,7 +74,7 @@
         # Re-assemble on change
         'exec:brunchWatch'
         # Run tests on change
-        'watch'
+        'watch:test'
         # Run local web server
         'exec:harpServer'
       ]


add input to list of includes

add input to list of includes

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -48,7 +48,7 @@
       app:
         options:
           name: 'app'
-          include: ['builtins', 'graphite', 'graph', 'opentsdb', 'github']
+          include: ['builtins', 'graphite', 'graph', 'opentsdb', 'github', 'input']
           out: 'dist/lead-app.js'
           mainConfigFile: 'build/requirejs_optimize_config.js'
           baseUrl: 'build'


add patch release task

add patch release task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -218,6 +218,7 @@
         additionalFiles: [
           'bower.json',
           'app/manifest.json'
+          'dist/manifest.json'
         ]

     # Compress files in dist to make Chromea Apps package
@@ -257,7 +258,17 @@
     'coffee:production',
     'jade:production',
     'stylus:production',
-    'minify',
+    'minify'
+  ]
+
+  grunt.registerTask 'release-minor', [
+    'production',
     'release:minor',
     'compress'
   ]
+
+  grunt.registerTask 'release-patch', [
+    'production',
+    'release:patch',
+    'compress'
+  ]


Update Gruntfile

Update Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -46,9 +46,6 @@
       coffee:
         files: ['src/<%= pkg.name %>.coffee', 'test/<%= pkg.name %>_test.coffee']
         tasks: ['coffee']
-      gruntfile:
-        files: '<%= jshint.gruntfile.src %>'
-        tasks: ['jshint:gruntfile']
       src:
         files: '<%= jshint.src.src %>'
         tasks: ['jshint:src', 'qunit']


Made Gruntfile compatible with Windows.

Made Gruntfile compatible with Windows.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,8 +6,11 @@
       options:
         failOnError: true
       jison:
-        command: 'jison src/grammar.y src/lexer.l -o src/parser.js &&
-                  mkdir -p lib/src/ && cp src/parser.js lib/src/'
+        command: [
+          'jison src/grammar.y src/lexer.l -o src/parser.js'
+          'mkdir -p lib/src/'
+          'cp src/parser.js lib/src/'
+        ].join if process.platform.match /^win/ then '&' else '&&'
       push_ghpages:
         command: 'git subtree push --prefix demo origin gh-pages'
         options:


Correcting GruntTask

Correcting GruntTask

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -48,7 +48,7 @@
                 expand: yes
                 cwd: "src/server/"
                 src: [
-                    "**/*.litcoffee"
+                    "**/*.coffee"
                 ]
                 dest: "lib/server/"
                 ext: ".js"
@@ -58,7 +58,7 @@
                 expand: yes
                 cwd: "src/client/scripts/"
                 src: [
-                    "**/*.litcoffee"
+                    "**/*.coffee"
                 ]
                 dest: "src/client/scripts/"
                 ext: ".js"
@@ -130,7 +130,7 @@
         watch:
             server:
                 files: [
-                    "src/server/**/*.litcoffee"
+                    "src/server/**/*.coffee"
                 ]
                 options:
                     nospawn: yes
@@ -142,7 +142,7 @@
                 ]
             client:
                 files: [
-                    "src/client/**/*.litcoffee"
+                    "src/client/**/*.coffee"
                 ]
                 options:
                     nospawn: yes


Fixing Gruntfile to auto compile coffeescript routes on change.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -14,7 +14,7 @@
         files: ['Gruntfile.coffee']
         tasks: ['coffeelint']
       coffee_routers:
-        files: ['routers/*.coffee']
+        files: ['routers/cs/*.coffee']
         tasks: ['coffee:routers']
       coffee_app:
         files: ['app.coffee']


Enable custom builds with only one language

With `grunt build uglify` droplet-full.min.js is ~1.57 mb.
With `LANGUAGE=javascript grunt build uglify` droplet-full.min.js is ~0.31 mb.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,6 +6,9 @@

 path = require 'path'
 fs = require 'fs'
+
+if process.env.LANGUAGE
+  ignoredLanguages = ['./src/languages/!(' + process.env.LANGUAGE + ').coffee']

 serveNoDottedFiles = (connect, options, middlewares) ->
   # Avoid leaking .git/.svn or other dotted files from test servers.
@@ -75,6 +78,7 @@
         files:
           'dist/droplet-full.js': ['./src/main.coffee']
         options:
+          ignore: ignoredLanguages
           transform: ['coffeeify']
           browserifyOptions:
             standalone: 'droplet'


Add any files in /generic to the firefox core build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -346,6 +346,10 @@
         # ... the generic core stuff
         }, {
           expand: true, cwd: 'build/compile-src/generic_core'
+          src: ['**'],
+          dest: firefoxDevPath + 'data/core/'
+        }, {
+          expand: true, cwd: 'build/compile-src/generic'
           src: ['**'],
           dest: firefoxDevPath + 'data/core/'
         }, {


shorter comment

shorter comment

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -112,7 +112,7 @@
           'client/**/*_test.js'
         ]

-    # build --stage tasks, build for production
+    # build --stage tasks

     cssmin:
       app:


Make browser build as ES5

Make browser build as ES5

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -33,8 +33,20 @@
             'acorn': 'commonjs acorn' # optional?
           module:
             rules: [
+              test: /noflo([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo([\\]+|\/)components([\\]+|\/)(.*)\.js$|fbp-graph([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo-runtime-([a-z]+)([\\]+|\/)(.*).js$/
+              use: [
+                loader: 'babel-loader'
+                options:
+                  presets: ['es2015']
+              ]
+            ,
               test: /\.coffee$/
-              use: ["coffee-loader"]
+              use: [
+                loader: 'coffee-loader'
+                options:
+                  transpile:
+                    presets: ['es2015']
+              ]
             ,
               test: /\.fbp$/
               use: ["fbp-loader"]


add ailas for esteWatch.

add ailas for esteWatch.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -159,6 +159,7 @@

   # tasks
+  grunt.registerTask 'watch', ['esteWatch']
   grunt.registerTask 'minify', ['ngmin', 'uglify']

   grunt.registerTask 'dev', [


Modified gruntfile to explicitly concatenate js in order

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -3,7 +3,7 @@
     pkg: grunt.file.readJSON('package.json')
     concat:
       source:
-        src: "source/*.js"
+        src: ["source/nes.js", "source/utils.js", "source/cpu.js", "source/keyboard.js", "source/mappers.js", "source/papu.js", "source/ppu.js", "source/rom.js", "source/ui.js"]
         dest: "build/jsnes.js"
     uglify:
       source:


Load mocha before the end, as that seems to be buggy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -205,10 +205,10 @@

   grunt.registerTask 'jail_iframe', []
   grunt.registerTask 'compile',  [
-    'clean', 'copy:build',  'copy:extension_events', 'coffee','copy:postFactlinkObject',
+    'clean', 'copy:build',  'copy:extension_events', 'coffee', 'copy:postFactlinkObject',
     'sass', 'cssUrlEmbed', 'cssmin',
-    'concat', 'uglify', 'code_inliner',
-    'shell:gzip_js_files', 'copy:dist', 'mocha'
+    'concat', 'mocha', 'uglify', 'code_inliner',
+    'shell:gzip_js_files', 'copy:dist'
   ]

   grunt.registerTask 'default', ['compile']


Don't copy vendor/apm to build folder

Don't copy vendor/apm to build folder

This is already copied as node_modules/atom-package-manager

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,7 +22,6 @@
         src: [
           'src/**/*.coffee'
           'static/**/*.coffee'
-          'vendor/**/*.coffee'
         ]
         dest: APP_DIR
         ext: '.js'
@@ -152,6 +151,7 @@
     ignoredPaths = [
       path.join('git-utils', 'deps')
       path.join('oniguruma', 'deps')
+      path.join('vendor', 'apm')
     ]
     ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})"
     nodeModulesFilter = new RegExp(ignoredPaths.join('|'))


Serve test files without leaking .git directory.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,6 +6,15 @@
     args: [message, '--urgency=low']
     fallback: 0
   ), ->
+
+serveNoDottedFiles = (connect, options, middlewares) ->
+  # Avoid leaking .git/.svn or other dotted files from test servers.
+  middlewares.unshift (req, res, next) ->
+    if req.url.indexOf('/.') < 0 then return next()
+    res.statusCode = 404
+    res.setHeader('Content-Type', 'text/html');
+    res.end "Cannot GET #{req.url}"
+  return middlewares

 module.exports = (grunt) ->
   grunt.initConfig
@@ -130,10 +139,12 @@
       testserver:
         options:
           hostname: '0.0.0.0'
+          middleware: serveNoDottedFiles
       qunitserver:
         options:
           hostname: '0.0.0.0'
           port: 8942
+          middleware: serveNoDottedFiles

     watch:
       options:


modify bump process

modify bump process

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -93,10 +93,8 @@
   ]

   grunt.registerTask 'dist', [
-    'clean'
-    'karma:single'
-    'coffee'
-    'bump:minor'
+    'build'
+    'bump:dist'
   ]

   grunt.registerTask 'test', [


updated bumper for git flow usage

updated bumper for git flow usage

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -49,7 +49,8 @@
     # Deployment
     bumper:
       options:
-        npm: true
+        push: false
+        createTag: false
         tasks: ["default"]
         files: ["package.json"]
         updateConfigs: ["pkg"]


Fix jsbeautifier prefs

Fix jsbeautifier prefs

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -69,10 +69,10 @@
     jsbeautifier:
       files: ['src/**/*.js']
       options:
+        mode: "VERIFY_ONLY"
         js:
-          mode: "VERIFY_ONLY"
           space_after_anon_function: true
-          wrap_line_length: 120
+          wrapLineLength: 120
           indent_size: 2

     # CoffeeLint


Fix the Grunt file

Fix the Grunt file

Jira: RC-3134
Signed-off-by: Tri Pham <fc636f3676e034f799509d6bbe041469b536c7ab@turn.com>

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -105,7 +105,7 @@
       all:
         options:
           port: 9000
-          bases: './demo/'
+          bases: ['./demo/', './']
           livereload: true

     watch:


mixpanel: add more events; remove unused code

--- client/ide/fs/fsfile.coffee
+++ client/ide/fs/fsfile.coffee
@@ -41,6 +41,8 @@

         if useEncoding
           content = KD.utils.utf8Decode content # Convert to String
+
+        KD.mixpanel "Fetch contents, success"

       callback.call @, err, content
       @emit "fs.job.finished", err, content
@@ -174,3 +176,4 @@
       if err then warn err
       @emit "fs.save.finished", err, res
       callback? err,res
+      KD.mixpanel "Save file, success"  unless err


styleguide: Change response name to eventData

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -209,11 +209,11 @@
    * Open a series of file paths, in the format of klient's openFiles
    * event.
    *
-   * @param {Object} response - An object formatted as a Klient event
-   *  response.
-   * @param {Array<string>} response.files - A list of file paths
+   * @param {Object} eventData - An object formatted as a Klient event
+   *  data.
+   * @param {Array<string>} eventData.files - A list of file paths
   ###
-  handleKlientOpenFiles: (response) -> @openFiles response.files
+  handleKlientOpenFiles: (eventData) -> @openFiles eventData.files

   setActiveTabView: (tabView) ->


IDE: code fix after merge

IDE: code fix after merge

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -30,7 +30,6 @@
 KlientEventManager            = require 'app/kite/klienteventmanager'
 IDELayoutManager              = require './workspace/idelayoutmanager'
 StackAdminMessageController   = require './views/stacks/stackadminmessagecontroller'
-StackProgressModal            = require 'ide/views/modals/stackprogressmodal'

 require('./routes').init()


Added function for rounding.

Added function for rounding.

--- client/lib/annotator.coffee
+++ client/lib/annotator.coffee
@@ -14,6 +14,16 @@

   setTextContent: (pageNumber, textContent) =>
     @_pages[pageNumber - 1].textContent = textContent
+
+  _roundArea: (area) =>
+    areaRounded = _.clone area
+    areaRounded.left = Math.floor areaRounded.left
+    areaRounded.top = Math.floor areaRounded.top
+    areaRounded.width += area.left - areaRounded.left
+    areaRounded.height += area.top - areaRounded.top
+    areaRounded.width = Math.ceil areaRounded.width
+    areaRounded.height = Math.ceil areaRounded.height
+    areaRounded

   textLayer: (pageNumber) =>
     page = @_pages[pageNumber - 1]


made line number background opaque

made line number background opaque

--- client/prefs-colours.coffee
+++ client/prefs-colours.coffee
@@ -78,19 +78,19 @@

 builtInSchemes = [
   {name: 'Default', styles: '''
-    num=fg:8 str=fg:088 zld=fg:008 var=fg:8 quad=fg:808 fn=fg:008 op1=fg:00f op2=fg:00f ns=fg:8 asgn=fg:00f
-    diam=fg:00f par=fg:00f sqbr=fg:00f semi=fg:00f dfn=fg:00f trad=fg:8 kw=fg:800 idm=fg:00f com=fg:088 err=fg:f00
-    lnum=fg:008 mtch=bg:ff8,bgo:.5 srch=bg:f80,bgo:.5 mod=bg:e,bgo:1 sel=bg:ddf,bgo:.5 tc=bg:d,bgo:1
+    asgn=fg:00f com=fg:088 dfn=fg:00f diam=fg:00f err=fg:f00 fn=fg:008 idm=fg:00f kw=fg:800 lnum=fg:008,bg:f,bgo:1
+    mod=bg:e,bgo:1 mtch=bg:ff8,bgo:.5 norm=bg:f,bgo:1 ns=fg:8 num=fg:8 op1=fg:00f op2=fg:00f par=fg:00f quad=fg:808
+    sel=bg:ddf,bgo:.5 semi=fg:00f sqbr=fg:00f srch=bg:f80,bgo:.5 str=fg:088 tc=bg:d,bgo:1 trad=fg:8 var=fg:8 zld=fg:008
   '''}
   {name: 'Francisco Goya', styles: '''
-    norm=fg:9c7,bg:0,bgo:1 cur=lb:f00 lnum=fg:b94,bg:010,bgo:1 srch=bg:b96,bgo:.75,fg:0 mod=bg:1,bgo:1
-    sel=bg:048,bgo:.5 err=fg:f00,bg:822,bgo:.5,B:1,U:1 kw=fg:aa2 num=fg:a8b op1=fg:d95 fn=fg:0f0
-    op2=fg:fd6 sqbr=fg:8 com=fg:b,I:1 semi=fg:8 str=fg:dae zld=fg:d9f,B:1 lbl=U:1,bg:642,bgo:.5 idm=B:1 tc=bg:1,bgo:1
-    glob=B:1 dfn=fg:a7b dfn2=fg:eb4 dfn3=fg:c79 dfn4=fg:cd0 dfn5=fg:a0d diam=fg:ff0 asgn=fg:ff0 mtch=fg:0,bg:ff8,bgo:.75
+    asgn=fg:ff0 com=fg:b,I:1 cur=lb:f00 dfn2=fg:eb4 dfn3=fg:c79 dfn4=fg:cd0 dfn5=fg:a0d dfn=fg:a7b diam=fg:ff0
+    err=fg:f00,bg:822,bgo:.5,B:1,U:1 fn=fg:0f0 glob=B:1 idm=B:1 kw=fg:aa2 lbl=U:1,bg:642,bgo:.5 lnum=fg:b94,bg:010,bgo:1
+    mod=bg:1,bgo:1 mtch=fg:0,bg:ff8,bgo:.75 norm=fg:9c7,bg:0,bgo:1 num=fg:a8b op1=fg:d95 op2=fg:fd6 sel=bg:048,bgo:.5
+    semi=fg:8 sqbr=fg:8 srch=bg:b96,bgo:.75,fg:0 str=fg:dae tc=bg:1,bgo:1 zld=fg:d9f,B:1
   '''}
   {name:'Albrecht Dürer', styles: '''
-    num=fg:8 str=fg:8 zld=fg:8 quad=fg:808 ns=fg:8 diam=B:1 kw=B:1 idm=U:1,bg:e,bgo:.5 com=I:1
-    err=fg:f,bg:0,bgo:.5,B:1,I:1,U:1 mtch=bg:c,bgo:.5 srch=bg:c,bgo:.5 mod=bg:e,bgo:1 glb=I:1 tc=bg:e,bgo:1
+    com=I:1 diam=B:1 err=fg:f,bg:0,bgo:.5,B:1,I:1,U:1 glb=I:1 idm=U:1,bg:e,bgo:.5 kw=B:1 lnum=bg:f,bgo:1 mod=bg:e,bgo:1
+    mtch=bg:c,bgo:.5 norm=bg:f,bgo:1 ns=fg:8 num=fg:8 quad=fg:808 srch=bg:c,bgo:.5 str=fg:8 tc=bg:e,bgo:1 zld=fg:8
   '''}
   {name:'Kazimir Malevich', styles: ''}
 ].map(decodeScheme).map (x) -> x.frozen = 1; x


fix test setting

fix test setting

--- caspertests/helpers/settings.coffee
+++ caspertests/helpers/settings.coffee
@@ -3,7 +3,7 @@
 env = "localdev"
 settings = {
   localdev : "http://localhost:8080"
-  gaedev : "https://dev-goappstarter.appspot.com"
+  gaedev : "https://dev-goisky-tools.appspot.com"
 }

 if casper.cli.has('env')


making view safe for upgrade to backbone 1.1

--- source/javascripts/src/view_xlformbuilder.coffee
+++ source/javascripts/src/view_xlformbuilder.coffee
@@ -49,6 +49,7 @@
     "click .shrink": "shrink"
     "click .menu-item": "selectMenuItem"
   initialize: (opts)->
+    @options = opts
     @button = @$el.find(".btn")
     @line = @$el.find(".line")
     if opts.action is "click-add-row"
@@ -187,7 +188,8 @@
    "click .add-row-btn": "expandRowSelector"
    "click .row-extras-summary": "expandCog"
    "click .glyphicon-cog": "expandCog"
-  initialize: ()->
+  initialize: (opts)->
+    @options = opts
     typeDetail = @model.get("type")
     @$el.attr("data-row-id", @model.cid)
     # @model.on "change", @render, @


Add missing callback

Add missing callback

--- spec/feature/example_bounded_context.spec.coffee
+++ spec/feature/example_bounded_context.spec.coffee
@@ -75,12 +75,14 @@
             'ExampleEntity': ExampleEntity

         exampleContext.addCommands
-          someBoundedContextFunction: (params) ->
+          someBoundedContextFunction: (params, callback) ->
             @aggregate.command
               id: params.id
               name: 'Example'
               methodName: 'someRootFunction'
               methodParams: [1]
+            .then =>
+              callback null

       it 'then it should have triggered the correct DomainEvent', (done) ->


Make create test thinner

Make create test thinner

--- spec/javascripts/unit/order_cycle_spec.js.coffee
+++ spec/javascripts/unit/order_cycle_spec.js.coffee
@@ -182,44 +182,27 @@

     describe 'creating an order cycle', ->
       it 'redirects to the order cycles page on success', ->
-        OrderCycle.order_cycle =
-          incoming_exchanges: [
-            {enterprise_id: "2", active: true}
-            {enterprise_id: "7", active: true}
-            {enterprise_id: "9", active: false}
-            ]
-          outgoing_exchanges: []
-          name: "name"
-          orders_open_at: "2012-12-14 07:30:00"
-          orders_close_at: "2012-12-22 04:15:00"
-          coordinator_id: "7"
-
+        OrderCycle.order_cycle = 'this is the order cycle'
+        spyOn(OrderCycle, 'removeInactiveExchanges')
         $httpBackend.expectPOST('/admin/order_cycles.json', {
-          order_cycle:
-            incoming_exchanges: [
-              {enterprise_id:"2",active:true}
-              {enterprise_id:"7",active:true}
-              ]
-            outgoing_exchanges:[]
-            name:"name"
-            orders_open_at:"2012-12-14 07:30:00"
-            orders_close_at:"2012-12-22 04:15:00"
-            coordinator_id:"7"
+          order_cycle: 'this is the order cycle'
           }).respond {success: true}

         OrderCycle.create()
         $httpBackend.flush()
+        expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled()
         expect($window.location).toEqual('/admin/order_cycles')

       it 'does not redirect on error', ->
+        OrderCycle.order_cycle = 'this is the order cycle'
+        spyOn(OrderCycle, 'removeInactiveExchanges')
         $httpBackend.expectPOST('/admin/order_cycles.json', {
-          order_cycle:
-            incoming_exchanges: []
-            outgoing_exchanges:[]
+          order_cycle: 'this is the order cycle'
           }).respond {success: false}

         OrderCycle.create()
         $httpBackend.flush()
+        expect(OrderCycle.removeInactiveExchanges).toHaveBeenCalled()
         expect($window.location).toEqual(undefined)


Minor comment mistake.

Minor comment mistake.

--- Heap.coffee
+++ Heap.coffee
@@ -29,7 +29,7 @@

     removeMin: ->
         ###
-        __Returns:_ the smallest item (the root).
+        _Returns:_ the smallest item (the root).
         ###
         return undefined if @_data.length is 1
         return @_data.pop() if @_data.length is 2


Environments: EnvironmentContainer: Calculate itemHeight correctly

--- client/Environments/views/scene/environmentcontainer.coffee
+++ client/Environments/views/scene/environmentcontainer.coffee
@@ -54,7 +54,7 @@
     index = 0
     for _key, dia of @dias
       dia.setX 20
-      dia.setY 68 + index * 64
+      dia.setY 68 + index * (@itemHeight + 20)
       index++

     @updateAddButton()


activity: fix usage of comment input value accessor methods

--- client/Social/Activity/views/comments/newcommentform.coffee
+++ client/Social/Activity/views/comments/newcommentform.coffee
@@ -51,9 +51,9 @@

   mention: (username) ->

-    value = @getValue()
+    value = @input.getValue()

-    @setValue \
+    @input.setValue \
       if value.indexOf("@#{username}") >= 0 then value
       else if value.length is 0 then "@#{username} "
       else "#{value} @#{username} "


EnvironmentListController: Show stack title if there is multiple stack.

--- client/app/lib/environment/environmentlistcontroller.coffee
+++ client/app/lib/environment/environmentlistcontroller.coffee
@@ -16,7 +16,7 @@
     @removeAllItems()

     if stacks
-      @instantiateListItems stacks
+      @addListItems stacks
       return

     @showLazyLoader()
@@ -30,4 +30,13 @@
       return if showError err, \
         KodingError : "Failed to fetch stacks, please try again later."

-      @instantiateListItems stacks
+      @addListItems stacks
+
+
+  addListItems: (stacks) ->
+
+    @instantiateListItems stacks
+
+    if stacks.length > 1
+      view.title.show()  for view in @getItemsOrdered()
+      @getView().setClass 'multi-stack'


Extend EnvironmentListController from KodingListController and handle all back-end logic here

--- client/app/lib/environment/environmentlistcontroller.coffee
+++ client/app/lib/environment/environmentlistcontroller.coffee
@@ -1,45 +1,85 @@
-kd = require 'kd'
-
-showError  = require 'app/util/showError'
-sortStacks = require 'app/util/sortEnvironmentStacks'
+kd                    = require 'kd'
+isKoding              = require 'app/util/isKoding'
+showError             = require 'app/util/showError'
+sortStacks            = require 'app/util/sortEnvironmentStacks'
+ComputeHelpers        = require '../providers/computehelpers'
+KDNotificationView    = kd.NotificationView
+EnvironmentListItem   = require './environmentlistitem'
+KodingListController  = require 'app/kodinglist/kodinglistcontroller'

-module.exports = class EnvironmentListController extends kd.ListViewController
+module.exports = class EnvironmentListController extends KodingListController
+

   constructor: (options = {}, data) ->

+    options.wrapper         ?= no
+    options.itemClass       ?= EnvironmentListItem
+    options.scrollView      ?= no
+    options.noItemFoundText ?= "You don't have any #{if isKoding() then 'machines' else 'stacks'}."
+
+    options.fetcherMethod    = (query, options, callback) ->
+      kd.singletons.computeController.fetchStacks (err, stacks) -> callback err, stacks
+
     super options, data

-    @loadItems()
+
+  bindEvents: ->
+
+    super
+
+    { computeController, appManager, router } = kd.singletons
+
+    listView = @getListView()
+
+    computeController.on 'RenderStacks', @bound 'loadItems'
+
+    listView.on 'ItemAction', ({ action, item, options }) =>
+
+      switch action
+        when 'StackReinitRequested' then @handleStackReinitRequest  item
+        when 'StackDeleteRequested' then @handleStackDeleteRequest  item
+        when 'NewMachineRequest'    then @handleNewMachineRequest   item

-  loadItems: (stacks) ->
+  handleNewMachineRequest: (provider) ->

-    @removeAllItems()
+    ComputeHelpers.handleNewMachineRequest { provider }, (machineCreated) =>
+      @getListView().emit 'ModalDestroyRequested', not machineCreated

-    if stacks
-      @addListItems stacks
-      return

-    @showLazyLoader()
+  handleStackDeleteRequest: (item) ->
+
+    { computeController, router } = kd.singletons
+    listView = @getListView()
+
+    stack = item.getData()
+    computeController.destroyStack stack, (err) =>
+      return  if showError err
+
+      new KDNotificationView title : 'Stack deleted'
+
+      computeController.reset yes, -> router.handleRoute '/IDE'
+      @emit 'DestroyYourStacksView'
+
+
+  handleStackReinitRequest: (item) ->

     { computeController } = kd.singletons

-    computeController.fetchStacks (err, stacks) =>
+    stack = item.getData()
+    computeController.reinitStack stack, ->
+      item.reinitButton.hideLoader()

-      @hideLazyLoader()
-
-      return if showError err, \
-        { KodingError : 'Failed to fetch stacks, please try again later.' }
-
-      @addListItems stacks
+    computeController.once 'RenderStacks', =>
+      @getListView().emit 'ModalDestroyRequested', yes, yes

   addListItems: (stacks) ->

     stacks = sortStacks stacks

-    @instantiateListItems stacks
+    super stacks

     return  if stacks.length is 1


Teamwork: Changed KD.utils.xssEncode to Encoder.XSSEncode.

--- client/landing/CollaborativeWorkspace/panes/chatpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/chatpane.coffee
@@ -92,7 +92,7 @@
   appendToChatItem: (params) ->
     {details} = params
     @lastChatItem.messageList.addSubView new KDCustomHTMLView
-      partial  : KD.utils.xssEncode details.body
+      partial  : Encoder.XSSEncode details.body
       cssClass : details.cssClass

   updateDate: (timestamp) ->


xmpp connection: always tell node-xmpp to reconnect

should fix issue #21

--- src/xmpp/connection.coffee
+++ src/xmpp/connection.coffee
@@ -29,6 +29,8 @@

         # Setup connection:
         @jid = config.jid
+        # Anyone wants reconnecting, regardless of the config file:
+        config.reconnect = true
         @conn = new xmpp.Component(config)
         @conn.on "online", =>
             @emit "online"


Modes; fix focus return value for InsertModeTrigger.

--- content_scripts/mode_insert.coffee
+++ content_scripts/mode_insert.coffee
@@ -51,9 +51,10 @@
     @push
       focus: (event) =>
         triggerSuppressor.unlessSuppressed =>
-          return unless DomUtils.isFocusable event.target
-          new InsertMode
-            targetElement: event.target
+          @alwaysContinueBubbling =>
+            if DomUtils.isFocusable event.target
+              new InsertMode
+                targetElement: event.target

     # We may already have focussed an input, so check.
     if document.activeElement and DomUtils.isEditable document.activeElement
@@ -61,7 +62,7 @@
         targetElement: document.activeElement

 # Used by InsertModeBlocker to suppress InsertModeTrigger; see below.
-triggerSuppressor = new Utils.Suppressor true
+triggerSuppressor = new Utils.Suppressor true # Note: true == @continueBubbling

 # Suppresses InsertModeTrigger.  This is used by various modes (usually by inheritance) to prevent
 # unintentionally dropping into insert mode on focusable elements.


Allow packages to register files for theming

--- packages/rocketchat-theme/server/server.coffee
+++ packages/rocketchat-theme/server/server.coffee
@@ -32,6 +32,7 @@

 RocketChat.theme = new class
 	variables: {}
+	packageCallbacks: []
 	files: [
 		'assets/stylesheets/global/_variables.less'
 		'assets/stylesheets/utils/_emojione.import.less'
@@ -70,6 +71,11 @@
 		]

 		content.push Assets.getText file for file in @files
+
+		for packageCallback in @packageCallbacks
+			result = packageCallback()
+			if _.isString result
+				content.push result

 		content = content.join '\n'

@@ -120,5 +126,8 @@

 		return items.join '\n'

+	addPackageAsset: (cb) ->
+		@packageCallbacks.push cb
+
 	getCss: ->
 		return RocketChat.settings.get 'css'


fixed packageDocument specs to use the proper constructor

--- spec/javascripts/models/packageDocument_spec.coffee
+++ spec/javascripts/models/packageDocument_spec.coffee
@@ -4,7 +4,7 @@

 		beforeEach ->
 			stubFileSystem()
-			@packageDocument = new Readium.Models.PackageDocument({}, {file_path: "some/path"})
+			@packageDocument = new Readium.Models.PackageDocument({book: {}, file_path: "some/path"})

 		it 'exists in the namespace', ->
 			expect(Readium.Models.PackageDocument).toBeDefined()
@@ -17,13 +17,14 @@
 			spyOn(@packageDocument.onSpinePosChanged, "apply")
 			@packageDocument.trigger("change:spine_position")
 			expect(@packageDocument.onSpinePosChanged.apply).toHaveBeenCalled()
+

 	describe "parsing the xml", ->

 		beforeEach ->
 			xml_string = jasmine.getFixtures().read('package_document.xml')
 			@xml = new window.DOMParser().parseFromString(xml_string, 'text/xml')
-			@packageDocument = new Readium.Models.PackageDocument()
+			@packageDocument = new Readium.Models.PackageDocument({file_path: "some/path"})
 			@packageDocument.uri_obj = new URI("http://google.ca")
 			spyOn(@packageDocument, "crunchSpine")
 			@json = @packageDocument.parse(@xml)


Tweak more numbers

Tweak more numbers

--- web/source/javascripts/title.js.coffee
+++ web/source/javascripts/title.js.coffee
@@ -82,7 +82,7 @@
     # set up agents
     agents = []
     maxAgents = 1200
-    numAdd = 10
+    numAdd = 8
     update = ->
       # new agents
       for i in [1..numAdd]


Reset the restore-backup file chooser on save.

It's just cleaner this way.

--- pages/options.coffee
+++ pages/options.coffee
@@ -47,6 +47,8 @@
     if restoreSettingsVersion?
       bgSettings.set "settingsVersion", restoreSettingsVersion
       restoreSettingsVersion = null
+    # Reset the restore-backup input.
+    $("chooseFile").value = ""
     # We need to apply migrations in case we are restoring an old backup.
     bgSettings.applyMigrations()


Fixed: an error was shown in the console when an event was created from the modal

--- client/app/views/event_modal.coffee
+++ client/app/views/event_modal.coffee
@@ -99,7 +99,7 @@

         start = moment @startField.val(), @inputDateTimeFormat
         end = moment @endField.val(), @inputDateTimeFormat
-
+
         options =
             language: window.app.locale
             autoclose: true
@@ -155,7 +155,7 @@
             @reminders.push reminder
             reminder.on 'remove', (removedReminder) =>
                 @reminders.splice @reminders.indexOf(removedReminder), 1
-
+
             reminder.render()
             @$('#reminder-container').append reminder.$el

@@ -209,9 +209,9 @@
             dtE = moment.tz @endField.val(), @inputDateFormat,
                     window.app.timezone
             # Model has non-inclusive end-date, but UI has inclusive end-date,
-            # which means a difference of one day.
+            # which means a difference of one day.
             dtE.add 'day', 1
-
+
             data.start = H.momentToDateString(dtS)
             data.end = H.momentToDateString(dtE)

@@ -247,7 +247,9 @@
         if not validModel
             @$('.alert').remove()
             @$('.control-group').removeClass('error')
-            @handleError error for error in @model.validationError
+            validationErrors = @model.validationError
+            if validationErrors?
+                @handleError error for error in validationErrors

     handleError: (error) =>
         switch error.field


Fix typo

Fix typo

--- client/components/SubmitList.coffee
+++ client/components/SubmitList.coffee
@@ -63,7 +63,7 @@
             if @props.bestSubmits.length

Хорошие решения

             else if @props.result.solved == 0
-
Хорошие решения <FontAwesome name="question-circle-o"/>

+
Хорошие решения <FontAwesome name="question-circle-o"/>

             }
             <SubmitForm material={@props.material} problemId={@props.material._id} reloadSubmitList={@props.handleReload}/>
             {


Fix cursor top position in multi-cursor module when the quill container
is scrolled.

Cursors are absolutely positioned with respect to the top of the
.ql-container.
quill.getBounds().top returns a smaller integer when the .ql-container
is scrolled vertically.
As a result, positioning a cursor requires adding container.scrollTop to
the cursor bounds.

--- src/modules/multi-cursor.coffee
+++ src/modules/multi-cursor.coffee
@@ -102,7 +102,7 @@

   _updateCursor: (cursor) ->
     bounds = @quill.getBounds(cursor.index)
-    cursor.elem.style.top = (bounds.top - @quill.container.scrollTop) + 'px'
+    cursor.elem.style.top = (bounds.top + @quill.container.scrollTop) + 'px'
     cursor.elem.style.left = bounds.left + 'px'
     cursor.elem.style.height = bounds.height + 'px'
     flag = cursor.elem.querySelector('.cursor-flag')