activity/flux: remove helper functions from messages store

--- client/activity/lib/flux/stores/messagesstore.coffee
+++ client/activity/lib/flux/stores/messagesstore.coffee
@@ -4,8 +4,6 @@
 KodingFluxStore      = require 'app/flux/store'
 { createFakeMessage } = require '../helper'

-IS_LIKED_KEYPATH = ['interactions', 'like', 'isInteracted']
-LIKE_INTERACTION_KEYPATH = ['interactions', 'like']

 ###*
  * Immutable version of a social message. see toImmutable util.
@@ -261,111 +259,3 @@
     return nextState

-###*
- * Adds given message to given messages map.
- *
- * @param {MessageCollection} messages
- * @param {ImmutableSocialMessage} message
- * @return {MessageCollection} _messages
-###
-addMessage = (messages, message) -> messages.set message.get('id'), message
-
-
-###*
- * It removes message with given messageId if exists.
- *
- * @param {MessageCollection} messages
- * @param {string} messageId
- * @return {MessageCollection} _messages
-###
-removeMessage = (messages, messageId) ->
-
-  if messages.has messageId
-  then messages.remove messageId
-  else messages
-
-
-###*
- * It sets `__removed` flag to message with given messageId.
- *
- * @param {MessageCollection} messages
- * @param {string} messageId
- * @return {MessageCollection} _messages
-###
-markMessageRemoved = (messages, messageId) ->
-
-  return messages  unless messages.has messageId
-
-  messages.update messageId, (message) -> message.set '__removed', yes
-
-
-###*
- * It removes `__removed` flag to message with given messageId.
- *
- * @param {MessageCollection} messages
- * @param {string} messageId
- * @return {MessageCollection} _messages
-###
-unmarkMessageRemoved = (messages, messageId) ->
-
-  if messages.hasIn [messageId, '__removed']
-  then messages.deleteIn [messageId, '__removed']
-  else messages
-
-
-###*
- * Sets isLiked status of given message to given state.
- *
- * @param {ImmutableSocialMessage} message
- * @param {boolean} state
- * @return {ImmutableSocialMessage} _message
-###
-setIsLiked = (message, state) -> message.setIn IS_LIKED_KEYPATH, state
-
-
-###*
- * Adds an actor (given userId) to like interaction of given message.
- *
- * @param {ImmutableSocialMessage} message
- * @param {boolean} state
- * @return {ImmutableSocialMessage} _message
-###
-addLiker = (message, userId) ->
-
-  message.updateIn LIKE_INTERACTION_KEYPATH, (likeInteraction) ->
-    likeInteraction
-      # add logged in user to actors preview.
-      .update 'actorsPreview', (preview) -> preview.unshift userId
-      # increase actors count by one.
-      .update 'actorsCount', (count) -> count + 1
-
-
-###*
- * Removes actor (given userId) from like interaction of given message.
- *
- * @param {ImmutableSocialMessage} message
- * @param {boolean} state
- * @return {ImmutableSocialMessage} _message
-###
-removeLiker = (message, userId) ->
-
-  message.updateIn LIKE_INTERACTION_KEYPATH, (likeInteraction) ->
-    likeInteraction
-      # filter out given userId from actorsPreview list.
-      .update 'actorsPreview', (preview) -> preview.filterNot (id) -> id is userId
-      # decrease actors count by one.
-      .update 'actorsCount', (count) -> count - 1
-
-
-###*
- * Removes fake message.
- *
- * @param {MessageCollection} messages
- * @param {string} clientRequestId
- * @return {MessageCollection} _messages
-###
-removeFakeMessage = (messages, clientRequestId) ->
-
-  messages.remove clientRequestId
-
-


DefineStackView: replace items based on the new pattern

--- client/admin/lib/views/stacks/definestackview.coffee
+++ client/admin/lib/views/stacks/definestackview.coffee
@@ -305,8 +305,8 @@
       for type, data of requiredData
         for field in data
           if content = jspath.getAt availableData[type], field
-            template = template.replace \
-              (new RegExp "{{#{type} #{field}}}", 'g'), content
+            search   = ///\${var.koding_#{type}_#{field}}///g
+            template = template.replace search, content
           else
             errors.push "Variable `#{field}` not found in `#{type}` data."


hide loader after hiding landing page

hide loader after hiding landing page

--- client/app/MainApp/lazy/staticgroupcontroller.coffee
+++ client/app/MainApp/lazy/staticgroupcontroller.coffee
@@ -273,6 +273,7 @@
       @lazyDomController.openPath "/#{@groupEntryPoint}/Activity"
     else
       @lazyDomController.hideLandingPage()
+    KD.utils.wait 600, -> $('#main-koding-loader').hide()

   decorateGuestStatus:->


Make sure cache views are set before onShow

--- core/app/backbone/factlink/caching_controller.coffee
+++ core/app/backbone/factlink/caching_controller.coffee
@@ -1,11 +1,11 @@
 class Backbone.Factlink.CachingController extends Backbone.Factlink.BaseController
   openController: ->
+    @cached_views = new Backbone.Factlink.DetachedViewCache
     super
-    @cached_views = new Backbone.Factlink.DetachedViewCache

   closeController: ->
+    @cached_views.cleanup()
     super
-    @cached_views.cleanup()

   onAction: -> @unbindFrom @permalink_event if @permalink_event?


Disable pistachios again

Disable pistachios again

--- config/main.rc.coffee
+++ config/main.rc.coffee
@@ -85,7 +85,7 @@
   presence      :
     exchange    : 'services-presence'
   client        :
-    pistachios  : yes
+    pistachios  : no
     version     : version
     minify      : yes
     watch       : no


.

Former-commit-id: d8f9d559e1da4360eab23f4ae710216d2e45f41c [formerly d8d2682c79abf95391d568dd0e678101f9108775] [formerly ad200d83f33d5d75ebf44a0bf6b4e52b49792642 [formerly 0bde9e5b77d7fd95ddeae58b84fa49b9dfb2874b]] [formerly 36ba8d31d12d42dff33364ca58bc1567e6b900fd [formerly 7fbbbface647b7df865985033a18c9917c4c75f1] [formerly 1b9f38210b8cd3a2c7aa7d38f7cffa75a10a6720 [formerly 1b9f38210b8cd3a2c7aa7d38f7cffa75a10a6720 [formerly 613f83726755ae894e78be77fffd9c39301789aa]]]]
Former-commit-id: 2b5dbde60e5db860eb64cf9879ced7f2c1f506c8 [formerly 3a57caef6b5c69119cf141288e1883d7ee6cb289] [formerly e92fdbd502cc562c62310e3c31c07bca8a58c700 [formerly 03ded5aa94bb7f25061724a8ee10c72b126dd81b]]
Former-commit-id: bcb40320952c06c44484f6e6629c98748b572641 [formerly b64915903368e86db7fad910e76d4a27d206d06e]
Former-commit-id: e8faaf8c931133300c1bf79aa7792d46124cbc4a
Former-commit-id: f340fda29eb7f30336424e55f1c0c834adb3afa0

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -117,7 +117,7 @@
     skip_element = (behind_el && $(behind_el).has($(el)).length > 0) ||
                     (!is_image && rgb.a == 0)

-    console.log 'checking ', el
+    console.log 'checking ', el, skip_element, is_image

     if skip_element
       hidden_els.push [el, el.style.visibility]


Disable built-in atom spell-check package

For now. Waiting for it to support French.

--- .config/atom/config.cson
+++ .config/atom/config.cson
@@ -14,6 +14,7 @@
       "package-generator"
       "symbols-view"
       "language-toml"
+      "spell-check"
     ]
     packagesWithKeymapsDisabled: [
       "refactor"


atom: update config

atom: update config

--- .atom/config.cson
+++ .atom/config.cson
@@ -75,13 +75,13 @@
     showOnStartup: false
   whitespace:
     ignoreWhitespaceOnCurrentLine: false
+".angular.html.text":
+  editor:
+    preferredLineLength: 100
 ".diff.source":
   editor:
     preferredLineLength: 100
 ".source.ts":
-  editor:
-    preferredLineLength: 100
-".angular.html.text":
   editor:
     preferredLineLength: 100
 ".source.ts.tsx":
@@ -90,9 +90,3 @@
 ".source.tsx":
   editor:
     preferredLineLength: 100
-".source.ts.tsx":
-  editor:
-    preferredLineLength: 100
-".source.tsx":
-  editor:
-    preferredLineLength: 100


atom-pdf-view: init config

atom-pdf-view: init config

--- .atom/config.cson
+++ .atom/config.cson
@@ -36,6 +36,7 @@
   "linter-python-pep257": {}
   "markdown-preview":
     useGitHubStyle: true
+  "pdf-view": {}
   "tree-view":
     autoReveal: true
     focusOnReveal: false


atom-latex: uninstall instead of disable

(cherry picked from commit 6b7896982314e59cd18ea579558de8dc41f9f179)

--- .atom/config.cson
+++ .atom/config.cson
@@ -12,7 +12,6 @@
   core:
     disabledPackages: [
       "linter-python-pep257"
-      "latex"
       "linter-chktex"
       "linter-pep257"
       "spell-check"


Adds default values to Atom config file.

--- .atom/config.cson
+++ .atom/config.cson
@@ -1,22 +1,8 @@
 "*":
   "atom-ide-ui":
     use:
-      "atom-ide-busy-signal": "default"
-      "atom-ide-code-actions": "default"
-      "atom-ide-code-format": "default"
-      "atom-ide-code-highlight": "default"
-      "atom-ide-console": "default"
-      "atom-ide-datatip": "default"
       "atom-ide-debugger": "default"
-      "atom-ide-definitions": "default"
-      "atom-ide-diagnostics": "default"
-      "atom-ide-diagnostics-ui": "default"
-      "atom-ide-find-references": "default"
-      "atom-ide-global": "default"
-      "atom-ide-outline-view": "default"
-      "atom-ide-signature-help": "default"
       "atom-ide-terminal": "default"
-      hyperclick: "default"
   "atom-python-virtualenv": {}
   "autocomplete-python":
     useKite: false
@@ -55,6 +41,8 @@
       cursorlineDecorationsZIndex: 0
       "highlight-selected": true
       "highlight-selectedDecorationsZIndex": 0
+      "minimap-autohide": true
+      "minimap-autohideDecorationsZIndex": 0
       "minimap-autohider": true
       "minimap-autohiderDecorationsZIndex": 0
   "package-sync":


don't override VM.from_heroku anymore

don't override VM.from_heroku anymore

--- js/app.coffee
+++ js/app.coffee
@@ -13,7 +13,7 @@
     @ab = (new CI.ABTests(ab_test_definitions)).ab_tests
     @error_message = ko.observable(null)
     @turbo_mode = ko.observable(false)
-    @from_heroku = false#ko.observable(window.renderContext.from_heroku)
+    @from_heroku = ko.observable(window.renderContext.from_heroku)

     # inner
     @build = ko.observable()


Set a limit to the beatmap stats

Set a limit to the beatmap stats

--- resources/assets/coffee/react/_components/beatmap-basic-stats.coffee
+++ resources/assets/coffee/react/_components/beatmap-basic-stats.coffee
@@ -47,7 +47,7 @@
         if stat == 'total_length'
           formatDuration value
         else if value > 1000
-          osu.formatNumberSuffixed(value, 1)
+          if value >= Math.pow(10, 15) then '∞' else osu.formatNumberSuffixed(value, 1)
         else
           osu.formatNumber(value)


subtle bug in the order of the rewriter rules was causing some if/else chains to get confused by implicit indentation

--- rewriter.coffee
+++ rewriter.coffee
@@ -45,8 +45,8 @@
   this.remove_mid_expression_newlines()
   this.move_commas_outside_outdents()
   this.close_open_calls_and_indexes()
+  this.add_implicit_indentation()
   this.add_implicit_parentheses()
-  this.add_implicit_indentation()
   this.ensure_balance(BALANCED_PAIRS)
   this.rewrite_closing_parens()
   this.tokens


New implementation of Rewriter#addImplicitIndentation, using Rewriter#detectEnd

--- rewriter.coffee
+++ rewriter.coffee
@@ -58,8 +58,8 @@
     levels = 0
     loop
       token = @tokens[i]
-      return action token, i if levels is 0 and condition token, i
-      return action token, i - 1 if not token or levels < 0
+      return action.call this, token, i     if levels is 0 and condition.call this, token, i
+      return action.call this, token, i - 1 if not token or levels < 0
       levels += 1 if include EXPRESSION_START, token[0]
       levels -= 1 if include EXPRESSION_END, token[0]
       i += 1
@@ -135,11 +135,11 @@
         stack.push '{'
         idx = if before[0] is '@' then i - 2 else i - 1
         @tokens.splice idx, 0, ['{', '{', token[2]]
-        condition = (token, i) =>
+        condition = (token, i) ->
           [one, two, three] = @tokens.slice(i + 1, i + 4)
           ((token[0] in ['TERMINATOR', 'OUTDENT']) and not ((two and two[0] is ':') or (one and one[0] is '@' and three and three[0] is ':'))) or
             (token[0] is ',' and one and (one[0] not in ['IDENTIFIER', 'STRING', '@', 'TERMINATOR', 'OUTDENT']))
-        action = (token, i) =>
+        action = (token, i) ->
           @tokens.splice i, 0, ['}', '}', token[2]]
         @detectEnd i + 2, condition, action
         return 2
@@ -154,12 +154,12 @@
       if prev and prev.spaced and include(IMPLICIT_FUNC, prev[0]) and include(IMPLICIT_CALL, token[0]) and
           not (token[0] is '!' and (post[0] in ['IN', 'OF']))
         @tokens.splice i, 0, ['CALL_START', '(', token[2]]
-        condition = (token, i) =>
+        condition = (token, i) ->
           [before, prev] = @tokens.slice(i - 2, i + 1)
           (not token.generated and @tokens[i - 1][0] isnt ',' and include(IMPLICIT_END, token[0]) and
             not (token[0] is 'INDENT' and (include(IMPLICIT_BLOCK, prev[0]) or before[0] is 'CLASS'))) or
             token[0] is 'PROPERTY_ACCESS' and prev[0] is 'OUTDENT'
-        action = (token, i) =>
+        action = (token, i) ->
           idx = if token[0] is 'OUTDENT' then i + 1 else i
           @tokens.splice idx, 0, ['CALL_END', ')', token[2]]
         @detectEnd i + 1, condition, action
@@ -179,31 +179,22 @@
           (@tokens[i + 2][0] is 'TERMINATOR' or @tokens[i + 2][0] is 'FINALLY')
         @tokens.splice i + 2, 0, @indentation(token)...
         return 4
-      return 1 unless include(SINGLE_LINERS, token[0]) and
-        post[0] isnt 'INDENT' and
-        not (token[0] is 'ELSE' and post[0] is 'IF')
-      starter = token[0]
-      [indent, outdent] = @indentation token
-      indent.generated = outdent.generated = true
-      @tokens.splice i + 1, 0, indent
-      idx = i + 1
-      parens = 0
-      loop
-        idx += 1
-        tok = @tokens[idx]
-        pre = @tokens[idx - 1]
-        if (not tok or
-            (include(SINGLE_CLOSERS, tok[0]) and tok[1] isnt ';' and parens is 0) or
-            (tok[0] is ')' and parens is 0)) and
-            not (tok[0] is 'ELSE' and starter not in ['IF', 'THEN'])
-          insertion = if pre[0] is "," then idx - 1 else idx
-          @tokens.splice insertion, 0, outdent
-          break
-        parens += 1 if tok[0] is '('
-        parens -= 1 if tok[0] is ')'
-      return 1 unless token[0] is 'THEN'
-      @tokens.splice i, 1
-      return 0
+      if include(SINGLE_LINERS, token[0]) and post[0] isnt 'INDENT' and
+          not (token[0] is 'ELSE' and post[0] is 'IF')
+        starter = token[0]
+        [indent, outdent] = @indentation token
+        indent.generated = outdent.generated = true
+        @tokens.splice i + 1, 0, indent
+        condition = (token, i) ->
+          (include(SINGLE_CLOSERS, token[0]) and token[1] isnt ';') and
+            not (token[0] is 'ELSE' and starter not in ['IF', 'THEN'])
+        action = (token, i) ->
+          idx = if @tokens[i - 1][0] is ',' then i - 1 else i
+          @tokens.splice idx, 0, outdent
+        @detectEnd i + 2, condition, action
+        @tokens.splice i, 1 if token[0] is 'THEN'
+        return 2
+      return 1

   # Ensure that all listed pairs of tokens are correctly balanced throughout
   # the course of the token stream.


transition after registration for invited user

--- @client/auth/auth.coffee
+++ @client/auth/auth.coffee
@@ -41,7 +41,8 @@
     # and transition if needed
     if (!@local.logged_in_last_render && current_user.logged_in) || \
        (auth.form == 'verify email' && current_user.verified) || \
-       (auth.form == 'user questions' && !current_user.needs_to_complete_profile)
+       (auth.form == 'user questions' && !current_user.needs_to_complete_profile) || \
+       (auth.form == 'create account via invitation' && !current_user.needs_to_complete_profile)
       if auth.after == 'transition to proposal results'
         setTimeout ->
           updateProposalMode('results', 'after_save')


Force-attach tutorial after loading

Force-attach tutorial after loading

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -119,6 +119,7 @@
         console?.log 'Get tutorial subject'
         getTutorialSubject().select()
         @tutorial.start()
+        setTimeout (=> @tutorial.attach()), 100

   onGettingNextSubject: =>
     @el.addClass 'loading'


Ensure that palette editor switches to last selected item.

--- app/controllers/palettes.coffee
+++ app/controllers/palettes.coffee
@@ -26,6 +26,7 @@
     @notations[@notation]

   change: (params) ->
+    @_item = null
     @params = params
     @type = @params.type[0..-2]
     @palette = Drawing.find(@params.d_id).palette()
@@ -81,10 +82,6 @@
       name: "Link"
       color: "gray"
       style: "dashed"
-
-  deactivate: ->
-    super
-    @_item = null

   define: (event) =>
     event.preventDefault()


Show webpack error

Show webpack error

--- plugins/webpack/webpack.coffee
+++ plugins/webpack/webpack.coffee
@@ -45,6 +45,9 @@

       webpack config
       .run (err, stats)->
+        if err
+          docpad.log 'error', "Webpack error: #{err}!"
+
         docpad.log 'info', stats.toString
           colors: true
           hash: false


added ability to manually set multi select via action

--- app/mixins/filter-params.coffee
+++ app/mixins/filter-params.coffee
@@ -29,6 +29,15 @@
       @set("#{multiFilter}Selected",Ember.A([]))
       @addObserver(multiFilter,@,'multiFilterParamObserver')
       @addObserver("#{multiFilter}Selected.@each",@,'multiFilterSelectedObserver')
+      @_actions["#{multiFilter.singularize()}Selected"] = ((selectedFilter) =>
+        selectedFilter.set('isFiltering',!selectedFilter.get('isFiltering'))
+        selectedFilters = @get("#{multiFilter}Selected")
+        if !!selectedFilters
+          if selectedFilter.get('isFiltering')
+            selectedFilters.pushObject(selectedFilter)
+          else
+            selectedFilters.removeObject(selectedFilter)
+      )

     return

@@ -41,7 +50,8 @@
       filterParams = if Ember.isArray(filterParam) then filterParam else filterParam.split(',')
       filterParams.forEach (fp) =>
         multiFilterKey = @get('multiFilters')[multiFilter]
-        multiFilterSelected.pushObject(@store.find(multiFilterKey,fp))
+        multiFilterRecord = @store.find(multiFilterKey,fp)
+        multiFilterSelected.pushObject(multiFilterRecord)

     Ember.RSVP.all(multiFilterSelected).then((resolvedMultiFilters) =>
       multiFilterSelected = resolvedMultiFilters
@@ -54,6 +64,7 @@
     filterSelected = @get(key.replace('@each',''))

     if !!filterSelected
+      filterSelected.setEach('isFiltering',true)
       filter = filterSelected.mapBy('id')
       if filter.length is 0
         @set(filterParam,undefined)


Clear out query if not member of group. Only clear group query

--- app/pages/project/classify.cjsx
+++ app/pages/project/classify.cjsx
@@ -79,7 +79,7 @@
     if @props.workflow and not @props.loadingSelectedWorkflow
       @loadAppropriateClassification(@props)

-    @validateUserGroup(@props)
+    @validateUserGroup(@props, @context)

   componentWillUpdate: (nextProps, nextState) ->
     @context.geordi.remember workflowID: nextProps?.workflow?.id
@@ -97,7 +97,7 @@
         @setState { classification: null }
         @loadAppropriateClassification(nextProps)
     if nextProps.loadingSelectedWorkflow is false and nextProps.user isnt null
-      @shouldWorkflowAssignmentPrompt(nextProps)
+      @shouldWorkflowAssignmentPrompt(nextProps, nextContext)

     if nextProps.location.query?.group isnt @props.location.query?.group or nextProps.user isnt @props.user
       @validateUserGroup(nextProps)
@@ -321,16 +321,28 @@
               'preferences.selected_workflow': props.preferences.settings.workflow_id
             props.preferences.save()

-  validateUserGroup: (props) ->
+  validateUserGroup: (props, context) ->
     if props.location.query?.group? and props.user?
       apiClient.type('user_groups').get(props.location.query.group).then (group) =>
-        @setState({ validUserGroup: group and group.links.users.includes(props.user.id) })
+        isUserMemberOfGroup = group.links?.users?.includes(props.user.id)
+        @setState({ validUserGroup: group and isUserMemberOfGroup })
+
+        if not isUserMemberOfGroup or not group
+          console.log('not group')
+          @clearUserGroupForClassification(props, context)

   clearUserGroupForClassification: (props, context) ->
     if (props.location.query?.group)
+      query = props.location.query
       @setState({ validUserGroup: false })
-      newLocation = Object.assign({}, props.location, { query: '' });
-      newLocation.search = '';
+
+      Object.keys(query).forEach (key) =>
+        if query[key] is 'group'
+          delete query[key]
+      console.log('clearing query', query)
+
+      newLocation = Object.assign({}, props.location, { query })
+      newLocation.search = ''
       context.router.push(newLocation);

 # For debugging:


Make intro animation really smooth

Make intro animation really smooth

--- app/scripts/scenery/City.coffee
+++ app/scripts/scenery/City.coffee
@@ -247,21 +247,27 @@
     c = [
         type: 'linear'
         x: -160
+        easingFn: 'easeInQuad'
         duration: 2500
       ,
         type: 'linear'
         y: -130
         duration: 2500
+        easingFn: 'easeInOutQuad'
         event: 'lift'
+      ,
+        type: 'delay'
+        duration: 1000
+        event: 'shipExterior'
       ,
         type: 'linear'
         x: 70
         y: -10
+        easingFn: 'easeInQuad'
         duration: 2500
-        event: 'shipExterior'
       ,
         type: 'delay'
-        duration: 500
+        duration: 1
         event: 'unlock'
       ,
         type: 'delay'
@@ -301,7 +307,7 @@
         @enableControl()
         @weaponsEnabled = yes
       @one 'lift', ->
-        block.elevator.tween({ y: block.elevator.y - 130 }, 2500)
+        block.elevator.tween({ y: block.elevator.y - 130 }, 2500, 'easeInOutQuad')
         Crafty('ScrollWall').each ->
           @addComponent 'Tween'
           @tween { y: 0 }, 5000


Cleanup

Cleanup

--- compiler/ContentAPI.coffee
+++ compiler/ContentAPI.coffee
@@ -147,20 +147,6 @@

-if fs.existsSync('.cache.json')
-    CACHE = JSON.parse(fs.readFileSync('.cache.json'))
-    for k,v of CACHE
-        if v.map?
-            CACHE[k] = v.map (o) -> new Model(o)
-        else
-            CACHE[k] = new Model(v)
-else
-    CACHE = {}
-
-saveCache = ->
-    fs.writeFileSync('.cache.json', JSON.stringify(CACHE))
-
-

 # Content API wrapper
 # Wraps object in models that provide _date helpers, etc


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animation.coffee
+++ test/animation.coffee
@@ -1,3 +1,4 @@
+###
 asyncTest "Animation should default to first frame", ->
   animation = Animation.fromPixieId 45

@@ -8,7 +9,8 @@

     start()
   , milliseconds
-
+###
+
 asyncTest "Animation should increment frame on update", ->
   animation = Animation.fromPixieId 45


Delete GoG-Parser.coffee

Delete GoG-Parser.coffee


Remove spaces before computing hash

Remove spaces before computing hash

--- scripts/hash-web-resources.coffee
+++ scripts/hash-web-resources.coffee
@@ -5,9 +5,13 @@
 extractText = (body) ->
   return htmlToText.fromString body

+removeSpaces = (body) ->
+  return body.replace(/\s+/g, ' ')
+
 module.exports = {
   computeHash: (body, contentType) ->
     if contentType and /text\/html/.test(contentType)
       body = extractText(body)
+      body = removeSpaces(body)
     return ssdeep.hash(body)
 }


Mock post requests to /logging

Mock post requests to /logging

--- test/demo-mock.coffee
+++ test/demo-mock.coffee
@@ -2,6 +2,8 @@
   'jquery'
   'mockjax'
 ], ($) ->
+
+  # GET

   $.mockjax
     url: '/me'
@@ -17,5 +19,11 @@
     if id
       return {proxy: 'data/content/' + id[1] + '.json'}

+  # POST
+
+  $.mockjax
+    url: '/logging'
+    type: 'post'
+
   # Load the actual app
   require(['cs!../scripts/config'])


Update the fuzz tester

Update the fuzz tester

--- test/fuzz/fuzz.coffee
+++ test/fuzz/fuzz.coffee
@@ -11,7 +11,7 @@

 generateCommand = do ->
-	commands = '0123456789+-*/%!`:\\$.,pg' # ~&
+	commands = '0123456789+-*/%!`:\\$.,g' # ~&

 	->
 		commands[Math.floor Math.random() * commands.length]
@@ -36,10 +36,9 @@
 	path

-getProgramState = (input = [], pathInvalidatedAhead) ->
+getProgramState = (input = []) ->
 	programState = new ProgramState()
 	programState.setInput input
-	programState.flags.pathInvalidatedAhead = pathInvalidatedAhead

 	programState.put = ->
 	programState.get = -> 55
@@ -47,12 +46,21 @@
 	programState

+compile = (compiler, path) ->
+	code ="""
+			stack = programState.stack;
+			#{compiler.assemble path}
+		"""
+	path.code = code
+	path.body = new Function 'programState', code
+
+
 makeExecute = (compiler) ->
-	(code, input, pathInvalidatedAhead) ->
+	(code, input) ->
 		path = getPath code
-		compiler.compile path
+		compile compiler, path

-		programState = getProgramState input, pathInvalidatedAhead
+		programState = getProgramState input
 		path.body programState

 		programState
@@ -67,14 +75,13 @@
 	true

-runSpec = (spec, executeList) ->
-	{ code, input, pathInvalidatedAhead } = spec

+runSpec = ({ code, input }, executeList) ->
 	execute = executeList[0]
-	firstResult = execute code, input, pathInvalidatedAhead
+	firstResult = execute code, input

 	(executeList.slice 1).forEach (execute, index) ->
-		result = execute code, input, pathInvalidatedAhead
+		result = execute code, input

 		if not arraysEqual firstResult.outRecord, result.outRecord
 			throw new Error """
@@ -111,11 +118,10 @@
 		spec = {
 			code: generateCode randInt 1, 20
 			input: []
-			pathInvalidatedAhead: Math.random() < 0.5
 		}

+		console.log 'running', spec.code
 		runSpec spec, executeList
-		console.log spec.code

 	return


Try making basic test

Try making basic test

--- test/main.test.coffee
+++ test/main.test.coffee
@@ -1,6 +1,42 @@
+
+
+a = new Backbone.SharedCollection [],
+  collectionId: "testing"
+
+b = new Backbone.SharedCollection [],
+  collectionId: "testing"
+
+fetchFor = (collection, cb) ->
+
+  sharejs.open "testingsharejs", "json", (err, doc) =>
+    throw err if err
+    collection.fetch
+      sharejsDoc: doc
+      success: -> cb()
+      error: -> cb new Error "failed to open"
+
+
+beforeEach (done) ->
+  fetchFor a, (err) ->
+    done err if err
+    fetchFor b, (err) ->
+      done err

 describe "test test", ->

   it "is ok", ->

+
+describe "model adding", ->
+
+  it "works", (done) ->
+
+    b.bind "add", (model) ->
+      done()
+
+    m = new Backbone.Model
+    a.add m
+
+    # m.set foo: "bar"
+


prevent withings konnector from crashing when no credentials is given

--- server/konnectors/withings.coffee
+++ server/konnectors/withings.coffee
@@ -13,7 +13,10 @@
     prefix: 'withings'

 hexMd5 = (name) ->
-    crypto.createHash('md5').update(name).digest('hex')
+    if name?
+        crypto.createHash('md5').update(name).digest('hex')
+    else
+        ''

 # Urls


Fixes #99: Poll operations at startup.

Fixes #99: Poll operations at startup.

--- server/lib/accounts-poller.coffee
+++ server/lib/accounts-poller.coffee
@@ -2,6 +2,7 @@
 async = require 'async'

 BankAccess  = require "../models/bankaccess"
+Config      = require "../models/kresusconfig"

 weboob = require './weboob-manager'

@@ -11,11 +12,16 @@
         @prepareNextCheck()

     prepareNextCheck: ->
-        # day after between 00:00am and 02:00am
+
+        if @timeout?
+            clearTimeout @timeout
+            @timeout = null
+
+        # day after between 02:00am and 04:00am
         delta = Math.random() * 120 | 0 # opa asm.js style
         now = moment()
         nextUpdate = now.clone().add(1, 'days')
-                            .hours(0)
+                            .hours(2)
                             .minutes(delta)
                             .seconds(0)

@@ -36,23 +42,20 @@
                 return

             process = (access, callback) ->
-                weboob.retrieveAccountsByBankAccess access, callback
+                weboob.retrieveOperationsByBankAccess access, callback

             async.each accesses, process, (err) =>
                 if err?
-                    console.log "Error when fetching accounts: #{err}"
+                    console.log "Error when fetching operations: #{err}"
                     return
+                console.log "All accounts have been polled."
+                @timeout = null
+                @prepareNextCheck()

-                process = (access, callback) ->
-                    weboob.retrieveOperationsByBankAccess access, callback
+module.exports = accountPoller = new AccountsPoller

-                async.each accesses, process, (err) =>
-                    if err?
-                        console.log "Error when fetching operations: #{err}"
-                        return
-                    console.log "All accounts have been polled."
-                    @prepareNextCheck()
+Config.byName 'weboob-installed', (err, found) ->
+    if err? or not found? or found.value isnt 'true'
+        return
+    accountPoller.checkAllAccesses()

-
-
-module.exports = new AccountsPoller


Add TB data import script

Add TB data import script

--- server/syncStructuredFeeds.coffee
+++ server/syncStructuredFeeds.coffee
@@ -5,27 +5,9 @@
 import Constants from '/imports/constants.coffee'

 module.exports = ->
-  # Import Cholera data
-  WHOCholeraDataUrl = "http://apps.who.int/gho/athena/data/GHO/CHOLERA_0000000001.json?profile=simple&filter=COUNTRY:*;REGION:*"
-  feed = Feeds.findOne(url: WHOCholeraDataUrl)
-  if feed
-    if moment().isBefore(moment(feed.addedDate).add(20, 'days'))
-      # Only reimport the data if it hasn't been updated in at least 20 days
-      return
-  Feeds.upsert({
-    url: WHOCholeraDataUrl
-  }, {
-    $set:
-      title: "WHO Annual Cholera Cases by Country"
-      addedDate: new Date()
-      structuredData: true
-  })
-  feedId = Feeds.findOne(url: WHOCholeraDataUrl)._id
-  HTTP.get WHOCholeraDataUrl, {}, (err, resp)->
-    if err
-      return console.log(err)
-    countryToGeoname = {}
-    resp.data.fact.forEach (fact) ->
+  countryToGeoname = {}
+  getCountryGeonames = (facts) ->
+    facts.forEach (fact) ->
       country = fact.dim.COUNTRY
       console.log country
       if not countryToGeoname[country]
@@ -34,22 +16,81 @@
             q: country
         countryToGeoname[country] = geonamesResult.data.hits[0]._source
         delete countryToGeoname[country].alternateNames
-    Incidents.remove(sourceFeed: feedId)
-    resp.data.fact.forEach (fact) ->
-      Incidents.insert
-        sourceFeed: feedId
-        constraining: true
-        dateRange:
-          type: "precise"
-          start: new Date(fact.dim.YEAR + "")
-          end: new Date(parseInt(fact.dim.YEAR) + 1 + "")
-        locations: [countryToGeoname[fact.dim.COUNTRY]]
-        cases: parseInt(fact.Value)
-        resolvedDisease:
-          id: "http://purl.obolibrary.org/obo/DOID_1498"
-          text: "Cholera"
-        species:
-          id: "tsn:180092"
-          text: "Homo sapiens"
+
+  # Import Cholera data
+  WHOCholeraDataUrl = "http://apps.who.int/gho/athena/data/GHO/CHOLERA_0000000001.json?profile=simple&filter=COUNTRY:*;REGION:*"
+  feed = Feeds.findOne(url: WHOCholeraDataUrl)
+  if not (feed and moment().isBefore(moment(feed.addedDate).add(20, 'days')))
+    # Only reimport the data if it hasn't been updated in at least 20 days
+    Feeds.upsert({
+      url: WHOCholeraDataUrl
+    }, {
+      $set:
+        title: "WHO Annual Cholera Cases by Country"
         addedDate: new Date()
+        structuredData: true
+    })
+    feedId = Feeds.findOne(url: WHOCholeraDataUrl)._id
+    HTTP.get WHOCholeraDataUrl, {}, (err, resp)->
+      if err
+        return console.log(err)
+      getCountryGeonames(resp.data.fact)
+      Incidents.remove(sourceFeed: feedId)
+      resp.data.fact.forEach (fact) ->
+        Incidents.insert
+          sourceFeed: feedId
+          constraining: true
+          dateRange:
+            type: "precise"
+            start: new Date(fact.dim.YEAR + "")
+            end: new Date(parseInt(fact.dim.YEAR) + 1 + "")
+          locations: [countryToGeoname[fact.dim.COUNTRY]]
+          cases: parseInt(fact.Value)
+          resolvedDisease:
+            id: "http://purl.obolibrary.org/obo/DOID_1498"
+            text: "Cholera"
+          species:
+            id: "tsn:180092"
+            text: "Homo sapiens"
+          addedDate: new Date()
+
+  # Import TB data
+  WHOTBDataUrl = "http://apps.who.int/gho/athena/data/GHO/MDG_0000000020,TB_e_inc_num,TB_e_inc_tbhiv_100k,TB_e_inc_tbhiv_num.json?profile=simple&filter=COUNTRY:*;REGION:*"
+  feed = Feeds.findOne(url: WHOTBDataUrl)
+  if not (feed and moment().isBefore(moment(feed.addedDate).add(20, 'days')))
+    Feeds.upsert({
+      url: WHOTBDataUrl
+    }, {
+      $set:
+        title: "WHO Annual TB Cases by Country"
+        addedDate: new Date()
+        structuredData: true
+    })
+    feedId = Feeds.findOne(url: WHOTBDataUrl)._id
+    HTTP.get WHOTBDataUrl, {}, (err, resp)->
+      if err
+        return console.log(err)
+      getCountryGeonames(resp.data.fact)
+      Incidents.remove(sourceFeed: feedId)
+      resp.data.fact.forEach (fact) ->
+        if fact.dim.GHO == "Number of incident tuberculosis cases"
+          parsedValue = /(\d+) \[(\d+)\-(\d+)\]/.exec(fact.Value)
+          maxValue = parsedValue[3]
+          Incidents.insert
+            sourceFeed: feedId
+            constraining: true
+            dateRange:
+              type: "precise"
+              start: new Date(fact.dim.YEAR + "")
+              end: new Date(parseInt(fact.dim.YEAR) + 1 + "")
+            locations: [countryToGeoname[fact.dim.COUNTRY]]
+            cases: parseInt(maxValue)
+            resolvedDisease:
+              id: "http://purl.obolibrary.org/obo/DOID_399"
+              text: "Tuberculosis"
+            species:
+              id: "tsn:180092"
+              text: "Homo sapiens"
+            addedDate: new Date()
+
     console.log "Constraining incidents updated"


fix Source::toString

fix Source::toString

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -1102,7 +1102,7 @@
   constructor: (@obs, @sync, @lazy = false) ->
     @queue = []
   subscribe: (sink) -> @obs.dispatcher.subscribe(sink)
-  toString: -> @obs.toString.call(this)
+  toString: -> @obs.toString()
   markEnded: -> @ended = true
   consume: ->
     if @lazy


Implement sampledBy using Bacon.when

Implement sampledBy using Bacon.when

--- src/Bacon.coffee
+++ src/Bacon.coffee
@@ -623,32 +623,12 @@
     else
       @subscribeInternal = new PropertyDispatcher(subscribe, handler).subscribe

-    @sampledBy = (sampler, combinator) =>
-      lazyCombinator =
-        if (combinator?)
-          combinator = toCombinator(combinator)
-          (myVal, otherVal) ->
-            combinator(myVal.value(), otherVal.value())
-        else
-          (myVal, otherVal) -> myVal.value()
-      myVal = None
-      subscribe = (sink) =>
-        unsubMe = (unsubAll) => this.subscribeInternal (event) =>
-          if event.hasValue()
-            myVal = new Some(event)
-          else if event.isError()
-            sink event
-        unsubOther = (unsubAll) => sampler.subscribe (event) =>
-          if event.hasValue()
-            myVal.forEach (myVal) =>
-              sink(event.apply(-> lazyCombinator(myVal, event)))
-          else
-            if event.isEnd()
-              unsubAll()
-            sink event
-        compositeUnsubscribe unsubMe, unsubOther
-
-      if sampler instanceof Property then new Property(subscribe) else new EventStream(subscribe)
+    @sampledBy = (sampler, combinator = former) =>
+      combinator = toCombinator combinator
+      thisSource = new Source(this, false, false, this.subscribeInternal)
+      samplerSource = new Source(sampler, true, false)
+      stream = Bacon.when([thisSource, samplerSource], combinator)
+      if sampler instanceof Property then stream.toProperty() else stream

     @subscribe = (sink) =>
       # TODO: it's unoptimal to do this bookkeepping per subscriber


basic movement of bot

basic movement of bot

--- keller.coffee
+++ keller.coffee
@@ -1,2 +1,10 @@
 keller = (me, map, targets) ->
-  null+  canvas = main_canvas
+
+  offX = map.x
+  offY = map.y
+
+  mX = Math.abs(canvas.clientWidth / 2 + offX)
+  mY = Math.abs(canvas.clientHeight / 2 + offY)
+
+  canvas.onmousemove({clientX: mX, clientY: mY})


Agent pie testing

Agent pie testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -286,14 +286,13 @@
         totalTickets = if totalTickets != 0 then totalTickets else 'No Results'
         totalLabel.append('text')
             .text(totalTickets)
-            .attr('font-size', '0px')
+            .attr('font-size',radiusi + 'px')
             .attr('text-anchor', 'middle')
             .attr('alignment-baseline', 'central')
             .attr('filter','url(#dropshadow)')
             .style("opacity", 0)
         totalLabel.transition()
             .duration(1000)
-            .attr('font-size',radiusi + 'px')
             .style("opacity", 1)

         oldPieData = piedata


fullpieagent testing

fullpieagent testing


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -585,8 +585,8 @@
       bbox = @getBBox()
       #bbox.width and bbox.height will
       #describe the size of the label text
-      #labelRadius = radius - 20
-      labelRadius = radiuso * 1.3
+      labelRadius = radius - 20
+      #labelRadius = radiuso * 1.3
       d.x = Math.sin(a) * labelRadius
       d.l = d.x - bbox.width / 2 - 2
       d.r = d.x + bbox.width / 2 + 2


Agent pie: start/end angle pie transition work

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -231,17 +231,17 @@
         path.transition().duration(2000).attrTween('d', (d,i) ->
             window.oldPieData[myInstanceDataId][i] = window.oldPieData[myInstanceDataId][i] ? []
             window.oldPieData[myInstanceDataId][i] = d
-            console.log('window.oldPieData[myInstanceDataId][i]',window.oldPieData[myInstanceDataId][i])
-
-            if myInstanceDataId = 'resolved' and i = 0
-                console.log('in tween - OLD DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
+            console.log('window.oldPieData['+myInstanceDataId+']['+i+']',window.oldPieData[myInstanceDataId][i])
+
+            #if myInstanceDataId = 'resolved' and i = 0
+            #    console.log('in tween - OLD DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
             #console.log('1. in tween - d: ',d)

             myInterpolater = d3.interpolate(window.oldPieData[myInstanceDataId][i], d)

             #window.oldPieData[myInstanceDataId][i] = myInterpolater(0) ? d
-            if myInstanceDataId = 'resolved' and i = 0
-                console.log('in tween - NEW DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
+            #if myInstanceDataId = 'resolved' and i = 0
+            #    console.log('in tween - NEW DATA - ' + myInstanceDataId + '[' + i + ']: ',window.oldPieData[myInstanceDataId][i])
             #console.log('2. in tween - d: ',d)
             return (t) ->
                 return arc(myInterpolater(t))


Plot capacity on the time-series load graph

--- ivy/app/assets/javascripts/testing_grounds.js.coffee
+++ ivy/app/assets/javascripts/testing_grounds.js.coffee
@@ -79,11 +79,25 @@

       focusNode(element, data.name)

-      (new LoadChart([{
+      values = downsampleCurve(data.load, 365)
+
+      chartData = [{
         key:    data.name,
         area:   true,
-        values: downsampleCurve(data.load, 365)
-      }])).render('.load-graph svg')
+        values: values
+      }]
+
+      if data.capacity
+        capacityValues = for point in values
+          { x: point.x, y: data.capacity }
+
+        chartData.push({
+          key:   'Capacity',
+          color: 'darkred',
+          values: capacityValues
+        })
+
+      (new LoadChart(chartData)).render('.load-graph svg')

     # Draw a rectangle around each node.


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -149,7 +149,7 @@
             b = i(t);
             return $(@node).arc(b)

-  renderX2: (data) ->
+  render: (data) ->
         #console.log("update pie", data);

         if !data
@@ -182,16 +182,16 @@
           .outerRadius(radiuso)
           .innerRadius(radiusi)

-        #svg = d3.select(@node).append("svg:svg")
+        svg = d3.select(@node).append("svg:svg")
+            .attr("width", width)
+            .attr("height", height)
+            .append("svg:g")
+            .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
+        #svg = d3.select(@node).append("svg")
         #    .attr("width", width)
         #    .attr("height", height)
-        #    .append("svg:g")
+        #    .append("g")
         #    .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
-        svg = d3.select(@node).append("svg")
-            .attr("width", width)
-            .attr("height", height)
-            .append("g")
-            .attr("transform", "translate(" + width/2 + "," + height/2 + ")")

         # end Build pie
         #console.log("update pie", data);
@@ -377,7 +377,7 @@
                 return "M" + (d.r-2) + "," + d.b + "L" + (d.l+2) + "," + d.b + " " + d.cx + "," + d.cy

         )
-  render: (data) ->
+  renderX3: (data) ->

         if !data
           data = @get("value")


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -179,7 +179,7 @@
             #    b = i(t)
             #    return arc b
         #)
-        path.transition().duration(750).attrTween('d', (a) ->
+        path.transition().duration(750).attrTween('a', (d) ->
             `var i`
             i = d3.interpolate(@_current, a)
             @_current = i(0)


--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -2,10 +2,9 @@

     @accessor 'data'

-    @oldPieData: ''

     ready: ->
-        #window.oldPieData = ''
+        @oldPieData: ''

     onData: (data) ->
         #$(@node).fadeOut().fadeIn()


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -482,7 +482,7 @@
     #            b = i(t)
     #            return arc(b)
     #    )
-    path.transition().duration(1500).attrTween 'd' ->
+    path.transition().duration(1500).attrTween 'd', () ->
         `var i`
         theOldDataInPie = @oldPieData
         # Interpolate the arcs in data space


special case for 'koding' group for now

special case for 'koding' group for now

Koding has different member-add/member-remove semantics than other groups
can (at least for now).  We should at some point extend all groups to allow them
to grant vms per-user, but for now group vms are shared, and the "koding" group
is a special case.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -12,13 +12,19 @@
     schema            :
       ip              :
         type          : String
-        default       : -> null
+        default       : null
       ldapPassword    :
         type          : String
-        default       : -> null
+        default       : null
       name            : String
       users           : [String]
       groups          : [String]
+      isEnabled       :
+        type          : Boolean
+        default       : yes
+      shouldDelete    :
+        type          : Boolean
+        default       : no

   do ->

@@ -26,6 +32,18 @@

     JGroup  = require './group'
     JUser   = require './user'
+
+    addVm = (target, user, {sudo, groups})->
+      vm = new JVM {
+        name: member.profile.nickname
+        users: [
+          { id: user.getId(), sudo: yes }
+        ]
+        groups: groups ? []
+      }
+      vm.save (err)-> target.addVm vm, handleError
+
+    wrapGroup =(group)-> [ { id: group.getId() } ]

     JUser.on 'UserCreated', (user)->
       console.warn 'User created hook needs to be implemented.'
@@ -37,16 +55,10 @@
       creator.fetchUser (err, user)->
         if err then handleError err
         else
-          vm = new JVM {
-            name: group.slug
-            users: [
-              { id: user.getId(), sudo: yes }
-            ]
-            groups: [
-              { id: group.getId() }
-            ]
+          addVm group, user, {
+            sudo    : yes
+            groups  : wrapGroup group
           }
-          vm.save (err)-> group.addVm vm, handleError

     JGroup.on 'GroupDestroyed', ({group, member})->
       group.fetchVms (err, vms)->
@@ -56,16 +68,26 @@
     JGroup.on 'MemberAdded', ({group, member})->
       member.fetchUser (err, user)->
         if err then handleError err
-        else group.fetchVms (err, vms)->
-          if err then handleError err
-          else vms.forEach (vm)->
-            vm.update {
-              $addToSet: users: { id: user.getId(), sudo: no }
-            }, handleError
+        else if group.slug is 'koding'
+          addVm member, user, sudo: yes
+        else
+          group.fetchVms (err, vms)->
+            if err then handleError err
+            else vms.forEach (vm)->
+              vm.update {
+                $addToSet: users: { id: user.getId(), sudo: no }
+              }, handleError

     JGroup.on 'MemberRemoved', ({group, member})->
       member.fetchUser (err, user)->
         if err then handleError err
+        else if group.slug is 'koding'
+          member.fetchVms (err, vms)->
+            if err then handleError err
+            else vms.forEach (vm)->
+              vm.update {
+                $set: { isEnabled: no, shouldDelete: yes }
+              }, handleError
         else
           # group.fetchVms (err, vms)->
           #   if err then handleError err
@@ -75,6 +97,7 @@
           JVM.update { groups: group.getId() }, { $pull: id: user.getId() }, handleError

     JGroup.on 'MemberRolesChanged', ({group, member})->
+      return  if group.slug 'koding'
       member.fetchUser (err, user)->
         if err then handleError err
         else


No need to check if delegate is guests since guests also need this.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -305,8 +305,6 @@

   @fetchVmRegion = secure (client, hostnameAlias, callback)->
     {delegate} = client.connection
-    if delegate.type is 'unregistered'
-      return callback null
     JVM.one {hostnameAlias}, (err, vm)->
       return callback err  if err or not vm
       callback null, vm.region


Fix default domain create issue with new user registers

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -546,8 +546,8 @@
             return console.error err  if err or not account
             # New account found
             webHome       = username
-            hostnameAlias = newHostNameAlias
-            vm.update {$set: {hostnameAlias, webHome}}, (err)=>
+            vm.update {$set: {hostnameAlias:newHostNameAlias, webHome}},(err)=>
+
               return console.error err  if err
               # VM hostnameAlias updated

@@ -559,10 +559,11 @@
                 return console.error err  if err
                 # Counter created

-                JVM.ensureDomainSettings {
-                  type:'user', nickname:username, groupSlug:'koding'
-                  account, vm
+                hostnameAliases = JVM.createAliases {
+                  nickname:username
+                  type:'user', uid, groupSlug:'koding'
                 }
+                JVM.createDomains account, hostnameAliases, hostnameAliases[0]

                 console.log """Migration completed for
                                #{hostnameAlias} to #{newHostNameAlias}"""


SocialWorker: Tests: fix coffeelint error

--- workers/social/lib/social/models/computeproviders/koding.test.coffee
+++ workers/social/lib/social/models/computeproviders/koding.test.coffee
@@ -93,37 +93,37 @@

       withCreatedUser ({ client, user, account, group }) ->

-          client.r = { user, account, group }
-          snapshot = {}
-
-          queue = [
-
-            ->
-              createSnapshot { originId : account.getId() }, (err, snapshot_) ->
-                expect(err).to.not.exist
-                snapshot = snapshot_
-                queue.next()
-
-            ->
-              options = generateDefaultOptions { snapshotId : snapshot.getId() }
-              Koding.create client, options, (err, data) ->
-                console.log data.meta
-                expect(err).to.not.exist
-                expect(data.meta).to.be.an 'object'
-                expect(data.meta.type).to.be.equal 'aws'
-                expect(data.meta.region).to.be.equal options.region
-                expect(data.meta.source_ami).to.be.empty
-                expect(data.meta.storage_size.toString()).to.be.equal options.storage
-                expect(data.meta.alwaysOn).to.be.false
-                expect(data.label).to.be.a 'string'
-                expect(data.credential).to.be.equal user.username
-                queue.next()
-
-            -> done()
-
-          ]
-
-          daisy queue
+        client.r = { user, account, group }
+        snapshot = {}
+
+        queue = [
+
+          ->
+            createSnapshot { originId : account.getId() }, (err, snapshot_) ->
+              expect(err).to.not.exist
+              snapshot = snapshot_
+              queue.next()
+
+          ->
+            options = generateDefaultOptions { snapshotId : snapshot.getId() }
+            Koding.create client, options, (err, data) ->
+              console.log data.meta
+              expect(err).to.not.exist
+              expect(data.meta).to.be.an 'object'
+              expect(data.meta.type).to.be.equal 'aws'
+              expect(data.meta.region).to.be.equal options.region
+              expect(data.meta.source_ami).to.be.empty
+              expect(data.meta.storage_size.toString()).to.be.equal options.storage
+              expect(data.meta.alwaysOn).to.be.false
+              expect(data.label).to.be.a 'string'
+              expect(data.credential).to.be.equal user.username
+              queue.next()
+
+          -> done()
+
+        ]
+
+        daisy queue

     it 'should fail if storage is not valid', (done) ->


Fix tableheader override in marked

Fix tableheader override in marked

--- angular/core/config/marked.coffee
+++ angular/core/config/marked.coffee
@@ -9,7 +9,7 @@

     renderer.paragraph = (text) -> _super.paragraph cook(text)
     renderer.listitem  = (text) -> _super.listitem  cook(text)
-    renderer.tablecell = (text) -> _super.tablecell cook(text)
+    renderer.tablecell = (text, flags) -> _super.tablecell cook(text), flags

     renderer.heading   = (text, level) ->
       _super.heading(emojione.shortnameToImage(text), level, text)


Minor cleanup

Minor cleanup

--- src/gui/tabs/journal/index.coffee
+++ src/gui/tabs/journal/index.coffee
@@ -8,7 +8,7 @@
 container = 0

 # on active
-exports.a = (pane)->
+exports.a = ->
   unseen = 0
   exports.$i.innerHTML = ''
   unless exports.v
@@ -36,7 +36,7 @@
     pane.appendChild z
   if exports.v
     scroll()
-  else if n > 1 or unseen
+  else if n > 1
     exports.$i.innerHTML = tHint ++unseen
   return


:art: Don't move down if already at lastBufferRow regardless boundToEditor value

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -186,11 +186,8 @@
     if (row = @getRowForSelectedItem()) >= 0
       @withIgnoreCursorMove => @editor.setCursorBufferPosition([row, 0])

-    if @direction is 'down' and @provider.boundToEditor
-      # Prevent side scroll of narrow editor
-      point = @provider.editor.getCursorBufferPosition()
-      if point.isGreaterThanOrEqual(_.last(@items).point)
-        return
+    if direction is 'down' and @editor.getCursorBufferPosition().row is @editor.getLastBufferRow()
+      return

     switch direction
       when 'up'


fix for validation error that doesn't exist any more

--- app/assets/javascripts/trade/routes/sandbox_shipments_route.js.coffee
+++ app/assets/javascripts/trade/routes/sandbox_shipments_route.js.coffee
@@ -5,8 +5,13 @@
   }

   model: (params, transition) ->
-    @validationError = Trade.ValidationError.find(params.validation_error_id)
-    Trade.SandboxShipment.find(params)
+    @annualReportUpload = @modelFor('annualReportUpload')
+    Trade.ValidationError.find(params.validation_error_id).then((validationError) =>
+      @validationError = validationError
+      Trade.SandboxShipment.find(params)
+    , (validationError) =>
+      @transitionTo('annual_report_upload', @annualReportUpload)
+    )

   setupController: (controller, model) ->
     controller.set('model', model)


file.yalm: test simplification

file.yalm: test simplification

--- test/file/yaml.coffee
+++ test/file/yaml.coffee
@@ -10,102 +10,103 @@
   scratch = test.scratch @

   they 'stringify an object', (ssh, next) ->
-    mecano.file.yaml
+    mecano
       ssh: ssh
+    .file.yaml
       content: user: preference: color: 'rouge'
       target: "#{scratch}/user.yml"
-    , (err, written) ->
-      return next err if err
-      written.should.be.true()
-      fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-        return next err if err
-        data.should.eql 'user:\n  preference:\n    color: rouge\n'
-        next()
+    , (err, status) ->
+      status.should.be.true() unless err
+    .file.assert
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    color: rouge\n'
+    .then next

   they 'merge an object', (ssh, next) ->
     content = 'user:\n  preference:\n    language: english\n'
     fs.writeFile ssh, "#{scratch}/user.yml", content, (err) ->
       return next err if err
-      mecano.file.yaml
+      mecano
         ssh: ssh
+      .file.yaml
         content: user: preference: language: 'french'
         target: "#{scratch}/user.yml"
         merge: true
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-          return next err if err
-          data.should.eql 'user:\n  preference:\n    language: french\n'
-          next()
+      , (err, status) ->
+        status.should.be.true() unless err
+      .file.assert
+        target: "#{scratch}/user.yml"
+        content: 'user:\n  preference:\n    language: french\n'
+      .then next

   they 'discard undefined and null', (ssh, next) ->
-    mecano.file.yaml
+    mecano
       ssh: ssh
+    .file.yaml
       content: user: preference: color: 'violet', age: undefined, gender: null
       target: "#{scratch}/user.yml"
       merge: true
-    , (err, written) ->
-      return next err if err
-      written.should.be.true()
-      fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-        return next err if err
-        data.should.eql 'user:\n  preference:\n    color: violet\n'
-        next()
+    , (err, status) ->
+      status.should.be.true() unless err
+    .file.assert
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    color: violet\n'
+    .then next

   they 'remove null within merge', (ssh, next) ->
-    content = 'user:\n  preference:\n    language: lovelynode\n    color: rouge\n'
-    fs.writeFile ssh, "#{scratch}/user.yml", content, (err) ->
-      return next err if err
-      mecano.file.yaml
-        ssh: ssh
-        content: user: preference:
-          color: 'rouge'
-          language: null
-        target: "#{scratch}/user.yml"
-        merge: true
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-          return next err if err
-          data.should.eql 'user:\n  preference:\n    color: rouge\n'
-          next()
+    mecano
+      ssh: ssh
+    .file
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    country: france\n    language: lovelynode\n    color: rouge\n'
+    .file.yaml
+      content: user: preference:
+        color: 'rouge'
+        language: null
+      target: "#{scratch}/user.yml"
+      merge: true
+    , (err, status) ->
+      status.should.be.true() unless err
+    .file.assert
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    color: rouge\n    country: france\n'
+    .then next

   they 'disregard undefined within merge', (ssh, next) ->
-    content = 'user:\n  preference:\n    language: node\n    name:    toto\n'
-    fs.writeFile ssh, "#{scratch}/user.yml", content, (err) ->
-      return next err if err
-      mecano.file.yaml
-        ssh: ssh
-        content: user: preference:
-          language: 'node'
-          name: null
-        target: "#{scratch}/user.yml"
-        merge: true
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-          return next err if err
-          data.should.eql 'user:\n  preference:\n    language: node\n'
-          next()
+    mecano
+      ssh: ssh
+    .file
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    language: node\n    name:    toto\n'
+    .file.yaml
+      target: "#{scratch}/user.yml"
+      content: user: preference:
+        language: 'node'
+        name: null
+      merge: true
+    , (err, status) ->
+      status.should.be.true() unless err
+    .file.assert
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    language: node\n'
+    .then next

   they 'disregard undefined within merge', (ssh, next) ->
-    content = 'user:\n  preference:\n    language: node\n  name: toto\ngroup: hadoop_user\n'
-    fs.writeFile ssh, "#{scratch}/user.yml", content, (err) ->
+    mecano
+    .file
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    language: node\n  name: toto\ngroup: hadoop_user\n'
+    .file.yaml
+      ssh: ssh
+      content:
+        group: null
+      target: "#{scratch}/user.yml"
+      merge: true
+    , (err, status) ->
       return next err if err
-      mecano.file.yaml
-        ssh: ssh
-        content:
-          group: null
-        target: "#{scratch}/user.yml"
-        merge: true
-      , (err, written) ->
-        return next err if err
-        written.should.be.true()
-        fs.readFile ssh, "#{scratch}/user.yml", 'utf8', (err, data) ->
-          return next err if err
-          data.should.eql 'user:\n  preference:\n    language: node\n  name: toto\n'
-          next()
+      status.should.be.true()
+    .file.assert
+      target: "#{scratch}/user.yml"
+      content: 'user:\n  preference:\n    language: node\n  name: toto\n'
+    .then next


yaml: fix test

yaml: fix test

--- test/file/yaml.coffee
+++ test/file/yaml.coffee
@@ -21,11 +21,10 @@
       content: 'user:\n  preference:\n    color: rouge\n'
     .then next

-
   they 'merge an object', (ssh, next) ->
     nikita
       ssh: ssh
-    file
+    .file
       target: "#{scratch}/user.yml"
       content: 'user:\n  preference:\n    language: english\n'
     .file.yaml


use @ instead of this

use @ instead of this

--- scripts/feed_checker.coffee
+++ scripts/feed_checker.coffee
@@ -47,7 +47,7 @@
           # ignore meta
           # .on 'meta', (meta) ->
           .on 'readable', ->
-            while item = this.read() then next.push item
+            while item = @read() then next.push item
           .on 'end', ->
             if prev.length != 0 then next.forEach (article) ->
               if is_new prev, article.guid


add metric for active workers

add metric for active workers

--- app/coffee/DispatchManager.coffee
+++ app/coffee/DispatchManager.coffee
@@ -20,7 +20,9 @@
 					[list_name, doc_key] = result
 					[project_id, doc_id] = Keys.splitProjectIdAndDocId(doc_key)
 					# Dispatch this in the background
+					Metrics.gauge "processingUpdates", "+1"  # increments/decrements gauge with +/- sign
 					UpdateManager.processOutstandingUpdatesWithLock project_id, doc_id, (error) ->
+						Metrics.gauge "processingUpdates", "-1"
 						logger.error err: error, project_id: project_id, doc_id: doc_id, "error processing update" if error?
 					callback()

@@ -39,4 +41,4 @@
 	createAndStartDispatchers: (number) ->
 		for i in [1..number]
 			worker = DispatchManager.createDispatcher()
-			worker.run()+			worker.run()


Remove console.log :(

Remove console.log :(

--- tutor/src/flux/task-panel.coffee
+++ tutor/src/flux/task-panel.coffee
@@ -46,7 +46,6 @@

     getTitlesForStepIndex: (taskId, stepIndex) ->
       crumbs = @_get(taskId) # TaskPanelStore.get(@props.id)
-      console.log crumbs
       previous = crumbs[stepIndex - 1] if stepIndex >= 0
       next = crumbs[stepIndex + 1]


fixing jquery error and tidying diffs from master

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -1,4 +1,5 @@
 {Controller} = require 'spine'
+$ = require 'jqueryify'
 SubjectViewer = require './subject_viewer'
 AnimalSelector = require './animal_selector'
 AnalyticsLogger = require 'lib/analytics'


change joined room name on cron.coffee

change joined room name on cron.coffee

--- scripts/cron.coffee
+++ scripts/cron.coffee
@@ -2,7 +2,7 @@

 module.exports = (robot) ->
   sayHello = ->
-      robot.send {room: "#sak39_sandbox"}, "おはようございます！"
+      robot.send {room: "#bot"}, "おはようございます！"

   cronjob = new cronJob(
     cronTime: "0 1 0 * * *"     # 実行時間 s m h d w m


refactored watchers

refactored watchers

--- src/bdate.coffee
+++ src/bdate.coffee
@@ -51,19 +51,23 @@
             scope.watchers.popup.result.handler = scope.$watch 'popup.result', (newVal, oldVal) ->
               if callback
                 callback newVal, oldVal
-#return if newVal is oldVal
-#              return if not newVal
-#              return if angular.equals {}, newVal
-#              if (not scope.bRange)
-#                scope.bModel = getFormattedDate(scope.popup.result)
-#              else
-#                scope.bModel = getFormattedDateRange(scope.popup.result)
             ,
               true
             return scope.watchers.popup.result.handler
           stop: () ->
             scope.watchers.popup.result.handler()
             scope.watchers.popup.result.handler = null
+      watchPopupResult: (callback) ->
+        scope.watchers.popup.result.start (newVal, oldVal) ->
+          return if newVal is oldVal
+          return if not newVal
+          return if angular.equals {}, newVal
+          if (not scope.bRange)
+            scope.bModel = getFormattedDate(scope.popup.result)
+          else
+            scope.bModel = getFormattedDateRange(scope.popup.result)
+          scope.watchers.popup.result.stop()
+          callback newVal, oldVal
       bModel:
         handler: null
         start: (callback) ->
@@ -76,6 +80,16 @@
         stop: () ->
           scope.watchers.bModel.handler()
           scope.watchers.bModel.handler = null
+      watchBModel: (callback) ->
+        scope.watchers.bModel.start (newVal, oldVal) ->
+          return if newVal is oldVal
+          return if not newVal
+
+          #TODO (S.Panfilov) should init popup value in case of bModel external change
+          console.log newVal
+
+          scope.watchers.bModel.stop()
+          callback newVal, oldVal
       bSource:
         handler: null
         start: (callback) ->
@@ -105,12 +119,13 @@
         stop: () ->
           scope.watchers.bEndSource.handler()
           scope.watchers.bEndSource.handler = null
-      watchSource: () ->
+      watchSource: (callback) ->
         if not scope.bRange
           scope.watchers.bSource.start (val) ->
             return if not val
             scope.isSourceReady = true
             scope.watchers.bSource.stop()
+            callback val
         else
           isStartSourceReady = false
           isEndSourceReady = false
@@ -121,6 +136,7 @@
               scope.isSourceReady = true
               scope.watchers.bStartSource.stop()
               scope.watchers.bEndSource.stop()
+              callback val
           scope.watchers.bEndSource.start (val) ->
             return if not val
             isEndSourceReady = true
@@ -128,6 +144,7 @@
               scope.isSourceReady = true
               scope.watchers.bStartSource.stop()
               scope.watchers.bEndSource.stop()
+              callback val

     processClick = (event) ->
@@ -154,6 +171,10 @@
         scope.popup.state.isOpen = false

     do () ->
+      scope.watchers.watchSource (
+        scope.watchers.watchPopupResult()
+        scope.watchers.watchBModel()
+      )
       if not scope.bRange
         scope.bRefresh()
       else


staging: set "latest.koding.com" as email host

--- config/main.staging.coffee
+++ config/main.staging.coffee
@@ -236,7 +236,7 @@
     disconnectTimeout: 3e3
     vhost       : 'kite'
   email         :
-    host        : "koding.com"
+    host        : "latest.koding.com"
     protocol    : 'https:'
     defaultFromAddress: 'hello@koding.com'
   emailWorker   :


Don't do completion filtering on "."

Don't do completion filtering on "."

Instead, show all available options.

--- lib/autocomplete/provider.coffee
+++ lib/autocomplete/provider.coffee
@@ -22,7 +22,10 @@
           .then (autocompleteInfo) ->
             items = []
             results = autocompleteInfo.params.results
-            sortedResults = filter(results, prefix, { key: 'completion'})
+            sortedResults = if prefix == "."
+              results
+            else
+              filter(results, prefix, { key: 'completion'})

             for result in sortedResults
               items.push


No more CoffeeScript

No more CoffeeScript

--- logger.coffee
+++ logger.coffee
@@ -1,182 +0,0 @@
-_inst = 0
-###
-@class Logger
-@summary Extend-able Logger class
-###
-class Logger
-  userId: new ReactiveVar null
-  constructor: ->
-    @prefix = ++_inst
-    if Meteor.isClient
-      self = @
-      if Package?['accounts-base']
-        Tracker.autorun ->
-          self.userId.set Meteor.userId()
-    @_emitters = []
-    @_rules = {}
-
-  ###
-  @memberOf Logger
-  @name _log
-  @param level    {String} - Log level Accepts 'ERROR', 'FATAL', 'WARN', 'DEBUG', 'INFO', 'TRACE', 'LOG' and '*'
-  @param message  {String} - Text human-readable message
-  @param data     {Object} - [optional] Any additional info as object
-  @param userId   {String} - [optional] Current user id
-  @summary Pass log's data to Server or/and Client
-  ###
-  _log: (level, message, data = {}, user) =>
-    uid = user or @userId.get()
-    for i, em of @_emitters
-      if @_rules?[em.name] and @_rules[em.name].enable is true
-        if @_rules[em.name].allow.indexOf('*') isnt -1 or @_rules[em.name] and @_rules[em.name].allow.indexOf(level) isnt -1
-          if level is "TRACE"
-            if _.isString data
-              _data = data
-              data = {data: _data}
-            data.stackTrace = @_getStackTrace()
-
-          if Meteor.isClient and em.denyClient is true
-            Meteor.call em.method, level, message, data, uid
-          else if @_rules[em.name].client is true and @_rules[em.name].server is true and em.denyClient is false
-            em.emitter level, message, data, uid
-            if Meteor.isClient
-              Meteor.call em.method, level, message, data, uid
-          else if Meteor.isClient and @_rules[em.name].client is false and @_rules[em.name].server is true
-            Meteor.call em.method, level, message, data, uid
-          else
-            em.emitter level, message, data, uid
-
-    return new @_message
-      level: level
-      error: level
-      reason: message
-      errorType: level
-      message: message
-      details: data
-      data: data
-      user: uid
-      userId: uid
-
-  ###
-  @memberOf Logger
-  @name _message
-  @class LoggerMessage
-  @param data {Object}
-  @summary Construct message object, ready to be thrown and stringified
-  ###
-  _message: class LoggerMessage
-    constructor: (data) ->
-      {@level, @error, @reason, @message, @details, @data, @user, @userId} = data
-      @toString = -> "[#{@reason}] \r\nLevel: #{@level}; \r\nDetails: #{JSON.stringify(Logger::antiCircular(@data))}; \r\nUserId: #{@userId};"
-
-  ###
-  @memberOf Logger
-  @name rule
-  @param name    {String} - Adapter name
-  @param options {Object} - Settings object
-         options.enable {Boolean} - Enable/disable adapter
-         options.filter {Array}   - Array of strings, accepts:
-                                   'ERROR', 'FATAL', 'WARN', 'DEBUG', 'INFO', 'TRACE', 'LOG' and '*'
-                                   in lowercase or uppercase
-                                   default: ['*'] - Accept all
-         options.client {Boolean} - Allow execution on Client
-         options.server {Boolean} - Allow execution on Server
-  @summary Enable/disable adapter and set it's settings
-  ###
-  rule: (name, options) =>
-    check name, String
-    check options, {
-      enable: Boolean
-      client: Match.Optional Boolean
-      server: Match.Optional Boolean
-      filter: Match.Optional [String]
-    }
-
-    if options.filter
-      for rule in options.filter
-        rule = rule.toUpperCase()
-
-    options.filter ?= ['*']
-    options.client ?= false
-    options.server ?= true
-    options.enable ?= true
-
-    @_rules[name] =
-      enable: options.enable
-      allow:  options.filter
-      client: options.client
-      server: options.server
-    return
-
-  ###
-  @memberOf Logger
-  @name add
-  @param name        {String}    - Adapter name
-  @param emitter     {Function}  - Function called on Meteor.log...
-  @param init        {Function}  - Adapter initialization function
-  @param denyClient  {Boolean}   - Strictly deny execution on client, only pass via Meteor.methods
-  @summary Register new adapter to be used within ostrio:logger package
-  ###
-  add: (name, emitter, init, denyClient = false) =>
-    init and init()
-    @_emitters.push
-      name: name
-      emitter: emitter
-      method: "#{@prefix}_logger_emit_#{name}"
-      denyClient: denyClient
-
-    if Meteor.isServer
-      method = {}
-      method["#{@prefix}_logger_emit_#{name}"] = (level, message, data, userId) ->
-        check level, String
-        check message, Match.Optional Match.OneOf Number, String, null
-        check data, Match.Optional Match.OneOf String, Object, null
-        check userId, Match.Optional Match.OneOf String, null
-        emitter level, message, data, (userId or @userId)
-      Meteor.methods method
-    return
-
-  ###
-  @memberOf Logger
-  @name info; debug; error; fatal; warn; trace; log; _
-  @param message {String} - Any text message
-  @param data    {Object} - [optional] Any additional info as object
-  @param userId  {String} - [optional] Current user id
-  @summary Functions below is shortcuts for _log() method
-  ###
-  info: (message, data, userId) -> @_log "INFO", message, data, userId
-  debug: (message, data, userId) -> @_log "DEBUG", message, data, userId
-  error: (message, data, userId) -> @_log "ERROR", message, data, userId
-  fatal: (message, data, userId) -> @_log "FATAL", message, data, userId
-  warn: (message, data, userId) -> @_log "WARN", message, data, userId
-  trace: (message, data, userId) -> @_log "TRACE", message, data, userId
-  log: (message, data, userId) -> @_log "LOG", message, data, userId
-  _: (message, data, userId) -> @_log "LOG", message, data, userId
-
-  ###
-  @memberOf Logger
-  @name antiCircular
-  @param data {Object} - Circular or any other object which needs to be non-circular
-  ###
-  antiCircular: (obj) ->
-    _cache = [];
-    _wrap = (obj) ->
-      for v, k of obj
-        if typeof v is "object" and v isnt null
-          if _cache.indexOf(v) isnt -1
-            obj[k] = "[Circular]"
-            return undefined
-          _cache.push v
-          return _wrap v
-    _wrap obj
-    return obj
-
-  ###
-  @memberOf Logger
-  @name _getStackTrace
-  @summary Prepare stack trace message
-  ###
-  _getStackTrace: ->
-    obj = {}
-    Error.captureStackTrace obj, @_getStackTrace
-    return obj.stack


Assign to cached load settings correctly

--- src/atom.coffee
+++ src/atom.coffee
@@ -76,8 +76,7 @@
   @getLoadSettings: -> getWindowLoadSettings()

   @updateLoadSetting: (key, value) ->
-    @getLoadSettings()
-    @loadSettings[key] = value
+    @getLoadSettings()[key] = value
     location.hash = encodeURIComponent(JSON.stringify(@loadSettings))

   @getCurrentWindow: ->


remove default sizing from button component

--- lib/components/eui-button.coffee
+++ lib/components/eui-button.coffee
@@ -15,6 +15,8 @@
   action: null
   class: null
   type: 'button'
+  width: 'auto'
+

   click: (event) ->
     event.preventDefault()


added another safe test on the lift function

--- test/util_test.coffee
+++ test/util_test.coffee
@@ -68,8 +68,14 @@

         expect(_.lift(fn)()).hold.reject('err')

-    it "returns a promise if it's already one", ->
-      fn = -> Promise.resolve('value')
+    describe "dealing with promises", ->
+      it "returns a promise if it's already one", ->
+        fn = -> Promise.resolve('value')

-      _.lift(fn)().then (value) ->
-        expect(value).eq 'value'+        _.lift(fn)().then (value) ->
+          expect(value).eq 'value'
+
+      it "raises a rejected promise when the given promise is failed throw an error", ->
+        fn = -> Promise.reject(new Error('err'))
+
+        expect(_.lift(fn)()).hold.reject('err')


Fix make input empty after new item has been added to tree

--- app/assets/javascripts/nested_sortable/inline_operations.js.coffee
+++ app/assets/javascripts/nested_sortable/inline_operations.js.coffee
@@ -40,7 +40,7 @@
   # Adding new item to the list
   $('ol.sortable_tree').siblings('form').on 'ajax:success', (event, data, status, xhr) ->
     sortable_tree = $(this).siblings('ol.sortable_tree')
-    $(this).find('input[type=text]').val('')
+    $(this).find('input').val('')
     list_item = $(this).siblings('ol.fake-node').find('li.node').clone()
     list_item.find('h4 > a').text(data.name)
     list_item.find('a.delete, a.edit').attr 'href', (index, attr) ->
@@ -53,4 +53,4 @@
   $('ol.sortable_tree').on 'ajax:success', '.controls a.delete', -> $(this).parent().parent().parent().remove()

   # edit clicked item
-  $('ol.sortable_tree').on 'click', 'h4 > a.edit', @edit_node+  $('ol.sortable_tree').on 'click', 'h4 > a.edit', @edit_node


grid: magic 500px offset

grid: magic 500px offset

--- app/_src/components/grid.coffee
+++ app/_src/components/grid.coffee
@@ -117,7 +117,7 @@
 				# Except for mobile...
 				@onEnterViewport()
 		else
-			watcher = scrollMonitor.create( @$context, -360 )
+			watcher = scrollMonitor.create( @$context, 500 )
 			watcher.recalculateLocation()
 			@addWatcherListeners()


Added a browser test for summary of game result, in particular we have black resignation. This test can be generalised and applied for more of the finished games and possibly even done in isolation, but for now this is enough to get started.

--- app/coffee/tests/browser.coffee
+++ app/coffee/tests/browser.coffee
@@ -1011,7 +1011,7 @@
 class ResignTest extends BrowserTest
   names: ['ResignTest', 'resign']
   description: "resignation"
-  numTests: 3
+  numTests: 6
   testBody: (test) =>
     BLACK_EMAIL = "quitter@nomo.re"
     WHITE_EMAIL = "recipient@easyw.in"
@@ -1037,6 +1037,16 @@
         "Black no longer sees the resigned game on his status page"
       test.assertDoesntExist (@lastGameSelector false),
         "it's not in the off-turn games list either"
+
+    # But it is visible on Black's finished games list:
+    casper.thenOpen serverUrl
+    casper.thenClick '.finished_games_link', ->
+      gamesCount = casper.evaluate -> $('#finished_games .game-list-row').length
+      test.assertEqual gamesCount, 1, "White: exactly one finished game listed"
+      test.assertSelectorHasText '.game-list-row', 'White (resignation)'
+    casper.thenClick (@lastFinishedGameSelector()),
+      test.assertSelectorHasText '#game-result-summary',
+                                 'White won by resignation'

 registerTest new ResignTest


Move the sorting of files to createdOn

Move the sorting of files to createdOn

--- app/components/file-list.coffee
+++ app/components/file-list.coffee
@@ -6,7 +6,7 @@
   project: null

   targetObject: "file"
-  sortProperties: ["id:desc"]
+  sortProperties: ["createdOn:desc"]

   classNames: ["columns", "margin-top"]


fix block init error handling

fix block init error handling

--- app/assets/javascripts/bem_blocks.coffee
+++ app/assets/javascripts/bem_blocks.coffee
@@ -4,6 +4,9 @@
 	bem_class: 'js-bem'
 	bem_class_selector: ".js-bem"
 	bemDataKey: 'bem'
+
+	blockInitializationErrorHandler: (e) ->
+		console.error "Can't initialize block #{name}, check declaration. #{e.name} : #{e.message}"

 	processOptions: ($dom, item_name, b_name, o) ->
 		if o.attrs
@@ -63,14 +66,13 @@
 			return $$.cache[bid]
 		try
 			block = new $$.decls[$$.utils.camelCase(name)]($b, params, name)
+			bid = block.id
+			$b.data cacheKey, bid
+			$$.cache[bid] = block
+			block
 		catch e
-			console.error "Can't initialize block #{name}, check declaration. #{e.name} : #{e.message}"
-
-		bid = block.id
-		$b.data cacheKey, bid
-		$$.cache[bid] = block
-		block
-
+			$$.blockInitializationErrorHandler(e) if $$.blockInitializationErrorHandler
+
 	makeMod: (name, value) ->
 		mod = name
 		mod += "_#{value}" if value


updating gallery coffeescript

updating gallery coffeescript

--- app/assets/javascripts/gallery.js.coffee
+++ app/assets/javascripts/gallery.js.coffee
@@ -1,17 +1,28 @@
 $(document).on "page:change", ->
   if $('#gallery').length > 0
-    $.ajax(url: "https://api.sketchfab.com/v2/models?tags=projectmosul").done (data) ->
-      for model in data['results']
-        width = 320
-        height = 240
-        html = """
-

-
-

-          #{model['name']}
-          by #{model['user']['username']}
-          on Sketchfab
-

-

-        """
-        $('#gallery').append html+    do ->
+      sketchfabAPI = 'https://api.sketchfab.com/v2/models?'
+      $.getJSON(sketchfabAPI,
+        tags: 'projectmosul'
+        format: 'json').done (data) ->
+        $.each data.results, (i, model) ->
+          if model.isPrintable == true
+            return false
+          else
+            width = 320
+            height = 240
+            html = """
+

+
+

+              #{model['name']}
+              by #{model['user']['username']}
+              on Sketchfab
+

+

+            """
+            $('#gallery').append html
+            return
+          return
+        return
+      return


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -465,6 +465,9 @@

     magenta = Color(255, 0, 255)

+    magenta.hue()
+    # => 300
+
     # modifies the color to be yellow
     magenta.hue(60)

@@ -498,7 +501,7 @@
     A getter / setter for the lightness value of the color. Passing no argument returns the
     current lightness value. Passing a value will set the lightness to that value and return the color.

-

+

     magenta = Color(255, 0, 255)

     magenta.lightness()
@@ -506,8 +509,14 @@

     darkerMagenta = magenta.lightness(0.75)

-    darkerMagenta.lightness()
-    # => 0.75
+    # to see what they look like
+    for color, index in ['yellow', 'gray']
+      canvas.drawRect
+        color: eval(color)
+        x: 20 + (60 * index)
+        y: 20 + (60 * index)
+        width: 60
+        height: 60

     @name lightness


:lipstick:

:lipstick:

--- src/color.coffee
+++ src/color.coffee
@@ -13,8 +13,7 @@
   #
   # Returns a {Color} or `null` if it cannot be parsed.
   @parse: (value) ->
-    return null if _.isArray(value)
-    return null if _.isFunction(value)
+    return null if _.isArray(value) or _.isFunction(value)
     return null unless _.isObject(value) or _.isString(value)

     try


Improved star styling a bit.

Improved star styling a bit.

--- app/assets/javascripts/ratings.js.coffee
+++ app/assets/javascripts/ratings.js.coffee
@@ -4,7 +4,7 @@

   if type == "starRatings"
     for i in [1..5]
-      star = $("")
+      star = $("")
       if rating >= i
         star.append $("")
       else
@@ -19,5 +19,6 @@
             initializeRatingInterface element, anime_slug, type

       element.append star
+      element.append $(" ")

   element.append $("

")


better implementation of show and hide guides js

--- app/assets/javascripts/utility.js.coffee
+++ app/assets/javascripts/utility.js.coffee
@@ -15,9 +15,11 @@
   $("#light-bg").click ->
     v_span = $(this).find(".js-verb")
     verb   = v_span.text()
+    v_sib  = $(this).siblings("#dark-bg").find(".js-verb")

     if verb == "Show"
       v_span.text("Hide")
+      v_sib.text("Hide")
       $("body").addClass("bg-blueprint").removeClass("dark-version")
     else
       v_span.text("Show")
@@ -26,10 +28,13 @@
   $("#dark-bg").click ->
     v_span = $(this).find(".js-verb")
     verb   = v_span.text()
+    v_sib  = $(this).siblings("#light-bg").find(".js-verb")

     if verb == "Show"
       v_span.text("Hide")
+      $(this).siblings("#light-bg").find(".js-verb").text("Hide")
       $("body").addClass("bg-blueprint").addClass("dark-version")
     else
       v_span.text("Show")
+      v_sib.text("Show")
       $("body").removeClass("bg-blueprint")


Lock ratio of canvas to 2:1 (width:height)

--- app/assets/javascripts/welcome.js.coffee
+++ app/assets/javascripts/welcome.js.coffee
@@ -4,7 +4,7 @@
   # Make sure canvas has proper height/width
   canvasElement = $('#guided-canvas')
   canvasElement[0].width = canvasElement.width()
-  canvasElement[0].height = canvasElement.height()
+  canvasElement[0].height = canvasElement.width() / 2

   # Load up the DYD magic
   canvas = new Canvas canvasElement[0]


Fix cow scaling

Fix cow scaling

--- dev/br.cson
+++ dev/br.cson
@@ -109,7 +109,7 @@
   elements: [
     { type: 'square-rounded', color: 'black', transform: '{square_to_diamond}' }
     { type: 'square-rounded', color: 'yellow', transform: 'scale(.95) {square_to_diamond}' }
-    { type: 'domestic-animal', color: 'black', transform: '{center2tri} translate(-6%,-10%) scale(.35)' }
+    { type: 'domestic-animal', color: 'black', transform: 'translate(-6%,-10%) scale(.35)' }
   ]

 'warning--cyclists_crossing--c-brazil':


renamed to C#

renamed to C#

--- lib/omnisharp-atom/linter.coffee
+++ lib/omnisharp-atom/linter.coffee
@@ -9,7 +9,7 @@

     @syntax: ['source.cs']

-    linterName: "CSharp"
+    linterName: "C#"

@@ -56,7 +56,7 @@
                 col: column,
                 level: error.LogLevel.toLowerCase(),
                 range: new Rng([line, start], [line, end]),
-                linter: "CSharp"
+                linter: @linterName
             }


Sync survey icons to columns

Sync survey icons to columns

--- app/classifier/tasks/survey/chooser.cjsx
+++ app/classifier/tasks/survey/chooser.cjsx
@@ -35,9 +35,9 @@
       'none'

   howManyColumns: ({length}) ->
-    if length < 5
+    if length <= 5
       1
-    else if length < 20
+    else if length <= 20
       2
     else
       3


fix(kanban): default filter color

fix(kanban): default filter color

--- app/coffee/modules/common/filters.coffee
+++ app/coffee/modules/common/filters.coffee
@@ -98,6 +98,9 @@

 darkerFilter = ->
     return (color, luminosity) ->
+        if !color
+            return 'transparent'
+
         # validate hex string
         color = new String(color).replace(/[^0-9a-f]/gi, '')
         if color.length < 6


Updating the while in rstrip to not use raw JS: https://github.com/jashkenas/coffee-script/issues/1387#issuecomment-1227511

--- pystr.coffee
+++ pystr.coffee
@@ -33,7 +33,7 @@
 String::lstrip = (c) -> c = c or '\\s\\s*'; @.replace(new RegExp("^#{c}"), '')

 # Strip characters from the end of the string, if no characters are passed in white space will be stripped.
-String::rstrip = (c) -> c = c or '\\s'; re = new RegExp(c); i = @length; `while(re.test(this.charAt(--i)))`; @[0...i+1]
+String::rstrip = (c) -> c = c or '\\s'; re = new RegExp(c); i = @length; continue while re.test(@charAt(--i)); @[0...i+1]

 # Strip characters from the front and end of the string, if no characters are passed in white space will be stripped.
 # Use the native `trim` if it's available and only whitespace is being stripped.


remove useless response param

remove useless response param

--- server/index.coffee
+++ server/index.coffee
@@ -91,7 +91,7 @@
         # statusLevels: yes

     @app.use "/#{config.get('apiSegment')}", (req, res, next) ->
-      badObject = bucketUtil.checkForDollarKeys req.body, res
+      badObject = bucketUtil.checkForDollarKeys req.body
       if badObject
         res.status(403).send("Disallowed object in request: " + JSON.stringify(badObject))
       else


Increase cursor row and column number by one.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -267,6 +267,6 @@
       {cursor, file} = data

       status.updatePartial """
-
#{cursor.row}:#{cursor.column}

+
#{++cursor.row}:#{++cursor.column}

#{file.name}

       """


Pricing: Plan: Reducing complexity of code

--- client/pricing/lib/index.coffee
+++ client/pricing/lib/index.coffee
@@ -32,10 +32,9 @@
       KodingAppsController.appendHeadElement 'script', paypalOptions, =>
         @emit 'ready'

-    appManager = kd.singleton "appManager"
-    appManager.on 'AppIsBeingShown', (app) =>
-      return  unless app instanceof PricingAppController
-      @getView().loadPlan()
+    kd.singletons.appManager.on 'AppIsBeingShown', (app) =>
+      @getView().loadPlan()  if app instanceof PricingAppController
+

   initAppStorage: ->


Remove random IE9 hack

Remove random IE9 hack


Auto-html-escape source code because ejudge does not do it

--- client/components/Submit.coffee
+++ client/components/Submit.coffee
@@ -68,7 +68,7 @@

     render: () ->

-

+
{convert(@props.submit.source, @state.encoding)}

             Кодировка:{" "}
             <ButtonGroup>
                 {


Cosmetic changes.

Cosmetic changes.

--- app/javascripts/modeling.coffee
+++ app/javascripts/modeling.coffee
@@ -25,15 +25,16 @@
         "<b>#{ key }</b>: #{ y }"

       # The session date x values.
-      xValues = (X_DATA_SPEC.accessor(mdlResult) for mdlResult in modelingResults)
+      xValues = (
+        X_DATA_SPEC.accessor(mdlResult) for mdlResult in modelingResults
+      )

       # The chart data specification.
       spec =
         x: X_DATA_SPEC
         y: dataSeriesSpec

-      # Return the standard chart configuration extended
-      # with the following:
+      # Return the standard chart configuration extended with the following:
       # * the xValues and xFormat properties
       # * the tooltip function
       _.extend Chart.configureChart(modelingResults, spec),


IDE: implemented temp named new collab preparation

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -328,7 +328,7 @@
                 @machineStateModal.once 'MachineTurnOnStarted', =>
                   KD.getSingleton('mainView').activitySidebar.initiateFakeCounter()

-          @prepareCollaboration()
+          @prepareCollaboration___()

           actionRequiredStates = [Pending, Stopping, Stopped, Terminating, Terminated]
           computeController.on "public-#{machineId}", (event) =>
@@ -1030,6 +1030,16 @@
     @chat.show()

+  prepareCollaboration___: ->
+
+    @rtm          = new RealTimeManager
+    { channelId } = @workspaceData
+
+    # return @rtm.ready => @statusBar.share.show()  unless channelId
+
+    @rtm.ready => @statusBar.share.show()
+
+
   startChatSession: (callback) ->

     {message} = KD.singletons.socialapi


Change default

Change default

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -20,7 +20,7 @@
     defaults:
       title: 'Untitled Book'
       type: 'book'
-      pages: 10
+      pages: 300
       author:
         name: 'Uknown'
         email: '#'


move AppView initialization to Router

move AppView initialization to Router

--- src/scripts/views/app-view.coffee
+++ src/scripts/views/app-view.coffee
@@ -31,7 +31,6 @@
     emitter.addListener 'entry-changed', @_onEntryChanged
     emitter.addListener 'selected', @_onEntryChanged
     emitter.addListener 'search-visible-changed', @_onSearchVisibleChanged
-    getEntryService().fetchAll()

   componentWillUnmount: ->
     emitter = getEntryViewer().getEventEmitter()


include associations even if attributes are empty

--- client/lanes/models/Base.coffee
+++ client/lanes/models/Base.coffee
@@ -198,9 +198,14 @@
             data = this.getAttributes(props:true, true)
         else
             data = this.unsavedAttributes()
-        if this.associations && (!_.isEmpty(data) || !this.isNew()) && !options.excludeAssociations
-            data.id = this.getId() unless this.isNew()
-            _.extend(data, this.associations.dataForSave(this, options))
+        if this.associations && !options.excludeAssociations
+            # empty associations are not included
+            associationData = this.associations.dataForSave(this, options)
+            _.extend(data, associationData)
+            # if submitting associations, we must include our id so they can be updated
+            unless this.isNew() or _.isEmpty(associationData)
+                data[@idAttribute] = this.getId()
+
         data

     unCacheDerived: (name) ->
@@ -255,7 +260,7 @@
         klass::session['updated_at'] ||= 'date'

         if klass::associations
-            klass::associations = new Lanes.Models.AssocationMap(klass)
+            klass::associations = new Lanes.Models.AssociationMap(klass)

     @afterExtended: (klass) ->
         return if klass::abstractClass


eventemitter fixed

eventemitter fixed

--- core/KDEventEmitter.coffee
+++ core/KDEventEmitter.coffee
@@ -12,21 +12,21 @@
   _on = (registry, eventName, callback)->
     # on can be defined before any emit, so create
     # the event registry, if it doesn't exist.
-    registry[event] ?= []
+    registry[eventName] ?= []
     # register the listeners callback.
-    registry[event].push callback
+    registry[eventName].push callback

   _off = (registry, eventName, callback)->
-    if event is "*"
+    if eventName is "*"
       registry = {}
     # reset the listener container so no event
     # will be propagated to previously registered
     # listener callbacks.
-    else if callback and registry[event]
-      cbIndex = registry[event].indexOf callback
-      registry[event].splice 1, cbIndex if cbIndex >= 0
+    else if callback and registry[eventName]
+      cbIndex = registry[eventName].indexOf callback
+      registry[eventName].splice 1, cbIndex if cbIndex >= 0
     else
-      registry[event] = []
+      registry[eventName] = []

   getEventParser = (event)->
@@ -44,44 +44,44 @@
   @emit : ->
     # slice the arguments, 1st argument is the event name,
     # rest is args supplied with emit.
-    [event, args...] = [].slice.call arguments
+    [eventName, args...] = [].slice.call arguments
     # create listener container if it doesn't exist
-    _e[event] ?= []
+    _e[eventName] ?= []
     # call every listener inside the container with the arguments (args)
-    listener.apply null,args for listener in _e[event] if _e[event]?
+    listener.apply null,args for listener in _e[eventName] if _e[eventName]?

-  @on   :(event,callback)->  _on _e, event, callback
-  @off  :(event, callback)-> _off _e, event, callback
+  @on   :(eventName, callback)-> _on _e, eventName, callback
+  @off  :(eventName, callback)-> _off _e, eventName, callback

   constructor:->
     @KDEventEmitterEvents  = {}
     @_e = @KDEventEmitterEvents[@constructor.name] = {}

-  emit:(event, args...)->
-    @_e[event] ?= []
+  emit:(eventName, args...)->
+    @_e[eventName] ?= []

     listenerStack = []

-    for own eventName of @_e
-      continue if eventName is event
-      parser = getEventParser eventName
-      if parser.test event
-        listenerStack = listenerStack.concat @_e[eventName].slice(0)
+    for own eventToBeFired of @_e
+      continue if eventToBeFired is eventName
+      parser = getEventParser eventToBeFired
+      if parser.test eventName
+        listenerStack = listenerStack.concat @_e[eventToBeFired].slice(0)

-    listenerStack = listenerStack.concat @_e[event].slice(0)
+    listenerStack = listenerStack.concat @_e[eventName].slice(0)

     listenerStack.forEach (listener)=>
       listener.apply @, args

-  on  :(event, callback) -> _on  @_e, event, callback
-  off :(event, callback) -> _off @_e, event, callback
-  unsubscribe:(event, callback) -> _off @_e, event, callback
+  on  :(eventName, callback) -> _on  @_e, eventName, callback
+  off :(eventName, callback) -> _off @_e, eventName, callback
+  unsubscribe:(eventName, callback) -> _off @_e, eventName, callback

-  once:(event, callback) ->
-    _callback = () =>
+  once:(eventName, callback) ->
+    _callback = =>
       args = [].slice.call arguments
-      @unsubscribe event, _callback
+      @unsubscribe eventName, _callback
       callback.apply @, args

-    @on event, _callback
+    @on eventName, _callback


don't double callback

don't double callback

--- src/service/device-service.coffee
+++ src/service/device-service.coffee
@@ -15,7 +15,7 @@

   run: (callback) =>
     @serviceDevice.update (error) =>
-      callback new Error "Couldn't update the service device! Things are probably very bad. #{error.message}" if error?
+      return callback new Error "Couldn't update the service device! Things are probably very bad. #{error.message}" if error?
       callback()

   onCreate: (options, callback) =>


webapp: fix newline wrapping of upload button in files listing

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -1974,7 +1974,8 @@
             actions      = {@props.actions} />

     render_new_file : ->
-        <Col sm=3 md=2>
+        # hsy: for me, without the "nowrap", the upload button usually shows up on the next line
+        <Col sm=3 md=2 style={whiteSpace: 'nowrap'}>
             <ProjectFilesNew
                 file_search   = {@props.file_search}
                 current_path  = {@props.current_path}


feeder: reload method implemented

feeder: reload method implemented

--- client/app/Applications/Feeder.kdapplication/FeedController.coffee
+++ client/app/Applications/Feeder.kdapplication/FeedController.coffee
@@ -154,6 +154,9 @@
       else if (firstVar > secondVar) then return -1
       else return 0

+  reload:->
+    {selection, defaultSort} = this
+    @changeActiveSort selection.activeSort or defaultSort.title

   loadFeed:(filter = @selection)=>


Remove trailing whitespaces

Remove trailing whitespaces

--- client/landing/app/Applications/Inbox.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Inbox.kdapplication/AppView.coffee
@@ -89,7 +89,7 @@
           messagesSplit.resizePanel "100%",0

     @on "MessageSelectedFromOutside", (item)=>
-
+
         messagesSplit.resizePanel "33%",0 unless messagesSplit.didResizeBefore
         messagesSplit.didResizeBefore = yes
         @newMessageBar.enableMessageActionButtons()


remove unneeded logging

remove unneeded logging

--- client/views/common/uploadUserFileModal/uploadUserFileModal.coffee
+++ client/views/common/uploadUserFileModal/uploadUserFileModal.coffee
@@ -66,19 +66,6 @@
 							systemLanguage = SystemLanguages.defaultValue

 						if email.match(/^([\w.-]+)@([\w.-]+)\.([a-zA-Z.]{2,6})$/i)
-							console.log
-								email: email
-								firstname: firstname
-								lastname: lastname
-								gender: gender
-								telefon: telefon
-								pioneer: pioneer
-								privilege: privilege
-								congregation: congregation
-								systemLanguage: systemLanguage
-								foreignLanguages: foreignLanguages
-								roles: roles
-
 							users.push
 								email: email
 								firstname: firstname


closes #251

closes #251

--- src/textwrap/helpers/tspan.coffee
+++ src/textwrap/helpers/tspan.coffee
@@ -13,9 +13,10 @@
     tspan
       .attr "x", x + "px"
       .attr "dx", dx + "px"
-      .attr "dy", if first and valign is "top" then "0px" else dy + "px"
-      .style "baseline-shift", if valign is "top" then "-5%" else if valign is "middle" then "13%" else "10%"
-      .attr "dominant-baseline", if valign is "top" then "hanging" else "alphabetical"
+      .attr "dy", dy + "px"
+      # .attr "dy", if first and valign is "top" then "0px" else dy + "px"
+      .style "baseline-shift", "0%"
+      .style "dominant-baseling", "alphabetical"
       .text w

   mirror = vars.rotate.value is -90 or vars.rotate.value is 90
@@ -158,14 +159,14 @@
   else
     h = lines * dy
     y = if valign is "middle" then height/2 - h/2 else height - h
+  y -= dy * 0.2

   translate = "translate(0," + y + ")"
-  rmod = if vars.rotate.value < 0 then width else height
-  flip = vars.rotate.value is 180 or vars.rotate.value is -180
-  if flip
+  if vars.rotate.value is 180 or vars.rotate.value is -180
     rx = vars.container.x + width/2
     ry = vars.container.y + height/2
   else
+    rmod = if vars.rotate.value < 0 then width else height
     rx = vars.container.x + rmod/2
     ry = vars.container.y + rmod/2
   rotate = "rotate(" + vars.rotate.value + ", " + rx + ", " + ry + ")"


Changed an .unshift to .push

Changed an .unshift to .push

--- src/game.coffee
+++ src/game.coffee
@@ -40,7 +40,7 @@

 		@player = new Player @, null, 0, 0, 'KayArr'
 		@camera.target = @player
-		@timeManager.targets.unshift @player
+		@timeManager.targets.push @player

 		@transitionToMap (MapGenerator.generateBigRoom @, 50, 25), 2, 2