Fix zoom to bounds behavior (jump to cairo)

--- app/scripts/mapctrl.coffee
+++ app/scripts/mapctrl.coffee
@@ -199,18 +199,14 @@
             it = getFeaturedItem(f)

             if it.name.toLowerCase() == val.toLowerCase()
-              # I don't really understand why this works, but it does...
-              # FIXME if you can!
-              numToUnpack = 2
-              if f.geometry.coordinates.length is 3
-                coordsToUse = f.geometry.coordinates
+              # Note: only assumes two different nesting levels
+              if f.geometry.coordinates[0][0].length == 2
+                numToUnpack = 1
               else
-                coordsToUse = [f.geometry.coordinates]
-              console.log coordsToUse
-              points = L.GeoJSON.coordsToLatLngs(coordsToUse, numToUnpack)
+                numToUnpack = 2
+              points = L.GeoJSON.coordsToLatLngs(f.geometry.coordinates, numToUnpack)
               # instantiate as multipolygon to get the bounds
               bounds = L.multiPolygon(points).getBounds()
-              console.log bounds
               map.fitBounds(bounds)
               return
         )


Atom: stop showing welcome pane on startup

--- config/.atom/config.cson
+++ config/.atom/config.cson
@@ -3,3 +3,5 @@
     telemetryConsent: "no"
   "exception-reporting":
     userId: "c93ce999-fa4a-49b6-867b-e63829f48fad"
+  welcome:
+    showOnStartup: false


Added Android and Mobile to BROWSER_TYPE

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -23,6 +23,8 @@
   ios: false
   iphone: false # also true for iPod touch
   ipad: false
+  android: false
+  mobile: false

 ###
 The DOM Element abstraction engine
@@ -879,6 +881,7 @@
   ###
   _warmup: ->
     _ua = navigator.userAgent
+    _av = navigator.appVersion
     _browserType = BROWSER_TYPE
     _browserType.opera    = !!~_ua.indexOf('Opera')
     _browserType.safari   = !!~_ua.indexOf('KHTML')
@@ -905,6 +908,8 @@
     _browserType.ios = _iPhone or _iPad
     _browserType.iphone = _iPhone
     _browserType.ipad = _iPad
+    _browserType.android = !!~_ua.indexOf('Android')
+    _browserType.mobile = !!~_av.indexOf('Mobile')
     @_domWaiter()
     null


Making #1050 a syntax error.

Making #1050 a syntax error.

--- lexer.coffee
+++ lexer.coffee
@@ -170,11 +170,11 @@
   commentToken: ->
     return 0 unless match = @chunk.match COMMENT
     [comment, here] = match
-    @line += count comment, '\n'
     if here
       @token 'HERECOMMENT', @sanitizeHeredoc here,
         herecomment: true, indent: Array(@indent + 1).join(' ')
       @token 'TERMINATOR', '\n'
+    @line += count comment, '\n'
     comment.length

   # Matches JavaScript interpolated directly into the source via backticks.
@@ -343,8 +343,11 @@
   # erasing all external indentation on the left-hand side.
   sanitizeHeredoc: (doc, options) ->
     {indent, herecomment} = options
-    return doc if herecomment and 0 > doc.indexOf '\n'
-    unless herecomment
+    if herecomment
+      if HEREDOC_ILLEGAL.test doc
+        throw new Error "block comment cannot contain \"*/\", starting on line #{@line + 1}"
+      return doc if doc.indexOf('\n') <= 0
+    else
       while match = HEREDOC_INDENT.exec doc
         attempt = match[1]
         indent = attempt if indent is null or 0 < attempt.length < indent.length
@@ -593,6 +596,8 @@

 HEREDOC_INDENT  = /\n+([^\n\S]*)/g

+HEREDOC_ILLEGAL = /\*\//
+
 ASSIGNED        = /^\s*@?([$A-Za-z_][$\w\x7f-\uffff]*|['"].*['"])[^\n\S]*?[:=][^:=>]/

 LINE_CONTINUER  = /// ^ \s* (?: , | \??\.(?!\.) | :: ) ///


Unified error handling in the lexer.

Unified error handling in the lexer.

--- lexer.coffee
+++ lexer.coffee
@@ -61,7 +61,7 @@
            @literalToken()

     @closeIndentation()
-    @carp "missing #{tag}" if tag = @ends.pop()
+    @error "missing #{tag}" if tag = @ends.pop()
     return @tokens if opts.rewrite is off
     (new Rewriter).rewrite @tokens

@@ -112,7 +112,7 @@
         id  = new String id
         id.reserved = yes
       else if id in RESERVED
-        @identifierError id
+        @error "Reserved word \"#{word}\""

     unless forcedIdentifier
       id  = COFFEE_ALIAS_MAP[id] if id in COFFEE_ALIASES
@@ -322,7 +322,8 @@
     tag  = value
     prev = last @tokens
     if value is '=' and prev
-      @assignmentError() if not prev[1].reserved and prev[1] in JS_FORBIDDEN
+      if not prev[1].reserved and prev[1] in JS_FORBIDDEN
+        @error "Reserved word \"#{@value()}\" can't be assigned"
       if prev[1] in ['||', '&&']
         prev[0] = 'COMPOUND_ASSIGN'
         prev[1] += '='
@@ -357,7 +358,7 @@
     {indent, herecomment} = options
     if herecomment
       if HEREDOC_ILLEGAL.test doc
-        @carp "block comment cannot contain \"*/\", starting"
+        @error "block comment cannot contain \"*/\", starting"
       return doc if doc.indexOf('\n') <= 0
     else
       while match = HEREDOC_INDENT.exec doc
@@ -391,16 +392,6 @@
   # Close up all remaining open blocks at the end of the file.
   closeIndentation: ->
     @outdentToken @indent
-
-  # The error for when you try to use a forbidden word in JavaScript as
-  # an identifier.
-  identifierError: (word) ->
-    throw SyntaxError "Reserved word \"#{word}\" on line #{@line + 1}"
-
-  # The error for when you try to assign to a reserved word in JavaScript,
-  # like "function" or "default".
-  assignmentError: ->
-    throw SyntaxError "Reserved word \"#{@value()}\" on line #{@line + 1} can't be assigned"

   # Matches a balanced group such as a single or double-quoted string. Pass in
   # a series of delimiters, all of which must be nested correctly within the
@@ -428,7 +419,7 @@
       else if end is '"' and prev is '#' and letter is '{'
         stack.push end = '}'
       prev = letter
-    @carp "missing #{ stack.pop() }, starting"
+    @error "missing #{ stack.pop() }, starting"

   # Expand variables and expressions inside double-quoted strings using
   # Ruby-like notation for substitution of arbitrary expressions.
@@ -481,7 +472,7 @@
   # correctly balanced throughout the course of the token stream.
   pair: (tag) ->
     unless tag is wanted = last @ends
-      @carp "unmatched #{tag}" unless 'OUTDENT' is wanted
+      @error "unmatched #{tag}" unless 'OUTDENT' is wanted
       # Auto-close INDENT to support syntax like this:
       #
       #     el.click((event) ->
@@ -524,9 +515,10 @@
       if contents in ['\n', quote] then contents else match
     body = body.replace /// #{quote} ///g, '\\$&'
     quote + @escapeLines(body, heredoc) + quote
-
-  # Throws a syntax error from current `@line`.
-  carp: (message) -> throw SyntaxError "#{message} on line #{ @line + 1}"
+
+  # Throws a syntax error on the current `@line`.
+  error: (message) ->
+    throw SyntaxError "#{message} on line #{ @line + 1}"

 # Constants
 # ---------


adding in footer tests

adding in footer tests

--- test/all-tests.coffee
+++ test/all-tests.coffee
@@ -7,9 +7,9 @@
 # TODO figure out why.
 require './components/course-listing.spec'
 require './components/navbar.spec'
-require './components/homework/homework-plan.spec'
-require './components/homework/exercise-summary.spec'
-require './components/reading-plan.spec'
+require './components/task-plan/homework/homework-plan.spec'
+require './components/task-plan/homework/exercise-summary.spec'
+require './components/task-plan/footer.spec'

 require './components/task.spec'
 require './components/task-homework.spec'


Added collection method for moving and removing docs.

--- Collections.coffee
+++ Collections.coffee
@@ -67,3 +67,19 @@

   createTemporary: ->
     new Meteor.Collection(null)
+
+  moveDoc: (id, sourceCollection, destCollection) ->
+    order = sourceCollection.findOne(id)
+    unless order
+      throw new Error('Could not find doc with id ' + id + ' in collection ' + sourceCollection)
+    destCollection.insert order, (err, result) ->
+      if err
+        throw new Error('Failed to insert into destination collection when moving')
+      else
+        sourceCollection.remove id, (err2, result2) ->
+          if err2
+            throw new Error('Failed to remove from source collection when moving')
+
+  removeAllDocs: (collection) ->
+    _.each collection.find().fetch(), (order) ->
+      collection.remove(order._id)


fix error (function pointer used instead of result)

--- extension/chrome/js/src/content.coffee
+++ extension/chrome/js/src/content.coffee
@@ -149,7 +149,7 @@

     upgradeInstall: (oldVersion, newVersion) ->
       log 'upgrade from', oldVersion, newVersion
-      @isAuthorised.fail =>
+      @isAuthorised().fail =>
         @gettingStarted()
         return
       return


Start y-Axis with 0 by default

Start y-Axis with 0 by default

--- app/assets/javascripts/bitcharts/application.js.coffee
+++ app/assets/javascripts/bitcharts/application.js.coffee
@@ -1,13 +1,14 @@
 #= require jquery
 #= require chart.min.js

-Bitcharts =
+@Bitcharts = Bitcharts =

   defaultBorderWidth:  2
   defaultColorsString: '#78AEEB #55B662 #CB4FBA'
   defaultCornerRadius: 0
   defaultTension:      0.0
   defaultType:         'line'
+  defaultLinearScale:  ticks: { suggestedMin: 0 }

   hexToRgba: (hex, opacity) ->
     hex = hex.replace('#', '')
@@ -27,6 +28,7 @@
     rectangleDefaults.borderWidth = Bitcharts.defaultBorderWidth
     tooltipDefaults = Chart.defaults.global.tooltips
     tooltipDefaults.cornerRadius = Bitcharts.defaultCornerRadius
+    Chart.scaleService.updateScaleDefaults('linear', Bitcharts.defaultLinearScale)

   styleDataset: (dataset, color) ->
     dataset.borderColor          = color


Fix block upload

Fix block upload

--- apps/channel/client.coffee
+++ apps/channel/client.coffee
@@ -2,6 +2,7 @@
 _ = require 'underscore'
 sd = require("sharify").data
 mediator = require '../../lib/mediator.coffee'
+Block = require '../../models/block.coffee'
 Channel = require '../../models/channel.coffee'
 ChannelBlocks = require '../../collections/channel_blocks.coffee'
 CurrentUser = require '../../models/current_user.coffee'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/approachable.coffee
+++ src/approachable.coffee
@@ -10,7 +10,8 @@

       I.cooldowns[name].value = newValue

-      self.trigger "cooldown:#{name}", (newValue)
+      if (newValue isnt value) and (newValue is target)
+        self.trigger "cooldown:#{name}"

   addCooldown: (name, options={target: 0, approachBy: 1, value: 100}) ->
     unless I.cooldowns[name]


update main spec test

update main spec test

--- test/main.spec.coffee
+++ test/main.spec.coffee
@@ -26,17 +26,17 @@
     done()

   it "generate library file", (done) ->
-
+    destPath = "./test/server/static/.tmp/test.js"
     result = trm.generateLib {
       domain: "yahoo.com.tw"
-      destPath: "./tmp/test.js"
+      destPath: destPath
     }

     result.then (data) ->
       data.should.be.type('string')
       done()

-describe.only "run trm test", (done) ->
+describe "run trm test", (done) ->
   it "output / save a file", (done) ->
     destPath = "./test/server/static/.tmp/test.js"
     result = trm.generateLib {
@@ -66,3 +66,13 @@
     code.indexOf("\n").should.be.equal(-1)
     done()

+# TRM = require("../out/trm")
+# trmLib = new TRM()
+
+# describe.only "ad group id check", (done) ->
+#   it "verify ad group id is correct from url", (done) ->
+#     url = "http://apps.friday.tw/index.php?page=public&type=fb_xjzl_precious&trackPixelId=14&adGroupId=148"
+#     console.log trmLib
+#     result = trmLib._getAdGroupId(url)
+#     console.log result
+


Fixed assert_That_Folder_Exists test

Fixed assert_That_Folder_Exists test

--- test/assert/assert_String.test.coffee
+++ test/assert/assert_String.test.coffee
@@ -101,7 +101,10 @@

   it 'assert_That_Folder_Exists',->
     ''.assert_That_Folder_Exists.assert_Is_Function()
-    '.git'.assert_That_Folder_Exists().assert_Is_Equal_To('.git')
+    '_tmp_Folder'.folder_Create()
+    '_tmp_Folder'.assert_That_Folder_Exists().assert_Is_Equal_To('_tmp_Folder')
+    '_tmp_Folder'.folder_Delete()
+    '_tmp_Folder'.assert_That_Folder_Not_Exists().assert_Is_Equal_To('_tmp_Folder')
     (-> '.git'.assert_That_Folder_Exists()).assert_Not_Throws()
     (-> '.aaa'.assert_That_Folder_Exists()).assert_Throws()
     (-> 'aaa'.assert_Is_Folder()).assert_Throws (error)->


Add tests for repo.checkoutFile

Add tests for repo.checkoutFile

--- test/repo.test.coffee
+++ test/repo.test.coffee
@@ -596,3 +596,59 @@
         it "resets to HEAD~1, all changes get discarded completely", ->
           status.files.should.not.have.a.property file
           status.files.should.not.have.a.property 'rawr.txt'
+
+  describe.only "#checkoutFile", ->
+    repo    = null
+    git_dir = __dirname + "/fixtures/junk_checkoutFile"
+    status  = null
+    file    = "bla.txt"
+
+    # given a fresh new repo
+    beforeEach (done) ->
+      status = null
+      rimraf git_dir, (err) ->
+        return done err if err
+        fs.copy "#{__dirname}/fixtures/reset", "#{git_dir}", (err) ->
+          return done err if err
+          fs.rename "#{git_dir}/git.git", "#{git_dir}/.git", (err) ->
+            return done err if err
+            repo = git git_dir
+            fs.writeFile "#{git_dir}/#{file}", "hello", (err) ->
+              return done err if err?
+              repo.add "#{git_dir}/#{file}", (err) ->
+                done err
+
+    after (done) ->
+      rimraf git_dir, (err) ->
+        done err
+
+    describe "passing no explicit files", ->
+      beforeEach (done) ->
+        repo.checkoutFile ->
+          repo.status (err, _status) ->
+            status = _status
+            done err
+
+      it "discards changes in the working tree for all files", ->
+        status.files.should.have.a.property file
+        status.files[file].staged.should.be.true
+        status.files[file].tracked.should.be.true
+        status.files[file].type.should.eql 'A'
+
+        status.files.should.have.a.property 'rawr.txt'
+        status.files['rawr.txt'].staged.should.be.true
+        status.files['rawr.txt'].tracked.should.be.true
+        status.files['rawr.txt'].type.should.eql 'M'
+
+    describe "passing an explicit file", ->
+      beforeEach (done) ->
+        repo.checkoutFile 'rawr.txt', ->
+          repo.status (err, _status) ->
+            status = _status
+            done err
+
+      it "discard changes to the specified file", ->
+        status.files.should.have.a.property 'rawr.txt'
+        status.files['rawr.txt'].staged.should.be.true
+        status.files['rawr.txt'].tracked.should.be.true
+        status.files['rawr.txt'].type.should.eql 'M'


config: fix kloud configuration

config: fix kloud configuration

--- config/generateKonfig.coffee
+++ config/generateKonfig.coffee
@@ -142,6 +142,7 @@
     janitorSecretKey: credentials.janitor.secretKey
     vmWatcherSecretKey: credentials.vmwatcher.secretKey
     paymentWebHookSecretKey: credentials.paymentwebhook.secretKey
+    terraformerSecretKey: credentials.terraformer.secretKey

     awsAccessKeyId: credentials.awsKeys.vm_kloud.accessKeyId
     awsSecretAccessKey: credentials.awsKeys.vm_kloud.secretAccessKey


Fix gulpfile

Fix gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -22,6 +22,7 @@
   env == "production"

 gulp.task 'build', ['html', 'sass', 'compile_main', 'browserify'], ->
+gulp.task 'build_for_watch', ['html', 'sass', 'compile_main', 'watchify'], ->

 gulp.task 'clean', (cb) ->
   del(['dist/**/*', 'build/**/*'], cb)
@@ -130,7 +131,7 @@
   )
 gulp.task 'package', ['package:mac', 'package:linux']

-gulp.task 'watch', ['build', 'watchify'], ->
+gulp.task 'watch', ['build_for_watch'], ->
   electron.start()
   gulp.watch('src/main.js', ['compile_main', electron.restart])
   # gulp.watch(['src/**/*.{js,jsx}', '!src/main.js'], ['compile_renderer'])


- Added tests for switching models in views, reported in issue #74

--- test/knockback-view-model/test.coffee
+++ test/knockback-view-model/test.coffee
@@ -510,4 +510,119 @@
   kb.release(view_model)

   equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
-)+)
+
+test("18. model replacement", ->
+  kb.statistics = new kb.Statistics()
+  model_opts =
+    attributes:
+        prop: 1
+      defaults:
+        prop: 1
+
+  Model = if kb.Parse then kb.Model.extend('Model', model_opts) else kb.Model.extend(model_opts)
+
+  model1 = new Model
+  view_model = kb.viewModel(model1)
+
+  equal(view_model.prop(), 1, "sanity check")
+  model2 = new Model({ prop: 2 })
+  equal(model2.get('prop'), 2, "sanity check 2")
+
+  view_model.model(model2)
+
+  equal(model2.get('prop'), 2, "switched in model shouldn't inherit values from previous model")
+  equal(view_model.prop(), 2, "view model should have the value of the switched in model")
+
+  kb.release(view_model)
+
+  equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
+)
+
+test("19. model replacement with select", ->
+  kb.statistics = new kb.Statistics()
+
+  model_opts =
+    attributes:
+        prop: 1
+      defaults:
+        prop: 1
+
+  Model = if kb.Parse then kb.Model.extend('Model', model_opts) else kb.Model.extend(model_opts)
+
+  model1 = new Model
+  view_model = kb.viewModel(model1)
+
+
+  el = $('''
+

+      <select data-bind="value: prop">
+          <option value="" selected>---</option>
+          <option value="1">1</option>
+          <option value="2">2</option>
+      </select>
+
''')
+  $('body').append(el)
+
+  widget = el.find('select')
+
+  equal(widget.val(), "", "select should be empty to start with")
+  ko.applyBindings(view_model, el.get(0))
+  equal(widget.val(), "1", "select should be equal to the model after bindings applied")
+
+  model2 = new Model({prop : 2})
+  equal(model2.get('prop'), 2, "sanity check 2")
+  view_model.model(model2)
+
+  equal(widget.val(), 2, "model sets the select")
+  equal(model2.get('prop'), 2, "switched in model shouldn't inherit values from previous model")
+  equal(view_model.model(), model2, "view_model.model should be the same as the new model")
+  equal(view_model.prop(), 2, "view model should have the value of the switched in model")
+
+  el.remove()
+  kb.release(view_model)
+
+  equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
+)
+
+test("20. model replacement with input", ->
+  kb.statistics = new kb.Statistics()
+
+  model_opts =
+    attributes:
+        prop: 1
+      defaults:
+        prop: 1
+
+  Model = if kb.Parse then kb.Model.extend('Model', model_opts) else kb.Model.extend(model_opts)
+
+  model1 = new Model
+  view_model = kb.viewModel(model1)
+
+
+  el = $('''
+

+      <input data-bind="value: prop" />
+
''')
+  $('body').append(el)
+
+  widget = el.find('input')
+
+  equal(widget.val(), "", "input should be empty to start with")
+  ko.applyBindings(view_model, el.get(0))
+  equal(widget.val(), "1", "input should be equal to the model after bindings applied")
+
+  model2 = new Model({prop : 2})
+  equal(model2.get('prop'), 2, "sanity check 2")
+  view_model.model(model2)
+
+  equal(widget.val(), 2, "model sets the select")
+  equal(model2.get('prop'), 2, "switched in model shouldn't inherit values from previous model")
+  equal(view_model.model(), model2, "view_model.model should be the same as the new model")
+  equal(view_model.prop(), 2, "view model should have the value of the switched in model")
+
+  el.remove()
+  kb.release(view_model)
+
+  equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
+)


Issue #870 ... placeholders in destructuring assignment.

--- nodes.coffee
+++ nodes.coffee
@@ -908,6 +908,7 @@
     {value}   = this
     {objects} = @variable.base
     unless olen = objects.length
+      return false if top
       code = value.compile o
       return if o.level >= LEVEL_OP then "(#{code})" else code
     isObject = @variable.isObject()
@@ -968,7 +969,7 @@
         val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
       assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_TOP
     assigns.push vvar unless top
-    code = assigns.join ', '
+    code = (compact assigns).join ', '
     if o.level < LEVEL_LIST then code else "(#{code})"

   # When compiling a conditional assignment, take care to ensure that the


reverting to 26a28abcb572eef4ba20151ab415ba2dc5632055 behaviour

This is only slightly problematic for the most pathological of cases
where a prelude is followed by a set of statements, none of which
generate top-level variables or attempt to return. In these cases, the
non-prelude statements will be indented. See related discussion at
e4b3e838e2f308d5d39eb54361374fb171ef58c9.

--- nodes.coffee
+++ nodes.coffee
@@ -243,12 +243,16 @@
   # It would be better not to generate them in the first place, but for now,
   # clean up obvious double-parentheses.
   compileRoot: (o) ->
-    o.indent  = if o.bare then '' else TAB
+    o.indent  = ""
     o.scope   = new Scope null, this, null
     o.level   = LEVEL_TOP
     @spaced   = yes
     prelude   = ""
-    unless o.bare
+    hasReturn = no
+    @traverseChildren no, (e) ->
+      hasReturn = yes if e instanceof Return
+      !hasReturn
+    if hasReturn or not o.bare
       preludeExps = for exp, i in @expressions
         e = exp.unwrap()
         break unless e instanceof Comment or e instanceof Literal
@@ -257,9 +261,13 @@
       @expressions = preludeExps
       prelude = "#{@compileNode o}\n" if preludeExps.length
       @expressions = rest
+      # We assume that we will need the safety wrapper.
+      # This is the best we can do without compiling twice.
+      o.indent = TAB
     code = @compileWithDeclarations o
-    return prelude + code if o.bare
-    "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+    if hasReturn or (not o.bare and o.scope.variables.length > 1)
+      return "#{prelude}(function() {\n#{code}\n}).call(this);\n"
+    prelude + code

   # Compile the expressions body for the contents of a function, with
   # declarations of all inner variables pushed up to the top.


Fixed a small bug about flexible splats that occured when the leading array was a literal

--- nodes.coffee
+++ nodes.coffee
@@ -375,9 +375,9 @@
       if not (arg instanceof SplatNode)
         prev: args[i - 1]
         if i is 1 and prev.substr(0, 1) is '[' and prev.substr(prev.length - 1, 1) is ']'
-          args[i - 1]: "${prev.substr(0, prev.length - 2)}, $code]"
+          args[i - 1]: "${prev.substr(0, prev.length - 1)}, $code]"
           continue
-        else if i > 1 and prev.substr(8, 1) is '[' and prev.substr(prev.length - 2, 1) is ']'
+        else if i > 1 and prev.substr(0, 9) is '.concat([' and prev.substr(prev.length - 2, 2) is '])'
           args[i - 1]: "${prev.substr(0, prev.length - 2)}, $code])"
           continue
         else


Ticket #423. When functions are generated within comprehensions ... the comprehensions should close over the element instead of sharing it.

--- nodes.coffee
+++ nodes.coffee
@@ -1188,44 +1188,48 @@
   # comprehensions. Some of the generated code can be shared in common, and
   # some cannot.
   compileNode: (o) ->
-    topLevel:      del(o, 'top') and not @returns
+    topLevel:       del(o, 'top') and not @returns
     range:          @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
     source:         if range then @source.base else @source
+    codeInBody:     @body.contains (n) -> n instanceof CodeNode
     scope:          o.scope
     name:           @name and @name.compile o
     index:          @index and @index.compile o
-    scope.find name  if name and not @pattern
-    scope.find index if index
-    bodyDent:      @idt 1
+    scope.find name  if name and not @pattern and not codeInBody
+    scope.find index if index and not codeInBody
     rvar:           scope.freeVariable() unless topLevel
     ivar:           if range then name else index or scope.freeVariable()
-    varPart:       ''
+    varPart:        ''
     body:           Expressions.wrap([@body])
     if range
-      sourcePart:  source.compileVariables o
-      forPart:     source.compile merge o, {index: ivar, step: @step}
+      sourcePart:   source.compileVariables o
+      forPart:      source.compile merge o, {index: ivar, step: @step}
     else
       svar:         scope.freeVariable()
-      sourcePart:  "$svar = ${ @source.compile(o) };"
+      sourcePart:   "$svar = ${ @source.compile(o) };"
       if @pattern
-        varPart:   new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + "\n"
+        namePart:   new AssignNode(@name, literal("$svar[$ivar]")).compile(merge o, {indent: @idt(1), top: true}) + "\n"
       else
-        varPart:   "$bodyDent$name = $svar[$ivar];\n" if name
+        namePart:   "$name = $svar[$ivar]" if name
       unless @object
         lvar:       scope.freeVariable()
-        stepPart:  if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
-        forPart:   "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $stepPart"
-    sourcePart:    (if rvar then "$rvar = []; " else '') + sourcePart
-    sourcePart:    if sourcePart then "$@tab$sourcePart\n$@tab" else @tab
-    returnResult:  @compileReturnValue(rvar, o)
-
-    body:           ClosureNode.wrap(body, true) if topLevel and body.contains (n) -> n instanceof CodeNode
+        stepPart:   if @step then "$ivar += ${ @step.compile(o) }" else "$ivar++"
+        forPart:    "$ivar = 0, $lvar = ${svar}.length; $ivar < $lvar; $stepPart"
+    sourcePart:     (if rvar then "$rvar = []; " else '') + sourcePart
+    sourcePart:     if sourcePart then "$@tab$sourcePart\n$@tab" else @tab
+    returnResult:   @compileReturnValue(rvar, o)
+
     body:           PushNode.wrap(rvar, body) unless topLevel
     if @guard
       body:         Expressions.wrap([new IfNode(@guard, body)])
+    if codeInBody
+      body.unshift  literal "var $namePart" if namePart
+      body:         ClosureNode.wrap(body, true)
+    else
+      varPart:      "${@idt(1)}$namePart;\n" if namePart
     if @object
-      forPart: "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)"
-    body:           body.compile(merge(o, {indent: bodyDent, top: true}))
+      forPart:      "$ivar in $svar) { if (${utility('hasProp')}.call($svar, $ivar)"
+    body:           body.compile(merge(o, {indent: @idt(1), top: true}))
     vars:           if range then name else "$name, $ivar"
     close:          if @object then '}}' else '}'
     "${sourcePart}for ($forPart) {\n$varPart$body\n$@tab$close$returnResult"


Consolidate announce options into function

--- src/bitbucket-pr.coffee
+++ src/bitbucket-pr.coffee
@@ -14,17 +14,16 @@
 DEFAULT_ROOM = process.env.HUBOT_BITBUCKET_PULLREQUEST_ROOM

 # Set default actions to announce
-DEFAULT_ANNOUNCE_OPTIONS = [
-  'created', 'updated', 'declined', 'merged',
-  'comment_created', 'approve', 'unapprove'
-]

 getEnvAnnounceOptions = ->
   # Replace announce options if set in the environment
   if process.env.HUBOT_BITBUCKET_PULLREQUEST_ANNOUNCE
     process.env.HUBOT_BITBUCKET_PULLREQUEST_ANNOUNCE.replace(/[^a-z\,]+/, '').split(',')
-
-ANNOUNCE_OPTIONS = getEnvAnnounceOptions() ? DEFAULT_ANNOUNCE_OPTIONS
+  # Fall back to default actions to announce
+  else
+    ['created', 'updated', 'declined', 'merged', 'comment_created', 'approve', 'unapprove']
+
+ANNOUNCE_OPTIONS = getEnvAnnounceOptions()

 ENCOURAGEMENTS = [
   ':thumbsup:', 'That was a nice thing you did.', 'Boomtown',


[Feature] App quit on window closed

[Feature] App quit on window closed

--- src/browser/main.coffee
+++ src/browser/main.coffee
@@ -40,7 +40,7 @@
     args = parseCommandLine()

     app.on "window-all-closed", ->
-        app.quit() if process.platform isnt "darwin"
+        app.quit()
         return

     app.on "ready", ->


Added basic tests for new functions

Added basic tests for new functions

--- test/whip.spec.coffee
+++ test/whip.spec.coffee
@@ -126,3 +126,16 @@
 # should choose the right branch
 assert (ip '(if true 42 420)')
 assert (ip '(if false 42 420)') is 420
+
+# comparatives
+
+# should return true
+# TODO: Expand this sonofabitch
+assert ip '(greater 10 5)'
+assert ip '(lesser 5 10)'
+assert ip '(equal 10 10)'
+assert ip '(not false)'
+assert ip('((-> (x) (<< "bar" x)) ("foo"))').is ['foo', 'bar']
+
+# should return length
+assert ip('(length (1 2))') is 2


Refactor with destructuring and expand InStateMenu.

A bit cleaner than storing a reference to the game instance.

Adds a pauseHandler, which defaults to pausing the whole game (including
menu interactions).

--- src/in-state-menu.coffee
+++ src/in-state-menu.coffee
@@ -10,35 +10,40 @@

   class InStateMenu

-    constructor: (@textItems, @game, @options) ->
+    constructor: (@textItems, game, options = {}) ->
+      {@pauseHandler, @layout, @toggleKeyCode} = _.defaults options,
+        layout: { y: 120, baseline: 40 }
+        pauseHandler: (paused) -> game.paused = paused
+        toggleKeyCode: Keyboard.P
+
+      {@add, @height, @input, @width, @world} = game
+
       @_initialize()

     _initialize: ->
-      @group = @game.add.group()
+      @group = @add.group()

-      @overlay = @game.add.graphics 0, 0, @group
+      @overlay = @add.graphics 0, 0, @group
       @overlay.beginFill 0x000000, 0.2
-      @overlay.drawRect 0, 0, @game.width, @game.height
+      @overlay.drawRect 0, 0, @width, @height
       @overlay.endFill()

-      @add = @game.add # For TextMixin.
-      @world = @game.world # For TextMixin.
-      @layout = @options?.layout or { y: 120, baseline: 40 }
       for [text, style] in @textItems
         _.defaults style, { fill: '#fff', font: 'Enriqueta' }
         @addCenteredText text, @layout, style, @group

-      @toggleKey = @game.input.keyboard.addKey @options?.hotkey or Keyboard.P
-      @toggleKey.onDown.add @toggle, @
+      @toggleKey = @input.keyboard.addKey @toggleKeyCode
+      @toggleKey.onDown.add => @toggle()

       @toggle off

     destroy: ->
       @toggleKey.onDown.removeAll()

-    toggle: (visible) ->
-      visible ?= not @group.visible
-      @group.visible = visible
+    toggle: (toggled) ->
+      toggled ?= not @group.visible
+      @group.visible = toggled
+      @pauseHandler toggled

   _.extend InStateMenu::, Helpers.TextMixin


Keep the original contentInset when using scroll components in the flow component

--- framer/Components/FlowComponent.coffee
+++ framer/Components/FlowComponent.coffee
@@ -58,7 +58,7 @@

 	reset: ->
-
+
 		if @_stack
 			for item in @_stack
 				item.layer.visible = false unless item.layer is @_initial
@@ -180,9 +180,9 @@

 		# Maybe people (Jorn) pass in a layer accidentally
 		options = {} if options instanceof(Framer._Layer)
-
+
 		options = _.defaults({}, options, {count: 1, animate: true})
-
+
 		if options.count > 1
 			count = options.count
 			@showPrevious(animate: false, count: 1) for n in [2..count]
@@ -250,6 +250,7 @@
 		# If this layer is a ScrollComponent we do not have to add another one
 		if layer instanceof ScrollComponent
 			scroll = layer
+			scroll._originalContentInset ?= scroll.contentInset

 		layer.point = Utils.pointZero()

@@ -265,9 +266,9 @@
 			# to access the magically created scroll component.
 			_addListener = layer.addListener
 			layer.on = (event, args...) ->
-
+
 				_addListener.apply(layer, [event, args...])
-
+
 				# But only the actual scroll events
 				if event in [
 					Events.ScrollStart,
@@ -285,9 +286,11 @@
 		scroll.scrollHorizontal = layer.width > width
 		scroll.scrollVertical = layer.height > height

-		contentInset = {}
-		contentInset.top = @header.height if @header
-		contentInset.bottom = @footer.height if @footer
+		contentInset =
+			top: scroll._originalContentInset?.top ? 0
+			bottom: scroll._originalContentInset?.bottom ? 0
+		contentInset.top += @header.height if @header
+		contentInset.bottom += @footer.height if @footer
 		scroll.contentInset = contentInset

 		return scroll


make sure start end end points exist and check whether conten is animating on scrollend

--- framer/Components/PageComponent.coffee
+++ framer/Components/PageComponent.coffee
@@ -155,8 +155,8 @@

 	_scrollEnd: =>

-		return if @_isAnimating
-
+		return if @content.isAnimating
+
 		velocity = @content.draggable.velocity

 		# See if we meet the minimum velocity to scroll to the next page. If not we snap
@@ -174,9 +174,10 @@
 			start = @content.draggable._layerStartPoint
 			end = @content.draggable.layer.point

-			if start.x isnt end.x or start.y isnt end.y
-				@snapToPage(@closestPage, true, @animationOptions)
-				return
+			if start? and end?
+				if start.x isnt end.x or start.y isnt end.y
+					@snapToPage(@closestPage, true, @animationOptions)
+					return

 		# Figure out which direction we are scrolling to and make a sorted list of
 		# layers on that side, sorted by absolute distance so we can pick the first.


auth worker: warn when there is no correlation name for a kite request.

--- workers/auth/lib/auth/authworker.coffee
+++ workers/auth/lib/auth/authworker.coffee
@@ -245,6 +245,9 @@
           return console.error "No service info! #{messageData.name}"

         { serviceUniqueName, serviceGenericName, loadBalancing } = serviceInfo
+
+        if messageData.serviceType is 'kite' and not messageData.correlationName
+          console.warn "No correlation name!", messageData, routingKey

         params = {
           serviceGenericName


fix(option): custom spa.html file when no spa.src.dir is supplied

--- config/config-spa.coffee
+++ config/config-spa.coffee
@@ -25,9 +25,10 @@
 	distFile = options.spa.dist.file or srcFile
 	srcDir   = getSrcDir custom, options.spa.src.dir
 	if custom
+		distDir = options.spa.src.dir or ''
 		customDistDir = path.join(
 			config.dist.app.client.dir
-			options.spa.src.dir
+			distDir
 		)
 		customDistPath = path.join customDistDir, srcFile


unbind unnecessarily bound functions in publisher

--- src/lib/Publisher.coffee
+++ src/lib/Publisher.coffee
@@ -41,8 +41,7 @@

     if @confirm then @confirmMode()

-  publish: (exchange, routingKey, data, options, cb)=>
-
+  publish: (exchange, routingKey, data, options, cb)->
     if typeof options is 'function'
       cb = options
       options = {}
@@ -101,7 +100,7 @@
       cb() if cb?

-  _onMethod: (channel, method, args)=>
+  _onMethod: (channel, method, args)->
     @previousMethod = method
     @previousArgs   = args

@@ -111,7 +110,7 @@
           # debug 4, ()=> return JSON.stringify args
           @_gotSeq args.deliveryTag, args.multiple

-  _onContentHeader: (channel, classInfo, weight, properties, size)=>
+  _onContentHeader: (channel, classInfo, weight, properties, size)->
     switch @previousMethod
       when methods.basicReturn
         if properties.headers?['x-seq']?
@@ -120,7 +119,7 @@
   _onContent: (channel, data)->
     # Content is not needed efen on a basicReturn

-  _waitForSeq: (seq, cb)=>
+  _waitForSeq: (seq, cb)->
     if typeof cb is 'function'
       @seqCallbacks[seq] = cb
     else


Refactoring to always pass by checksum method

--- src/local/watcher.coffee
+++ src/local/watcher.coffee
@@ -119,7 +119,7 @@
     createDoc: (filePath, stats, callback) =>
         absPath = path.join @syncPath, filePath
         [mimeType, fileClass] = @getFileClass absPath
-        @checksumer.push filePath: absPath, (err, checksum) ->
+        @checksum absPath, (err, checksum) ->
             doc =
                 path: filePath
                 docType: 'file'


Group/worker: added new field(favicon) to group collection

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -254,6 +254,7 @@
       customize     :
         coverPhoto  : String
         logo        : String
+        favicon     : String
       payment       :
         plan        : String
         paymentQuota: Number


KodingAppsController: Make sure the application code publicly reachable before attempting to run it

--- client/Main/kodingappscontroller.coffee
+++ client/Main/kodingappscontroller.coffee
@@ -139,7 +139,17 @@
           loader     :
             color    : "#ffffff"
             diameter : 16
-          callback   : => @runApprovedApp jApp, options, -> modal.destroy()
+          callback   : =>
+            $.ajax
+              type     : "HEAD"
+              url      : jApp.urls.script
+              complete : (res, state)=>
+                modal.destroy()
+                if res.status is 200
+                  @runApprovedApp jApp, options, -> modal.destroy()
+                else
+                  new KDNotificationView
+                    title : "Application is not reachable"

         cancel       :
           style      : "modal-cancel"


DrawingBoard: Emit change event when drawing board is created by user.

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -142,7 +142,9 @@
   createDrawingBoard: (paneHash) ->
     drawingPane = new IDE.DrawingPane { hash: paneHash }
     @createPane_ drawingPane, { name: 'Drawing' }
-    @emitChange  drawingPane, context: {}
+
+    unless paneHash
+      @emitChange  drawingPane, context: {}

   createPreview: (url) ->
     previewPane = new IDE.PreviewPane { url }


sms functionaliteit (opschoning)

sms functionaliteit (opschoning)

--- client/templates/includes/footer.coffee
+++ client/templates/includes/footer.coffee
@@ -20,9 +20,6 @@
       phone_number = selected_users[0].telephone_nr
       "sms:#{phone_number}"
 #      "whatsapp://send?abid=#{phone_number}"
-#    else
-#      skype_ids = (user.skype_id for user in selected_users when user.skype_id).join(';')
-#      "skype:#{skype_ids}?call"

 Template.footer.events
   'click .logout': (e) ->


I wish BBT would actually be popular enough to flood Google

--- chrome/content/zotero-better-bibtex/errorReport.coffee
+++ chrome/content/zotero-better-bibtex/errorReport.coffee
@@ -71,33 +71,15 @@
       encrypt = new JSEncrypt()
       encrypt.setPublicKey(Zotero.File.getContentsFromURL('resource://zotero-better-bibtex/error-reporting.pub.pem'))
       encrypted = encrypt.encrypt(report)
+      report = "http://zotplus.github.io/report.html##{encrypted}"
+      report = Zotero.File.getContentsFromURL("http://is.gd/create.php?format=simple&url=#{encodeURIComponent(report)}")

-      Zotero.HTTP.doPost('https://www.googleapis.com/urlshortener/v1/url', JSON.stringify({longUrl: "http://zotplus.github.io/report.html##{encrypted}"}), ((xmlhttp) ->
-        wizard = document.getElementById('zotero-error-report')
-        return  unless wizard
+      wizard.advance()
+      wizard.getButton('cancel').disabled = true
+      wizard.canRewind = false

-        if !xmlhttp.responseText || xmlhttp.status != 200
-          #ps.alert(null, Zotero.getString('general.error'), Zotero.getString('errorReport.repoCannotBeContacted'))
-          ps.alert(null, Zotero.getString('general.error'), "Status: #{xmlhttp.status}")
-          wizard.rewind()
-          return
-
-        try
-          report = JSON.parse(xmlhttp.responseText).id
-        catch
-          ps.alert(null, Zotero.getString('general.error'), Zotero.getString('errorReport.invalidResponseRepository'))
-          wizard.rewind()
-          return
-
-        wizard.advance()
-        wizard.getButton('cancel').disabled = true
-        wizard.canRewind = false
-
-        document.getElementById('zotero-report-id').setAttribute('value', report)
-        document.getElementById('zotero-report-result').hidden = false
-        return
-        ), {'Content-Type': 'application/json'})
-
+      document.getElementById('zotero-report-id').setAttribute('value', report)
+      document.getElementById('zotero-report-result').hidden = false
       return
     )
     return


Hackathon: removed unused counts from the response

--- workers/social/lib/social/models/campaigns/wfgh.coffee
+++ workers/social/lib/social/models/campaigns/wfgh.coffee
@@ -63,7 +63,7 @@

   getUserStats = (username, callback) ->

-    JWFGH.one {username}, (err, applied)->
+    JWFGH.one { username }, (err, applied)->

       return callback err  if err

@@ -71,41 +71,32 @@
       isApproved  = applied?.approved
       isWinner    = applied?.winner

-      callback null, {isApplicant, isApproved, isWinner}
+      callback null, { isApplicant, isApproved, isWinner }

   @getStats = (account, callback) ->

-    JCampaign.get 'WFGH', (err, campaign) ->
+    JCampaign.get 'WFGH', (err, _campaign) ->

-      if err or not campaign or not campaign.content.active
+      if err or not _campaign or not _campaign.content.active

         return callback message : 'expired'

-      JWFGH.count {}, (err, totalApplicants)->
+      kallback = (err, userStats) ->
+        {isApplicant, isApproved, isWinner} = userStats

-        return callback err  if err
+        campaign           = _campaign.content
+        totalApplicants    = campaign.totalApplicants or 0
+        approvedApplicants = campaign.approvedApplicants or 0

-        JWFGH.count approved : yes, (err, realApprovedApplicants)->
+        res = { approvedApplicants, totalApplicants, isApplicant, isApproved, isWinner, campaign }

-          return callback err  if err
+        callback err, res

-          kallback = (err, userStats) ->
-            {isApplicant, isApproved, isWinner} = userStats
-
-            approvedApplicants = campaign.content.approvedApplicants or 0
-            aac                = realApprovedApplicants
-
-            callback err, {
-              totalApplicants, approvedApplicants, aac
-              isApplicant, isApproved, isWinner
-              campaign: campaign.content
-            }
-
-          if username = account?.profile?.nickname
-          then getUserStats username, kallback
-          else kallback null, {
-            isWinner      : no
-            isApproved    : no
-            isApplication : no
-          }
+      if username = account?.profile?.nickname
+      then getUserStats username, kallback
+      else kallback null, {
+        isWinner      : no
+        isApproved    : no
+        isApplication : no
+      }


location: add signature annotations

location: add signature annotations

--- workers/social/lib/social/models/location/index.coffee
+++ workers/social/lib/social/models/location/index.coffee
@@ -1,4 +1,4 @@
-{Model, secure, daisy} = require 'bongo'
+{Model, secure, daisy, signature} = require 'bongo'

 module.exports = class JLocation extends Model

@@ -6,12 +6,15 @@

   @set
     sharedMethods :
-      static      : [
-        'importAll'
-        'importStates'
-        'one'
-        'fetchStatesByCountryCode'
-      ]
+      static      :
+        importAll:
+          (signature Function)
+        importStates:
+          (signature Function)
+        one:
+          (signature Object, Function)
+        fetchStatesByCountryCode:
+          (signature String, Function)
     schema        :
       zip         : String
       city        : String


Explain why SSH builds are running

Explain why SSH builds are running

--- js/inner.coffee
+++ js/inner.coffee
@@ -295,6 +295,8 @@
           "First build"
         when "retry"
           "Manual retry of build #{@retry_of()}"
+        when "ssh"
+          "Retry of build #{@retry_of()}, with SSH enabled"
         when "auto-retry"
           "Auto-retry of build #{@retry_of()}"
         when "trigger"


Show the plan wizard to people who havent paid

--- js/inner.coffee
+++ js/inner.coffee
@@ -820,7 +820,7 @@
       @chosenPlan(data.plan)
       @concurrency(data.concurrency or 1)
       @parallelism(data.parallelism or 1)
-      if @chosenPlan
+      if @chosenPlan()
         @advanceWizard(5)

   loadOrganizations: () =>


No longer flushing for delete

No longer flushing for delete

--- admin/app/scripts/main.coffee
+++ admin/app/scripts/main.coffee
@@ -91,7 +91,8 @@
       console.log(error)
       console.log(response)
       @client.indices.flush(()->
-        location.reload())
+        location.reload()
+      )
     )

   submitDeleteDataset: ()=>
@@ -102,8 +103,7 @@
     }, (error, response)=>
       console.log(error)
       console.log(response)
-      @client.indices.flush(()->
-        location.reload())
+      location.reload()
     )

   newDataset: ()=>


hoge deploy

hoge deploy

--- app/assets/javascripts/calendar.coffee
+++ app/assets/javascripts/calendar.coffee
@@ -1,4 +1,4 @@
-# global = this
+global = this
 #
 # global.refetch = ->
 #   $('#calendar').fullCalendar 'refetchEvents'


Make results window draggable

Make results window draggable

--- app/assets/javascripts/circuits.coffee
+++ app/assets/javascripts/circuits.coffee
@@ -555,6 +555,7 @@

 showResultsContainer = ->
   $("#resultsContainer").show()
+  $("#resultsContainer").draggable()

 hideResultsContainer = ->
   $("#resultsContainer").hide()


fixed bug with saving list

fixed bug with saving list

--- app/assets/javascripts/lists.js.coffee
+++ app/assets/javascripts/lists.js.coffee
@@ -35,18 +35,17 @@
     @selected

 class App.backbone.List extends Backbone.Model
+  change: -> @save()
+
   changeProperties: (properties) ->
-    @setItems()
-    @set(properties)
+    @set($.extend properties, items: App.root.asJson().children)
     @propertiesView.render()
     @save()

   updateItems: ->
-    @setItems()
+    @set items: App.root.asJson().children
     @save

-  setItems: ->
-    @set items: App.root.asJson().children

 class App.backbone.Lists extends Backbone.Collection
   model: App.backbone.List


Correction of update popup. Improve fluidity.

--- app/assets/javascripts/nodes.js.coffee
+++ app/assets/javascripts/nodes.js.coffee
@@ -83,21 +83,22 @@
             @construct_list()
       # Updates the date for all the characters of the map
       update_date: (new_date)->
-        @last_date = new_date
-        if !@date_blocked
-          # One modif max for 500 ms
-          @date_blocked = true
-          setTimeout( =>
-            @date_blocked = false
-            # Ensure that the last date set is the one on the slider
+        if new_date != @last_date
+          @last_date = new_date
+          if !@date_blocked
+            # One modif max for 500 ms
+            @date_blocked = true
             setTimeout( =>
-              if !@date_blocked && @last_date != new_date
-                @update_date(@last_date)
-            100)
-          500)
-          console.log(new_date)
-          for c in @list
-            c.update_date(new_date)
+              @date_blocked = false
+              # Ensure that the last date set is the one on the slider
+              setTimeout( =>
+                if !@date_blocked && @last_date != new_date
+                  @update_date(@last_date)
+              10)
+            15)
+            console.log(new_date)
+            for c in @list
+              c.update_date(new_date)

     # A character is the character and its nodes
     # @character: Character model from server
@@ -118,6 +119,7 @@
         @node = $.extend({}, @nodes[@next_node_index])
         @node_obj.update_node_content(@node)
         @update_node_obj_position()
+        @last_node_index = @next_node_index
       # Update position of @node and @node_obj
       # @node_obj must exists
       update_node_obj_position: ->
@@ -138,14 +140,16 @@
           diff_dates = jv_date(suiv_node.begin_at) - jv_date(cur_node.end_at)
           diff_dates_current = jv_date(@date) - jv_date(cur_node.end_at)
           if diff_dates == 0
+            # Nodes times are touching each other
+            # Whoops ! Warning: Div by 0 !
             new_lat = cur_lat
             new_lng = cur_lng
           else
             new_lat = cur_lat + diff_lat * (diff_dates_current/diff_dates)
             new_lng = cur_lng + diff_lng * (diff_dates_current/diff_dates)
-          console.log("char:", @character.name)
-          console.log('latlng:', cur_lat, cur_lng, new_lat, new_lng)
-          console.log('diffs:', diff_lat, diff_lng, diff_dates, diff_dates_current)
+          #console.log("char:", @character.name)
+          #console.log('latlng:', cur_lat, cur_lng, new_lat, new_lng)
+          #console.log('diffs:', diff_lat, diff_lng, diff_dates, diff_dates_current)
         @node.latitude = new_lat
         @node.longitude = new_lng
         @node_obj.update_node_position(@node)
@@ -257,7 +261,7 @@
             @node.longitude = latlng.lng
             @update_on_server()
         )
-        @update_popup()
+        @create_popup()

       # Updates only node position according to the content of node
       # Beware to not change other fields of node, except real.
@@ -267,22 +271,27 @@
       # Updates the position of the marker on the map
       update_latlng: ->
         @marker.setLatLng([@node.latitude, @node.longitude])
-        @marker.update()
+        #@marker.update()

       # Update whole content of node (popup, position..) according to the content of node
       update_node_content: (node) ->
         console.log("Update node content", @node.title, "->", node.title)
         @node = node
         @update_popup()
+      popup_content: ->
+        "
#{@node.title}
#{@node.resume}
#{@character_names}"
+
       # Destroy the popup
       destroy_popup: ->
         console.log("destroy a popup")
         @marker.closePopup()
         @marker.unbindPopup()
+      create_popup: ->
+        console.log("create a popup", @node.title)
+        @marker.bindPopup @popup_content()
       update_popup: ->
-        console.log("create a popup", @node.title)
-        @destroy_popup()
-        @marker.bindPopup "
#{@node.title}
#{@node.resume}
#{@character_names}"
+        console.log("update a popup", @node.title)
+        @marker.setPopupContent @popup_content()

       # Destroy popup and marker
       destroy: ->


Display upload test for IE uploads.

Display upload test for IE uploads.

--- app/assets/javascripts/s3_relay.coffee
+++ app/assets/javascripts/s3_relay.coffee
@@ -53,18 +53,23 @@
       uuid = data.uuid

       uploadList = $(".s3r-upload-list", container)
-      uploadList.append("

#{fileName}

")
+      uploadList.append("

#{fileName}

")
       fileColumn = $(".s3r-upload-list ##{uuid} .s3r-file-url", container)
       progressColumn = $(".s3r-upload-list ##{uuid} .s3r-progress", container)
       progressBar = $(".s3r-bar", progressColumn)
       progressMeter = $(".s3r-meter", progressColumn)

       xhr.upload.addEventListener "progress", (ev) ->
-        percentage = ((ev.position / ev.totalSize) * 100.0).toFixed(0)
-        progressMeter.css "width", "#{percentage}%"
+        if ev.position
+          percentage = ((ev.position / ev.totalSize) * 100.0).toFixed(0)
+          progressBar.show()
+          progressMeter.css "width", "#{percentage}%"
+        else
+          progressColumn.text("Uploading...")  # IE can't get position

       xhr.onreadystatechange = (ev) ->
         if xhr.readyState is 4
+          progressColumn.text("")  # IE can't get position
           progressBar.remove()

           if xhr.status == 201


fix small bug with advanced option expansion

--- app/assets/javascripts/tasks.js.coffee
+++ app/assets/javascripts/tasks.js.coffee
@@ -95,6 +95,7 @@
     advanced_section = $(this)
     if  advanced_section.find('[data-error]').size() > 0
       advanced_section.show()
+      $('.expand_button').html('[–]')

   # enable button if we return back in history to a page where button was disabled during submission
   $('.task_params input[type="submit"]').attr('disabled', false)


Add model name with two property

Add model name with two property

--- lib/helpers/metadata_builder.coffee
+++ lib/helpers/metadata_builder.coffee
@@ -103,11 +103,19 @@
       return metadata
     constructEntityMetaDatas: (model)->
       if model.modelName?
-        if @metadatas[model.modelName]?
-          return @metadatas[model.modelName]
+        mdName = model.modelName.split('.')
+        if mdName.length is 1
+          if @metadatas[model.modelName]?
+            return @metadatas[model.modelName]
+          else
+            console.warn("The metadatas does not have properties for this model name.")
+            return {}
         else
-          console.warn("The metadatas does not have properties for this model name.")
-          return {}
+          if @metadatas[mdName[0]][mdName[1]]?
+            return @metadatas[mdName[0]][mdName[1]]
+          else
+            console.warn("The metadatas does not have properties for this model name.")
+            return {}
       else
         throw new ArgumentNullException('The model sould have a model name in order to build its metadatas')


Fix typo for Batman.Filters.titleize

Fix typo for Batman.Filters.titleize

--- modules/view_layer/filters.coffee
+++ modules/view_layer/filters.coffee
@@ -200,7 +200,7 @@
       falsyVal

 do ->
-  for k in ['capitalize', 'titlelize', 'singularize', 'underscore', 'camelize', 'toSentence', 'ordinalize']
+  for k in ['capitalize', 'titleize', 'singularize', 'underscore', 'camelize', 'toSentence', 'ordinalize']
     Filters[k] = buntUndefined(helpers[k])


修复创建项目路径显示bug

修复创建项目路径显示bug

--- lib/project/new-project-info.coffee
+++ lib/project/new-project-info.coffee
@@ -36,8 +36,9 @@
   openFolder: ->
     openDirectory(title: 'Select Path')
     .then (destPath) =>
-      console.log  destPath
-      path = destPath[0] +'/'+ @appId.getText()
+      console.log destPath[0]
+      path = "#{destPath[0]}/#{@appId.getText()}"
+      console.log  path
       @appPath.setText path

   getElement: ->


Add checkpoint right before boss

Add checkpoint right before boss

--- app/scripts/lazerscripts/stage1.coffee
+++ app/scripts/lazerscripts/stage1.coffee
@@ -248,8 +248,13 @@
         @if((-> @player(1).active), @drop(item: 'pool', inFrontOf: @player(1)))
         @if((-> @player(2).active), @drop(item: 'pool', inFrontOf: @player(2)))
       )
-      @mineSwarm()
-      @setScenery('UnderBridge')
+      @parallel(
+        @mineSwarm()
+        @sequence(
+          @wait 5000
+          @setScenery('UnderBridge')
+        )
+      )
       @async @showText 'Warning!', color: '#FF0000', mode: 'blink'
       @while(
         @waitForScenery('UnderBridge', event: 'enter')
@@ -258,6 +263,7 @@
       @setSpeed 75
       @waitForScenery('UnderBridge', event: 'inScreen')
       @setSpeed 0
+      @checkpoint @checkpointStart('UnderBridge', 234000)
       @placeSquad Game.Scripts.Stage1BossStage1
       @parallel(
         @if((-> @player(1).active), @drop(item: 'life', inFrontOf: @player(1)))


Bug fix

Bug fix

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -478,7 +478,7 @@
                 beforeClose: =>
                     return if @data is false

-                    fileName = @data.filename.trim()
+                    fileName = @data.filename.trim() if @data.filename
                     message = @data.message

                     if ((fileName and fileName.length is 0) or not fileName) and @data.afterStart is 'autoTail'


Remove console logging to not spam output

--- MeteorNoops.coffee
+++ MeteorNoops.coffee
@@ -1,5 +1,5 @@
 if not Meteor.server
-  console.log("spacejamio:cli - Meteor.server is not defined, creating noop implementations for all meteor ddp methods")
+  # Meteor.server is not defined, creating noop implementations for all meteor ddp methods
   _.each(['publish', 'methods', 'call', 'apply', 'onConnection'],
   (name) ->
     Meteor[name] = ->


Remove '(global)' which is not used in Powershell.

--- grammars/powershell.cson
+++ grammars/powershell.cson
@@ -79,12 +79,6 @@
   {
     'match': '\\b([1-9]+[0-9]*|0)'
     'name': 'constant.numeric.integer.decimal.powershell'
-  }
-  {
-    'captures':
-      '1':
-        'name': 'storage.modifier.global.powershell'
-    'match': '\\b(global)\\b'
   }
   {
     'comment': 'keywords that delimit flow blocks'


hand-merged son's branch

hand-merged son's branch

--- components/inputs/tokenizedinput.coffee
+++ components/inputs/tokenizedinput.coffee
@@ -32,7 +32,6 @@
     @input.on "keyup", @keyUpOnInput.bind @

   keyDownOnInput:(event)->
-    log "asdasd"
     @decorateLayer()
     # @layer.setClass "hide-tokens"


Revert "Count prefix applies only to leading mapping."

This reverts commit 7774beb6643c0d905f9caba4345453790af948ad.

Reverting this.  It can be done better (and capture some other incorrect
cases).

--- content_scripts/mode_key_handler.coffee
+++ content_scripts/mode_key_handler.coffee
@@ -89,18 +89,14 @@

   handleKeyChar: (keyChar) ->
     bgLog "Handling key #{keyChar}, mode=#{@name}."
-    nextKeyState = (mapping[keyChar] for mapping in @keyState when keyChar of mapping)
-    command = (mapping for mapping in nextKeyState when "command" of mapping)[0]
+    # Advance the key state.  The new key state is the current mappings of keyChar, plus @keyMapping.
+    @keyState = [(mapping[keyChar] for mapping in @keyState when keyChar of mapping)..., @keyMapping]
+    command = (mapping for mapping in @keyState when "command" of mapping)[0]
     if command
-      # Any count prefix applies only to the leading @keyState mapping; e.g., `7gj` should be `1j`.
-      @countPrefix = 0 unless command == @keyState[0][keyChar]
       count = if 0 < @countPrefix then @countPrefix else 1
       bgLog "Calling mode=#{@name}, command=#{command.command}, count=#{count}."
       @reset()
       @commandHandler {command, count}
-    else
-      # Advance the key state.
-      @keyState = [nextKeyState..., @keyMapping]
     false # Suppress event.

 root = exports ? window


Add a credit for AHRC funding

Add a credit for AHRC funding

--- content/science.coffee
+++ content/science.coffee
@@ -14,4 +14,6 @@

Validation of biological collections as a source of phenological data for use in climate change studies: a case study with the orchid Ophrys sphegodes

Botanical Society of Britain and Ireland

Biological Records Centre: recording schemes

+
+
‘Orchid Observers’ is funded by the Arts and Humanities Research Council (Science in Culture theme), as part of Constructing Scientific Communities.

                  '''


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/clamp_bounds.coffee
+++ src/clamp_bounds.coffee
@@ -41,8 +41,6 @@
     width: 32
     height: 32

-
-
   self.bind "update", ->
     I.x = I.x.clamp(I.width / 2, App.width - I.width / 2)
     I.y = I.y.clamp(I.height / 2, App.height - I.height / 2)


now plots just added from ipython are reflected in CDXContextView

--- src/coffee/views.coffee
+++ src/coffee/views.coffee
@@ -112,6 +112,9 @@
     @views_rendered = [false]
     @child_models = []
     super(options)
+    @mainlist = $("

")
+    @$el.append(@mainlist)
+

   delegateEvents: ->
@@ -126,8 +129,6 @@
     return callback

   build_children : () ->
-    @mainlist = $("

")
-    @$el.append(@mainlist)
     view_specific_options = []
     for spec, plot_num in @mget('children')
       model = @model.resolve_ref(spec)
@@ -144,10 +145,11 @@
     return null

   render_deferred_components : (force) ->
-    if _.all(@views_rendered, _.identity)
+    if _.all(@views_rendered, _.identity) and @views_rendered.length == @views.length
       return
+    @mainlist.html('')
     super(force)
-    @mainlist.html('')
+
     for view, view_num in _.values(@views)
       view.render_deferred_components(true)
       @views_rendered[view_num] = false


Add login data param.

Add login data param.

--- lib/chat.coffee
+++ lib/chat.coffee
@@ -40,7 +40,8 @@
   disconnect : () ->
   # Indicates a successful login.
   # @param username [String]
-  loginConfirmed : (username) ->
+  # @param data [Object]
+  loginConfirmed : (username, data) ->
   # Indicates a login error.
   # @param error [Object] Error.
   loginRejected : (error) ->
@@ -97,7 +98,7 @@
 #
 # @example Socket.io client example
 #   socket = ioClient.connect url, params
-#   socket.on 'loginConfirmed', (username) ->
+#   socket.on 'loginConfirmed', (username, authData) ->
 #     socket.emit 'roomJoin', roomName, (error, data) ->
 #       # this is a socket.io ack waiting callback.
 #       # socket is joined the room, or an error occurred. we get here
@@ -1004,7 +1005,7 @@
         return @rejectLogin socket, error
     @chatState.loginUser userName, socket, (error, user) ->
       if error then return @rejectLogin socket, error
-      fn = -> socket.emit 'loginConfirmed', userName
+      fn = -> socket.emit 'loginConfirmed', userName, {}
       if userState then user.initState userState, fn
       else fn()


Remove alert debugs

Remove alert debugs

--- annotran/static/scripts/annotator/plugin/substitution.coffee
+++ annotran/static/scripts/annotator/plugin/substitution.coffee
@@ -27,17 +27,14 @@

   startSubstitution: (event = {}) =>
     if this.original_document == ""
-      alert("storing")
       this.original_document = `$("body").html()`

     if this.original_document != `$("body").html()`
       `
-      alert(this.original_document);
       $("body").html(this.original_document);
       `
       return null
     else
-      alert("running sub")
       this.makeSubstitution(event)


`delete`?

`delete`?

--- app/assets/javascripts/views/users/users_edit_view.js.coffee
+++ app/assets/javascripts/views/users/users_edit_view.js.coffee
@@ -17,7 +17,6 @@
     unless data.password is data.password_confirmation
       @setErrors password: ["Las contraseñas no coinciden"]
       return false
-    delete data.password_confirmation
     user = @get("model")
     user.setProperties data
     user.save().then $.proxy(@successEditing, @), $.proxy(@failureEditing, @)


[koding_node_modules][kd-kite-datastore] removed parentheses from function call

--- node_modules_koding/kd-kite-datastore/lib/kd-kite-datastore/adapters/datastorebaseadapter.coffee
+++ node_modules_koding/kd-kite-datastore/lib/kd-kite-datastore/adapters/datastorebaseadapter.coffee
@@ -2,10 +2,10 @@

   constructor: (@config = null)->

-  throwErr:()->
+  throwErr:->
     throw new Error 'method must be implemented'

-  set:(key, value) ->
+  set:(key, value, callback)->
     @throwErr()

   get: (key, callback) ->


Nicely format error message

Nicely format error message

--- lib/buffer-search.coffee
+++ lib/buffer-search.coffee
@@ -116,8 +116,9 @@
         try
           @editor.scanInBufferRange regex, Range(start, end), ({range}) =>
             newMarkers.push(@createMarker(range)) unless range.isEmpty()
-        catch e
-          @emitter.emit 'did-error', e
+        catch error
+          error.message = "Search string is too large"
+          @emitter.emit 'did-error', error
           return false
       else
         return false


fix summary and don't allow space before ++ or --

--- src/scripts/karma.coffee
+++ src/scripts/karma.coffee
@@ -37,11 +37,11 @@

 module.exports = (robot) ->
   karma = new Karma robot
-  robot.hear /(\S+[^+])\+\+(\s|$)/, (msg) ->
+  robot.hear /(\S+[^+\s])\+\+(\s|$)/, (msg) ->
     karma.add msg.match[1].toLowerCase()
     msg.reply "The operation succeeded."

-  robot.hear /(\S+[^-])--(\s|$)/, (msg) ->
+  robot.hear /(\S+[^-\s])--(\s|$)/, (msg) ->
     karma.subtract msg.match[1].toLowerCase()
     msg.reply "The operation succeeded."

@@ -57,6 +57,6 @@
                "(#{s[2].karma}). Lowest karma: \"#{s[s.length-1].name}\" " +
                "(#{s[s.length-1].karma}), \"#{s[s.length-2].name}\" " +
                "(#{s[s.length-2].karma}), and \"#{s[s.length-3].name}\" " +
-               "(#{s[s.length-2].karma})."
+               "(#{s[s.length-3].karma})."
       else
         msg.send "There aren't enough items with karma to give a top and bottom 3"


Fix for tests before new core event lands

--- lib/main.coffee
+++ lib/main.coffee
@@ -37,8 +37,8 @@
             'ctrl-shift-tab ^ctrl': 'unset!'
         atom.keymaps.add(keyBindSource, disabledBindings, 0)

-    atom.keymaps.onDidLoadUserKeymap => @updateTraversalKeybinds()
     atom.config.observe configKey, => @updateTraversalKeybinds()
+    atom.keymaps.onDidLoadUserKeymap? => @updateTraversalKeybinds()

     # If the command bubbles up without being handled by a particular pane,
     # close all tabs in all panes


Allow assets to be moved off position, don't sync

Fixes #3529

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -222,8 +222,9 @@
     ################################### Video/playback controls

     $scope.updatePosition = () ->
-      if video && $scope.asset?.segment.contains(ruler.position.o) && isFinite($scope.asset.segment.l)
-        video[0].currentTime = (ruler.position.o - (if $scope.editing == 'position' then $scope.current.l else $scope.asset.segment.l)) / 1000
+      seg = (if $scope.editing == 'position' then $scope.current else $scope.asset?.segment)
+      if seg && video && seg.contains(ruler.position.o) && seg.lBounded
+        video[0].currentTime = (ruler.position.o - seg.l) / 1000
       return

     seekOffset = (o) ->


Fix timeline video seeking when non-current

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -96,8 +96,8 @@
       styles

     seekOffset = (o) ->
-      if video && $scope.current?.asset?.segment.contains(o) && isFinite($scope.current.asset.segment.l)
-        video[0].currentTime = (o - $scope.current.asset.segment.l) / 1000
+      if video && $scope.asset?.segment.contains(o) && isFinite($scope.asset.segment.l)
+        video[0].currentTime = (o - $scope.asset.segment.l) / 1000
       ruler.position = o
       return

@@ -356,8 +356,8 @@
         $scope.playing = video[0].playbackRate
         return
       timeupdate: ->
-        if $scope.current?.asset && isFinite($scope.current.asset.segment.l)
-          ruler.position = $scope.current.asset.segment.l + 1000*video[0].currentTime
+        if $scope.asset && isFinite($scope.asset.segment.l)
+          ruler.position = $scope.asset.segment.l + 1000*video[0].currentTime
           if ruler.selection.uBounded && ruler.position >= ruler.selection.u
             video[0].pause()
             seekOffset(ruler.selection.l) if ruler.selection.lBounded


Streamline setup autoremoval

Streamline setup autoremoval

--- src/setup/suicide.coffee
+++ src/setup/suicide.coffee
@@ -1,38 +1,20 @@
 ###
 Remove setup files
 ###
-magic = "#{PACKAGE.name}-dist"
-ext = ".source"
-src = argv0.up()
+module.exports = ->
+  src = argv0.up()

-module.exports = ->
-  unless magic == src.bn().substring 0, magic.length
+  unless "#{PACKAGE.name}-dist" == src.bn()
     return ->

-  ini = file "#{argv0}#{ext}"
-
-  unless ini.y()
-    return prepare
+  if wsh.Interactive
+    return ->
+      run 0, 'wscript', '//B', '//E:JScript', argv0
+      return

   wsh.Sleep 3000
-  # Actual remove

-  try
-    folder ini.load()
-    .rm true
-
-  src.rm true
-
-  exit()
+  try src.rm true
+  finally
+    exit()
   return
-
-prepare = ->
-  tmp = folder cache, "#{magic}-#{rnd 8}"
-  .mk true
-
-  argv0.cp me2 = file tmp, argv0.bn()
-  file "#{me2}#{ext}"
-  .save src
-  run 0, me2
-
-  return


mp4: Remove unnecessary code

mp4: Remove unnecessary code

--- mp4.coffee
+++ mp4.coffee
@@ -1068,33 +1068,21 @@

 # gsst: unknown
 class GoogleGSSTBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # gstd: unknown
 class GoogleGSTDBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # gssd: unknown
 class GoogleGSSDBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # gspu: unknown
 class GoogleGSPUBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # gspm: unknown
 class GoogleGSPMBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # gshh: unknown
 class GoogleGSHHBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 # Media Data Box (mdat): audio/video frames
 # Defined in ISO 14496-12
@@ -1356,8 +1344,6 @@
     return

 class CTOOBox extends GenericDataBox
-  read: (buf) ->
-    super buf

 mp4file = new MP4File 'example.mp4'
 mp4file.parse()


Hide the moderation report preference (always immediate)

--- app/pages/settings/email.cjsx
+++ app/pages/settings/email.cjsx
@@ -82,7 +82,7 @@

         <PromiseRenderer promise={talkClient.type('subscription_preferences').get()} pending={->

} then={(preferences) =>

-            {for preference in @sortPreferences(preferences) when preference.category isnt 'system' then do (preference) =>
+            {for preference in @sortPreferences(preferences) when preference.category isnt 'system' and preference.category isnt 'moderation_reports' then do (preference) =>
               <ChangeListener key={preference.id} target={preference} handler={=>

                   {@nameOfPreference(preference)}


macOS previous/next conversation default keyboard shortcuts from Messages.app

--- src/ui/views/menu.coffee
+++ src/ui/views/menu.coffee
@@ -134,10 +134,12 @@
             type: 'separator'
         }, {
             label: 'Previous Conversation',
+            accelerator: 'Control+Shift+Tab'
             enabled: viewstate.loggedin
             click: -> action 'selectNextConv', -1
         }, {
             label: 'Next Conversation',
+            accelerator: 'Control+Tab'
             enabled: viewstate.loggedin
             click: -> action 'selectNextConv', +1
         }, {


Workspace: Fixed updating terminal references on firebase node.

--- client/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
+++ client/app/CommonViews/workspace/views/workspacefloatingpanelauncher.coffee
@@ -106,11 +106,12 @@
       delegate   : @panel
       sessionKey : @sessionKeys.terminal

-    @terminalPane.webterm.on "WebTermConnected", =>
-      @keysRef.child("terminal").set
-        key    : @terminalPane.remote.session
-        host   : KD.nick()
-        vmName : KD.getSingleton("vmController").defaultVmName
+    if @workspace.amIHost()
+      @terminalPane.webterm.on "WebTermConnected", =>
+        @keysRef.child("terminal").set
+          key    : @terminalPane.remote.session
+          host   : KD.nick()
+          vmName : KD.getSingleton("vmController").defaultVmName

   createPreviewPane: ->
     @container.addSubView @preview = new KDView


Forgot to remove a test for clean_query

Forgot to remove a test for clean_query

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -2538,7 +2538,7 @@
                         continue
                     result_multiple_line_comment = @regex.multiple_line_comment.exec query.slice i
                     if result_multiple_line_comment?
-                        result_query += query.slice(start, i).replace(/\n/g, '\\\\n')
+                        result_query += query.slice(start, i).replace(/\n/g, '')
                         start = i
                         to_skip = result_multiple_line_comment[0].length-1
                         start += result_multiple_line_comment[0].length


Switch to db_config

Switch to db_config

--- admin/static/coffee/tables/index.coffee
+++ admin/static/coffee/tables/index.coffee
@@ -69,13 +69,13 @@

         fetch_data: =>
-            query = r.dbList().filter( (db) ->
-                db.ne('rethinkdb')
+            query = r.db(system_db).table('db_config').filter( (db) ->
+                db("name").ne('rethinkdb')
             ).orderBy(r.row).map (db) ->
-                db: db
-                id: db # TODO: Use db_config (when available) and remove this merge
+                db: db("name")
+                id: db("uuid")
                 tables: r.db(system_db).table('table_status').orderBy((table) -> table("name"))
-                    .filter({db: db}).merge( (table) ->
+                    .filter({db: db("name")}).merge( (table) ->
                         shards: table("shards").count()
                         replicas: table("shards").nth(0).count()
                     ).merge( (table) ->


Explicit error on missing collection

Explicit error on missing collection

Fixes #27

--- paginate.coffee
+++ paginate.coffee
@@ -1,6 +1,7 @@
 @__Pages = class Pages
   constructor: (collection, settings = {}) ->
     @setCollection collection
+    @Collection or throw "Pages can't find the <" + collection + "> collection";
     @setId @Collection._name
     @requested = []
     @received = []


tag属性支持数组写入

tag属性支持数组写入

--- src/core/element.coffee
+++ src/core/element.coffee
@@ -99,7 +99,11 @@
 			setter: (tag)->
 				cola.tagManager.unreg(t, @) for t in @_tag if @_tag
 				if tag
-					@_tag = ts = tag.split(tagSplitter)
+					if tag instanceof Array
+						ts = tag
+					else
+						ts = tag.split(tagSplitter)
+					@_tag = ts
 					cola.tagManager.reg(t, @) for t in ts
 				else
 					@_tag = null


Describe method countOnHandPlaceholder for VOs

--- app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee
+++ app/assets/javascripts/admin/variant_overrides/controllers/variant_overrides_controller.js.coffee
@@ -110,6 +110,10 @@
       .error (data, status) ->
         $timeout -> StatusMessage.display 'failure', $scope.updateError(data, status)

+  # Variant override count_on_hand field placeholder logic:
+  #     on_demand true  -- Show "On Demand"
+  #     on_demand false -- Show empty value to be set by the user
+  #     on_demand nil   -- Show producer on_hand value
   $scope.countOnHandPlaceholder = (variant, hubId) ->
     variantOverride = $scope.variantOverrides[hubId][variant.id]


fix deprecated API call

fix deprecated API call

--- src/wye-tee-merge.coffee
+++ src/wye-tee-merge.coffee
@@ -56,7 +56,7 @@
       substream_ended = true
       pushable.end() if bystream_ended
     else
-      pushable.push d
+      pushable.send d
   subline.push @$drain()
   #.........................................................................................................
   byline.push bystream
@@ -71,3 +71,5 @@
   confluence = @$merge pushable, @pull byline...
   return { sink: pair.sink, source: confluence, }

+
+


sankey: fixed link layout

sankey: fixed link layout

play with link colors

--- app/assets/javascripts/d3/sankey.coffee
+++ app/assets/javascripts/d3/sankey.coffee
@@ -115,24 +115,39 @@

     # returns the absolute y coordinate of the left anchor point
     #
+    # This method should definitely be simplifiwed
     left_y:  =>
-      offset = 0
+      offset = null
       for link in @left.right_links
         # push down the first link
-        offset += link.value() / 2 if offset == 0
-        break if link == this
-        offset += link.value()
+        if offset == null
+          offset = link.value() / 2
+          break if link == this
+          offset = link.value()
+        else
+          if link == this
+            offset += link.value() / 2
+            break
+          else
+            offset += link.value()
       @left.y_offset() + offset

     # see above
     #
     right_y:  =>
-      offset = 0
+      offset = null
       for link in @right.left_links
         # push down the first link
-        offset += link.value() / 2 if offset == 0
-        break if link == this
-        offset += link.value()
+        if offset == null
+          offset = link.value() / 2
+          break if link == this
+          offset = link.value()
+        else
+          if link == this
+            offset += link.value() / 2
+            break
+          else
+            offset += link.value()
       @right.y_offset() + offset

     left_x:  => @left.x_center() + @module.Node.width / 2
@@ -158,7 +173,7 @@

     value: =>
       if @gquery
-        @gquery.get('future_value') / 8
+        @gquery.get('future_value') / 4
       else
         10

@@ -176,6 +191,7 @@
         attr("width", @width)

       colors = d3.scale.category20()
+      link_color = d3.scale.category20()

       @links = @svg.selectAll('path.link').
         data(@module.links.models, (d) -> d.cid).
@@ -183,7 +199,8 @@
         append("svg:path").
         attr("class", "link").
         style("stroke-width", (link) -> link.value()).
-        style("stroke", (link) -> link.color()).
+        #style("stroke", (link, i) -> link.color()).
+        style("stroke", (link, i) -> link_color(i)).
         style("fill", "none").
         style("opacity", 0.8).
         on('mouseover', ->
@@ -199,7 +216,7 @@
         enter().
         append("rect").
         attr("x", (d) => @x(@module.Node.horizontal_spacing * d.get('column') + 20)).
-        attr("y", (d) => @y(100 * d.get ('row'))).
+        attr("y", (d) => @y(d.y_offset())).
         attr("width", @x @module.Node.width).
         attr("height", (d) => @y d.value()).
         attr("stroke", "gray").
@@ -244,6 +261,8 @@
         domain([0, 700]).
         range([0, @height])

+      # This is the function that will take care of drawing the links once we've
+      # set the base points
       @link_line = d3.svg.line().
         interpolate("basis").
         x((d) -> @x(d.x)).


Refactor events handling js

Refactor events handling js

--- app/assets/javascripts/events.js.coffee
+++ app/assets/javascripts/events.js.coffee
@@ -1,22 +1,15 @@
 $ ->
   $('.jobs_controller.index_action').each ->
-    new Altria.ServerEvent().on 'build.started build.finished', (attributes) ->
-      $.ajax
-        url: "/jobs/#{attributes.job_id}",
-        success: (data) ->
+    new Altria.ServerEvent()
+      .on 'build.started build.finished', (attributes) ->
+        $.ajax "/jobs/#{attributes.job_id}", success: (data) ->
           $("#job#{attributes.job_id}").replaceWith(data)

   $('.jobs_controller.show_action').each ->
-    event = new Altria.ServerEvent()
-
-    event.on 'build.finished', (attributes) ->
-      $.ajax
-        url: "/builds/#{attributes.id}"
-        success: (data) ->
+    new Altria.ServerEvent()
+      .on 'build.finished', (attributes) ->
+        $.ajax "/builds/#{attributes.id}", success: (data) ->
           $("#build#{attributes.id}").replaceWith(data)
-
-    event.on 'build.started', (attributes) ->
-      $.ajax
-        url: "/builds/#{attributes.id}"
-        success: (data) ->
+      .on 'build.started', (attributes) ->
+        $.ajax "/builds/#{attributes.id}", success: (data) ->
           $('.builds ul').prepend(data)


Allow refresh_token grantType

Allow refresh_token grantType

--- model.coffee
+++ model.coffee
@@ -48,7 +48,7 @@
 		if debug is true
 			console.log '[OAuth2Server]', 'in grantTypeAllowed (clientId:', clientId, ', grantType:', grantType + ')'

-		return callback(false, grantType in ['authorization_code'])
+		return callback(false, grantType in ['authorization_code', 'refresh_token'])

 	saveAccessToken: Meteor.bindEnvironment (token, clientId, expires, user, callback) ->


Fixed the global-search typeahead

Fixed the global-search typeahead

--- app/assets/javascripts/global.js.coffee
+++ app/assets/javascripts/global.js.coffee
@@ -121,12 +121,15 @@
     )

   $("#global-search").typeahead(
-    source: (query, process) ->
-      return $.get(root_url + 'search', { q: query }, (data) -> return process(data))
-    updater: (item) ->
-      $("#global-search").val(item)
-      $("#global-search-form").submit()
-      return item
+    remote: root_url + 'search?q=%QUERY'
+  )
+
+  $(document).on('typeahead:selected', "#global-search", (event, datum) ->
+    $(this).val(datum['value'])
+    $("#global-search-form").submit()
+  )
+  .on('keydown', "#global-search", (e) ->
+    $("#global-search-form").submit() if e.which == 13
   )

   # $("[rel~=popover]").popover( offset: 10, trigger: 'focus' )


fix double check for all issues

fix double check for all issues

--- app/assets/javascripts/issues.js.coffee
+++ app/assets/javascripts/issues.js.coffee
@@ -38,7 +38,7 @@

   initChecks: ->
     $(".check_all_issues").click ->
-      $(".selected_issue").attr "checked", @checked
+      $(".selected_issue").prop("checked", @checked)
       Issues.checkChanged()

     $(".selected_issue").bind "change", Issues.checkChanged


modified js-tree

modified js-tree

--- app/assets/javascripts/jstree.js.coffee
+++ app/assets/javascripts/jstree.js.coffee
@@ -1,7 +1,6 @@
 $ ->
-  $(".selector").jstree({
-3	        "plugins" : [ "checkbox" ],
-                "checkbox":{ "two_state": "true",
-                             "real_checkboxes": "true"
-    },
-4	    })
+  $(".selector").jstree
+#   plugins : [ "checkbox" ]
+#   checkbox:
+#     two_state: "true"
+#     real_checkboxes: "true"


Clean up tool display

Clean up tool display

--- app/classifier/tasks/drawing/index.cjsx
+++ app/classifier/tasks/drawing/index.cjsx
@@ -93,10 +93,19 @@
         {icons[tool.type]}{' '}
         <input type="radio" className="drawing-tool-input" checked={i is (@props.annotation._toolIndex ? 0)} onChange={@handleChange.bind this, i} />
         <Markdown>{tool.label}</Markdown>
-        {if tool.min? or tool.max?
-          {tool.min ? 0}–{tool.max ? '∞'}}
-        {unless count is 0
-          ({count})}
+
+
+          {if count isnt 0
+            count + ' '}
+          {if count isnt 0 and (tool.min? or tool.max?)
+            ' of '}
+          {if tool.min?
+            <span style={color: 'red' if count < tool.min}>{tool.min} required}
+          {if tool.min? and tool.max?
+            ', '}
+          {if tool.max?
+            {tool.max} maximum}
+

     <GenericTask question={@props.task.instruction} help={@props.task.help} answers={tools} required={@props.task.required} />


Update health check for new end points

Update health check for new end points

--- app/coffee/HealthCheckController.coffee
+++ app/coffee/HealthCheckController.coffee
@@ -1,42 +1,53 @@
-ObjectId = require("mongojs").ObjectId
 request = require("request")
 async = require("async")
-_ = require("underscore")
-crypto = require("crypto")
 settings = require("settings-sharelatex")
 port = settings.internal.tags.port
 logger = require "logger-sharelatex"
+ObjectId = require("mongojs").ObjectId

+request = request.defaults({timeout: 3000})
+
+buildUrl = (path) ->
+	"http://localhost:#{port}#{path}"

 module.exports =
 	check : (callback)->
 		project_id = ObjectId()
 		user_id = ObjectId(settings.tags.healthCheck.user_id)
 		tagName = "smoke-test-tag"
-		getOpts = (endPath)-> {url:"http://localhost:#{port}/user/#{user_id}#{endPath}", timeout:3000}
-		logger.log user_id:user_id, opts:getOpts(), tagName:tagName, project_id:project_id, "running health check"
-		jobs = [
-			(cb)->
-				opts = getOpts("/project/#{project_id}/tag")
-				opts.json = {name: tagName}
-				request.post(opts, cb)
-			(cb)->
-				opts = getOpts("/tag")
-				opts.json = true
-				request.get opts, (err, res, body)->
+		request.post {
+			url: buildUrl("/user/#{user_id}/tag"),
+			json:
+				name: tagName
+		}, (err, res, body) ->
+			return callback(err) if err?
+			if res.statusCode != 200
+				return callback new Error("unexpected statusCode: #{res.statusCode}")
+			logger.log {tag: body, user_id, project_id}, "health check created tag"
+			tag_id = body._id
+
+
+			request.post {
+				url: buildUrl("/user/#{user_id}/tag/#{tag_id}/project/#{project_id}")
+			}, (err, res, body) ->
+				return callback(err) if err?
+				if res.statusCode != 204
+					return callback new Error("unexpected statusCode: #{res.statusCode}")
+
+				request.get {
+					url: buildUrl("/user/#{user_id}/tag"),
+					json: true
+				}, (err, res, tags) ->
+					return callback(err) if err?
 					if res.statusCode != 200
-						return cb("status code not 200, its #{res.statusCode}")
-
-					hasTag = _.some body, (tag)->
-						tag.name == tagName and _.contains(tag.project_ids, project_id.toString())
-					if hasTag
-						cb()
-					else
-						cb("tag not found in response")
-		]
-		async.series jobs, (err)->
-			if err?
-				callback(err)
-			opts = getOpts("/project/#{project_id}/tag")
-			opts.json = {name: tagName}
-			request.del opts, callback
+						return callback new Error("unexpected statusCode: #{res.statusCode}")
+					hasTag = false
+					for tag in tags
+						logger.log {tag:tag, project_id: project_id.toString()}, "checking tag"
+						if project_id.toString() in tag.project_ids
+							hasTag = true
+							break
+					if !hasTag
+						return callback new Error("tag was not found in response")
+
+					request.del buildUrl("/user/#{user_id}/tag/#{tag_id}"), callback


Carry due_date when promoting an issue to an US

--- app/coffee/modules/issues/detail.coffee
+++ app/coffee/modules/issues/detail.coffee
@@ -625,6 +625,7 @@
                 tags: issue.tags
                 is_blocked: issue.is_blocked
                 blocked_note: issue.blocked_note
+                due_date: issue.due_date
             }

             onSuccess = ->


Remove debugging line.

Remove debugging line.

--- app/assets/javascripts/favorite.coffee
+++ app/assets/javascripts/favorite.coffee
@@ -22,7 +22,6 @@

   html = usersLinks users.slice(0, 6)

-  window.x = users
   if users.length > 6
     html += ", #{usersLinks users.slice(6)}"
     html += " (#{users.length - 6} more)"


onSameCall -> onSameContext with support for member editing areas

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -23,10 +23,17 @@
       return false

   initCallSelectionDragNDrops = () ->
-    onSameCall = (elem1, elem2) ->
+    sameContext = (droppable, draggable) ->
       getCallFormId = (elem) ->
-        elem.parents('.call-for-application:first').find('form.edit_call').attr('id')
-      getCallFormId(elem1) == getCallFormId(elem2)
+        call = elem.parents('.call-for-application:first')
+        if call.length > 0
+          call.find('form.edit_call').attr('id')
+        else
+          elem.parents('form.edit_organ').attr('id')
+      droppableId = getCallFormId(droppable)
+      draggableId = getCallFormId(draggable)
+      droppableId? and draggableId? and droppableId == draggableId
+

     saveSelection = (droppable, draggable) ->
       form = draggable.find('form')
@@ -61,7 +68,7 @@
         saveSelection(droppable, ui.draggable)
       accept: (draggable) ->
         return false unless draggable.hasClass 'member-card'
-        onSameCall $(@), draggable
+        sameContext $(@), draggable

     $('.call-for-application.open .member-card-empty, .edit-members .member-card-empty').droppable
       activeClass: 'highlight-drop-area'
@@ -69,7 +76,7 @@
         return false unless draggable.hasClass 'member-card'
         return false if $(@).find('.member-card').length > 0
         return false if $(@).hasClass('no-deputy')
-        onSameCall $(@), draggable
+        sameContext $(@), draggable
       drop: (event, ui) ->
         droppable = $(@)
         droppable.prepend(ui.draggable)


Refactored: variable names shouldn't be be abreviate

--- app/controllers/index_controller.coffee
+++ app/controllers/index_controller.coffee
@@ -1,9 +1,7 @@
-load 'application'
-
 #------------------ BEGIN REQUIRE ------------------
 #instanciate Databrowser classes (datasystem, searchengine)
-ds = require './db/dataSystem'
-se = require('./db/searchEngine')(ds)
+dataSystem = require './db/dataSystem'
+searchEngine = require('./db/searchEngine')(dataSystem)

 #instanciate Noesis helpers
 oObjectHelper = require './noesis-tools/oObjectHelper'
@@ -18,21 +16,21 @@
 action 'doctypes', ->

     #index several id for test
-    #ds.indexId "39bade34f76d6b32234c3974c8004ca9", ["description"]
-    #ds.indexId "39bade34f76d6b32234c3974c80059f0", ["description"]
+    #dataSystem.indexId "39bade34f76d6b32234c3974c8004ca9", ["description"]
+    #dataSystem.indexId "39bade34f76d6b32234c3974c80059f0", ["description"]

     #prepare request
     requests = []
     requests.push (callback) -> #0 -> doctypes
-        ds.getDoctypes(callback)
+        dataSystem.getDoctypes(callback)
     requests.push (callback) -> #1 -> metadoctypes
-        ds.getView(callback, ds.getPATH().metadoctype.getallbyrelated)
-        #ds.applyModelRequest(callback, 'Metadoctype', 'getAllByRelated')
+        dataSystem.getView(callback, dataSystem.getPATH().metadoctype.getallbyrelated)
+        #dataSystem.applyModelRequest(callback, 'Metadoctype', 'getAllByRelated')
     requests.push (callback) -> #2 -> sums
-        ds.getView(callback, ds.getPATH().common.getsumsbydoctype, {group : true})
-        #ds.applyModelRequest(callback, 'All', 'getSumsByDoctype', {group : true})
+        dataSystem.getView(callback, dataSystem.getPATH().common.getsumsbydoctype, {group : true})
+        #dataSystem.applyModelRequest(callback, 'All', 'getSumsByDoctype', {group : true})
     requests.push (callback) -> #3 -> permissions
-        ds.getView(callback, ds.getPATH().application.getpermissions)
+        dataSystem.getView(callback, dataSystem.getPATH().application.getpermissions)

     #agregate callback
     async.parallel requests, (error, results) ->
@@ -72,7 +70,7 @@

 #search
 action 'search', ->
-    if req.query?
+    if req.query?

         tabDoctypes = req.query.doctype || null

@@ -83,60 +81,60 @@

             #page params
             page = parseInt(req.query.page, 10)
-            nbPerPage = parseInt(req.query.nbperpage, 10)
+            nbPerPage = parseInt(req.query.nbperpage, 10)

             #skip & limit + deleted lines params
-            nbDeleted = if req.query.deleted? then parseInt(req.query.deleted, 10) else 0
+            nbDeleted = if req.query.deleted? then parseInt(req.query.deleted, 10) else 0
             pageParams['limit'] = nbPerPage
-            if page > 1
+            if page > 1
                 pageParams['skip'] = (nbPerPage * (page - 1)) - nbDeleted

             #query param
             if req.query.query? and req.query.query isnt ""
-                pageParams['query'] = req.query.query
+                pageParams['query'] = req.query.query

             #prepare multiple page process
             # newKey = req.query.doctype.join('_')
-            # if not ds.pageCountMatrix[newKey]?
-            #     ds.pageCountMatrix[newKey] = []
+            # if not dataSystem.pageCountMatrix[newKey]?
+            #     dataSystem.pageCountMatrix[newKey] = []

             #----VERIFY DOCTYPE 'ALL' REQUESTS
             tabUnregistered = []
-            for dt in tabDoctypes
-                if not ds.registeredPatterns[dt.toLowerCase()]?
+            for dt in tabDoctypes
+                if not dataSystem.registeredPatterns[dt.toLowerCase()]?
                     tabUnregistered.push dt.toLowerCase()

-            if tabUnregistered.length > 0
+            if tabUnregistered.length > 0

                 #verify if doctype exist
                 requests = []
                 requests.push (callback) -> #0 -> doctypes
-                    ds.getDoctypes(callback)
+                    dataSystem.getDoctypes(callback)
                 async.parallel requests, (error, results) ->
                     if error
                         res.send {'no_result' : 'Error : Server error occurred while retrieving data.'}
                         console.log error
-                    else
+                    else

-                        #compare submitted doctype and existing doctype for more security
-                        bError = false
-                        tabRegisteredDoctypes = results[0]
+                        #compare submitted doctype and existing doctype for more security
+                        bError = false
+                        tabRegisteredDoctypes = results[0]
                         for dtUnreg in tabUnregistered
                             bUnknowDoctype = true
                             for dtReg, index in tabRegisteredDoctypes
                                 if not bError
                                     if dtUnreg.toLowerCase() is dtReg.toLowerCase()
-                                        bUnknowDoctype = false
-                                    if bUnknowDoctype and index is tabRegisteredDoctypes.length-1
+                                        bUnknowDoctype = false
+                                    if bUnknowDoctype and index is tabRegisteredDoctypes.length-1
                                         res.send {'no_result': 'Error : You try to access to an unknown doctype.'}
                                         bError = true

                         if not bError
                             #prepare request 'all' for each doctypes
-                            setupRequestsAll = ds.prepareDballRequests(tabUnregistered)
+                            setupRequestsAll = dataSystem.prepareDballRequests(tabUnregistered)

                             #agregate callbacks
                             if setupRequestsAll.length > 0
@@ -145,14 +143,14 @@
                                         console.log error
                                         res.send {'no_result' : 'Error : Server error occurred while retrieving data.'}
                                     else
-                                        se.doBasicSearch(res, tabDoctypes, pageParams)
+                                        searchEngine.doBasicSearch(res, tabDoctypes, pageParams)
                             else
                                 res.send {'no_result' : 'Error : Server error occurred while retrieving data.'}

-            else
-                se.doBasicSearch(res, tabDoctypes, pageParams)
+            else
+                searchEngine.doBasicSearch(res, tabDoctypes, pageParams)

-
+
         else
             res.send {'no_result' : 'Error : Unknown research parameters'}

@@ -161,7 +159,7 @@
     if req.params.id?
         requests = []
         requests.push (callback) -> #0 -> delete
-                ds.deleteById callback, req.params.id
+                dataSystem.deleteById callback, req.params.id
         async.parallel requests, (error, results) ->
             if error
                 res.send('error', 'Server error occurred while trying to remove data.')


scroll makes links scrollable.

scroll makes links scrollable.

--- app/client/js/react/pages/songs.coffee
+++ app/client/js/react/pages/songs.coffee
@@ -10,7 +10,7 @@
   ###
   constructor: (routes, touch, songsStore) ->
     {div,p,ul,li} = React.DOM
-    {a} = touch.none 'a'
+    {a} = touch.scroll 'a'

     @component = React.createClass


Work on edge case to detect if there is a profile or not

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -17,6 +17,7 @@
             profiler: true
         history: []
         focus_on_codemirror: true
+        last_query_has_profile: true
         #saved_data: {}

@@ -2341,7 +2342,7 @@
                     @id_execution++ # Update the id_execution and use it to tag the callbacks
                     rdb_global_callback = @generate_rdb_global_callback @id_execution
                     # Date are displayed in their raw format for now.
-                    console.log @state.options.profiler
+                    @state.last_query_has_profile = @state.options.profiler
                     rdb_query.private_run {connection: @driver_handler.connection, timeFormat: "raw", profile: @state.options.profiler}, rdb_global_callback # @rdb_global_callback can be fire more than once
                     return true
                 else if rdb_query instanceof DataExplorerView.DriverHandler
@@ -2365,12 +2366,6 @@
         generate_rdb_global_callback: (id_execution) =>
             rdb_global_callback = (error, results) =>
                 if @id_execution is id_execution # We execute the query only if it is the last one
-                    if results.profile?
-                        cursor = results.value
-                        @profile = results.profile
-                    else
-                        cursor = results
-                        @profile = null # @profile is null if the user deactivated the profiler
                     get_result_callback = @generate_get_result_callback id_execution

                     if error?
@@ -2384,6 +2379,14 @@
                             broken_query: true

                         return false
+
+                    if results.profile? and @state.last_query_has_profile is true
+                        cursor = results.value
+                        @profile = results.profile
+                    else
+                        cursor = results
+                        @profile = null # @profile is null if the user deactivated the profiler
+

                     if @index is @queries.length # @index was incremented in execute_portion
                         if cursor?


Reverse direction, don't ignore commands in comments

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/SuggestionManager.coffee
@@ -39,18 +39,15 @@
 			return commands

 		# Ignore single letter commands since auto complete is moot then.
-		# Ignore commands which occur after a comment character '%'
-		commandRegex: /^([^%]*?)\\([a-zA-Z][a-zA-Z]+)/m
+		commandRegex: /\\([a-zA-Z][a-zA-Z]+)/

 		nextCommand: () ->
 			i = @doc.search(@commandRegex)
 			if i == -1
 				return false
 			else
-				matchObject = @doc.match(@commandRegex)
-				preMatch = matchObject[1]
-				match = matchObject[2]
-				@doc = @doc.substr(i + preMatch.length + match.length + 1)
+				match = @doc.match(@commandRegex)[1]
+				@doc = @doc.substr(i + match.length + 1)
 				return match

 		consumeWhitespace: () ->


Add SF as default font

Add SF as default font

--- framer/Components/ShareComponent.coffee
+++ framer/Components/ShareComponent.coffee
@@ -8,7 +8,8 @@
 			width: options.parent.width if options and options.parent
 			ignoreEvents: false
 			style:
-				fontFamily: "Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
+				fontFamily: "-apple-system, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
+				textAlign: "left"
 				fontSize: "14px"
 				color: "#111"
 				lineHeight: "1"
@@ -25,7 +26,7 @@
 			height: 33
 			ignoreEvents: false
 			style:
-				fontFamily: "Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
+				fontFamily: "-apple-system, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
 				fontWeight: "500"
 				webkitUserSelect: "text"
 				borderRadius: 3
@@ -305,7 +306,7 @@

 		descriptionStyle =
 			fontSize: "14px"
-			fontFamily: "Roboto"
+			fontFamily: "-apple-system, Roboto, Helvetica Neue, Helvetica, Arial, sans-serif"
 			lineHeight: "1.5"
 			wordWrap: "break-word"