fixed a problem with preffix and postfix length calculation

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -45,8 +45,8 @@
         editorDiv.append '

'

         @editor = new PlayerCodeEditor 'ace-editor', \
-            @commands, @codeConfig.initial, @codeConfig.prefix, @codeConfig.postfix, \
-            @editorConfig.freeformEditting
+            @commands, @codeConfig.initial, @codeConfig.show, @codeConfig.prefix, \
+            @codeConfig.postfix, @editorConfig.freeformEditting
         @interpreter = new CodeInterpreter @commands

         @setUpInsertButtons()
@@ -210,7 +210,7 @@
     ###
         Creates and provides functionality for an Ace editor representing player's code.
     ###
-    constructor: (@editorDivId, @commands, codeText, @codePrefix, @codeSuffix, @freeEdit) ->
+    constructor: (@editorDivId, @commands, codeText, @wrapCode, @codePrefix, @codeSuffix, @freeEdit) ->
         ###
             Sets internal variables, the default text and buttons
             and their event handlers.
@@ -221,10 +221,15 @@
         @editSession.setUseSoftTabs true
         @editor.setReadOnly !@freeEdit

+        if @wrapCode
+            @codeText = @codePrefix + codeText + '\n' + @codeSuffix
+        else
+            @codePrefix = ""
+            @codeSuffix = ""
+            @codeText = codeText
+
         @codePrefixLength = codePrefix.split('\n').length - 1
-        @codeSuffixLength = codeSuffix.split('\n').length
-
-        @codeText = @codePrefix + codeText + '\n' + @codeSuffix
+        @codeSuffixLength = codeSuffix.split('\n').length - 1

         @enableKeyboardShortcuts()


TabToOpen: remove console.log.

TabToOpen: remove console.log.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -546,7 +546,6 @@
             # suggestion, then custom search-engine mode should be activated.
             suggestion.customSearchMode = engine.keyword
             suggestion.title = suggestion.insertText
-            console.log suggestion.insertText, engine unless engine.description
             suggestion.type = engine.description ? "custom search"
             break


Search completion; minor changes to comments.

--- background_scripts/completion.coffee
+++ background_scripts/completion.coffee
@@ -360,6 +360,7 @@
   cancel: ->
     CompletionSearch.cancel()

+  # Look up the search engine and, if one is found, then note it and remove its keyword from the query terms.
   triageRequest: (request) ->
     @searchEngines.use (engines) =>
       { queryTerms, query } = request
@@ -501,9 +502,10 @@
     return @mostRecentQuery = arguments if @filterInProgress

     # Provide each completer with an opportunity to see (and possibly alter) the request before it is
-    # launched.
-    for completer in @completers
-      completer.triageRequest? request
+    # launched.  This is primarily for SearchEngineCompleter, which notes a search query and removes any
+    # keyword from the queryTerms.  Then, other completers don't include keywords in their matching and
+    # relevancy scores.
+    completer.triageRequest? request for completer in @completers

     RegexpCache.clear()
     { queryTerms } = request


inject audioService into rootScope

inject audioService into rootScope

--- www/js/audio.coffee
+++ www/js/audio.coffee
@@ -33,6 +33,13 @@
           @media.stop()
           @media.release()

-      if $cordovaDevice.getPlatform() != 'browser'
-        $delegate.player = Player.instance()
+
+      document.addEventListener 'deviceready', ->
+        if $cordovaDevice.getPlatform() != 'browser'
+          $delegate.player = Player.instance()
+
       return $delegate
+
+  .run ($rootScope, audioService) ->
+    document.addEventListener 'deviceready', ->
+      $rootScope.audioService = audioService


Utilise `eachSummaryFrame`

Utilise `eachSummaryFrame`

--- plugins/bugherd/index.coffee
+++ plugins/bugherd/index.coffee
@@ -27,8 +27,8 @@

       {{error.url}}

-      Stacktrace:{{#each error.stacktrace}}{{#if inProject}}
-      {{file}}:{{lineNumber}} - {{method}}{{/if}}{{/each}}
+      Stacktrace:{{#eachSummaryFrame error.stacktrace}}
+      {{file}}:{{lineNumber}} - {{method}}{{/eachSummaryFrame}}
     """
   )


Clean indents

Clean indents

--- client/app/components/file_picker.coffee
+++ client/app/components/file_picker.coffee
@@ -227,20 +227,20 @@
         li className: "file-item", key: @props.key,
             i className: "mime #{type} fa #{iconClass}"
             a
-                'className': 'file-name'
-                'onClick': @doDisplay
-                'data-file-url': file.url
-                'data-file-name': file.generatedFileName
-                'data-file-type': file.contentType
+                'className'         : 'file-name'
+                'onClick'           : @doDisplay
+                'data-file-url'     : file.url
+                'data-file-name'    : file.generatedFileName
+                'data-file-type'    : file.contentType
                 file.generatedFileName

             span className: 'file-size',
                 "\(#{(file.length / 1000).toFixed(1)}Ko\)"
             span className: 'file-actions',
                 a
-                    className: "fa fa-download"
-                    'download': file.generatedFileName
-                    'href': getFileURL file
+                    'className' : 'fa fa-download'
+                    'download'  : file.generatedFileName
+                    'href'      : getFileURL file
                 if @props.editable
                     i className: "fa fa-times delete", onClick: @doDelete


:herb::new: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -8,7 +8,7 @@
 dependencies:
   "jquery-utils": "STRd6/jquery-utils:v0.1.2"
   runtime: "STRd6/runtime:v0.1.1"
-  "touch-canvas": "STRd6/touch-canvas:v0.2.0-alpha.11"
+  "touch-canvas": "STRd6/touch-canvas:v0.2.0-alpha.12"
   "commando": "STRd6/commando:v0.9.0"
 width: 480
 height: 320


:clock8::star: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -8,7 +8,7 @@
 dependencies:
   appcache: "STRd6/appcache:v0.1.0"
   compression: "STRd6/lz-string:v1.3.3"
-  facebook: "distri/facebook:v0.1.1-alpha.1"
+  facebook: "distri/facebook:v0.1.1-alpha.4"
   "jquery-utils": "STRd6/jquery-utils:v0.1.2"
   runtime: "STRd6/runtime:v0.1.1"
   "touch-canvas": "STRd6/touch-canvas:v0.2.0"


Show mem and time in submit table

Show mem and time in submit table

--- client/components/SubmitListTable.coffee
+++ client/components/SubmitListTable.coffee
@@ -10,15 +10,24 @@

 import styles from './SubmitListTable.css'

+maxVal = (submit, field) ->
+    res = -1
+    for index, result of (submit.results?.tests || [])
+        val = +result[field]
+        if val > res
+            res = val
+    return if res < 0 then undefined else res
+
 export default SubmitListTable = (props) ->

+                    Время попытки
+                    Результат
+                    Язык
                     Время
-                    Результат
-
-
+                    Память

                     {if props.handleDiffClick
@@ -31,10 +40,14 @@
                     [cl, message] = outcomeToText(submit.outcome)
                     if submit._id == props.activeId
                         cl += " " + styles.active
+                    time = maxVal(submit, "time")
+                    mem = maxVal(submit, "max_memory_used")

                         {moment(submit.time).format('YYYY-MM-DD HH:mm:ss')}
                         {message}
                         {submit.language}
+                        {if time? then time / 1000 else ""}
+                        {mem || ""}
                         {submit.comments?.length && <FontAwesome name="comment"/> || ""}
                         Подробнее
                         {if props.handleDiffClick


Remove ssl hack

Remove ssl hack

--- plugins/hipchat/index.coffee
+++ plugins/hipchat/index.coffee
@@ -13,8 +13,6 @@
     "#{host}#{path}?auth_token=#{config.authToken}"

   @receiveEvent: (config, event, callback) ->
-    process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
-
     # Build the message
     if event.error
       details =


failing test for combineAsArray.startWith created in dispatch-loop

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -1176,6 +1176,12 @@
         left = series(1, [1, 2, 3]).toProperty(0)
         left.startWith('pow')
       [0, 1, 2, 3], unstable)
+  describe "works with combineAsArray", ->
+    result = null
+    a = Bacon.constant("lolbal")
+    result = Bacon.combineAsArray([a.map(true), a.map(true)]).map("right").startWith("wrong")
+    result.onValue((x) -> result = x)
+    expect(result).to.equal("right")
   it "toString", ->
     expect(Bacon.constant(2).startWith(1).toString()).to.equal("Bacon.constant(2).startWith(1)")

@@ -1594,13 +1600,20 @@
     expectStreamEvents(
       -> Bacon.repeatedly(t(1), [1, 2, 3]).toProperty().changes().take(1)
       [1])
-  it "works with independent observables created within the dispatch loop", ->
-    calls = 0
-    Bacon.once(1).onValue ->
-      Bacon.combineAsArray([Bacon.constant(1)]).onValue ->
-        calls++
-    expect(calls).to.equal(1)
-
+  describe "works with independent observables created within the dispatch loop", ->
+    it "combineAsArray", ->
+      calls = 0
+      Bacon.once(1).onValue ->
+        Bacon.combineAsArray([Bacon.constant(1)]).onValue ->
+          calls++
+      expect(calls).to.equal(1)
+    it "combineAsArray.startWith", ->
+      result = null
+      Bacon.once(1).onValue ->
+        a = Bacon.constant("lolbal")
+        result = Bacon.combineAsArray([a.map(true), a.map(true)]).map("right").startWith("wrong");
+        result.onValue((x) -> result = x)
+      expect(result).to.equal("right")

 describe "Bacon.combineAsArray", ->
   describe "initial value", ->


more specs for throttle

more specs for throttle

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -322,6 +322,10 @@
     expectStreamEvents(
       -> series(2, [1, error(), 2]).throttle(t(7))
       [error(), 2])
+  it "waits for a quiet period before outputing anything", ->
+    expectStreamTimings(
+      -> series(2, [1, 2, 3, 4]).throttle(t(3))
+      [[11, 4]])

 describe "EventStream.bufferWithTime", ->
   it "returns events in bursts, passing through errors", ->


Add no keybinding match

Add no keybinding match

--- lib/keybinding-resolver-view.coffee
+++ lib/keybinding-resolver-view.coffee
@@ -39,6 +39,10 @@
     @subscribe atom.keymap, "parital-keybindings-triggered", (keystrokes, keybindings) =>
       @updatePartial(keystrokes, keybindings)

+    @subscribe atom.keymap, "no-keybindings-triggered", (keystrokes) =>
+      @keystroke.html $$ -> @span class: 'keystroke', "#{keystrokes}"
+      @commands.empty()
+
   detach: ->
     super


improved test runner

improved test runner

--- test/helpers.coffee
+++ test/helpers.coffee
@@ -3,7 +3,7 @@
 assert = require "assert"
 Testify = require "testify"

-[_n, _f, property_name, count] = process.argv
+[_n, _f, property_name, test_number] = process.argv

 exports.test = (constructor, version) ->

@@ -17,10 +17,10 @@
       if !property
         throw new Error "No such property to test: '#{property_name}'"
       else
-        run_suite constructor, t, property_name, property
+        run_attribute constructor, t, property_name, property
     else
       for name, property of properties
-        run_suite constructor, t, name, property
+        run_attribute constructor, t, name, property

 ignores = [ "optional" ]
@@ -29,29 +29,33 @@
   properties = {}
   files = fs.readdirSync("test/JSON-Schema-Test-Suite/tests/#{version}")
   for path in files when !(ignores.some (i) -> i == path)
+    continue if path.indexOf(".") == 0
     key = path.split(".")[0]
     string = fs.readFileSync("test/JSON-Schema-Test-Suite/tests/#{version}/#{path}", "utf8")
     properties[key] = JSON.parse string
   properties

-run_suite = (constructor, context, name, property) ->
+run_attribute = (constructor, context, name, attribute) ->
   context.test name, (context) ->

-    for suite, i in property
-      if count && parseInt(count) == i
-        return
+    if test_number
+      suite = attribute[parseInt(test_number) - 1]
+      run_suite(constructor, context, suite)
+    else

-      context.test suite.description, (context) ->
-        validator = new constructor(suite.schema)
+      for suite, i in attribute
+        run_suite(constructor, context, suite)

-        for document in suite.tests
-          context.test document.description, ->
-            result = validator.validate(document.data)
-            assert.equal result.valid, document.valid
+
+run_suite = (constructor, context, suite) ->
+  context.test suite.description, (context) ->
+    validator = new constructor(suite.schema)
+
+    for document in suite.tests
+      context.test document.description, ->
+        result = validator.validate(document.data)
+        assert.equal result.valid, document.valid

-
-
-


Remove redundant server directory

Remove redundant server directory


Wait for support on drone.io with saucelabs


Remove bad ref

Remove bad ref

--- bokehjs/src/coffee/api/models.coffee
+++ bokehjs/src/coffee/api/models.coffee
@@ -5,7 +5,6 @@
   BoxAnnotation:                          require("../models/annotations/box_annotation").Model
   PolyAnnotation:                         require("../models/annotations/poly_annotation").Model
   Span:                                   require("../models/annotations/span").Model
-  Title:                                  require("../models/annotations/title").Model
   #Overlay:                               require("../models/annotations/overlay").Model
   Tooltip:                                require("../models/annotations/tooltip").Model


- [misc] changed options in htmlmin module to strip comments from cdata and removed minify options since it should be done by other modules

--- tasks/grunt-contrib-htmlmin.coffee
+++ tasks/grunt-contrib-htmlmin.coffee
@@ -8,14 +8,13 @@
     build:
       options:
         removeComments: true
+        removeCommentsFromCDATA: true
         collapseWhitespace: true
         conservativeCollapse: true
         collapseBooleanAttributes: true
         removeEmptyAttributes: true
         removeScriptTypeAttributes: true
         removeStyleLinkTypeAttributes: true
-        minifyJS: true
-        minifyCSS: true
       files: [
         expand: true
         cwd: '<%= path.build.root %>'


Fix cleared filters submitting "0" as value in queries

--- app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -54,6 +54,7 @@
     $scope.fetchProducts()

   $scope.fetchProducts = ->
+    removeClearedValues()
     params = {
       'q[name_cont]': $scope.query,
       'q[supplier_id_eq]': $scope.producerFilter,
@@ -64,6 +65,11 @@
     }
     RequestMonitor.load(BulkProducts.fetch(params).$promise).then ->
       $scope.resetProducts()
+
+  removeClearedValues = ->
+    delete $scope.producerFilter if $scope.producerFilter == "0"
+    delete $scope.categoryFilter if $scope.categoryFilter == "0"
+    delete $scope.importDateFilter if $scope.importDateFilter == "0"

   $timeout ->
     if $scope.showLatestImport


Changing per_page should also reset the query

--- app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -46,7 +46,7 @@
     .catch (message) ->
       $scope.api_error_msg = message

-  $scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter]', ->
+  $scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter, per_page]', ->
     $scope.page = 1 # Reset page when changing filters for new search

   $scope.changePage = (newPage) ->


Scale merit hourly load chart with demand

The maximum value of the chart was being determined by the sum of all
producers. This resulted in excess demand being rendered off the scale.

--- app/assets/javascripts/d3/merit_order_hourly_supply.coffee
+++ app/assets/javascripts/d3/merit_order_hourly_supply.coffee
@@ -142,4 +142,8 @@
           else
             result[index] = value

+      # Add the peak demand so that it isn't rendered off-screen when demand
+      # exceeds supply.
+      result.push(d3.max(@rawChartData[0].values))
+
       d3.max(result)


`typeInjection` was private, change that to `injection`.

--- app/assets/javascripts/initializers/current_user.js.coffee
+++ app/assets/javascripts/initializers/current_user.js.coffee
@@ -9,5 +9,5 @@
       user = store.find 'user', window.currentUserName
       controller.set 'content', user

-    container.typeInjection 'controller', 'currentUser', 'controller:currentUser'
-    container.typeInjection 'route', 'currentUser', 'controller:currentUser'
+    container.injection 'controller', 'currentUser', 'controller:currentUser'
+    container.injection 'route', 'currentUser', 'controller:currentUser'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/metered.coffee
+++ test/metered.coffee
@@ -18,6 +18,6 @@

   equals obj.I.maxHealth, 150

-test "should set both <attribute> and max<att>", ->
+test "should set both <attribute> and max<attribute>", ->

 module()


masons masters day 2 game input in #66

masons masters day 2 game input in #66

--- tests/TestGame.coffee
+++ tests/TestGame.coffee
@@ -622,8 +622,192 @@
   ]
 }

+game_16_03_2 = {
+  "referee": "Наталья Сергеевна",
+  "date": "2014-03-16",
+  "result": "mafia_win",
+  "firstKilledAtNight": "Олифер",
+  "firstKilledByDay": "Кошка",
+  "bestMoveAuthor": "Олифер",
+  "bestMoveAccuracy": 1,
+  "players": [
+    {
+      "name": "Озб",
+      "role": "don",
+      "fouls": 1,
+      "likes": 7,
+      "isBest": true,
+      "extraScores": 3.5
+    },
+    {
+      "name": "Кошка",
+      "role": "mafia",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 2
+    },
+    {
+      "name": "Меля",
+      "role": "citizen",
+      "fouls": 1,
+      "likes": 1,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Кудряш",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Sam",
+      "role": "sheriff",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Малика",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Мим",
+      "role": "mafia",
+      "fouls": 0,
+      "likes": 2,
+      "isBest": false,
+      "extraScores": 2
+    },
+    {
+      "name": "kors",
+      "role": "citizen",
+      "fouls": 1,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Олифер",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Женька-Печенька",
+      "role": "citizen",
+      "fouls": 1,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    }
+  ]
+}
+
+game_16_03_1 = {
+  "referee": "kazzantip",
+  "date": "2014-03-16",
+  "result": "citizens_win",
+  "firstKilledAtNight": "kors",
+  "firstKilledByDay": "Олифер",
+  "bestMoveAuthor": "kors",
+  "bestMoveAccuracy": 2,
+  "players": [
+    {
+      "name": "Меля",
+      "role": "mafia",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Олифер",
+      "role": "don",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Женька-Печенька",
+      "role": "citizen",
+      "fouls": 1,
+      "likes": 2,
+      "isBest": false,
+      "extraScores": 1.5
+    },
+    {
+      "name": "Малика",
+      "role": "mafia",
+      "fouls": 1,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 0
+    },
+    {
+      "name": "Озб",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 4,
+      "isBest": true,
+      "extraScores": 2
+    },
+    {
+      "name": "Мим",
+      "role": "citizen",
+      "fouls": 2,
+      "likes": 3,
+      "isBest": true,
+      "extraScores": 2
+    },
+    {
+      "name": "Sam",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 1,
+      "isBest": false,
+      "extraScores": 1.5
+    },
+    {
+      "name": "Кошка",
+      "role": "citizen",
+      "fouls": 0,
+      "likes": 1,
+      "isBest": true,
+      "extraScores": 2
+    },
+    {
+      "name": "Кудряш",
+      "role": "citizen",
+      "fouls": 1,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 1.5
+    },
+    {
+      "name": "kors",
+      "role": "sheriff",
+      "fouls": 0,
+      "likes": 0,
+      "isBest": false,
+      "extraScores": 1.5
+    }
+  ]
+}
+
 games = [game_10_03_3, game_10_03_3_1,
-         game_16_03_3,
+         game_16_03_2, game_16_03_3, game_16_03_1,
          game_20_03_1, game_20_03_2, game_20_03_3, game_20_03_4]

 module.exports = games


Add git sections.

Add git sections.

--- prompt.coffee
+++ prompt.coffee
@@ -1,5 +1,6 @@
 module.exports = (Impromptu, section) ->
   system = Impromptu.module.require 'impromptu-system'
+  git = Impromptu.module.require 'impromptu-git'

   section 'user',
     content: [system.user, system.shortHost]
@@ -13,6 +14,24 @@
     background: 'blue'
     foreground: 'black'

+  section 'git:in',
+    when: git.branch
+    content: 'in'
+    background: 'black'
+
+  section 'git:branch',
+    content: git.branch
+    background: 'green'
+    foreground: 'black'
+
+  section 'git:status',
+    when: git.status
+    content: [git.untracked, git.modified, git.deleted]
+    format: (untracked, modified, deleted) ->
+      "(#{untracked}/#{modified}/#{deleted})"
+    background: 'green'
+    foreground: 'white'
+
   section 'end',
     content: '\n$'
     foreground: 'blue'


Use native Array reduce method (underscore is not defined).

--- prompt.coffee
+++ prompt.coffee
@@ -1,3 +1,4 @@
+
 module.exports = (Impromptu, section) ->
   system = Impromptu.module.require 'impromptu-system'
   git = Impromptu.module.require 'impromptu-git'
@@ -30,7 +31,7 @@
     foreground: 'magenta'
     background: 'white'
     format: (statuses) ->
-      staged = _.reduce statuses, (memo, status) ->
+      staged = statuses.reduce (memo, status) ->
         memo += 1 if status.staged
         memo
       , 0


Update Checkout service to send payment details when transitioning to payment state

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -49,24 +49,15 @@
       @put(params, ignoreLoadingIndicator: true)

     setPayment: ->
-      order  = Cart.current
+      order = Cart.current
+      card  = order.creditCard
       paymentMethodId = @_findPaymentMethodId()

       params =
         'order[payments_attributes][][payment_method_id]': paymentMethodId
         'order[existing_card]': ''
         'state': 'payment'
-
-      @put(params, ignoreLoadingIndicator: true)
-
-    complete: ->
-      order = Cart.current
-      card  = order.creditCard
-      paymentMethodId = @_findPaymentMethodId()
-
-      params =
         order: {}
-        'order[payments_attributes][][payment_method_id]': paymentMethodId
         payment_source: {}

       if card.id
@@ -82,7 +73,12 @@

         params.payment_source[paymentMethodId] = sourceParams

-      @put(params)
+      @put(params, ignoreLoadingIndicator: true)
+
+    complete: ->
+      order = Cart.current
+
+      @put()
         .then (data) ->
           Cart.lastOrder = Sprangular.extend(data, Sprangular.Order)


Move tests into the actual methods they test

--- test/lib/models/device-spec.coffee
+++ test/lib/models/device-spec.coffee
@@ -13,18 +13,76 @@
       @dependencies = {database: @database, getGeo: @getGeo}
       done error

-  describe 'when a device already exists', ->
-    beforeEach (done) ->
-      @uuid = '66e20044-7262-4c26-84f0-c2c00fa02465';
-      @devices.insert {uuid: @uuid}, done
-      @sut = new Device(uuid: @uuid, @dependencies)
-
+  describe '->fetch', ->
+    describe "when a device doesn't exist", ->
+      beforeEach (done) ->
+        @sut = new Device
+        @sut.fetch (@error) => done()
+
+      it 'should respond with an error', ->
+        expect(@error).to.exist
+        expect(@error.message).to.equal 'Device not found'
+
+    describe 'when a device exists', ->
+      beforeEach (done) ->
+        @uuid = 'b3da16bf-8397-403c-a520-cfb5f6bac798'
+        @devices.insert uuid: @uuid, name: 'hahahaha', done
+
+      beforeEach (done) ->
+        @sut = new Device uuid: @uuid, @dependencies
+        @sut.fetch (@error, @device) => done()
+
+      it 'should respond with the device', ->
+        expect(@device.name).to.equal 'hahahaha'
+
+      it 'should respond with no error', ->
+        expect(@error).not.to.exist
+
+  describe '->sanitize', ->
+    describe 'when update is called with one good and one bad param', ->
+      beforeEach ->
+        @sut = new Device
+        @result = @sut.sanitize name: 'guile', '$natto': 'fermented soybeans'
+
+      it 'should strip the bad params', ->
+        expect(@result['$natto']).to.not.exist
+
+      it 'should leave the good param', ->
+        expect(@result.name).to.equal 'guile'
+
+    describe 'when update is called with a nested bad param', ->
+      beforeEach ->
+        @sut = new Device
+        @result = @sut.sanitize name: 'guile', foo: {'$natto': 'fermented soybeans'}
+
+      it 'should strip the nested bad param', ->
+        expect(@result.foo).to.deep.equal {}
+
+      it 'should leave the good param', ->
+        expect(@result.name).to.equal 'guile'
+
+    describe 'when update is called with a bad param nested in an object in an array', ->
+      beforeEach ->
+        @sut = new Device
+        @result = @sut.sanitize name: 'guile', foo: [{'$natto': 'fermented soybeans'}]
+
+      it 'should strip the offending param', ->
+        expect(@result.foo).to.deep.equal [{}]
+
+      it 'should keep the good param', ->
+        expect(@result.name).to.equal 'guile'
+
+  describe '->save', ->
     describe 'when a device is saved', ->
+      beforeEach (done) ->
+        @uuid = '66e20044-7262-4c26-84f0-c2c00fa02465';
+        @devices.insert {uuid: @uuid}, done
+
       beforeEach (done) ->
         @getGeo = sinon.stub().yields null, {city: 'phoenix'}
         @dependencies.getGeo = @getGeo
         @sut = new Device(uuid: @uuid, @dependencies)
-        @sut.set name: 'VW bug', ipAddress: '192.168.1.1'
+        @sut.set name: 'VW bug', online: true, ipAddress: '192.168.1.1'
         @sut.save done

       beforeEach (done) ->
@@ -43,213 +101,188 @@
       it 'should set geo with city', ->
         expect(@device.geo.city).to.equal 'phoenix'

-      it 'should call getGeo', ->
-        expect(@getGeo).to.have.been.called
-
-    describe 'when update is called with one good and one bad param', ->
-      beforeEach ->
-        @sut = new Device { uuid: @uuid, name: 'guile', '$natto': 'fermented soybeans'}, @dependencies
-
-      it 'should update the record', ->
-        expect(@sut.attributes.name).to.equal 'guile'
-        expect(@sut.attributes['$natto']).to.not.exist
-
-    describe 'when update is called with a nested bad param', ->
-      beforeEach ->
-        @sut = new Device {uuid: @uuid, name: 'guile', foo: {'$natto': 'fermented soybeans'}}, @dependencies
-
-      it 'should update the record', ->
-          expect(@sut.attributes.name).to.equal 'guile'
-          expect(@sut.attributes.foo).to.deep.equal {}
-
-    describe 'when update is called with a bad param nested in an object in an array', ->
-      beforeEach ->
-        @sut = new Device {uuid: @uuid, name: 'guile', foo: [{'$natto': 'fermented soybeans'}]}, @dependencies
-
-      it 'should update the record', ->
-        expect(@sut.attributes.name).to.equal 'guile'
-        expect(@sut.attributes.foo).to.deep.equal [{}]
-
-    describe 'when a device is saved with a different name', ->
-      beforeEach (done) ->
-        @sut.set name: 'Corvette Stingray'
-        @sut.save done
-
-      it 'should update the record in devices', (done) ->
-        @devices.findOne {uuid: @uuid}, (error, device) =>
-          expect(device.name).to.equal 'Corvette Stingray'
-          done()
-
+      it 'should set onlineSince', ->
+        expect(@device.onlineSince.getTime()).to.be.closeTo moment().utc().valueOf(), 1000
+
+    describe 'when two devices exist', ->
+      beforeEach (done) ->
+        @uuid1 = '8172bd75-905f-409e-91d7-121ac0456229'
+        @devices.insert {uuid: @uuid1}, done
+
+      beforeEach (done) ->
+        @uuid2 = '190f8795-cc33-46d4-834e-f6b91920af77'
+        @devices.insert {uuid: @uuid2}, done
+
+      describe 'when first device is modified', ->
+        beforeEach (done) ->
+          @sut = new Device uuid: @uuid1, foo: 'bar', @dependencies
+          @sut.save done
+
+        it 'should update the correct device, because this would never happen in real life', (done) ->
+          @devices.findOne {uuid: @uuid1}, (error, device) =>
+            return done error if error?
+            expect(device.foo).to.equal 'bar'
+            done()
+
+        it 'should update the correct device, because this would never happen in real life', (done) ->
+          @devices.findOne {uuid: @uuid2}, (error, device) =>
+            return done error if error?
+            expect(device.foo).to.not.exist
+            done()
+
+      describe 'when second device is modified', ->
+        beforeEach (done) ->
+          @sut = new Device uuid: @uuid2, foo: 'bar', @dependencies
+          @sut.save done
+
+        it 'should not update the first device', (done) ->
+          @devices.findOne {uuid: @uuid1}, (error, device) =>
+            return done error if error?
+            expect(device.foo).to.not.exist
+            done()
+
+        it 'should update second device', (done) ->
+          @devices.findOne {uuid: @uuid2}, (error, device) =>
+            return done error if error?
+            expect(device.foo).to.equal 'bar'
+            done()
+
+  describe '->set', ->
+    describe 'when called with a new name', ->
+      beforeEach ->
+        @sut = new Device name: 'first'
+        @sut.set name: 'second'
+
+      it 'should update the name', ->
+        expect(@sut.attributes.name).to.equal 'second'
+
+    describe 'when set is called disallowed keys', ->
+      beforeEach ->
+        @sut = new Device
+        @sut.set $$hashKey: true
+
+      it 'should remove keys beginning with $', ->
+        expect(@sut.attributes.$$hashKey).to.not.exist
+
+    describe 'when called with an online of "false"', ->
+      beforeEach ->
+        @sut = new Device
+        @sut.set online: 'false'
+
+      it 'should set online to true, cause strings is truthy, yo', ->
+        expect(@sut.attributes.online).to.be.true
+
+    describe 'when set is called with an online of false', ->
+      beforeEach ->
+        @sut = new Device
+        @sut.set online: false
+
+      it 'should set online to false', ->
+        expect(@sut.attributes.online).to.be.false
+
+    describe 'when set doesnt mention online', ->
+      beforeEach ->
+        @sut = new Device
+        @sut.set name: 'george'
+
+      it 'should leave online alone', ->
+        expect(@sut.attributes.online).to.not.exist
+
+  describe '->validate', ->
     describe 'when created with a different uuid', ->
-      beforeEach (done) ->
-        @sut = new Device(uuid: 'wrong-uuid', @dependencies)
-        @sut.set name: 'Corvette Stingray'
-        @sut.save (@error) => done()
-
-      it 'should respond with an error', ->
-        expect(@error).to.exist
-        expect(@error.message).to.equal 'Device not found'
-
-    describe 'when updated with a different uuid', ->
-      beforeEach (done) ->
+      beforeEach ->
+        @sut = new Device uuid: 'f853214e-69b9-4ca7-a11e-7ee7b1f8f5be'
         @sut.set uuid: 'different-uuid'
-        @sut.save (@error) => done()
-
-      it 'should respond with an error', ->
-        expect(@error).to.exist
-        expect(@error.message).to.equal 'Cannot modify uuid'
+        @result = @sut.validate()
+
+      it 'should return false', ->
+        expect(@result).to.be.false
+
+      it 'should set error on the device', ->
+        expect(@sut.error).to.exist
+        expect(@sut.error.message).to.equal 'Cannot modify uuid'

     describe 'when updated with the same uuid', ->
-      beforeEach (done) ->
+      beforeEach ->
+        @uuid = '758a080b-fd29-4413-8339-53cc5de3a649'
+        @sut = new Device uuid: @uuid
         @sut.set uuid: @uuid
-        @sut.save (@error) => done()
-
-      it 'should not respond with an error', ->
-        expect(@error).to.not.exist
-
-    describe 'when a device is instantiated with a name', ->
-      beforeEach ->
-        @sut = new Device {uuid: @uuid, name: 'Daf'}, @dependencies
-
-      describe 'when the device is saved', ->
-        beforeEach (done) ->
-          @sut.save done
-
-        it 'should update the device record with the name', (done) ->
-          @devices.findOne uuid: @uuid, (error, device) =>
-            done error if error?
-            expect(device.name).to.equal 'Daf'
-            done()
-
-      describe 'when a set is called and the device is saved', ->
-        beforeEach (done) ->
-          @sut.set foo: 'bar'
-          @sut.save done
-
-        it 'should store the name', (done) ->
-          @devices.findOne uuid: @uuid, (error, device) =>
-            done error if error?
-            expect(device.name).to.equal 'Daf'
-            done()
-
-    describe 'when saved with a token', ->
-      beforeEach (done) ->
-        @attributes = {uuid: @uuid, name: 'Cherokee', token : 'plain-text-token'}
-        @sut = new Device @attributes, @dependencies
-        @sut.save(done)
-
-      it 'should update a hash of the token', (done) ->
-        @database.devices.findOne { uuid: @uuid }, (error, device) =>
-          return done error if error?
-          expect(bcrypt.compareSync(@attributes.token, device.token)).to.be.true
-          done()
-
-  describe 'when a device with a token exists', ->
-    beforeEach (done) ->
-      @uuid = '028ca74a-7e64-47fd-b55a-8abcd0c09dae'
-      @hashedToken = bcrypt.hashSync 'cool-token', 8
-      @devices.insert {uuid: @uuid, token: @hashedToken}, done
-
-    describe 'when the device is saved with no new token', ->
-      beforeEach (done) ->
-        @sut = new Device uuid: @uuid, @dependencies
-        @sut.save done
-
-      it 'should not modify the token', (done) ->
-        @devices.findOne uuid: @uuid, (error, device) =>
-          return done error if error?
-          expect(device.token).to.equal @hashedToken
-          done()
-
-    describe 'when instantiated with the hashed token', ->
-      beforeEach (done) ->
-        @sut = new Device uuid: @uuid, token: @hashedToken, @dependencies
-        @sut.save done
-
-      it 'should not rehash the token', (done) ->
-        @devices.findOne uuid: @uuid, (error, device) =>
-          return done error if error?
-          expect(device.token).to.equal @hashedToken
-          done()
-
-  describe 'when a device exists with online', ->
-    beforeEach (done) ->
-      @uuid = 'dab71557-c8a4-45d9-95ae-8dfd963a2661'
-      @onlineSince = new Date(1422484953078)
-      @attributes = {uuid: @uuid, online: true, onlineSince: @onlineSince}
-      @devices.insert @attributes, done
-
-    describe 'when we save it with online true', ->
-      beforeEach (done) ->
-        @sut = new Device uuid: @uuid, online: true, @dependencies
-        @sut.save done
-
-      it 'should not update onlineSince', (done) ->
-        @devices.findOne uuid: @uuid, (error, device) =>
-          return done error if error?
-          onlineSinceTime = device.onlineSince.getTime()
-          expect(onlineSinceTime).to.equal @onlineSince.getTime()
-          done()
-
-  describe 'when set is called with an online of "false"', ->
-    beforeEach ->
-      @sut = new Device
-      @sut.set online: 'false'
-
-    it 'should set online to true, cause strings is truthy, yo', ->
-      expect(@sut.attributes.online).to.be.true
-
-  describe 'when set is called disallowed keys', ->
-    beforeEach ->
-      @sut = new Device
-      @sut.set $$hashKey: true
-
-    it 'should remove keys beginning with $', ->
-      expect(@sut.attributes.$$hashKey).to.not.exist
-
-  describe 'when set is called with an online of false', ->
-    beforeEach ->
-      @sut = new Device
-      @sut.set online: false
-
-    it 'should set online to false', ->
-      expect(@sut.attributes.online).to.be.false
-
-  describe 'when set doesnt mention online', ->
-    beforeEach ->
-      @sut = new Device
-      @sut.set name: 'george'
-
-    it 'should leave online alone', ->
-      expect(@sut.attributes.online).to.not.exist
-
-    it 'should not set onlineSince', ->
-      expect(@sut.attributes.onlineSince).to.not.exist
-
-  describe 'when updating multiple devices', ->
-    beforeEach (done) ->
-      @uuid1 = '8172bd75-905f-409e-91d7-121ac0456229'
-      @uuid2 = '190f8795-cc33-46d4-834e-f6b91920af77'
-      createDevice1 = (callback) =>
-        @devices.insert {uuid: @uuid1}, =>
-          @device1 = new Device uuid: @uuid1, token: '123', foo: 'bar', {database : @database}
-          @device1.save (error) ->
-            callback error
-
-      createDevice2 = (callback) =>
-        @devices.insert {uuid: @uuid2}, =>
-          @device2 = new Device uuid: @uuid2, token: '123', bar: 'baz', {database : @database}
-          @device2.save (error) ->
-            callback error
-
-      async.series [createDevice1, createDevice2], done
-
-    it 'should update the correct device, because this would never happen in real life', (done) ->
-      @devices.findOne {uuid: @uuid1}, (error, device) =>
-        expect(device.foo).to.equal 'bar'
-        done()
-
-    it 'should update the correct device, because this would never happen in real life', (done) ->
-      @devices.findOne {uuid: @uuid2}, (error, device) =>
-        expect(device.bar).to.equal 'baz'
-        done()
+        @result = @sut.validate()
+
+      it 'should return true', ->
+        expect(@result).to.be.true
+
+      it 'should not set an error on the device', ->
+        expect(@sut.error).to.not.exist
+
+  describe '->addGeo', ->
+    describe 'when a device has an ipAddress', ->
+      beforeEach (done) ->
+        @dependencies.getGeo = sinon.stub().yields null, {city: 'smallville'}
+        @sut = new Device ipAddress: '127.0.0.1', @dependencies
+        @sut.addGeo done
+
+      it 'should call getGeo with the ipAddress', ->
+        expect(@dependencies.getGeo).to.have.been.calledWith '127.0.0.1'
+
+      it 'should set the getGeo response on attributes', ->
+        expect(@sut.attributes.geo).to.deep.equal {city: 'smallville'}
+
+    describe 'when a device has no ipAddress', ->
+      beforeEach (done) ->
+        @dependencies.getGeo = sinon.spy()
+        @sut = new Device {}, @dependencies
+        @sut.addGeo done
+
+      it 'should not call getGeo', ->
+        expect(@dependencies.getGeo).not.to.have.been.called
+
+  describe '->addHashedToken', ->
+    describe 'when a device exists', ->
+      beforeEach (done) ->
+        @uuid = 'd17f2411-6465-4a02-b658-6b5c992fb7b2'
+        @attributes = {uuid: @uuid, name: 'Cherokee', token : bcrypt.hashSync('cool-token', 8)}
+        @devices.insert @attributes, done
+
+      describe 'when the device has an unhashed token', ->
+        beforeEach (done) ->
+          @sut = new Device uuid: @uuid, token: 'new-token', @dependencies
+          @sut.addHashedToken(done)
+
+        it 'should hash the token', ->
+          expect(bcrypt.compareSync('new-token', @sut.attributes.token)).to.be.true
+
+      describe 'when the device has no token', ->
+        beforeEach (done) ->
+          @sut = new Device uuid: @uuid, @dependencies
+          @sut.addHashedToken(done)
+
+        it 'should not modify the token', ->
+          expect(@sut.token).not.to.exist
+
+      describe 'when instantiated with the hashed token', ->
+        beforeEach (done) ->
+          @sut = new Device @attributes, @dependencies
+          @sut.addHashedToken done
+
+        it 'should not rehash the token', ->
+          expect(@sut.attributes.token).to.equal @attributes.token
+
+  describe '->addOnlineSince', ->
+    describe 'when a device exists with online', ->
+      beforeEach (done) ->
+        @uuid = 'dab71557-c8a4-45d9-95ae-8dfd963a2661'
+        @onlineSince = new Date(1422484953078)
+        @attributes = {uuid: @uuid, online: true, onlineSince: @onlineSince}
+        @devices.insert @attributes, done
+
+      describe 'when set online true', ->
+        beforeEach (done) ->
+          @sut = new Device uuid: @uuid, online: true, @dependencies
+          @sut.addOnlineSince done
+
+        it 'should not update onlineSince', ->
+          expect(@sut.attributes.onlineSince).not.to.exist
+
+
+


line_count: rename test label

line_count: rename test label

--- test/properties.lines_count.coffee
+++ test/properties.lines_count.coffee
@@ -1,7 +1,7 @@

 parse = require '../src'

-describe 'properties number of lines', ->
+describe 'properties lines_count', ->

   it 'adds up with default settings', (next) ->
     parser = parse()


Another test case.

Another test case.

Trying to hunt down some weirdness.

--- test/render_operations.spec.coffee
+++ test/render_operations.spec.coffee
@@ -54,3 +54,12 @@

     it 'should make sure the  tags are within the
 tags', ->
       (expect @res).to.equal '
a b

c
'
+
+    describe 'When there is a change at the beginning, in a
', ->
+      beforeEach ->
+        before = ['
', 'this', ' ', 'is', ' ', 'awesome', '
']
+        after = ['
', 'I', ' ', 'is', ' ', 'awesome', '
']
+        @res = @cut before, after
+
+      it 'should keep the change inside the
', ->
+        (expect @res).to.equal '
thisI is awesome
'


Scenario 37. Updated

Scenario 37. Updated

Scenario 37. Two tags with two attribute, a space at the beginning of
the first attribute

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -386,7 +386,7 @@
     # console.log expectedOutput
     expect(output).to.equal(expectedOutput)

-  it 'Scenario 37. Two tags with two attribute, a space in the first at the beginning', ->
+  it 'Scenario 37. Two tags with two attribute, a space at the beginning of the first attribute', ->

     input = fs.readFileSync './test/scenarios/scenario-37-input.cohtml', 'utf8'
     # console.log input


Style fix.

Style fix.

--- test/src/response_type_test.coffee
+++ test/src/response_type_test.coffee
@@ -68,7 +68,7 @@
           buffer = @xhr.response
           expect(buffer).to.be.instanceOf Buffer
           stringChars = for i in [0...buffer.length]
-            String.fromCharCode buffer.readUInt8 i
+            String.fromCharCode buffer.readUInt8(i)
           expect(stringChars.join('')).to.equal @jsonString
           done()
         @xhr.open 'GET', @jsonUrl


ordered container

ordered container

--- packages/cbga:core/component.coffee
+++ packages/cbga:core/component.coffee
@@ -18,6 +18,8 @@
     moveTo: (container, properties) ->
         properties ?= {}
         properties._container = container._toDb?() ? container
+        if properties._container[1]._id?
+            properties._container[1] = properties._container[1]._id
         for name, value of properties
             @[name] = value
         @emit 'changed', $set: properties
@@ -56,7 +58,7 @@
         rulesName = if @type is 'game'
             @owner.rules
         else
-            @owner.game().rules
+            CBGA.Games.findOne(@owner._game).rules
         @rules = CBGA.getGameRules rulesName
         unless @owner instanceof CBGA._DbModelBase
             rulesName = if @type is 'game'
@@ -76,3 +78,33 @@
         if typeof selector isnt 'string'
             selector._container = @_toDb()
         @rules.findComponents selector, options
+
+    # For completeness, because I surprised myself expecting it to exist
+    findOne: (selector, options) ->
+        options ?= {}
+        options.limit = 1
+        @find(selector, options).fetch()[0]
+
+class CBGA.OrderedContainer extends CBGA.Container
+    find: (selector, options) ->
+        options ?= {}
+        options.sort ?= position: 1
+        super selector, options
+
+    shuffle: ->
+        ids = _.pluck CBGA.Components.find(_container: @_toDb(),
+            fields: _id: 1
+        ).fetch(), '_id'
+        _.shuffle ids
+        @repack ids
+
+    repack: (ids) ->
+        unless ids?
+            ids = _.pluck CBGA.Components.find(_container: @_toDb(),
+                fields: _id: 1
+            ).fetch(), '_id'
+        position = 0
+        for id in ids
+            CBGA.Components.update id, $set: position: id
+            position += 1
+        position


added empty users and weight fields in up and down votes


pricing: simplify `JPaymentPlan#subscribe$` call

--- workers/social/lib/social/models/payment/resourceplan.coffee
+++ workers/social/lib/social/models/payment/resourceplan.coffee
@@ -51,10 +51,7 @@
       options.quantities = quantities
       options.couponCode = plan.couponCodes?[promotionType]

-      plan.subscribe$ client, paymentMethodId, options, (err, subscription) ->
-        return callback err  if err
-
-        callback null, subscription
+      plan.subscribe$ client, paymentMethodId, options, callback

   @subscribe = secure (client, options, callback) ->
     JPaymentPlan = require './plan'


New jQuery style

New jQuery style

--- extensions/tree/tree-pane.coffee
+++ extensions/tree/tree-pane.coffee
@@ -15,7 +15,7 @@

     @render()

-    $('#tree li').live 'click', (event) =>
+    $(document).on 'click', '#tree li', (event) =>
       $('#tree .active').removeClass 'active'

       el = $(event.currentTarget)


Allow published content to be added without deriving it first

--- src/scripts/modules/media/tabs/contents/popovers/add/modals/add-page.coffee
+++ src/scripts/modules/media/tabs/contents/popovers/add/modals/add-page.coffee
@@ -55,19 +55,13 @@
       e.preventDefault()

       data = $(e.originalEvent.target).serializeArray()
-      models = []

       if data.length is 1
         @newPage(data[0].value)
       else
         _.each data, (input) =>
           if input.name isnt 'title'
-            models.push
-              derivedFrom: input.name
-
-        # FIX: Currently always just derive a page from published content
-        if models.length
-          @model.create(models)
+            @model.add({id: input.name, title: input.value})

       $('.modal-backdrop').remove() # HACK: Ensure bootstrap modal backdrop is removed


Don't override `i` whoops

Don't override `i` whoops

--- resources/assets/coffee/react/profile-page/header.coffee
+++ resources/assets/coffee/react/profile-page/header.coffee
@@ -326,8 +326,8 @@
     ]

     div className: bn,
-      for row, i in rows when _.compact(row).length > 0
-        div key: i, className: "#{bn}__row #{bn}__row--#{i}", row
+      for row, j in rows when _.compact(row).length > 0
+        div key: j, className: "#{bn}__row #{bn}__row--#{j}", row

   renderJoinDate: =>


scroll sync when first time render markdown

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -51,6 +51,9 @@

     # this variable will check if it is the first time to render MathJax for markdown.
     @firstTimeRenderMathJax = true
+
+    # this variable will check if it is the first time to render markdown
+    @firstTimeRenderMarkdowon = true

     # presentation mode
     @presentationMode = false
@@ -143,6 +146,7 @@
     @rootDirectoryPath = @editor.getDirectoryPath()
     @projectDirectoryPath = @getProjectDirectoryPath()
     @firstTimeRenderMathJax = true
+    @firstTimeRenderMarkdowon = true

     if @disposables # remove all binded events
       @disposables.dispose()
@@ -431,6 +435,15 @@
     @bindEvents()

     @mainModule.emitter.emit 'on-did-render-preview', {htmlString: html, previewElement: @element}
+
+    if @firstTimeRenderMarkdowon
+      @firstTimeRenderMarkdowon = false
+      cursor = @editor.cursors[0]
+      return if not cursor
+      if @presentationMode
+        @scrollSyncForPresentation cursor.getBufferRow()
+      else
+        @scrollSyncToLineNo cursor.getScreenRow()

   bindEvents: ->
     @bindTagAClickEvent()


fix codeChunk output div issue

fix codeChunk output div issue

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -8,6 +8,7 @@
 katex = require 'katex'
 matter = require('gray-matter')
 {allowUnsafeEval} = require 'loophole'
+cheerio = require 'cheerio'

 {getMarkdownPreviewCSS} = require './style'
 plantumlAPI = require './puml'
@@ -1028,6 +1029,26 @@

     exec "#{cmd} #{filePath}"

+  insertCodeChunksResult: (htmlContent)->
+    # insert outputDiv and outputElement accordingly
+    $ = cheerio.load(htmlContent, {decodeEntities: true})
+    codeChunks = $('.code-chunk')
+    for codeChunk in codeChunks
+      $codeChunk = $(codeChunk)
+      dataArgs = $codeChunk.attr('data-args')
+      idMatch = dataArgs.match(/\s*id\s*:\s*\"([^\"]*)\"/)
+
+      continue if !idMatch or !idMatch[1]
+      id = idMatch[1]
+      outputDiv = @codeChunksData[id]?.outputDiv
+      outputElement = @codeChunksData[id]?.outputElement
+
+      if outputDiv # append outputDiv result
+        $codeChunk.append("
#{outputDiv.innerHTML}
")
+
+    $.html()
+
+
   getHTMLContent: ({isForPrint, offline, useRelativeImagePath, phantomjsType})->
     isForPrint ?= false
     offline ?= false
@@ -1043,6 +1064,10 @@
     htmlContent = @formatStringAfterParsing(res.html)
     slideConfigs = res.slideConfigs
     yamlConfig = res.yamlConfig or {}
+
+
+    # replace code chunks inside htmlContent
+    htmlContent = @insertCodeChunksResult(htmlContent)

     # as for example black color background doesn't produce nice pdf
     # therefore, I decide to print only github style...


誤爆の修正

誤爆の修正

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -1,5 +1,5 @@
 module.exports = (robot) ->
-  robot.hear /((.*))の天気(.*)/i, (msg) ->
+  robot.hear /((.*))の天気/i, (msg) ->
    switch msg.match[1]
       when '今日'
         day = 0


Client:Profile: Added form input focuser

--- client/Account/views/username.coffee
+++ client/Account/views/username.coffee
@@ -141,6 +141,10 @@
     @emailForm.inputs.firstName.setDefaultValue firstName
     @emailForm.inputs.lastName.setDefaultValue lastName

+    query = KD.utils.parseQuery()
+    if query.focus
+      @emailForm.inputs[query.focus].setFocus()
+
     if @user.status is "unconfirmed"
       o =
         tagName      : "a"


Refresh unreadMark on load more messages from history

--- client/lib/RoomHistoryManager.coffee
+++ client/lib/RoomHistoryManager.coffee
@@ -37,6 +37,9 @@
 			heightDiff = wrapper.scrollHeight - previousHeight
 			wrapper.scrollTop += heightDiff

+			Meteor.defer ->
+				readMessage.refreshUnreadMark(rid)
+
 			room.isLoading.set false
 			room.loaded += result.length
 			if result.length < limit


Create counter issue

Create counter issue

--- server/src/Component/Builtin/Database/CouchMuffin.coffee
+++ server/src/Component/Builtin/Database/CouchMuffin.coffee
@@ -31,7 +31,7 @@

     _counter: (callback) ->
         @_dataSource.bucket.counter @_counterKey, 1, (error, result) ->
-            @_createCounter callback if error and error.code == 13
+            return @_createCounter callback if error and error.code == 13
             return callback error if error
             return callback null, result.value


BW & XY: Fix mirror move crashing if no target.

--- server/bw/data/moves.coffee
+++ server/bw/data/moves.coffee
@@ -693,7 +693,7 @@
 makeIdentifyMove("Miracle Eye", ["Psychic"])

 extendMove 'Mirror Move', ->
-  @execute = (battle, user, targets) ->
+  @hit = (battle, user, targets) ->
     target = targets[0]
     move = target.lastMove
     if !move? || !move.hasFlag("mirror")


Use source if there is no sourceRaw

Use source if there is no sourceRaw

--- server/models/submit.coffee
+++ server/models/submit.coffee
@@ -35,7 +35,7 @@

 submitsSchema.methods.calculateHashes = () ->
     logger.info("calculating hashes for submit #{@_id}")
-    @hashes = calculateHashes(@sourceRaw.toString())
+    @hashes = calculateHashes((@sourceRaw or @source).toString())
     logger.info("calculating hashes for submit #{@_id}, have #{@hashes.length} hashes")
     for h in @hashes
         hash = new Hash


show the number of seeders for the torrent

--- src/main.coffee
+++ src/main.coffee
@@ -51,7 +51,7 @@

   buildResult: (result) ->
     row = $("
")
-    link = $("#{result.name()}")
+    link = $("[#{result.seeders()}] #{result.name()}")
     link.appendTo(row)
     $.when(result.torrentUrl()).then (url) -> link.attr("href", url)
     row


add os version in spec

add os version in spec

--- src/spec.coffee
+++ src/spec.coffee
@@ -445,6 +445,13 @@

       current_operations: -> @genval.current_operations

+      os_version: ->
+        {guest_metrics} = @data
+        if guest_metrics
+          guest_metrics.os_version
+        else
+          null
+
       memory: ->
         {metrics, guest_metrics} = @data


Skin fixes, attr observe, fix candidate hook cache clearing,

--- packages/shrub-skin/client/index.coffee
+++ packages/shrub-skin/client/index.coffee
@@ -18,21 +18,21 @@
 			directive.candidateKeys ?= []
 			directive.candidateKeys.unshift 'id'

-			candidateHooksInvoked = {}
-
 			currentSkinKey = null
 			defaultSkinKey = config.get 'packageConfig:shrub-skin:default'

 			# Proxy link function to add our own directive retrieval and
 			# compilation step.
 			link = directive.link
-			directive.link = (scope, element, attrs, controller) ->
-
+			directive.link = (scope, element, attr, controller) ->
+
+				candidateHooksInvoked = {}
+
 				# Save top-level arguments for later calls to link functions.
 				topLevelArgs = arguments

 				# Current template candidate.
-				candidate = null
+				candidate = undefined

 				recalculateCandidate = ->

@@ -48,7 +48,7 @@
 					# interpolate candidate keys, and falling back to
 					# attribute values, if any. Candidate arrays are
 					# joined by single dashes.
-					candidateList = do (scope, attrs) ->
+					candidateList = do ->
 						list = []

 						for keys in directive.candidateKeys
@@ -56,8 +56,9 @@

 							item = []
 							for key in keys
-								specific = $interpolate("{{#{key}}}")(scope)
-								specific ?= attrs[key]
+
+								specific = scope[key]
+								specific = attr[key] unless specific

 								item.push specific if specific

@@ -79,10 +80,10 @@
 						for uri in candidateTemplates
 							return uri if skinAssets?.templates?[uri]

-						return
+						return null

 					# If the candidate changed, clear the hook invocation
-					# cache and relink
+					# cache and relink.
 					if candidate isnt oldCandidate
 						candidateHooksInvoked = {}

@@ -116,14 +117,13 @@

 				applySkin = (skinKey) ->
 					currentSkinKey = skinKey
-
-					candidateHooksInvoked = {}
 					recalculateCandidate()

 				applySkin defaultSkinKey

 				# Relink again every time the skin changes.
 				$rootScope.$on 'shrub-skin.changed', (event, skinKey) ->
+					candidateHooksInvoked = {}
 					applySkin skinKey

 				# Set watches for all candidate-related values.
@@ -136,8 +136,8 @@
 						continue if keysSeen[key]
 						keysSeen[key] = true

-						watchers.push -> scope[attrs[key]]
-						watchers.push -> $interpolate("{{#{key}}}")(scope)
+						attr.$observe key, recalculateCandidate
+						watchers.push -> scope[attr[key]]

 				scope.$watchGroup watchers, recalculateCandidate


migrated xmpp plugin

migrated xmpp plugin

--- plugins/src/scaleApp.strophe.coffee
+++ plugins/src/scaleApp.strophe.coffee
@@ -2,31 +2,11 @@
 Copyright (c) 2012 - 2013 Markus Kohlhase <mail@markus-kohlhase.de>
 ###

-throw new Error "This plugin only can be used in the browser" unless window?
-console.warn "This plugin requires strophe.js" unless window.Strophe?
-
-scaleApp = window.scaleApp
-
-mediator = new scaleApp.Mediator
-
-ID               = "xmpp"
 DEFAULT_PATH     = "http-bind/"
 DEFAULT_PROTOCOL = "http"
 DEFAULT_PORT     = 5280
-CACHE_PREFIX     = "scaleApp.#{ID}.cache."
-
-# Variable that holds the Strophe connection object
-connection = null
-
-# Object that holds all connection sepecific options.
-connection_options =
-  host:     document.domain
-  port:     DEFAULT_PORT
-  path:     DEFAULT_PATH
-  protocol: DEFAULT_PROTOCOL
-  jid:      null
-  sid:      null
-  rid:      null
+
+CACHE_PREFIX     = "scaleApp.xmpp.cache."

 # Generates an appropriate bosh url for the connection.
 #
@@ -82,49 +62,42 @@
   "http://#{ domain }/#{ DEFAULT_PATH }"

 # Creates a new Strophe.Connection object with the appropriate options.
-create_connection_obj = ->
+create_connection_obj = (opt) ->

   new Strophe.Connection get_bosh_url
-    path:     connection_options.path
-    host:     connection_options.host
-    port:     connection_options.port
-    protocol: connection_options.protocol
+    path:     opt.path
+    host:     opt.host
+    port:     opt.port
+    protocol: opt.protocol

 key2cache   = (k) -> "#{CACHE_PREFIX}#{k}"

-saveData = ->
-  if sessionStorage?
-    for k in ["jid", "sid", "rid"] when connection[k]?
-      sessionStorage[key2cache k] = connection[k]
-
-    for k in ["host", "port", "path", "protocol"] when connection_options[k]?
-      sessionStorage[key2cache k] = connection_options[k]
-
-clearData = -> sessionStorage?.clear()
-
-onConnected = ->
-
-  # The BOSH connection get aborted by the escapae event.
-  # Therefore we have to prevent it!
-
-  fn       = (ev) -> ev.preventDefault?() if ev.keyCode is 27
-  onunload = -> if connection then saveData() else clearData()
-
-  if document.addEventListener?
-    for e in ["keydown", "keypress", "keyup"]
-      document.addEventListener e, fn, false
-
-    # Save connection properties before reloading the page.
-    window.addEventListener "unload", onunload, false
-
-  else if document.attachEvent?
-    for e in ["onkeydown", "onkeypress", "onkeyup"]
-      document.attachEvent e, fn
-
-    # Save connection properties before reloading the page.
-    document.attachEvent "onunload", onunload
-
-  connection.send $pres()
+saveData = (conn, opt)->
+  return unless sessionStorage?
+  for k in ["jid", "sid", "rid"] when conn[k]?
+    sessionStorage[key2cache k] = conn[k]
+
+  for k in ["host", "port", "path", "protocol"] when opt[k]?
+    sessionStorage[key2cache k] = opt[k]
+
+clearData = ->
+  return unless sessionStorage?
+  for k in ["jid", "sid", "rid", "host", "port", "path", "protocol"]
+    sessionStorage.removeItem key2cache k
+
+# Loads the connection data from session storage.
+restoreData = ->
+  return unless sessionStorage?
+  o = {}
+  for k in [ "jid", "sid", "rid", "host", "port", "path", "protocol" ]
+    j = key2cache k
+    o[k] = sessionStorage[j] if sessionStorage[j]
+
+hasConnectionData = (opt) ->
+  for k in [ "jid", "sid", "rid" ]
+    if not opt[k] or opt[k] is 'null' or opt[k] is 'undefined'
+      return false
+  true

 statusCodeToString = (s) ->
   switch s
@@ -138,150 +111,168 @@
     when 7 then "Disconnecting"
     when 8 then "Reconnected"

-# Processes the Strophe connection staus.
-#
-# Parameters:
-# (int) status - The connection status code. See <Strophe.Status>
-
-on_connection_change = (status) ->
-
-  console.info "xmpp status changed: " + statusCodeToString status
-
-  s = Strophe.Status
-
-  switch status
-
-    when s.ERROR
-      resetPlugin()
-      mediator.emit "error", "an error occoured"
-    when s.CONNECTING
-      resetPlugin()
-      mediator.emit "connecting"
-    when s.CONFAIL
-      resetPlugin()
-      mediator.emit "error", "could not connect to xmpp server"
-    when s.AUTHENTICATING
-      resetPlugin()
-      mediator.emit "authenticating"
-    when s.AUTHFAIL
-      resetPlugin()
-      mediator.emit "authfail"
-    when s.CONNECTED
-      updatePlugin connection
-      onConnected()
-      mediator.emit "connected"
-    when s.DISCONNECTED
-      clearData()
-      resetPlugin()
-      mediator.emit "disconnected"
-    when s.DISCONNECTING
-      resetPlugin()
-      mediator.emit "disconnecting"
-    when s.ATTACHED
-      updatePlugin connection
-      onConnected()
-      mediator.emit "attached"
-
-resetPlugin = ->
-  xmppPlugin.connection = null
-  xmppPlugin.jid = ""
-
-updatePlugin = (conn) ->
-  xmppPlugin.connection = conn
-  xmppPlugin.jid = conn.jid
-
-# Attaches an existing connection to the Strophe connection object.
-#
-# Parameters:
-# (Object) cookie - the cookie object that contains the connection properties
-# like host, port, path, rid, sid and jid.
-attach_connection = (opt) ->
-
-  connection = create_connection_obj()
-  connection.attach connection_options.jid
-    , connection_options.sid
-    , connection_options.rid
-    , on_connection_change
-    , 60
-
-# Connects to the xmpp server and login with the given JID.
-#
-# Parameters:
-# (String) jid  - Jabber ID
-# (String) pw - Password
-# (Object) opt - Connection options.
-
-login = (jid, pw, opt) ->
-
-  if opt?
-    connection_options.path     = opt.path     if opt.path
-    connection_options.port     = opt.port     if opt.port
-    connection_options.host     = opt.host     if opt.host
-    connection_options.protocol = opt.protocol if opt.protocol
-
-  connection = create_connection_obj()
-  connection.connect jid, pw, on_connection_change
-
-disconnect = ->
-
-  console.debug "try to disconnect"
-
-  if connection?.connected is true
-    try
-      connection.send $pres(type: "unavailable")
-      connection.pause()
-      connection.disconnect()
-    catch e
-  clearData()
-
-# Converts a Jabber ID to an css friendly id.
-#
-# It replaces '.' and '@' with '-'.
-#
-# Parameters:
-# (String) - Jabber ID
-jid_to_id = (jid) ->
-  Strophe.getBareJidFromJid(jid)
-    .replace("@", "-")
-    .replace(".", "-")
-    .replace ".", "-"
-
-# Loads the connection data from local storage.
-restoreData = ->
-  if sessionStorage?
-    for k in [ "jid", "sid", "rid", "host", "port", "path", "protocol" ]
-      j = key2cache k
-      connection_options[k] = sessionStorage[j]  if sessionStorage[j]
-
-hasConnectionData = ->
-  opt = connection_options
-  for k in [ "jid", "sid", "rid"  ]
-    if not opt[k] or opt[k] is 'null' or opt[k] is 'undefined'
-      return false
-  true
-
-init = ->
-  restoreData()
-  if hasConnectionData()
-    attach_connection()
+plugin = (core) ->
+
+  throw new Error "This plugin only can be used in the browser" unless window?
+  console.warn "This plugin requires strophe.js" unless window.Strophe?
+
+  mediator = new core.Mediator
+
+  # Variable that holds the Strophe connection object
+  connection = null
+
+  resetPlugin = ->
+    core.xmpp.connection = null
+    core.xmpp.jid = ""
+
+  updatePlugin = (conn) ->
+    core.xmpp.connection = conn
+    core.xmpp.jid = conn.jid
+
+  onConnected = ->
+
+    # The BOSH connection get aborted by the escapae event.
+    # Therefore we have to prevent it!
+
+    fn       = (ev) -> ev.preventDefault?() if ev.keyCode is 27
+    onunload = ->
+      if connection then saveData connection, connection_options
+      else clearData()
+
+    if document.addEventListener?
+      for e in ["keydown", "keypress", "keyup"]
+        document.addEventListener e, fn, false
+
+      # Save connection properties before reloading the page.
+      window.addEventListener "unload", onunload, false
+
+    else if document.attachEvent?
+      for e in ["onkeydown", "onkeypress", "onkeyup"]
+        document.attachEvent e, fn
+
+      # Save connection properties before reloading the page.
+      document.attachEvent "onunload", onunload
+
+    connection.send $pres()
+
+  # Processes the Strophe connection staus.
+  #
+  # Parameters:
+  # (int) status - The connection status code. See <Strophe.Status>
+  on_connection_change = (status) ->
+
+    console.info "xmpp status changed: " + statusCodeToString status
+
+    s = Strophe.Status
+
+    switch status
+
+      when s.ERROR
+        resetPlugin()
+        mediator.emit "error", "an error occoured"
+      when s.CONNECTING
+        resetPlugin()
+        mediator.emit "connecting"
+      when s.CONFAIL
+        resetPlugin()
+        mediator.emit "error", "could not connect to xmpp server"
+      when s.AUTHENTICATING
+        resetPlugin()
+        mediator.emit "authenticating"
+      when s.AUTHFAIL
+        resetPlugin()
+        mediator.emit "authfail"
+      when s.CONNECTED
+        updatePlugin connection
+        onConnected()
+        mediator.emit "connected"
+      when s.DISCONNECTED
+        clearData()
+        resetPlugin()
+        mediator.emit "disconnected"
+      when s.DISCONNECTING
+        resetPlugin()
+        mediator.emit "disconnecting"
+      when s.ATTACHED
+        updatePlugin connection
+        onConnected()
+        mediator.emit "attached"
+
+  # Attaches an existing connection to the Strophe connection object.
+  #
+  # Parameters:
+  # (Object) cookie - the cookie object that contains the connection properties
+  # like host, port, path, rid, sid and jid.
+  attach_connection = (opt) ->
+
+    connection = create_connection_obj opt
+    connection.attach opt.jid, opt.sid, opt.rid, on_connection_change, 60
+
+  connection_options = restoreData()
+
+  if connection_options and hasConnectionData connection_options
+    attach_connection connection_options
   else
     mediator.emit "disconnected"

-# TODO: create core plugin
-xmppPlugin =
-  init:       init
-  jid:        ""
-  connection: null
-  login:      login
-  logout:     disconnect
-  on:  -> mediator.on.apply mediator, arguments
-  off: -> mediator.off.apply mediator, arguments
-
-plugin =
-  id: ID
-  base:
-    xmpp: xmppPlugin
-
-scaleApp.plugin.register plugin if window?.scaleApp?
-module.exports = plugin if module?.exports?
-(define -> plugin) if define?.amd?
+    # Object that holds all connection sepecific options.
+    connection_options =
+      host:     document.domain
+      port:     DEFAULT_PORT
+      path:     DEFAULT_PATH
+      protocol: DEFAULT_PROTOCOL
+      jid:      null
+      sid:      null
+      rid:      null
+
+  # Connects to the xmpp server and login with the given JID.
+  #
+  # Parameters:
+  # (String) jid  - Jabber ID
+  # (String) pw - Password
+  # (Object) opt - Connection options.
+  login = (jid, pw, opt) ->
+
+    if opt?
+      connection_options.path     = opt.path     if opt.path
+      connection_options.port     = opt.port     if opt.port
+      connection_options.host     = opt.host     if opt.host
+      connection_options.protocol = opt.protocol if opt.protocol
+
+    connection = create_connection_obj connection_options
+    connection.connect jid, pw, on_connection_change
+
+  disconnect = ->
+
+    console.debug "try to disconnect"
+
+    if connection?.connected is true
+      try
+        connection.send $pres type: "unavailable"
+        connection.pause()
+        connection.disconnect()
+      catch e
+    clearData()
+
+  # public API
+  core.xmpp =
+    jid:        ""
+    connection: null
+    login:      login
+    logout:     disconnect
+    on:  -> mediator.on.apply mediator, arguments
+    off: -> mediator.off.apply mediator, arguments
+
+  null
+
+# AMD support
+if define?.amd?
+  define -> plugin
+
+# Browser support
+else if window?.scaleApp?
+  window.scaleApp.plugins.xmpp = plugin
+
+# Node.js support
+else if module?.exports?
+  module.exports = plugin


fix: shouldn't show edit / delete buttons for all points in twocol

Former-commit-id: fe65aacc4615a70bbb12afe9878ca51391da39dd [formerly ae1fd7b32cd50a9e04d278c30563a39a5afed326] [formerly 2f03405283c2a7353c6769c0adf8b472f105d98c [formerly a6a74fdb749c68f4e92e62cacb1ba4a60e03392a]] [formerly 17bc39ace1fc760000e6bd93f2b9a10131b8a1b6 [formerly 16221b33e6cc8390489b6f9d8a5bf2162ff3e533] [formerly 76fad5c3569b2d81e57aab794492eca92d377a9e [formerly 76fad5c3569b2d81e57aab794492eca92d377a9e [formerly fca6f69652248af4dddf295a0a5ef42ac4b7b4ac]]]]
Former-commit-id: f1d02faad8017ddfac648dc6c8ac1ee2774be378 [formerly bac9c56f04eb2ec6a770e86c5956cf58c538d541] [formerly 4e11692fa4cb144d88f540e0b440f34d6453fdf1 [formerly b4a35c4f8a9847af0d767082f2aa4e729ebe09b6]]
Former-commit-id: 7e37d4ccaf0f0385ba96d877ccef25b651448f08 [formerly 3d1bd92a16a9c374379d26362353486abf15ff61]
Former-commit-id: f15375de9b4ce16305b0dc7af5f99d6440239ef5
Former-commit-id: 554808e81492c8e1ef33f7d9039093f5f96bedfb

--- @client/point.coffee
+++ @client/point.coffee
@@ -272,7 +272,7 @@

         DIV null,
           if permit('update point', point) > 0 &&
-              @props.rendered_as == 'decision_board_point' || TWO_COL()
+              (@props.rendered_as == 'decision_board_point' || TWO_COL())
             A
               style:
                 fontSize: if browser.is_mobile then 18 else 14
@@ -287,7 +287,7 @@
               SPAN null, 'edit'

           if permit('delete point', point) > 0 &&
-              @props.rendered_as == 'decision_board_point' || TWO_COL()
+              (@props.rendered_as == 'decision_board_point' || TWO_COL())
             A
               'data-action': 'delete-point'
               style:


Fix inbox entry not being cleared after submission

--- app/assets/javascripts/inbox.coffee
+++ app/assets/javascripts/inbox.coffee
@@ -130,7 +130,7 @@
       inbox: true
     success: (data, status, jqxhr) ->
       if data.success
-        $("div.inbox-box[data-id=#{iid}]").slideUp()
+        $("div.inbox-entry[data-id=#{iid}]").slideUp()
       showNotification data.message, data.success
     error: (jqxhr, status, error) ->
       console.log jqxhr, status, error
@@ -162,7 +162,7 @@
         id: iid
       success: (data, status, jqxhr) ->
         if data.success
-          $("div.inbox-box[data-id=#{iid}]").slideUp()
+          $("div.inbox-entry[data-id=#{iid}]").slideUp()
           if document.getElementById('ib-delete-all')?
             del_all_btn = ($ "button#ib-delete-all")
           else


Oscillator

Oscillator


Oscillator

Oscillator

--- test/oscillator.coffee
+++ test/oscillator.coffee
@@ -11,5 +11,6 @@
   o(7.5) #=> 10
   o(22.5) #=> -10

+
 # Clear out the module
 module()


Factor out common assertion

Factor out common assertion

--- test/rules_test.coffee
+++ test/rules_test.coffee
@@ -4,14 +4,13 @@
 require('../src/scripts/rules') helper

 tests = [
-  (helper) ->
-    helper.adapter.receive 'helper: the rules'
-    assert.ok helper.sent[0].match /1\. A robot may not .+/
+  'helper: the rules'
 ]

 until tests.length == 0
   helper.reset()
-  test = tests.shift()
-  test(helper)
+  msg = tests.shift()
+  helper.adapter.receive msg
+  assert.ok helper.sent[0].match /1\. A robot may not .+/

 helper.stop()


Make a FootballGame class so games can have methods

Having a way to determine if a game is final is useful

--- src/Lidsys/Application/Controller/assets/lidsys/football-models.js.coffee
+++ src/Lidsys/Application/Controller/assets/lidsys/football-models.js.coffee
@@ -90,12 +90,14 @@
         return @games[year][week] if @games[year]? and @games[year][week]?
         @$http.get("/api/v1.0/football/schedule/#{year}/#{week}")
             .success((response) =>
-                teams = @teamService.getTeams()
-                games = response.games
-                for own game_id, game of games
-                    game.away_team = teams[game.away_team_id]
-                    game.home_team = teams[game.home_team_id]
-                    game.isStarted = moment().isAfter(game.start_time)
+                teams      = @teamService.getTeams()
+                games_data = response.games
+                games      = {}
+                for own game_id, game_data of games_data
+                    game_data.away_team = teams[game_data.away_team_id]
+                    game_data.home_team = teams[game_data.home_team_id]
+                    game = new FootballGame game_data
+                    games[game_id] = game

                 @games[year]       = {}
                 @games[year][week] = games
@@ -135,6 +137,25 @@

         throw "Games not yet loaded using 'loadGames' for year #{year} week #{week_num}" if not @games[year]? or not @games[year][week_num]?
         @games[year][week_num]
+
+
+
+window.FootballGame = class FootballGame
+    constructor: (game_data) ->
+        @game_id      = game_data.game_id
+        @away_team_id = game_data.away_team_id
+        @home_team_id = game_data.home_team_id
+        @away_team    = game_data.away_team
+        @home_team    = game_data.home_team
+        @away_score   = game_data.away_score
+        @home_score   = game_data.home_score
+        @start_time   = game_data.start_time
+
+    isStarted: ->
+        moment().isAfter(@start_time)
+
+    isFinal: ->
+        @away_score != null || @home_score != null


旧方式兼容

旧方式兼容

--- lib/utils/chameleon-box-view.coffee
+++ lib/utils/chameleon-box-view.coffee
@@ -11,7 +11,8 @@
     @div class: 'chameleon', =>
       @h1 options.title or desc.headtitle, class: 'box-title', outlet: 'title'
       @span class: 'icon icon-remove-close close-view', outlet: 'closeBtn', click: 'onCloseClick'
-      @div class: 'box', outlet: 'contentBox'
+      @div class: 'box', outlet: 'contentBox', =>
+        @subview  'contentView', options.subview
       @div class: 'clearfix', =>
         @button desc.cancel, class: 'btn cancel pull-left', outlet: 'cancelBtn', click: 'onCancelClick'
         @button desc.next, class: 'btn next pull-right', outlet: 'nextBtn', click: 'onNextClick'


Ensure that hidden popover contents are properly displayed.

--- app/assets/javascripts/cartilage/views/popover_view.js.coffee
+++ app/assets/javascripts/cartilage/views/popover_view.js.coffee
@@ -118,6 +118,7 @@
     @attachedElement = element
     @position = (position ?= "top")
     ($ @el).css { visibility: "hidden" }
+    ($ @contentElement).children().css("display", "block")
     ($ document.body).append @render().el
     @calculatePosition()
     ($ @el).hide().css { visibility: "visible" }


Allow clicking on alt packageName to search for packageName

--- lib/package-card.coffee
+++ lib/package-card.coffee
@@ -64,6 +64,10 @@
     @updateEnablement()
     @loadCachedMetadata()

+    @packageMessage.on 'click', 'a', (e) ->
+      atom.workspace.open(href) if href = this.getAttribute('href')
+      false
+
     if atom.packages.isBundledPackage(@pack.name)
       @installButton.hide()
       @uninstallButton.hide()
@@ -123,7 +127,9 @@
         if alt is 'core'
           @packageMessage.html marked("The features in `#{pack.name}` have been added to core. Please disable or uninstall this package.")
         else
-          @packageMessage.html marked("`#{pack.name}` has been replaced by `#{alt}`. Please uninstall this package and install `#{alt}`.")
+          @packageMessage.html marked """
+            `#{pack.name}` has been replaced by `#{alt}`. Please uninstall this package and install [`#{alt}`](atom://config/install/package:#{alt}).
+          """

   handleControlsEvent: (opts) ->
     if opts?.onSettingsView


paths

paths

--- lib/scriptloader.coffee
+++ lib/scriptloader.coffee
@@ -7,16 +7,22 @@

   load: (paths...) ->
     for path in paths
-      do (path) ->
-        @log.info("Loading scripts from " + path);
-        path = Path.resolve path
-        stats = Fs.statSync(path)
-        if stats.isFile()
-          @loadFile Path.resolve path
-        else
-          if Fs.existsSync(path)
-            for file in Fs.readdirSync(path).sort()
-              @loadFile Path.join path, file
+      if Array.isArray(path)
+        for p in path
+          @loadPath(p)
+      else
+        @loadPath(path)
+
+  loadPath: (path) ->
+    @log.info("Loading scripts from " + path);
+    path = Path.resolve path
+    stats = Fs.statSync(path)
+    if stats.isFile()
+      @loadFile Path.resolve path
+    else
+      if Fs.existsSync(path)
+        for file in Fs.readdirSync(path).sort()
+          @loadFile Path.join path, file

   loadFile: (file) ->
     ext = Path.extname file


make sliders lose focus

make sliders lose focus

--- core-coffee/forceSliders.coffee
+++ core-coffee/forceSliders.coffee
@@ -37,6 +37,7 @@
         force = @force # sorry - need this to be a DOM element below
         @$(mapping.selector).val(mapping.scale(mapping.f())).change ->
           mapping.f mapping.scale.invert($(this).val()) # <== here
+          $(this).blur()
           force.start()

       return this


Delay graph initialization until we have data

--- widgets/bar_graph/bar_graph.coffee
+++ widgets/bar_graph/bar_graph.coffee
@@ -2,37 +2,38 @@
   @accessor 'current', ->

   ready: ->
-    container = $(@node).parent()
-    # Gross hacks. Let's fix this.
-    width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
-    height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
+    @container = $(@node).parent()
+    this._initializeGraph(@get('items')) if @get('items')

-    @names = @get('items')
-    data = this._data(@get('items'))
+  onData: (data) ->
+    if data.items && @graph
+      $(data.items).each (i, item) =>
+        @graph.series[0].data[i].y = item.count
+      @graph.render()
+    else if data.items && !@graph
+      this._initializeGraph(data.items)
+
+  _initializeGraph: (items) =>
+    @names = items
+    data = this._data(items)

     @graph = new Rickshaw.Graph(
       element: @node
       renderer: 'bar'
-      width: width
-      height: height
+      width: @_width()
+      height: @_height()
       series: [data]
     )

-    x_axis = new Rickshaw.Graph.Axis.X(
-      graph: @graph
-      orientation: 'bottom'
-      tickFormat: this._format
-    )
+    # x_axis = new Rickshaw.Graph.Axis.X(
+    #   graph: @graph
+    #   orientation: 'bottom'
+    #   tickFormat: this._format
+    # )

     @graph.renderer.unstack = true

     @graph.render()
-
-  onData: (data) ->
-    if data.items
-      $(data.items).each (i, item) =>
-        @graph.series[0].data[i].y = item.count
-      @graph.render()

   _data: (items) ->
     color: '#fff'
@@ -42,3 +43,9 @@
   _format: (n) =>
     console.debug n
     @names[n].name
+
+  _width: =>
+    (Dashing.widget_base_dimensions[0] * @container.data("sizex")) +
+     Dashing.widget_margins[0] * 2 * (@container.data("sizex") - 1)
+  _height: =>
+    (Dashing.widget_base_dimensions[1] * @container.data("sizey"))


Extend users table redefine to include hyperlink to organization

--- app/assets/javascripts/users.coffee
+++ app/assets/javascripts/users.coffee
@@ -22,11 +22,11 @@

-
+

"""
       $newRow = $(row)
-      $newRow.find("td:eq(4)").text(organization.name)
+      $newRow.find("td:eq(4) a").text(organization.name).attr("href", organization.href)
       $newRow.find("td:eq(5)").text(organization.role)
       $newRow.attr("data-href", $row.attr("data-href"))
       $rows = $rows.add($newRow)


Add group by helper.

Add group by helper.

--- app/coffee/utils.coffee
+++ app/coffee/utils.coffee
@@ -50,8 +50,16 @@
     element.data('nextTextPosition', nextTextPosition + 1)
     element.text(text)

+groupBy = (coll, pred) ->
+    result = {}
+    for item in coll
+        result[pred(item)] = item
+
+    return result
+
 taiga = @.taiga
 taiga.bindOnce = bindOnce
 taiga.mixOf = mixOf
 taiga.trim = trim
 taiga.toggleText = toggleText
+taiga.groupBy = groupBy


Add cache=true to some almost readonly resources.

--- app/coffee/services/resource.coffee
+++ app/coffee/services/resource.coffee
@@ -252,7 +252,7 @@

     # Get a project
     service.getProject = (projectId) ->
-        return queryOne("projects", projectId)
+        return queryOne("projects", projectId, {cache:true})

     # Get a project stats
     service.getProjectStats = (projectId) ->
@@ -276,9 +276,9 @@

     # Get roles
     service.getRoles = (projectId) ->
-        return queryMany('roles', {project: projectId})
-
-    # Get roles
+        return queryMany('roles', {project: projectId}, {cache:true})
+
+    # Create role
     service.createRole = (projectId, role) ->
         role.project = projectId
         return $model.create("roles", role)
@@ -403,11 +403,13 @@
     service.search = (projectId, term, getAll) ->
         defered = $q.defer()

-        params =
+        params = {
             "method": "GET"
             "headers": headers()
             "url": $gmUrls.api("search")
             "params": {"project": projectId, "text": term, "get_all": getAll or false}
+            "cache": true
+        }

         promise = $http(params)
         promise.success (data, status) ->
@@ -813,7 +815,7 @@

     service.getIssueTypes = (projectId, filters={}) ->
         parameters = _.extend({}, filters, {project:projectId})
-        return queryMany("choices/issue-types", parameters)
+        return queryMany("choices/issue-types", parameters, {cache:true})

     service.createIssueType = (form) ->
         return $model.create("choices/issue-types", form)
@@ -827,7 +829,7 @@

     service.getPriorities = (projectId, filters={}) ->
         parameters = _.extend({}, filters, {project:projectId})
-        return queryMany("choices/priorities", parameters)
+        return queryMany("choices/priorities", parameters, {cache:true})

     service.createPriority = (form) ->
         return $model.create("choices/priorities", form)
@@ -855,6 +857,11 @@

     return service

+
+init = ($cacheFactory, $http) ->
+    $http.defaults.cache = $cacheFactory("httpCache", 512);
+
 module = angular.module('taiga.services.resource', ['taiga.config'])
+module.run(["$cacheFactory", "$http", init])
 module.factory('resource', ['$http', '$q', '$gmStorage', '$gmUrls', '$model', 'config', '$rootScope',
                             '$i18next', '$filter', '$log', ResourceProvider])


let vision never utterly replace hearing; only plants can scatter their babies away from themselves

--- evolution.coffee
+++ evolution.coffee
@@ -604,22 +604,11 @@
       babyCost: [ 1, 1, (t) -> t.health() / 2 ]
       babyThreshold: [ .5, .1, .9 ]
       babyTries: [ 2, 1, (t) -> Math.min( 100, t.babyTries() * 2 ) ]
-      dispersalRadius: [
-        => @radius * @dispersalStart()
-        (t) -> t.radius * t.dispersalMin()
-        (t) -> t.radius * t.dispersalMax()
-      ]
       mutationRate: [ .1, 0.01, 1 ]
       mutationRange: [ .1, 0.01, 1 ]
     }
-  # some things to control how far away from its mother a baby can be "born"
-  dispersalStart: -> 5
-  dispersalMin: -> 3
-  dispersalMax: -> 20
-  dispersalRadius: ->
-    dr = @genes.dispersalRadius[0]
-    dr = @genes.dispersalRadius[0] = dr() if typeof dr == 'function'
-    dr
+  # how far away from its mother a baby can be "born"
+  dispersalRadius: -> 3 * @radius
   # utility function for extending default genes
   mergeGenes: ( base, ext ) ->
     genes = {}
@@ -730,7 +719,18 @@
     @radius = options.radius || 4
     @type = Plant
   gain: -> .5
-  dispersalStart: -> 15
+  defaultGenes: ->
+    @mergeGenes super(), {
+      dispersalRadius: [
+        => @radius * 15
+        (t) -> t.radius * 3
+        (t) -> t.radius * 20
+      ]
+    }
+  dispersalRadius: ->
+    dr = @genes.dispersalRadius[0]
+    dr = @genes.dispersalRadius[0] = dr() if typeof dr == 'function'
+    dr
 class Animal extends Organism
   constructor: ( location, options = {} ) ->
     super location, options
@@ -743,7 +743,7 @@
   defaultGenes: ->
     @mergeGenes super(), {
       auditoryRange: [ Math.min( @universe.maxDistance / 3, 20 ), 10, @universe.maxDistance / 2 ]
-      visualAngle: [ .45, .1, 1 ]
+      visualAngle: [ .45, .1, .8 ]
       visualRange: [ Math.min( @universe.maxDistance / 2 , 30 ), 20, @universe.maxDistance ]
       g: [ 250, 1, 5000 ]
       jitter: [ .05, 0.01, 1 ]


Update pt-BR.coffee

Update pt-BR.coffee

Some translations for pt-br

--- app/locale/pt-BR.coffee
+++ app/locale/pt-BR.coffee
@@ -302,10 +302,10 @@
     teacher_additional_questions: "Isso é tudo! Se você precisar de ajuda adicional ou tiver perguntas, encontre-nos em __supportEmail__."
     dont_use_our_email_silly: "Não coloque o nosso email aqui! Use o email do seus pais ou responsável."
     want_codecombat_in_school: "Quer jogar CodeCombat todo o tempo?"
-#    eu_confirmation: "I agree to allow CodeCombat to store my data on US servers."
-#    eu_confirmation_place_of_processing: "Learn more about the possible risks"
-#    eu_confirmation_student: "If you are not sure, ask your teacher."
-#    eu_confirmation_individual: "If you do not want us to store your data on US servers, you can always keep playing anonymously without saving your code."
+    eu_confirmation: "Eu estou de acordo em permitir que o CodeCombat armazene os meus dados em servidores dos Estados Unidos."
+    eu_confirmation_place_of_processing: "Sabe mais possíveis riscos"
+    eu_confirmation_student: "Se você não tem certeza, pergunte ao seu professor."
+    eu_confirmation_individual: "Se você não deseja que armazenemos seus dados em servidores dos Estados Unidos, você sempre poderá jogar anônimamente sem salvar seu código"

   recover:
     recover_account_title: "Recuperar Conta"
@@ -2121,10 +2121,10 @@
     verifying_email: "Verificando seu endereço de email..."
     successfully_verified: "Seu endereço de email foi verificado com sucesso!"
     verify_error: "Algo deu errado ao verificar seu e-mail :("
-#    unsubscribe_from_marketing: "Unsubscribe __email__ from all CodeCombat marketing emails?"
+    unsubscribe_from_marketing: "Cancelar a inscrição de e-mails de marketing do CodeCombat para __email__ ?"
     unsubscribe_button: "sim, cancelar inscrição"
     unsubscribe_failed: "Falha"
-#    unsubscribe_success: "Success"
+    unsubscribe_success: "Sucesso"

   account_invoices:
     amount: "Valor em dólares americanos"


Tweaked when and how we shift the bounds of imported sprites. For things like Anya, especially Anya's attack, it wasn't working correctly.

--- app/lib/sprites/SpriteParser.coffee
+++ app/lib/sprites/SpriteParser.coffee
@@ -63,7 +63,13 @@
             instructions.push {t: c.t, gn: c.gn}
             break
       @addContainer {c: instructions, b: container.bounds}, container.name
-    for movieClip in movieClips
+    for movieClip, index in movieClips
+      if index is 0
+        for bounds in movieClip.frameBounds
+          bounds[0] -= @width / 2
+          bounds[1] -= @height / 2
+        movieClip.bounds[0] -= @width / 2
+        movieClip.bounds[1] -= @height / 2
       localGraphics = @getGraphicsFromBlock(movieClip, source)
       [shapeKeys, localShapes] = @getShapesFromBlock movieClip, source
       localContainers = @getContainersFromMovieClip movieClip, source, true
@@ -174,7 +180,7 @@
             frameBoundsSource = @subSourceFromRange frameBoundsRange, source
             if frameBoundsSource.search(/\[rect/) is -1  # some other statement; we don't have multiframe bounds
               console.log 'Didn\'t have multiframe bounds for this movie clip.'
-              frameBounds = [nominalBounds]
+              frameBounds = [_.clone(nominalBounds)]
             else
               lastRect = nominalBounds
               frameBounds = []
@@ -192,12 +198,7 @@
                   bounds = [0, 0, 1, 1]  # Let's try this.
                 frameBounds.push _.clone bounds
           else
-            frameBounds = [nominalBounds]
-
-          # Subtract half of width/height parsed from lib.properties
-          for bounds in frameBounds
-            bounds[0] -= @width / 2
-            bounds[1] -= @height / 2
+            frameBounds = [_.clone(nominalBounds)]

           functionExpressions.push {name: name, bounds: nominalBounds, frameBounds: frameBounds, expression: node.parent.parent, kind: kind}
     @walk ast, null, gatherFunctionExpressions


try to fixup -pnpres authorization

try to fixup -pnpres authorization

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -36,10 +36,10 @@
       if $scope.data.super
         ### Grant access to the SuperHeroes room for supers only! ###
         PubNub.ngGrant({channel:'SuperHeroes',auth_key:$rootScope.authKey,read:true,write:true,callback:->console.log('SuperHeroes! all set', arguments)})
-        PubNub.ngGrant({channel:"SuperHeroes-pnpres",auth_key:$rootScope.authKey,read:true,write:true,callback:->console.log('SuperHeroes! presence all set', arguments)})
+        PubNub.ngGrant({channel:"SuperHeroes-pnpres",auth_key:$rootScope.authKey,read:true,write:false,callback:->console.log('SuperHeroes! presence all set', arguments)})
         # Let everyone see the control channel so they can retrieve the rooms list
         PubNub.ngGrant({channel:'__controlchannel',read:true,write:true,callback:->console.log('control channel all set', arguments)})
-        PubNub.ngGrant({channel:'__controlchannel-pnpres',read:true,write:true,callback:->console.log('control channel presence all set', arguments)})
+        PubNub.ngGrant({channel:'__controlchannel-pnpres',read:true,write:false,callback:->console.log('control channel presence all set', arguments)})

       $location.path '/chat'

@@ -74,7 +74,7 @@

       # grant anonymous access to channel and presence
       PubNub.ngGrant({channel:channel,read:true,write:true,callback:->console.log("#{channel} all set", arguments)})
-      PubNub.ngGrant({channel:"#{channel}-pnpres",read:true,write:true,callback:->console.log("#{channel} presence all set", arguments)})
+      PubNub.ngGrant({channel:"#{channel}-pnpres",read:true,write:false,callback:->console.log("#{channel} presence all set", arguments)})

       # publish the channel creation message to the control channel
       PubNub.ngPublish { channel: $scope.controlChannel, message: channel }


Commit when we hit enter.

Commit when we hit enter.

--- keymaps/git.cson
+++ keymaps/git.cson
@@ -26,3 +26,4 @@

 '.git-commit-message-view .commit-button':
   'tab': 'git:focus-file-list'
+  'enter': 'git:commit'


Client: Activity: Updated modal props

Client: Activity: Updated modal props

--- client/app/lib/components/sidebarchannelssection/index.coffee
+++ client/app/lib/components/sidebarchannelssection/index.coffee
@@ -1,11 +1,12 @@
-React                   = require 'kd-react'
-immutable               = require 'immutable'
-SidebarList             = require 'app/components/sidebarlist'
-SidebarSection          = require 'app/components/sidebarsection'
-Modal                   = require 'app/components/modal'
-SidebarModalList        = require 'activity/components/sidebarmodallist'
-PublicChannelListItem   = require 'activity/components/publicchannellistitem'
-SidebarChannelsListItem = require 'app/components/sidebarchannelslistitem'
+React                    = require 'kd-react'
+immutable                = require 'immutable'
+SidebarList              = require 'app/components/sidebarlist'
+SidebarSection           = require 'app/components/sidebarsection'
+Modal                    = require 'app/components/modal'
+SidebarModalList         = require 'activity/components/sidebarmodallist'
+PublicChannelListItem    = require 'activity/components/publicchannellistitem'
+SidebarChannelsListItem  = require 'app/components/sidebarchannelslistitem'
+CreatePublicChannelModal = require 'activity/components/createpublicchannelmodal'

 module.exports = class SidebarChannelsSection extends React.Component

@@ -37,9 +38,9 @@

   getCreateChannelModalProps: ->

-    title              : 'Create a channel'
-    className          : 'CreateChannel-Modal'
-    buttonConfirmTitle : 'Create Channel'
+    title               : 'Create Channel'
+    className           : 'CreateChannel-Modal'
+    buttonConfirmTitle  : 'CREATE'

   renderFollowedChannelsModal: ->
@@ -69,6 +70,7 @@
     <div>
       <SidebarSection
         title="Channels"
+        itemComponent={CreatePublicChannelModal}
         modalProps={@getCreateChannelModalProps()}
         onHeaderClick={@bound 'showFollowedPublicChannelsModal'}
         className="SidebarChannelsSection">


security: fix an xss injection hazard in filetree

--- client/landing/app/MainApp/filetree/itemviews/fileitem.coffee
+++ client/landing/app/MainApp/filetree/itemviews/fileitem.coffee
@@ -77,13 +77,13 @@

   pistachio:->
-
-    path = FSHelper.plainPath @getData().path
-
+    data = @getData()
+    path = FSHelper.plainPath data.path
+    name = Encoder.XSSEncode data.name
     """
       {{> @arrow}}
       {{> @icon}}
       {{> @loader}}
-      {span.title[title="#{path}"]{ #(name)}}
+      #{name}

     """


bind with server errors

bind with server errors

--- src/main/resources/javascript/app/main_controller.coffee
+++ src/main/resources/javascript/app/main_controller.coffee
@@ -83,6 +83,8 @@
               # see WSController
               #Sources.add(new Source(json))
               modal.hide()
+            error: (err) ->
+              source.set_error("url.url_validator", err.responseText)

       Templates.modal_view.on 'button.close-modal', 'click', (e) ->
         modal.hide()


update test case names

update test case names

--- workers/social/lib/social/models/computeproviders/provisioner.test.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.test.coffee
@@ -113,7 +113,7 @@
       expectAccessDenied JProvisioner, 'some$', selector = {}, options = {}, done

-  it 'should be able to fetch provisioner data', (done) ->
+  it 'should be able to fetch provisioner data with valid request', (done) ->

     withConvertedUserAndProvisioner ({ client, provisioner }) ->
       selector = { slug : provisioner.slug }
@@ -131,7 +131,7 @@
       expectAccessDenied JProvisioner, 'one$', selector = {}, options = {}, done

-  it 'should be able to fetch provisioner data', (done) ->
+  it 'should be able to fetch provisioner data with valid request', (done) ->

     withConvertedUserAndProvisioner ({ client, provisioner }) ->
       selector = { slug : provisioner.slug }
@@ -221,7 +221,7 @@
         expectAccessDenied provisioner, 'update$', data = {}, done

-  it 'should be able to update provisioner data', (done) ->
+  it 'should be able to update provisioner data with valid request', (done) ->

     withConvertedUserAndProvisioner ({ client, account, provisioner }) ->


Given_, When_, Then_ & Step removed.

Given_, When_, Then_ & Step removed.

--- cakes.coffee
+++ cakes.coffee
@@ -126,30 +126,14 @@
 exports.Then = ->
 	gwt " Then:".yellow+" %s", arguments

-exports.Given_ = ->
-	dic 'describe', " Given:".yellow+" %s", arguments, padding:true
-
-exports.When_ = ->
-	dic 'describe', "  When:".yellow+" %s", arguments, padding:true
-
-exports.Then_ = ->
-	dic 'describe', "  Then:".yellow+" %s", arguments, padding:true
-
-
 exports.And = ->
 	gwt "  And".grey+"  %s", arguments, padding_color:'black'

 exports.But = ->
 	gwt "  But".grey+"  %s", arguments, padding_color:'black'

-
-exports.Step = ->
-	dic 'it', '%s', arguments, pending:true
-
-
 exports.Describe = exports.Spec = ->  # describe() start of spec file
 	dic 'describe', '=== %s ==='.blue, arguments

-
 # Add function names to global scope.
 (global[name] = func for name, func of module.exports)


fix spec

fix spec

--- spec/validators_spec.coffee
+++ spec/validators_spec.coffee
@@ -264,14 +264,14 @@
     it "adds a validation that adds no errors if the attribute is not present", ->
       v = new @V.ConfirmationValidator "foo"
       spyOn @model, "addError"
-      spyOn(@model, "get").and.callFake ( attr ) -> if attr is "foo" then "something" else undefined
-      v.validate @model
-      expect( @model.addError ).not.toHaveBeenCalled()
-
-    it "adds a validation that adds no errors if the attribute is empty", ->
-      v = new @V.ConfirmationValidator "foo"
-      spyOn @model, "addError"
-      spyOn(@model, "get").and.callFake ( attr ) -> if attr is "foo" then "something" else ""
+      spyOn(@model, "get").and.callFake ( attr ) -> if attr is "foo" then undefined else "something"
+      v.validate @model
+      expect( @model.addError ).not.toHaveBeenCalled()
+
+    it "adds a validation that adds no errors if the attribute is empty", ->
+      v = new @V.ConfirmationValidator "foo"
+      spyOn @model, "addError"
+      spyOn(@model, "get").and.callFake ( attr ) -> if attr is "foo" then "" else "something"
       v.validate @model
       expect( @model.addError ).not.toHaveBeenCalled()


Exit url fix

Exit url fix

--- app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
+++ app/assets/javascripts/ckeditor/plugins/effective_regions/plugin.js.coffee
@@ -63,6 +63,7 @@
 Exit = {
   exec: (editor) ->
     url = $.cookie('effective_regions_editting')
+    url ||= window.location.href.replace('?edit=true', '')

     if url != undefined && url.length > 0
       window.location = url


Reinitialize `page` params when changing order cycle

--- app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee
@@ -22,6 +22,7 @@
   $rootScope.$on "orderCycleSelected", ->
     $scope.update_filters()
     $scope.clearAll()
+    $scope.page = 1

   $scope.update_filters = ->
     order_cycle_id = OrderCycle.order_cycle.order_cycle_id


Fixed error handling for shippingRateSelection

--- app/assets/javascripts/sprangular/directives/shippingRatesSelection.coffee
+++ app/assets/javascripts/sprangular/directives/shippingRatesSelection.coffee
@@ -27,7 +27,7 @@
         address = order.actualShippingAddress()

         Cart.shippingRates({countryId: address.countryId, stateId: address.stateId, zipcode: address.zipcode})
-            .then (results) ->
+            .then ((results) ->
               $scope.rates = results

               order.shipping_method_id = null unless _.find(results, (rate) -> rate.shippingMethodId == order.shipping_method_id)
@@ -35,7 +35,7 @@
               if order.shipping_method_id == null && results.length > 0
                 order.shipping_method_id = results[0].shippingMethodId

-              $scope.loading = false
-            .error ->
+              $scope.loading = false), (->
+
               $scope.rates = []
-              $scope.loading = false
+              $scope.loading = false)


Fixed bundle generator.

Fixed bundle generator.

--- gulp/lib/bundle-generator.coffee
+++ gulp/lib/bundle-generator.coffee
@@ -55,6 +55,7 @@
         #{MODULE_FUNCTIONS}["#{modulePath}"] = function() {
             var require = #{CREATE_REQUIRE}("#{basePath}");
             var module = { exports: {} };
+            var exports = module.exports;

             #{moduleContents}