Add model building and inspection test cases for k-means

--- h2o-web/src/main/steam/scripts/h2o-proxy.tests.coffee
+++ h2o-web/src/main/steam/scripts/h2o-proxy.tests.coffee
@@ -30,7 +30,7 @@
     async.waterfall operations, -> t.end(); go()

 test 'airlines ingest and model building flow', (t) ->
-  t.plan 30
+  t.plan 35

   createCloud (_, go) ->
     ensureNoFramesExist = (go) ->
@@ -131,7 +131,7 @@
             t.fail 'job poll failed'
             go error
           else
-            if job.progress < 1 or job.status is 'CREATED' or job.status is 'RUNNING'
+            if job.status is 'CREATED' or job.status is 'RUNNING'
               delay poll, 1000, go
             else
               go null, job
@@ -140,7 +140,6 @@
         if error
           go error
         else
-          t.equal job.progress, 1, 'job progress ok'
           t.equal job.status, 'DONE', 'job status ok'
           go null, job.dest.name

@@ -148,10 +147,10 @@
     inspectFrame = (frameKey, go) ->
       _.requestInspect frameKey, (error, result) ->
         if error
-          t.fail 'inspect request failed'
-          go error
-        else
-          t.pass 'got inspect reply'
+          t.fail 'frame inspect request failed'
+          go error
+        else
+          t.pass 'got frame inspect reply'
           tdiff t, (readGoldJson 'inspect-allyears2k_headers-zip.json'), result
           go null, frameKey

@@ -174,7 +173,30 @@
         else
           t.pass 'got model builders reply'
           tdiff t, (readGoldJson 'model-builders-kmeans.json'), result, exclude: [ 'model_builders.kmeans.job' ]
-          go null, frameKey, result
+          go null, frameKey
+
+    buildAirlinesKmeansModel = (frameKey, go) ->
+      parameters = src: frameKey, K: 2
+      _.requestModelBuild 'kmeans', parameters, (error, result) ->
+        if error
+          t.fail 'model build request failed'
+          go error
+        else
+          t.pass 'got model build reply'
+          tdiff t, (readGoldJson 'kmeans-allyears2k_headers-zip.json'), result, include: [ 'jobs.#.description', 'jobs.#.dest', 'jobs.#.exception' ]
+          t.ok isString result.key.name, 'has job name'
+          go null, result.key.name
+
+    inspectModel = (modelKey, go) ->
+      _.requestInspect modelKey, (error, result) ->
+        if error
+          t.fail 'model inspect request failed'
+          go error
+        else
+          t.pass 'got model inspect reply'
+          result.schema.parameters[3].actual_value = "(random)"
+          tdiff t, (readGoldJson 'inspect-kmeans-allyears2k_headers-zip.json'), result, exclude: [ 'schema.output.clusters', 'schema.output.rows', 'schema.output.mses', 'schema.output.mse', 'schema.output.iters' ]
+          go null, modelKey

     operations = [
       ensureNoFramesExist
@@ -188,6 +210,9 @@
       inspectFrame
       getFrame
       getKmeansModelBuilder
+      buildAirlinesKmeansModel
+      pollJob
+      inspectModel
     ]
     async.waterfall operations, -> t.end(); go()


Messages#show - JSON highlighting now takes effect on initial page load

--- app/assets/javascripts/admin/routers/messages.js.coffee
+++ app/assets/javascripts/admin/routers/messages.js.coffee
@@ -7,5 +7,6 @@
     message.fetch().done ->
       view = new Augury.Views.Messages.Show(model: message)
       $('#integration_main').html view.render().el
+      SyntaxHighlighter.highlight()
 )


Fixed account show controller to use StateParams

--- app/assets/javascripts/controllers/accounts/show.coffee
+++ app/assets/javascripts/controllers/accounts/show.coffee
@@ -1,9 +1,9 @@
-angular.module("coledger").controller("AccountsShowController", ['$scope', '$location', '$routeParams', 'Resources', 'flash'
-  ($scope, $location, $routeParams, Resources, flash) ->
+angular.module("coledger").controller("AccountsShowController", ['$scope', '$location', '$stateParams', 'Resources', 'flash'
+  ($scope, $location, $stateParams, Resources, flash) ->
     $scope.account = {}
-    $scope.project = Resources.Project.get(id: $routeParams.project_id)
+    $scope.project = Resources.Project.get(id: $stateParams.project_id)
     $scope.refreshAccount = ->
-      $scope.accountPromise = Resources.Account.get(project_id: $routeParams.project_id, id: $routeParams.id).$promise
+      $scope.accountPromise = Resources.Account.get(project_id: $stateParams.project_id, id: $stateParams.id).$promise
       $scope.accountPromise.then (data) ->
         $scope.account = data


Focus form field on button click.

Focus form field on button click.

--- app/assets/javascripts/repository_directories.js.coffee
+++ app/assets/javascripts/repository_directories.js.coffee
@@ -18,6 +18,7 @@
     setup_form_clone = (clone_el) ->
       setup_form_submission_handling(clone_el)
       animateIn(clone_el)
+      $('input[name="repository_directory[name]"]', clone_el).focus()

     remove_form_clone = (form_el, callback) ->
       animateOut(form_el, () ->


Added Address.shortAddress()

Added Address.shortAddress()

--- app/assets/javascripts/sprangular/models/address.coffee
+++ app/assets/javascripts/sprangular/models/address.coffee
@@ -17,6 +17,9 @@

   fullName: ->
     "#{@firstname} #{@lastname}"
+
+  shortAddress: ->
+    "#{@fullName()}, #{@addressLine()}, #{@city} #{@state.abbr}, #{@zipcode}"

   serialize: ->
     id: @id


Added Variant.isAvailable()

Added Variant.isAvailable()

--- app/assets/javascripts/sprangular/models/variant.coffee
+++ app/assets/javascripts/sprangular/models/variant.coffee
@@ -9,3 +9,6 @@
       extra: image.extra_url

     @image = @images[0]
+
+  isAvailable: ->
+    !@track_inventory || @in_stock


Use more generic core.excludeVcsIgnoredPaths config key

--- lib/load-paths-task.coffee
+++ lib/load-paths-task.coffee
@@ -12,7 +12,7 @@

     command = require.resolve 'nak'
     args = ['--list', rootPath]
-    args.unshift('--addVCSIgnores') if config.get('fuzzyFinder.hideVcsIgnoredPaths')
+    args.unshift('--addVCSIgnores') if config.get('core.excludeVcsIgnoredPaths')
     args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0
     args.unshift('--follow')


Fix for game root launch

Fix for game root launch

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -700,7 +700,7 @@
   "Ren'Py":
     "File Based":
       command: "renpy"
-      args: (context) -> [context.filepath.substr(0, context.filepath.lastIndexOf("/"))]
+      args: (context) -> [context.filepath.substr(0, context.filepath.lastIndexOf("game"))]

   RSpec:
     "Selection Based":


Revert back to lastIndexOf

Revert back to lastIndexOf

Likelihood of "game" in path prior to Ren'Py project root greater than having a second "game" folder inside the Ren'Py project.

--- lib/grammars.coffee
+++ lib/grammars.coffee
@@ -700,7 +700,7 @@
   "Ren'Py":
     "File Based":
       command: "renpy"
-      args: (context) -> [context.filepath.substr(0, context.filepath.indexOf("/game"))]
+      args: (context) -> [context.filepath.substr(0, context.filepath.lastIndexOf("/game"))]

   RSpec:
     "Selection Based":


group-setup-page form submit saves group, refetches memberships, and redirects to new group's page

--- app/components/group-setup-page/group-setup-page.coffee
+++ app/components/group-setup-page/group-setup-page.coffee
@@ -1,4 +1,3 @@
-# (EL) NOTE: may want to use this as an 'edit group' page in the future
 # http://i.imgur.com/RMs4njZ.gifv

 module.exports =
@@ -9,9 +8,11 @@
       global.cobudgetApp.membershipsLoaded
   url: '/setup_group'
   template: require('./group-setup-page.html')
-  controller: ($location, $scope) ->
+  controller: ($location, Records, $scope) ->

     $scope.createGroup = (formData) ->
-      $scope.group.name = formData.name
-      $scope.group.save().then ->
-        $location.path("/groups/#{$scope.group.id}")
+      Records.groups.build(name: formData.name).save().then ->
+        Records.memberships.fetchMyMemberships().then (data) ->
+          newGroup = _.find data.groups, (group) ->
+            group.name == formData.name
+          $location.path("/groups/#{newGroup.id}")


Capture stdout/stderr during uninstall

Capture stdout/stderr during uninstall

--- lib/package-manager.coffee
+++ lib/package-manager.coffee
@@ -87,6 +87,6 @@
       error.stderr = errorLines.join('\n')
       callback(error)

-  new BufferedNodeProcess({command, args, exit})
+  new BufferedNodeProcess({command, args, stdout, stderr, exit})

 module.exports = {renderMarkdownInMetadata, install, uninstall, getAvailable}


Document resin/auth

Document resin/auth

--- lib/resin/auth/auth.coffee
+++ lib/resin/auth/auth.coffee
@@ -6,10 +6,42 @@
 errors = require('../errors/errors')
 settings = require('../settings')

+# Authenticate with the server
+#
+# @private
+#
+# @param {Object} credentials in the form of username, password
+# @option credentials {String} username the username
+# @option credentials {String} password user password
+# @param {Function} callback (error, token)
+#
+# @note You should use login() when possible, as it takes care of saving the token as well.
+#
+# @example Authenticate
+#		resin.auth.authenticate credentials, (error, token) ->
+#			throw error if error?
+#			console.log(token)
+#
 exports.authenticate = (credentials, callback) ->
 	server.post settings.get('urls.authenticate'), credentials, (error, response) ->
 		return callback(error, response?.body)

+# Login to Resin.io
+#
+# Is the login is successful, the token is persisted between sessions.
+#
+# @param {Object} credentials in the form of username, password
+# @option credentials {String} username the username
+# @option credentials {String} password user password
+# @param {Function} callback (error)
+#
+# @note This function saves the token to the directory configured in dataPrefix
+#
+# @example Login to Resin.io
+#		resin.auth.login credentials, (error) ->
+#			throw error if error?
+#			console.log('I\'m logged in!')
+#
 exports.login = (credentials, callback) ->
 	async.waterfall([

@@ -21,14 +53,61 @@

 	], callback)

-# Handy aliases
-exports.isLoggedIn = token.hasToken
-exports.getToken = token.getToken
+# Check if you're logged in
+#
+# @param {Function} callback (isLoggedIn)
+#
+# @example Check if logged in
+#		resin.auth.isLoggedIn (isLoggedIn) ->
+#			if isLoggedIn
+#				console.log('I\'m in!')
+#			else
+#				console.log('Too bad!')
+#
+exports.isLoggedIn = (callback) ->
+	token.hasToken(callback)

-# TODO: Maybe we should post to /logout or something
-# like that to invalidate the token on the server?
-exports.logout = token.clearToken
+# Get current logged in user's token
+#
+# @param {Function} callback (error, isLoggedIn)
+#
+# @note This function simply delegates to resin.token.getToken() for convenience
+#
+# @example Get curren token
+#		resin.auth.getToken (error, token) ->
+#			throw error if error?
+#			console.log(token)
+#
+exports.getToken = (callback) ->
+	token.getToken(callback)

+# Logout from Resin.io
+#
+# @param {Function} callback (error)
+#
+# @example Logout from Resin.io
+#		resin.auth.logout (error) ->
+#			throw error if error?
+#			console.log('I\'m out!')
+#
+# @todo Maybe we should post to /logout or something to invalidate the token on the server?
+#
+exports.logout = (callback) ->
+	token.clearToken(callback)
+
+# Parse colon separated credentials
+#
+# @private
+#
+# @param {String} colon separated credentials (username:password)
+# @param {Function} callback (error, credentials)
+#
+#	@example Parse credentials
+#		resin.auth.parseCredentials 'johndoe:secret', (error, credentials) ->
+#			throw error if error?
+#			console.log(credentials.username)
+#			console.log(credentials.password)
+#
 exports.parseCredentials = (credentials, callback) ->
 	result = credentials.split(':')


#2 Fixing getHgWorkingCopyRevision() method.

--- lib/hg-utils.coffee
+++ lib/hg-utils.coffee
@@ -288,8 +288,7 @@
   # Returns a {String} with the current working copy revision
   getHgWorkingCopyRevision: () ->
     try
-      revisions = @hgCommand(['id', '-i', @rootPath])
-      return revisions.split(':')[1]
+      return @hgCommand(['id', '-i', @rootPath])
     catch error
       @handleHgError(error)
       return null


comment form submit event bus for now

comment form submit event bus for now

--- client/shared/helpers/form.coffee
+++ client/shared/helpers/form.coffee
@@ -108,9 +108,9 @@

 submit = (scope, model, options = {}) ->
   # fetch draft from server and listen for changes to it
-  if model.hasDrafts and model.isNew() and AbilityService.isLoggedIn()
-    model.fetchAndRestoreDraft()
-    EventBus.watch scope, model.draftFields, model.planDraftFetch, true
+  # if model.hasDrafts and model.isNew() and AbilityService.isLoggedIn()
+  #   model.fetchAndRestoreDraft()
+  #   EventBus.watch scope, model.draftFields, model.planDraftFetch, true

   submitFn  = options.submitFn  or model.save
   confirmFn = options.confirmFn or (-> false)


console debug toolbar

console debug toolbar

--- lib/runtime/console.coffee
+++ lib/runtime/console.coffee
@@ -5,6 +5,7 @@
 {client} = require '../connection'

 modules = require './modules'
+debug = require './debugger'

 {evalrepl, clearLazy} = client.import rpc: ['evalrepl'], msg: ['clearLazy']

@@ -52,6 +53,7 @@
     atom.views.getView(@c).classList.add 'julia'
     history.read().then (entries) =>
       @c.history.set entries
+    @c.toolbar = @toolbar

   ignored: [/^WARNING: Method definition .* overwritten/]
   ignore: (s) ->
@@ -95,6 +97,16 @@
     {name: 'shell', prefix: ';', icon: 'terminal', grammar: 'source.shell'}
   ]

+  toolbar: [{
+      type: 'group'
+      children: [
+        {icon: 'link-external', alt: 'Debug: Finish Function', onclick: ()->debug.finish()}
+        {icon: 'arrow-down', alt: 'Debug: Next Line', onclick: ()->debug.nextline()}
+        {icon: 'triangle-right', alt: 'Debug: Next Expression', onclick: ()->debug.stepexpr()}
+        {icon: 'sign-in', alt: 'Debug: Step into Function', onclick: () -> debug.stepin()}
+      ]
+  }]
+
   input: ->
     new Promise (resolve) =>
       @c.output type: 'input', icon: 'file-text', eval: ->


try to make travis happy

try to make travis happy

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -32,6 +32,7 @@
   del(['./dist/*'], done)

 gulp.task '_build', ['_cleanDist'], (done) ->
+  env(vars:{ NODE_ENV: 'production' })
   webpackConfig = require './webpack.config'
   config = _.extend({}, webpackConfig, {
     plugins: [


clean up gulpfile and add coverage badge

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -64,16 +64,7 @@
       }
     }

-# this is ugly but it works...
-gulp.task 'coverage2', ->
-  run 'mocha --compilers coffee:coffee-script/register
-    -r blanket -r should
-    -R html-cov', { silent: true }
-    .exec()
-    .pipe rename 'coverage.html'
-    .pipe gulp.dest '/Users/mc/Desktop'
-
-# this is also ugly and might work...
+# this is also ugly but it works
 gulp.task 'coverage', [ 'test' ], ->
   run 'mocha --compilers coffee:coffee-script/register
     -r blanket -r should


Remove all items helper for kddiacontainer

--- components/dia/kddiacontainer.coffee
+++ components/dia/kddiacontainer.coffee
@@ -30,3 +30,6 @@
   addItem:(data, options={})->
     itemClass = @getOption 'itemClass'
     @addDia new itemClass options, data
+
+  removeAllItems:->
+    dia.destroy?() for _, dia of @dias


not actually using BASE_URL

not actually using BASE_URL

--- coach/configs/webpack.base.coffee
+++ coach/configs/webpack.base.coffee
@@ -5,7 +5,3 @@
     libraryTarget: 'umd'
     library: 'OpenStaxConceptCoach'
     umdNamedDefine: true
-  plugins: [
-    # Pass the BASE_URL along
-    new webpack.EnvironmentPlugin( 'BASE_URL' )
-  ]


Refactor & add line

Refactor & add line

--- src/coffee/main.coffee
+++ src/coffee/main.coffee
@@ -1,27 +1,6 @@
-selected = null

 @init = ->
-  mode = new Mode('line')
-  stage = new createjs.Stage('logistics')
-  cities = [
-    new City(100, 100, 5),
-    new City(50, 100, 3),
-    new City(150, 50, 3)
-  ]
-  lines = [new Line(cities[0], cities[1])]
-  lines.forEach (line) -> stage.addChild(line.shape())
-  cities.forEach (city) ->
-    stage.addChild(city.container)
-  stage.on 'stagemousedown', (m) ->
-    selected = nearlyCities(cities, m.stageX, m.stageY)
-    if selected?
-      selected.select(true)
-      stage.update()
-  stage.on 'stagemouseup', (m) ->
-    if selected?
-      selected.select(false)
-      stage.update()
-  stage.update()
+  new Board()

 nearlyCities = (cities, x, y) ->
   targets = _.filter cities, (c) -> c.diff(x, y) <= 15
@@ -32,3 +11,40 @@
 log = (mes) ->
   text = logElem.val() + '\n' + mes
   logElem.val(text)
+
+class Board
+  constructor: ->
+    @mode = new Mode('line')
+    @stage = new createjs.Stage('logistics')
+    @cities = [
+      new City(100, 100, 5),
+      new City(50, 100, 3),
+      new City(150, 50, 3)
+    ]
+    @lines = [new Line(@cities[0], @cities[1])]
+    @setMouseEvent()
+    @refreshView()
+    @selected = null
+    @stage.update()
+
+  setMouseEvent: ->
+    @stage.on 'stagemousedown', (m) =>
+      @selected = nearlyCities(@cities, m.stageX, m.stageY)
+      if @selected?
+        @selected.select(true)
+        @stage.update()
+    @stage.on 'stagemouseup', (m) =>
+      to = nearlyCities(@cities, m.stageX, m.stageY)
+      if @selected?
+        @selected.select(false)
+        if to?
+          line = new Line(@selected, to)
+          @lines.push(line)
+          @refreshView()
+        @stage.update()
+
+  refreshView: ->
+    @stage.clear()
+    @lines.forEach (line) => @stage.addChild(line.shape())
+    @cities.forEach (city) =>
+      @stage.addChild(city.container)


Add date ranges

Add date ranges

--- src/bdate.coffee
+++ src/bdate.coffee
@@ -10,7 +10,7 @@
   scope:
     bModel: '='
     bSource: '=?'
-    bStartSource: '=?'
+    : '=?'
     bEndSource: '=?'
     bRange: '=?'
     bRootClasses: '@?'
@@ -30,9 +30,11 @@
     $scope.data =
       date: null

-    $scope.$watch 'bSource', ->
-      $scope.isDataReady = true
-    , true
+
+    #TODO (S.Panfilov) should improve wait for data in case or ranges bStartSource and bEndSource
+    #$scope.$watch 'bSource', ->
+    $scope.isDataReady = true
+    #, true

     getFormattedDate = (dmy) ->
       datetime = new Date(dmy.year, dmy.month - 1, dmy.day).getTime()


update row selection to ignore elements matching a supplied list of selectors

--- coffee/directives/fixtable.coffee
+++ coffee/directives/fixtable.coffee
@@ -162,7 +162,14 @@
 			scope.rowSelected = (row) ->
 				return getSelectedItemIndex(row) isnt -1

-			scope.toggleRowSelection = (row) ->
+			scope.toggleRowSelection = (row, event) ->
+
+				# don't toggle row if clicked element is in the ignore list
+				if ignore = scope.options.rowSelectionIgnore
+					ignoredElements = element[0].querySelectorAll ignore.join ','
+					Array::slice.call ignoredElements
+					if Array::slice.call(ignoredElements).indexOf(event.target) >= 0 then return
+
 				if scope.rowSelected row
 					scope.selectedItems.splice getSelectedItemIndex(row), 1
 					scope.$emit 'fixtableUnselectRow', row


Endret port til 4000

Endret port til 4000

--- coffee/nasjonalturbase.api.coffee
+++ coffee/nasjonalturbase.api.coffee
@@ -65,5 +65,5 @@
   console.error err.stack
   res.jsonp 500, {'err':err}

-app.listen 3000
-console.log 'Nasjonal turbase running on port 3000'
+app.listen 4000
+console.log 'Nasjonal turbase running on port 4000'


.noReact()  추가

.noReact()  추가

--- lib/knot.coffee
+++ lib/knot.coffee
@@ -35,6 +35,7 @@

   reactivity 계열 - pullout호출을 자동화 시킴
+    * noReact : 반응하지 마라.
     * consecution: 들어오는 즉시 나감. 처리함수의 연속.
     * asap : 가능한 빨리 큐에서 꺼냄. 비동기
     * debounce: 지연된 시간내의 것을 모아서.
@@ -131,7 +132,7 @@
   getStatus: ()-> @status
   setStatus: (@status)->
     @_trigging()
-
+

   _trigging: ()->
     self = this
@@ -151,9 +152,7 @@

   when: (when_cfg)->
     @_triggers.push when_cfg
-
-  manual: (callback)->
-    @pullOut callback
+

   pullOut: (callback)->
     self = this
@@ -182,6 +181,11 @@
     return this

   # auto reactivity 계열
+  noReact: ()->
+    self = this
+    self.reactivity.clear()
+    return self
+
   consecution : ()->
     self = this
     self.reactivity.clear()


cleanup and race condition fix

cleanup and race condition fix

--- lib/utils/multipart.coffee
+++ lib/utils/multipart.coffee
@@ -6,52 +6,38 @@
 MultipartParser = require('formidable/lib/multipart_parser').MultipartParser
 Stream = require('stream').Stream
 StringDecoder = require('string_decoder').StringDecoder
-streamBuffers = require('stream-buffers')
+WritableStreamBuffer = require('stream-buffers').WritableStreamBuffer
 Promise = require('bluebird')

 # Multipart parser derived from formidable library. See https://github.com/felixge/node-formidable

-parseMultipart = (buffer, _multipartBoundary) -> Promise.try () ->
-  parser = getParser(_multipartBoundary)
-  if parser instanceof Error
-    return Promise.reject(parser)
-  parser.write(buffer)
-  dataBufferList = []
-  for streamBuffer in parser.streamBufferList
-    dataBufferList.push
-      buffer: streamBuffer.streamBuffer.getContents()
-      mime: streamBuffer.mime
-      description: streamBuffer.description
-      contentDescription: streamBuffer.contentDescription
-      contentId: streamBuffer.contentId
-      objectId: streamBuffer.objectId
-  dataBufferList
+_kebabToCamel = (str) ->
+  str.replace /\w-\w/g, (boundary) ->
+    boundary.charAt(0) + boundary.charAt(2).toUpperCase()

-
-getParser = (_multipartBoundary) ->
-  streamBufferList = []
+parseMultipart = (buffer, _multipartBoundary) -> new Promise (resolve, reject) ->
   parser = new MultipartParser()
+  encoding = 'utf8'
+  bufferList = []
   headerField = ''
   headerValue = ''
-  part = {}
-  encoding = 'utf8'
-  ended = false
-  maxFields = 1000
-  maxFieldsSize = 2 * 1024 * 1024
+  part = null
+  done = false
+  transferBuffer = null
+  transferEncoding = null
+  writableStreamBuffer = null

   parser.onPartBegin = () ->
-    part = new Stream()
-    part.readable = true
-    part.headers = {}
-    part.name = null
-    part.filename = null
-    part.mime = null
-    part.transferEncoding = 'binary'
-    part.transferBuffer = ''
+    part = {}
+    transferEncoding = 'binary'
+    transferBuffer = ''
     headerField = ''
     headerValue = ''
+    writableStreamBuffer = new WritableStreamBuffer
+      initialSize: 100 * 1024
+      incrementAmount: 10 * 1024

   parser.onHeaderField = (b, start, end) ->
     headerField += b.toString(encoding, start, end)
@@ -61,84 +47,72 @@

   parser.onHeaderEnd = () =>
     headerField = headerField.toLowerCase()
-    part.headers[headerField] = headerValue
     if headerField == 'content-disposition'
-      m = headerValue.match(/\bname="([^"]+)"/i)
-      if m
-        part.name = m[1]
-      part.filename = self._fileName(headerValue)
+      dispositions = headerValue.split(/\s*;\s*/)
+      for disposition,i in dispositions
+        if i == 0
+          part['dispositionType'] = disposition
+        else
+          split = disposition.indexOf('=')
+          if split > -1
+            paramName = disposition.substr(0, split)
+            if disposition.charAt(split+1) == '"'
+              split++
+            end = disposition.length
+            if disposition.charAt(disposition.length-1) == '"'
+              end--
+            paramValue = disposition.substring(split+1, end)
+            part[_kebabToCamel(paramName)] = paramValue
     else if headerField == 'content-type'
       part.mime = headerValue
     else if headerField == 'content-transfer-encoding'
-      part.transferEncoding = headerValue.toLowerCase()
+      transferEncoding = headerValue.toLowerCase()
+    else
+      part[_kebabToCamel(headerField)] = headerValue
     headerField = ''
     headerValue = ''

   parser.onHeadersEnd = () ->
-    switch part.transferEncoding
+    if done
+      return
+    switch transferEncoding

       when 'binary', '7bit', '8bit'
         parser.onPartData = (b, start, end) ->
-          part.emit('data', b.slice(start, end))
+          writableStreamBuffer.write(b.slice(start, end))
         parser.onPartEnd = () ->
-          part.emit('end')
+          part.buffer = writableStreamBuffer.getContents()
+          bufferList.push(part)

       when 'base64'
+        # base64 encoding has 4 characters for every three bytes of binary encoding; therefore you
+        # can only safely decode from base64 in multiples of 4-character chunks
+
         parser.onPartData = (b, start, end) ->
-          part.transferBuffer += b.slice(start, end).toString('ascii')
-
-          ###
-           four bytes (chars) in base64 converts to three bytes in binary
-           encoding. So we should always work with a number of bytes that
-           can be divided by 4, it will result in a number of bytes that
-           can be divided vy 3.
-          ###
-
-          offset = parseInt(part.transferBuffer.length / 4, 10) * 4
-          part.emit('data', new Buffer(part.transferBuffer.substring(0, offset), 'base64'))
-          part.transferBuffer = part.transferBuffer.substring(offset)
+          transferBuffer += b.slice(start, end).toString('ascii')
+          offset = Math.floor(transferBuffer.length / 4) * 4
+          writableStreamBuffer.write(new Buffer(transferBuffer.substring(0, offset), 'base64'))
+          transferBuffer = transferBuffer.substring(offset)

         parser.onPartEnd = () ->
-          part.emit('data', new Buffer(part.transferBuffer, 'base64'))
-          part.emit('end')
+          writableStreamBuffer.write(new Buffer(transferBuffer, 'base64'))
+          part.buffer = writableStreamBuffer.getContents()
+          bufferList.push(part)

       else
-        return new Error('unknown transfer-encoding')
-    handlePart(part)
+        done = true
+        reject(new Error("unknown content-transfer-encoding: #{JSON.stringify(transferEncoding)}"))

   parser.onEnd = () ->
-    ended = true
+    resolve(bufferList)

-  handlePart = (part) ->
-    fieldsSize = 0
-    if part.filename == undefined
-      value = ''
-      decoder = new StringDecoder(encoding)
-      part.on 'data', (buffer) ->
-        fieldsSize += buffer.length
-        if fieldsSize > maxFieldsSize
-          logger.error('maxFieldsSize exceeded, received ' + fieldsSize + ' bytes of field data')
-          return
-        value += decoder.write(buffer)
+  parser.initWithBoundary(_multipartBoundary)
+  parser.write(buffer)
+  err = parser.end()
+  if err
+    if done
       return
-    writableStreamBuffer = new (streamBuffers.WritableStreamBuffer)(
-      initialSize: 100 * 1024
-      incrementAmount: 10 * 1024
-    )
-    part.on 'data', (buffer) ->
-      if buffer.length == 0
-        return
-      writableStreamBuffer.write(buffer)
-    part.on 'end', ->
-      streamBufferList.push
-        streamBuffer: writableStreamBuffer
-        mime: part.mime
-        contentDescription: part.headers['content-description']
-        contentId: part.headers['content-id']
-        objectId: part.headers['object-id']
-
-  parser.initWithBoundary _multipartBoundary
-  parser.streamBufferList = streamBufferList
-  parser
+    done = true
+    reject(err)

 module.exports.parseMultipart = parseMultipart


changed default to no grid, changed available contexts comment to class variable

--- clock.coffee
+++ clock.coffee
@@ -16,9 +16,10 @@
       rotate_context:   "minute"
       show_labels:      true
       show_percentage:  false
-      show_grid:        true
+      show_grid:        false
       line_width:       50
-    @contexts = ["minute", "hour", "day", "week", "month", "year", "life"] # available contexts: second minute hour day week month year decade life century millenium earth
+    @available_contexts: ['second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'decade', 'life', 'century', 'millenium', 'earth']
+    @contexts = ['minute', 'hour', 'day', 'week', 'month', 'year', 'life']
     @radii = []
     @styles = {}
     @canvas = $("#clock")[0]


changed default configuration for displaying percentages

--- clock.coffee
+++ clock.coffee
@@ -12,7 +12,7 @@
     rotate:           false
     rotate_context:   "minute"
     show_labels:      true
-    show_percentage:  true
+    show_percentage:  false
     show_grid:        true
     line_width:       50


beat hold fix

beat hold fix

--- src/coffee/makio/audio/VJ.coffee
+++ src/coffee/makio/audio/VJ.coffee
@@ -12,8 +12,8 @@
 	@levelsData = [] #levels of each frequecy - from 0 - 1 . no sound is 0. Array [levelsCount]
 	@levelHistory = []

-	@BEAT_HOLD_TIME = 0.35 #num of frames to hold a beat
-	@BEAT_DECAY_RATE = 0.98
+	@BEAT_HOLD_TIME = 0.5 #num of frames to hold a beat
+	@BEAT_DECAY_RATE = 0.99
 	@BEAT_MIN = 0.13 #level less than this is no beat

 	#BPM STUFF


Fix up scale/range validation logic in bokehjs

--- bokehjs/src/coffee/models/canvas/cartesian_frame.coffee
+++ bokehjs/src/coffee/models/canvas/cartesian_frame.coffee
@@ -49,11 +49,16 @@

     for name, range of ranges
       if range instanceof DataRange1d or range instanceof Range1d
-        if not scale instanceof LogScale or not scale instanceof LinearScale
+        if scale not instanceof LogScale and scale not instanceof LinearScale
           throw new Error("Range #{range.constructor.name} is incompatible is Scale #{scale.constructor.name}")
+        # special case because CategoricalScale is a subclass of LinearScale, should be removed in future
+        if scale instanceof CategoricalScale
+          throw new Error("Range #{range.constructor.name} is incompatible is Scale #{scale.constructor.name}")
+
       if range instanceof FactorRange
-        if not scale instanceof CategoricalScale
+        if scale not instanceof CategoricalScale
           throw new Error("Range #{range.constructor.name} is incompatible is Scale #{scale.constructor.name}")
+
       if scale instanceof LogScale and range instanceof DataRange1d
         range.scale_hint = "log"


Leave the form state alone after success

--- components/auth_modal/view.coffee
+++ components/auth_modal/view.coffee
@@ -84,7 +84,7 @@
         @showError message

   onSubmitSuccess: (model, response, options) =>
-    @reenableForm()
+    @reenableForm null, reset: false

     if response.error?
       @showError _.capitalize response.error


split refac after effects

split refac after effects

--- components/split/splitview.coffee
+++ components/split/splitview.coffee
@@ -17,6 +17,7 @@

     super options,data

+    @setClass "kdsplitview kdsplitview-#{@getOptions().type} #{@getOptions().cssClass}"
     @panels       = []
     @panelsBounds = []
     @resizers     = []
@@ -26,7 +27,6 @@

     @_sanitizeSizes()

-    @_putClassNames()
     @_createPanels()
     @_calculatePanelBounds()
     @_putPanels()
@@ -38,14 +38,11 @@

     @listenWindowResize()

-  _putClassNames:->
-    @setClass "kdsplitview kdsplitview-#{@getOptions().type} #{@getOptions().cssClass}"
-
-  # CREATE PANELS
+  # CREATE/REMOVE PANELS
   _createPanels:->
+
     panelCount = @getOptions().sizes.length
-    @panels = for i in [0...panelCount]
-      @_createPanel i
+    @panels = (@_createPanel i for i in [0...panelCount])

   _createPanel:(index)->

@@ -87,7 +84,20 @@

     no

+  _panelIsBeingDestroyed:(panel)->
+
+    index         = @getPanelIndex panel
+    o             = @getOptions()
+    @panels       = @panels.slice(0,index).concat(@panels.slice(index+1))
+    @sizes        = @sizes.slice(0,index).concat(@sizes.slice(index+1))
+    @panelsBounds = @panelsBounds.slice(0,index).concat(@panelsBounds.slice(index+1))
+
+    o.minimums.splice index, 1
+    o.maximums.splice index, 1
+    o.views.splice    index, 1 if o.views[index]?
+
   # CREATE RESIZERS
+
   _createResizers:->

     @resizers = for i in [1...@sizes.length]
@@ -122,7 +132,8 @@
     totalOccupied = 0
     splitSize     = @_getSize()

-    newSizes = for size,i in (if @sizes.length > 0 then @sizes else o.sizes)
+    # newSizes = for size,i in (if @sizes.length > 0 then @sizes else o.sizes)
+    newSizes = for size,i in o.sizes
       if size is null
         nullCount++
         null
@@ -136,18 +147,18 @@
     @sizes = for size in newSizes
       if size is null
         nullSize = (splitSize - totalOccupied) / nullCount
-        Math.round(nullSize)
-      else
-        Math.round(size)
-
-    @sizes
+        Math.round nullSize
+      else
+        Math.round size
+
+    return @sizes

   _sanitizeSize:(size)->
     if "number" is typeof size or /px$/.test(size)
-      parseInt size,10
+      parseInt size, 10
     else if /%$/.test size
       splitSize = @_getSize()
-      splitSize / 100 * parseInt size,10
+      splitSize / 100 * parseInt size, 10

   _setMinsAndMaxs:->
     @getOptions().minimums ?= []
@@ -163,7 +174,6 @@
   _setSize:(size)->
     if @getOptions().type is "vertical" then @setWidth size else @setHeight size

-
   _getParentSize:->
     type = @getOptions().type
     if @parent
@@ -180,51 +190,6 @@
       else
         size

-  _resetSizeValues:->
-
-    # THIS RESIZES ALL PANELS BY THEIR CURRENT PERCENTAGES <- BAD
-
-    # for i in [0...@sizes.length]
-    #   splitSize = @_getSize()
-    #   panelSize = @panels[i].size = @panels[i]._getSize()
-
-    #   newSizeInPercentage = Math.round panelSize * 100 / splitSize
-
-    #   if newSizeInPercentage isnt Infinity and isNaN(newSizeInPercentage) isnt true
-    #     @getOptions().sizes[i] = "#{newSizeInPercentage}%"
-    #   else
-    #     @getOptions().sizes[i] = null
-
-    # total = 0
-    # total += parseInt(size,10) for size in @getOptions().sizes
-    # lastPanelSize = parseInt(@getOptions().sizes[@getOptions().sizes.length-1])
-    # if total < 100
-    #   lastPanelSize += 100 - total
-    #   @getOptions().sizes[@getOptions().sizes.length-1] = "#{lastPanelSize}%"
-    # else
-    #   lastPanelSize -= total - 100
-    #   @getOptions().sizes[@getOptions().sizes.length-1] = "#{lastPanelSize}%"
-
-
-    # ONLY RESIZE PANELS WHICH ARENT GIVEN ANY SIZE INFO WHEN INSTANTIATED
-    panelsToBeResized = []
-    for size,index in @options.sizes
-      panelsToBeResized.push index if size is null
-
-    if panelsToBeResized.length is 0
-      panelsToBeResized = (index for size,index in @options.sizes)
-
-    for size,index in @sizes
-      if index in panelsToBeResized
-        @options.sizes[index] = size
-
-    # for size,index in @getOptions().sizes
-    #   newSizeInPercentage = Math.round @panels[index]._getSize() * 100 / @_getSize()
-    #   @getOptions().sizes[index] = if newSizeInPercentage isnt Infinity and isNaN(newSizeInPercentage) isnt true
-    #     "#{newSizeInPercentage}%"
-    #   else null
-
-
   _resizePanels:->

     @_sanitizeSizes()
@@ -239,8 +204,6 @@
   _windowDidResize:(event)=>

     @_setSize @_getParentSize()
-    @_resetSizeValues()
-
     @_resizePanels()
     @_repositionPanels()
     @_setPanelPositions()
@@ -272,9 +235,32 @@
       $('body').removeClass "resize-in-action"

   ### PUBLIC METHODS ###
-  isVertical : -> @getOptions().type is "vertical"
+
+  isVertical:-> @getOptions().type is "vertical"
+
+  getPanelIndex:(panel)->
+
+    for p,i in @panels
+      if p.getId() is panel.getId()
+        return i
+
+  hidePanel:(panelIndex,callback = noop)=>
+
+    panel = @panels[panelIndex]
+    panel._lastSize = panel._getSize()
+    @resizePanel 0,panelIndex,()=>
+      callback.call @,(panel : panel, index : panelIndex )
+
+  showPanel:(panelIndex,callback = noop)=>
+
+    panel = @panels[panelIndex]
+    newSize = panel._lastSize or @getOptions().sizes[panelIndex] or 200
+    panel._lastSize = null
+    @resizePanel newSize,panelIndex,()->
+      callback.call @,(panel : panel, index : panelIndex )

   resizePanel:(value = 0,panelIndex = 0,callback = noop)=>
+
     @_resizeDidStart()

     value     = @_sanitizeSize value
@@ -343,25 +329,6 @@
         panel1._setSize p1size
         race()
         resizer._setOffset p0offset if resizer
-
-  hidePanel:(panelIndex,callback = noop)=>
-    panel = @panels[panelIndex]
-    panel._lastSize = panel._getSize()
-    @resizePanel 0,panelIndex,()=>
-      callback.call @,(panel : panel, index : panelIndex )
-
-  showPanel:(panelIndex,callback = noop)=>
-    panel = @panels[panelIndex]
-    newSize = panel._lastSize or @getOptions().sizes[panelIndex] or 200
-    panel._lastSize = null
-    @resizePanel newSize,panelIndex,()->
-      callback.call @,(panel : panel, index : panelIndex )
-
-  getPanelIndex:(panel)->
-
-    for p,i in @panels
-      if p.getId() is panel.getId()
-        return i

   splitPanel:(index, options)->

@@ -426,18 +393,6 @@

     return newPanel

-  _panelIsBeingDestroyed:(panel)->
-
-    index         = @getPanelIndex panel
-    o             = @getOptions()
-    @panels       = @panels.slice(0,index).concat(@panels.slice(index+1))
-    @sizes        = @sizes.slice(0,index).concat(@sizes.slice(index+1))
-    @panelsBounds = @panelsBounds.slice(0,index).concat(@panelsBounds.slice(index+1))
-
-    o.minimums.splice index, 1
-    o.maximums.splice index, 1
-    o.views.splice    index, 1 if o.views[index]?
-
   removePanel:(index)->

     l = @panels.length


event.preventDefault()

event.preventDefault()

--- lib/main.coffee
+++ lib/main.coffee
@@ -24,6 +24,7 @@

   Template.lang.events
     "click a.lang": (event) ->
+      event.preventDefault()
       lang = event.currentTarget.id
       document.cookie = "lang=" + lang + ";expires=Sat, 01 Jan 2050 00:00:00 GMT"
       T9n.language = lang


:racehorse: Defer calling getPath()

:racehorse: Defer calling getPath()

--- lib/main.coffee
+++ lib/main.coffee
@@ -4,7 +4,6 @@
 globule = require 'globule'
 {spawnSync} = require 'child_process'
 {getPath} = require 'consistent-path'
-env = Object.assign({}, process.env, {PATH: getPath()})
 prefixPath = null

 module.exports =
@@ -63,6 +62,7 @@
       return require path.join(__dirname, '..', 'node_modules', 'sass-lint')
     if @globalPath is '' and prefixPath is null
       npmCommand = if process.platform is 'win32' then 'npm.cmd' else 'npm'
+      env = Object.assign({}, process.env, {PATH: getPath()})
       try
         prefixPath = spawnSync(npmCommand, [
           'get'
@@ -88,7 +88,7 @@
         config = if projectConfig isnt null then projectConfig else globalConfig

         try
-          linter = this.findExecutable()
+          linter = @findExecutable()
         catch error
           if error.message is 'prefix' then atom.notifications.addError """
             **Error getting $PATH - linter-sass-lint**\n


修复版本回退造成的一些问题

修复版本回退造成的一些问题

--- lib/main.coffee
+++ lib/main.coffee
@@ -95,7 +95,7 @@
                 # ascClip = "assets/#{filename}"
                 # clipboard.writeText(ascClip)

-                @insertUrl "![](assets/#{insertPath}#{filename})", editor
+                @insertText "![](assets/#{insertPath}#{filename})", editor

         return false


Update atom-linter usage

Update atom-linter usage

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,5 +1,5 @@
 {CompositeDisposable} = require 'atom'
-{findFile, exec} = helpers = require 'atom-linter'
+{findFile} = helpers = require 'atom-linter'
 path = require 'path'

 module.exports =


@notificationLog -> @notificationsLog

@notificationLog -> @notificationsLog

--- lib/main.coffee
+++ lib/main.coffee
@@ -58,9 +58,9 @@
             dismissable: true
           atom.notifications.addFatalError("Uncaught #{error.stack.split('\n')[0]}", options)

-    @notificationLog = new NotificationsLog
-    @notificationLog.onItemClick @logItemClick.bind(this)
-    @subscriptions.add atom.commands.add 'atom-workspace', 'notifications:toggle-logs', => @notificationLog.toggle()
+    @notificationsLog = new NotificationsLog
+    @notificationsLog.onItemClick @logItemClick.bind(this)
+    @subscriptions.add atom.commands.add 'atom-workspace', 'notifications:toggle-logs', => @notificationsLog.toggle()

   deactivate: ->
     @subscriptions.dispose()
@@ -102,10 +102,10 @@
       timeSpan = notification.getTimestamp() - @lastNotification.getTimestamp()
       unless timeSpan < @duplicateTimeDelay and notification.isEqual(@lastNotification)
         @notificationsElement.appendChild(atom.views.getView(notification).element)
-        @notificationLog.addNotification(notification)
+        @notificationsLog.addNotification(notification)
     else
       @notificationsElement.appendChild(atom.views.getView(notification).element)
-      @notificationLog.addNotification(notification)
+      @notificationsLog.addNotification(notification)

     notification.setDisplayed(true)
     @lastNotification = notification


Add command to toggle the log panel

Add command to toggle the log panel

--- lib/main.coffee
+++ lib/main.coffee
@@ -40,6 +40,8 @@

     @disposables.add atom.workspace.observeActivePaneItem (item) =>
       @itemUpdate(item)
+
+    atom.commands.add 'atom-workspace', 'log:toggle-log-panel', => @toggleLogPanel()

   deactivate: ->
     @disposables.dispose()
@@ -52,11 +54,10 @@

     @grammarDisposable.add item.observeGrammar? (grammar) =>
       @removeLogPanel()
-      @addLogPanel(item) if grammar.name is 'Log'
+      if grammar.name is 'Log' && atom.config.get 'language-log.showFilterBar'
+        @addLogPanel(item)

   addLogPanel: (textEditor) ->
-    return unless atom.config.get 'language-log.showFilterBar'
-
     # Create new log view if opened log differs from previous
     unless @logView?.textEditor is textEditor
       LogView ?= require './log-view'
@@ -67,3 +68,9 @@

   removeLogPanel: ->
     @logPanel?.destroy()
+
+  toggleLogPanel: ->
+    if @logPanel?
+      @removeLogPanel()
+    else
+      @addLogPanel(atom.workspace.getActiveTextEditor())


Fix lesson time estimate dropdown in NewHomeView

--- app/views/NewHomeView.coffee
+++ app/views/NewHomeView.coffee
@@ -55,7 +55,7 @@
       middle: {'introduction-to-computer-science': '1-3', 'computer-science-5': '7-10', default: '5-8', total: '25-35 hours (about one semester)'}
       high: {'introduction-to-computer-science': '1', 'computer-science-5': '6-9', default: '5-6', total: '22-28 hours (about one semester)'}
     level = if e then $(e.target).val() else 'middle'
-    @$el.find('#courses-container .course-details').each ->
+    @$el.find('#courses-row .course-details').each ->
       slug = $(@).data('course-slug')
       duration = levels[level][slug] or levels[level].default
       $(@).find('.course-duration .course-hours').text duration


simplified removing and adding to channel

--- core/app/backbone/models/fact.coffee
+++ core/app/backbone/models/fact.coffee
@@ -15,24 +15,22 @@
     @get('fact_wheel').opinion_types[type].percentage

   removeFromChannel: (channel, opts={}) ->
-    opts.url = channel.url() + "/" + "remove" + "/" + @get("id") + ".json"
-    oldSuccess = opts.success
-    opts.success = =>
-      indexOf = @get("containing_channel_ids").indexOf(channel.id)
-      @get("containing_channel_ids").splice indexOf, 1  if indexOf
-      if oldSuccess isnt `undefined`
-        oldSuccess()
+    $.ajax _.extend {}, opts,
+      type: "post"
+      url: channel.url() + "/remove/" + @get("id") + ".json"
+      success: =>
+        indexOf = @get("containing_channel_ids").indexOf(channel.id)
+        @get("containing_channel_ids").splice indexOf, 1  if indexOf
+        opts.success?()

-    $.ajax _.extend(type: "post", opts)

   addToChannel: (channel, opts={}) ->
-    opts.url = channel.url() + "/" + "add" + "/" + @get("id") + ".json"
-    oldSuccess = opts.success
-    opts.success = =>
-      @get("containing_channel_ids").push channel.id
-      oldSuccess()  if oldSuccess isnt `undefined`
-
-    $.ajax _.extend(type: "post", opts)
+    $.ajax _.extend {}, opts,
+      type: "post"
+      url: channel.url() + "/add/" + @get("id") + ".json"
+      success: =>
+        @get("containing_channel_ids").push channel.id
+        opts.success?()

   getFactWheel: ->
     new Wheel _.extend {}, @get("fact_wheel"),


ready for heroku test

ready for heroku test

--- sockets.coffee
+++ sockets.coffee
@@ -2,8 +2,8 @@
 db = require "./models"

 db.sequelize.sync().success(() ->
-  server = app.listen (process.env.PORT || 3000), () ->
-    console.log('APPLICATION STARTED ON PORT %d'.green, process.env.PORT || 3000)
+  server = app.listen (process.env.PORT || 8000), () ->
+    console.log('APPLICATION STARTED ON PORT %d'.green, process.env.PORT || 8000)

   io = module.exports = require('socket.io').listen(server, {log: false})
   #add production options as well


coordinator: Fix role lookup in addInitial

--- src/coordinator.coffee
+++ src/coordinator.coffee
@@ -204,7 +204,9 @@
     callback = defaultCallback if not callback

     part = @participants[participantId]
-    part = @participants[participantsByRole(@participants, participantId)] if not part?
+    id = participantsByRole(@participants, participantId)[0]
+    part = @participants[id] if not part?
+
     port = findPort part, 'inport', inport
     return @broker.sendTo 'inqueue', port.queue, message, callback

@@ -214,7 +216,8 @@
     callback = defaultCallback if not callback

     part = @participants[participantId]
-    part = @participants[participantsByRole(@participants, participantId)] if not part?
+    id = participantsByRole(@participants, participantId)[0]
+    part = @participants[id] if not part?

     debug 'subscribeTo', participantId, outport
     port = findPort part, 'outport', outport


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/color.coffee
+++ src/color.coffee
@@ -41,6 +41,8 @@
     parsedColor = null

     if arguments.length > 2
+      alpha = if arguments[3]? then arguments[3] else 1
+      parsedColor = [parseInt(arguments[0]), parseInt(arguments[1]), parseInt(arguments[2]), parseFloat(alpha)]
     else if color.length > 2 && Object.prototype.toString.call(color) == '[object Array]'
       alpha = if color[3]? then color[3] else 1
       parsedColor = [color[0], color[1], color[2], alpha]


minor cleanup

minor cleanup

--- client/Framework/components/tabs/KDTabHandleView.coffee
+++ client/Framework/components/tabs/KDTabHandleView.coffee
@@ -52,11 +52,9 @@
     @setAttribute "title", title
     # @getDomElement().find("span.close-tab").css "color", @getDelegate().getDomElement().css "background-color"

-  isHidden: ->
-    @getOptions().hidden
+  isHidden:-> @getOptions().hidden

-  getWidth: ->
-    @$().outerWidth(no) or 0
+  getWidth:-> @$().outerWidth(no) or 0

   cloneElement: (x) ->
     return if @$cloned


Preferred Domain: add event for checking user’s preferred domain

--- client/app/MainApp/maincontroller/maincontroller.coffee
+++ client/app/MainApp/maincontroller/maincontroller.coffee
@@ -64,6 +64,9 @@
   accountChanged:(account, firstLoad = no)->
     @userAccount             = account
     connectedState.connected = yes
+
+    @on "pageLoaded.as.loggedIn", (account)-> # ignore othter parameters
+      KD.utils.setPreferredDomain account if account

     account.fetchMyPermissionsAndRoles (err, permissions, roles)=>
       return warn err  if err


fixing selected toggle

fixing selected toggle

--- client/controllers/incidentReports/incidentTable.coffee
+++ client/controllers/incidentReports/incidentTable.coffee
@@ -52,7 +52,7 @@

 Template.incidentTable.events
   'click table.incident-table tr': (event, instance) ->
-    instance.data.incidents.update({_id: @_id}, {$set: {selected: true}})
+    instance.data.incidents.update({_id: @_id}, {$set: {selected: !@selected}})

   'click .reject': (event, instance) ->
     changeIncidentStatus(false, instance)


WindowController: resize listeners simplified

--- src/core/windowcontroller.coffee
+++ src/core/windowcontroller.coffee
@@ -39,7 +39,6 @@
     @focusListeners        = []

     @bindEvents()
-    @setWindowProperties()

     super options, data

@@ -62,9 +61,7 @@

     $(window).bind @keyEventsToBeListened.join(' '), @bound "key"

-    $(window).bind "resize", (event)=>
-      @setWindowProperties event
-      @notifyWindowResizeListeners event
+    window.addEventListener "resize", @bound 'notifyWindowResizeListeners'

     document.addEventListener 'scroll', do =>
       timer  = null
@@ -289,20 +286,11 @@
   unregisterWindowResizeListener:(instance)->
     delete @windowResizeListeners[instance.id]

-  setWindowProperties:(event)->
-    @winWidth  = window.innerWidth
-    @winHeight = window.innerHeight
-
-  notifyWindowResizeListeners:(event, throttle = no, duration = 17)->
+  notifyWindowResizeListeners: (event)->
     event or= type : "resize"
-    fireResizeHandlers = =>
-      for own key, instance of @windowResizeListeners when instance._windowDidResize
-        instance._windowDidResize event
-    if throttle
-      KD.utils.killWait @resizeNotifiersTimer
-      @resizeNotifiersTimer = KD.utils.wait duration, fireResizeHandlers
-    else do fireResizeHandlers
+    for own key, inst of @windowResizeListeners when inst._windowDidResize
+      inst._windowDidResize event

 do ->
-  KD           = require './kd.coffee'
+  KD = require './kd.coffee'
   KD.registerSingleton "windowController", new KDWindowController


WindowController: don't call _windowDidResize on null instances

--- src/core/windowcontroller.coffee
+++ src/core/windowcontroller.coffee
@@ -274,7 +274,7 @@

   notifyWindowResizeListeners: (event)->
     event or= type : "resize"
-    for own key, inst of @windowResizeListeners when inst._windowDidResize
+    for own key, inst of @windowResizeListeners when inst?._windowDidResize
       inst._windowDidResize event

 do ->


fix reload

fix reload

--- app/assets/javascripts/dataclips/dataclips.js.coffee
+++ app/assets/javascripts/dataclips/dataclips.js.coffee
@@ -25,7 +25,8 @@

   view = new Dataclips.View(collection: collection)

-  collection.on "reset", Dataclips.proxy.clear
+  collection.on "reset", ->
+    Dataclips.proxy.clear()

   collection.on "batchInsert", (data) =>
     total_entries_count = data.total_entries_count


Ckeditor [ci skip]

Ckeditor [ci skip]

Remove old instances if found

--- app/assets/javascripts/rails_admin/ra.widgets.coffee
+++ app/assets/javascripts/rails_admin/ra.widgets.coffee
@@ -150,6 +150,9 @@

     goCkeditors = ->
       $('form [data-richtext=ckeditor]').not('.ckeditored').each (index, domEle) ->
+        try
+          if instance = window.CKEDITOR.instances[this.id]
+            instance.destroy(true)
         window.CKEDITOR.replace(this, $(this).data('options'))
         $(this).addClass('ckeditored')


Show category search content in the search dropdown.

--- app/assets/javascripts/search_autocomplete.js.coffee
+++ app/assets/javascripts/search_autocomplete.js.coffee
@@ -67,8 +67,14 @@
   getData: (term, callback) ->
     _this = @

-    # Do not trigger request if input is empty
-    return if @searchInput.val() is ''
+    unless term
+      return unless @hasLocationBadge()
+
+      if contents = @getCategoryContents()
+        @searchInput.data('glDropdown').filter.options.callback contents
+        @enableAutocomplete()
+
+      return

     # Prevent multiple ajax calls
     return if @loadingSuggestions
@@ -122,6 +128,27 @@
     ).always ->
       _this.loadingSuggestions = false

+
+  getCategoryContents: ->
+
+    userId         = gon.current_user_id
+    projectSlug    = gl.utils.getProjectSlug()
+    projectOptions = gl.projectOptions[projectSlug]
+
+    return null if not projectSlug or not projectOptions
+
+    { issuesPath, mrPath, projectName } = projectOptions
+
+    return [
+      { header: "Go to in #{projectName}" }
+      { text: 'Issues assigned to me', url: "#{issuesPath}/?assignee_id=#{userId}" }
+      { text: "Issues I've created",   url: "#{issuesPath}/?author_id=#{userId}"   }
+      'separator'
+      { text: 'Merge requests assigned to me', url: "#{mrPath}/?assignee_id=#{userId}" }
+      { text: "Merge requests I've created",   url: "#{mrPath}/?author_id=#{userId}"   }
+    ]
+
+
   serializeState: ->
     {
       # Search Criteria
@@ -209,6 +236,13 @@
     @isFocused = true
     @wrap.addClass('search-active')

+    if @hasLocationBadge() and @getValue() is ''
+      @getData()
+
+
+  getValue: -> return @searchInput.val()
+
+
   onClearInputClick: (e) =>
     e.preventDefault()
     @searchInput.val('').focus()
@@ -228,6 +262,10 @@
     badgeText = "#{category}#{value}"
     @locationBadgeEl.text(badgeText).show()
     @wrap.addClass('has-location-badge')
+
+
+  hasLocationBadge: -> return @wrap.is '.has-location-badge'
+

   restoreOriginalState: ->
     inputs = Object.keys @originalState
@@ -257,13 +295,14 @@

       @getElement("##{input}").val('')

+
   removeLocationBadge: ->
+
     @locationBadgeEl.hide()
-
-    # Reset state
     @resetSearchState()
-
     @wrap.removeClass('has-location-badge')
+    @disableAutocomplete()
+

   disableAutocomplete: ->
     @searchInput.addClass('disabled')


スワイプが動作しなくなっていたのを修正

スワイプが動作しなくなっていたのを修正

--- app/assets/javascripts/views/database_view.js.coffee
+++ app/assets/javascripts/views/database_view.js.coffee
@@ -33,10 +33,10 @@
     if CommonView.isPhoneDevice()
       $arcanaTable.swipe (
         swipeLeft: (e) ->
-          prevTargetPage()
+          $('.pager-prev').click()
           e.preventDefault()
         swipeRight: (e) ->
-          nextTargetPage()
+          $('.pager-next').click()
           e.preventDefault()
       )

@@ -59,12 +59,14 @@
     view.sortOrderState.onValue (state) -> renderOrderState(state)

   replaceTableArea = (as) ->
+    $arcanaTable.hide()
     tbody = $arcanaTable.children("tbody")
     tbody.empty()

     _.forEach as, (a) ->
       tr = renderTableArcana(a)
       tbody.append(tr)
+    $arcanaTable.fadeIn('slow')
     @

   renderTableHeader = ->


Render this view into the right spot.

Render this view into the right spot.

--- app/assets/javascripts/views/projects/show.js.coffee
+++ app/assets/javascripts/views/projects/show.js.coffee
@@ -103,7 +103,7 @@
     # If there are users not assigned to this project in the current company, show them here
     unassignedUsers = @model.getUnassignedUsers()
     unless unassignedUsers.isEmpty()
-      @$el.append StaffPlan.Templates.Projects.show.addSomeone
+      @$el.find('section.main').append StaffPlan.Templates.Projects.show.addSomeone
         unassignedUsers: unassignedUsers.map (u) -> u.attributes
         projectId: @model.id


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -156,7 +156,7 @@
             # Attempt to look up module in global namespace
             for key, value of root
               if value is Module
-                Module._name = moduleName = key # Cache
+                Module._name = moduleName = key # Cache module name

           if moduleName
             unless I.includedModules.include moduleName


show key when suggesting completions

show key when suggesting completions

--- lib/provider.coffee
+++ lib/provider.coffee
@@ -75,7 +75,7 @@
                 text: resultTemplate.replace("[key]", word.key)
                 displayText: word.title
                 replacementPrefix: prefix
-                # leftLabel: word.key
+                leftLabel: word.key
                 rightLabel: word.by
                 className: word.type
                 iconHTML: "<i class='icon-#{icon}'></i>"


Removed setting of unused classes

Removed setting of unused classes

--- js-library/app/js/jail_iframe/classes/set_position_of_element.coffee
+++ js-library/app/js/jail_iframe/classes/set_position_of_element.coffee
@@ -1,36 +1,17 @@
 FactlinkJailRoot.set_position_of_element = (top, left, window, el) ->
-  setLeft = (element) ->
-    element.addClass "left"
-    element.removeClass "right"
-  setRight = (element) ->
-    element.addClass "right"
-    element.removeClass "left"
-  setTop = (element) ->
-    element.addClass "top"
-    element.removeClass "bottom"
-  setBottom = (element) ->
-    element.addClass "bottom"
-    element.removeClass "top"
-
   x = left
   y = top
   x -= 30

   if $(window).width() < (x + el.outerWidth(true) - $(window).scrollLeft())
     x = $(window).width() - el.outerWidth(true)
-    setLeft el
   else
     x = $(window).scrollLeft()  if x < $(window).scrollLeft()
-    setRight el

   y -= 6 + el.outerHeight(true)
   if y < $(window).scrollTop()
     y = $(window).scrollTop() + el.outerHeight(true) + 14
-    setTop el
-  else
-    setBottom el

   el.css
     top: y + "px"
     left: x + "px"
-


reenable unit select js

reenable unit select js

--- app/assets/javascripts/all.js.coffee
+++ app/assets/javascripts/all.js.coffee
@@ -39,10 +39,10 @@

   # $("input.search-typeahead").delay(100).focus()

-  # $("select#select_default_unit").change ->
-  #   if $(@).val()
-  #     #console.log $(@).val()
-  #     $(@).closest('form').submit()
+  $("select#select_default_unit").change ->
+    if $(@).val()
+      #console.log $(@).val()
+      $(@).closest('form').submit()

   # $("#scoutz-navbar-collapse").on('shown.bs.collapse', ->
   #   #console.log "scoutz-navbar-collapse:shown"


refactors

refactors

--- app/assets/javascripts/dom.js.coffee
+++ app/assets/javascripts/dom.js.coffee
@@ -8,11 +8,11 @@

   # widgetMouseout - handle when a user mouses over a product widget.
   widgetMouseover: (ev) ->
-    $(ev.currentTarget).find('.overlay').removeClass('hide')
+    $(ev.currentTarget).find('.overlay').removeClass 'hide'

   # widgetMouseout - handle when a user mouses out from a product widget.
   widgetMouseout: (ev) ->
-    $(ev.currentTarget).find('.overlay').addClass('hide')
+    $(ev.currentTarget).find('.overlay').addClass 'hide'

   # ajaxComplete - figure out what the ajax was for, and execute it.
   ajaxComplete: (xhr, status) =>
@@ -35,7 +35,7 @@
       $flash.addClass 'error'

     $flash.text status.getResponseHeader('Flash-Message')
-    $flash.show()
+    $flash.removeClass 'hide'

   # ajaxPopup - when an ajax request is complete, if it is for a poup, show it.
   ajaxPopup: (xhr, status) =>
@@ -54,7 +54,7 @@
     $contact = $ '#contact'
     $contact.foundation 'reveal', 'close'

-    $flash.hide()
+    $flash.addClass 'hide'
     $flash.text ''
     $contact.find('input[type=text]').val ''
     $contact.find('textarea').val ''


Fix region splitting logic to update 1 and add 1

instead of removing one and adding one, which caused segment_items
to become undraggable because an undraggable segment was covering them.

--- app/assets/javascripts/mix.js.coffee
+++ app/assets/javascripts/mix.js.coffee
@@ -45,9 +45,8 @@
   time ||= wavesurfer.getCurrentTime()
   # Assume that time is always within a region, since we initialize a region over full audio at start
   splitCandidate = this.getRegionsAt(time)[0]
-  wavesurfer.addRegion { start: splitCandidate.start, end: round1000(time), drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
+  wavesurfer.regions.list[splitCandidate.region_id].update(end: round1000(time))
   wavesurfer.addRegion { start: round1000(time), end: splitCandidate.end, drag: false, resize: false, data: { type: 'segment' }, color: randomColor(0.3) }
-  wavesurfer.regions.list[splitCandidate.region_id].remove()

 wavesurfer.getRegionsAt = (time) ->
   region for region in regionsArray() when (region.start <= time && time < region.end)


account all typed words when search for similar people

--- app/assets/javascripts/people.coffee
+++ app/assets/javascripts/people.coffee
@@ -20,27 +20,18 @@

   $('.js-person-name').on 'keyup', ->
     name = $(this).val().replace(/^\s+|\s+$/g, '');
+    container = $('.js-similar-people-container')
     if name.length > 1
-      $.ajax(url: "/people.json?q[name_cont]=#{name}&q[id_not_eq]=#{$(this).data('current-id')}")
+      query = "/people.json?q[id_not_eq]=#{$(this).data('current-id')}"
+      for part in name.split(/\s+/)
+        query = query + "&q[name_cont_all][]=#{part}"
+
+      $.ajax(url: query)
         .done (response)->
+          container.html('')
           if response.length > 0
-            $('.js-similar-people-container').html("Found #{response.length} similar people")
+            container.html("Found #{response.length} similar people")
             if response.length < 10
-              $('.js-similar-people-container').append(": ")
+              container.append(": ")
               for person in response
-                $('.js-similar-people-container').append("#{person["name"]}  ")
-          else
-            $('.js-similar-people-container').html('Similar people not found')
-
-      if name.indexOf(' ') > 0
-        reverse_name = name.split(/\s+/).reverse().join(' ')
-        $.ajax(url: "/people.json?q[name_cont]=#{reverse_name}&q[id_not_eq]=#{$(this).data('current-id')}")
-          .done (response)->
-            if response.length > 0
-              if $('.js-similar-people-container').html().length > 0
-                $('.js-similar-people-container').append('. ')
-              $('.js-similar-people-container').append("Found #{response.length} with reversed name")
-              if response.length < 10
-                $('.js-similar-people-container').append(": ")
-                for person in response
-                  $('.js-similar-people-container').append("#{person["name"]}  ")
+                container.append("#{person["name"]}  ")


cleanup

cleanup

--- project/coffee/Main.coffee
+++ project/coffee/Main.coffee
@@ -12,10 +12,8 @@
 IS_LIVE = false

 # ONLY EXPOSE APP GLOBALLY IF LOCAL OR DEV'ING
-# view = if IS_LIVE then {} else (window or document)
-view = window
+view = if IS_LIVE then {} else (window or document)

 # DECLARE MAIN APPLICATION
 view.B = new App IS_LIVE
-console.log view.B
 view.B.init()


* removed timeout

* removed timeout

--- app/assets/javascripts/webrtc.coffee
+++ app/assets/javascripts/webrtc.coffee
@@ -191,7 +191,6 @@
     peer_connection = new RTCPeerConnection(@pc_config, {"optional": []} )
     @peers[peer_id] = peer_connection
     peer_connection.oniceconnectionstatechange = (ev) ->
-      console.log('oniceconnectionstatechange', ev)
       console.log('peer_connection.iceConnectionState == ' + peer_connection.iceConnectionState )
       if( peer_connection.iceConnectionState == 'completed' )
         console.log('WE HAVE A PEER CONNECTION....')
@@ -205,6 +204,8 @@
                     'candidate': event.candidate.candidate
                   }
         })
+    peer_connection.onsignalingstatechange = (ev) ->
+      console.log('Signaling state changed to: ' + peer_connection.signalingState)

     peer_connection.onaddstream = (event) =>
       console.log("onAddStream: ", event)
@@ -287,9 +288,7 @@
       @doGetUserMedia( =>
         peer = @addPeer({uuid:peer_id, sendOffer:false})
         # now we have the peer so lets try this again.
-        setTimeout(=>
-          @sessionDescription(config)
-        ,3500)
+        @sessionDescription(config)
       )
     else
       remote_description = config.session_description
@@ -341,7 +340,7 @@
       , ->
         console.log('Added the ICE Candidate.')
       , (err) ->
-        console.log('[ERROR] - ICE Candidate.' + err)
+        console.log('[ERROR] - ICE Candidate. ' + err)
       )
     else
       #alert('[ERROR] - no peer for peer_id: '+config.peer_id)


keep one extra build until per-page pdf serving is enabled

--- app/coffee/OutputCacheManager.coffee
+++ app/coffee/OutputCacheManager.coffee
@@ -90,7 +90,7 @@
 						# pass back the list of new files in the cache
 						callback(err, results)
 						# let file expiry run in the background, expire all previous files if per-user
-						OutputCacheManager.expireOutputFiles cacheRoot, {keep: buildId, limit: if perUser then 0 else null}
+						OutputCacheManager.expireOutputFiles cacheRoot, {keep: buildId, limit: if perUser then 1 else null}

 	archiveLogs: (outputFiles, compileDir, buildId, callback = (error) ->) ->
 		archiveDir = Path.join(compileDir, OutputCacheManager.ARCHIVE_SUBDIR, buildId)


Delete a console.log sentence

Delete a console.log sentence

--- app/coffee/controllers/issues.coffee
+++ app/coffee/controllers/issues.coffee
@@ -652,7 +652,6 @@
     openModal: () ->
         @loadProjectTags()
         @scope.formOpened = true
-        console.log @scope
         @scope.form = @scope.context.us

         @scope.$broadcast("checksley:reset")


More detail of user info

More detail of user info

--- lib/restivus.coffee
+++ lib/restivus.coffee
@@ -192,7 +192,10 @@
     get: (collection) ->
       get:
         action: ->
-          entity = collection.findOne @urlParams.id, fields: profile: 1
+          entity = collection.findOne @urlParams.id, fields:
+                                                        profile: 1
+                                                        username: 1
+                                                        'profile.portrait': 1
           if entity
             {status: "success", data: entity}
           else


Note bug in settings.

Note bug in settings.

(This bug has been around for quite some time.  I just noticed it now.)

--- lib/settings.coffee
+++ lib/settings.coffee
@@ -55,6 +55,8 @@

   set: (key, value) ->
     # Don't store the value if it is equal to the default, so we can change the defaults in the future.
+    # FIXME(smblott).  This test is broken for exclusionRules (for which it is never true).  In this case, we
+    # need some kind of structural equality (or perhaps comparison of JSONified strings).
     if value == @defaults[key]
       @clear key
     else


Add oneRow() do database

Add oneRow() do database

--- src/db/database.coffee
+++ src/db/database.coffee
@@ -17,16 +17,21 @@
     run: (stmt, callback) ->
         @execute(stmt, { onDone: () -> callback(null) }, callback)

-    scalar: (query, callback) ->
+    scalar: (query, callback) -> @_selectOneRow(query, 'array')
+
+    oneRow: (stmt, callback) -> @_selectOneRow(query, 'object')
+
+    _selectOneRow: (query, rowShape) ->
         opt = {
-            rowShape: 'array'
+            rowShape: rowShape
             onAllRows: (rows) ->
                 if (rows.length != 1)
                     e = "Expected query #{query} to return 1 row, " +
                         "but it returned #{rows.length} rows."
                     callback(e)

-                callback(null, rows[0][0])
+                v = (if rowShape == 'array' then rows[0][0] else rows[0])
+                callback(null, v)
         }
         @execute(query, opt, callback)


Guard against possibly missing function

Guard against possibly missing function

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -26,7 +26,7 @@
     @loaded = true

   loadSnippetsFromPackage: (pack, done) ->
-    if pack.getType() is 'textmate'
+    if pack.getType?() is 'textmate'
       @loadTextMateSnippets(pack.path, done)
     else
       @loadAtomSnippets(pack.path, done)


:art:

:art:

--- lib/snippets.coffee
+++ lib/snippets.coffee
@@ -118,7 +118,7 @@
   handleUserSnippetsDidChange: ->
     userSnippetsPath = @getUserSnippetsPath()
     atom.config.transact =>
-      @clearSnippetsForPath(userSnippetsPath)
+      @clearUnparsedSnippetsForPath(userSnippetsPath)
       @loadSnippetsFile userSnippetsPath, (result) =>
         @add(userSnippetsPath, result)

@@ -179,10 +179,10 @@
         else if not body?
           snippetsByPrefix[prefix] = null

-      @storeSnippetsByPrefix(snippetsByPrefix, filePath, selector)
+      @storeUnparsedSnippetsByPrefix(snippetsByPrefix, filePath, selector)
     return

-  storeSnippetsByPrefix: (value, path, selector) ->
+  storeUnparsedSnippetsByPrefix: (value, path, selector) ->
     newValue = {}
     setValueAtKeyPath(newValue, "snippets", value)
     value = newValue
@@ -191,17 +191,17 @@
     settingsBySelector[selector] = value
     @scopedPropertyStore.addProperties(path, settingsBySelector, priority: @priorityForSource(path))

-  clearSnippetsForPath: (path) ->
+  clearUnparsedSnippetsForPath: (path) ->
     for scopeSelector of @scopedPropertyStore.propertiesForSource(path)
       settings = @scopedPropertyStore.propertiesForSourceAndSelector(path, scopeSelector)
       @scopedPropertyStore.removePropertiesForSourceAndSelector(path, scopeSelector)

-  snippetsByPrefixForScopes: (descriptor) ->
+  parsedSnippetsByPrefixForScope: (descriptor) ->
     scopeDescriptor = ScopeDescriptor.fromObject(descriptor)
-    snippetsAttributesByPrefix = @scopedPropertyStore.getPropertyValue(scopeDescriptor.getScopeChain(), "snippets")
-    snippetsAttributesByPrefix ?= {}
+    unparsedSnippetsByPrefix = @scopedPropertyStore.getPropertyValue(scopeDescriptor.getScopeChain(), "snippets")
+    unparsedSnippetsByPrefix ?= {}
     snippets = {}
-    for prefix, attributes of snippetsAttributesByPrefix
+    for prefix, attributes of unparsedSnippetsByPrefix
       continue if typeof attributes?.body isnt 'string'

       {name, body, bodyTree, description, descriptionMoreURL} = attributes
@@ -265,7 +265,7 @@
     longestPrefixMatch

   getSnippets: (editor) ->
-    @snippetsByPrefixForScopes(editor.getLastCursor().getScopeDescriptor())
+    @parsedSnippetsByPrefixForScope(editor.getLastCursor().getScopeDescriptor())

   snippetToExpandUnderCursor: (editor) ->
     return false unless editor.getLastSelection().isEmpty()
@@ -320,4 +320,4 @@
   provideSnippets: ->
     bundledSnippetsLoaded: => @loaded
     insertSnippet: @insert.bind(this)
-    snippetsByPrefixForScopes: @snippetsByPrefixForScopes.bind(this)
+    snippetsByPrefixForScopes: @parsedSnippetsByPrefixForScope.bind(this)


Don't use intermediary variable

Don't use intermediary variable

--- resources/assets/coffee/_classes/changelog-chart.coffee
+++ resources/assets/coffee/_classes/changelog-chart.coffee
@@ -106,12 +106,10 @@
       .domain [0, 1]

   drawLines: ->
-    layer = @svgWrapper
+    @svgWrapper
       .selectAll 'g'
       .data @data
       .enter()
-
-    layer
       .append 'path'
       .attr 'class', (d) => "changelog-chart__area changelog-chart__area--#{@classScale d.key}"
       .attr 'd', @areaFunction


another s/amountCents/amount, although harmless bug this time

--- src/node_modules/bucket-model/bucket-model.coffee
+++ src/node_modules/bucket-model/bucket-model.coffee
@@ -46,7 +46,7 @@
                   id: currentUser.id
                   name: currentUser.name
                 bucketId: @id
-                amountCents: 0
+                amount: 0
               })

         groupContributions:


FindTeam handler: code cleanup

FindTeam handler: code cleanup

--- servers/lib/server/handlers/findteam.coffee
+++ servers/lib/server/handlers/findteam.coffee
@@ -35,16 +35,14 @@

       { profile : { nickname } } = account

-      Tracker.identify nickname, { email }, (err) ->
-        return next err  if err
-        Tracker.track nickname, {
-          to      : email
-          subject : Tracker.types.REQUESTED_TEAM_LIST
-        }, {
-          teams         : groups.map (group) -> helper.createTeamItem group
-          findTeamUrl   : "#{protocol}//#{hostname}/Teams/FindTeam"
-          createTeamUrl : "#{protocol}//#{hostname}/Teams/Create"
-        }, next
+      Tracker.identifyAndTrack nickname, {
+        to      : email
+        subject : Tracker.types.REQUESTED_TEAM_LIST
+      }, {
+        teams         : groups.map (group) -> helper.createTeamItem group
+        findTeamUrl   : "#{protocol}//#{hostname}/Teams/FindTeam"
+        createTeamUrl : "#{protocol}//#{hostname}/Teams/Create"
+      }, next
   ]

   async.waterfall queue, (err) ->


[Router] revert preserving of client.ol_context

We do not enter this line twice, it would result in multiple event
 handlers too.

--- services/real-time/app/coffee/Router.coffee
+++ services/real-time/app/coffee/Router.coffee
@@ -55,7 +55,9 @@
 		app.post "/client/:client_id/disconnect", httpAuth, HttpApiController.disconnectClient

 		session.on 'connection', (error, client, session) ->
-			client.ol_context = {} unless client.ol_context
+			# init client context, we may access it in Router._handleError before
+			#  setting any values
+			client.ol_context = {}

 			client?.on "error", (err) ->
 				logger.err { clientErr: err }, "socket.io client error"


better support for html strings

better support for html strings

--- lib/tree.coffee
+++ lib/tree.coffee
@@ -21,7 +21,9 @@
       [head, children] = data
       @treeView head, (@fromJson child for child in children)
     else
-      $$ -> @div data
+      view = $$ -> @div()
+      view.append data
+      view

   toggle: (view) ->
     view.find('> .body').toggle()


change error message on email update failure

--- services/web/app/coffee/Features/User/UserUpdater.coffee
+++ services/web/app/coffee/Features/User/UserUpdater.coffee
@@ -81,7 +81,7 @@
 					logger.err error:error, 'problem removing users email'
 					return callback(error)
 				if res.n == 0
-					return callback(new Error('Cannot remove default email'))
+					return callback(new Error('Cannot remove email'))
 				callback()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -44,7 +44,8 @@

   Point:: =
     ###*
-    @nam
+    @name clamp
+
     ###
     clamp: (n) ->
       @copy().clamp$()


Documentation

Documentation

--- src/point.coffee
+++ src/point.coffee
@@ -407,6 +407,14 @@

     ###*
     Computed the length of this point as though it were a vector from (0,0) to (x,y)
+
+

+       point = Point(5, 7)
+
+       point.length()
+    => 8.602325267042627
+

+
     @name length
     @methodOf Point#


Calc exponentiation display fix [Fixes #4809590]

--- javascripts/lib/math/expression_to_string.coffee
+++ javascripts/lib/math/expression_to_string.coffee
@@ -25,15 +25,14 @@
           });

         ref.forType(comps.classes.exponentiation, {
-          base: ->
-            ref.refine(@unrefined().base()).toString(include_parentheses_if_single: false)
-          power: ->
-            ref.refine(@unrefined().power()).toString(include_parentheses_if_single: true)
+          base: (method="toString")->
+            ref.refine(@unrefined().base())[method](include_parentheses_if_single: false)
+          power: (method="toString")->
+            ref.refine(@unrefined().power())[method](include_parentheses_if_single: true)
           toString: ->
             "#{@base()} ^ #{@power()}"
-
           toHTMLString: ->
-            "#{@base()} ˆ #{@power()}"
+            "#{@base('toHTMLString')} ˆ #{@power('toHTMLString')}"
           });

         ref.forType(comps.classes.multiplication, {


use view jquery

use view jquery

--- app/views/play/menu/GuideView.coffee
+++ app/views/play/menu/GuideView.coffee
@@ -103,7 +103,7 @@

   onHidden: ->
     if @vimeoListenerAttached
-      player = $('#help-video-player')[0]
+      player = @$('#help-video-player')[0]
       player.contentWindow.postMessage JSON.stringify(method: 'pause'), '*'
     createjs?.Sound?.setVolume?(@volume ? ( me.get('volume') ? 1.0))
     Backbone.Mediator.publish 'level:docs-hidden', {}


Fix formatString in XDate -- Related to #1596

--- admin/static/coffee/log_view.coffee
+++ admin/static/coffee/log_view.coffee
@@ -137,8 +137,8 @@
                 too_many_new_entries: @num_new_entries > @max_log_entries
                 max_log_entries: @max_log_entries
                 has_logs: @current_logs.length > 0
-                from_date: new XDate(@current_logs[0]?.get('timestamp')*1000).toString("MMMM M, yyyy 'at' HH:mm:ss")
-                to_date: new XDate(@current_logs[@displayed_logs-1]?.get('timestamp')*1000).toString("MMMM M, yyyy 'at' HH:mm:ss")
+                from_date: new XDate(@current_logs[0]?.get('timestamp')*1000).toString("MMMM d, yyyy 'at' HH:mm:ss")
+                to_date: new XDate(@current_logs[@displayed_logs-1]?.get('timestamp')*1000).toString("MMMM d, yyyy 'at' HH:mm:ss")
                 logs_for:
                     general:    @type is 'general'
                     machine:    @type is 'machine'


fixed #260 by inserting HTML instead of jQuery fadeId

--- app/assets/javascripts/annotation_lifecycle.coffee
+++ app/assets/javascripts/annotation_lifecycle.coffee
@@ -36,8 +36,9 @@
       $(this).remove()
     )

-    this.$blankSlate = $('.no-annotation-document-blank-slate')
-    this.$blankSlate.css('display', 'none')
+    $blankSlate = $('.no-annotation-document-blank-slate')
+    this.blankSlateTemplate = $blankSlate[0].outerHTML
+    $blankSlate.remove()

   startIteration: ->
     console.log 'AnnotationLifecycle: start new iteration & clean DOM container'
@@ -52,7 +53,7 @@
     $('.interfaces-staging > div:not(.template)').remove()

     if data.code == 404
-      _this.$blankSlate.fadeIn()
+      $('.interfaces-staging').append(_this.blankSlateTemplate)
       return

     for interfaceType, payload of data


Keep sticky elements after AJAX requests

Turbolinks doesn't get on well with Foundation's Sticky, and so we need
to manually trigger the event on Turbolinks' `page:load`.


Fix loading of name form settings.

Fix loading of name form settings.

--- src/proto.coffee
+++ src/proto.coffee
@@ -43,10 +43,11 @@
 # Fetch a Gist from the GitHub API. Calls the callback whether or not the
 # request was successful.
 getGist = (url, cb) ->
+getGist = (url, callback) ->
     GIST_API = 'https://api.github.com/gists'
     post_req = rest.get(GIST_API + url)
     post_req.on 'complete', (data, response) ->
-        cb(data, response.statusCode)
+        callback(data, response.statusCode)

 # Initialize a project using the specified project name and the default
@@ -78,23 +79,24 @@
             if status_code isnt 200
                 quitWithMsg("Unable to fetch gist: #{ status_code }")
             else
+                # Load the Gist contents into a template for the init.
+                templates = {}
+                for proto_f in PROTO_FILES
+                    # If the needed file isn't in the Gist, warn and quit.
+                    if not data.files?[proto_f]?
+                        quitWithMsg("Gist is invalid Proto project, missing file: #{ proto_f }")
+                    else
+                        templates[proto_f] = data.files[proto_f].content
+
                 if cli_args[1]?
                     # If there is a second name specified, use that as the
                     # project name.
                     project_name = cli_args[1]
                 else
                     # Use the name specified name in the settings file.
-                    project_name = JSON.parse(data.files['settings.json'].content).name
+                    project_name = JSON.parse(templates['settings.json']).name

                 stamp("Fetched Gist, project name is #{ project_name }")
-
-                # Load the Gist contents into a template for the init.
-                templates = {}
-                for f in PROTO_FILES
-                    # If the needed file isn't in the Gist, warn and quit.
-                    if not data.files[f]?
-                        quitWithMsg("Gist is invalid Proto project, bad file: #{ k }")
-                    templates[k] = v.content

                 doInit(templates)


Adds an explicit height and width to the Loan Calculator tool

--- app/assets/javascripts/syndication/tools.js.coffee
+++ app/assets/javascripts/syndication/tools.js.coffee
@@ -272,6 +272,8 @@
       cy:
         path: "cy/tools/cyfrifiannell-benthyciadau/loan"
       title: "Loan calculator"
+      width: "100%"
+      height: "1200px"

     loan_calculator_beta:
       en:


NewContactForm cleaned up

NewContactForm cleaned up

--- app/coffee/modules/contact/new/views/layout.coffee
+++ app/coffee/modules/contact/new/views/layout.coffee
@@ -4,10 +4,29 @@
   className: 'container-fluid'

   behaviors:
+    ModelEvents: {}
     SubmitButton: {}
+
+    Flashes:
+      success:
+        message:  'Contact created.'
+      error:
+        message:  'Error creating Contact.'
+
+  onRequest: ->
+    @disableSubmit() # TODO - disable inputs as well?
+
+  onError: ->
+    @flashError()
+    @enableSubmit()
+
+  onSync: ->
+    @flashSuccess()
+    window.location = '#contact_methods' # TODO - redirection, state service?

   onSubmit: (e) ->
     attrs = Backbone.Syphon.serialize(@)
+    console.log attrs
     # TODO - native contact service
     # Backbone.channel('native:contact').request('add', attrs)


only unzip wakatime-cli.zip if the file exists

--- lib/wakatime.coffee
+++ lib/wakatime.coffee
@@ -190,10 +190,11 @@
     )

 unzip = (file, outputDir, cleanup) ->
-    zip = new AdmZip(file)
-    zip.extractAllTo(outputDir, true)
-    if cleanup
-        fs.unlink(file)
+    if fs.existsSync(file)
+        zip = new AdmZip(file)
+        zip.extractAllTo(outputDir, true)
+        if cleanup
+            fs.unlink(file)

 sendHeartbeat = (file, lineno, isWrite) ->
     time = Date.now()


调整词云单词大小

调整词云单词大小

--- app/front/app/search/views/index/cloud-view.coffee
+++ app/front/app/search/views/index/cloud-view.coffee
@@ -28,7 +28,7 @@

     d3.layout.cloud().size([@svg_w * 1.8, @svg_h * 1.8])
       .words(words
-      .map (d) -> return {text: d['word'], size: 18 + 6 * d['word_count']})
+      .map (d) -> return {text: d['word'], size: 16 + 4 * d['word_count']})
       .padding(5)
       .rotate((d, i)-> return (i % 2 ) * 90)
       .font("Impact")


Bugfix

Bugfix

--- app/lib/chart.coffee
+++ app/lib/chart.coffee
@@ -112,8 +112,7 @@
   brushed: =>
     if @brush.empty()
       @brushing = false
-      xExtent = d3.extent @parent.data, (d) -> d.x
-      @parent.xScale.domain xExtent
+      @parent.render()
     else
       @brushing = true
       @parent.xScale.domain @brush.extent()


Update cs.coffee

Update cs.coffee

Translation of couple of things into czech.

--- app/locale/cs.coffee
+++ app/locale/cs.coffee
@@ -66,39 +66,39 @@
 #    want_coco: "Want CodeCombat at your school?"

   nav:
-#    map: "Map"
+    map: "Mapa"
     play: "Úrovně" # The top nav bar entry where players choose which levels to play
     community: "Komunita"
     courses: "Kurzy"
     blog: "Blog"
     forum: "Fórum"
     account: "Účet"
-#    my_account: "My Account"
+    my_account: "Můj Účet"
     profile: "Profil"
     home: "Domů"
     contribute: "Přispívat"
     legal: "Licence"
-#    privacy: "Privacy"
+    privacy: "Soukromí"
     about: "O stránkách"
     contact: "Kontakt"
     twitter_follow: "Sledovat na Twitteru"
-#    my_classrooms: "My Classes"
-#    my_courses: "My Courses"
+    my_classrooms: "Moje Třídy"
+    my_courses: "Moje Kurzy"
     careers: "Kariéry"
-#    facebook: "Facebook"
-#    twitter: "Twitter"
-#    create_a_class: "Create a Class"
-#    other: "Other"
-#    learn_to_code: "Learn to Code!"
-#    toggle_nav: "Toggle navigation"
-#    schools: "Schools"
-#    get_involved: "Get Involved"
-#    open_source: "Open source (GitHub)"
-#    support: "Support"
-#    faqs: "FAQs"
-#    help_pref: "Need help? Email"
-#    help_suff: "and we'll get in touch!"
-#    resource_hub: "Resource Hub"
+    facebook: "Facebook"
+    twitter: "Twitter"
+    create_a_class: "Vytvořit Třídu"
+    other: "Jiné"
+    learn_to_code: "Nauč se kódit!"
+    toggle_nav: "Přepnout navigaci"
+    schools: "Školy"
+    get_involved: "Zapojit se"
+    open_source: "Open source (GitHub)"
+    support: "Podpora"
+    faqs: "FAQ"
+    help_pref: "Potřebujete pomoc? Email"
+    help_suff: "a my se ozveme!"
+    resource_hub: "Resource Hub"

   modal:
     close: "Zavřít"
@@ -117,9 +117,9 @@

   play:
     play_as: "Hrát jako" # Ladder page
-#    get_course_for_class: "Assign Game Development and more to your classes!"
-#    request_licenses: "Contact our school specialists for details."
-#    compete: "Compete!" # Course details page
+    get_course_for_class: "Přiřaď Herní Vývoj a jiné do svých tříd!"
+    request_licenses: "Kontaktujte specialisty naší školy pro více detailů."
+    compete: "Soupeřit!" # Course details page
     spectate: "Dívat se" # Ladder page
     players: "hráči" # Hover over a level on /play
     hours_played: "hodin nahráno" # Hover over a level on /play
@@ -134,15 +134,15 @@
     heroes: "Hrdinové" # Tooltip on hero shop button from /play
     achievements: "Úspěchy" # Tooltip on achievement list button from /play
     settings: "Nastavení" # Tooltip on settings button from /play
-#    poll: "Poll" # Tooltip on poll button from /play
+    poll: "Hlasování" # Tooltip on poll button from /play
     next: "Další" # Go from choose hero to choose inventory before playing a level
     change_hero: "Změnit hrdinu" # Go back from choose inventory to choose hero
     buy_gems: "Zakoupit drahokamy"
     subscription_required: "Předplatné vyžadováno"
-#    subscribers_only: "Subscribers Only!"
-#    subscribe_unlock: "Subscribe to Unlock!"
-#    subscriber_heroes: "Subscribe today to immediately unlock Amara, Hushbaum, and Hattori!"
-#    subscriber_gems: "Subscribe today to purchase this hero with gems!"
+    subscribers_only: "Pouze předplatitelé!"
+    subscribe_unlock: "Předplaťte si pro odemknutí!"
+    subscriber_heroes: "Předplaťte si dnes pro okamžité odemčení Amary, Hushbauma a Hattoriho!"
+    subscriber_gems: "Předplaťte si dnes pro nákup tohoto hrdiny za drahokamy!"
     anonymous: "Anonymní hráč"
     level_difficulty: "Obtížnost: "
 #    play_classroom_version: "Play Classroom Version" # Choose a level in campaign version that you also can play in one of your courses


A few lines translated into Russian (#5557)

A few lines translated into Russian

--- app/locale/ru.coffee
+++ app/locale/ru.coffee
@@ -199,7 +199,7 @@
   modal:
     close: "Закрыть"
     okay: "OK"
-#    cancel: "Cancel"
+    cancel: "Отменить"

   not_found:
     page_not_found: "Страница не найдена"
@@ -1006,12 +1006,12 @@
     level_to_unlock: "Разблокируется на уровне:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
     restricted_to_certain_heroes: "Только определённые герои могут играть на этом уровне."

-#  char_customization_modal:
-#    heading: "Customize Your Hero"
-#    body: "Body"
-#    name_label: "Hero's Name"
-#    hair_label: "Hair Color"
-#    skin_label: "Skin Color"
+  char_customization_modal:
+    heading: "Настроить Вашего Героя"
+    body: "Туловище"
+    name_label: "Имя Героя"
+    hair_label: "Цвет волос"
+    skin_label: "Цвет кожи"

   skill_docs:
     function: "функция" # skill types
@@ -1858,10 +1858,10 @@
 #    gd3_curriculum: "Game Development 3 - Curriculum Guide"
 #    gd3_curriculum_desc: "Lesson plans for Game Development 3."
     wd1: "Веб-разработка 1"
-#    wd1_curriculum: "Web Development 1 - Curriculum Guide"
+    wd1_curriculum: "Веб-разработка 1 - руководство"
 #    wd1_curriculum_desc: "Scope and sequence, lesson plans, activities, and more for Web Development 1."
 #    wd1_headlines: "Headlines & Headers Activity"
-#    wd1_headlines_example: "View sample solution"
+    wd1_headlines_example: "Посмотреть пример решения"
 #    wd1_headlines_desc: "Why are paragraph and header tags important? Use this activity to show how well-chosen headers make web pages easier to read. There are many correct solutions to this!"
     wd1_html_syntax: "Руководство по синтаксису HTML"
 #    wd1_html_syntax_desc: "One-page reference for the HTML style students will learn in Web Development 1."
@@ -1871,7 +1871,7 @@
     wd2_jquery_syntax: "Руководство по синтаксису функций jQuery"
 #    wd2_jquery_syntax_desc: "One-page reference for the jQuery functions students will learn in Web Development 2."
 #    wd2_quizlet_worksheet: "Quizlet Planning Worksheet"
-#    wd2_quizlet_worksheet_instructions: "View instructions & examples"
+    wd2_quizlet_worksheet_instructions: "Посмотреть инструкцию и примеры"
 #    wd2_quizlet_worksheet_desc: "Before your students build their personality quiz project at the end of Web Development 2, they should plan out their quiz questions, outcomes and responses using this worksheet. Teachers can distribute the instructions and examples for students to refer to."
     student_overview: "Обзор"
     student_details: "Сведения о ученике"
@@ -2532,7 +2532,7 @@
     continue_statements: "Оператор \"continue\""
     dom_events: "События DOM"
     dynamic_styling: "Динамические стили"
-#    events: "Events"
+    events: "События"
     event_concurrency: "Параллелизм событий"
     event_data: "Данные событий"
     event_handlers: "Обработчики событий"
@@ -2574,8 +2574,8 @@
     math_trigonometry: "Тригонометрия"
     object_literals: "Литералы объектов"
     parameters: "Параметры"
-#    programs: "Programs"
-#    properties: "Properties"
+    programs: "Программы"
+    properties: "Свойства"
     property_access: "Доступ к свойствам"
     property_assignment: "Назначение свойств"
     property_coordinate: "Свойства координат"


Remove explosion at end

Remove explosion at end

--- app/scripts/lazerscripts/stage1/heli_attack.coffee
+++ app/scripts/lazerscripts/stage1/heli_attack.coffee
@@ -91,7 +91,6 @@
         )
       )
       @endDecoy()
-      @bigExplosion()
     )

   fireRockets: (amount) ->


Little bit of August's translations for UA locale (#5946)

* august update for translate

* Fixed UA locale

Co-authored-by: Valentin Bryukhanov <53545457af642b0f8712bb2cc004f120ce4436f4@gmail.com>

--- app/locale/uk.coffee
+++ app/locale/uk.coffee
@@ -209,7 +209,7 @@
     resource_hub: "Ресурси"
 #    apcsp: "AP CS Principles"
     parent: "Батьки"
-#    browser_recommendation: "For the best experience we recommend using the latest version of Chrome. Download the browser here!"
+    browser_recommendation: "Щоб найкраще все працювало, рекомендумо використовувати останню версію Chrome. Завантажте цій браузер тут!"

   modal:
     close: "Закрити"
@@ -239,10 +239,7 @@
     anon_signup_enter_code: "Введить код класу:"
     anon_signup_ask_teacher: "Ще немає? Запитай у свого вчителя!"
     anon_signup_create_class: "Бажаєте створити клас?"
-#    anon_signup_enter_code: "Enter Class Code:"
-#    anon_signup_ask_teacher: "Don't have one? Ask your teacher!"
-#    anon_signup_create_class: "Want to create a class?"
-#    anon_signup_setup_class: "Set up a class, add your students, and monitor progress!"
+    anon_signup_setup_class: "Налаштуйте клас, дадайте студентів та монітор розвитку!"
     anon_signup_create_teacher: "Створити безкоштовний акаунт вчителя"
     play_as: "Грати за" # Ladder page
     get_course_for_class: "Призначте розробку ігор та інше для ваших класів!"
@@ -747,7 +744,7 @@
     project_published_noty: "Ваш рівень був опублікований!"
     keep_editing: "Продовжуйте Редагувати"
 #    learn_new_concepts: "Learn new concepts"
-#    watch_a_video: "Watch a video on __concept_name__"
+    watch_a_video: "Дивится відео щодо __concept_name__"
 #    concept_unlocked: "Concept Unlocked"
 #    use_at_least_one_concept: "Use at least one concept: "
 #    command_bank: "Command Bank"
@@ -788,14 +785,14 @@
 #    future_eng_5: "program which creates learning and work opportunities for all K-12 students in the United States who wish to pursue computer science."

   live_class:
-#    title: "Дякую!"
+    title: "Дякую!"
     content: "Вражаюче! Ми зараз запустили онлайн-клас."
     link: "Вже готові прямувати до кодування?"

-#  code_quest:
-#    great: "Great!"
+  code_quest:
+    great: "Неймовірно!"
 #    join_paragraph: "Join the largest international Python AI coding tournament for all ages and compete for the top of the leaderboard! This month-long global battle starts August 1st and includes $5k worth of prizes and a virtual awards ceremony where we'll announce winners and recognize your coding skills."
-#    link: "Click here to register and learn more"
+    link: "Тиснить тут щоб зареєструватись та дізнатись більше"
 #    global_tournament: "Global Tournament"
 #    register: "Register"
 #    date: "Aug 1 - Aug 31"
@@ -934,22 +931,22 @@
     support_part1: "Потрібна допомога з опціями оплати? Напишіть на" # {change}
     support_part2: "support@codecombat.com"

-#  announcement:
-#    now_available: "Now available for subscribers!"
-#    subscriber: "subscriber"
+  announcement:
+    now_available: "Тепер доступний для підпісників!"
+    subscriber: "підписник"
 #    cuddly_companions: "Cuddly Companions!" # Pet Announcement Modal
-#    kindling_name: "Kindling Elemental"
-#    kindling_description: "Kindling Elementals just want to keep you warm at night. And during the day. All the time, really."
-#    griffin_name: "Baby Griffin"
-#    griffin_description: "Griffins are half eagle, half lion, all adorable."
-#    raven_name: "Raven"
-#    raven_description: "Ravens are excellent at gathering shiny bottles full of health for you."
-#    mimic_name: "Mimic"
-#    mimic_description: "Mimics can pick up coins for you. Move them on top of coins to increase your gold supply."
-#    cougar_name: "Cougar"
-#    cougar_description: "Cougars like to earn a PhD by Purring Happily Daily."
-#    fox_name: "Blue Fox"
-#    fox_description: "Blue foxes are very clever and love digging in the dirt and snow!"
+    kindling_name: "Палаючий Елементаль"
+    kindling_description: "Палаючі Елементалі хочуть щоб тобі було гарячє вночі. Та й вдень. Та й завжди, курво!"
+    griffin_name: "Малеча Гріфон"
+    griffin_description: "Гріфон то є навпіл молодий орел, навпіл - міцний лев, тобто кішечка. Тому це все - найпречудово."
+    raven_name: "Крук"
+    raven_description: "Крук вміє чудово підбирати все сяючє, особливо пляшечки здоров'я для тебе"
+    mimic_name: "Вдавач"
+    mimic_description: "Вдавач може зібрати монети для тебе. Відправ його то купи монет щоб збільшити твої золоті запаси."
+    cougar_name: "Пума"
+    cougar_description: "Пума обожнюють отримувати вчену ступінь щоденними радісними випробуваннями."
+    fox_name: "Сумна лисичка"
+    fox_description: "Сумні лисички дуже чесні й полюбляють копатися у багнюці та снігу!"
 #    pugicorn_name: "Pugicorn"
 #    pugicorn_description: "Pugicorns are some of the rarest creatures and can cast spells!"
 #    wolf_name: "Wolf Pup"


Fixed #958. LevelLoader wasn't handling cached session objects passed in with the supermodel.

--- app/lib/LevelLoader.coffee
+++ app/lib/LevelLoader.coffee
@@ -59,13 +59,13 @@
       url = "/db/level/#{@levelID}/session"
       url += "?team=#{@team}" if @team

-    @session = new LevelSession().setURL url
-    @supermodel.loadModel(@session, 'level_session', {cache:false})
+    session = new LevelSession().setURL url
+    @session = @supermodel.loadModel(session, 'level_session', {cache:false}).model
     @session.once 'sync', -> @url = -> '/db/level.session/' + @id

     if @opponentSessionID
-      @opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
-      @supermodel.loadModel(@opponentSession, 'opponent_session')
+      opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}"
+      @opponentSession = @supermodel.loadModel(opponentSession, 'opponent_session').model

   # Supermodel (Level) Loading


Fix for buildLoop still trying to loop after LevelLoader destruction for some reason.

--- app/lib/LevelLoader.coffee
+++ app/lib/LevelLoader.coffee
@@ -229,7 +229,7 @@
       res.markLoading()
       @spriteSheetsToBuild.push res

-    @buildLoopInterval = setInterval @buildLoop, 5
+    @buildLoopInterval = setInterval @buildLoop, 5 if @spriteSheetsToBuild.length

   maybeLoadURL: (url, Model, resourceName) ->
     return if @supermodel.getModel(url)


removed self from autobind event args

removed self from autobind event args

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -51,7 +51,7 @@
       if typeof event == "function"
         self.bind(eventName, event)
       else
-        self.bind(eventName, eval( "(function(self) {#{event}})" ))
+        self.bind(eventName, eval( "(function() {#{event}})" ))

   self.trigger('create') unless I.created
   I.created = true


nodes: added THIS

nodes: added THIS

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -9,8 +9,9 @@
 {compact, flatten, merge, del, include, indexOf, starts, ends, last} = require './helpers'

 # Constant functions for nodes that don't need customization.
-YES = -> yes
-NO  = -> no
+YES  = -> yes
+NO   = -> no
+THIS = -> this

 #### BaseNode

@@ -139,7 +140,7 @@
   class:    'BaseNode'
   children: []

-  unwrap          : -> this
+  unwrap          : THIS
   isStatement     : NO
   isPureStatement : NO
   isComplex       : YES
@@ -273,8 +274,7 @@
   constructor: (@expression) ->
     super()

-  makeReturn: ->
-    this
+  makeReturn: THIS

   compile: (o) ->
     expr = @expression.makeReturn()
@@ -408,8 +408,7 @@
   constructor: (@comment) ->
     super()

-  makeReturn: ->
-    this
+  makeReturn: THIS

   compileNode: (o) ->
     @tab + '/*' + @comment.replace(/\n/g, '\n' + @tab) + '*/'
@@ -1283,8 +1282,7 @@
     super()

   # A **ThrowNode** is already a return, of sorts...
-  makeReturn: ->
-    return this
+  makeReturn: THIS

   compileNode: (o) ->
     "#{@tab}throw #{@expression.compile(o)};"