coffee bug fixed

coffee bug fixed

--- app/assets/javascripts/question_bank/questions.coffee
+++ app/assets/javascripts/question_bank/questions.coffee
@@ -1,93 +1,99 @@
 jQuery(document).on "ready page:load", ->
   jQuery(document).on 'click', '.form-question-single-choice .add-choice', ->
-    $choice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')
-    next_choice_answer_index = $choice_answer_indexs.find('.radio:not(.hidden)').length
-    dom = $choice_answer_indexs.find('.radio:first').clone()
+    jQuerychoice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')
+    next_choice_answer_index = jQuerychoice_answer_indexs.find('.radio:not(.hidden)').length
+    dom = jQuerychoice_answer_indexs.find('.radio:first').clone()
     dom.removeClass('hidden')
     dom.find('input[name="question[choices][]"]').val("")
     dom.find('input[name="question[choice_answer_indexs][]"]').val(next_choice_answer_index)
     jQuery(this).before(dom)

   jQuery(document).on 'click', '.form-question-single-choice .delete-choice', ->
-    $choice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')
+    jQuerychoice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')

-    $delete_radio = jQuery(this).closest('.radio')
-    delete_index = parseInt($delete_radio.find('input:first').val())
-    last_index   = $choice_answer_indexs.find('.radio:not(.hidden)').length - 1
+    jQuerydelete_radio = jQuery(this).closest('.radio')
+    delete_index = parseInt(jQuerydelete_radio.find('input:first').val())
+    last_index   = jQuerychoice_answer_indexs.find('.radio:not(.hidden)').length - 1
     for index in [delete_index..last_index]
-      $radio = $choice_answer_indexs.find('.radio:not(.hidden)').eq(index)
-      $radio.find('input:first').val(index - 1)
+      jQueryradio = jQuerychoice_answer_indexs.find('.radio:not(.hidden)').eq(index)
+      jQueryradio.find('input:first').val(index - 1)

-    $delete_radio.remove()
+    jQuerydelete_radio.remove()

   jQuery(document).on 'submit', '.form-question-single-choice form', (evt)->
-    $choice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')
-    $choice_answer_indexs.find('.radio.hidden').remove()
+    jQuerychoice_answer_indexs = jQuery('.form-question-single-choice .question_choice_answer_indexs')
+    jQuerychoice_answer_indexs.find('.radio.hidden').remove()

   jQuery(document).on 'click', '.form-question-multi-choice .add-choice', ->
-    $choice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')
-    next_choice_answer_index = $choice_answer_indexs.find('.checkbox:not(.hidden)').length
-    dom = $choice_answer_indexs.find('.checkbox:first').clone()
+    jQuerychoice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')
+    next_choice_answer_index = jQuerychoice_answer_indexs.find('.checkbox:not(.hidden)').length
+    dom = jQuerychoice_answer_indexs.find('.checkbox:first').clone()
     dom.removeClass('hidden')
     dom.find('input[name="question[choices][]"]').val("")
     dom.find('input[name="question[choice_answer_indexs][]"]').val(next_choice_answer_index)
     jQuery(this).before(dom)

   jQuery(document).on 'click', '.form-question-multi-choice .delete-choice', ->
-    $choice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')
+    jQuerychoice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')

-    $delete_checkbox = jQuery(this).closest('.checkbox')
-    delete_index = parseInt($delete_checkbox.find('input:first').val())
-    last_index   = $choice_answer_indexs.find('.checkbox:not(.hidden)').length - 1
+    jQuerydelete_checkbox = jQuery(this).closest('.checkbox')
+    delete_index = parseInt(jQuerydelete_checkbox.find('input:first').val())
+    last_index   = jQuerychoice_answer_indexs.find('.checkbox:not(.hidden)').length - 1
     for index in [delete_index..last_index]
-      $checkbox = $choice_answer_indexs.find('.checkbox:not(.hidden)').eq(index)
-      $checkbox.find('input:first').val(index - 1)
+      jQuerycheckbox = jQuerychoice_answer_indexs.find('.checkbox:not(.hidden)').eq(index)
+      jQuerycheckbox.find('input:first').val(index - 1)

-    $delete_checkbox.remove()
+    jQuerydelete_checkbox.remove()

   jQuery(document).on 'submit', '.form-question-multi-choice form', (evt)->
-    $choice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')
-    $choice_answer_indexs.find('.checkbox.hidden').remove()
+    jQuerychoice_answer_indexs = jQuery('.form-question-multi-choice .question_choice_answer_indexs')
+    jQuerychoice_answer_indexs.find('.checkbox.hidden').remove()

-  jQuery(document).on 'click','.form-question-mapping .delete',->
+  jQuery(document).on 'click','.page-new-question-mapping .delete',->
     position_atr = jQuery(this).closest('.item').find('input').attr('name');
     zhengze = new RegExp(/[0-9]+/)
     position = zhengze.exec(position_atr)
-    item_length = jQuery(".form-question-mapping .item").length
-    x
+    item_length = jQuery(".page-new-question-mapping .item").length
     for x in [position...item_length]
-      q = x - 1
-      jQuery(".form-question-mapping .item").eq(x).find("input").attr('name','question[mapping_answer]['+q+'][]')
-    if item_length == 1
-      fuben = jQuery(".form-question-mapping .item:first").clone()
-      jQuery(".form-question-mapping .item:first").after(fuben)
-      jQuery(".form-question-mapping .item:last").addClass('hidden')
+      q = x-1
+      jQuery(".page-new-question-mapping .item").eq(x).find("input").attr('name','question[mapping_answer]['+q+'][]')
+    if item_length==1
+      jQuery(".page-new-question-mapping .item:first input").val("")
+      fuben = jQuery(".page-new-question-mapping .item:first").clone()
+      jQuery(".page-new-question-mapping .item:first").after(fuben)
+      jQuery(".page-new-question-mapping .item:last").addClass('hidden')
     jQuery(this).closest('.item').remove();
-
-  jQuery(document).on 'click','.form-question-mapping .append',->
+  jQuery(document).on 'click','.page-new-question-mapping .append',->
     count_hidden = jQuery('.option-key-field .hidden').length
-    if count_hidden == 1
+    if count_hidden==1
       jQuery('.option-key-field .hidden input').attr('name','question[mapping_answer][0][]')
       jQuery('.option-key-field .hidden' ).removeClass('hidden')
     else
-      shuzi = jQuery('.form-question-mapping .item:last input ').attr('name');
+      shuzi = jQuery('.page-new-question-mapping .item:last input ').attr('name');
       zhengze = new RegExp(/[0-9]+/)
       shuzi = zhengze.exec(shuzi)
-      shuzi = Number(shuzi) + 1
-      atr = jQuery(this).closest('.form-question-mapping').find(".item:last").clone()
+      shuzi = Number(shuzi)+1
+      atr = jQuery(this).closest('.page-new-question-mapping').find(".item:last").clone()
       jQuery(this).closest('.add-items').before(atr);
-      jQuery(this).closest('.form-question-mapping').find(".item:last").removeClass('hidden')
-      jQuery(this).closest('.form-question-mapping').find(".item:last input").attr('name','question[mapping_answer]['+shuzi+'][]')
-      jQuery(this).closest('.form-question-mapping').find(".item:last input").val()
+      jQuery(this).closest('.page-new-question-mapping').find(".item:last").removeClass('hidden')
+      jQuery(this).closest('.page-new-question-mapping').find(".item:last input").attr('name','question[mapping_answer]['+shuzi+'][]')
+      jQuery(this).closest('.page-new-question-mapping').find(".item:last input").val('')
+  jQuery(document).on 'submit','.page-new-question-mapping',->
+    jQuery('.page-new-question-mapping .answer .hidden').remove()

   jQuery(document).on 'click','.form-question-fill .insert',->
     str = jQuery('.form-question-fill').find("[name='question[content]']").val();
     str = str+" ___ "
     jQuery('.form-question-fill').find("[name='question[content]']").val(str);

-  jQuery(document).on 'click','.form-question-fill .delete ',->
+  jQuery(document).on 'click','.page-new-question-fill .delete ',->
+    count = jQuery('.page-new-question-fill .answer').length
+    if count ==1
+      fuben = jQuery(this).closest(".answer").clone()
+      fuben.addClass('hidden')
+      jQuery(this).closest(".answer").before(fuben)
     jQuery(this).closest(".answer").remove()

   jQuery(document).on 'click','.form-question-fill .append',->
@@ -96,3 +102,7 @@
     blank.find("input").val("")
     jQuery('.form-question-fill .answer:last').after(blank)

+  jQuery(document).on 'submit','.page-new-question-fill ',->
+    jQuery('.page-new-question-fill .answer.hidden').remove()
+
+


Update ca.js.coffee

Update ca.js.coffee

--- app/assets/javascripts/uploadcare/locale/ca.js.coffee
+++ app/assets/javascripts/uploadcare/locale/ca.js.coffee
@@ -6,9 +6,9 @@
       default: 'Error'
       baddata: 'Valor incorrecte'
       size: 'Massa gran'
-      upload: 'No s'ha pogut carregar'
+      upload: 'No s\'ha pogut carregar'
       user: 'Carrega cancel·lada'
-      info: 'No s'ha pogut carregar la informació'
+      info: 'No s\'ha pogut carregar la informació'
       image: 'Només es permeten imatges'
     draghere: 'Arrossega els fitxers fins aquí'
     file:
@@ -56,11 +56,11 @@
           error:
             default:
               title: 'La pujada ha fallat'
-              line1: 'S'ha produït un error durant la pujada.'
+              line1: 'S\'ha produït un error durant la pujada.'
               line2: 'Si us plau, provi-ho de nou.'
             image:
               title: 'Només imatges'
-              line1: 'Només s'accepten fitxers d'imatges.'
+              line1: 'Només s\'accepten fitxers d\'imatges.'
               line2: 'Si us plau, provi-ho de nou amb un altre fitxer.'
             size:
               title: 'Límit de mida'
@@ -68,7 +68,7 @@
               line2: 'Si us plau provi-ho de nou amb un altre fitxer.'
             loadImage:
               title: 'Error'
-              text: 'No s'ha pogut carregar la imatge'
+              text: 'No s\'ha pogut carregar la imatge'
       footer:
         text: 'Els fitxers han estat carregats, gestionats i processats per'


Remove unused getDocOpsLength method

Remove unused getDocOpsLength method

--- services/document-updater/app/coffee/RedisManager.coffee
+++ services/document-updater/app/coffee/RedisManager.coffee
@@ -151,8 +151,5 @@
 			[length, _] = results
 			callback(error, length)

-	getDocOpsLength: (doc_id, callback = (error, length) ->) ->
-		rclient.llen keys.docOps(doc_id: doc_id), callback
-
 	getDocIdsInProject: (project_id, callback = (error, doc_ids) ->) ->
 		rclient.smembers keys.docsInProject(project_id: project_id), callback


Object: Added isDestroyed method.

Object: Added isDestroyed method.

--- src/lib/core/Object.coffee
+++ src/lib/core/Object.coffee
@@ -183,6 +183,16 @@

   ###*
+    Returns the object's destroy state.
+
+    @export
+    @return {boolean} Whether the object is destroyed or not.
+  ###
+  isDestroyed: ->
+    return @destroyed or no
+
+
+  ###*
     Events enum emitted by the Object at some point.

     @enum {string}


Resolving a small merge conflict.

Resolving a small merge conflict.

--- lib/atom-isort.coffee
+++ lib/atom-isort.coffee
@@ -58,6 +58,7 @@
         include_trailing_comma: atom.config.get 'atom-isort.includeTrailingComma'
         force_sort_within_sections: atom.config.get 'atom-isort.forceSortWithinSections'
         force_alphabetical_sort: atom.config.get 'atom-isort.forceAlphabeticalSort'
+        settings_path: editor.getPath()

     pyResponse = require('child_process').spawnSync(
       'python',


Fix correct clearing of canvas

Fix correct clearing of canvas

--- src/core/Engine.coffee
+++ src/core/Engine.coffee
@@ -64,7 +64,7 @@
     return

   draw: ->
-    @context.clearRect(0, 0, @width, @height)
+    @context.clearRect(0, 0, @width * PX, @height * PX)
     for entities, i in @entities
       # entities in level 0 are not drawn
       if i and entities


cola.defaultAction.map方法

cola.defaultAction.map方法

--- src/core/action.coffee
+++ src/core/action.coffee
@@ -96,6 +96,9 @@
 		return data.toJSON()
 	return data

+cola.defaultAction.map = (str)->
+	return cola.util.parseStyleLikeString(str)
+
 cola.defaultAction.formatDate = cola.util.formatDate

 cola.defaultAction.formatNumber = cola.util.formatNumber


router: s/frag/frags

router: s/frag/frags

--- src/core/router.coffee
+++ src/core/router.coffee
@@ -115,7 +115,7 @@
     userRoute = userRoute.slice 1  if (userRoute.indexOf '!') is 0
     @visitedRoutes.push userRoute  if @visitedRoutes.last isnt userRoute

-    [frag, query...] = (userRoute ? @getDefaultRoute?() ? '/').split '?'
+    [frags, query...] = (userRoute ? @getDefaultRoute?() ? '/').split '?'

     query = @utils.parseQuery query.join '&'

@@ -127,15 +127,25 @@
     node = @tree
     params = {}

-    frag = frag.split '/'
-    frag.shift() # first edge is garbage like '' or '#!'
+    frags = frags.split '/'
+    frags.shift() # first edge is garbage like '' or '#!'

-    frag = frag.filter Boolean
+    frags = frags.filter Boolean

-    path = "/#{frag.join '/'}"
+    path = "/#{frags.join '/'}"

     qs = @utils.stringifyQuery query
     path += "?#{qs}"  if qs.length
+
+    for edge in frags
+      if node[edge]
+        node = node[edge]
+      else
+        param = node[':']
+        if param?
+          params[param.name] = edge
+          node = param
+        else @handleNotFound frags.join '/'

     if not suppressListeners and shouldPushState and not replaceState and path is @currentPath
       @emit 'AlreadyHere', path
@@ -146,16 +156,6 @@
     if shouldPushState
       method = if replaceState then 'replaceState' else 'pushState'
       history[method] objRef, path, path
-
-    for edge in frag
-      if node[edge]
-        node = node[edge]
-      else
-        param = node[':']
-        if param?
-          params[param.name] = edge
-          node = param
-        else @handleNotFound frag.join '/'

     routeInfo = {params, query}
     @emit 'RouteInfoHandled', {params, query, path}


Using crazy new include lookup for non-string modules

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -172,7 +172,7 @@
         self[name](args...)

     # Initial module inclue, for reconstructing objects from JSON
-
+    for

     return self
 )()


Update speed sample to run all performance metrics

--- samples/speed.coffee
+++ samples/speed.coffee
@@ -7,12 +7,6 @@
 each = require 'each'
 pad = require 'pad'

-###########################################################################################
-
-# lines = 100000
-# md5 = '9229fad768f525f6cac64bd158f6e022'
-lines = 1000000
-md5 = 'e837b60bbab8cb38259ff7b135a98aba'
 sizes = [
   0
   64
@@ -26,13 +20,9 @@
   1024*1024*64
   1024*1024*128
 ]
-times = 5
+times = 3

-BufferedStream = require "../src/FixedBufferedStream"
-
-###########################################################################################
-
-Generator = ->
+Generator = (@lines) ->
   @count = 0
   @readable = true
   process.nextTick @resume.bind @
@@ -42,7 +32,7 @@
 Generator.prototype.resume = ->
   @paused = false
   while not @paused and @readable
-    return @destroy() if @count++ is lines
+    return @destroy() if @count++ is @lines
     @emit 'data', '♥✈☺♬☑♠☎☻♫☒♤☤☹♪♀✩✉☠✔♂★✇♺♨❦☁✌❁☂♝❀☭☃\n'

 Generator.prototype.pause = ->
@@ -52,8 +42,6 @@
   @readable = false
   @emit 'end'
   @emit 'close'
-
-###########################################################################################

 pretty =
   bytes: (value) ->
@@ -70,40 +58,45 @@
       time = time / div
     print

-###########################################################################################
-
-run = (size, callback) ->
-  # Prepare the pipe
-  input = new Generator
-  buffer = new BufferedStream size
-  output = fs.createWriteStream "#{__dirname}/speed.#{pretty.bytes size}.out"
-  # Call the pipe
-  input.pipe(buffer).pipe(output)
-  # See how that worked
-  output.on 'error', (err)->
-    console.log err
-  output.on 'close', ->
-    callback()
-
-check = (size, callback) ->
-  exec "md5 #{__dirname}/speed.#{pretty.bytes(size).replace(' ','\\ ')}.out", (err, stdout, stderr) ->
-    /\ (\w*)$/.exec(stdout.trim())[1].should.eql md5
-    callback()
-
-each(sizes)
-.on 'item', (next, size) ->
-  time = Date.now()
-  each()
-  .times(times)
-  .on 'item', (next) ->
-    run size, ->
-      check size, next
-  .on 'error', ->
-    next()
-  .on 'end', ->
-    console.log pad(pretty.bytes(size), 7), ':', pretty.time((Date.now()-time)/times)
-    next()
-.on 'error', (e) ->
-  console.log e
-.on 'end', ->
+each([
+  require '../src/FixedBufferedStream'
+  require '../src/StackBufferedStream'
+])
+.on 'item', (next, BufferedStream) ->
+  each([
+    {lines: 100000, md5: '9229fad768f525f6cac64bd158f6e022'}
+    {lines: 1000000, md5: 'e837b60bbab8cb38259ff7b135a98aba'}
+  ])
+  .on 'item', (next, item) ->
+    {lines, md5} = item
+    each(sizes)
+    .on 'item', (next, size) ->
+      time = Date.now()
+      each()
+      .times(times)
+      .on 'item', (next) ->
+        # Prepare the pipe
+        input = new Generator lines
+        buffer = new BufferedStream size
+        output = fs.createWriteStream "#{__dirname}/speed.#{pretty.bytes size}.out"
+        # Call the pipe
+        input.pipe(buffer).pipe(output)
+        # See how that worked
+        output.on 'error', (err)->
+          console.log err
+        output.on 'close', ->
+          # Validate file md5
+          exec "md5 #{__dirname}/speed.#{pretty.bytes(size).replace(' ','\\ ')}.out", (err, stdout, stderr) ->
+            /\ (\w*)$/.exec(stdout.trim())[1].should.eql md5
+            next()
+      .on 'error', ->
+        next()
+      .on 'end', ->
+        console.log pad(pretty.bytes(size), 7), ':', pretty.time((Date.now()-time)/times)
+        next()
+    .on 'both', next
+  .on 'both', next
+.on 'error', (err) ->
+  console.error err
+.on 'end', (err) ->
   console.log 'done'


Indent the else statements properly.

Indent the else statements properly.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -136,7 +136,7 @@
       consequent = @walk(node.consequent)
       alt = []
       if node.alternate
-        alt = [ 'else', "\n", @walk(node.alternate) ]
+        alt = [ i, 'else', "\n", @walk(node.alternate) ]

       [ i, 'if ', test, "\n", consequent, alt ]


node depth helper

node depth helper

--- src/textorum/helper.coffee
+++ src/textorum/helper.coffee
@@ -94,6 +94,13 @@
           node = node.nextSibling
       return node

+    depth: (node) ->
+      nodeDepth = 0
+      while node.parentElement and node isnt prevnode
+        prevnode = node
+        nodeDepth += 1
+        node = node.parentElement
+      return nodeDepth

     getChildByName: (node, names, namespaceURI) ->
       for child in node.childNodes


Fix CSS order on save

Fix CSS order on save

--- atom.cson
+++ atom.cson
@@ -52,6 +52,8 @@
   linter:
     lintOnChange: false
   "linter-eslint": {}
+  "linter-stylelint-plus":
+    fixOnSave: true
   "linter-ui-default":
     panelHeight: 192.75
     showPanel: true


修改项目配置中模块属性的类型

修改项目配置中模块属性的类型

--- lib/utils/util.coffee
+++ lib/utils/util.coffee
@@ -66,7 +66,7 @@
       name: options.appName
       identifier: options.appId
       mainModule: ''
-      modules:[]
+      modules: {}
       version: '0.0.1'
       description: ''
       dependencies: {}
@@ -127,7 +127,7 @@
     user = @store('chameleon').account
     if typeof user is 'undefined'
       return false
-    else
+    else
       return true

   writeFile: (file, textContent, cb) ->


Dance - dont have 2 moves next to one another

--- scripts/dance.coffee
+++ scripts/dance.coffee
@@ -10,7 +10,12 @@
 module.exports = (robot) ->
   robot.respond /dance/i, (msg) ->
     num = limit+1
+
+    last = n = 0
+
     maneuver = while num -= 1
-        msg.random moves
+        while n is last
+            n = msg.random moves
+        last = n

     msg.send maneuver.join(' ')


Update hubot.coffee

Update hubot.coffee

--- scripts/hubot.coffee
+++ scripts/hubot.coffee
@@ -12,7 +12,17 @@

 module.exports = (robot) ->

-  pkg = require Path.join __dirname, '..', 'package.json'
+  messages = [
+    "(´-`).oO(ここ、俺の部屋なんやけどなー)"
+    "あー、オイル美味い (*´▽｀)◇ゞゴクゴク"
+    "( ´ ▽ ` )ﾉ ﾀﾀﾞｲﾏｧ 出かけてました！"
+    "ヽ(;´Д`)ノ練習ﾔﾒﾃ～ "
+  ]
+
+  robot.hear /./i, (msg) ->
+    return unless msg.envelope.room is "#hubot"
+    return if Math.random() * 10 > 2
+    msg.send msg.random messages

   robot.respond /version/i, (msg) ->
     msg.send "#{pkg.version} (hubot: #{robot.version})"
@@ -31,6 +41,8 @@
       }
       return

+    pkg = require Path.join __dirname, '..', 'package.json'
+
     console.log "deployed: #{pkg.version}, current: #{robot.brain.data.hubot.lastVersion}"
     if Semver.gt pkg.version, robot.brain.data.hubot.lastVersion
       robot.brain.data.hubot.lastVersion = pkg.version


Modification on livetoken.loginWithToken

Simplification of ident test

--- livetoken-base.coffee
+++ livetoken-base.coffee
@@ -32,6 +32,12 @@

 	return result

+
+
+##############################################
+############ SERVER SIDE METHODS #############
+##############################################
+
 if Meteor.isServer
 	Accounts.livetoken = {
 		auth: "EMAIL-ONLY"
@@ -93,12 +99,25 @@

 		userDoc.emails = [{address: options.email.toLowerCase(), verified: false}]

-		# Check if this phone is already existing somewhere
+		# Check if this user is already existing somewhere depending on auth method
+		switch Accounts.livetoken.auth
+			when 'EMAIL-ONLY'
+				user = Meteor.users.find({'email': userDoc.email}).fetch()
+			when 'PHONE-ONLY'
+				user = Meteor.users.find({'phone': userDoc.phone}).fetch()
+			when 'EMAIL-OR-PHONE'
+				user = Meteor.users.find({$or: [{'phone': userDoc.phone}, {'email': userDoc.email}]}).fetch()
+			when 'EMAIL-AND-PHONE'
+				user = Meteor.users.find({'email': userDoc.email, 'phone': userDoc.phone}).fetch()
+			else
+				throw new Meteor.Error 'unconfigured-authentication', 'Please configure your authentication method first.'
+		unless user.length is 0
+			throw new Meteor.Error 'existing-user', 'Sorry this user already exists.'

 		# Insert user in standard Accounts object
 		userId = Accounts.insertUserDoc options, userDoc

-		Meteor.users.update userId, { $set: {email: options.email.toLowerCase(), phone: options.phone services: userDoc.services, emails: userDoc.emails, try: 0} }
+		Meteor.users.update userId, { $set: {'email': userDoc.email, 'phone': options.phone, 'services': userDoc.services, 'emails': userDoc.emails, 'try': 0} }

 		return userId

@@ -378,6 +397,14 @@

 		return true

+
+
+
+
+##############################################
+############ CLIENT SIDE METHODS #############
+##############################################
+
 if Meteor.isClient
 	Meteor.getAuthMethods = () ->
 		Meteor.call 'livetoken.authMethods', (err, res) ->
@@ -455,19 +482,17 @@

 			if auth.name is 'ident'
 				if Session.get 'livetoken.emailToken'
-					ident = Session.get 'livetoken.emailToken'
-					creds[ident] = { type: 'email', token: options.ident }
-					user.email = ident
+					auth.name = 'email'
 				else if Session.get 'livetoken.phoneToken'
-					ident = Session.get 'livetoken.phoneToken'
-					creds[ident] = { type: 'phone', token: options.ident }
+					auth.name = 'phone'
 				else
 					throw new Meteor.Error 'empty-authentication', 'Your authentication type is not set.'
-			else
-				ident = Session.get "livetoken.#{auth.name}Token"
-				creds[ident] = { type: auth.name, token: options[auth.name] }
-				if auth.name is 'email'
-					user.email = ident
+				options[auth.name] = options.ident
+
+			ident = Session.get "livetoken.#{auth.name}Token"
+			creds[ident] = { type: auth.name, token: options[auth.name] }
+			if auth.name is 'email'
+				user.email = ident

 		if Object.keys(creds).length > 0
 			credentials = [creds]
@@ -497,4 +522,3 @@
 				callback err
 			else
 				callback res
-


Panel container probably not public

Panel container probably not public

--- src/panel-container.coffee
+++ src/panel-container.coffee
@@ -1,6 +1,5 @@
 {Emitter, CompositeDisposable} = require 'event-kit'

-# Public:
 module.exports =
 class PanelContainer
   constructor: ({@viewRegistry, @orientation}) ->


Unsubscribe from the editor model with ::unsubscribe

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -527,7 +527,7 @@

     if @editor
       @saveScrollPositionForEditor()
-      @editor.off(".editor")
+      @unsubscribe(@editor)

     @editor = editor


Movable collision.

Movable collision.

--- main.coffee
+++ main.coffee
@@ -1,6 +1,67 @@
 #!vanilla

 pi = Math.PI
+
+class Canvas
+
+    @width = 360
+    @height = 360
+
+    @canvas = document.querySelector('canvas')
+    @canvas.width = @width
+    @canvas.height = @height
+    @ctx = @canvas.getContext('2d')
+
+    @clear: -> @ctx.clearRect(0, 0, @width, @height)
+
+    @square: (pos, size, color) ->
+        @ctx.fillStyle = color
+        @ctx.fillRect(pos.x, pos.y, size, size)
+
+
+class Trans
+
+    @w = Canvas.width
+    @h = Canvas.height
+    @ax = [-1, 1, -1, 1]
+
+    @x2X = d3.scale.linear() # to pixels
+        .domain(@ax[0..1])
+        .range([0, @w])
+
+    @X2x = @x2X.invert
+
+    @y2Y = d3.scale.linear()
+        .domain(@ax[2..3])
+        .range([@h, 0])
+
+    @Y2y = @y2Y.invert
+
+    @fn: (x, xm, ym) ->
+        n = -0.5+(x+0.5)*(2*ym+1)/(2*xm+1)
+        p = ym+(x-xm)*(1-2*ym)/(1-2*xm)
+        n*(x<xm) + p*(x>=xm)
+
+class Vector
+
+    z = -> new Vector
+
+    constructor: (@x=0, @y=0) ->
+
+    add: (v=z()) ->
+        @x += v.x
+        @y += v.y
+        this
+
+    mag: () -> Math.sqrt(@x*@x + @y*@y)
+
+    ang: () -> Math.atan2(@y, @x)
+
+    polar: (m, a) ->
+        @x = m*Math.cos(a)
+        @y = m*Math.sin(a)
+        this
+

 class d3Object

@@ -20,7 +81,7 @@
     width = 480 - margin.left - margin.right
     height = 480 - margin.top - margin.bottom

-    constructor: (xm, ym) ->
+    constructor: (@xm=0, @ym=0) ->

         super "plot"
         chartArea = @obj
@@ -43,8 +104,8 @@
             .attr("id", "plot")
             .attr("transform", "translate(#{margin.left},#{margin.top})")

-        Xm = @xScale(xm)
-        Ym = @yScale(ym)
+        Xm = Trans.x2X(@xm)
+        Ym = Trans.y2Y(@ym)

         @marker0 = @marker('black')
         @marker0.attr("cx", Xm)
@@ -59,9 +120,10 @@
         @l2.attr("y2", Ym)

     roofFn: (x) ->
-        xm = @xScale.invert(@marker0.attr('cx'))
-        ym = @yScale.invert(@marker0.attr('cy'))
-        (x<0)*(-0.5+(x+0.5)*(2*ym+1)/(2*xm+1)) + (x>=0)*(ym+(x-xm)*(1-2*ym)/(1-2*xm))
+        xm = Trans.X2x(@marker0.attr('cx'))
+        ym = Trans.Y2y(@marker0.attr('cy'))
+        #(x<0)*(-0.5+(x+0.5)*(2*ym+1)/(2*xm+1)) + (x>=0)*(ym+(x-xm)*(1-2*ym)/(1-2*xm))
+        Trans.fn(x, xm, ym)

     roofLine: (xFixed, yFixed) ->
         @plot.append("line")
@@ -86,7 +148,9 @@
                 .on("drag", => @dragMarker(m, d3.event.x, d3.event.y)) #, guide))
             )

-    dragMarker: (marker, u, v) -> #, guide) ->
+    dragMarker: (marker, u, v) ->
+        @xm = Trans.X2x u
+        @ym = Trans.Y2y v
         marker.attr("cx", u)
         marker.attr("cy", v)
         @l1.attr('x2', u).attr('y2', v)
@@ -218,42 +282,6 @@
             .attr(@bar.rangeDir, (d,i) => (1-d.count/cmax)*width)
             .attr(@bar.rangeAttr, (d, i) => d.count/cmax*width)

-class Canvas
-
-    @width = 360
-    @height = 360
-
-    @canvas = document.querySelector('canvas')
-    @canvas.width = @width
-    @canvas.height = @height
-    @ctx = @canvas.getContext('2d')
-
-    @clear: -> @ctx.clearRect(0, 0, @width, @height)
-
-    @square: (pos, size, color) ->
-        @ctx.fillStyle = color
-        @ctx.fillRect(pos.x, pos.y, size, size)
-
-class Vector
-
-    z = -> new Vector
-
-    constructor: (@x=0, @y=0) ->
-
-    add: (v=z()) ->
-        @x += v.x
-        @y += v.y
-        this
-
-    mag: () -> Math.sqrt(@x*@x + @y*@y)
-
-    ang: () -> Math.atan2(@y, @x)
-
-    polar: (m, a) ->
-        @x = m*Math.cos(a)
-        @y = m*Math.sin(a)
-        this
-

 class Sunlight

@@ -263,14 +291,8 @@
     h: Canvas.height
     O: -> new Vector 0, 0

-    constructor: (@pos=@O(), @xm, @ym) ->
-
-        #@q = (y) -> Math.floor((y+0.5)*20)
-
-        g = (x) ->
-            (x<0)*(-0.5+(x+0.5)*(2*@ym+1)/(2*@xm+1)) + (x>=0)*(@ym+(x-@xm)*(1-2*@ym)/(1-2*@xm))
-
-
+    constructor: (@pos=@O()) ->
+
         # sim units -> screen units
         @xScale = d3.scale.linear()
             .domain([-1, 1])
@@ -284,7 +306,7 @@
         @velocity = []
         @velocity[0] = new Vector 0, 2
         @velocity[1] = @O()
-        @setCollision(g)
+        @setCollision()
         @setState 0

         #@colPos = []
@@ -306,15 +328,14 @@
             @setState(1)
             count[@bin] += 1
             colPos.push(@pos)
-            #console.log "colPos", colPos
+
         @d += @vel.mag()
         @pos.add @vel

-    setCollision: (g) ->
-        y = g(@xScale.invert(@pos.x))
+    setCollision: () ->
+        y = Trans.fn(@xScale.invert(@pos.x), plot.xm, plot.ym)
         @limit =  @yScale(y)
         @bin = Math.floor((y+0.5)*20)
-

     collision: -> @pos.y > @limit

@@ -326,7 +347,7 @@
     rate: 2
     cx: Canvas.width/2

-    constructor: (@xm, @ym)->
+    constructor: () ->
         @photons = []

     emit: () ->
@@ -334,12 +355,13 @@
             @photons.push(@emitPhoton()) for [0...@rate]
         @photons = @photons.filter (photon) => photon.visible()
         for photon in @photons
+            photon.setCollision()
             photon.move()
             photon.draw()

     emitPhoton: ->
         pos = new Vector @cx + @l*(Math.random()-0.5), 1
-        new Sunlight(pos, @xm, @ym)
+        new Sunlight(pos)

 class Simulation

@@ -348,14 +370,8 @@
         xm = 0
         ym = 0.275

-        #g = (x) ->
-        #    (x<0)*(-0.5+(x+0.5)*(2*ym+1)/(2*xm+1)) + (x>=0)*(ym+(x-xm)*(1-2*ym)/(1-2*xm))
-
-        #x = [-0.5, 0, 0.5]
-        #@p = new Plot x, (g(u) for u in x)
-        @p = new Plot(xm, ym)
-
-        #@sun = new Sun xm, ym
+        #@p = new Plot(xm, ym)
+        @sun = new Sun
         #@h = new Histo

     start: () ->
@@ -363,11 +379,11 @@

     snapshot: () ->
         Canvas.clear()
-        #@sun.emit()
+        @sun.emit()
         #@h.update(count)

-        @p.hline([colPos[0].x,colPos[0].y])
-        @p.vline([colPos[0].x,colPos[0].y])
+        #@p.hline([colPos[0].x,colPos[0].y])
+        #@p.vline([colPos[0].x,colPos[0].y])

         colPos.pop() for c in colPos

@@ -381,6 +397,9 @@

 count = (0 for [0..19])
 colPos = []
+
+plot = new Plot
+
 sim = new Simulation
 $("#params4b").on "click", =>
     sim.stop()


Well, let's move it.

Well, let's move it.

--- main.coffee
+++ main.coffee
@@ -16,6 +16,11 @@

 'use strict'

+if process.env.NODE_ENV is 'production'
+	console.log 'Loading New Relic...'
+	newrelic = require 'newrelic'
+	console.log 'Loaded New Relic.'
+
 config = require "#{__dirname}/config.js"
 lib = require "#{__dirname}/lib.coffee"

@@ -26,11 +31,6 @@
 express = require 'express'
 sync = require 'sync'

-
-if process.env.NODE_ENV is 'production'
-	console.log 'Loading New Relic...'
-	newrelic = require 'newrelic'
-	console.log 'Loaded New Relic.'

 dbConnection = anyDB.createPool config.DATABASE_URL, min: 2, max: 20
 dbConnection.query 'SELECT version()', [], (error, result) ->


:telescope::icecream: Updated in browser at strd6.github.io/editor

--- main.coffee
+++ main.coffee
@@ -205,7 +205,7 @@
       count = 0

       while currentState.time < t
-        [event, nextState] = player.readEvent(currentState)
+        [event, nextState] = player.readEvent(currentState, true)
         break unless event
         currentState = handleEvent(event, nextState)
         count += 1
@@ -213,6 +213,6 @@
       return count

     setInterval ->
-      consumed = consumeEventsUntilTime(context.currentTime + 0.125)
+      consumed = consumeEventsUntilTime(context.currentTime - timeOffset + 0.025)
       # console.log "Consumed:", consumed
     , 15


Refactor resultant into own method.

Refactor resultant into own method.

--- src/environment.coffee
+++ src/environment.coffee
@@ -26,20 +26,21 @@
     # @param element {Array} the element to update
     # @param milliseconds {Number} the number of ms that have elapsed
     elapse: (elements, milliseconds) ->
-
         for element in elements
-            # Calculate the forces acting on the element.
-            forces = [
-                @drag(element),
-                @gravity(element)
-            ]
-            resultant = forces.reduce (t, s) -> t.add(s)    #FIXME: not portable
-
-            # Apply them.
+            resultant = @resultant(element, milliseconds)
             element.applyForce(resultant, milliseconds)
-
         # HACK: Don't compile the loop into an expression.
         return this
+
+
+    # Return the resultant of all environmental forces on the element.
+    resultant : (element, milliseconds) ->
+        forces = [
+            @drag(element),
+            @gravity(element)
+        ]
+        #FIXME: not portable
+        return forces.reduce (t, s) -> t.add(s)

     # Return the force of drag on the element.
     #


added node sizing to Paths visualization (w/ custom label positioning)

--- src/viz/types/paths.coffee
+++ src/viz/types/paths.coffee
@@ -68,9 +68,11 @@
       yDomain.unshift i
     i++

+  labelSpace = if vars.size.value and !vars.small then 30 else 0
+
   y = d3.scale.ordinal()
     .domain yDomain
-    .range d3.range rowHeight/2, vars.height.viz + rowHeight/2, (vars.height.viz - rowHeight)/(rows-1)
+    .range d3.range rowHeight/2 - labelSpace, vars.height.viz + rowHeight/2 - labelSpace, (vars.height.viz - rowHeight)/(rows-1)

   columns = paths["all"].length

@@ -80,16 +82,16 @@
     .domain [0, columns - 1]
     .rangeRound [columnWidth/2, vars.width.viz - columnWidth/2]

-  overlap = if vars.size.value then vars.nodes.overlap else 0.4
-
-  maxRadius = d3.min([columnWidth,rowHeight]) * overlap
+  minRadius = 5
+  maxRadius = d3.min([columnWidth,rowHeight - labelSpace]) * 0.4

   sizeDomain = d3.extent vars.data.app, (node) ->
-    fetchValue vars, node, vars.size.value
+    val = fetchValue vars, node, vars.size.value
+    return val or 0

   size = vars.size.scale.value
     .domain sizeDomain
-    .rangeRound [2, maxRadius]
+    .rangeRound [minRadius, maxRadius]

   for node in vars.data.app

@@ -102,9 +104,20 @@
     node.d3plus.y = y(pathLookup[node[vars.id.value]])

     if vars.size.value
-      node.d3plus.r = size(fetchValue vars, node, vars.size.value)
+      val = fetchValue vars, node, vars.size.value
+      node.d3plus.r = if val then size(val) else minRadius
     else
       node.d3plus.r = maxRadius
+
+    if node.d3plus.r < columnWidth * 0.1 and !vars.small
+      node.d3plus.label =
+        x: 0
+        y: node.d3plus.r + vars.labels.padding*2
+        w: columnWidth * 0.6
+        h: labelSpace + maxRadius - node.d3plus.r
+        resize: false
+    else
+      delete node.d3plus.label

   for path, pathInt in viz.paths


Caption to alt text

Caption to alt text

--- plugins/image.coffee
+++ plugins/image.coffee
@@ -54,8 +54,8 @@

-                        Caption<input type=\"text\" id=\"caption\" />
-                        <button id=\"#{@options.uuid}-#{widget.widgetName}-addimage\">Add Image</button>
+                        Caption<input type=\"text\" id=\"caption-sugg\" />
+

@@ -70,6 +70,10 @@

+

+                        Caption<input type=\"text\" id=\"caption-search\" />
+
+

UPLOAD

"
@@ -103,6 +107,8 @@
                     jQuery("##{widget.options.uuid}-search-activeImageContainer").show()
                     firstimage = jQuery(".#{widget.widgetName}-search-imageThumbnail").first().addClass "imageThumbnailActive"
                     jQuery("##{widget.options.uuid}-search-activeImage, ##{widget.options.uuid}-search-activeImageBg").attr "src", firstimage.attr "src"
+
+                    jQuery("#metadata-search").show()

                 search = (page) ->
                     page = page || 1
@@ -225,12 +231,19 @@
                     image = ui.draggable[0]
                     tmpImg = new Image()
                     tmpImg.src = image.src
-                    altText = image.alt
+                    if not tmp
+                        if @startPlace.parents(".tab-suggestions").length > 0
+                            altText = jQuery("#caption-sugg").val()
+                        else if @startPlace.parents(".tab-search").length > 0
+                            altText = jQuery("#caption-search").val()
+                        else
+                            altText = jQuery(image).attr("alt")
+
                     imageInsert = $("").attr(
                         src: tmpImg.src
                         width: tmpImg.width
                         height: tmpImg.height
-                        alt: jQuery('#caption').val()
+                        alt: altText
                         class: (if tmp then "tmp" else "")
                     ).css("display", "none")
                     imageInsert
@@ -269,6 +282,8 @@
                     else
                         false

+                startPlace: ""
+
             dnd =
                 handleDragEvent: (event, ui) ->
                     tmpObject = $(".tmp", editable)
@@ -291,9 +306,11 @@
                     $.ui.ddmanager.prepareOffsets(
                         $(event.target).data('draggable'), event
                     )
-
+
                     $(event.target).remove() if internalDrop
-                    $(document).trigger('startPreventSave');
+
+                    jQuery(document).trigger('startPreventSave')
+                    helper.startPlace = jQuery(event.target)

                 handleStopEvent: (event, ui) ->
                     internalDrop = helper.checkOrigin(event)
@@ -416,6 +433,7 @@
             editable = $(@options.editable.element)
             # keep a reference of options for context changes
             widgetOptions = @options
+
             offset = editable.offset()
             third = parseFloat(editable.width() / 3)
             overlayMiddleConfig =


Update curl path

Update curl path

--- src/script/setup/paths.coffee
+++ src/script/setup/paths.coffee
@@ -1,4 +1,5 @@

 vtex.curl.config
   paths:
-    'flight': '//io.vtex.com.br/front-libs/flight/1.1.4-vtex'+    'flight': '//io.vtex.com.br/front-libs/flight/1.1.4-vtex'
+  pluginPath: '//io.vtex.com.br/front-libs/curl/0.8.10-vtex.2/plugin/'


subtle terminal copy/paste issues...

subtle terminal copy/paste issues...

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -179,8 +179,7 @@
         if not IS_MOBILE
             @textarea.on 'blur', =>
                 if @_focusing?          # see comment in @focus.
-                    delete @_focusing
-                    @textarea.focus()   # steal it back.
+                    @_focus_hidden_textarea()
                 else
                     @blur()

@@ -417,7 +416,8 @@

         e = @element.find(".salvus-console-terminal")

-        e.mousedown () => @pause_rendering(false)
+        e.mousedown () =>
+            @pause_rendering(false)

         e.mouseup () =>
             if not getSelection().toString()
@@ -428,7 +428,9 @@
                 # nothing in the terminal is selected
                 @unpause_rendering()

-        e.on('copy', @unpause_rendering)
+        e.on 'copy', =>
+            @unpause_rendering()
+            setTimeout(@focus, 0)  # must happen in next cycle or copy will not work due to loss of focus.

     _init_colors: () =>
         colors = Terminal.color_schemes[@opts.color_scheme].colors
@@ -531,8 +533,7 @@
             $(document).on 'click', @_click
         else
             @_mousedown = (e) =>
-                t = $(e.target)
-                if t.hasParent(@element).length > 0
+                if $(e.target).hasParent(@element).length > 0
                     @focus()
                 else
                     @blur()
@@ -744,7 +745,9 @@
             pb.val('')
             @session?.write_data(data)

-        pb.on('paste', (() -> setTimeout(f,0)))
+        pb.on 'paste', =>
+            pb.val('')
+            setTimeout(f,0)

     #######################################################################
     # Public API
@@ -910,15 +913,17 @@

         focused_console = @
         @is_focused = true
+        @textarea.blur()
         $(@terminal.element).focus()

         if IS_MOBILE
             @element.find(".salvus-console-input-line").focus()
         else
+            @_focus_hidden_textarea()
             @terminal.focus()
-            @_focus_hidden_textarea()

         $(@terminal.element).addClass('salvus-console-focus').removeClass('salvus-console-blur')
+        setTimeout((()=>delete @_focusing), 0)   # critical!

     set_title: (title) ->
         @opts.set_title?(title)


Rename log file

Rename log file

--- src/spec/products.spec.coffee
+++ src/spec/products.spec.coffee
@@ -15,7 +15,7 @@
       name: "#{package_json.name}-#{package_json.version}"
       streams: [
         {level: 'info', stream: process.stderr}
-        {level: 'info', path: "./brickfox-logger.log"}
+        {level: 'info', path: "./sphere-brickfox-connector.log"}
       ]

   beforeEach ->


Change the default pathToHTML to '/assets/batman/html' for batman.rails

--- src/extras/batman.rails.coffee
+++ src/extras/batman.rails.coffee
@@ -1,3 +1,4 @@
+Batman.config.viewPrefix = '/assets/batman/html'

 numericKeys = [1, 2, 3, 4, 5, 6, 7, 10, 11]
 date_re = ///


change filesize to be human readable before sending it to the method

--- app.coffee
+++ app.coffee
@@ -82,7 +82,7 @@
           $('#re-authenticate').show()
           $('#upload_button').addClass('disabled')
           $('#upload_button input').prop("disabled", true)
-        # file.size = o.formatFileSize(file.size) if file.size
+        file.size = o.formatFileSize(file.size) if file.size
         rows = rows.add(downloadRowHTML(file))
       return rows
     # done: (e,data) ->


fix restriction for zombie

fix restriction for zombie

--- app.coffee
+++ app.coffee
@@ -272,7 +272,7 @@
 			res.viewData.users = teamUsers
 			res.render('teamHuman', res.viewData)

-app.get '/teamZombie', authorize(), (req, res) ->
+app.get '/teamZombie', authorize('zombie'), (req, res) ->
 	tmpl_dir = __dirname + '/views/news/forZombie/'
 	# read all news in the directory
 	fs.readdir tmpl_dir, (err, news) ->


key in on success

key in on success

--- app.coffee
+++ app.coffee
@@ -26,22 +26,20 @@

 app.post '/ringonce', (req, res) ->
   twiml = new twilio.TwimlResponse()
-  twiml.say 'Trying YOLK again.'
-  .dial process.env.FORWARD,
-    timeout: 10
-    timeLimit: 5
-    action: '/ring'
+  if req.body['DialCallStatus'] == 'completed'
+    twiml.redirect '/keyin'
+  else
+    twiml.say 'Trying YOLK again.'
+    .dial process.env.FORWARD,
+      timeout: 10
+      timeLimit: 5
+      action: '/ring'
   send_xml res, twiml

 app.post '/ring', (req, res) ->
   twiml = new twilio.TwimlResponse()
   if req.body['DialCallStatus'] == 'completed'
-    twiml.say 'Doorbell Yolk!'
-    .gather
-      action: '/keyin'
-      numDigits: 4
-    , () ->
-      @.say 'enter your code'
+    twiml.redirect '/keyin'
   else
     twiml.say 'Sorry buddy, no answer.'
     twiml.say 'Denied!!'


Use marimba soundfont

Use marimba soundfont

--- app.coffee
+++ app.coffee
@@ -40,19 +40,20 @@
 		key.element.classList[if matches then "remove" else "add"] "lowlight"
 		key.element.classList[if matches then "add" else "remove"] "highlight"

+instrument = "marimba" # "acoustic_grand_piano"
 MIDI.loadPlugin
 	# api: "audiotag"
 	api: "webaudio"
-	# soundfontUrl: "https://gleitz.github.io/midi-js-soundfonts/FluidR3_GM/" #"./soundfont/"
+	# soundfontUrl: "https://gleitz.github.io/midi-js-soundfonts/FluidR3_GM/"
 	soundfontUrl: "./lib/soundfont/"
-	instrument: "acoustic_grand_piano"
-	# instrument: "marimba"
+	instrument: instrument
 	onprogress: (state, progress)->
 		console?.log(state, progress)
 	onerror: (err)->
 		console?.error(err)
 	onsuccess: ->
 		console?.log "MIDI.js loaded"
+		MIDI.programChange(0, MIDI.GM.byName[instrument].number);

 class Key
 	constructor: (@note, firstMIDI)->
@@ -166,6 +167,4 @@
 keys_container.addEventListener "pointerout", (e)->
 	if pointers[e.pointerId]
 		pointers[e.pointerId].key?.release()
-		pointers[e.pointerId].key = null
-		# e.target.key?.release()
-
+		pointers[e.pointerId].key = null


add garbage collection manual process

add garbage collection manual process

--- app.coffee
+++ app.coffee
@@ -101,3 +101,9 @@
 	throw error if error?
 	logger.info "github-latex-ci starting up, listening on #{host}:#{port}"

+if global.gc?
+	gcTimer = setInterval () ->
+		global.gc()
+		logger.log process.memoryUsage(), "global.gc"
+	, 3 * oneMinute = 60 * 1000
+	gcTimer.unref()


Added CORS support

Added CORS support

--- app.coffee
+++ app.coffee
@@ -16,6 +16,14 @@

 {Store} = require("./lib/stores/#{config.store}")
 store = new Store(config)
+
+app.all "/api/*", (req, res, next) ->
+  res.header("Access-Control-Allow-Origin", "*")
+  res.header("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers")) # I hear an echo. Do you?
+  res.header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE")
+  res.header("Access-Control-Max-Age", 60 * 60 * 24 * 2) # 2 days
+
+  next()

 # Expose the public api for plunks
 app.resource "api/v1/plunks", require("./api/plunks")(store)


uploadRowHTML abstracted into its own method

--- app.coffee
+++ app.coffee
@@ -1,4 +1,23 @@
 $ ->
+  uploadRowHTML = (filename, filesize=-1, error=null) ->
+    row = $(
+      '
' +
+      '' +
+      '' +
+      ' - ' +
+      '' +
+      '	' +
+      'uploading to Dropbox...	' +
+      '' +
+      '' +
+      '	' +
+      "
"
+    )
+    row.find('.name').text(filename)
+    row.find('.size').text(filesize)
+    row.find('.error').text(error) if error
+    row
+
   $('#upload').fileupload({
     dataType: 'json',
     autoUpload: true,
@@ -15,24 +34,7 @@
         console.log(file)
         console.log("filename = " + file.name)
         console.log("size = " + o.formatFileSize(file.size))
-        row = $(
-          '
' +
-          '' +
-          '' +
-          ' - ' +
-          '' +
-          '	' +
-          'uploading to Dropbox...	' +
-          '' +
-          '' +
-          '	' +
-          "
"
-        )
-
-        row.find('.name').text(file.name)
-        row.find('.size').text(o.formatFileSize(file.size))
-        row.find('.error').text(file.error) if file.error
-        rows = rows.add(row)
+        rows = rows.add(uploadRowHTML(file.name, o.formatFileSize(file.size), file.error))
       return rows
     downloadTemplate: (o) ->
       console.log("downloadtemplate")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -1,5 +1,5 @@
 ###*
-
+A

 @see GameState
 @name LevelState


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/function_extensions.coffee
+++ src/function_extensions.coffee
@@ -4,7 +4,8 @@

   return ->
     return memo if ran
-    ran = tr
+    ran = true
+

   _.once = function(func) {


Unwrap object.

Unwrap object.

--- src/coffee/utils/order-utils.coffee
+++ src/coffee/utils/order-utils.coffee
@@ -80,39 +80,42 @@
   actionsMapReturnInfo: (diff, old_obj) ->

     actions = []
-    returnInfoDiffs = diff['returnInfo']
-    if returnInfoDiffs
+    returnInfoDeltas = diff['returnInfo']
+    if returnInfoDeltas
       # iterate over returnInfo instances
-      _.each _.keys(returnInfoDiffs), (returnInfoIndex) ->
-        if returnInfoIndex isnt '_t'
-          returnInfoDiff = returnInfoDiffs[returnInfoIndex]
-          if _.isArray returnInfoDiff
-            # returnInfo was added
-            returnInfo = _.last returnInfoDiff
+      _.chain returnInfoDeltas
+        .filter (item, key) -> key isnt '_t'
+        .map (returnInfoDelta, returnInfoDeltaKey) ->
+          if _.isArray returnInfoDelta
+            # get last added item
+            returnInfo = _.last returnInfoDelta
             action =
               action: 'addReturnInfo'
-            _.each _.keys(returnInfo), (key) ->
-              action[key] = returnInfo[key]
+            _.each returnInfo, (value, key) ->
+              action[key] = value
             actions.push action

             # TODO: split into multiple actions (addReturnInfo + setReturnShipmentState/setReturnPaymentState)
             #   in case shipmentState/paymentState already transitioned to a non-initial state
           else
-            returnInfo = returnInfoDiff
-            # iterate over returnInfo items instances
-            _.each _.keys(returnInfo.items), (returnInfoItemIndex) ->
-              if returnInfoItemIndex isnt '_t'
-                returnInfoItem = returnInfo.items[returnInfoItemIndex]
+            returnInfo = returnInfoDelta
+            # iterate over returnItem instances
+            actions = _.chain returnInfo.items
+              .filter (item, key) -> key isnt '_t'
+              .map (item, itemKey) ->
                 # iterate over all returnInfo status actions
-                _.each actionsListReturnInfoState(), (actionDefinition) ->
-                  returnItemState = returnInfoItem[actionDefinition.key]
-                  if returnItemState
-                    action = {}
-                    action['action'] = actionDefinition.action
-                    action['returnItemId'] = old_obj.returnInfo[returnInfoIndex].items[returnInfoItemIndex].id
-                    action[actionDefinition.key] = helper.getDeltaValue returnItemState
-                    actions.push action
-    actions
+                _.chain actionsListReturnInfoState()
+                  .filter (actionDefinition) -> _.has(item, actionDefinition.key)
+                  .map (actionDefinition) ->
+                    action =
+                      action: actionDefinition.action
+                      returnItemId: old_obj.returnInfo[returnInfoDeltaKey].items[itemKey].id
+                    action[actionDefinition.key] = helper.getDeltaValue item[actionDefinition.key]
+                    action
+                  .value()
+              .value()
+       .value()
+    _.flatten actions

 ###
 Exports object


Client: Use BW data by default.

Client: Use BW data by default.

--- app.coffee
+++ app.coffee
@@ -42,7 +42,7 @@

 # Routing
 renderHomepage = (req, res) ->
-  {SpeciesData, FormeData, MoveData, ItemData} = require './data/bw'
+  {SpeciesData, FormeData, MoveData, ItemData} = require './server/bw/data'
   local = process.env.NODE_ENV in [ 'development', 'test' ]
   res.render 'index.jade', {local, SpeciesData, FormeData, MoveData, ItemData}

@@ -77,7 +77,7 @@
 # API
 app.namespace "/v1/api", ->
   app.get '/pokemon', (req, res) ->
-    {SpeciesData} = require './data/bw'
+    {SpeciesData} = require './server/bw/data'
     res.json(SpeciesData)

 userList = []
@@ -258,7 +258,7 @@
 httpServer.listen(PORT)

 generateUsername = ->
-  {SpeciesData} = require './data/bw'
+  {SpeciesData} = require './server/bw/data'
   randomName = (name  for name of SpeciesData)
   randomName = randomName[Math.floor(Math.random() * randomName.length)]
   randomName = randomName.split(/\s+/)[0]


fix login name

fix login name

--- app.coffee
+++ app.coffee
@@ -67,7 +67,7 @@

   @get '/': ->
     console.log @session.name
-    @session.name = "mizchi"  # for debug
+    # @session.name = "mizchi"  # for debug
     if @session.name
       @render index:
         id : @session.name


Patch fs for asar

Patch fs for asar

--- app.coffee
+++ app.coffee
@@ -2,8 +2,14 @@

 app = require 'app'
 BrowserWindow = require 'browser-window'
-path = require 'path'
+path = require 'path-extra'
 fs = require 'fs-extra'
+
+# Patch fs for current Electron
+fs.accessSync = (filePath, mode) ->
+  if fs.existsSync(filePath)
+    return true
+  throw new Error("ENOENT: no such file or directory, access '#{filePath}'")

 # Environment
 global.ROOT = __dirname


Check if League of Legends.exe exist.

Check if League of Legends.exe exist.

Garena LoL installations does not include lol.launcher.exe.

--- app.coffee
+++ app.coffee
@@ -116,6 +116,11 @@

   else
     if fs.existsSync(process.cwd() + '/lol.launcher.exe')
+      GLOBAL.lolInstallPath = process.cwd() + '/Config/Champions/'
+      cb null
+
+    # Also check League of Legends.exe in case it's a Garena installation.
+    else if fs.existsSync(process.cwd() + '/League of Legends.exe')
       GLOBAL.lolInstallPath = process.cwd() + '/Config/Champions/'
       cb null


Removed dev mode

Removed dev mode

--- app.coffee
+++ app.coffee
@@ -1,4 +1,4 @@
-dev = true
+dev = !true
 #Module dependencies
 express  = require "express"
 stylus   = require "stylus"


fixed path typos for HAKI_PATH/hakiapp

fixed path typos for HAKI_PATH/hakiapp

--- app.coffee
+++ app.coffee
@@ -69,27 +69,27 @@
 	(callback) -> exec('systemctl start openvpn@client', callback)
 	(callback) -> exec('systemctl enable openvpn@client', callback)
 	# haki user tasks
-	(callback) -> hakiExec('mkdir hakiapp', cwd: '/home/haki', callback)
-	(callback) -> hakiExec('git init', cwd: '/home/haki/hakiapp', callback)
-	(callback) -> hakiExec("git remote add origin #{state.gitUrl}", cwd: '/home/haki/hakiapp', callback)
+	(callback) -> hakiExec('mkdir hakiapp', cwd: HAKI_PATH, callback)
+	(callback) -> hakiExec('git init', cwd: "#{HAKI_PATH}/hakiapp", callback)
+	(callback) -> hakiExec("git remote add origin #{state.gitUrl}", cwd: "#{HAKI_PATH}/hakiapp", callback)
 	# done
 	(callback) -> console.log('Bootstrapped') ; callback()
 ]

 updateRepo = (callback) ->
 	tasks1 = [
-		(callback) -> hakiExec('git pull origin master', cwd: '/home/haki/hakiapp', callback)
-		(stdout, stderr, callback) -> hakiExec('git rev-parse HEAD', cwd: '/home/haki/hakiapp', callback)
+		(callback) -> hakiExec('git pull origin master', cwd: "#{HAKI_PATH}/hakiapp", callback)
+		(stdout, stderr, callback) -> hakiExec('git rev-parse HEAD', cwd: "#{HAKI_PATH}/hakiapp", callback)
 		(stdout, stderr, callback) -> callback(null, stdout.trim())
 	]

 	tasks2 = [
 		(callback) ->
 			console.log("Checking for package.json")
-			if fs.existsSync('hakiapp/package.json')
+			if fs.existsSync("#{HAKI_PATH}/hakiapp/package.json")
 				console.log("Found, npm installing")
 				ps = spawn('sudo', ['-u', 'haki', 'npm', 'install'],
-					cwd: 'hakiapp'
+					cwd: "#{HAKI_PATH}/hakiapp"
 					stdio: [0, 1, 2]
 				)
 				ps.on('exit', callback)
@@ -99,10 +99,10 @@
 				callback()
 		(callback) ->
 			console.log("Checking for Procfile")
-			if fs.existsSync('hakiapp/Procfile')
+			if fs.existsSync("#{HAKI_PATH}/hakiapp/Procfile")
 				console.log("Found Procfile, starting app..")
 				ps = spawn('foreman', ['start'],
-					cwd: 'hakiapp'
+					cwd: "#{HAKI_PATH}/hakiapp"
 					stdio: [0, 1, 2]
 					uid: posix.getpwnam('haki').uid
 				)


chat error fixed

chat error fixed

--- client/landing/app/MainApp/sidebar/sidebarview.coffee
+++ client/landing/app/MainApp/sidebar/sidebarview.coffee
@@ -370,8 +370,8 @@
       { title : "Help",  callback : -> @getSingleton('mainController').emit "ShowInstructionsBook" }
       { title : "About", callback : -> @showAboutDisplay() }
       { title : "Chat",  loggedIn : yes, callback : ->
-          @getSingleton('bottomPanelController').emit "TogglePanel", "chat"
-          unless location.hostname is "localhost"
-            new KDNotificationView title : "Coming soon..."
+        # @getSingleton('bottomPanelController').emit "TogglePanel", "chat"
+        # unless location.hostname is "localhost"
+        new KDNotificationView title : "Coming soon..."
       }
     ]


Force the progress bar update when we get a code response answer.

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -40,6 +40,11 @@
     if response.progress_changed
         @el.attr progress: response.progress_status
         @el.trigger('progressChanged')
+
+  forceUpdate: (response) =>
+    @el.attr progress: response.progress_status
+    @el.trigger('progressChanged')
+

   queueing: =>
     @queued_items = @$(".xqueue")
@@ -70,8 +75,8 @@
           @bind()

       @num_queued_items = @new_queued_items.length
-      @updateProgress response
       if @num_queued_items == 0
+        @forceUpdate response
         delete window.queuePollerID
       else
         # TODO: Some logic to dynamically adjust polling rate based on queuelen


refactor handlers for messages

refactor handlers for messages

--- components/environment_movement/world_movement.coffee
+++ components/environment_movement/world_movement.coffee
@@ -6,42 +6,33 @@
 mq_server = 'amqp://54.76.183.35:5672'
 context = require('rabbit.js').createContext(mq_server);
 exchange_name = 'alex2'
-
+
 connected_rooms = []
-user = {}
-
+users = {}
+
+handlers = {
+    'door_created': (message)->
+        console.log(message)
+        connected_rooms.push(message)
+    'movement_successful': (message)->
+        console.log(message)
+        users[message.user_id].current_location = message.current_location
+}
+
 data_provider.getPreviousData( (data)->
-
-
-    connected_rooms  = data.filter((row)-> row.topic == 'door_created')
-                        .map((row)-> row.content )
-
-    user.current_location = data.filter((row)-> row.topic == 'movement_successful')
-                                .map((row)-> row.content.room_name)
-                                .last()
-
-    console.log(connected_rooms)
+    for row in data
+        handlers[row.topic]?(row.content)
 )

 context.on('ready', ->
-    door_created_sub = context.socket('SUB',{routing:'topic'})

-    door_created_sub.connect(exchange_name,'door_created',->
-        console.log('door_created listener')
-        door_created_sub.on('data',(message)->
-            console.log(message)
-            connected_rooms.push(JSON.parse(message))
+    Object.keys(handlers).forEach((handler_name)->
+        subscriber = context.socket('SUB',{routing:'topic'})
+        subscriber.connect(exchange_name,handler_name,->
+            console.log("#{handler_name} listener")
+            subscriber.on('data',(string_message)->
+                handlers[handler_name](JSON.parse(string_message))
+            )
         )
     )
-
-
-    movement_successful_sub = context.socket('SUB',{routing:'topic'})
-    movement_successful_sub.connect(exchange_name,'movement_successful',->
-        console.log('movement_successful listener')
-        movement_successful_sub.on('data',(message)->
-            message = JSON.parse(message)
-            user.current_location = message.current_location
-        )
-    )
-
 )


Properly unescape device ID in API calls

--- api.coffee
+++ api.coffee
@@ -4,6 +4,7 @@
 url = require 'url'
 db = require './db'
 mongodb = require 'mongodb'
+querystring = require 'querystring'

 # regular expression objects
 TASKS_REGEX = /^\/devices\/([a-zA-Z0-9\-\_\%]+)\/tasks\/?$/
@@ -56,6 +57,11 @@
   , 500)

+sanitizeTask = (device, task) ->
+  task.device = device
+  task.timestamp = mongodb.Timestamp()
+  return task
+
 cluster = require 'cluster'
 numCPUs = require('os').cpus().length

@@ -98,7 +104,7 @@
       body = request.getBody()
       urlParts = url.parse(request.url, true)
       if PRESETS_REGEX.test(urlParts.pathname)
-        presetName = PRESETS_REGEX.exec(urlParts.pathname)[1]
+        presetName = querystring.unescape(PRESETS_REGEX.exec(urlParts.pathname)[1])
         if request.method == 'PUT'
           preset = JSON.parse(body)
           preset._id = presetName
@@ -129,8 +135,8 @@
           response.end('405 Method Not Allowed')
       else if TAGS_REGEX.test(urlParts.pathname)
         r = TAGS_REGEX.exec(urlParts.pathname)
-        deviceId = r[1]
-        tag = r[2]
+        deviceId = querystring.unescape(r[1])
+        tag = querystring.unescape(r[2])
         if request.method == 'POST'
           db.devicesCollection.update({'_id' : deviceId}, {'$addToSet' : {'_tags' : tag}}, {safe: true}, (err) ->
             if err
@@ -154,12 +160,10 @@
           response.end('405 Method Not Allowed')
       else if TASKS_REGEX.test(urlParts.pathname)
         if request.method == 'POST'
-          deviceId = TASKS_REGEX.exec(urlParts.pathname)[1]
+          deviceId = querystring.unescape(TASKS_REGEX.exec(urlParts.pathname)[1])
           if body
             # queue given task
-            task = JSON.parse(body)
-            task.device = deviceId
-            task.timestamp = mongodb.Timestamp()
+            task = sanitizeTask(deviceId, JSON.parse(body))

             db.tasksCollection.save(task, (err) ->
               if err
@@ -210,7 +214,7 @@
           response.writeHead 405, {'Allow': 'GET'}
           response.end('405 Method Not Allowed')
       else if FILES_REGEX.test(urlParts.pathname)
-        filename = FILES_REGEX.exec(urlParts.pathname)[1]
+        filename = querystring.unescape(FILES_REGEX.exec(urlParts.pathname)[1])
         if request.method == 'PUT'
           metadata = {
             SoftwareVersion : request.headers.softwareversion,


Deleting unused API file

Deleting unused API file

--- api.coffee
+++ api.coffee
@@ -1,171 +0,0 @@
-Q = require 'q'
-mongoose = require 'mongoose'
-mongo = mongoose.mongo
-ObjectID = mongo.ObjectID
-bus = require './bus'
-
-models = require('./db').models
-User = models.user
-Competition = models.competition
-Vote = models.vote
-CompetitionMembership = models.competition_membership
-CompetitionCandidate = models.competition_candidate
-
-# Provides a set of methods for voting data CRUD operations
-class InternalApi
-  constructor: (@db) ->
-    throw "must provide db connection" unless @db
-
-  createUser: (name) ->
-    console.log "[API] create user (name=#{name})"
-    Q.nbind(User.create, User)(name: name)
-    .then (val) ->
-      bus.emit('user:created', val)
-      val
-
-  listUsers: (userId) ->
-    # TODO: set .lean() on query for performance
-    console.log "[API] list users (userId=#{userId})"
-    if userId?
-      query = _id: ObjectID(userId)
-    Q(User.find(query).exec())
-
-  listCompetitionsByOwner: (ownerId, showClosed = false) ->
-    console.log "[API] list competitions by owner (ownerId=#{ownerId})"
-    if not ownerId?
-      deferred = Q.defer()
-      deferred.reject(
-        new Error("must provide ownerId to list competitions")
-      )
-      return deferred.promise
-
-    query = owner_id: ObjectID(ownerId)
-    unless showClosed
-      query.open = true
-    Q(Competition.find(query).exec())
-
-  listCompetitions: (competitionId, showClosed = false) ->
-    console.log "[API] list competitions (competitionId=#{competitionId}, showClosed=#{showClosed})"
-    query = {}
-    unless showClosed
-      query.open = true
-
-    if competitionId?
-      query._id = ObjectID(competitionId)
-
-    Q(Competition.find(query).exec())
-
-  listCompetitionsByMembership: (userId, showClosed = false) ->
-    console.log "[API] list competitions by membership (userId=#{userId}, showClosed=#{showClosed})"
-    Q(
-      CompetitionMembership.find(
-        user_id: userId
-      )
-      .exec()
-    )
-    .then((memberships) ->
-      if memberships?.length == 0
-        console.log "[API] found no competition memberships (userId=#{userId})"
-        return []
-
-      membershipIds = memberships.map((membership) ->
-        _id: membership.competition_id
-      )
-      anyCompetition = { $or: membershipIds }
-      dbQuery = Competition.find(anyCompetition)
-      unless showClosed
-        dbQuery.where('open').equals(true)
-      Q(dbQuery.exec())
-    )
-
-  listCompetitionMemberships: (competitionId) ->
-    console.log "[API] list competition memberships (competitionId=#{competitionId})"
-    if competitionId?
-      query = competition_id: ObjectID(competitionId)
-
-    Q(CompetitionMembership.find(query).exec())
-
-  joinCompetition: (userId, competitionId) ->
-    unless userId? and competitionId?
-      throw new Error("must provide userId and competitionId")
-
-    console.log "[API] join competition (userId=#{userId}, competitionId=#{competitionId})"
-    props =
-      user_id: userId
-      competition_id: competitionId
-
-    # "Upsert" membership document
-    Q(
-      CompetitionMembership.findOneAndUpdate(
-        props,
-        props,
-        { upsert: true }
-      ).exec()
-    ).then (competitionMembership) ->
-      bus.emit('competitionMembership:withdrawn', competitionMembership)
-      competitionMembership
-
-  withdrawFromCompetition: (userId, competitionId) ->
-    console.log "[API] withdraw from competition (userId=#{userId}, competitionId=#{competitionId})"
-    props =
-      user_id: userId
-      competition_id: competitionId
-    Q(
-      CompetitionMembership.remove(props).exec()
-    ).then ->
-      bus.emit('competitionMembership:created', props)
-      props
-
-  startCompetition: (ownerId, name) ->
-    console.log "[API] start competition (ownerId=#{ownerId}, name=#{name})"
-    Q.nbind(Competition.create, Competition)(
-        name: name
-        owner_id: ObjectID(ownerId)
-        open: true
-        type: 'multi'
-    )
-    .then (competition) ->
-      bus.emit('competition:started', competition)
-      competition
-
-  endCompetition: (competitionId) ->
-    console.log "[API] end competition (competitionId=#{competitionId})"
-    Q(
-      Competition.findOneAndUpdate(
-        { _id: ObjectID(competitionId) },
-        { open: false }
-      ).exec()
-    ).then (competition) ->
-      bus.emit('competition:ended', competition)
-      competition
-
-  voteFor: (userId, competitionId, choiceId) ->
-    console.log "[API] vote for (userId=#{userId}, competitionId=#{competitionId}, choiceId=#{choiceId})"
-    query =
-      user_id: userId
-    toUpdate =
-      choice_id: choiceId
-
-    # Only allows one vote per userId/competitionId pair
-    Q(
-      Vote.findOneAndUpdate(
-        query,
-        toUpdate,
-        { upsert: true }
-      ).exec()
-    )
-    .then (vote) ->
-      bus.emit('vote:cast', vote)
-      vote
-
-  withdrawVoteFor: (userId, competitionId, choiceId) ->
-    console.log "[API] withdraw vote for (userId=#{userId}, competitionId=#{competitionId}, choiceId=#{choiceId})"
-    props =
-      user_id: userId
-    Q(
-      Vote.remove(props).exec()
-    ).then ->
-      bus.emit('vote:withdrawn', props)
-      props
-
-module.exports = InternalApi


Added file extension for exports.

Added file extension for exports.

--- APIBlueprintGenerator.coffee
+++ APIBlueprintGenerator.coffee
@@ -96,4 +96,6 @@

 APIBlueprintGenerator.identifier = "io.apiary.PawExtensions.APIBlueprintGenerator"
 APIBlueprintGenerator.title = "API Blueprint Generator"
+APIBlueprintGenerator.fileExtension = "md"
+
 registerCodeGenerator APIBlueprintGenerator


Fix for github post receive hook

Fix for github post receive hook

--- app.coffee
+++ app.coffee
@@ -131,7 +131,10 @@
     # TODO: move this to GitNotificationService
     payloadForwarder = (mountPath, toMessageType, enableBacklog) =>
       @app.post mountPath, (req,res) ->
-        data = req.body
+        if req.body and req.body.payload
+          data = JSON.parse data.payload
+        else
+          data = req.body
         data.room = req.params.room if req.params.room
         return res.send(".room is required.") unless data.room
         io.sockets.in(data.room).emit( toMessageType, data)


Finding battles auto-validate teams.

Finding battles auto-validate teams.

--- app.coffee
+++ app.coffee
@@ -83,8 +83,10 @@

   'find battle': (user) ->
     # TODO: Take team from player.
-    # TODO: Validate team.
-    team = defaultTeam
+    team = defaultTeam.clone()
+    if !server.validateTeam(team)
+      user.send("error", "Your team is invalid!")
+      return
     server.queuePlayer(user, team)
     if server.queuedPlayers().length >= 2
       battles = server.beginBattles()
@@ -278,7 +280,7 @@
     ability: "Cute Charm"
   }
   {
-    name: "Haunter"
+    name: "Gengar"
     item: "Leftovers"
     moves: ["Substitute", "Disable", "Shadow Ball", "Focus Blast"]
     ability: "Levitate"


platform mapping

platform mapping

--- app.coffee
+++ app.coffee
@@ -42,9 +42,20 @@
 app.use express.static(path.join(__dirname, "/public"))

+platformMap = {
+  "darwin": "darwin_x64"
+  "osx": "darwin_x64"
+  "windows_64": "windows_x64"
+  "windows_32": "windows_ia32"
+  "linux_64": "linux-64"
+  "linux_32": "linux-32"
+}
+
 formatVersion = (v, platform) ->
   platform = platform || "darwin_x64"
   platform = platform.replace("win32", "windows")
+  platform = platformMap[platform]
+
   data = {
     version: v.version,
     url: v.urls[platform],


Use req.sendStatus instead of res.status(…).send

--- app.coffee
+++ app.coffee
@@ -178,11 +178,11 @@
 			# hash first because Woltlab Community Framework uses double salted hashes
 			bcrypt.hash reqAuth.pass, auth.users[reqAuth.name].passwd, (err, hash) ->
 				if err?
-					res.status(500).send '500 Internal Server Error'
+					res.sendStatus 500
 					return
 				bcrypt.compare hash, auth.users[reqAuth.name].passwd, (err, result) ->
 					if err?
-						res.status(500).send '500 Internal Server Error'
+						res.sendStatus 500
 						return
 					if result
 						callback reqAuth.name
@@ -428,7 +428,7 @@
 			return

 		unless req.accepts 'xml'
-			res.status(406).send 'Not Acceptable'
+			res.sendStatus 406
 			return

 		res.type 'xml'
@@ -547,12 +547,12 @@
 				if isAccessible username, req.params[0], req.params[1]
 					debug "#{username} downloaded #{req.params[0]}/#{req.params[1].toLowerCase()}"
 					logDownload req.params[0], req.params[1]
-					res.download "#{config.packageFolder}/#{req.params[0]}/#{req.params[1].toLowerCase()}.tar", "#{req.params[0]}_v#{req.params[1]}.tar", (err) -> res.status(404).send '404 Not Found' if err?
+					res.download "#{config.packageFolder}/#{req.params[0]}/#{req.params[1].toLowerCase()}.tar", "#{req.params[0]}_v#{req.params[1]}.tar", (err) -> res.sendStatus 404 if err?
 				else
 					debug "#{username} tried to download #{req.params[0]}/#{req.params[1].toLowerCase()}"
 					askForCredentials req, res
 			else
-				res.status(404).send '404 Not Found'
+				res.sendStatus 404

 	checkAuth req, res, callback

@@ -561,14 +561,14 @@
 	host = config.basePath ? "#{req.protocol}://#{req.header 'host'}"
 	versionNumber = packageList?[req.params[0]]?.packageinformation?.version[0]
 	unless versionNumber?
-		res.status(404).send '404 Not Found'
+		res.sendStatus 404
 		return
 	res.redirect 301, "#{host}/#{req.params[0]}/#{versionNumber.toLowerCase().replace (new RegExp ' ', 'g'), '_'}"

-app.get '/app.coffee', (req, res) -> res.type('txt').sendFile "#{__dirname}/app.coffee", (err) -> res.status(404).send '404' if err?
+app.get '/app.coffee', (req, res) -> res.type('txt').sendFile "#{__dirname}/app.coffee", (err) -> res.sendStatus 404 if err?

 # throw 404 on any unknown route
-app.all '*', (req, res) -> res.status(404).send '404 Not Found'
+app.all '*', (req, res) -> res.sendStatus 404

 # Creates watchers for every relevant file in the package folder
 do ->


Default passRedirects to true

Default passRedirects to true

--- app.coffee
+++ app.coffee
@@ -12,7 +12,7 @@
 DEFAULTS =
   port: 80
   httpsPort: 443
-  passRedirects: false
+  passRedirects: true

 commander
   .usage("Run it in the root of your project to start proxying requests as defined in the project's .vee file")


adds port conditionally.

adds port conditionally.

--- app.coffee
+++ app.coffee
@@ -26,4 +26,4 @@
 # Locations routes
 app.use '/locations', locationRoutes

-app.listen '3000'
+app.listen env.process.port || 3000


Fixing Enter on Expand view

Fixing Enter on Expand view

--- keymaps/atom-elixir.cson
+++ keymaps/atom-elixir.cson
@@ -29,3 +29,6 @@
   'tab': 'elixir-quoted-view:focus-next'
   'shift-tab': 'elixir-quoted-view:focus-previous'
   'enter': 'editor:newline'
+
+'.elixir-expand-view atom-text-editor[mini]':
+  'enter': 'editor:newline'


Static utility methods

Static utility methods

--- public/app/block.coffee
+++ public/app/block.coffee
@@ -47,4 +47,14 @@

             for y in [0..@size.height]
                 for x in [0..@size.width]
-                    add_block(x, y)+                    add_block(x, y)
+
+
+        ## Static utility
+        @world_to_block: (x, y) ->
+            [w, h] = [@prototype.size.width, @prototype.size.height]
+            [NaN, NaN]
+
+        @block_to_world: (x, y) ->
+            [w, h] = [@prototype.size.width, @prototype.size.height]
+            [NaN, NaN]


Sort out persistence

Sort out persistence

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -221,6 +221,7 @@
       linking.unlinkItems toRemove, lookup
       deleteMember.call(this, member, callback).then((result) ->
         sdk.loadTeamFees(member.teamId)
+        sdk.loadStatisticAggregates(member.teamId)
         return result
         ).fail((err) ->
           linking.linkItems toRemove, lookup
@@ -335,10 +336,13 @@
   # Update member statistics when saving statisticData
   wrapSave sdk, 'bulkSaveStatisticData', (templates) ->
     if templates[0]? and templates[0].memberId?
-      sdk.loadMemberStatistics teamId: templates[0].teamId
+      teamId = templates[0].teamId
+      sdk.loadMemberStatistics teamId: teamId
+      sdk.loadStatisticAggregates teamId: teamId

   wrapSave sdk, 'saveStatisticDatum', (statisticDatum) ->
     sdk.loadMemberStatistics statisticId: statisticDatum.statisticId
+    sdk.loadStatisticAggregates teamId: statisticDatum.teamId

   # Remove deleted member statisticData when using bulk delete command
   wrapMethod sdk, 'bulkDeleteStatisticData', (bulkDeleteStatisticData) ->
@@ -349,8 +353,11 @@

       linking.unlinkItems toRemove, lookup

-      bulkDeleteStatisticData.call(this, member, event).then(->
-        sdk.loadMemberStatistics(teamId: member.teamId)
+      bulkDeleteStatisticData.call(this, member, event).then((result) ->
+        promises.when(
+          sdk.loadMemberStatistics(teamId: member.teamId)
+          sdk.loadStatisticAggregates(teamId: member.teamId)
+        ).then -> result
       ).fail((err) ->
         linking.linkItems toRemove, lookup
         err


add in verify email and callback

add in verify email and callback

--- shared/router.coffee
+++ shared/router.coffee
@@ -74,19 +74,17 @@

   # TEP:  Add for it seems the normal URL gets swallowed
-  @route 'verifyEmail',
+  @route 'entryVerifyEmail',
     path: 'verify-email/:token'
     onBeforeAction: (pause) ->
-      console.log("verifyEmail")
       try
         Accounts.verifyEmail @params.token, ->
-          console.log("Verify Email Run")
+          console.log("Email Verified")
           AccountsEntry?.settings?.verifyEmailCallback?()
       catch e
         console.log("Email verify error", e)
         AccountsEntry?.settings?.verifyEmailCallback?(e)
       if AccountsEntry?.settings?.homeRoute?
-        console.log('verifyEmail go home')
         Router.go AccountsEntry.settings.homeRoute
       pause()


small fix

small fix

--- lib/beer_can/rule.coffee
+++ lib/beer_can/rule.coffee
@@ -45,9 +45,9 @@
     @subjects.includes('all') || @subjects.includes(subject) || matchesSubjectClass(subject)
   end

+  # TODO: Fix!
   matchesSubjectClass -> (subject)
-    @subjects.matchesAny -> (sub)
-      # TODO: Fix!
+    @subjects.matchesAny -> (sub)
       a = sub.kind_of(Module) && (subject.kind_of?(sub)
       b = subject.class.to_s == sub.to_s
       c = subject.kind_of?(Module) && subject.ancestors.include?(sub))


regenerate command-table.coffee

regenerate command-table.coffee

--- lib/command-table.coffee
+++ lib/command-table.coffee
@@ -607,6 +607,32 @@
   file: "./motion"
   commandName: "vim-mode-plus:move-to-last-character-of-line"
   commandScope: "atom-text-editor"
+MoveToLastNonblankCharacterOfLineAndDown:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-last-nonblank-character-of-line-and-down"
+  commandScope: "atom-text-editor"
+MoveToFirstCharacterOfLine:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-first-character-of-line"
+  commandScope: "atom-text-editor"
+MoveToFirstCharacterOfLineUp:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-first-character-of-line-up"
+  commandScope: "atom-text-editor"
+MoveToFirstCharacterOfLineDown:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-first-character-of-line-down"
+  commandScope: "atom-text-editor"
+MoveToFirstCharacterOfLineAndDown:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-first-character-of-line-and-down"
+  commandScope: "atom-text-editor"
+MoveToFirstLine:
+  file: "./motion"
+  commandName: "vim-mode-plus:move-to-first-line"
+  commandScope: "atom-text-editor"
+MoveToScreenColumn:
+  file: "./motion"
 MoveToBeginningOfScreenLine:
   file: "./motion"
   commandName: "vim-mode-plus:move-to-beginning-of-screen-line"
@@ -618,30 +644,6 @@
 MoveToLastCharacterOfScreenLine:
   file: "./motion"
   commandName: "vim-mode-plus:move-to-last-character-of-screen-line"
-  commandScope: "atom-text-editor"
-MoveToLastNonblankCharacterOfLineAndDown:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-last-nonblank-character-of-line-and-down"
-  commandScope: "atom-text-editor"
-MoveToFirstCharacterOfLine:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-first-character-of-line"
-  commandScope: "atom-text-editor"
-MoveToFirstCharacterOfLineUp:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-first-character-of-line-up"
-  commandScope: "atom-text-editor"
-MoveToFirstCharacterOfLineDown:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-first-character-of-line-down"
-  commandScope: "atom-text-editor"
-MoveToFirstCharacterOfLineAndDown:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-first-character-of-line-and-down"
-  commandScope: "atom-text-editor"
-MoveToFirstLine:
-  file: "./motion"
-  commandName: "vim-mode-plus:move-to-first-line"
   commandScope: "atom-text-editor"
 MoveToLastLine:
   file: "./motion"


Simplify editor-linter

Simplify editor-linter

* Moving `catch` up the promise chain removes one logical branch. there doesn't
  appear to be any functional difference between deleting the previous results
  and overwriting them with an empty array. It's what would happen if there were
  no errors.
* replacing the `progress` getter/setter with `_promiseLock` makes it clear
  that this is a private function and simplifies the process of setting and
  releasing the lock

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -9,6 +9,10 @@
       throw new Error('Missing "type". found keys', Object.keys(r))

   return results
+
+showError = (error) ->
+  atom.notifications.addError "#{error.message}", {detail: error.stack, dismissable: true}
+  return []

 class EditorLinter
   constructor: (@linter, @editor) ->
@@ -27,19 +31,19 @@
     )

   lint: (onChange) ->
-    return if @progress onChange
     return if @editor isnt @linter.activeEditor
-    @progress onChange, true
-    @lint true unless onChange

-    scopes = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition()).scopes
-    scopes.push '*' # Ensure that everything can be linted with a wildcard.
-    promises = @lintResults onChange, scopes
-    Promise.all(promises).then =>
-      @progress onChange, false
-    .catch ->
-      console.error arguments[0].stack
-      @progress onChange, false
+    # When linting is triggered by a save we need to also run back through and
+    # run the linters that trigger on the fly.
+    if not onChange
+      @lint true
+
+    @_promiseLock(onChange, =>
+      scopes = @editor.scopeDescriptorForBufferPosition(@editor.getCursorBufferPosition()).scopes
+      scopes.push '*' # Ensure that everything can be linted with a wildcard.
+      return Promise.all(@lintResults(onChange, scopes))
+    )
+    undefined

   lintResults: (onChange, scopes) ->
     return @linter.linters.map (linter) =>
@@ -52,29 +56,23 @@
         # catch any exceptions thrown by `linter.lint`
         resolve(linter.lint(@editor, @buffer))
       ).then(validateResults)
+      .catch(showError)
       .then((results) =>
         if linter.scope is 'project' then @linter.messagesProject.set linter, results
         else @messages.set linter, results
-      ).catch((error) =>
-        if linter.scope is 'project' then @linter.messagesProject.delete linter
-        else @messages.delete linter
-        atom.notifications.addError "#{error.message}", {detail: error.stack, dismissable: true}
-      ).then(=> # finally
+
         @emitter.emit 'did-update'
         @linter.view.render() if @editor is @linter.activeEditor
       )

-  progress: (onChange, newValue) ->
-    if typeof newValue is 'undefined'
-      if onChange
-        return @inProgressFly
-      else
-        return @inProgress
-    else
-      if onChange
-        @inProgressFly = newValue
-      else
-        @inProgress = newValue
+  _promiseLock: (onChange, callback) ->
+    key = if onChange then 'inProgressFly' else 'inProgress'
+
+    return if @[key]
+    @[key] = true
+    callback().catch(showError).then( =>
+      @[key] = false
+    )

   destroy: ->
     @emitter.emit 'did-destroy'


reset width on collapse

reset width on collapse

--- lib/editor/result.coffee
+++ lib/editor/result.coffee
@@ -86,6 +86,12 @@
     @expanded = false
     @expMarker?.destroy()
     @decorateMarker()
+    setTimeout (=>
+      elRect = el.getBoundingClientRect()
+      w = elRect.width + elRect.left - 40 -
+          @view.parentElement.getBoundingClientRect().left
+      if w < 100 then w = 100
+      @view.style.maxWidth = w + 'px'), 50

   createView: (opts) ->
     {content, fade, loading} = opts


fix resolveConfigPath

fix resolveConfigPath

--- lib/flex-tool-bar.coffee
+++ lib/flex-tool-bar.coffee
@@ -137,18 +137,20 @@
       @reloadToolbar()

   resolveConfigPath: (configFilePath = atom.config.get('flex-tool-bar.toolBarConfigurationFilePath'), createIfNotFound = true) ->
-    unless fs.isFileSync(configFilePath)
-      configFilePath = fs.resolve configFilePath, 'toolbar', VALID_EXTENSIONS
-
-    if configFilePath
-      @configFilePath = configFilePath
+    configPath = configFilePath
+    unless fs.isFileSync(configPath)
+      configPath = fs.resolve configPath, 'toolbar', VALID_EXTENSIONS
+
+    if configPath
+      @configFilePath = configPath
       return true
     else if createIfNotFound
-      exists = fs.existsSync(configFilePath)
-      if (exists and fs.isDirectorySync(configFilePath)) or (not exists and path.extname(configFilePath) not in VALID_EXTENSIONS)
-        configFilePath = path.resolve configFilePath, 'toolbar.cson'
-      if @createConfig configFilePath
-        @configFilePath = configFilePath
+      configPath = configFilePath
+      exists = fs.existsSync(configPath)
+      if (exists and fs.isDirectorySync(configPath)) or (not exists and path.extname(configPath) not in VALID_EXTENSIONS)
+        configPath = path.resolve configPath, 'toolbar.cson'
+      if @createConfig configPath
+        @configFilePath = configPath
         return true

     return false


Fix case sensitive issue with q library

Fix case sensitive issue with q library

--- lib/js/steambuddy.coffee
+++ lib/js/steambuddy.coffee
@@ -5,7 +5,7 @@
 parser = new Parser();
 SlackIntegration = require('./integrations/slack_integration.js')
 User = require('./user.js')
-$q = require('Q')
+$q = require('q')
 async = require('async')

 PLAYER_SUMMARY_URL = process.env.PLAYER_SUMMARY_URL


fixed a typo

fixed a typo

--- lib/linter-flake8.coffee
+++ lib/linter-flake8.coffee
@@ -35,7 +35,7 @@
   updateCommand: ->
     cmd = 'flake8'
     maxLineLength = atom.config.get 'linter-flake8.maxLineLength'
-    errorCodes = atom.config.get 'linter-pep8.ignoreErrorCodes'
+    errorCodes = atom.config.get 'linter-flake8.ignoreErrorCodes'

     if maxLineLength
       cmd = "#{cmd} --max-line-length=#{maxLineLength}"


Fix and close #45

Fix and close #45

--- lib/linter-pylama.coffee
+++ lib/linter-pylama.coffee
@@ -85,9 +85,10 @@
     if projectPath and projectPath not in pythonPath
       pythonPath.push projectPath

-    processPath = path.normalize env.PWD
-    if processPath and processPath not in pythonPath
-      pythonPath.push processPath
+    if env.PWD
+      processPath = path.normalize env.PWD
+      if processPath and processPath not in pythonPath
+        pythonPath.push processPath

     env.PYTHONPATH = pythonPath.join path.delimiter
     env


fix _ file

fix _ file

--- lib/router/router.coffee
+++ lib/router/router.coffee
@@ -21,7 +21,9 @@
                 if fs.existsSync file
                     routes=["#{path}/*"]
                     routes.push path if path isnt ''
-                    @app.all routes, require file
+                    str=routes.join('|')
+                    str.replace /[\-\[\]\/\{\}\(\)\+\?\.\\\^\$]/g, "\\$&"
+                    @app.all str, require file

             for file in files
                 if file not in ["_.js","_.coffee"] and not (path is '' and file in ["index.coffee","index.js"])


create package panels

create package panels

--- lib/settings-view.coffee
+++ lib/settings-view.coffee
@@ -33,6 +33,10 @@
     process.nextTick => @initializePanels()

   handlePackageEvents: ->
+    @subscribe @packageManager, 'package-installed theme-installed', ({name}) =>
+      if pack = atom.packages.getLoadedPackage(name)
+        @addPackagePanel(pack)
+
     @subscribe @packageManager, 'package-uninstalled', ({name}) =>
       @removePanel(name)
       @showPanel('Packages') if name is @activePanelName
@@ -57,6 +61,7 @@
     @addCorePanel 'Installed Packages', 'package', => new InstalledPackagesPanel(@packageManager)
     @addCorePanel 'Themes', 'paintcan', => new ThemesPanel(@packageManager)

+    @addPackagePanel(pack) for pack in @getPackages()
     @showPanel(@panelToShow) if @panelToShow
     @showPanel('Settings') unless @activePanelName
     @sidebar.width(@sidebar.width()) if @isOnDom()
@@ -103,6 +108,10 @@
         @a class: "icon icon-#{iconName}", name
     @menuSeparator.before(panelMenuItem)
     @addPanel(name, panelMenuItem, panel)
+
+  addPackagePanel: (pack) ->
+    @addPanel pack.name, null, =>
+      new InstalledPackageView(pack, @packageManager)

   addPanel: (name, panelMenuItem, panelCreateCallback) ->
     @panelCreateCallbacks ?= {}


Sends a message when trying to join a message that doesn't exist

--- packages/rocketchat-slashcommands-join/join.coffee
+++ packages/rocketchat-slashcommands-join/join.coffee
@@ -9,7 +9,7 @@
 		params: '#channel'
 else
 	class Join
-		constructor: (command, params) ->
+		constructor: (command, params, item) ->
 			if command isnt 'join' or not Match.test params, String
 				return

@@ -23,6 +23,12 @@
 			room = RocketChat.models.Rooms.findOneByNameAndTypeNotContainigUsername(channel, 'c', user.username)

 			if not room?
+				RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
+					_id: Random.id()
+					rid: item.rid
+					ts: new Date
+					msg: 'The channel `#' + channel + '` does not exist.'#TODO: Make this a language setting
+				}
 				return

 			Meteor.call 'joinRoom', room._id


Refactored some more

Refactored some more

--- src/lazy-runtime/LazyRuntime.coffee
+++ src/lazy-runtime/LazyRuntime.coffee
@@ -125,23 +125,18 @@

 LazyRuntime::_turn = (pointer, char) ->
-	if char == '|'
-		if @programState.pop() == 0
-			pointer.turn 'v'
-		else
-			pointer.turn '^'
-		pointer.advance()
-	else if char == '_'
-		if @programState.pop() == 0
-			pointer.turn '>'
-		else
-			pointer.turn '<'
-		pointer.advance()
-	else if char == '?'
-		pointer.turn '^<v>'[Math.random() * 4 | 0]
-		pointer.advance()
-	else
-		pointer.turn char
+	dir = switch char
+		when '|'
+			if @programState.pop() then '^' else 'v'
+		when '_'
+			if @programState.pop() then '<' else '>'
+		when '?'
+			'^<v>'[Math.random() * 4 | 0]
+
+	pointer.turn dir
+	pointer.advance()
+
+	return

 LazyRuntime::execute = (@playfield, options, input = []) ->


Fix content size bug

Fix content size bug

--- framer/Components/ScrollComponent.coffee
+++ framer/Components/ScrollComponent.coffee
@@ -94,9 +94,12 @@
 		# this for example to take scaling into account.

 		contentFrame = @content.contentFrame()
+
+		print "contentFrame", contentFrame
+
 		return size =
-			width:  Math.max(@width, contentFrame.width)
-			height: Math.max(@height, contentFrame.height)
+			width:  Math.max(@width,  contentFrame.x + contentFrame.width)
+			height: Math.max(@height, contentFrame.y + contentFrame.height)

 	setContentLayer: (layer) ->
@@ -314,21 +317,18 @@
 		# This function wraps the given layer into a scroll or page component

 		scroll = new @
-			backgroundColor: null
-		scroll.frame = layer.frame
+
+		# If no superlayer was set, we are going to assume
+		if layer.superLayer
+			scroll.frame = layer.superLayer.frame
+		else
+			scroll.width = Screen.width - scroll.x
+			scroll.height = Screen.height - scroll.y

 		layerIndex = layer.index
 		scroll.superLayer = layer.superLayer
 		scroll.index = layerIndex
-
-		# Correct the position for the scroll content
-
-		contentFrame = layer.contentFrame()
-		layer.width =  Math.max(layer.contentFrame().width,  layer.width) + contentFrame.x
-		layer.height = Math.max(layer.contentFrame().height, layer.height) + contentFrame.y
-		layer.x = 0
-		layer.y = 0
-
-		scroll.content.addSubLayer(layer)
+
+		layer.superLayer = scroll.content

 		return scroll


Port Subgraph tests

Port Subgraph tests

--- spec/Subgraph.coffee
+++ spec/Subgraph.coffee
@@ -1,25 +1,28 @@
 if typeof process isnt 'undefined' and process.execPath and process.execPath.match /node|iojs/
   chai = require 'chai' unless chai
-  subgraph = require '../src/components/Graph.coffee'
-  graph = require '../src/lib/Graph.coffee'
   noflo = require '../src/lib/NoFlo.coffee'
   path = require 'path'
   root = path.resolve __dirname, '../'
   urlPrefix = './'
 else
-  subgraph = require 'noflo/src/components/Graph.js'
-  graph = require 'noflo/src/lib/Graph.js'
-  noflo = require 'noflo/src/lib/NoFlo.js'
+  noflo = require 'noflo'
   root = 'noflo'
   urlPrefix = '/'

 describe 'Graph component', ->
   c = null
   g = null
-  beforeEach ->
-    c = subgraph.getComponent()
-    g = noflo.internalSocket.createSocket()
-    c.inPorts.graph.attach g
+  loader = null
+  before (done) ->
+    loader = new noflo.ComponentLoader root
+    loader.listComponents done
+  beforeEach (done) ->
+    loader.load 'Graph', (err, instance) ->
+      return done err if err
+      c = instance
+      g = noflo.internalSocket.createSocket()
+      c.inPorts.graph.attach g
+      done()

   class Split extends noflo.Component
     constructor: ->
@@ -49,8 +52,8 @@
       chai.expect(c.ready).to.be.true
     it 'should not contain a network', ->
       chai.expect(c.network).to.be.null
-    it 'should not have a baseDir', ->
-      chai.expect(c.baseDir).to.be.null
+    it 'should have a baseDir', ->
+      chai.expect(c.baseDir).to.equal root
     it 'should only have the graph inport', ->
       chai.expect(c.inPorts.ports).to.have.keys ['graph']
       chai.expect(c.outPorts.ports).to.be.empty
@@ -200,7 +203,7 @@
         ]

   describe 'with a Graph instance', ->
-    gr = new graph.Graph 'Hello, world'
+    gr = new noflo.Graph 'Hello, world'
     gr.baseDir = root
     gr.addNode 'Split', 'Split'
     gr.addNode 'Merge', 'Merge'


Super view tests and general test clean up

--- spec/compiler.coffee
+++ spec/compiler.coffee
@@ -10,7 +10,7 @@

   describe '@horizontal [#b1][#b2]; // simple connection ', ->
     source = """
-    @-gss-horizontal [#b1][#b2];
+            @horizontal [#b1][#b2];
     """
     result = null
     expect =
@@ -30,7 +30,7 @@

   describe '@vertical [#b1]-[#b2]-[#b3]-[#b4]-[#b5]; // connection chain with implicit hgaps ', ->
     source = """
-    @-gss-vertical [#b1]-[#b2]-[#b3];
+            @vertical [#b1]-[#b2]-[#b3];
     """
     result = null
     expect =
@@ -44,11 +44,12 @@
         ]
       ]
     it '/ the result should match the expectation', ->
+      result = parser.parse source
       chai.expect(result).to.eql expect

-  describe '@horizontal [#b1]-100-[#b2]-[#b3] gap(1); // explicit gaps ', ->
+  describe '@horizontal [#b1]-100-[#b2]-8-[#b3]; // explicit gaps ', ->
     source = """
-    @-gss-h [#b1]-100-[#b2]-[#b3] gap(1);
+            @horizontal [#b1]-100-[#b2]-8-[#b3];
     """
     result = null
     expect =
@@ -56,15 +57,16 @@
         [
           'ccss'
           "#b1[right] + 100 == #b2[left]"
-          "#b2[right] + 1 == #b3[left]"
+          "#b2[right] + 8 == #b3[left]"
         ]
       ]
     it '/ the result should match the expectation', ->
+      result = parser.parse source
       chai.expect(result).to.eql expect

   describe '@horizontal [#b1]~[#b2]; // simple cushion ', ->
     source = """
-    @-gss-h [#b1]~[#b2];
+            @horizontal [#b1]~[#b2];
     """
     result = null
     expect =
@@ -75,11 +77,12 @@
         ]
       ]
     it '/ the result should match the expectation', ->
+      result = parser.parse source
       chai.expect(result).to.eql expect

   describe '@horizontal [#b1]~-~[#b2]~100~[#b3]; // cushions w/ gaps ', ->
     source = """
-    @-gss-h [#b1]~-~[#b2]~100~[#b3];
+            @horizontal [#b1]~-~[#b2]~100~[#b3];
     """
     result = null
     expect =
@@ -91,5 +94,81 @@
         ]
       ]
     it '/ the result should match the expectation', ->
+      result = parser.parse source
       chai.expect(result).to.eql expect
+
+  describe '@vertical |[#sub]| in(#parent); // flush with super view', ->
+    source = """
+            @vertical |[#sub]| in(#parent);
+    """
+    result = null
+    expect =
+      [
+        [
+          'ccss'
+          '#parent[top] == #sub[top]'
+          '#sub[bottom] == #parent[bottom]'
+        ]
+      ]
+
+    it '/ the result should match the expectation', ->
+      result = parser.parse source
+      chai.expect(result).to.eql expect
+
+  describe '@horizontal |-[#sub1]-[#sub2]-| in(#parent); // super view with standard gaps', ->
+    source = """
+            @horizontal |-[#sub1]-[#sub2]-| in(#parent);
+    """
+    result = null
+    expect =
+      [
+        [
+          'ccss'
+          '#parent[left] + [hgap] == #sub1[left]'
+          '#sub1[right] + [hgap] == #sub2[left]'
+          '#sub2[right] + [hgap] == #parent[right]'
+        ]
+      ]
+
+    it '/ the result should match the expectation', ->
+      result = parser.parse source
+      chai.expect(result).to.eql expect
+
+  describe '@horizontal |-1-[#sub]-2-| in(#parent); // super view with explicit gaps', ->
+    source = """
+            @horizontal |-1-[#sub]-2-| in(#parent);
+    """
+    result = null
+    expect =
+      [
+        [
+          'ccss'
+          '#parent[left] + 1 == #sub[left]'
+          '#sub[right] + 2 == #parent[right]'
+        ]
+      ]
+
+    it '/ the result should match the expectation', ->
+      result = parser.parse source
+      chai.expect(result).to.eql expect
+
+  describe '@horizontal |~[#sub]~2~| in(#parent); // super view with cushions', ->
+    source = """
+            @horizontal |~[#sub]~2~| in(#parent);
+    """
+    result = null
+    expect =
+      [
+        [
+          'ccss'
+          '#parent[left] <= #sub[left]'
+          '#sub[right] + 2 <= #parent[right]'
+        ]
+      ]
+
+    it '/ the result should match the expectation', ->
+      result = parser.parse source
+      chai.expect(result).to.eql expect
+
+


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -5,7 +5,7 @@
     engine.add 'TextEffect'
       x: 50
       y: 50
-      velocity:
+

 @name TextEffect


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_effect.coffee
+++ src/text_effect.coffee
@@ -36,7 +36,7 @@
   self = GameObject(I)

   self.bind "update", ->
-    I.alpha = 1 - (I.age / I.duration)
+    I.alpha = (1 - (I.age / I.duration)).clamp(0, 1)

   self.unbind "draw"
   self.bind "draw", (canvas) ->


added todo of idea commands for the organization

--- src/organizations.coffee
+++ src/organizations.coffee
@@ -21,6 +21,14 @@
 github = new GithubiApi version: "3.0.0", debug: true, headers: Accept: "application/vnd.github.moondragon+json"

 admins = []
+
+
+# TODO - ideal commands
+# - base
+# - create (team|repo) with (extras)
+# - list (teams|members|repos) of (extra)
+# - move (member|members|repos|repo) (to|from) (team) to (team)
+# - remove (member|team) from (team|organization)


Add ERROR_ACCESS_DENIED into src/win32/error.coffee

--- src/win32/error.coffee
+++ src/win32/error.coffee
@@ -5,6 +5,7 @@
   @ERROR_CODES:
     0: 'ERROR_SUCCESS'
     2: 'ERROR_FILE_NOT_FOUND'
+    5: 'ERROR_ACCESS_DENIED'
     6: 'ERROR_INVALID_HANDLE'
     14: 'ERROR_OUTOFMEMORY'
     87: 'ERROR_INVALID_PARAMETER'


fixed viewAppended

fixed viewAppended

--- client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemDiscussion.coffee
+++ client/landing/app/Applications/Activity.kdapplication/ListItems/ActivityListItemDiscussion.coffee
@@ -35,30 +35,12 @@
         click:->
           appManager.tell "Activity", "createContentDisplay", data

-
   viewAppended:()->
-    return if @getData().constructor is bongo.api.CDiscussion
+    return if @getData().constructor is bongo.api.CDiscussionActivity
     super()
     @setTemplate @pistachio()
     @template.update()

-    # if @getData().repliesCount? and @getData().repliesCount > 0
-    #   opinionController = @opinionBox.opinionController
-    #   opinionController.fetchRelativeOpinions 3, 0, (err, opinions)=>
-    #     for opinion in opinions
-    #       @opinionBox.opinionList.addItem opinion, null
-    #     @opinionBox.opinionList.emit "RelativeOpinionsWereAdded"
-
-    # if @getData().repliesCount > 3
-    #   @opinionBox.addSubView test = new KDCustomHTMLView
-    #     title : "show more"
-    #     tagName : "a"
-    #     cssClass : "activity-opinion-more"
-    #     attributes :
-    #       href: "#"
-    #     partial : "View all the opinions ("+(@getData().repliesCount-3)+" more)"
-    #     click:->
-    #       log "pew"

   click:(event)->
     if $(event.target).is("[data-paths~=title]")