Make virtual methods more descriptive

Make virtual methods more descriptive

--- lib/elvis-backbone.coffee
+++ lib/elvis-backbone.coffee
@@ -1,8 +1,8 @@
 el = @elvis

-virtual = -> ->
-  throw new Exception('Must be implemented in sub class')
+virtual = (fname) -> ->
+  throw new Exception("#{fname} must be implemented in sub class")

 ###
@@ -73,7 +73,7 @@
       @setAttr(@_element, 'text')
     @_element

-  getValue: virtual()
+  getValue: virtual('getValue')

   _getValue: ->
     values = (@getValue(attr) for attr in @attrs)
@@ -93,9 +93,9 @@
     @subscribe(attr) for attr in @attrs
     @update()

-  setValue: virtual()
+  setValue: virtual('setValue')

-  subscribe: virtual()
+  subscribe: virtual('subscribe')

   update: ->
     attr = @_getValue()


#11: :bug: Do not fold when there is no editor

--- lib/fold-functions.coffee
+++ lib/fold-functions.coffee
@@ -142,6 +142,10 @@
     if not editor
       editor = atom.workspace.getActiveTextEditor()

+    if not editor
+      @debugMessage('no editor, skipping')
+      return
+
     @debugMessage('fold functions:', action)
     @indentLevel = @indentLevel || null
     hasFoldableLines = false


Fix proxy setting

Fix proxy setting

--- lib/plugin-manager.coffee
+++ lib/plugin-manager.coffee
@@ -115,6 +115,9 @@
           registry: @config_.mirror.server
         if @config_.proxy
           npmConfig.http_proxy = 'http://127.0.0.1:12450'
+        else
+          if npmConfig.http_proxy?
+            delete npmConfig.http_proxy
         npm.load npmConfig, =>
           resolve @config_


Resolve with summary.

Resolve with summary.

--- lib/product-import.coffee
+++ lib/product-import.coffee
@@ -40,7 +40,6 @@

   performStream: (chunk, cb) ->
     @_processBatches(chunk).then -> cb()
-    .catch (err) -> cb(err)

   _processBatches: (products) ->
     batchedList = _.batchList(products, 30) # max parallel elem to process
@@ -64,7 +63,7 @@
           switch r.statusCode
             when 201 then @_summary.created++
             when 200 then @_summary.updated++
-        Promise.resolve('done')
+        Promise.resolve(@_summary)
     ,{concurrency: 1} # run 1 batch at a time


Bug fix for many to many

Bug fix for many to many

--- lib/relations/many.coffee
+++ lib/relations/many.coffee
@@ -185,7 +185,7 @@
     # return VirtualCursor(query, {model: model, relation: @}) if @manual_fetch # TODO: need to write tests and generalize the checks isFetchable

     json = if model instanceof Backbone.Model then model.attributes else model
-    (query = _.clone(query or {}))[@reverse_relation.foreign_key] = json.id
+    (query = _.clone(query or {}))[if @join_table then @join_key else @reverse_relation.foreign_key] = json.id
     (query.$values or= []).push('id') if key is @virtual_id_accessor
     return @reverse_model_type.cursor(query)


Use gear icon in section heading

Use gear icon in section heading

--- lib/settings-panel.coffee
+++ lib/settings-panel.coffee
@@ -23,7 +23,7 @@

     @append $$ ->
       @section class: 'config-section', =>
-        @div class: 'block section-heading', title
+        @div class: 'block section-heading icon icon-gear', title
         @div class: 'section-body', =>
           for name in _.keys(settings).sort()
             appendSetting.call(this, namespace, name, settings[name])


onDidStopChanging is called on init

onDidStopChanging is called on init

--- lib/show-todo-view.coffee
+++ lib/show-todo-view.coffee
@@ -91,7 +91,9 @@
     @disposables.add atom.workspace.observeTextEditors (editor) =>
       @disposables.add editor.onDidSave => @model.search()

-    @filterEditorView.getModel().onDidStopChanging => @filter()
+    @filterEditorView.getModel().onDidStopChanging =>
+      @filter() if @firstTimeFilter
+      @firstTimeFilter = true

     @scopeButton.on 'click', @toggleSearchScope
     @optionsButton.on 'click', @toggleOptions


Removing 'where' from COFFEE_KEYWORDS, and adding a missing equals sign to a test.

--- lexer.coffee
+++ lexer.coffee
@@ -515,7 +515,7 @@
 COFFEE_KEYWORDS = COFFEE_ALIASES.concat [
   'then', 'unless', 'until', 'loop'
   'yes', 'no', 'on', 'off'
-  'of', 'by', 'where', 'when'
+  'of', 'by', 'when'
 ]

 # The list of keywords that are reserved by JavaScript, but not used, or are


CLI/source inlined css compiler fix

CLI/source inlined css compiler fix

--- cli/source.coffee
+++ cli/source.coffee
@@ -268,7 +268,7 @@
     // embedded css-styles
     (function() {
       var style = document.createElement('style');
-      var rules = document.createTextNode("#{style}")#{if not_lovely then '' else ".replace(/url\\(\"\\//g, 'url(\"'+ Lovely.hostUrl)"};
+      var rules = document.createTextNode("#{style}"#{if not_lovely then '' else ".replace(/url\\(\"\\//g, 'url(\"'+ Lovely.hostUrl)"});

       style.type = 'text/css';
       document.getElementsByTagName('head')[0].appendChild(style);


A little more exciting default place

A little more exciting default place

--- client/app.coffee
+++ client/app.coffee
@@ -24,11 +24,13 @@
   crs = new L.Proj.CRS('EPSG:32633',
       '+proj=utm +zone=33 +ellps=WGS84 +datum=WGS84 +units=m +no_defs',
       {
-        resolutions: resolutions
+        resolutions: resolutions,
+        # transformation:  new L.Transformation(1, -4200000, -1, -2000000)
       }
     )

-  rectangle_editor = new L.RectangleEditor([[59.9,10.7],[59.928893,10.769715]])
+  rectangle_editor = new L.RectangleEditor([[67.177414,15.212889],[67.035306,15.674314]])
+  rectangle_editor.crs = crs;
   map = new L.Map('map', {
     crs: crs,
     scale: (zoom) ->
@@ -36,13 +38,15 @@
     ,
     layers: [
       new L.TileLayer(config.tilesUrl, {
+        attribution: "N50 UTM33 (Bengler)",
         minZoom: 1,
         maxZoom: resolutions.length-1,
         continuousWorld: true,
-        worldCopyJump: false
+        worldCopyJump: false,
+        noWrap: true
       })
     ],
-    center: [59.918893,10.739715],
+    center: [67.098449,15.449095],
     zoom: 18
   });
   rectangle_editor.addTo(map)


Move all the JS under one .ready() call. Not sure if correct.

--- base/assets/javascripts/base.coffee
+++ base/assets/javascripts/base.coffee
@@ -6,12 +6,17 @@
 window.onload = ->
   ($ '.hero-image img').animate({opacity: 1}, 300)

-# Search field highlighting.
 ($ document).ready ->
+  # Search field highlighting.
   ($ '#id_q').focus ->
     ($ this).closest('.searchbar').addClass 'focus'
   ($ '#id_q').blur ->
     ($ this).closest('.searchbar').removeClass 'focus'
+
+  # Happenings-related calls.
+  ($ document).on 'click', '.happening-toggle', ->
+    ($ '.happening-decade-list').toggleClass 'visible'
+    ($ '.happening-toggle .ss-icon:last-child').toggleClass 'ss-navigatedown ss-navigateup'

 # NProgress-related calls.
 ($ document).on 'page:fetch', ->
@@ -22,9 +27,3 @@

 ($ document).on 'page:restore', ->
   NProgress.remove()
-
-# Happenings-related calls.
-($ document).ready ->
-  ($ document).on 'click', '.happening-toggle', ->
-    ($ '.happening-decade-list').toggleClass 'visible'
-    ($ '.happening-toggle .ss-icon:last-child').toggleClass 'ss-navigatedown ss-navigateup'


ListViewController: Add default value for lastToFirst

--- src/components/list/listviewcontroller.coffee
+++ src/components/list/listviewcontroller.coffee
@@ -22,6 +22,7 @@
     # rename these options
     options.noItemFoundWidget     or= null
     options.noMoreItemFoundWidget or= null
+    options.lastToFirst            ?= yes

     Object.defineProperty this, "itemsOrdered", get : =>
       warn "KDListViewController::itemsOrdered is deprecated."
@@ -155,7 +156,6 @@

   removeItem: (itemInstance, itemData, index) ->
-
     return unless itemInstance or itemData or index?

     @getListView().removeItem itemInstance, itemData, index
@@ -179,7 +179,8 @@
   unregisterItem: (itemInstance, index) ->

     @emit 'UnregisteringItem', itemInstance, index
-    actualIndex = if @getOptions().lastToFirst then @getItemCount() - index - 1 else index
+    actualIndex = if @getOptions().lastToFirst then index else @getItemCount() - index - 1
+
     @getListItems().splice actualIndex, 1
     if itemInstance.getData()?
       delete @itemsIndexed[itemInstance.getItemDataId()]


Nodejs Web Server Tests: use csrf middleware for createteam handler

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -60,7 +60,7 @@

 # temp endpoints @cihangir will reorganize these - SY
 app.post '/-/teams/validate-token'               , require './handlers/checktoken'
-app.post '/-/teams/create'                       , require './handlers/createteam'
+app.post '/-/teams/create'                       , csrf,   require './handlers/createteam'
 app.post '/-/teams/join'                         , require './handlers/jointeam'
 app.post '/-/teams/early-access'                 , require './handlers/earlyaccess'
 app.post '/-/teams/verify-domain'                , require './handlers/verifyslug'


Add Emojis.isSupported()

Add Emojis.isSupported()

--- emojis.coffee
+++ emojis.coffee
@@ -112,3 +112,21 @@
 # Replace emoji shortnames with images.
 Emojis.parse = (text) ->
   parse(text, Emojis.template)
+
+Emojis.isSupported = ->
+  if !document.createElement('canvas').getContext
+    return
+
+  context = document.createElement('canvas').getContext('2d')
+
+  if typeof context.fillText isnt 'function'
+    return
+
+  # :smile: String.fromCharCode(55357) + String.fromCharCode(56835)
+  smile = String.fromCodePoint(0x1F604)
+
+  context.textBaseline = "top"
+  context.font = "32px Arial"
+  context.fillText(smile, 0, 0)
+
+  context.getImageData(16, 16, 1, 1).data[0] isnt 0


List view: Accessibility improvement to navigate through rows with 'up' and 'down'. See #60818

--- src/elements/ListView/ListViewTreeNode.coffee
+++ src/elements/ListView/ListViewTreeNode.coffee
@@ -860,7 +860,6 @@
 		CUI.dom.append(element, contentDiv)
 		@

-
 	moveToNewFather: (new_father, new_child_idx) ->
 		old_father = @father
 		old_father.removeChild(@)
@@ -877,3 +876,38 @@
 		CUI.resolvedPromise()

 	moveNodeAfter: (to_node, new_father, after) ->
+
+
+	addedToListView: (DOMNodes) ->
+		tree = @getTree()
+
+		# Just for usability/accessibility. Use 'up' and 'down' to navigate through rows.
+		element = DOMNodes[0]
+		if element
+			CUI.Events.listen
+				type: "keydown"
+				node: element
+				call: (ev) =>
+					keyboardKey = ev.getKeyboardKey()
+					if keyboardKey not in ["Up", "Down"]
+						return
+
+					nextIdx = @getRowIdx()
+					if keyboardKey == "Up"
+						if nextIdx == 0
+							CUI.dom.findPreviousElement(element, "div[tabindex]")?.focus()
+							return
+
+						nextIdx--
+					else
+						nextIdx++
+
+					row = tree.getRow(nextIdx)
+					if not row?[0]
+						CUI.dom.findNextElement(element, "div[tabindex]")?.focus()
+						return
+
+					row[0].focus()
+					return
+
+		return super(DOMNodes)


Create restart method to re-render graph

--- graphedit-coffee/coffee/graphedit.coffee
+++ graphedit-coffee/coffee/graphedit.coffee
@@ -5,10 +5,12 @@
 # GRAPHEDIT CLASS DEFINITION
 # =========================
 class GraphEdit
-	constructor: ( element, options ) ->
+  constructor: ( element, options ) ->
     $el = $(element)
-
-	# config
+
+    me = @
+
+    # config
     @width = 960
     @height = 500
     @colors = d3.scale.category10()
@@ -22,7 +24,6 @@
     @zoom = d3.behavior.zoom().scaleExtent([.1,8]).on "zoom", @redraw

     # init visual
-    me = @
     @canvas = d3
       .select element
       .append "svg"
@@ -40,58 +41,34 @@
       .append "g"

     # init nodes
-    @node_data = ({id:a, reflexive:false} for a in [1..10])
-    @link_data = [{"source":1, "target":3, "value":4}, {"source":1, "target":5, "value":4}]
+    @node_data = ({id:a, reflexive:false} for a in [1..4])
+    @link_data = [{"source":1, "target":3, "value":4}, {"source":1, "target":3, "value":4}]
+    @force = d3.layout.force()
+      #.nodes @node_data
+      #.links @link_data
+      .size [@width, @height]
+      .linkDistance 150
+      .charge -500
+      .on 'tick', @tick
+      .start()

-    #force layout
-    @force = d3.layout.force()
-        .nodes @node_data
-        .links @link_data
-        .size [@width, @height]
-        .linkDistance 150
-        .charge -500
-        .on 'tick', @tick
-        .start()
+    @nodes = @svg.selectAll ".node"
+    @links = @svg.selectAll ".link"

-    # add links
-    @links = @svg.selectAll ".link"
-        .data @link_data
-        .enter()
-        .append "line"
-        .attr "class", "link"
-        .style "stroke", "#000"
-        .style "stroke-width", () -> 1
-
-    # add nodes
-    @nodes = @svg
-        .selectAll ".node"
-        .data @node_data
-        .enter()
-        .append "circle"
-        .attr "class", "node"
-        .attr "r", 5
-        .style "fill", (d) -> me.colors(d.id)
-        .call me.force.drag
-
-        .on "mousedown", (d) ->
-          me.mousedown_node = true
-          me.scale = me.zoom.scale()
-          me.translate = me.zoom.translate()
-          me.select this
-        .on "mouseup", (d) ->
-          me.mousedown_node = false
-          me.zoom.scale me.scale
-          me.zoom.translate me.translate
+    #@setNodes(@node_data)
+    #@setLinks([{"source":1, "target":3, "value":4}, {"source":1, "target":3, "value":4}])
+    @restart()

-	_constructor: GraphEdit
+  _constructor: GraphEdit

-	method : =>
-		alert "I am a method"
+  method : =>
+    alert "I am a method"
+    0

   #animation
-	tick : =>
+  tick : =>
     @nodes.attr "cx", (d) -> d.x
     @nodes.attr "cy", (d) -> d.y

@@ -127,20 +104,75 @@
       @drawSelection()

   redraw : =>
-  	if not @mousedown_node
-		  @svg.attr "transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")"
+    if not @mousedown_node
+      @svg.attr "transform", "translate(" + d3.event.translate + ")" + "scale(" + d3.event.scale + ")"
+
+  resetForce : =>
+    @force
+      .links @link_data
+      .nodes @node_data
+      .start()
+
+  restart : =>
+    me = @
+    @links = @links.data(@link_data)
+
+    # update existing links
+    @links
+      .style "stroke", "#F00"
+      .attr "class", "link"
+
+    # new links
+    @links.enter()
+      .append "line"
+      .attr "class", "link"
+      .style "stroke", "#000"
+      .style "stroke-width", () -> 1
+
+    @links.exit().remove()
+
+    @nodes = @nodes.data(@node_data, (d)-> d.id)
+
+    # update existing nodes
+    @nodes
+      .style "fill", (d) -> me.colors(d.id)
+
+    # new nodes
+    @nodes.enter()
+
+      .append "circle"
+      .attr "class", "node"
+      .attr "r", 5
+      .style "fill", (d) -> me.colors(d.id)
+      .call me.force.drag
+
+      .on "mousedown", () ->
+        me.mousedown_node = true
+        me.scale = me.zoom.scale()
+        me.translate = me.zoom.translate()
+        me.select this
+      .on "mouseup", () ->
+        me.mousedown_node = false
+        me.zoom.scale me.scale
+        me.zoom.translate me.translate
+
+    # clear old ones
+    @nodes.exit().remove()
+
+    # update force
+    @resetForce()

 # GRAPHEDIT PLUGIN DEFINITION
 # ==========================

-$.fn.graphEdit = ( option ) ->
-	this.each ->
-		$this = $(@)
-		data = $this.data 'graphEdit'
-		if !data then $this.data 'graphEdit', (data = new GraphEdit @, option)
-		if typeof option is 'string' then data[option].call $this
+$.fn.graphEdit = ( option, params ) ->
+  this.each ->
+    $this = $(@)
+    data = $this.data 'graphEdit'
+    if !data then $this.data 'graphEdit', (data = new GraphEdit @, option)
+    if typeof option is 'string' then data[option].call $this, params

 $.fn.graphEdit.Constructor = GraphEdit

@@ -149,5 +181,5 @@
 # ===================================

 $ ->
-	$('body').on 'click.graphEdit.data-api', '[data-pluginNameAction^=Action]', ( e ) ->
-		$(e.target).graphEdit()
+  $('body').on 'click.graphEdit.data-api', '[data-pluginNameAction^=Action]', ( e ) ->
+    $(e.target).graphEdit()


fix a mismerged file

fix a mismerged file

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -73,6 +73,16 @@
     # @statusLEDs = new StatusLEDView
     @statusLEDs = new KDView
       cssClass : 'status-leds'
+
+    @virtualizationButtons = new VirtualizationControls
+
+  resetAdminNavController:->
+    @utils.wait 1000, =>
+      @adminNavController.removeAllItems()
+      if KD.isLoggedIn()
+        KD.whoami().fetchRole? (err, role)=>
+          if role is "super-admin"
+            @adminNavController.instantiateListItems adminNavItems.items

   setListeners:->

@@ -304,6 +314,14 @@
       { title : "Keyboard Shortcuts", icon : "shortcuts", action: "showShortcuts" }
     ]

+  adminNavItems =
+    id    : "admin-navigation"
+    title : "admin-navigation"
+    items : [
+      # { title : "Kite selector", loggedIn : yes, callback : -> new KiteSelectorModal }
+      { title : "Admin Panel",     loggedIn : yes, callback : -> new AdminModal }
+    ]
+
   footerMenuItems =
     id    : "footer-menu"
     title : "footer-menu"
@@ -315,4 +333,4 @@
         # unless location.hostname is "localhost"
         new KDNotificationView title : "Coming soon..."
       }
-    ]
+    ]


Collaboration: Error handling

Collaboration: Error handling

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -235,6 +235,10 @@

     @setMachineUser [targetUser], no, (err) =>

+      if err
+        showError "Failed to kick #{targetUser}"
+        return throwError err
+
       kd.singletons.socialapi.channel.kickParticipants options, (err, result) =>

         return showError err  if err
@@ -547,7 +551,7 @@
     { Collaboration } = remote.api
     Collaboration.stop @rtmFileId, @workspaceData, (err) =>

-      return warn err  if err
+      return throwError err  if err

       kd.utils.killRepeat @pingInterval

@@ -718,7 +722,9 @@

     @rtm.createFile @getRealTimeFileName()

-    @setMachineSharingStatus on
+    @setMachineSharingStatus on, (err) ->
+
+      throwError err  if err

   # should clean realtime manager.
@@ -795,9 +801,9 @@
     if @amIHost
       @listChatParticipants (accounts) =>
         usernames = getUsernames accounts
-        @setMachineUser usernames, status
+        @setMachineUser usernames, status, callback
     else
-      @setMachineUser [nick()], status
+      @setMachineUser [nick()], status, callback

   setMachineUser: (usernames, share = yes, callback = kd.noop) ->
@@ -827,8 +833,7 @@

               action = if share then 'added' else 'removed'
               message = "#{username} couldn't be #{action} as an user"
-              callback { message }
-              console.error message
+              callback {message}

       sinkrow.dash queue, callback

@@ -930,9 +935,7 @@
         else args.join ' '

     argIndex = 0
-    error = new Error """
+    console.error """
       IDE.CollaborationController:
       #{ format.replace /%s/g, -> args[argIndex++] or '%s' }
     """
-
-    throw error


collaboration: controller add statemachine transition helper

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -686,6 +686,12 @@
     IDEMetrics.collect 'StatusBar.collaboration_button', 'shown'

+  setCollaborationState: (state) ->
+
+    @stateMachine.transition state
+    @emit 'change', { state }
+
+
   initCollaborationStateMachine: ->

     @stateMachine = new machina.Fsm


Eliminate duplicate configuration

Eliminate duplicate configuration

--- webpack.coffee
+++ webpack.coffee
@@ -57,8 +57,6 @@
       filename: '[name]-bundle.js'
       chunkFilename: '[chunkhash].js'
     plugins: []
-    resolveLoader:
-      root: path.join __dirname, 'node_modules'
     module: loaders: [
         test: /\.woff$/
         loader: 'url-loader?prefix=font/&limit=5000&mimetype=application/font-woff'


View: style fixes (thanks @tenedor)

View: style fixes (thanks @tenedor)

--- coffee/src/util/test.coffee
+++ coffee/src/util/test.coffee
@@ -3,12 +3,12 @@
 test = athena.lib.util.test

 # construct, render, and display view
-test.view_with_options = (options, viewClass, appendTo) ->
-  viewClass = viewClass || lib.View
+test.view_with_options = (options, ViewClass, appendTo) ->
+  ViewClass = ViewClass || lib.View
   appendTo = appendTo || 'body'
-  view = new viewClass options;
+  view = new ViewClass options
   view.render()
-  $(appendTo).append view.$el;
+  $(appendTo).append view.$el
   view

 # construct, render, and display view with content


Implement accent independent regexp for token matching

--- coffee/storage_freak.coffee
+++ coffee/storage_freak.coffee
@@ -187,8 +187,9 @@
         re = new RegExp "#{value[0]}|#{value[1]}", 'ig'
         query = query.replace re, "(?:#{value[0]}|#{value[1]})"

-      return new RegExp "#{query}", 'ig' if type == "infix"
-      return new RegExp "^#{query}", 'ig' if type == "prefix"
+      return new RegExp "#{query}", 'ig'        if type == 'infix'
+      return new RegExp "^#{query}", 'ig'       if type == 'prefix'
+      return new RegExp "\\b#{query}\\b", 'ig'  if type == 'token'

     ## REFACTOR THOSE
     ## MAYBE REMOVE THOSE??


activity: do not list activities if filter is changed [fixes #78607248]

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -293,9 +293,13 @@

   populate: (callback = noop) ->

+    filter = @currentFilter
+
     @fetch null, (err, items = []) =>

       return KD.showError err  if err
+
+      return  if @currentFilter isnt filter

       @listController.hideLazyLoader()
       items.forEach @bound 'appendMessageDeferred'


managed: Check for pre-existing machine, and added heartbeat delay

--- client/app/lib/providers/managed/helpers.coffee
+++ client/app/lib/providers/managed/helpers.coffee
@@ -7,7 +7,10 @@
 # a new kite.
 MAX_HEARTBEATS = 60
 # The default heartbeat interval, in seconds.
-HEARTBEAT_INTERVAL = 5
+HEARTBEAT_INTERVAL = 10
+# The default number of seconds that heartbeat will wait, before
+# starting.
+HEARTBEAT_DELAY = 15

 # Not exported
@@ -106,10 +109,14 @@

       clearInterval intervalId

-      # Check the old kites vs the new kites
+      # Find a new kite, from the kite list.
+      # NOTE: This only handles the first kite. This is mostly because
+      # we need a point at which we stop "heartbeating". If multiple
+      # new kites are needed in the future, we probably want to add
+      # a parameter for the number of kites to "heartbeat" for.
       newKite = null
       for kite in kites
-        if oldKites[kite.keyId]
+        unless kite.machine
           newKite = kite
           break

@@ -133,17 +140,14 @@
     queryPromise.catch (err) ->
       kallback err  if err

-  # First, query for the kites to find the original kites
-  # After that's done, start the heartbeat.
-  queryKites()
-    .then (kites = []) ->
-      oldKites[kite.keyId] = kite  for kite in kites
+  # The delay serves to give klient time to connect, reducing unneeded
+  # heartbeats.
+  heartbeatDelay = HEARTBEAT_DELAY - interval
+  heartbeatDelay = 0  if heartbeatDelay < 0
+
+  setTimeout (->
       intervalId = setInterval heartbeat, interval * 1000
-      # Call the heartbeat immediately *after* setInterval,
-      # so we can clear it if it is immediately available.
-      heartbeat()
-    .catch (err) ->
-      kallback err
+  ), heartbeatDelay * 1000
   return


managed: Added heartbeatKites helper function

--- client/app/lib/providers/managed/helpers.coffee
+++ client/app/lib/providers/managed/helpers.coffee
@@ -1,6 +1,15 @@
 nick = require 'app/util/nick'
 kd   = require 'kd'

+
+# The maximum number of heartbeat attempts when looking for
+# a new kite.
+MAX_HEARTBEATS = 60
+# The default heartbeat interval, in seconds.
+HEARTBEAT_INTERVAL = 5
+
+
+# Not exported
 getIp = (url)->
   el = global.document.createElement 'a'
   el.href = url
@@ -8,50 +17,136 @@
   return el.hostname

-module.exports =
+queryKites = ->

-  queryKites: ->
+  { generateQueryString } = require 'app/kite/kitecache'
+  { computeController, kontrol } = kd.singletons

-    { generateQueryString } = require 'app/kite/kitecache'
-    { computeController, kontrol } = kd.singletons
+  return kontrol
+    .queryKites
+      query         :
+        username    : nick()
+        environment : 'managed'
+    .timeout 5000
+    .then (result)->

-    return kontrol
-      .queryKites
-        query         :
-          username    : nick()
-          environment : 'managed'
-      .timeout 5000
-      .then (result)->
-
-        if result?.kites?.length
-          {kites} = result
-          kites.forEach (kite)->
-            kite.queryString = generateQueryString kite.kite
-            kite.machine     = computeController
-              .findMachineFromQueryString kite.queryString
-            kite.ipAddress   = getIp kite.url
-          return kites
-        else
-          return []
+      if result?.kites?.length
+        {kites} = result
+        kites.forEach (kite)->
+          kite.queryString = generateQueryString kite.kite
+          kite.machine     = computeController
+            .findMachineFromQueryString kite.queryString
+          kite.ipAddress   = getIp kite.url
+        return kites
+      else
+        return []

-  createMachine: (kite, callback)->
+createMachine = (kite, callback)->

-    { computeController } = kd.singletons
+  { computeController } = kd.singletons

-    stack = computeController.stacks.first._id
+  stack = computeController.stacks.first._id

-    computeController.create {
-      provider    : 'managed'
-      queryString : kite.queryString
-      ipAddress   : kite.ipAddress
-      label       : kite.kite.hostname
-      stack
-    }, callback
+  computeController.create {
+    provider    : 'managed'
+    queryString : kite.queryString
+    ipAddress   : kite.ipAddress
+    label       : kite.kite.hostname
+    stack
+  }, callback

-  updateMachineData: ({machine, kite}, callback)->
+updateMachineData = ({machine, kite}, callback)->

-    { queryString, ipAddress } = kite
-    { computeController } = kd.singletons
-    computeController.update machine, {queryString, ipAddress}, callback
+  { queryString, ipAddress } = kite
+  { computeController } = kd.singletons
+  computeController.update machine, {queryString, ipAddress}, callback
+
+
+###*
+ * Repeatedly queryKites at the rate of interval (per second) until
+ * a kite is found.
+ *
+ * @param {Number} interval - The heartbeat interval (in seconds).
+ * @param {Function(err:Error)} callback
+###
+heartbeatKites = (interval = HEARTBEAT_INTERVAL, callback = kd.noop) ->
+
+  { router, computeController } = kd.singletons
+
+  beatCount  = 0
+  oldKites   = {}
+  intervalId = null
+
+  # A callback wrapper, to help ensure clearInterval is always called
+  kallback = (err) ->
+    # It's possible that clearInterval gets called twice, but that
+    # is not a problem in Chrome at least.
+    clearInterval intervalId
+    callback err
+
+  # Called on every interval, until canceled
+  heartbeat = ->
+
+    beatCount++
+    if beatCount > MAX_HEARTBEATS
+      return kallback new Error "Maximum heartbeat limit of
+        #{MAX_HEARTBEATS} reached."
+
+    queryPromise = queryKites()
+    queryPromise.then (kites) ->
+      # No kites ready, wait for another heartbeat
+      return unless kites?.length
+
+      clearInterval intervalId
+
+      # Check the old kites vs the new kites
+      newKite = null
+      for kite in kites
+        if oldKites[kite.keyId]
+          newKite = kite
+          break
+
+      # If there is no newKite, return and wait for another heartbeat
+      unless newKite
+        return
+
+      createMachine newKite, (err, machine) ->
+        return kallback err  if err
+
+        # Route to the IDE
+        kd.utils.defer -> router.handleRoute "/IDE/#{machine.slug}"
+
+        # Get the klient kite, and the info from that klient so we
+        # can popup a Provider specific modal
+        # TODO: Is there a better way to get the klient kite?
+        # TODO: Uncomment, after klientInfo method is available.
+        #klient = computeController.machinesById[machine._id].getBaseKite()
+        #klient.klientInfo kallback
+        kallback null
+
+    queryPromise.catch (err) =>
+        kallback err  if err
+
+  # First, query for the kites to find the original kites
+  # After that's done, start the heartbeat.
+  queryKites()
+    .then (kites = []) ->
+      oldKites[kite.keyId] = kite  for kite in kites
+      intervalId = setInterval heartbeat, interval * 1000
+      # Call the heartbeat immediately *after* setInterval,
+      # so we can clear it if it is immediately available.
+      heartbeat()
+    .catch (err) =>
+      kallback err
+
+
+module.exports = {
+  createMachine
+  heartbeatKites
+  updateMachineData
+  queryKites
+}
+
+


formatted

formatted

--- config_prod.coffee
+++ config_prod.coffee
@@ -10,7 +10,7 @@
         'js/vendor.js': /^vendor/
       order:
         before: [
-	        'vendor/js/console-helper.js',
+          'vendor/js/console-helper.js',
           'vendor/js/jquery-1.8.0.js',
           'vendor/scripts/bootstrap.js',
           'vendor/js/underscore-1.3.3.js',


build: Added keymaps for linux

build: Added keymaps for linux

Change-Id: Ib837513471dc7cc7082d4b36c4ae30255b39ca5d

--- keymaps/build.cson
+++ keymaps/build.cson
@@ -1,5 +1,8 @@
 '.platform-darwin':
-  'alt-cmd-b': 'build:trigger'
+  'cmd-alt-b': 'build:trigger'
+
+'.platform-linux':
+  'ctrl-alt-b': 'build:trigger'

 '.build':
   'escape': 'build:stop'


Cleanup.

Cleanup.

- Since I'm disabling all non jumpy keymaps, might as well simplify the
  cson.

--- keymaps/jumpy.cson
+++ keymaps/jumpy.cson
@@ -10,9 +10,7 @@
 '.editor':
   'cmd-space': 'jumpy:toggle'

-
-'.editor.jumpy-specificity-1.jumpy-specificity-2.jumpy-jump-mode,
-.editor.jumpy-specificity-1.jumpy-specificity-2.jumpy-jump-mode.vim-mode:not(.insert-mode)':
+'.editor.jumpy-jump-mode':
   'space': 'jumpy:clear'
   'escape': 'jumpy:clear'
   'cmd-space': 'jumpy:clear'


try parsing the error message for temporary backwards-compat

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -404,7 +404,9 @@
       osKite?.vmOff()

   handlePrepareError: (err) ->
-    title = err.message  if err.message
+    title =
+      try JSON.parse err.message
+      catch then err.message

     if title and /limit reached/.test title
       title += " Please upgrade to run more VMs."


sidebar: minor code styling

sidebar: minor code styling

--- client/app/lib/mainview.coffee
+++ client/app/lib/mainview.coffee
@@ -1,22 +1,22 @@
-kd = require 'kd'
-KDCustomHTMLView = kd.CustomHTMLView
-KDCustomScrollView = kd.CustomScrollView
-KDScrollView = kd.ScrollView
-KDButtonView = kd.ButtonView
-KDView = kd.View
-sinkrow = require 'sinkrow'
-globals = require 'globals'
-trackEvent = require './util/trackEvent'
-remote = require('./remote').getInstance()
-isLoggedIn = require './util/isLoggedIn'
-whoami = require './util/whoami'
-ActivitySidebar = require './activity/sidebar/activitysidebar'
-AvatarArea = require './avatararea/avatararea'
-CustomLinkView = require './customlinkview'
-GlobalNotificationView = require './globalnotificationview'
-MainTabView = require './maintabview'
-TopNavigation = require './topnavigation'
+kd                      = require 'kd'
+KDCustomHTMLView        = kd.CustomHTMLView
+KDCustomScrollView      = kd.CustomScrollView
+KDScrollView            = kd.ScrollView
+KDButtonView            = kd.ButtonView
+KDView                  = kd.View
+sinkrow                 = require 'sinkrow'
+globals                 = require 'globals'
+trackEvent              = require './util/trackEvent'
+remote                  = require('./remote').getInstance()
+isLoggedIn              = require './util/isLoggedIn'
+whoami                  = require './util/whoami'
 isKoding                = require './util/isKoding'
+ActivitySidebar         = require './activity/sidebar/activitysidebar'
+AvatarArea              = require './avatararea/avatararea'
+CustomLinkView          = require './customlinkview'
+GlobalNotificationView  = require './globalnotificationview'
+MainTabView             = require './maintabview'
+TopNavigation           = require './topnavigation'
 environmentDataProvider = require 'app/userenvironmentdataprovider'


fix old share property name + code formatting

The use of old 'shareWithCozy' property was blocking error handling, as
the event was never detected as shared.

--- client/app/models/event.coffee
+++ client/app/models/event.coffee
@@ -105,7 +105,7 @@
     isShared: ->
         attendees = @get 'attendees'
         cozyAttendees = attendees?.find (attendee) ->
-            return attendee.shareWithCozy
+            return attendee.isSharedWithCozy
         return cozyAttendees?

@@ -163,7 +163,7 @@
             # And then deal with the sharing
             # The goal is to detect any error occurring asynchronously
             if @isShared() and not @hasSharing()
-               @tryGetShareID 5, 2000, ( err, shareID ) =>
+                @tryGetShareID 5, 2000, (err, shareID) =>
                     if err
                         throw
                             name: 'EventSharingError'


Remove debugging code

Remove debugging code

--- client/lanes/components/shared/JobProgress.cjsx
+++ client/lanes/components/shared/JobProgress.cjsx
@@ -14,8 +14,6 @@
         <BS.ProgressBar now={@job.progress * 100} />

     render: ->
-        console.log 'jp rend'
-        window.jbs = @job
         return null if @props.onlyExecuting and not @job.isExecuting

{@props.message}


StackEditor: Editor improvements for Logs support

--- client/new-stack-editor/lib/views/editor.coffee
+++ client/new-stack-editor/lib/views/editor.coffee
@@ -21,6 +21,22 @@
     @_getSession().setValue content

+  addContent: (content) -> @ready =>
+
+    session = @_getSession()
+    [ row, column ] = [ session.getLength(), 0 ]
+    session.insert { row, column }, "#{content}\n"
+    @scrollToBottom()
+
+
+  scrollToBottom: -> @ready =>
+
+    { editor } = @aceView.ace
+    line = @_getSession().getLength()
+    editor.scrollToLine line, yes, yes, kd.noop
+    editor.gotoLine line, 0, yes
+
+
   focus: -> @ready =>
     @aceView.ace.focus()

@@ -28,6 +44,10 @@
   setReadOnly: (state) -> @ready =>

     @aceView.ace.setReadOnly state, no
+    state = if state then 'none' else 'block'
+    @aceView.ace.editor.renderer.$cursorLayer.element.style.display = state
+
+
   viewAppended: ->

     super


Use grunt APIs for read, write, and delete

--- build/tasks/create-installer.coffee
+++ build/tasks/create-installer.coffee
@@ -1,10 +1,9 @@
 fs = require 'fs'
 path = require 'path'
 _ = require 'underscore-plus'
-rimraf = require 'rimraf'

 module.exports = (grunt) ->
-  {spawn} = require('./task-helpers')(grunt)
+  {spawn, rm} = require('./task-helpers')(grunt)

   grunt.registerTask 'create-installer', 'Create the Windows installer', ->
     if process.platform != 'win32'
@@ -16,13 +15,13 @@
     atomDir = path.join(buildDir, 'Atom')

     packageInfo = JSON.parse(fs.readFileSync(path.join(atomDir, 'resources', 'app', 'package.json'), {encoding: 'utf8'}))
-    inputTemplate = fs.readFileSync(path.join('build', 'windows', 'atom.nuspec.erb'), {encoding: 'utf8'})
+    inputTemplate = grunt.file.read(path.join('build', 'windows', 'atom.nuspec.erb'))

     ## NB: Build server has some sort of stamp on the version number
     packageInfo.version = packageInfo.version.replace(/-.*$/, '')

     targetNuspecPath = path.join(buildDir, 'atom.nuspec')
-    fs.writeFileSync(targetNuspecPath, _.template(inputTemplate, packageInfo))
+    grunt.file.write(targetNuspecPath, _.template(inputTemplate, packageInfo))

     cmd = 'build/windows/nuget.exe'
     args = ['pack', targetNuspecPath, '-BasePath', atomDir, '-OutputDirectory', buildDir]
@@ -38,7 +37,7 @@

       ## NB: Gonna clear Releases for now, in the future we need to pull down
       ## the existing version
-      rimraf.sync(releasesDir)
+      rm(releasesDir)

       cmd = 'build/windows/update.com'
       args = ['--releasify', path.join(buildDir, pkgs), '-r', releasesDir]


we don't need to unbind jquery events, as the DOM element removal takes care of this

--- client/code/router/main.coffee
+++ client/code/router/main.coffee
@@ -17,7 +17,6 @@
   importer: true

 Backbone.View::close = ->
-  @$el.off()
   @off()
   @remove()


Fixed Firefox bug with SSH in modal fixes #329

--- client/code/view/subnav.coffee
+++ client/code/view/subnav.coffee
@@ -284,7 +284,7 @@
           window.location = "/dataset/#{dataset.get 'box'}/"

   gitSshTool: (e) ->
-      event.stopPropagation()
+      e.stopPropagation()
       showOrAddSSH window.selectedTool, 'tool'

   showDropdownMenuCloser: ->


Add live reload option

Add live reload option

--- root/Gruntfile.coffee
+++ root/Gruntfile.coffee
@@ -43,6 +43,8 @@
       gruntfile:
         files: "src/**/*"
         tasks: ["compile"]
+      options:
+        livereload: true
     s3:
       options:
         key: "<%= config.aws.key %>"


Remove unused file

Remove unused file

--- client/landing/includes.coffee
+++ client/landing/includes.coffee
@@ -741,7 +741,6 @@
   "stylus/app.group.summary.styl",
   "stylus/app.user.styl",
   "stylus/app.markdown.styl",
-  "stylus/app.vm.styl",
   "stylus/temp.styl",
   # "stylus/app.landing.styl",
   # "stylus/app.predefined.styl",


Include version in socket path on mac and linux

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -19,12 +19,6 @@

 LocationSuffixRegExp = /(:\d+)(:\d+)?$/

-DefaultSocketPath =
-  if process.platform is 'win32'
-    '\\\\.\\pipe\\atom-sock'
-  else
-    path.join(os.tmpdir(), "atom-#{process.env.USER}.sock")
-
 # The application's singleton class.
 #
 # It's the entry point into the Atom application and maintains the global state
@@ -36,7 +30,11 @@

   # Public: The entry point into the Atom application.
   @open: (options) ->
-    options.socketPath ?= DefaultSocketPath
+    unless options.socketPath?
+      if process.platform is 'win32'
+        options.socketPath = '\\\\.\\pipe\\atom-sock'
+      else
+        options.socketPath = path.join(os.tmpdir(), "atom-#{options.version}-#{process.env.USER}.sock")

     createAtomApplication = -> new AtomApplication(options)


#1661 - Skip logic doesn't update when user changes option value

--- jsapp/xlform_model_view/view.rowDetail.SkipLogic.coffee
+++ jsapp/xlform_model_view/view.rowDetail.SkipLogic.coffee
@@ -268,14 +268,26 @@
       super(target)

     bind_event: (handler) ->
-      super 'change', handler
-
-
-    constructor: (@responses) ->
-      super(_.map @responses.models, (response) ->
+      handle_model_change = () =>
+        @options = @rebuild_options()
+        @render()
+        handler()
+        return
+
+      for option in @responses.models
+        option.off 'change', null, @
+        option.on 'change', handle_model_change, @
+
+      super 'change', () ->
+        handler.apply @, arguments
+
+    rebuild_options: () ->
+      return _.map @responses.models, (response) ->
         text: response.get('label')
         value: response.get('name')
-      )
+
+    constructor: (@responses) ->
+      super(@rebuild_options())

   class viewRowDetailSkipLogic.SkipLogicCriterion extends viewRowDetailSkipLogic.Base
     tagName: 'div'


added control for if kite is not found

added control for if kite is not found

--- workers/social/lib/social/models/kites.coffee
+++ workers/social/lib/social/models/kites.coffee
@@ -116,6 +116,9 @@
             if err
               callback err
               fin()
+            else if not kite
+              # if relation found but source is not found
+              fin()
             else
               teasers.push(kite)
               fin()


Improved web socket port detection (added support for window.location.port).

--- app/assets/javascripts/app/lib/app_post/websocket.coffee
+++ app/assets/javascripts/app/lib/app_post/websocket.coffee
@@ -199,10 +199,13 @@
       protocol = 'wss://'

     if @backend is 'websocket'
-      @ws = new window.WebSocket( protocol + window.location.hostname + '/ws' )
+      port = ''
+      if window.location.port && window.location.port != ''
+        port = ":#{window.location.port}"
+      @ws = new window.WebSocket("#{protocol}#{window.location.hostname}#{port}/ws")
     else if @backend is 'websocketPort'
       @backend_port = App.Config.get('websocket_port') || '6042'
-      @ws           = new window.WebSocket( protocol + window.location.hostname + ':' + @backend_port + '/' )
+      @ws           = new window.WebSocket("#{protocol}#{window.location.hostname}:#{@backend_port}/")
     else
       @_ajaxInit()


fix unbalanced paren

fix unbalanced paren

--- examples/app/spectrogram/waterfall.coffee
+++ examples/app/spectrogram/waterfall.coffee
@@ -47,7 +47,7 @@
     for i in [0...@model.gram_length]
       @image[@tile][i*@model.tile_width+@col] = buf32[i]

-    sx = @xscale.v_compute(x))
+    sx = @xscale.v_compute(x)
     sy = @yscale.compute(0)
     sw = Math.ceil(@xscale.compute(@model.tile_width) - @xscale.compute(0))
     sh = Math.ceil(@yscale.compute(@max_freq))


fix account delete bug

fix account delete bug

after account deleted, unneccesary account was created.

--- coffee/services/redmine.coffee
+++ coffee/services/redmine.coffee
@@ -3,8 +3,9 @@
   _redmines = {}

   return (auth, remove) ->
-    if remove and _redmines[auth.url]?
-      delete _redmines[auth.url]
+    if remove
+      if _redmines[auth.url]?
+        delete _redmines[auth.url]
       return
     if not _redmines[auth.url]
       _redmines[auth.url] = new Redmine(auth, $http, Base64, $rootScope)


Fix wrong event handler declaration in coffeescript, for the click event on the New Folder popup.

When applying the changes on dca2030ae423cc6070cb4001e7d5c4de132e44fd the line declaring this hander was half-changed between the old and the new format. The result was an invalid coffeescript line which resulted in invalid javacript and messed up the app's javascript code, making exceptions to be thrown and some handlers not work.


update currentWeek check

update currentWeek check

--- app/assets/javascripts/timesheet/controllers/week.coffee
+++ app/assets/javascripts/timesheet/controllers/week.coffee
@@ -23,7 +23,8 @@
         $location.path('/week/' + $scope.datepickerSelect)

     $scope.isCurrentWeek = ->
-      $scope.date is moment($scope.currentDate).startOf('isoWeek').format('YYYY-MM-DD')
+      date = moment($scope.date)
+      date.startOf('isoWeek').format('YYYY-MM-DD') is moment($scope.currentDate).startOf('isoWeek').format('YYYY-MM-DD')

     $scope.endOfWeek = ->
       date = moment($scope.date)


outcome gradebook: skip deleted section enrollments

fixes CNVS-12206

test plan:
  * create a section in a course
  * enroll the test student in the section
      (I think resetting the test student may do this?)
  * delete the section
  * load the outcome gradebook
  * verify that it loads correctly

Change-Id: I0c9fc90aceb91e94a5f2ed5cc523140ee0656510
Reviewed-on: https://gerrit.instructure.com/43720
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Sean Lewis <e0580b9aacf857d9a3792f1965500f217c0fc9c9@instructure.com>
Product-Review: Braden Anderson <5d57265d16cfe2e0f2e6eb7f13e876bb378ef0f1@instructure.com>

--- app/coffeescripts/gradebook2/OutcomeGradebookGrid.coffee
+++ app/coffeescripts/gradebook2/OutcomeGradebookGrid.coffee
@@ -188,6 +188,7 @@
         return null unless Grid.Util.sectionFilter(section, rollup)
         student = Grid.Util.lookupStudent(rollup.links.user)
         section = Grid.Util.lookupSection(rollup.links.section)
+        return null unless section
         row =
           student: _.extend(
             grades_html_url: "/courses/#{section.course_id}/grades/#{student.id}#tab-outcomes" # probably should get this from the enrollment api


Use prompt marker when cycling commands

Use prompt marker when cycling commands

--- lib/atom-slime-repl-view.coffee
+++ lib/atom-slime-repl-view.coffee
@@ -308,9 +308,9 @@
     # Sets the command at the prompt
     lastrow = @editor.getLastBufferRow()
     lasttext = @editor.lineTextForBufferRow(lastrow)
-    range = new Range([lastrow, 0], [lastrow, lasttext.length])
-    newtext = "#{@prompt}#{cmd}"
-    @editor.setTextInBufferRange(range, newtext)
+    promptEnd = @promptMarker.getBufferRange().end
+    range = new Range(promptEnd, [lastrow, lasttext.length])
+    @editor.setTextInBufferRange(range, cmd)
     @editor.getBuffer().groupLastChanges()


Plain shader: Add support for custom matrix inputs.

--- engine/material_shaders/plain.coffee
+++ engine/material_shaders/plain.coffee
@@ -5,7 +5,7 @@
         {data, _input_list, inputs, _texture_list} = @material
         for u in data.uniforms or []
             {varname, value} = u
-            _input_list.push inputs[varname] = {value, type: value.length or 1}
+            _input_list.push inputs[varname] = {value}
             if value.type == 'TEXTURE'
                 _texture_list.push inputs[varname]

@@ -49,6 +49,12 @@
             else if value.b?
                 "v=#{value_code};gl.uniform3f(locations[#{loc_idx}],
                     v.r, v.g, v.b);"
+            else if value.m15?
+                "gl.uniformMatrix4fv(locations[#{loc_idx}], false,
+                    #{value_code}.toJSON());"
+            else if value.m08?
+                "gl.uniformMatrix3fv(locations[#{loc_idx}], false,
+                    #{value_code}.toJSON());"
             else
                 "gl.uniform1f(locations[#{loc_idx}], #{value_code});"
             locations.push uloc


filters blocks with no channel_ids (closes #174)

--- collections/user_blocks.coffee
+++ collections/user_blocks.coffee
@@ -43,6 +43,6 @@

       collection = collection.concat(grouped_blocks['undefined']) if grouped_blocks['undefined']

-      return collection
+      return _.reject collection, (item) -> item.class is 'Block' and item.channel_ids.length < 1
     else
-      super+      super


(#20) Fix wrong indentation for env command

--- lib/autocomplete/command.coffee
+++ lib/autocomplete/command.coffee
@@ -119,7 +119,7 @@
     latex:
       begin:
         displayText: 'begin'
-        snippet: 'begin{$1}\n  $2\n\\\\end{$1}'
+        snippet: 'begin{$1}\n\t$2\n\\\\end{$1}'
         chainComplete: true
       cite:
         displayText: 'cite'


Patch around the slope thing

Patch around the slope thing

--- entity.coffee
+++ entity.coffee
@@ -134,7 +134,7 @@
     baseSpeed += 1 if @right
     baseSpeed *= Constants.k('movement_speed')
     physics = body.GetWorld().UserData
-    [x_, y_] = physics.rotate([baseSpeed, 0])
+    [x_, y_] = physics.rotate([baseSpeed, -Constants.k('movement_upforce')])
     body.ApplyForce(new Box2D.Common.Math.b2Vec2(x_, y_),
                     body.GetWorldCenter())
     @next.doTick()


jupyter: copy/select didn't work when cell goes from unfocused to focused

--- src/smc-webapp/jupyter/codemirror-static.cjsx
+++ src/smc-webapp/jupyter/codemirror-static.cjsx
@@ -51,6 +51,11 @@
             misc_page.clear_selection()
             @props.actions.select_cell_range(@props.id)
             event.stopPropagation()
+            return
+        if window.getSelection().toString()
+            # User is selected some text in the cell; if we switch to edit mode
+            # then the selection would be cleared, which is annoying.  NOTE that
+            # this makes the behavior slightly different than official Jupyter.
             return
         @props.actions.set_mode('edit')
         @props.actions.unselect_all_cells()


Options

Options

- new method "getOptions" to return current options.

--- src/elements/Options/Options.coffee
+++ src/elements/Options/Options.coffee
@@ -166,9 +166,12 @@
 		for opt, idx in @__options
 			if opt.value == value
 				found = idx
+				break
 		assert(found != null, "CUI.Options.__getCheckboxByValue", "Value #{value} not found in Options.", options: @__options)
 		@__checkboxes[found]

+	getOptions: ->
+		@__options

 	setValue: (v, flags={}) ->
 		flags.__set_on_data = true


Setting up new applications right after its creation.

--- cli/src/commands/add_project.coffee
+++ cli/src/commands/add_project.coffee
@@ -2,6 +2,7 @@
 path = require 'path'
 pn   = path.normalize
 exec = (require "child_process").exec
+spawn = (require "child_process").spawn

 module.exports = class AddProject

@@ -33,3 +34,7 @@
           files = stdout.split("\n").slice 0, -1
           for file in files
             console.log "#{'Created'.bold} #{file}".green
+
+          # install dependencies through a spawned `make setup` call
+          console.log '\nApplication created, setting up...\n\n'
+          spawn 'make', ['setup'], {cwd: @target, stdio: 'inherit'}


Use a step parameter to sample the histogram calculation

--- components/GetHistogram.coffee
+++ components/GetHistogram.coffee
@@ -1,6 +1,8 @@
 noflo = require 'noflo'
 d3 = require 'd3-color'
 # @runtime noflo-nodejs
+
+defaultStep = 1

 zero = (a) ->
   for i in [0...a.length]
@@ -41,14 +43,15 @@
   res.c[c] += 1
   do callback

-computeHistogram = (data, res, callback) ->
-  pixels = data.length/4
-  countEntries = 0
+computeHistogram = (data, res, step=defaultStep, callback) ->
+  rgba = 4
   coutHistograms = 0
-  for i in [0...data.length] by 4
-    addEntryHistogram data, res, i, ->
-      countEntries++
-      if countEntries == pixels
+  step = if (data.length < (step * rgba)) or (step < 1) then 1 else step
+  pixels = data.length / (rgba * step)
+  last = data.length - step * rgba
+  for index in [0...data.length] by step * rgba
+    addEntryHistogram data, res, index, ->
+      if index >= last
         # Normalize such that 1.0 means all pixels have this color
         for histogram of res
           normalize res[histogram], pixels, ->
@@ -63,6 +66,8 @@

   c.inPorts.add 'canvas',
     datatype: 'object'
+  c.inPorts.add 'step',
+    datatype: 'number'
   c.outPorts.add 'histogram',
     datatype: 'object'
   c.outPorts.add 'error',
@@ -70,6 +75,7 @@

   noflo.helpers.WirePattern c,
     in: ['canvas']
+    params: ['step']
     out: ['histogram', 'error']
     forwardGroups: true
     async: true
@@ -81,6 +87,7 @@
       do callback
       return

+    c.params.step = defaultStep unless c.params.step
     ctx = canvas.getContext '2d'
     imageData = ctx.getImageData 0, 0, canvas.width, canvas.height
     result =
@@ -93,6 +100,6 @@
       s: zero new Array 101 # [0.0, 1.0] -> [0, 101]
       c: zero new Array 135 # https://github.com/gka/chroma.js/issues/63
       l: zero new Array 101 # [0.0, 1.0] -> [0, 101]
-    computeHistogram imageData.data, result, ->
+    computeHistogram imageData.data, result, c.params.step, ->
       outPorts.histogram.send result
       do callback


Remove dead test

Remove dead test

It tests functionality that no longer matters since we don’t subscribe
to decorations being destroyed in DisplayBuffer

--- spec/display-buffer-spec.coffee
+++ spec/display-buffer-spec.coffee
@@ -1237,11 +1237,6 @@
       decoration.destroy()
       expect(displayBuffer.decorationForId(decoration.id)).not.toBeDefined()

-    it "does not leak disposables", ->
-      disposablesSize = displayBuffer.disposables.disposables.size
-      decoration.destroy()
-      expect(displayBuffer.disposables.disposables.size).toBe(disposablesSize - 1)
-
     describe "when a decoration is updated via Decoration::update()", ->
       it "emits an 'updated' event containing the new and old params", ->
         decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()


Documentation

Documentation

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -17,9 +17,6 @@
    # now player has all the methods provided by this module
    player.position()
 => {x: 10, y: 50}
-
-   player.collides({x: 15, y: 55, width: 20, height: 20})
-=> true
 </pre></code>

 @see GameObject


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/bounded.coffee
+++ src/bounded.coffee
@@ -43,7 +43,7 @@
   ###*
   Get the object closest to this one.

-  @param @param {Object|Array|String} groupB An object or set of objects to check collisions with
+  @param {Object|Array|String} groupB An object or set of objects to check collisions with
   ###
   closest: (selector) ->
     if Object.isString(selector)


don't make virtual tables on db update_schema

--- salvus/rethink.coffee
+++ salvus/rethink.coffee
@@ -202,9 +202,9 @@
                 @db.tableList().run (err, x) =>
                     if err
                         cb(err)
-                    tables = (t for t in misc.keys(SCHEMA) when t not in x and not t.virtual)
+                    tables = (t for t,s of SCHEMA when t not in x and not s.virtual)
                     if tables.length > 0
-                        dbg("creating #{tables.length} tables")
+                        dbg("creating #{tables.length} tables: #{tables.join(', ')}")
                     async.map(tables, ((table, cb) => @db.tableCreate(table, table_options(table)).run(cb)), cb)
             (cb) =>
                 f = (name, cb) =>


Remove server-side santization of project name, it's all escaped client side now

--- services/web/app/coffee/Features/Editor/EditorController.coffee
+++ services/web/app/coffee/Features/Editor/EditorController.coffee
@@ -263,7 +263,6 @@
 			callback?()

 	renameProject: (project_id, newName, callback = (err) ->) ->
-		newName = sanitize.escape(newName)
 		ProjectDetailsHandler.renameProject project_id, newName, =>
 			EditorRealTimeController.emitToRoom project_id, 'projectNameUpdated', newName
 			callback()


Change forme to default when species changes

--- client/app/js/models/pokemon.coffee
+++ client/app/js/models/pokemon.coffee
@@ -26,8 +26,9 @@
     # Skip teambuilder-specific properties.
     return  if @get('teambuilder') != true

-    # Set to default ability when the species changes
+    # Set to default forme and ability when the species changes
     @on 'change:name', =>
+      @set('forme', 'default')
       @set('ability', @getAbilities()[0], silent: true)

     @on 'change:ivs', (model, ivs)=>


fix mentions (#5242)

fix mentions (#5242)

--- client/shared/helpers/listen.coffee
+++ client/shared/helpers/listen.coffee
@@ -9,10 +9,10 @@
     updateMentionables = ->
       chain = Records.users.collection.chain().find(id: {'$in': $scope.mentionableUserIds})
       chain = chain.where (u) ->
-        _.isString(u.username) &&
-        (u.name.toLowerCase().startsWith($scope.q) or
-         (u.username || "").toLowerCase().startsWith($scope.q) or
-         u.name.toLowerCase().includes(" #{$scope.q}"))
+        name = (u.name || "").toLowerCase()
+        username = (u.username || "").toLowerCase()
+        name.startsWith($scope.q) or username.startsWith($scope.q) or name.includes(" #{$scope.q}")
+
       $scope.mentionables = _.sortBy(chain.data(), (u) -> (0 - Records.events.find(actorId: u.id).length))

     fetchThenUpdate = _.throttle ->


Some syntax fixes.

Some syntax fixes.

--- src/channel.coffee
+++ src/channel.coffee
@@ -20,7 +20,7 @@
     @handlers.push handler

   remove: (handler) ->
-    @handlers = (for _handler in @handlers when _handler isnt handlers)
+    @handlers = (_handler for _handler in @handlers when _handler isnt handlers)

   forward: (channel) ->
     @receive (message) =>


Fixes model teardown

Fixes model teardown

--- spec/backbone.associate.spec.coffee
+++ spec/backbone.associate.spec.coffee
@@ -66,11 +66,13 @@

     it 'does overwrite defaults', ->
       expected = { one: { foo: 'bar' } }
+      defaults = @modelA::defaults
       @modelA::defaults = expected
       model = new @modelA {}, parse: true
       for key, association of @associations
         if _.has expected, key
           expect(model[key]().attributes).toEqual expected[key]
+      @modelA::defaults = defaults

   describe 'parsing', ->


tweak slow rect test colors

tweak slow rect test colors

--- static/coffee/unittest/perf_test.coffee
+++ static/coffee/unittest/perf_test.coffee
@@ -7,7 +7,7 @@
 y = (Math.sin(y) for y in x)
 widths = (0.01 for i in x)
 heights = (0.2 for i in x)
-colors = ("rgb(#{ Math.floor(155+100*val) }, #{ Math.floor(180-50*val) }, #{ Math.floor(100+50*val) })" for val in y)
+colors = ("rgb(#{ Math.floor(155+100*val) }, #{ Math.floor(100+50*val) }, #{ Math.floor(120-50*val) })" for val in y)
 source = Collections('ColumnDataSource').create(
   data:
     x: x


don't refresh channels as much, not needed anymore, we don't have unread counts

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -64,7 +64,7 @@
     clearTimeout @_currentTimeout if @_currentTimeout?
     delete @_currentTimeout
     callMyselfSoon = =>
-      @_currentTimeout = setTimeout _.bind(args.callee, this), 59*1000
+      @_currentTimeout = setTimeout _.bind(args.callee, this), (10*60*1000)-1

     @fetch


bits: Add mark() and marked_bytes()

bits: Add mark() and marked_bytes()

--- bits.coffee
+++ bits.coffee
@@ -307,6 +307,18 @@
     else
       Bits.parse_bits_uint @buf, byteOffset * 8, 8

+  mark: ->
+    if not @marks?
+      @marks = [ @byte_index ]
+    else
+      @marks.push @byte_index
+
+  marked_bytes: ->
+    if (not @marks?) or (@marks.length is 0)
+      throw new Error "The buffer has not been marked"
+    startIndex = @marks.pop()
+    return @buf[startIndex..@byte_index-1]
+
   @searchBytesInArray: (haystack, needle, from_pos=0) ->
     if from_pos >= haystack.length
       return -1


bits: Add usage

bits: Add usage

--- bits.coffee
+++ bits.coffee
@@ -1,4 +1,44 @@
-# Utility functions for buffer operation
+# Utility for Buffer operations
+
+###
+# Usage
+
+    Bits = require './bits'
+
+    # Reader
+    buf = new Buffer [
+      0b11010001
+      0b11110000
+      0x7f, 0xff, 0xff, 0xff
+      0x80, 0x00, 0x00, 0x00
+    ]
+    myBits = new Bits buf  # A Bits instance holds a cursor
+    console.log myBits.read_bit()
+    # => 1
+    console.log myBits.read_bits 2
+    # => 2
+    myBits.skip_bits 5
+    console.log myBits.read_byte()  # Returns a number
+    # => 240
+    console.log myBits.read_bytes 2  # Returns a Buffer instance
+    # => <Buffer 7f ff>
+    myBits.push_back_bytes 2  # Move the cursor two bytes back
+    console.log myBits.read_int 32
+    # => 2147483647
+    console.log myBits.read_int 32
+    # => -2147483648
+
+    # Writer
+    myBits = new Bits()
+    myBits.create_buf()
+    myBits.add_bit 1         # 0b1_______
+    myBits.add_bits 2, 1     # 0b101_____
+    myBits.add_bits 5, 3     # 0b10100011
+    myBits.add_bits 8, 0xff  # 0b10100011, 0b11111111
+    resultBuf = myBits.get_created_buf()  # Returns a Buffer instance
+    Bits.printBinary resultBuf
+    # => 10100011 11111111
+###

 class Bits
   constructor: (buffer) ->


Ensure callback to Command::spawn is only called once

Signed-off-by: Katrina Uychaco <ef0b2ab5bd1ac851826b76acd653f3c4825374fb@github.com>

--- src/command.coffee
+++ src/command.coffee
@@ -28,10 +28,15 @@
       else
         errorChunks.push(chunk)

-    spawned.on 'error', (error) ->
-      callback(error, Buffer.concat(errorChunks).toString(), Buffer.concat(outputChunks).toString())
-    spawned.on 'close', (code) ->
-      callback(code, Buffer.concat(errorChunks).toString(), Buffer.concat(outputChunks).toString())
+    onChildExit = (errorOrExitCode) ->
+      spawned.removeListener 'error', onChildExit
+      spawned.removeListener 'close', onChildExit
+      callback?(errorOrExitCode, Buffer.concat(errorChunks).toString(), Buffer.concat(outputChunks).toString())
+
+    spawned.on 'error', onChildExit
+    spawned.on 'close', onChildExit
+
+    spawned

   fork: (script, args, remaining...) ->
     args.unshift(script)


Tweak to burger data.

Tweak to burger data.

--- scripts/burger.coffee
+++ scripts/burger.coffee
@@ -20,7 +20,7 @@
         "rating": "4",
         "minutes": "4",
         "distance": "0.2",
-        "address": "High Street, Northern Quarter",
+        "address": "High Street",
         "postcode": "M4 1HP",
         "latitude": "53.484476",
         "longitude": "-2.236841"


simplify

simplify

--- src/compile.coffee
+++ src/compile.coffee
@@ -153,14 +153,14 @@
     dynamic

   in: (ast, model, controller) ->
-    Compile.bound ast, model, controller, (dynamic, value) ->
+    @bound ast, model, controller, (dynamic, value) ->
       if value
         dynamic.replace([compileAll(ast.children, value, controller)])
       else
         dynamic.clear()

   if: (ast, model, controller) ->
-    Compile.bound ast, model, controller, (dynamic, value) ->
+    @bound ast, model, controller, (dynamic, value) ->
       if value
         dynamic.replace([compileAll(ast.children, model, controller)])
       else if ast.else
@@ -169,7 +169,7 @@
         dynamic.clear()

   unless: (ast, model, controller) ->
-    Compile.bound ast, model, controller, (dynamic, value) ->
+    @bound ast, model, controller, (dynamic, value) ->
       if value
         dynamic.clear()
       else
@@ -186,4 +186,4 @@
     dynamic

 compile = (ast, model, controller) -> Compile[ast.type](ast, model, controller)
-compileAll = (asts, model, controller) -> Compile[ast.type](ast, model, controller) for ast in asts
+compileAll = (asts, model, controller) -> compile(ast, model, controller) for ast in asts


format complex numbers with infinite Re or Im part

--- src/complex.coffee
+++ src/complex.coffee
@@ -25,7 +25,7 @@
     if isNaN(@re) or isNaN(@im) then throw DomainError 'NaN'

   toString: ->
-    "#{@re}J#{@im}".replace /-/g, '¯'
+    "#{@re}J#{@im}".replace /-|Infinity/g, '¯'

   @exp = exp = (x) ->
     x = complexify x


Added comment for why logging is added here

--- scripts/config.coffee
+++ scripts/config.coffee
@@ -65,6 +65,9 @@
       exports: 'Backbone'

     marionette:
+      # Load the Backbone Logger before Marionette, since Marionette clones `Backbone.Events`.
+      # Waiting until after Marionette loads requires modifying every single Marionette component,
+      # or nearly all of them (as they nearly all individually clone `Backbone.Events`).
       deps: ['underscore', 'backbone', 'cs!helpers/logger']
       exports: 'Marionette'


don't allow output inside of renderables

--- src/context.coffee
+++ src/context.coffee
@@ -175,26 +175,31 @@
         $item

       renderable: (o, fn) ->
-        o._lead_render = fn
+        nested_context = run_context.create_nested_context
+          renderable_list_builder: add_renderable: -> throw new Error 'Output functions not allowed inside a renderable'
+        o._lead_render = -> run_context.in_context nested_context, fn
         o

       nested: (className, fn, args...) ->
         $item = $ "<div class='#{className}'/>"
         run_context.current_context.nested_item $item, fn, args...

-      create_nested_context: ($item) ->
+      create_nested_renderable_context: ($item) ->
         renderable = delayed_renderable_list_builder $item
-        nested_context = _.extend {}, run_context,
+        run_context.create_nested_context
           renderable_list_builder: renderable
+
+      create_nested_context: (overrides) ->
+        nested_context = _.extend {}, run_context, overrides

       detached:  (fn, args) ->
         $item = $ "<div/>"
-        nested_context = run_context.create_nested_context $item
+        nested_context = run_context.create_nested_renderable_context $item
         run_context.in_context nested_context, fn, args
         nested_context.renderable_list_builder

       nested_item: ($item, fn, args...) ->
-        nested_context = run_context.create_nested_context $item
+        nested_context = run_context.create_nested_renderable_context $item
         run_context.add_renderable nested_context.renderable_list_builder
         run_context.in_context nested_context, fn, args


Fixed bug i caused.

Fixed bug i caused.

--- app/assets/javascripts/chat.js.erb.coffee
+++ app/assets/javascripts/chat.js.erb.coffee
@@ -5,9 +5,11 @@

   chat_input_validation = () ->
     input = $("#new_message > #message")
-    if (input.attr('value').match(/^\S*$/) != null) and (input.attr('value').length >= 1) == true
+    if (input.attr('value').match(/^\s*$/) == null) and (input.attr('value').length >= 1) == true
       true
     else
+      console.log input.attr('value').match(/^\S*$/)
+      console.log (input.attr('value').length >= 1)
       input.attr('value','')
       false


Student rating ajax call now fadesout face before call and only shows after success

--- app/assets/javascripts/domready.js.coffee
+++ app/assets/javascripts/domready.js.coffee
@@ -10,11 +10,13 @@
   $('#track_end_time').pickatime({format: 'hh:i a'})

   # Student rating AJAX
+  $('.choices a').on "ajax:before", ->
+    $ratings = $(this).closest('.ratings')
+    $ratings.find('.choices-toggle').fadeOut()
+    $ratings.find('.choices').toggleClass('show')
+
   $('.choices a').on "ajax:success", (e, data) ->
-    $checkpoint = $("#checkpoint#{data.checkpoint_id}")
-    $checkpoint.find('.choices-toggle').fadeOut ->
-      $(this).html(data.partial).fadeIn()
-    $checkpoint.find('.choices').toggleClass('show')
+    $("#checkpoint#{data.checkpoint_id} .choices-toggle").html(data.partial).fadeIn()

     # SegmentIO event: Student Rates Checkpoint
     analytics.track "Rate checkpoint",


Organize permissions

Organize permissions

--- packages/rocketchat-authorization/server/startup.coffee
+++ packages/rocketchat-authorization/server/startup.coffee
@@ -5,126 +5,46 @@
 	# then we can define edit-<type>-message instead of edit-message
 	# 2. admin, moderator, and user roles should not be deleted as they are referened in the code.
 	permissions = [
-
-		{ _id: 'view-statistics',
-		roles : ['admin']}
-
-		{ _id: 'run-import',
-		roles : ['admin']}
-
-		{ _id: 'view-privileged-setting',
-		roles : ['admin']}
-
-		{ _id: 'edit-privileged-setting',
-		roles : ['admin']}
-
-		{ _id: 'view-room-administration',
-		roles : ['admin']}
-
-		{ _id: 'view-user-administration',
-		roles : ['admin']}
-
-		{ _id: 'view-full-other-user-info',
-		roles : ['admin']}
-
-		{ _id: 'add-user-to-room',
-		roles : ['admin', 'owner', 'moderator']}
-
-		{ _id: 'create-user',
-		roles : ['admin']}
-
-		{ _id: 'edit-other-user-info',
-		roles : ['admin']}
-
-		{ _id: 'edit-other-user-password',
-		roles : ['admin']}
-
-		{ _id: 'assign-admin-role',
-		roles : ['admin']}
-
-		{ _id: 'edit-other-user-active-status',
-		roles : ['admin']}
-
-		{ _id: 'delete-user',
-		roles : ['admin']}
-
-		{ _id: 'view-other-user-channels',
-		roles : ['admin']}
-
-		{ _id: 'add-oauth-service',
-		roles : ['admin']}
-
-		{ _id: 'run-migration',
-		roles : ['admin']}
-
-		{ _id: 'create-c',
-		roles : ['admin', 'user']}
-
-		{ _id: 'delete-c',
-		roles : ['admin']}
-
-		{ _id: 'edit-room',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'edit-message',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'delete-message',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'remove-user',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'mute-user',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'ban-user',
-		roles : ['admin', 'moderator', 'owner']}
-
-		{ _id: 'set-moderator',
-		roles : ['admin', 'owner']}
-
-		{ _id: 'set-owner',
-		roles : ['admin', 'owner']}
-
-		{ _id: 'create-p',
-		roles : ['admin', 'user']}
-
-		{ _id: 'delete-p',
-		roles : ['admin']}
-
-		{ _id: 'delete-d',
-		roles : ['admin']}
-
-		{ _id: 'bulk-register-user',
-		roles : ['admin']}
-
-		{ _id: 'bulk-create-c',
-		roles : ['admin']}
-
-		{ _id: 'view-c-room',
-		roles : ['admin', 'user', 'bot']}
-
-		{ _id: 'view-p-room',
-		roles : ['admin', 'user']}
-
-		{ _id: 'view-d-room',
-		roles : ['admin', 'user']}
-
-		{ _id: 'access-permissions',
-		roles : ['admin']}
-
-		{ _id: 'manage-assets',
-		roles : ['admin']}
-
-		{ _id: 'manage-integrations',
-		roles : ['admin', 'bot']}
-
-		{ _id: 'manage-oauth-apps',
-		roles : ['admin']},
-
-		{ _id: 'view-logs',
-		roles : ['admin']}
+		{ _id: 'access-permissions', roles : ['admin'] }
+		{ _id: 'add-oauth-service', roles : ['admin'] }
+		{ _id: 'add-user-to-room', roles : ['admin', 'owner', 'moderator'] }
+		{ _id: 'assign-admin-role', roles : ['admin'] }
+		{ _id: 'ban-user', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'bulk-create-c', roles : ['admin'] }
+		{ _id: 'bulk-register-user', roles : ['admin'] }
+		{ _id: 'create-c', roles : ['admin', 'user'] }
+		{ _id: 'create-p', roles : ['admin', 'user'] }
+		{ _id: 'create-user', roles : ['admin'] }
+		{ _id: 'delete-c', roles : ['admin'] }
+		{ _id: 'delete-d', roles : ['admin'] }
+		{ _id: 'delete-message', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'delete-p', roles : ['admin'] }
+		{ _id: 'delete-user', roles : ['admin'] }
+		{ _id: 'edit-message', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'edit-other-user-active-status', roles : ['admin'] }
+		{ _id: 'edit-other-user-info', roles : ['admin'] }
+		{ _id: 'edit-other-user-password', roles : ['admin'] }
+		{ _id: 'edit-privileged-setting', roles : ['admin'] }
+		{ _id: 'edit-room', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'manage-assets', roles : ['admin'] }
+		{ _id: 'manage-integrations', roles : ['admin', 'bot'] }
+		{ _id: 'manage-oauth-apps', roles : ['admin'] }
+		{ _id: 'mute-user', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'remove-user', roles : ['admin', 'moderator', 'owner'] }
+		{ _id: 'run-import', roles : ['admin'] }
+		{ _id: 'run-migration', roles : ['admin'] }
+		{ _id: 'set-moderator', roles : ['admin', 'owner'] }
+		{ _id: 'set-owner', roles : ['admin', 'owner'] }
+		{ _id: 'view-c-room', roles : ['admin', 'user', 'bot'] }
+		{ _id: 'view-d-room', roles : ['admin', 'user'] }
+		{ _id: 'view-full-other-user-info', roles : ['admin'] }
+		{ _id: 'view-logs', roles : ['admin'] }
+		{ _id: 'view-other-user-channels', roles : ['admin'] }
+		{ _id: 'view-p-room', roles : ['admin', 'user'] }
+		{ _id: 'view-privileged-setting', roles : ['admin'] }
+		{ _id: 'view-room-administration', roles : ['admin'] }
+		{ _id: 'view-statistics', roles : ['admin'] }
+		{ _id: 'view-user-administration', roles : ['admin'] }
 	]

 	for permission in permissions


fix error with NaN because wrong delegation

--- app/assets/javascripts/invoices.js.coffee
+++ app/assets/javascripts/invoices.js.coffee
@@ -8,7 +8,7 @@
     $('.unpaid-worklogs').on 'click touchstart', 'a', (e) ->
       e.preventDefault()
       _that.totalInput().val($(e.currentTarget).data().total / 100)
-    $('.unpaid-expenses').on 'click touchstart', 'a', (e) ->
+    $('.unpaid-expenses').on 'click touchstart', '.add-total', (e) ->
       e.preventDefault()
       input = _that.totalInput()
       oldval = input.val()


removed some unused code

removed some unused code

--- app/assets/javascripts/preorder.js.coffee
+++ app/assets/javascripts/preorder.js.coffee
@@ -24,7 +24,6 @@
     $("#video_image").on "click", ->
       $("#player").removeClass("hidden")
       $(this).hide()
-      $("#player iframe").click()

     # if they are using the optional payment options section on the checkout page, need to conditional fix the email
     # field and button to the bottom of the page so it's displayed after selecting a radio button


Removes "that" bad trick in selector JS

Removes "that" bad trick in selector JS

--- app/assets/javascripts/selector.js.coffee
+++ app/assets/javascripts/selector.js.coffee
@@ -73,18 +73,17 @@
       if id is null or id is undefined or id is ""
         @initializing = false
         return @valueField.val()
-      that = this
       url = this.sourceURL()
       $.ajax
         url: url
         dataType: "json"
         data:
           id: id
-        success: (data, status, request) ->
+        success: (data, status, request) =>
           listItem = $.parseJSON(request.responseText)[0]
           if listItem?
-            that._select listItem.id, listItem.label, triggerEvents
-            that.element.prop("ready", true)
+            @_select listItem.id, listItem.label, triggerEvents
+            @element.prop("ready", true)
           else
             console.warn "JSON cannot be parsed. Get: #{request.responseText}."
         error: (request, status, error) ->
@@ -172,12 +171,11 @@
           parameters = {}
           if selected.data("new-item").length > 0
             parameters.name = selected.data("new-item")
-          that = this
           E.dialog.open @element.data("selector-new-item"),
             data: parameters
             returns:
-              success: (frame, data, status, request) ->
-                that._set(request.getResponseHeader("X-Saved-Record-Id"), true)
+              success: (frame, data, status, request) =>
+                @_set(request.getResponseHeader("X-Saved-Record-Id"), true)
                 frame.dialog "close"
               invalid: (frame, data, status, request) ->
                 frame.html request.responseText
@@ -233,9 +231,8 @@

     _focusOut: (event) ->
       # console.log "focusout"
-      that = this
-      setTimeout ->
-        that._closeMenu()
+      setTimeout =>
+        @_closeMenu()
       , 300
       true


ellipse scaling from onDrag

ellipse scaling from onDrag

--- app/classifier/drawing-tools/ellipse.cjsx
+++ app/classifier/drawing-tools/ellipse.cjsx
@@ -83,8 +83,9 @@
     </DrawingToolRoot>

   handleMainDrag: (e, d) ->
-    @props.mark.x += d.x / @props.scale.horizontal
-    @props.mark.y += d.y / @props.scale.vertical
+    difference = @props.normalizeDifference(e, d)
+    @props.mark.x += difference.x
+    @props.mark.y += difference.y
     @props.onChange @props.mark

   handleRadiusHandleDrag: (coord, e, d) ->


calendar: handle concluded checked courses

fixes CNVS-12384

test plan:
  * create two courses
  * open calendar 2 and show events from both
  * conclude one of your courses
  * open calendar 2
  * verify that events from the remaining course are displayed

Change-Id: Ia17b0e0391aed343af2c45dc62a4bc9284830259
Reviewed-on: https://gerrit.instructure.com/33262
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Joel Hough <4816dabf8db1bc6cac35b3a24cab2ff844b5b0c7@instructure.com>
QA-Review: Trevor deHaan <85df72112e8a9484fec7b3db2e31789c2c2d4e07@instructure.com>
Product-Review: Braden Anderson <a6cc2cef7d25f27847876bdb81651755fcebacf0@instructure.com>

--- app/coffeescripts/calendar/sidebar.coffee
+++ app/coffeescripts/calendar/sidebar.coffee
@@ -10,7 +10,7 @@
   'compiled/jquery.kylemenu'
   'jquery.instructure_misc_helpers'
   'vendor/jquery.ba-tinypubsub'
-], ($, {map}, userSettings, contextListTemplate, undatedEventsTemplate, commonEventFactory, EditEventDetailsDialog, EventDataSource) ->
+], ($, _, userSettings, contextListTemplate, undatedEventsTemplate, commonEventFactory, EditEventDetailsDialog, EventDataSource) ->

   class VisibleContextManager
     constructor: (contexts, selectedContexts, @$holder) ->
@@ -19,10 +19,14 @@
              catch e
                {}

+      availableContexts = (c.asset_string for c in contexts)
       @contexts   = fragmentData.show.split(',') if fragmentData.show
       @contexts or= selectedContexts
       @contexts or= userSettings.get('checked_calendar_codes')
-      @contexts or= (c.asset_string for c in contexts[0...10])
+      @contexts or= availableContexts
+
+      @contexts = _.intersection(@contexts, availableContexts)
+      @contexts = @contexts.slice(0, 10)

       @notify()

@@ -73,7 +77,7 @@
     $holder.on 'click keyclick', '.context_list_context', (event) ->
       visibleContexts.toggle $(this).data('context')
       userSettings.set('checked_calendar_codes',
-        map($(this).parent().children('.checked'), (c) -> $(c).data('context')))
+        _.map($(this).parent().children('.checked'), (c) -> $(c).data('context')))

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


Lookup package's repository URL on atom.io

Closes #29

--- src/develop.coffee
+++ src/develop.coffee
@@ -2,11 +2,11 @@
 path = require 'path'

 _ = require 'underscore-plus'
-npm = require 'npm'
-npmconf = require 'npmconf'
 optimist = require 'optimist'
+request = require 'request'
 require 'colors'

+auth = require './auth'
 config = require './config'
 Command = require './command'
 Link = require './link'
@@ -50,15 +50,28 @@
         callback(null, npm)

   getRepositoryUrl: (packageName, callback) ->
-    @loadNpm ->
-      npm.commands.view [packageName, 'repository'], true, (error, data={}) ->
+    auth.getToken (error, token) ->
+      if error?
+        callback(error)
+        return
+
+      requestSettings =
+        url: "#{config.getAtomPackagesUrl()}/#{packageName}"
+        json: true
+        headers:
+          authorization: token
+      request.get requestSettings, (error, response, body={}) ->
         if error?
-          callback(error)
+          callback("Request for package information failed: #{error.message}")
+        else if response.statusCode is 200
+          if repositoryUrl = body.repository.url
+            callback(null, repositoryUrl)
+          else
+            callback("No repository URL found for package: #{packageName}")
         else
-          if repoUrl = _.values(data)[0]?.repository?.url
-            callback(null, repoUrl)
-          else
-            callback("#{packageName} has no repository url")
+          message = body.message ? body.error ? body
+          callback("Request for package information failed: #{message}")
+

   cloneRepository: (repoUrl, packageDirectory, options) ->
     command = "git"


fix wrong path in usage text for apm develop

--- src/develop.coffee
+++ src/develop.coffee
@@ -26,7 +26,7 @@

       Clone the given package's Git repository to the directory specified,
       install its dependencies, and link it for development to
-      ~/.atom/packages/dev/<package_name>.
+      ~/.atom/dev/packages/<package_name>.

       If no directory is specified then the repository is cloned to
       ~/github/<package_name>. The default folder to clone packages into can


Fixed gh-pages task

Fixed gh-pages task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -66,7 +66,7 @@
         ].join(' && ')

       "gh-pages-push":
-        commands: [
+        command: [
           "cd gh-pages"
           "git add ."
           "git ci -am 'updating pages'"


Fix compress task

Fix compress task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -114,6 +114,7 @@
     #      key based assignment. See below.
     uglify: `undefined`
     cssmin: `undefined`
+    compress: readCoffee "build/compress.coffee"

     # JS config, mostly the requirejs configuration
     # full example: https://github.com/jrburke/r.js/blob/master/build/example.build.js
@@ -182,11 +183,9 @@
   grunt.loadNpmTasks "grunt-contrib-coffee"
   grunt.loadNpmTasks "grunt-contrib-uglify"
   grunt.loadNpmTasks "grunt-contrib-qunit"
+  grunt.loadNpmTasks "grunt-contrib-compress"

-
-  # Ease of use aliases for users who want the zip and docs
-  grunt.registerTask "docs", "js css legacy_tasks:docs"
-  grunt.registerTask "zip", "js css legacy_tasks:zip"
+  grunt.registerTask "zip", "js css compress:zip".split(' ')

   grunt.event.on 'qunit.spawn', (url) ->
     grunt.log.ok("Running test: " + url)


chore: changed test task to include coffee compile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -49,4 +49,4 @@
   grunt.loadNpmTasks 'grunt-browserify'

   grunt.registerTask 'default', ['coffee', 'uglify', 'jasmine']
-  grunt.registerTask 'test', ['jasmine']
+  grunt.registerTask 'test', ['coffee:spec', 'jasmine']


update

update

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -38,7 +38,7 @@
 		grunt.config.set('coffee.dist.dest', '<%= pkg.devDistDirectory %>/<%= pkg.name %>-latest.js')
 		grunt.config.set('uglify.dist.dest', '<%= pkg.devDistDirectory %>/<%= pkg.name %>-<%= pkg.version %>.js')
 	else
-		grunt.config.set('concat.dist.dest', '<%= pkg.distDirectory %>/<%= pkg.name %>-latest.js')
+		grunt.config.set('coffee.dist.dest', '<%= pkg.distDirectory %>/<%= pkg.name %>-latest.js')
 		grunt.config.set('uglify.dist.dest', '<%= pkg.distDirectory %>/<%= pkg.name %>-<%= pkg.version %>.js')

 	# Dependencies


Use meta.banner for minimized file banners.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -8,11 +8,9 @@

 		# create meta data
 		meta:
-			banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
-				'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
-				'<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
-				'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
-				' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
+			banner: '/*! <%= pkg.name %> <%= pkg.version %> - ' +
+				'<%= grunt.template.today("yyyy-mm-dd") %> - ' +
+				'Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> */'

 		# compile Stylus module files to CSS
 		stylus:
@@ -62,7 +60,7 @@
 		cssmin:
 			app:
 				options:
-					banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+					banner: '<%= meta.banner %>'
 				files:
 					'./htdocs/css/default.min.css': [
 						'./htdocs/css/default.css'
@@ -71,7 +69,7 @@
 		# minify the main JavaScript file
 		uglify:
 			options:
-				banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> */\n'
+				banner: '<%= meta.banner %>'
 			app:
 				files: {
 					'./htdocs/js/modernizr.min.js': [


Switched back to non-server testing for mocha.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -28,11 +28,11 @@

     mocha:
       test:
+        src: ['test/index.html']
         options:
-          urls: ["http://localhost:#{project.port}/test/"]
           reporter: 'Spec'
           run: true
-          timeout: 30000
+          timeout: 15000

     coffee:
       dist:
@@ -89,6 +89,5 @@
     ]
   grunt.registerTask "test", [
     'coffee'
-    'connect:server'
     'mocha'
   ]


in progress

in progress

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -26,17 +26,33 @@
 # TODO: Move this into common-grunt-rules in uproxy-lib.
 # We need *.ts files for typescript compilation
 # and *.html files for polymer vulcanize compilation
-Rule.symlink = (dir, dest='') =>
-  { files: [ {
-    expand: true,
-    overwrite: true,
-    cwd: dir,
-    src: ['**/*.ts', '**/*.html'],
-    dest: 'build/compile-src/' + dest} ] }
+Rule.symlink = (dir, dest='', exclude='') =>
+  if (exclude!='')
+    console.log(exclude);
+    { files: [ {
+        expand: true,
+        overwrite: true,
+        cwd: dir,
+        src: ['**/*.ts', '**/*.html', exclude],
+        dest: 'build/compile-src/' + dest} ] }
+  else
+    console.log('inner no exclude');
+    { files: [ {
+      expand: true,
+      overwrite: true,
+      cwd: dir,
+      src: ['**/*.ts', '**/*.html'],
+      dest: 'build/compile-src/' + dest} ] }

 # Use symlinkSrc with the name of the module, and it will automatically symlink
 # the path to its src/ directory.
-Rule.symlinkSrc = (module) => Rule.symlink Path.join(getNodePath(module), 'src')
+Rule.symlinkSrc = (module, exclude='') =>
+  if (exclude!='')
+    console.log(exclude);
+    Rule.symlink Path.join(getNodePath(module), 'src', exclude)
+  else
+    console.log('no exclude');
+    Rule.symlink Path.join(getNodePath(module), 'src')
 Rule.symlinkThirdParty = (module) =>
   Rule.symlink(Path.join(getNodePath(module), 'third_party'), 'third_party')

@@ -115,7 +131,7 @@
     symlink:
       # Symlink all module directories in `src` into compile-src, and
       # merge `third_party` from different places as well.
-      typescriptSrc: Rule.symlinkSrc '.'
+      typescriptSrc: Rule.symlinkSrc('.','!**/polymer/**')
       thirdPartyTypescriptSrc: Rule.symlinkThirdParty '.'

       uproxyNetworkingThirdPartyTypescriptSrc: Rule.symlinkThirdParty 'uproxy-networking'


chore(Gruntfile): Not allow interrupt on watch task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -257,8 +257,6 @@
     # Watch all js, css and jade changes
     #
     watch:
-      options:
-        interrupt: true
       js:
         files: ["src/**/*.coffee", "src/*.coffee"]
         tasks: [


added comment about project version

added comment about project version

--- services/web/app/coffee/models/Project.coffee
+++ services/web/app/coffee/models/Project.coffee
@@ -22,7 +22,7 @@
 	readOnly_refs     :   [ type:ObjectId, ref:'User' ]
 	rootDoc_id        :   {type: ObjectId}
 	rootFolder        :   [FolderSchema]
-	version           :   {type: Number}
+	version           :   {type: Number} # incremented for every change in the project structure (folders and filenames)
 	publicAccesLevel  :   {type: String, default: 'private'}
 	compiler          :   {type:String, default:'pdflatex'}
 	spellCheckLanguage :   {type:String, default:'en'}


refactoring.

refactoring.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -33,9 +33,10 @@
       jade: (path) -> 'jade:develop'

     coffee:
+      options:
+        bare: true
       production:
         options:
-          bare: true
           join: true
         files: [
           '<%= config.dist %>/scripts/script.js': [
@@ -45,8 +46,6 @@
           ]
         ]
       develop:
-        options:
-          bare: true
         files: [
           expand: true
           cwd: 'coffee/'
@@ -55,8 +54,6 @@
           ext: '.js'
         ]
       test:
-        options:
-          bare: true
         files: [
           expand: true
           cwd: 'test/'
@@ -86,8 +83,7 @@
     jade:
       production:
         options:
-          data: (dest, src) ->
-            return { production: true }
+          data: (dest, src) -> return { production: true }
         files: [
           expand: true
           cwd: 'jade/'
@@ -97,8 +93,7 @@
         ]
       develop:
         options:
-          data: (dest, src) ->
-            return { production: false }
+          data: (dest, src) -> return { production: false }
         files: [
           expand: true
           cwd: 'jade/'
@@ -164,19 +159,19 @@

   # tasks
-  grunt.registerTask "minify", ["ngmin", "uglify"]
+  grunt.registerTask 'minify', ['ngmin', 'uglify']

-  grunt.registerTask "dev", [
-    "bower:dev",
-    "coffee:develop",
-    "jade:develop",
-    "stylus:develop"]
+  grunt.registerTask 'dev', [
+    'bower:dev',
+    'coffee:develop',
+    'jade:develop',
+    'stylus:develop']

-  grunt.registerTask "production", [
-    "bower:install",
-    "chromeManifest:dist",
-    "coffee:production",
-    "jade:production",
-    "stylus:production",
-    "minify"]
+  grunt.registerTask 'production', [
+    'bower:production',
+    'chromeManifest:dist',
+    'coffee:production',
+    'jade:production',
+    'stylus:production',
+    'minify']


lowercase file extension when seeing preview

--- services/web/public/coffee/models/File.coffee
+++ services/web/public/coffee/models/File.coffee
@@ -7,7 +7,7 @@

 		previewUrl: () ->
-			extension = @get("name").split(".").pop()
+			extension = @get("name").split(".").pop()?.toLowerCase()
 			needsConverting = (["eps", "pdf"].indexOf(extension) != -1)
 			queryString = if needsConverting then "?format=png" else ""
 			url = "#{@downloadUrl()}#{queryString}"


Lint Gruntfile.coffee

Lint Gruntfile.coffee

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -17,6 +17,7 @@
         max_line_length:
           level: 'ignore'

+      gruntfile: ['Gruntfile.coffee']
       src: ['src/*.coffee']
       test: ['spec/*.coffee']

@@ -37,7 +38,7 @@
     grunt.file.delete('lib') if grunt.file.exists('lib')
     grunt.file.delete('gen') if grunt.file.exists('gen')

-  grunt.registerTask('lint', ['coffeelint:src', 'coffeelint:test'])
+  grunt.registerTask('lint', ['coffeelint'])
   grunt.registerTask('default', ['coffeelint', 'coffee'])
   grunt.registerTask('test', ['default', 'lint', 'shell:test'])
   grunt.registerTask('prepublish', ['clean', 'build-grammars', 'test'])


Make it possible to run tests selectively

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -31,6 +31,7 @@
         src: ['spec/*.coffee']
         options:
           reporter: 'spec'
+          grep: process.env.TESTS

     # Web server for the browser tests
     connect:


Remove temporary package directory during release task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -484,6 +484,8 @@
         run "zip -r #{ releaseDirName }-src.zip #{ pkg.name }"
         run "tar -Hzcf #{ releaseDirName }-src.tar.gz #{ pkg.name }"

+        run "rm -rf #{ pkg.name }"
+
     grunt.registerTask 'release-help', 'Prints the post-release steps', ->
         grunt.log.ok 'Push the code and tags: git push && git push --tags'
         grunt.log.ok "Go to #{ pkg.homepage }/releases to update the release descriptions and upload the binaries"


chore(grunt): Added copy example files task to e2e task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -93,6 +93,7 @@
     "coffee"
     "stylus"
     "jade"
+    "copy:example"
     "concatDeploy"
     "clean"
     "replace:src"


fix(Gruntfile): Fixed vendor.styl compile bug

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -203,7 +203,7 @@
         options: interrupt: true
       vendorCss:
         files: ["vendor/vendor.styl"]
-        tasks: ["stylus:vendor", "concat:vendorCss", "clean"]
+        tasks: ["stylus:vendor", "concat:css", "clean"]
         options: interrupt: true
       jade:
         files: ["src/**/*.jade"]


Remove now-absent concat task

Remove now-absent concat task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,7 +71,7 @@
         options:
           livereload: 35729
         files: ['test/*.js', 'src/*.js'],
-        tasks: ['jshint', 'concat']
+        tasks: ['jshint']

     # Web server
     connect:
@@ -113,13 +113,13 @@
       done(!error?)

   # Release meta-task
-  grunt.registerTask "release", ["jshint", "concat", "uglify", "docco", "git-tag", "s3"]
+  grunt.registerTask "release", ["jshint", "uglify", "docco", "git-tag", "s3"]

   # Run a webserver for testing
   grunt.registerTask "server", ["connect:server:keepalive"]

   # Run tests
-  grunt.registerTask "test", ["jshint", "concat", "connect:test", "watch:test"]
+  grunt.registerTask "test", ["jshint", "connect:test", "watch:test"]

   # Default meta-task
-  grunt.registerTask "default", ["jshint", "regex-replace", "uglify", "docco"]+  grunt.registerTask "default", ["jshint", "regex-replace", "uglify", "docco"]


Normalized: the way error message are sent

--- app/controllers/request_controller.coffee
+++ app/controllers/request_controller.coffee
@@ -82,7 +82,7 @@
     removeAllDocs = (res) ->
         async.forEachSeries res, removeFunc, (err) ->
             if err
-                send error: true, msg: err.message, 500
+                send error: err.message, 500
             else
                 delFunc()

@@ -112,12 +112,12 @@
             db.save "_design/#{params.type}", design_doc, (err, res) ->
                 if err
                     console.log "[Definition] err: " + JSON.stringify err
-                    send error: true, msg: err.message, 500
+                    send error: err.message, 500
                 else
                     send success: true, 200

         else if err
-            send error: true, msg: err.message, 500
+            send error: err.message, 500

         else
             views = res.views
@@ -126,7 +126,7 @@
                 db.merge "_design/#{params.type}", {views:views}, (err, res) ->
                     if err
                         console.log "[Definition] err: " + JSON.stringify err
-                        send error: true, msg: err.message, 500
+                        send error: err.message, 500
                     else
                         send success: true, 200

@@ -136,7 +136,7 @@
         if err and err.error is 'not_found'
             send error: "not found", 404
         else if err
-            send error: true, msg: err.message, 500
+            send error: err.message, 500
         else
             views = res.views
             request.get @appName, params, (path) =>
@@ -147,6 +147,6 @@
                     db.merge "_design/#{params.type}", {views:views}, (err, res) ->
                         if err
                             console.log "[Definition] err: " + JSON.stringify err
-                            send error: true, msg: err.message, 500
+                            send error: err.message, 500
                         else
                             send success: true, 204


Update weasel copy

Update weasel copy

Edited 10/29/2015

--- app/lib/field-guide-content/weasel.coffee
+++ app/lib/field-guide-content/weasel.coffee
@@ -1,6 +1,6 @@
 module.exports =
   description: '''
-    There are two weasel species in Gorongosa: striped weasel (Poecilogale albinucha) and zorilla (Ictonyx striatus). The striped weasel is a small weasel with a long, sinuous body, very short limbs, and a long tail. They have black fur with a yellowish-white patch on the top of their head that splits into white stripes that run the length of their back and join again at their tail, which is all yellowish-white.
+    Two weasel species live in Gorongosa: the striped weasel (Poecilogale albinucha) and the zorilla (Ictonyx striatus). The striped weasel is a small weasel with a long, sinuous body, very short limbs, and a long tail. They have black fur with a yellowish-white patch on the top of their head that splits into white stripes that run the length of their back and join again at their tail, which is all yellowish-white.

     A skunklike mustelid, the zorilla has long, shaggy, jet-black hair, with four wide white stripes that run from nape to tail. Their head is broad but blunt with an individual pattern of white spots between the eyes and a stripe on each cheek. Their tail and the back of their ears are also mostly white. Like skunks, the zorilla may emit a strong-smelling liquid from its anal glands when threatened. However, the smell is less pungent than that of a skunk.
   '''
@@ -23,17 +23,17 @@

   sections: [{
     title: 'Habitat'
-    content: 'Grasslands, woodlands, montane grasslands, and within agricultural areas.'
+    content: 'Grasslands, woodlands, montane grasslands, and within agricultural areas'
   }, {
     title: 'Diet'
-    content: 'Invertebrates, reptiles, birds, eggs, and small mammals.'
+    content: 'Invertebrates, reptiles, birds, eggs, and small mammals'
   }, {
     title: 'Predators'
     content: 'Larger African carnivores'
   }, {
     title: 'Behavior'
     content: '''
-
 Zorillas and striped weasels are nocturnal and solitary. They are most active after dark and then return to their burrows, which are often just a pile of stones, before dawn. Much of their time is spent searching for food by digging or sniffing out concealed insects, reptiles, and small mammals.

+
Zorillas and striped weasels are nocturnal and solitary. They are most active after dark and then return to their burrows, which are often just a pile of stones, before dawn. Much of their time is spent searching for food by digging or sniffing out concealed insects, reptiles, and small mammals.

     '''
   }, {
     title: 'Breeding'
@@ -46,7 +46,7 @@
     style: 'focus-box'
     content: '''

-
Like skunks, the zorilla and striped weasels emits a strong-smelling liquid from its anal glands when threatened.

+
Like skunks, zorillas and striped weasels emit a strong-smelling liquid from their anal glands when threatened.

When threatened, the zorilla will play dead until the threat has passed, sometimes for as long as half an hour.

The striped weasel is one of the smallest carnivores in Africa.

The striped weasel is regarded by some to be a good luck charm.


Added Individual.coffee

Added Individual.coffee

--- app/scripts/controllers/Individual.coffee
+++ app/scripts/controllers/Individual.coffee
@@ -13,7 +13,7 @@
       if not error
         for rep in data
           rep.fullname = "#{rep.title}. #{rep.first_name} #{rep.last_name}"
-          $scope.reps_names_list.push({name: rep.fullname})
+          $scope.reps_names_list.push({name: rep.fullname, bioguide_id: rep.bioguide_id})
           rep.chamber = rep.chamber.charAt(0).toUpperCase() + rep.chamber.slice(1)  # cap first letter
           rep.party_name = if rep.party is "D" then "Democrat" else if rep.party is "R" then "Republican" else rep.party
           $scope.reps[rep.bioguide_id] = {}
@@ -23,7 +23,7 @@

     $scope.get_all_reps_in_office()
   ])
-  .controller('ChartCtrl', ['$scope', '$http', 'ApiGet' ($scope, $http) ->
+  .controller('ChartCtrl', ['$scope', '$http', 'ApiGet', ($scope, $http) ->
     $scope.source = {}
     $scope.sources = []

@@ -37,8 +37,5 @@
         $scope.selected_rep.funding.contributors = data.json
         $scope.loaded.contributors = true
       else console.log "Error: ", error
-
-
-
-  # ])
+  ])
   #Add new controller to module hered


Fix route rendering test to compile on CS 1.1.3.

--- tests/batman/view/route_rendering_test.coffee
+++ tests/batman/view/route_rendering_test.coffee
@@ -13,9 +13,9 @@
     @App.stop()

 asyncTest 'should set href for URL fragment', 1, ->
-  @App.on 'run', ->
+  @App.on 'run', =>
     helpers.render 'click', {}, (node) =>
-      equal node.attr('href'), Batman.Navigator.defaultClass()::linkTo("/test")
+      equal node.attr('href'), Batman.navigator.linkTo("/test")
       QUnit.start()
   @App.run()


add ability to query if a feature impl exists

--- library/gearbox/exchange.coffee
+++ library/gearbox/exchange.coffee
@@ -103,6 +103,27 @@

     # Query all the services present in the established ecosystem
     # for the feature designated by the supplied string. In case if
+    # at least one implementation of the designated function found,
+    # the method will return a truthful value, indicating that this
+    # features is indeed implemented somewhere. In case if nothing
+    # is found, methods returns a false value to indicate sitation.
+    featureExists: external (designate) ->
+        noEco = "service ecosystem is missing of root"
+        unknown = "no feature designation is supplied"
+        empty = "feature #{designate} is not provided"
+        assert _.isArray(@root.ecosystem or 0), noEco
+        assert _.isString(designate or null), unknown
+        assert fp = (srv) -> _.isObject srv.$features
+        assert normal = try (designate.toLowerCase())
+        hits = (sx) -> try _.has sx.$features, normal
+        getAligningsOrd = (srv) -> srv.$aligning or 0
+        services = _.filter @root.ecosystem or [], fp
+        services = _.sortBy services, getAligningsOrd
+        filtered = _.filter services or Array(), hits
+        return if _.isEmpty(filtered) then no else yes
+
+    # Query all the services present in the established ecosystem
+    # for the feature designated by the supplied string. In case if
     # there is no services with this feature, the exception thrown.
     # Otherwise, pick the last (according to the aligning) matching
     # service, and invoke the `action` with the designated feature.