bugfix, added comments, removed logs

bugfix, added comments, removed logs

--- data/Triplets._coffee
+++ data/Triplets._coffee
@@ -287,55 +287,74 @@
     else
       userNodeId = 0

+    # Pattern that matches 1st degree resources.
+    # Orientation is saved for future developments, for instance, when orientation will be shown
+    # with arrows. Right now, the data is here but it is not used.
     outgoingRelation =  '(resource) -[:RELATED_TO]-> (connection) -[:RELATED_TO]-> (otherResource)'
     incomingRelation =  '(resource) <-[:RELATED_TO]- (connection) <-[:RELATED_TO]- (otherResource)'

+    # Function that produces a pattern that matches 2nd degree resources,
+    # if an oriented relation such as above 'outgoingRelation'/'incomingRelation' is given as parameter.
     make2dot0Relation = (direction) ->  "(originRs) -[:RELATED_TO]- () -[:RELATED_TO]- #{direction}"

+    # Patterns that match 2nd degree resources, with orientation.
     outgoing2dot0Relation = make2dot0Relation(outgoingRelation)
     incoming2dot0Relation = make2dot0Relation(incomingRelation)

-    #make2dot5Relation = (direction) ->  "(originRs) -[:RELATED_TO]- () -[:RELATED_TO]- () -[:RELATED_TO]- () -[:RELATED_TO]- #{direction} -[:RELATED_TO]- () -[:RELATED_TO]- () -[:RELATED_TO]- () -[:RELATED_TO]- (originRs)"
+    # Function that produces a pattern that matches connections between 2nd degree resources,
+    # if an oriented relation such as above 'outgoingRelation'/'incomingRelation' is given as parameter.
     make2dot5Relation = (direction) -> "originRs -[:RELATED_TO*4]- #{direction} -[:RELATED_TO*4]- originRs"

+    # Patterns that match relationships between 2nd degree resources, with orientation.
     incoming2dot5Relation = make2dot5Relation(incomingRelation)
     outgoing2dot5Relation = make2dot5Relation(outgoingRelation)

-    baseQuery = (origin, relation) ->"""
-                           START #{origin} = node({resourceNodeId}), user = node(#{userNodeId})
-                           MATCH #{relation},
-                           (#{origin}) -[:CREATED_BY]- (resourceCreator),
-                           (otherResource) -[:CREATED_BY]- (otherResourceCreator),
-                           (otherResource) -[?:RELATED_TO]- (otherResourceConnections),
-                           (connection) -[?:VOTED_UP]- (upvotes),
-                           (connection) -[?:VOTED_DOWN]- (downvotes),
-                           (connection) -[:CREATED_BY]- (connectionCreator),
-                           (user) -[hasVotedUp?:VOTED_UP]-> (connection),
-                           (user) -[hasVotedDown?:VOTED_DOWN]-> (connection),
-                           (connection) -[?:COMMENT_OF]- (comments)
-                           RETURN connection, connectionCreator, resource, resourceCreator, otherResource, otherResourceCreator,
-                           count(distinct comments) AS commentCount,
-                           count(distinct otherResourceConnections) AS otherResourceConnectionCount,
-                           count(distinct upvotes) AS upVoteCount,
-                           count(distinct downvotes) AS downVoteCount,
-                           count(distinct hasVotedUp) AS userVotedUp,
-                           count(distinct hasVotedDown) AS userVotedDown
-                           """
-
-    query1 = (relation) -> baseQuery("resource", relation)
-
-    query2 = (relation) -> baseQuery("originRs", relation)
-
-    console.log("query strings defined")
-
+    # Base Cypher query, to fetch needed information.
+    # parameter 'origin': the starting resource. Must take parameter 'relation' into account.
+    # parameter 'relation': the relationship pattern that will be matched. Must take parameter 'origin' into account.
+    # parameter 'limit': the maximum amount of results returned.
+    baseQuery = (origin, relation, limit) ->  """
+                                              START #{origin} = node({resourceNodeId}), user = node(#{userNodeId})
+                                              MATCH #{relation},
+                                              (resource) -[:CREATED_BY]- (resourceCreator),
+                                              (otherResource) -[:CREATED_BY]- (otherResourceCreator),
+                                              (otherResource) -[?:RELATED_TO]- (otherResourceConnections),
+                                              (connection) -[?:VOTED_UP]- (upvotes),
+                                              (connection) -[?:VOTED_DOWN]- (downvotes),
+                                              (connection) -[:CREATED_BY]- (connectionCreator),
+                                              (user) -[hasVotedUp?:VOTED_UP]-> (connection),
+                                              (user) -[hasVotedDown?:VOTED_DOWN]-> (connection),
+                                              (connection) -[?:COMMENT_OF]- (comments)
+                                              RETURN distinct connection, connectionCreator, resource, resourceCreator, otherResource, otherResourceCreator,
+                                              count(distinct comments) AS commentCount,
+                                              count(distinct otherResourceConnections) AS otherResourceConnectionCount,
+                                              count(distinct upvotes) AS upVoteCount,
+                                              count(distinct downvotes) AS downVoteCount,
+                                              count(distinct hasVotedUp) AS userVotedUp,
+                                              count(distinct hasVotedDown) AS userVotedDown
+                                              LIMIT #{limit}
+                                              """
+
+                                              #TODO
+                                              #ORDER BY userVotedUp or other relevance criterium
+
+    # Function that returns a Cypher query to get 1st degree resources.
+    query1 = (relation) -> baseQuery("resource", relation, "8")
+
+    # Function that returns a Cypher query to get 2nd degree resources or connections between 2nd degree resources.
+    query2 = (relation) -> baseQuery("originRs", relation, "4")
+
+    # 1st degree resources: graph 1.0
     outgoingQuery =      query1(outgoingRelation)
     incomingQuery =      query1(incomingRelation)
+
+    # 2nd degree resources: graph 2.0
     outgoing2dot0Query = query2(outgoing2dot0Relation)
     incoming2dot0Query = query2(incoming2dot0Relation)
+
+    # connections berween 2nd degree resources: graph 2.5
     outgoing2dot5Query = query2(outgoing2dot5Relation)
     incoming2dot5Query = query2(incoming2dot5Relation)
-
-    console.log("queries defined")

     resource = Resources.find(resourceId, _)
     params =
@@ -346,18 +365,13 @@
     incoming2dot0Results = GraphDB.get().query(incoming2dot0Query, params, _)
     outgoing2dot5Results = GraphDB.get().query(outgoing2dot5Query, params, _)
     incoming2dot5Results = GraphDB.get().query(incoming2dot5Query, params, _)
-    console.log("outgoing2dot5Results=")
-    console.log(outgoing2dot5Results)
-    console.log("incoming2dot5Results=")
-    console.log(incoming2dot5Results)
+
     resourceConnectionCount = outgoingResults.length
     + incomingResults.length      \
     + outgoing2dot0Results.length \
     + incoming2dot0Results.length \
     + outgoing2dot5Results.length \
     + incoming2dot5Results.length
-
-    console.log("params defined")

     triplets = []

@@ -373,8 +387,6 @@
       connection = new Connection(row.connection, row.connectionCreator)
       new Triplet(connection, startResource, endResource, data)

-    console.log("makeTriplet defined")
-
     pushOutgoingTriplets = (set) ->
       for row in set
         startResource = new Resource(row.resource, row.resourceCreator)
@@ -383,14 +395,9 @@
         endResourceConnectionCount = row.otherResourceConnectionCount
         triplets.push makeTriplet(row, startResource, endResource, startResourceConnectionCount, endResourceConnectionCount)

-    console.log("pushOutgoingTriplets defined")
-
     pushOutgoingTriplets(outgoingResults)
-    console.log("pushOutgoingTriplets(outgoingResults)")
     pushOutgoingTriplets(outgoing2dot0Results)
-    console.log("pushOutgoingTriplets(outgoing2dot0Results)")
     pushOutgoingTriplets(outgoing2dot5Results)
-    console.log("pushOutgoingTriplets(outgoing2dot5Results)")

     pushIncomingTriplets = (set) ->
       for row in set
@@ -400,13 +407,8 @@
         endResourceConnectionCount = resourceConnectionCount
         triplets.push makeTriplet(row, startResource, endResource, startResourceConnectionCount, endResourceConnectionCount)

-    console.log("pushIncomingTriplets defined")
-
     pushIncomingTriplets(incomingResults)
-    console.log("pushIncomingTriplets(incomingResults)")
     pushIncomingTriplets(incoming2dot0Results)
-    console.log("pushIncomingTriplets(incoming2dot0Results)")
     pushIncomingTriplets(incoming2dot5Results)
-    console.log("pushIncomingTriplets(incoming2dot5Results)")

     triplets


tidied up crawler path

tidied up crawler path

--- crawler.coffee
+++ crawler.coffee
@@ -13,8 +13,8 @@
 # `phantomjs crawler.coffee http://your-server/ output-folder/ page-timeout ignore-regex`.
 base = system.args[2]
 home = system.args[1]
+ignoreRegex = system.args[4]
 timeout = system.args[3] || 3000
-ignoreRegex = system.args[4]

 if ignoreRegex
   ignoreRegex = new RegExp system.args[4]
@@ -57,7 +57,7 @@

   if name
     # Creates a file path tree according to the named address of the document.
-    fs.makeTree name
+    fs.makeTree base + name

     # Checks for and adds a trailing slash if there isn't one there.
     if (name.charAt name.length - 1) isnt '/'


:bug: Null out TabsToSpaces reference on deactivation

Fixes #38

This change nulls out the TabsToSpaces reference on package deactivation
to hopefully force a reload of the TabsToSpaces code on package upgrade.

--- lib/index.coffee
+++ lib/index.coffee
@@ -31,6 +31,9 @@
     @commands.dispose()
     @editorObserver.dispose()

+    # Null out to theoretically force loading a new version on upgrade
+    TabsToSpaces = null
+
   # Private: Creates event handlers.
   #
   # * `editor` {TextEditor} to attach the event handlers to


Update viewer url to something more sane.

--- src/proto.coffee
+++ src/proto.coffee
@@ -10,7 +10,7 @@
 { htmlResponse }    = require './http_utils'
 VERSION             = require './version'

-VIEWER_URL  = 'http://tranquil-scrubland-4645.herokuapp.com/'
+VIEWER_URL  = 'http://proto-cli.herokuapp.com/'
 PROTO_FILES = ['script.coffee', 'markup.jade', 'style.styl', 'settings.json', 'notes.md']

 CWD         = process.cwd()


排除两文件名

排除两文件名

--- lib/project/new-project.coffee
+++ lib/project/new-project.coffee
@@ -101,7 +101,7 @@
     Util.readDir fp, (err,files) =>
       return console.error err if err
       files.forEach (file,i) =>
-        unless file is '.githolder'
+        unless file is '.githolder' or file is 'butterfly-slim'
           configPath = Path.join fp,file,desc.moduleConfigFileName
           Util.readJson configPath, (err,json) =>
             # return console.error err if err


Revert "teamwork: imrpove response handling in `TeamworkApp::doCurlRequest`"

This reverts commit 73b1d1805d3ddcf9774345a2b1865e48adca6e71.

--- client/Teamwork/Views/teamworkapp.coffee
+++ client/Teamwork/Views/teamworkapp.coffee
@@ -222,21 +222,22 @@
       vmName  : vmController.defaultVmName
     , (err, res) ->
       return warn err  if err
-      return warn res.stderr  if res and res.exitStatus > 0
-
+      return warn res.stderr if res.exitStatus > 0
+
+      contents  = res.stdout
       extension = FSItem.getFileExtension path
       error     = null

       switch extension
         when "json"
           try
-            manifest = JSON.parse res
+            manifest = JSON.parse contents
           catch err
             error    = "Manifest file is broken for #{path}"

           callback error, manifest
         when "md"
-          callback errorMessage, KD.utils.applyMarkdown error, res
+          callback errorMessage, KD.utils.applyMarkdown error, contents

   fetchManifestFile: (path, callback = noop) ->
     $.ajax


Welcome: app made full size

Welcome: app made full size

--- client/app/lib/mainviewcontroller.coffee
+++ client/app/lib/mainviewcontroller.coffee
@@ -87,7 +87,7 @@
     html     = global.document.documentElement
     mainView = @getView()

-    fullSizeApps    = [ 'Login', 'Pricing', 'Activity', 'Teams' ]
+    fullSizeApps    = [ 'Login', 'Pricing', 'Activity', 'Teams', 'Welcome' ]
     appsWithSidebar = [
       'Activity', 'Members', 'content-display', 'Apps', 'Dashboard', 'Account'
       'Environments', 'Bugs', 'Welcome'


Document the new quote command.

Document the new quote command.

--- src/quote.coffee
+++ src/quote.coffee
@@ -15,6 +15,7 @@
 #   hubot quotestats - Show who's been quoted the most!
 #   hubot verbatim quote: [...] - Enter a quote into the quote file exactly as given.
 #   hubot slackapp quote: [...] - Parse a quote from the Slack app's paste format.
+#   hubot buffer quote - Enter a quote from the buffer.
 #
 # Author:
 #   smashwilson


Move all helpers to the bottom of range.coffee

--- src/range.coffee
+++ src/range.coffee
@@ -8,21 +8,6 @@
 # https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
 ELEMENT_NODE = 1
 TEXT_NODE = 3
-
-
-insertAfter = (node, referenceNode) ->
-  parent = referenceNode.parentNode
-  next = referenceNode.nextSibling
-  if next then return parent.insertBefore(node, next)
-  return parent.appendChild(node)
-
-
-# https://github.com/Raynos/DOM-shim/issues/11
-splitText = (node, offset) ->
-  tail = node.cloneNode(false)
-  tail.deleteData(0, offset)
-  node.deleteData(offset, node.length - offset)
-  return insertAfter(tail, node)

 # Public: Creates a wrapper around a range object obtained from a DOMSelection.
@@ -349,6 +334,23 @@
       end: @end
       endOffset: @endOffset
     }
+
+
+# Insert a Node as the successor sibling of a reference Node.
+insertAfter = (node, referenceNode) ->
+  parent = referenceNode.parentNode
+  next = referenceNode.nextSibling
+  if next then return parent.insertBefore(node, next)
+  return parent.appendChild(node)
+
+
+# Split a TextNode at an offset, returning the successor.
+# https://github.com/Raynos/DOM-shim/issues/11
+splitText = (node, offset) ->
+  tail = node.cloneNode(false)
+  tail.deleteData(0, offset)
+  node.deleteData(offset, node.length - offset)
+  return insertAfter(tail, node)

 # Get all the text Nodes within a Node.


Don't auto-hide modal on button click

Don't auto-hide modal on button click

--- client/lanes/components/modal/Modal.cjsx
+++ client/lanes/components/modal/Modal.cjsx
@@ -30,7 +30,6 @@
             @props.onOk?(this)
         else
             @props.onCancel?(this)
-        @_hide()

     componentWillReceiveProps: (nextProps) ->
         @setState(show: nextProps.show) if nextProps.show?
@@ -38,6 +37,10 @@
     _hide: ->
         @context.viewport.modalProps.show = false
         @setState(show: false)
+
+    show: ->
+        @context.viewport.modalProps.show = true
+        @setState(show: true)

     hide: ->
         @_hide()


Remove observables from Build.status

Remove observables from Build.status

--- js/inner.coffee
+++ js/inner.coffee
@@ -100,14 +100,14 @@
   constructor: (json) ->
     # make the actionlogs observable
     json.action_logs = (new ActionLog(j) for j in json.action_logs) if json.action_logs
-    super json, {}, ["build_num"]
+    super json, {}, ["build_num", "status"]

     @url = @komp =>
       "#{@project_path()}/#{@build_num}"

     @style = @komp =>
-      klass = switch @status()
+      klass = switch @status
         when "failed"
           "important"
         when "infrastructure_fail"
@@ -133,7 +133,7 @@
       return result

-    @status_words = @komp => switch @status()
+    @status_words = @komp => switch @status
       when "infrastructure_fail"
         "circle bug"
       when "timedout"
@@ -141,7 +141,7 @@
       when "no_tests"
         "no tests"
       else
-        @status()
+        @status

     @committer_mailto = @komp =>
       if @committer_email()


removed http

removed http

--- src/robot.coffee
+++ src/robot.coffee
@@ -424,12 +424,20 @@
   #   app = express()

   #   app.use (req, res, next) =>
+  #     if req.headers['x-forwarded-proto'] != 'https' and env == 'production'
+  #       return res.redirect(301, [
+  #         'https://'
+  #         req.get('Host')
+  #         req.url
+  #       ].join(''))
   #     res.setHeader "X-Powered-By", "hubot/#{@name}"
   #     next()

   #   app.use express.basicAuth user, pass if user and pass
   #   app.use express.query()

+  #   ## set the view engine to ejs
+  #   app.set('view engine', 'ejs')

   #   app.use(bodyParser.urlencoded({
   #       extended: true


Remove unused require

Remove unused require

--- src/robot.coffee
+++ src/robot.coffee
@@ -8,8 +8,6 @@
 Response                                                = require './response'
 {Listener,TextListener}                                 = require './listener'
 {TextMessage,EnterMessage,LeaveMessage,CatchAllMessage} = require './message'
-
-inspect = require('util').inspect

 HUBOT_DEFAULT_ADAPTERS = [
   'campfire',


fix typo

fix typo

--- src/robot.coffee
+++ src/robot.coffee
@@ -197,7 +197,7 @@

     if hostname
       setInterval =>
-        httpclient.create("#{hostname}/hubot/ping")
+        HttpClient.create("#{hostname}/hubot/ping")
           .post() (err, res, body) =>
             @logger.info "keep alive ping!"
       , 1200000


Include apostrophe in word regex

Include apostrophe in word regex

--- lib/spell-check-handler.coffee
+++ lib/spell-check-handler.coffee
@@ -1,6 +1,6 @@
 module.exports =
   findMisspellings: (text) ->
-    wordRegex = /(?:^|\s)([a-zA-Z]+)(?=\s|\.|$)/g
+    wordRegex = /(?:^|\s)([a-zA-Z']+)(?=\s|\.|$)/g
     row = 0
     misspellings = []
     for line in text.split('\n')


KDView: setData: use KDData.getEmitter instead

--- lib/core/view.coffee
+++ lib/core/view.coffee
@@ -1,7 +1,8 @@
 debug = require('debug') 'kd:view'
 $ = require 'jquery'
 KD = require './kd'
-KDObject        = require './object'
+KDObject = require './object'
+KDData = require './data'
 MutationSummary = require 'kd-shim-mutation-summary'
 Pistachio = require './pistachio'

@@ -181,21 +182,21 @@

   setData: (data) ->

+    # Check if data is set on the view and has an emitter
+    # turn it off to prevent duplicate handlers
     if @data?
-      if @data.off
-        @data.off 'update', @bound 'render'
-      else if @data.__proxy__
-        @data.__proxy__.off? 'update', @bound 'render'
+      if emitter = KDData.getEmitter @data
+        emitter.off 'update', @bound 'render'

     super data

     if @data?
-      if @data.on
-        @data.on 'update', @bound 'render'
-      # If the data is `KDData` which has a __proxy__ field for
-      # providing events on updated fields, we need to listen that one ~ GG
-      else if @data.__proxy__
-        @data.__proxy__.on? 'update', @bound 'render'
+      # If the data is extended from KDEventEmitter or KDData
+      # KDData.getEmitter will return the proper emitter for
+      # update events which we can re-render the view by
+      # listening update events of ~ GG
+      if emitter = KDData.getEmitter @data
+        emitter.on 'update', @bound 'render'

     @render()  if @parentIsInDom


core/view: use debug to log lazyElement found warning

--- lib/core/view.coffee
+++ lib/core/view.coffee
@@ -1,3 +1,4 @@
+debug = require('debug') 'kd:view'
 $ = require 'jquery'
 KD = require './kd'
 KDObject        = require './object'
@@ -170,7 +171,7 @@
     @domElement = $ el

     if @lazy
-      # warn "lazyElement found with id #{domId}"
+      debug "lazyElement found with id #{domId}"
       @utils.defer => @emit 'viewAppended'

   setDomId:(id)->
@@ -427,7 +428,6 @@
       if shouldPrepend
       then @prepend subView, selector
       else @append subView, selector
-    # else log "lazy view", subView

     subView.on "ViewResized", -> subView.parentDidResize()


Fix NRE bug in `latex.sync`

Fix NRE bug in `latex.sync`

Fixes #53

--- lib/latex.coffee
+++ lib/latex.coffee
@@ -101,7 +101,7 @@

     {filePath, lineNumber} = @getEditorDetails()
     opener = @getOpener()
-    opener.open(@pdfFile, filePath, lineNumber)
+    opener?.open(@pdfFile, filePath, lineNumber)

   isTexFile: (filePath) ->
     # TODO: Improve; will suffice for the time being.


added run and clear button

added run and clear button

--- lib/swift-debugger-view.coffee
+++ lib/swift-debugger-view.coffee
@@ -10,10 +10,14 @@
       @subview 'commandEntryView', new TextEditorView
         mini: true,
         placeholderText: 'po foo'
-       @button outlet: 'runBtn', click: 'runApp', class: 'btn', =>
-         @span 'run'
-       @button outlet: 'cleatBtn', click: 'clearOutput', class: 'btn', =>
-         @span 'clear'
+      @button outlet: 'runBtn', click: 'runApp', class: 'btn', =>
+        @span 'run'
+      @button outlet: 'cleatBtn', click: 'clearOutput', class: 'btn', =>
+        @span 'clear'
+      @button outlet: 'stepOver', click: 'stepOverBtnPressed', class: 'btn', =>
+        @span 'next line'
+      @button outlet: 'resume', click: 'resumeBtnPressed', class: 'btn', =>
+        @span 'resume'
       @div class: 'panel-body', outlet: 'outputContainer', =>
         @pre class: 'command-output', outlet: 'output'


msgflo-setup: Setup participants before queues

--- src/setup.coffee
+++ src/setup.coffee
@@ -331,18 +331,6 @@
     program.help()
     process.exit()

-  setupBindings options, (err, bindings) ->
-    throw err if err
-    console.log 'Set up bindings:\n', pretty bindings
-
-    if options.forever
-      console.log '--forever enabled, keeping alive'
-      setInterval () ->
-        null # just keep alive
-      , 1000
-    else
-        process.exit 0
-
   maybeSetupParticipants = (options, callback) ->
     return callback null, {}
   maybeSetupParticipants = setupParticipants if options.participants
@@ -350,3 +338,15 @@
     throw err if err
     console.log 'Set up participants', Object.keys(p)

+    setupBindings options, (err, bindings) ->
+      throw err if err
+      console.log 'Set up bindings:\n', pretty bindings
+
+      if options.forever
+        console.log '--forever enabled, keeping alive'
+        setInterval () ->
+          null # just keep alive
+        , 1000
+      else
+          process.exit 0
+


Add missing `updateStatus` method on FileView elements

--- lib/file-view.coffee
+++ lib/file-view.coffee
@@ -33,6 +33,7 @@
     @element.isPathEqual = @isPathEqual.bind(this)
     @element.file = @file
     @element.fileName = @fileName
+    @element.updateStatus = @updateStatus.bind(this)

   updateStatus: ->
     @element.classList.remove('status-ignored', 'status-modified',  'status-added')


Fixed crash when client sends request with no Host header.

--- lib/proxy.coffee
+++ lib/proxy.coffee
@@ -153,7 +153,7 @@
       { pathname, search } = URL.parse(req.url)
       url = URL.format(
         protocol: "https:"
-        hostname:  req.headers.host.split(":")[0]
+        hostname:  (req.headers.host || "").split(":")[0]
         port:      port
         pathname:  pathname
         search:    search


Changed constant style.

Changed constant style.

--- lib/saml2.coffee
+++ lib/saml2.coffee
@@ -10,12 +10,12 @@
 xmlenc        = require 'xml-encryption'
 zlib          = require 'zlib'

-xmlns =
-  saml: 'urn:oasis:names:tc:SAML:2.0:assertion'
-  samlp: 'urn:oasis:names:tc:SAML:2.0:protocol'
-  md: 'urn:oasis:names:tc:SAML:2.0:metadata'
-  ds: 'http://www.w3.org/2000/09/xmldsig#'
-  xenc: 'http://www.w3.org/2001/04/xmlenc#'
+XMLNS =
+  SAML: 'urn:oasis:names:tc:SAML:2.0:assertion'
+  SAMLP: 'urn:oasis:names:tc:SAML:2.0:protocol'
+  MD: 'urn:oasis:names:tc:SAML:2.0:metadata'
+  DS: 'http://www.w3.org/2000/09/xmldsig#'
+  XENC: 'http://www.w3.org/2001/04/xmlenc#'

 # Creates an AuthnRequest and returns it as a string of xml along with the randomly generated ID for the created
 # request.
@@ -23,8 +23,8 @@
   id = '_' + crypto.randomBytes(21).toString('hex')
   xml = xmlbuilder.create
     AuthnRequest:
-      '@xmlns': xmlns.samlp
-      '@xmlns:saml': xmlns.saml
+      '@xmlns': XMLNS.SAMLP
+      '@xmlns:saml': XMLNS.SAML
       '@Version': '2.0'
       '@ID': id
       '@IssueInstant': (new Date()).toISOString()
@@ -42,8 +42,8 @@
 create_metadata = (issuer, assert_endpoint, signing_certificate, encryption_certificate) ->
   xmlbuilder.create
     'md:EntityDescriptor':
-      '@xmlns:md': xmlns.md
-      '@xmlns:ds': xmlns.ds
+      '@xmlns:md': XMLNS.MD
+      '@xmlns:ds': XMLNS.DS
       '@entityID': issuer
       'md:SPSSODescriptor': [
           '@protocolSupportEnumeration': 'urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol',
@@ -60,8 +60,8 @@
 create_logout_request = (issuer, name_id, session_index, destination) ->
   xmlbuilder.create
     'samlp:LogoutRequest':
-      '@xmlns:samlp': xmlns.samlp
-      '@xmlns:saml': xmlns.saml
+      '@xmlns:samlp': XMLNS.SAMLP
+      '@xmlns:saml': XMLNS.SAML
       '@ID': '_' + crypto.randomBytes(21).toString('hex')
       '@Version': '2.0'
       '@IssueInstant': (new Date()).toISOString()
@@ -99,7 +99,7 @@
   {
     '@use': use
     'ds:KeyInfo':
-      '@xmlns:ds': xmlns.ds
+      '@xmlns:ds': XMLNS.DS
       'ds:X509Data':
         'ds:X509Certificate':
           cert_data.replace(/[\r\n|\n]/g, '')
@@ -121,7 +121,7 @@

 # Takes in an xml @dom containing a SAML Status and returns true if at least one status is Success.
 check_status_success = (dom) ->
-  status = dom.getElementsByTagNameNS(xmlns.samlp, 'Status')
+  status = dom.getElementsByTagNameNS(XMLNS.SAMLP, 'Status')
   return false unless status.length is 1
   for status_code in status[0].childNodes
     if status_code.attributes?
@@ -142,10 +142,10 @@
   cb = _.wrap cb, (fn, err, args...) -> setTimeout (-> fn to_error(err), args...), 0

   try
-    encrypted_assertion = dom.getElementsByTagNameNS(xmlns.saml, 'EncryptedAssertion')
+    encrypted_assertion = dom.getElementsByTagNameNS(XMLNS.SAML, 'EncryptedAssertion')
     return cb new Error("Expected 1 EncryptedAssertion; found #{encrypted_assertion.length}.") unless encrypted_assertion.length is 1

-    encrypted_data = encrypted_assertion[0].getElementsByTagNameNS(xmlns.xenc, 'EncryptedData')
+    encrypted_data = encrypted_assertion[0].getElementsByTagNameNS(XMLNS.XENC, 'EncryptedData')
     return cb new Error("Expected 1 EncryptedData inside EncryptedAssertion; found #{encrypted_data.length}.") unless encrypted_data.length is 1

     xmlenc.decrypt encrypted_data[0].toString(), (key: format_pem(private_key, 'PRIVATE KEY')), cb
@@ -157,7 +157,7 @@
 # appear to be valid.
 parse_response_header = (dom) ->
   for response_type in ['Response', 'LogoutResponse']
-    response = dom.getElementsByTagNameNS(xmlns.samlp, response_type)
+    response = dom.getElementsByTagNameNS(XMLNS.SAMLP, response_type)
     break if response.length > 0
   throw new Error("Expected 1 Response; found #{response.length}") unless response.length is 1

@@ -175,13 +175,13 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns the NameID. If there is no NameID found,
 # it will return null. It will throw an error if the Assertion is missing or does not appear to be valid.
 get_name_id = (dom) ->
-  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
+  assertion = dom.getElementsByTagNameNS(XMLNS.SAML, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  subject = assertion[0].getElementsByTagNameNS(xmlns.saml, 'Subject')
+  subject = assertion[0].getElementsByTagNameNS(XMLNS.SAML, 'Subject')
   throw new Error("Expected 1 Subject; found #{subject.length}") unless subject.length is 1

-  nameid = subject[0].getElementsByTagNameNS(xmlns.saml, 'NameID')
+  nameid = subject[0].getElementsByTagNameNS(XMLNS.SAML, 'NameID')
   return null unless nameid.length is 1

   nameid[0].firstChild?.data
@@ -189,10 +189,10 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns the SessionIndex. It will throw an error
 # if there is no SessionIndex, no Assertion, or the Assertion does not appear to be valid.
 get_session_index = (dom) ->
-  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
+  assertion = dom.getElementsByTagNameNS(XMLNS.SAML, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  authn_statement = assertion[0].getElementsByTagNameNS(xmlns.saml, 'AuthnStatement')
+  authn_statement = assertion[0].getElementsByTagNameNS(XMLNS.SAML, 'AuthnStatement')
   throw new Error("Expected 1 AuthnStatement; found #{authn_statement.length}") unless authn_statement.length is 1

   for attr in authn_statement[0].attributes
@@ -204,19 +204,19 @@
 # Takes in an xml @dom of an object containing a SAML Assertion and returns and object containing the attributes
 # contained within the Assertion. It will throw an error if the Assertion is missing or does not appear to be valid.
 parse_assertion_attributes = (dom) ->
-  assertion = dom.getElementsByTagNameNS(xmlns.saml, 'Assertion')
+  assertion = dom.getElementsByTagNameNS(XMLNS.SAML, 'Assertion')
   throw new Error("Expected 1 Assertion; found #{assertion.length}") unless assertion.length is 1

-  attribute_statement = assertion[0].getElementsByTagNameNS(xmlns.saml, 'AttributeStatement')
+  attribute_statement = assertion[0].getElementsByTagNameNS(XMLNS.SAML, 'AttributeStatement')
   throw new Error("Expected 1 AttributeStatement inside Assertion; found #{attribute_statement.length}") unless attribute_statement.length is 1

   assertion_attributes = {}
-  for attribute in attribute_statement[0].getElementsByTagNameNS(xmlns.saml, 'Attribute')
+  for attribute in attribute_statement[0].getElementsByTagNameNS(XMLNS.SAML, 'Attribute')
     for attr in attribute.attributes
       if attr.name is 'Name'
         attribute_name = attr.value
     throw new Error("Invalid attribute without name") unless attribute_name?
-    assertion_attributes[attribute_name] = _(attribute.getElementsByTagNameNS(xmlns.saml, 'AttributeValue')).map (attribute_value) -> attribute_value.childNodes[0].data
+    assertion_attributes[attribute_name] = _(attribute.getElementsByTagNameNS(XMLNS.SAML, 'AttributeValue')).map (attribute_value) -> attribute_value.childNodes[0].data
   assertion_attributes

 # Takes in an object containing SAML Assertion Attributes and returns an object with certain common attributes changed
@@ -318,10 +318,10 @@
           response = { response_header }
           cb_wf new Error("SAML Response was not success!") unless check_status_success(saml_response)
           switch
-            when saml_response.getElementsByTagNameNS(xmlns.samlp, 'Response').length is 1
+            when saml_response.getElementsByTagNameNS(XMLNS.SAMLP, 'Response').length is 1
               response.type = 'authn_response'
               parse_authn_response saml_response, @private_key, identity_provider.certificate, cb_wf
-            when saml_response.getElementsByTagNameNS(xmlns.samlp, 'LogoutResponse').length is 1
+            when saml_response.getElementsByTagNameNS(XMLNS.SAMLP, 'LogoutResponse').length is 1
               response.type = 'logout_response'
               setImmediate cb_wf, null, {}
         (result, cb_wf) ->


Add EM2 support to the screenplay

Add EM2 support to the screenplay

--- library/membrane/visual.coffee
+++ library/membrane/visual.coffee
@@ -128,11 +128,14 @@
         prepared = JSON.stringify context
         runtime = "(#{coffee}).apply(this)"
         installer = "var context = #{prepared}"
+        em = -> _.extend @, EventEmitter2.prototype
+        applicator = "(#{em}).apply(context)"
         _.forIn this, (value, key, object) ->
             return unless _.isObject value.remote
             return unless src = value.remote.source
             set = "context.%s = (#{src})()"
             installer += "\r\n#{format set, key}\r\n"
+        context.sources.unshift applicator
         context.sources.unshift installer
         context.sources.unshift runtime
         return context


a little fix in the composition sys

a little fix in the composition sys

--- library/nucleus/compose.coffee
+++ library/nucleus/compose.coffee
@@ -180,7 +180,7 @@
             assert tails = alternative.pop().rebased compound
             rebased = (acc, cls) -> cls.rebased acc; cls
             @rebased _.foldr alternative, rebased, tails
-            return this.refactoring compound or null
+            @refactoring compound; @implanted? compound

     # An important complementary part of the dynamic recomposition
     # system. The refactoring procedure is a recursive algorithm that


minor change in the condition

minor change in the condition

--- library/nucleus/restful.coffee
+++ library/nucleus/restful.coffee
@@ -74,9 +74,10 @@
     # accept or decline. Do this by calling `decide` with a boolean!
     # Especially useful for service with the same resource but with
     # different conditions, such as mobile only and desktop only.
-    @condition: (synopsis, limitation) ->
+    @condition: (xsynopsis, xlimitation) ->
         return try @conditions if arguments.length is 0
-        limitation = try _.find arguments, _.isFunction
+        synopsis = _.find(arguments, _.isString) or null
+        limitation = try _.find(arguments, _.isFunction)
         generic = "service condition: #{limitation.name}"
         try synopsis = generic unless _.isString synopsis
         noLimitation = "a limitation has to be function"


Fix minor mistake in the scoping

Fix minor mistake in the scoping

--- library/nucleus/scoping.coffee
+++ library/nucleus/scoping.coffee
@@ -51,12 +51,12 @@
     # and a synopsis (short description of the scope) parameters.
     # The constructor of the scope should only associate the data.
     # The scope startup logic should be implemented in the method.
-    constructor: (@tag, @synopsis) ->
-        assert _.isString(@tag), "invalid tag supplied"
-        assert _.isString(@synopsis), "invalid synopsis"
+    constructor: (@tag, synopsis) ->
+        assert _.isString(@tag), "invalid tag"
+        @synopsis = synopsis if _.isString synopsis
         @directory = @constructor.DIRECTORY or __dirname
         initializer = _.find arguments, _.isFunction
-        initializer?.call this, @tag, @synopsis
+        initializer?.call this, @tag, synopsis

     # Push the current scope instance into the global registry
     # of scopes, unless this instance already exists there. This


migration: pass options by reference

migration: pass options by reference

--- lib/migration.coffee
+++ lib/migration.coffee
@@ -33,7 +33,7 @@
           unless ctx.config[key]
             options = null
             break
-          options = merge {}, ctx.config[key]
+          options = ctx.config[key]
         else
           unless options[key]
             options = null
@@ -43,7 +43,7 @@
         ip: ctx.config.ip
         hostname: ctx.config.shortname
         fqdn: ctx.config.host
-      options: merge {}, nikita_options, options
+      options: merge options, nikita_options, options
     # console.log '>>', srv_ctxs
     if use_srv.local
       srv_ctxs = srv_ctxs[0]
@@ -58,13 +58,13 @@
       unless @config[key]
         options = null
         continue
-      options = merge {}, @config[key]
+      options = @config[key]
     else
       unless options[key]
         options = null
         continue
       options = options[key]
-  options = merge {}, nikita_options, options
+  options = merge options, nikita_options, options
   use: use
     # ssl: @contexts('masson/core/ssl').filter( (ctx) => ctx.config.host is @config.host ).map (ctx) -> options: ctx.config.ssh
     # mariadb: @contexts('masson/commons/mariadb/server').map (ctx) -> options: ctx.config.mariadb.server


Fix energy for old characters. Closes #541.

--- lib/migration.coffee
+++ lib/migration.coffee
@@ -323,6 +323,21 @@
 		['characters', 'addCol', 'gender uonline_gender']
 		['characters', 'rawsql', "UPDATE characters SET race='orc', gender='male' WHERE player IS NOT NULL"]
 	]
+	[
+		# Fix energy for old characters
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 220, energy_max = 220 WHERE race = 'orc' AND gender = 'male'"]
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 200, energy_max = 200 WHERE race = 'orc' AND gender = 'female'"]
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 170, energy_max = 170 WHERE race = 'human' AND gender = 'male'"]
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 160, energy_max = 160 WHERE race = 'human' AND gender = 'female'"]
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 150, energy_max = 150 WHERE race = 'elf' AND gender = 'male'"]
+		['characters', 'rawsql',
+			"UPDATE characters SET energy = 140, energy_max = 140 WHERE race = 'elf' AND gender = 'female'"]
+	]
 ]

 exports.getMigrationsData = ->


Make client conform to id-style references in the backend API.

--- src/views.coffee
+++ src/views.coffee
@@ -40,7 +40,7 @@
                 # clear_markers()
                 # if division_layer
                 #     map.removeLayer division_layer
-                self.draw_units data.objects
+                self.draw_units data.results
         draw_units: (unit_list) ->
             # todo: refactor into a model/collection
             # 100 = ei tiedossa, 101 = kunnallinen, 102 = kunnan tukema, 103 = kuntayhtymä,
@@ -68,9 +68,7 @@
             for unit in unit_list
                 color = ptype_to_color[unit.provider_type]
                 icon = null
-                for srv_url in unit.services
-                    arr = srv_url.split '/'
-                    id = parseInt arr[arr.length-2], 10
+                for id in unit.services
                     srv = tree_by_id[id]
                     if not srv?
                         continue
@@ -150,12 +148,8 @@
                 back = null
             else
                 if @collection.chosen_service
-                    parent_service_url = @collection.chosen_service.attributes.parent
                     heading = @collection.chosen_service.attributes.name.fi
-                    if parent_service_url
-                        back = parent_service_url.replace('/v1/service/', '').replace('/', '')
-                    else
-                        back = 'root'
+                    back = @collection.chosen_service.attributes.parent or 'root'
                 else
                     back = null
             s = _.template container_template,


Use generateUrl

Use generateUrl

--- BackofficeBundle/Resources/public/coffee/adminFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/adminFormView.coffee
@@ -47,10 +47,8 @@
               html: response
             )
             if $('#node_nodeId', viewContext.$el).length > 0
-              nodeId = $('#node_nodeId', viewContext.$el).val()
-              route = Backbone.history.fragment.split('/')
-              route[2] = nodeId
-              displayRoute = route.toString().replace(/,/g, '/')
+              displayRoute = appRouter.generateUrl "showNode",
+                nodeId: $('#node_nodeId', viewContext.$el).val()
             else
               displayRoute = Backbone.history.fragment
               Backbone.history.loadUrl(displayRoute)


Add exports key'

Add exports key'

--- src/world.coffee
+++ src/world.coffee
@@ -9,6 +9,7 @@
 LABEL = "_LABEL"
 CHILDREN = "_CHILDREN"
 AUTHORITY = "_AUTHORITY"
+EXPORTS = "_EXPORTS"
 INDEX = "_INDEX"
 CSS = "_CSS"

@@ -70,6 +71,8 @@
     assert _.isFunction(closure), "#{key}: #{closure} is not a function"
     closure(@, args)

+  export_events: ->
+    @
   # TODO: refactor import_dict methods somewhere
   _import_children: (children) ->
     result = @rx().array()
@@ -82,10 +85,11 @@

   import_dict: (dict) ->
     for key, value of dict
-      value = @_from_dict(value) if (key == AUTHORITY)
+      value = @_from_dict(value) if key == AUTHORITY
       value = @_import_children(value) if key == CHILDREN
       value = @rx().array(value) if _.isArray(value)
       @put(key, value)
+    @export_events()
     this

   _spawn_world: (label) ->


adds tools app to setup

adds tools app to setup

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -104,7 +104,8 @@
       'announcements',
       'shortcuts_id',
       'avatar_image',
-      'registered'
+      'registered',
+      'post_address'
     ]

   app.use artsyError.helpers
@@ -116,6 +117,7 @@
   app.use require "../apps/root"
   app.use require "../apps/registration"
   app.use require "../apps/getting_started"
+  app.use require "../apps/tools"
   app.use require "../apps/auth"
   app.use require "../apps/about"
   app.use require "../apps/search"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -7,6 +7,8 @@

 test "shouldn't remove object inside activeBounds", ->
   obj = GameObject
+    x: 50
+    y: 50

 test "should remove objects outside of activeBounds", 2, ->


Fixes in argumentsSpec.coffee

Fixes in argumentsSpec.coffee

--- test/argumentsSpec.coffee
+++ test/argumentsSpec.coffee
@@ -28,7 +28,7 @@
           fn = ->
           wrapper = subject(fn)

-        it 'should return arguments list with empty description and function', ->
+        it 'should return arguments list with empty description and existing function', ->
           expect(wrapper.it()).toEqual [' ', fn]

       describe 'first - string, second - function', ->
@@ -46,7 +46,7 @@
           descr = 'some text'
           wrapper = subject(fn, descr)

-        it 'should return arguments list with empty description and function', ->
+        it 'should return arguments list with empty description and existing function', ->
           expect(wrapper.it()).toEqual [' ', fn]

       describe 'first - function, second - function', ->


redux: rewrite project_log

redux: rewrite project_log

--- src/smc-webapp/project_log.cjsx
+++ src/smc-webapp/project_log.cjsx
@@ -24,7 +24,7 @@
 underscore = require('underscore')
 immutable  = require('immutable')

-{React, ReactDOM, Actions, Store, Table, rtypes, rclass, FluxComponent}  = require('./r')
+{React, ReactDOM, Actions, Store, Table, rtypes, rclass, Redux}  = require('./smc-react')
 {Col, Row, Button, ButtonGroup, ButtonToolbar, Input, Panel, Well} = require('react-bootstrap')
 {Icon, Loading, TimeAgo, FileLink, r_join, Space, Tip} = require('./r_misc')
 {User} = require('./users')
@@ -50,7 +50,7 @@

     clear_and_focus_input : ->
         @refs.project_log_search.getInputDOMNode().focus()
-        @props.actions.setTo(search:'', page:0)
+        @props.actions.setState(search:'', page:0)

     render_clear_button : ->
         <Button onClick={@clear_and_focus_input}>
@@ -72,12 +72,12 @@

     keydown : (e) ->
         if e.keyCode == 27
-            @props.actions.setTo(search:'', page:0)
+            @props.actions.setState(search:'', page:0)

     on_change : (e) ->
         e.preventDefault()
         x = @refs.project_log_search.getValue()
-        @props.actions.setTo(search : x, page : 0)
+        @props.actions.setState(search : x, page : 0)

     render : ->
         <form onSubmit={@do_open_selected}>
@@ -310,16 +310,20 @@
             return false
     return true

-ProjectLog = rclass
+ProjectLog = (name) -> rclass
     displayName : 'ProjectLog'

+    reduxProps :
+        "#{name}" :
+            project_log : rtypes.immutable
+            search      : rtypes.string
+            page        : rtypes.number
+        users :
+            user_map    : rtypes.immutable
+
     propTypes :
-        project_log : rtypes.object
-        user_map    : rtypes.object
-        search      : rtypes.string
-        page        : rtypes.number
-        actions     : rtypes.object.isRequired
-        flux        : rtypes.object
+        actions : rtypes.object.isRequired
+        redux   : rtypes.object

     getDefaultProps : ->
         search : ''   # search that user has requested
@@ -344,10 +348,10 @@

     previous_page : ->
         if @props.page > 0
-            @props.actions.setTo(page: @props.page-1)
+            @props.actions.setState(page: @props.page-1)

     next_page : ->
-        @props.actions.setTo(page: @props.page+1)
+        @props.actions.setState(page: @props.page+1)

     process_log_entry : (x, users) ->
         x.search = search_string(x, users)
@@ -358,7 +362,7 @@
             return

         if not immutable.is(next_user_map, @_last_user_map) and @_log?
-            users = @props.flux.getStore('users')
+            users = @props.redux.getStore('users')
             # Update any names that changed in the existing log
             next_user_map.map (val, account_id) =>
                 if not immutable.is(val, @_last_user_map?.get(account_id))
@@ -383,7 +387,7 @@
                         v.push(x)
                 new_log = v
             # process new log entries (search/name info)
-            users = @props.flux.getStore('users')
+            users = @props.redux.getStore('users')
             new_log = (@process_log_entry(x, users) for x in new_log)

             # combine logs
@@ -422,7 +426,7 @@
         </ButtonGroup>

     render_log_panel : ->
-        if not @props.flux
+        if not @props.redux
             return <Loading/>
         # get visible log
         log = @visible_log()
@@ -462,19 +466,20 @@
     render : ->

<Icon name='history' /> Project activity log

-            {if @props.flux and @props.project_log then @render_log_panel() else <Loading/>}
+            {if @props.redux and @props.project_log then @render_log_panel() else <Loading/>}

-render = (project_id, flux) ->
-    store = flux.getProjectStore(project_id)
-    actions = flux.getProjectActions(project_id)
-    <FluxComponent flux={flux} connectToStores={[store.name, 'users']}>
-        <ProjectLog actions={actions} />
-    </FluxComponent>
-
-exports.render_log = (project_id, dom_node, flux) ->
+render = (project_id, redux) ->
+    store   = redux.getProjectStore(project_id)
+    actions = redux.getProjectActions(project_id)
+    C = ProjectLog(store.name)
+    <Redux redux={redux} connectToStores={[store.name, 'users']}>
+        <C actions={actions} redux={redux} />
+    </Redux>
+
+exports.render_log = (project_id, dom_node, redux) ->
     #console.log("mount project_log")
-    ReactDOM.render(render(project_id, flux), dom_node)
+    ReactDOM.render(render(project_id, redux), dom_node)

 exports.unmount = (dom_node) ->
     #console.log("unmount project_log")


Tooltips can now override the default options

--- src/space-pen-extensions.coffee
+++ src/space-pen-extensions.coffee
@@ -22,6 +22,7 @@
   container: 'body'
   html: true
   placement: 'auto top'
+  viewportPadding: 2

 modifiers =
   cmd: '⌘'
@@ -60,16 +61,20 @@
     "#{replaceModifiers(bindings[0].keystroke)}"
   else
     ''
+# options from http://getbootstrap.com/javascript/#tooltips
+jQuery.fn.setTooltip = (tooltipOptions, {command, commandElement}={}) ->
+  atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery})

-jQuery.fn.setTooltip = (title, {command, commandElement}={}) ->
-  atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery})
+  tooltipOptions = {title: tooltipOptions} if _.isString(tooltipOptions)

   bindings = if commandElement
     atom.keymap.keyBindingsForCommandMatchingElement(command, commandElement)
   else
     atom.keymap.keyBindingsForCommand(command)

-  this.tooltip(jQuery.extend(tooltipDefaults, {title: "#{title} #{getKeystroke(bindings)}"}))
+  tooltipOptions.title = "#{tooltipOptions.title} #{getKeystroke(bindings)}"
+
+  this.tooltip(jQuery.extend(tooltipDefaults, tooltipOptions))

 jQuery.fn.setTooltip.getKeystroke = getKeystroke
 jQuery.fn.setTooltip.replaceModifiers = replaceModifiers


fix spec

fix spec

--- test/config-loader.coffee
+++ test/config-loader.coffee
@@ -1,9 +1,7 @@
 describe 'configLoader', ->
   it 'returns an instance of applying the long option', ->
     config = configLoader(__dirname + '/fixtures/migrations.js', 'development')
-    current = config.getCurrent()
-
-    assert.ok current.settings.host == 'localhost'
-    assert.ok current.settings.user == 'root'
-    assert.ok current.settings.password == 'password'
-    assert.ok current.settings.database == 'test'
+    assert.ok config.host == 'localhost'
+    assert.ok config.user == 'root'
+    assert.ok config.password == 'password'
+    assert.ok config.database == 'test'


fix test

fix test

--- test/crashpad.test.coffee
+++ test/crashpad.test.coffee
@@ -58,7 +58,7 @@
       expect(@response.body).to.deep.equal
         statusCode: 400
         error: 'Bad Request'
-        message: 'you messed up, yo: you messed up, yo'
+        message: 'you messed up, yo'

   describe 'unauthorized requests', ->


Web/insert_stops.coffee: remove console.log calls

--- Web/insert_stops.coffee
+++ Web/insert_stops.coffee
@@ -4,14 +4,10 @@
 preferred_stops = []

 $.getJSON('preferred_stops.json').done((data) ->
-      console.log("read preferred_stops.json")
       $(document).ready(data.map (stop) -> preferred_stops.push(stop))
   ).done(

     $.getJSON 'bus_info.json', (bus_info) ->
-      console.log("read bus_info.json")
-      console.log("preferred stops: #{preferred_stops}")
-
       buses = (bus for bus of bus_info).sort()
       for bus in buses
         dir = (dir for dir of bus_info[bus].stops)


add after block to tests

add after block to tests

--- desktop/apps/editorial_features/test/routes.coffee
+++ desktop/apps/editorial_features/test/routes.coffee
@@ -46,26 +46,28 @@
 describe 'Venice route', ->

   beforeEach ->
+    sinon.stub Backbone, 'sync'
+    Backbone.sync.yieldsTo 'success', { name: 'Inside the Biennale' }
     @res = { render: sinon.stub(), locals: { sd: {} }, redirect: sinon.stub() }
     @next = sinon.stub()
     routes.__set__ 'sd', {EF_VENICE: '123'}

+  afterEach ->
+    Backbone.sync.restore()
+
   it 'sets a video index', ->
-    Backbone.sync = sinon.stub().yieldsTo 'success', { name: 'Inside the Biennale' }
     @req = { params: { id: '3' } }
     routes.venice(@req, @res, @next)
     @res.render.args[0][0].should.equal 'components/venice_2017/templates/index'
     @res.render.args[0][1].videoIndex.should.equal 3

   it 'defaults to the first video', ->
-    Backbone.sync = sinon.stub().yieldsTo 'success', { name: 'Inside the Biennale' }
     @req = { params: { id: 'blah' } }
     routes.venice(@req, @res, @next)
     @res.render.args[0][0].should.equal 'components/venice_2017/templates/index'
     @res.render.args[0][1].videoIndex.should.equal 1

   it 'sets a curation', ->
-    Backbone.sync = sinon.stub().yieldsTo 'success', { name: 'Inside the Biennale' }
     @req = { params: { id: '3' } }
     routes.venice(@req, @res, @next)
     @res.render.args[0][0].should.equal 'components/venice_2017/templates/index'


FIX: Teaching Period get method

FIX: Teaching Period get method

--- src/app/api/models/teaching-period.coffee
+++ src/app/api/models/teaching-period.coffee
@@ -35,14 +35,15 @@
       resource.get(
         {id: id}
         (success) ->
-          match = _.find data.loadedPeriods.id == success.id
-
-          if match?
-            _.extend match, success
-            onSuccess match
-          else
-            data.loadedPeriods << success
-            onSuccess success
+          indexOfTeachingPeriods = 0
+          for teachingperiod in data.loadedPeriods
+            if teachingperiod.id == success.id
+              onSuccess data.loadedPeriods[indexOfTeachingPeriods]
+            else
+              data.loadedPeriods << success
+              onSuccess success
+
+            indexOfTeachingPeriods++
         (error) ->
           onFailure error
       )


fix indentation

fix indentation

--- test/graphite.test.coffee
+++ test/graphite.test.coffee
@@ -1,4 +1,4 @@
- expect = require 'expect.js'
+expect = require 'expect.js'
 graphite = require '../app/graphite'

 describe 'graphite', ->


Add a JS hook for has_many_remove:after

Add a JS hook for has_many_remove:after

--- app/assets/javascripts/active_admin/lib/has_many.js.coffee
+++ app/assets/javascripts/active_admin/lib/has_many.js.coffee
@@ -13,6 +13,7 @@

     parent.trigger 'has_many_remove:before', [ to_remove ]
     to_remove.remove()
+    parent.trigger 'has_many_remove:after', [ to_remove ]

   # Provides before and after creation hooks:
   # $ ->


Corrige arredondamento em números negativos

--- app/assets/javascripts/angular_app/filters/round.js.coffee
+++ app/assets/javascripts/angular_app/filters/round.js.coffee
@@ -1,8 +1,9 @@
 angular.module('InescApp').filter 'round', ->
   (value, sufix='') ->
-    decimalCases = if value > 100
+    absValue = Math.abs(value)
+    decimalCases = if absValue > 100
                      0
-                   else if value > 10
+                   else if absValue > 10
                      1
                    else
                      2


Generating trees

Generating trees

--- app/assets/javascripts/controllers/game_animator.js.coffee
+++ app/assets/javascripts/controllers/game_animator.js.coffee
@@ -2,7 +2,6 @@

 window.GameAnimator = class extends Animator
   movementAnimationSpeed: 300
-

   # loops: # [StartFrame, EndFrame, Speed]
     # object: {frames: [0,  3], speed: 0.2}
@@ -31,12 +30,11 @@
     @background_layer.addChild(@background_sprite)

     @snowman_sprite = @.createSnowmanSprite()
-    @lumberjack_sprite = @.createLumberjackSprite()
-    @rabbit_sprite = @.createRabbitSprite()
+
+    for i in [0..70]
+      @object_layer.addChild(@.createTreeSprite())

     @object_layer.addChild(@snowman_sprite)
-    @object_layer.addChild(@lumberjack_sprite)
-    @object_layer.addChild(@rabbit_sprite)

     @sprites_added = true
@@ -124,6 +122,16 @@
     sprite.source = object
     sprite

+  createTreeSprite: ->
+    sprite = PIXI.Sprite.fromFrame("tree.png")
+    sprite.position.x = @.objectToSceneX(_.random(0, canvasSize.width))
+    sprite.position.y = @.objectToSceneY(_.random(0, canvasSize.height))
+    sprite.anchor.x = 0.5
+    sprite.anchor.y = 1
+    sprite.scale.y = _.random(0, 10) * 0.03 + 0.7
+    sprite.scale.x = sprite.scale.y * _.shuffle([-1, 1])[0]
+    sprite
+
   objectToSceneX: (coordinate)->
     coordinate # Offset logic goes here


Fix arrows

Fix arrows

--- static/scripts/main.coffee
+++ static/scripts/main.coffee
@@ -142,7 +142,7 @@
         messages: []

     connectionsToPeerIds: (connections) ->
-        _.map connections, (connection) => connection.peer
+        _.map connections, (connection) -> connection.peer

     componentWillMount: ->
         who = getSegments()[1]
@@ -151,7 +151,7 @@

         @connections = []

-        peer.on "error", (error) =>
+        peer.on "error", (error) ->
             alert error.type

         peer.on "open", (peerId) =>
@@ -178,7 +178,7 @@

                 connection = peer.connect "x"

-                connection.on "error", (error) =>
+                connection.on "error", (error) ->
                     alert error

                 connection.on "open", =>
@@ -199,7 +199,7 @@

                                 do (connection) =>

-                                    connection.on "error", (error) =>
+                                    connection.on "error", (error) ->
                                         alert error.type

                                     connection.on "open", =>


Pass an option hash so that the tooltip for entry buttons is attached to the page body.

This removes a bug in some browsers (at least Chrome) in which the buttons moved a bit to the left on mouseover.

See discussions about this issue:

https://github.com/twbs/bootstrap/issues/6811
https://github.com/twbs/bootstrap/pull/6378

The bootstrap docs, by the way, describe the "container" option for the "tooltip" function, but they don't really explain what it does nor that it fixes this issue.

--- app/assets/javascripts/ng-services/ng-TooltipSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-TooltipSvc.js.coffee
@@ -14,7 +14,7 @@
   entry_tooltips: (entry)->
     # Do not enable tooltips in smartphone and tablet-sized screens
     enquire.register md_min_media_query, ->
-      $("#entry-#{entry.id} a[data-toggle='tooltip']").tooltip()
+      $("#entry-#{entry.id} a[data-toggle='tooltip']").tooltip container: 'body'

   #---------------------------------------------
   # Enable tooltips on navbar buttons


#53 修改报表显示

#53 修改报表显示

--- app/assets/javascripts/stat_graph_read_analytics.js.coffee
+++ app/assets/javascripts/stat_graph_read_analytics.js.coffee
@@ -1,7 +1,7 @@
 class window.ReadAnalyticsStatGraph
   init: (log) ->
     if $("#read-analytics").length
-      Morris.Area
+      Morris.Line
         element: "read-analytics"
         data: JSON.parse(log)
         xkey: "period"


hide navbar dropdown after click on mobile

--- app/assets/javascripts/states/main/views/nav_bar.js.coffee
+++ app/assets/javascripts/states/main/views/nav_bar.js.coffee
@@ -15,6 +15,12 @@
       "a#profile": [ "profile" ]

     events:
+      "click a": (e) ->
+        # hide the navbar-toggle on click (only visible on xs or sm screens)
+        e = $(e.target)
+        if !(e.hasClass("dropdown-toggle") or e.parent().hasClass("dropdown-toggle"))
+          @$("button.navbar-toggle:visible").click()
+
       "click a#logout": (e) ->
         v = @
         $.ajax("/users/sign_out", {


Actually used Rubular to figure out the regex this time.

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -64,7 +64,7 @@
     content = content.replace(/(\[REDACTED\])/ig,"[REDACTED]")

     # Italic
-    content = content.replace(/(\A|\s)\/([^\/]+)\/(\z|\s)/ig,"$2")
+    content = content.replace(/(()|(\s))[\/]{1}([^\/\\n]+)[\/]{1}/ig," $4 ")

     elem = $("

")


Added while & return tests

Added while & return tests

--- test/spec/Evaluator-spec.coffee
+++ test/spec/Evaluator-spec.coffee
@@ -87,6 +87,24 @@
 				}
 				sum
 			'''
+			'''
+				function f() {
+					while (true) {
+						return 123
+					}
+				}
+				f()
+			'''
+			'''
+				function f() {
+					while (true) {
+						while (true) {
+							return 123
+						}
+					}
+				}
+				f()
+			'''
 		]], ['recursion', [
 			'''
 				function sum(n) {


:fire: no longer used method

:fire: no longer used method

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -44,6 +44,9 @@

   getProp: (name) ->
     this[name] if this["__#{name}"]?
+
+  hasProp: (name) ->
+    "__#{name}" of this

   @defineLazyProperty('swrap', './selection-wrapper', false)
   @defineLazyProperty('utils', './utils', false)


removes extraneous newline

removes extraneous newline

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -167,7 +167,6 @@
     for commandName, operationFn of operationCommands
       do (operationFn) =>
         commands[commandName] = (event) => @pushOperations(operationFn(event))
-
     @registerCommands(commands)

   # Private: Push the given operations onto the operation stack, then process


<tests> Sensor - Adding one small condition to be sure...

--- tests/controllers/sensor.coffee
+++ tests/controllers/sensor.coffee
@@ -41,6 +41,7 @@
         it 'should reply with the created Sensor', ->
             expect(@err).to.not.exist
             expect(@response.statusCode).to.equal 201
+            expect(@body).to.exist
             expect(@body.customId).to.equal sensor.customId
             expect(@body.name).to.equal sensor.name
             expect(@body.driverId).to.equal sensor.driverId
@@ -177,13 +178,12 @@

     describe 'When we delete a Sensor (DELETE /sensors/:id) and its Driver doesn\'t allow it', ->

-        sensor = fixturesSensor.supportedSensor1
-
         before (done) ->
             # We modify our test-driver so that it returns errors when asked to remove a device:
             store["driverModule"].setRemovableFlag false

             # We add the device to try on:
+            sensor = fixturesSensor.supportedSensor1
             sensor.driverId = store.driver.id
             helpers.createSensor(sensor) () ->
                 store.sensorId = helpers.getInStore('sensor').id


Document toJSON unit tests added

Document toJSON unit tests added

--- tests/src/document/utils.coffee
+++ tests/src/document/utils.coffee
@@ -17,7 +17,8 @@
             loaded = true
             view = Document.fromHTML viewUid, html
             Document.parse view
-            view
+            json = JSON.stringify view
+            Document.fromJSON json
     else
         cache = require VIEWS_CACHE_FILE
         if utils.isClient and modules?


- Track page views in snowplow.

- Track page views in snowplow.

--- app/core/Tracker.coffee
+++ app/core/Tracker.coffee
@@ -111,7 +111,7 @@
     # https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
     ga? 'send', 'pageview', url
     ga?('codeplay.send', 'pageview', url) if features.codePlay
-
+    window.snowplow 'trackPageView'
     # Mixpanel
     mixpanel?.track('page viewed', 'page name' : name, url : url) if includeMixpanel(name)


Renamed pluck to map to be consistent with lodash.

--- test/unit/restSpec.coffee
+++ test/unit/restSpec.coffee
@@ -12,11 +12,11 @@
         actual = REST.where(input)
         expect(actual, "The numeric condition is incorrect").to.deep.equal(expected)

-    describe 'pluck', ->
+    describe 'map', ->
       it 'should project the fields', ->
         input = ['a', 'b']
         expected = projection: '{"a": 1,"b": 1}'
-        actual = REST.pluck(input)
+        actual = REST.map(input)
         expect(actual, "The string condition is incorrect").to.deep.equal(expected)

     describe 'omit', ->


Don't expose 'projRoot' or store on 'axiom.core'

--- lib/core.coffee
+++ lib/core.coffee
@@ -40,24 +40,32 @@
   config:
     blacklist: []
     timeout: 2000
-    projectRoot: undefined

   # a place to record what responders we have attached
   responders: {}

   init: (config, modules) ->
-    core.projectRoot = util.findProjRoot()

     requireModule = (moduleName) ->
       # Get a project directory-relative path for the axiom module's
       # corresponding NPM module.
       npmName = "axiom-#{moduleName}"
       npmModuleName = util.projRel path.join('node_modules', npmName)
-      require npmModuleName
+      return require npmModuleName

     core.reset()
     modules or= []
+
+    # Attempt to load a global 'Axiom.*' file from the project root
+    try
+      axiomFile = require util.projRel 'Axiom'
+      _.merge core.config, axiomFile
+    catch err
+      throw err unless err.code is 'MODULE_NOT_FOUND'
+
+    # Merge in any programatically-passed config object
     _.merge core.config, config
+
     core.modules = _.union core.modules, modules

     # Require and load axiom-base
@@ -81,15 +89,16 @@
   load: (moduleName, module) ->
     {config} = module
     config or= {}
-    services = law.create {
-      services: module.services
-      # We create a custom resolver for 'lib' dependencies which, within
-      # a Law service, loads an NPM module relative to the project root.
-      resolvers:
-        lib: (name) ->
-          requireName = path.join util.projRel('node_modules'), name
-          return require(requireName)
-    }
+    # Merge config overrides from '$projectRoot/axiom/<moduleName>'
+    overrideConfigPath = path.join util.projRel('axiom'), moduleName
+    try
+      overrideConfig = require overrideConfigPath
+      _.merge config, overrideConfig
+    catch err
+      throw err unless err.code is 'MODULE_NOT_FOUND'
+
+    # Initialize the services using a project-relative 'lib' resolver
+    services = law.create {services: module.services}

     contexts = {}


Don't render particles that are to far offscreen

(increases performance)

--- src/components/utils/WaterSplashes.coffee
+++ src/components/utils/WaterSplashes.coffee
@@ -109,10 +109,24 @@
           pos = Math.random()
           duration = (@minSplashDuration + (vy * 4) + (pos * 100)) * 3
           factor = 210 / @minSplashDuration
+
+          particleX = @x + (i * coverage) + (pos * coverage) - (@waterRadius * 2)
+          particleY = sealevel + @minOffset - (@waterRadius * 2)
+
+          # Prevent particles to spawn to far out of screen.
+          OFFSET = 100
+          if (
+            particleX + OFFSET < Crafty.viewport.x ||
+            particleX - OFFSET > Crafty.viewport.x + Crafty.viewport.width ||
+            particleY + OFFSET < Crafty.viewport.y ||
+            particleY - OFFSET > Crafty.viewport.y + Crafty.viewport.height
+          )
+            continue
+
           splashPool.get()
             .attr(
-              x: @x + (i * coverage) + (pos * coverage) - (@waterRadius * 2)
-              y: sealevel + @minOffset - (@waterRadius * 2)
+              x: particleX
+              y: particleY
               z: @z + 3
               w: @waterRadius * 4
               h: @waterRadius * 4
@@ -133,4 +147,5 @@
             .particle(
               duration: duration
             )
+
     @_lastWaterY = @y


fix bug

fix bug

--- lib/main.coffee
+++ lib/main.coffee
@@ -48,7 +48,7 @@
           atom.notifications.addError("**language-innosetup**: no valid `ISCC.exe` was specified in your config", dismissable: false)
           return

-        exec "\"ISCC\" \"#{script}\"", (error, stdout, stderr) ->
+        exec "\"#{isccBin}\" \"#{script}\"", (error, stdout, stderr) ->
           if error isnt null
             # isccBin error from stdout, not error!
             atom.notifications.addError("**#{script}**", detail: error, dismissable: true)


local not developed yet

local not developed yet

--- lib/main.coffee
+++ lib/main.coffee
@@ -75,7 +75,7 @@
       else
         createDyndocViewer(filePath: pathname)

-    rendererDyndoc = rendererDyndocLocal
+    rendererDyndoc = rendererDyndocRemote

   coffee: ->
     selection = atom.workspace.getActiveEditor().getSelection()


:racehorse: Start task on next tick to not impact startup

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,6 +5,8 @@
       default: []

   activate: (state) ->
+    @active = true
+
     atom.commands.add 'atom-workspace',
       'fuzzy-finder:toggle-file-finder': =>
         @createProjectView().toggle()
@@ -13,7 +15,8 @@
       'fuzzy-finder:toggle-git-status-finder': =>
         @createGitStatusView().toggle()

-    @startLoadPathsTask() if atom.project.getPaths().length > 0
+    if atom.project.getPaths().length > 0
+      process.nextTick => @startLoadPathsTask()

     for editor in atom.workspace.getTextEditors()
       editor.lastOpened = state[editor.getPath()]
@@ -33,6 +36,7 @@
       @gitStatusView = null
     @projectPaths = null
     @stopLoadPathsTask()
+    @active = false

   serialize: ->
     paths = {}
@@ -64,6 +68,7 @@
   startLoadPathsTask: ->
     @stopLoadPathsTask()

+    return unless @active
     PathLoader = require './path-loader'
     @loadPathsTask = PathLoader.startTask (@projectPaths) =>


Switch to config schema

Switch to config schema

--- lib/main.coffee
+++ lib/main.coffee
@@ -2,10 +2,16 @@
 TabBarView = require './tab-bar-view'

 module.exports =
-  configDefaults:
-    showIcons: true
-    tabScrolling: process.platform is 'linux'
-    tabScrollingThreshold: 120
+  config:
+    showIcons:
+      type: 'boolean'
+      default: true
+    tabScrolling:
+      type: 'boolean'
+      default: process.platform is 'linux'
+    tabScrollingThreshold:
+      type: 'integer'
+      default: 120

   activate: ->
     @tabBarViews = []


Restore old on DOM technique

Restore old on DOM technique

--- src/space-pen.coffee
+++ src/space-pen.coffee
@@ -194,7 +194,7 @@
 # Trigger attach event when views are added to the DOM
 callAttachHook = (element) ->
   return unless element instanceof jQuery and element[0]
-  onDom = element.isOnDom?()
+  onDom = element.parents?('html').length > 0

   elementsWithHooks = []
   if element[0].getAttribute('callAttachHooks')


adapt to matrix api changes

adapt to matrix api changes

--- src/space/rgb.coffee
+++ src/space/rgb.coffee
@@ -44,11 +44,11 @@
   ]
   # calculate LU decomposition of xyz base
   bxyzLU = lu bxyz
-  w = M [ @white.X, @white.Y, @white.Z ], 1
+  w = M 3, [ @white.X, @white.Y, @white.Z ]
   # get the needed scales or the columns of bxyz (sum of the columns of the base must be the white point)
   bxyzLU.solve w, w # calculate in place
   # scale bxyz to get the wanted XYZ base (sum of columns is white point)
-  bxyz = bxyz.mult M.diag w
+  bxyz = bxyz.mult M.diag w.array
   @base = bxyz.array
   @baseInv = (lu bxyz).getInverse().array