get multiple zooms working

get multiple zooms working

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -32,7 +32,7 @@
     'textarea[name="alt-talk-comment"]' : 'altTalkComment'

   events:
-    'click button[id="toggle-zoom"]'       : 'onToggleZoom'
+    'click button[id="toggle-zoom"]'       : 'onClickZoom'
     'click button[id="toggle-fav"]'        : 'onToggleFav'
     'click button[id="help"]'              : 'onClickHelp'
     'click button[id="tutorial"]'          : 'onClickTutorial'
@@ -48,7 +48,11 @@
   constructor: ->
     super
     window.classifier = @
-    @zoomRange = 15.00
+
+    # zoom levels [days]: 2x, 10x, 20x
+    @zoomRange = 30 # set to first zoom level
+    @zoomRanges = []
+    @zoomLevel = 0
     isZoomed: false
     ifFaved: false

@@ -91,13 +95,14 @@

     $.getJSON jsonFile, (data) =>
       @canvasGraph?.marks.destroyAll()
-
       @marksContainer.appendChild(@canvas)
       @canvasGraph = new CanvasGraph(@canvas, data)
       @canvasGraph.plotPoints()
       @canvasGraph.enableMarking()
       @drawSliderAxisNums()

+      @zoomRanges = [ @canvasGraph.largestX, (@canvasGraph.largestX * 10/@canvasGraph.largestX), @canvasGraph.largestX * 3/@canvasGraph.largestX ]
+
       @el.find("#ui-slider").noUiSlider
         start: 0
         range:
@@ -105,27 +110,33 @@
           "max": @canvasGraph.largestX - @zoomRange

   onChangeScaleSlider: ->
+    console.log 'updating slider'
     val = +@el.find("#ui-slider").val()
     # @focusCenter = +@el.find('#scale-slider').val() + @zoomRange/2
     # xMin = @focusCenter-@zoomRange/2
     # xMax = @focusCenter+@zoomRange/2

-    return unless @isZoomed
-    @canvasGraph.plotPoints(val, (val + @zoomRange))
-
-  onToggleZoom: ->
-    @isZoomed = !@isZoomed
-    if @isZoomed
-      @canvasGraph.zoomInTo(0, @zoomRange)
-      @el.find("#toggle-zoom").addClass("toggled")
-      @el.find("#ui-slider").val(0)
-      @el.find(".noUi-handle").fadeIn(150)
-    else
-      @canvasGraph.zoomOut()
-      @el.find("#toggle-zoom").removeClass("toggled")
-      @el.find("#ui-slider").val(@canvasGraph.smallestX)
-      @el.find(".noUi-handle").fadeOut(150)
-
+    return if @zoomLevel is 0 or @zoomLevel > @zoomRanges.length
+    console.log 'FOO: ', val, ', ', @zoomRanges[@zoomLevel]
+    @canvasGraph.plotPoints( val, val + @zoomRanges[@zoomLevel] )
+
+  onClickZoom: ->
+    @zoomLevel = @zoomLevel + 1
+    if @zoomLevel is 0 or @zoomLevel > @zoomRanges.length-1
+        console.log 'not zoomed'
+        @canvasGraph.zoomOut()
+        @el.find("#toggle-zoom").removeClass("toggled")
+        @el.find("#ui-slider").val(0)
+        @el.find(".noUi-handle").fadeOut(150)
+        @zoomLevel = 0
+      else
+        console.log 'zoom level: ', @zoomLevel
+        console.log 'zoom range: ', @zoomRanges[@zoomLevel]
+        @canvasGraph.zoomInTo(0, @zoomRanges[@zoomLevel])
+        @el.find("#toggle-zoom").addClass("toggled")
+        @el.find("#ui-slider").val(0)
+        @el.find(".noUi-handle").fadeIn(150)
+
   onToggleFav: ->
     favButton = @el.find("#toggle-fav")[0]
     if @isFaved


aram version patch

aram version patch

#fixes 40

--- lib/aram.coffee
+++ lib/aram.coffee
@@ -32,6 +32,7 @@
 ###
 requestData = (step, r) ->
   champs = {}
+  aramVer = r.riotVer.split('.').splice(0, 2).join('.')
   async.eachLimit r.aramChamps, 3, (champ, next) ->
     cl 'Processing ARAM: '+champ

@@ -43,7 +44,7 @@
         return next null

       data.map = '12'
-      data.title = 'ARAM ' + r.riotVer
+      data.title = 'ARAM ' + aramVer
       champs[champ] = {}
       champs[champ].aram = data


a few more changes

a few more changes

--- app/controllers/classifier.coffee
+++ app/controllers/classifier.coffee
@@ -551,16 +551,15 @@
     console.log @Subject.current.metadata.known_objects

     objectsData = @Subject.current.metadata.known_objects
-    for frame in ['0001'] when objectsData[frame] isnt undefined # display only first frame
-      @knownAsteroidMessage.show()
+    for frame, i in ['0001'] when objectsData[frame] isnt undefined # display only first frame
       for knownObject, i in [objectsData[frame]...] when knownObject.good_known #and knownObject.object is '(161969)'
+        @knownAsteroidMessage.show()
         # console.log 'knownObject (',knownObject.x,',',knownObject.y,'): ', knownObject
         radius = 10
         x = Math.round(knownObject.x)/256 * 190
         y = Math.round(knownObject.y)/256 * 190
         for surface in [@markingSurfaceList...]
           surface.addShape 'ellipse', class: "known-asteroid", opacity: 0.75, cx: x, cy: y, rx: radius, ry: radius, fill: "none", stroke: "rgb(20,200,20)", 'stroke-width': 2
-          # surface.addShape 'ellipse', class: "known-asteroid", opacity: 0.75, cx: x_avg, cy: y_avg, rx: radius, ry: radius, fill: "none", stroke: "rgb(20,200,20)", 'stroke-width': 2

     # # console.log @Subject.current.metadata.known_objects
     # for knownAsteroid in @Subject.current.metadata.known_objects


Fixed cpuWeight type

Fixed cpuWeight type

--- app/modules/vm/index.coffee
+++ app/modules/vm/index.coffee
@@ -499,7 +499,7 @@
       if CPUs isnt VM.CPUs.number
         $data.CPUs = +CPUs
       if cpuWeight isnt (VM.cpuWeight)
-        $data.cpuWeight = cpuWeight
+        $data.cpuWeight = +cpuWeight || undefined
       if name_label isnt VM.name_label
         $data.name_label = name_label
       if name_description isnt VM.name_description


Interface creation GUI Fix

Interface creation GUI Fix

--- app/modules/vm/index.coffee
+++ app/modules/vm/index.coffee
@@ -572,6 +572,7 @@

       return xoApi.call 'vm.createInterface', params
       .then (id) ->
+        $scope.creatingVif = false
         # console.log(id)
         xoApi.call 'vif.connect', {id}
       .catch (err) ->


Added and changed available concepts for 1UP levels (#158)

* Added and changed available concepts for 1UP levels

--- app/schemas/concepts.coffee
+++ app/schemas/concepts.coffee
@@ -1,8 +1,15 @@
 # NOTE: 'automatic' is whether rob's auto-tagger can find it.

 concepts = [
+  # Ozaria 1UP
+  {concept: 'basic_syntax', name: 'Basic Syntax', description: 'Using programming language rules.', automatic: true, tagger: '*'}
+  {concept: 'variables', name: 'Variables', description: 'Storing and updating data in variables.', automatic: true, tagger: 'VariableDeclaration'}
+  {concept: 'algorithms', name: 'Algorithms', description: 'Writing sequences of code.', automatic: false}
+  {concept: 'conditionals', name: 'Conditionals', description: 'Writing programs with branched logic.', automatic: false}
+  {concept: 'debugging', name: 'Debugging', description: 'Finding and fixing code errors.', automatic: false}
+
+  # Deprecated
   {concept: 'advanced_strings', name: '', description: '', automatic: '??', deprecated: true}
-  {concept: 'algorithms', name: '', description: '', automatic: false, deprecated: true}
   {concept: 'boolean_logic', name: '', description: '', automatic: '', deprecated: true}
   {concept: 'basic_html', name: '', description: '', automatic: '', deprecated: true}
   {concept: 'basic_css', name: '', description: '', automatic: '', deprecated: true}
@@ -40,7 +47,6 @@
   {concept: 'basic_css_selectors', name: 'Basic CSS selectors', description: 'CSS Selectors introduce in WD1.', automatic: true}
   {concept: 'basic_html_attributes', name: 'Basic HTML Attributes', description: 'HTML Attributes introduced in WD1.', automatic: true}
   {concept: 'basic_html_tags', name: 'Basic HTML Tags', description: 'HTML Tags introduced in WD1.', automatic: true}
-  {concept: 'basic_syntax', name: 'Basic Syntax', description: 'Writing code of any sort.', automatic: true, tagger: '*'}
   {concept: 'binary', name: 'Binary', description: 'Implementations of binary, (0b001100, toString(2))', automatic: 'Sometimes', tagger: (ast) ->
       _.some ast.find('CallExpression.callee>MemberExpression.property>Identifier[name="toString"]'), (n) ->
         call = n.parent.parent
@@ -113,7 +119,6 @@
   {concept: 'strings_concatenation', name: 'String Concatenation', description: 'Using the string concatenation operator +.', automatic: false}
   {concept: 'strings_substrings', name: 'Substring', description: 'Using substr or substring.', automatic: true, tagger: 'CallExpression>MemberExpression>Identifier[name="substr"],CallExpression>MemberExpression>Identifier[name="substring"]'}
   {concept: 'trees', name: 'Data Structures - Trees', description: 'Implementations of the tree data structure.', automatic: false}
-  {concept: 'variables', name: 'Variables', description: 'Storing data in variables.', automatic: true, tagger: 'VariableDeclaration'}
   {concept: 'vectors', name: 'Vectors', description: 'Using Vectors, like new Vector() or common Vector methods like rotate, magnitude, dot, heading, normalize, and limit', automatic: true, tagger: 'Identifier[name=Vector]'}
   {concept: 'while_condition_loops', name: 'While Loops with Conditionals', description: 'While loops with a breaking conditional.', automatic: true, tagger: 'WhileStatement.test>*:not(Literal)'}
   {concept: 'while_loops_simple', name: 'While Loops', description: 'Simple while-true loops.', automatic: true, tagger: 'WhileStatement.test>Literal'}


Working here in the boring train

Working here in the boring train

--- www/modules.coffee
+++ www/modules.coffee
@@ -13,7 +13,7 @@

 	setInterval(window.randomColor, 10000)
-
+

 window.lw_show_ModuleList = ->
 	# Init interface
@@ -30,3 +30,8 @@

#{Name}

"

 	lw_mods.innerHTML += "
Logout
"
+
+	window.if = new window.main_interface
+	window.if.setTitle("Huiiiii")
+
+	window.if.build()


use for loop instead of forEach

use for loop instead of forEach

--- lib/main.coffee
+++ lib/main.coffee
@@ -41,8 +41,7 @@
         atom.notifications.addFatalError(message, options)

     @subscriptions.add atom.commands.add 'core:cancel': ->
-      atom.notifications.getNotifications().forEach (notification) ->
-        notification.dismiss()
+      notification.dismiss() for notification in atom.notifications.getNotifications()

   deactivate: ->
     @subscriptions.dispose()


Made tiles a little bit smaller.

Made tiles a little bit smaller.

--- assets/javascripts/application.coffee
+++ assets/javascripts/application.coffee
@@ -9,7 +9,7 @@

 Dashing.on 'ready', ->
   Dashing.widget_margins ||= [5, 5]
-  Dashing.widget_base_dimensions ||= [450, 420]
+  Dashing.widget_base_dimensions ||= [420, 420]
   Dashing.numColumns ||= 4

   contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns


Fixed User#update_locale to call Travis#updateLocale on locale change

--- assets/scripts/app/models/user.coffee
+++ assets/scripts/app/models/user.coffee
@@ -32,7 +32,7 @@
   ).property()

   updateLocale: (locale) ->
-    Travis.set 'locale', locale
+    Travis.updateLocale(locale)
     @setWithSession('locale', locale)

     transaction = @get('transaction')


Fix merge conflict

Fix merge conflict

--- lib/main.coffee
+++ lib/main.coffee
@@ -58,7 +58,7 @@

         return helpers.exec(atom.config.get('linter-flake8.executablePath'), parameters, stdin: fileText).then (result) ->
           toReturn = []
-          regex = /(\d+):(\d+):\s((E|W|F|C|N|D)\d{2,3})\s+(.*)/g
+          regex = /(\d+):(\d+):\s((E|W|F|C|N|H|D)\d{2,3})\s+(.*)/g

           while (match = regex.exec(result)) isnt null
             line = parseInt(match[1]) or 0


overwork config

overwork config

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,10 +1,21 @@
 # Dependencies
 {exec} = require 'child_process'
+os = require 'os'
+
+# Set platform defaults
+if os.platform() is 'win32'
+  which  = "where"
+else
+  which  = "which"

 module.exports = NslCore =
+  config:
+    pathToJar:
+      title: "Path To JAR"
+      description: "Specify the full path to `nsL.jar`"
+      type: "string"
+      default: ""
   subscriptions: null
-  which: null
-  prefix: null

   activate: (state) ->
     {CompositeDisposable} = require 'atom'
@@ -41,7 +52,7 @@
         exec "\"java\" -jar \"#{nslJar}\" \"#{script}\"", (error, stdout, stderr) ->
           if error isnt null
             # nslJar error from stdout, not error!
-            atom.notifications.addError(script, detail: error, dismissable: true)
+            atom.notifications.addError("**#{script}**", detail: error, dismissable: true)
           else
             atom.notifications.addSuccess("Compiled successfully", detail: stdout, dismissable: false)
     else
@@ -49,22 +60,11 @@
       atom.beep()

   getPath: (callback) ->
-    @getPlatform()
-
+
     # Find Java
-    exec "\"#{@which}\" java", (error, stdout, stderr) ->
+    exec "\"#{which}\" java", (error, stdout, stderr) ->
       if error isnt null
         atom.notifications.addError("**language-nsl**: Java is not in your `PATH` [environmental variable](http://superuser.com/a/284351/195953)", dismissable: true)
       else
         callback stdout
       return
-
-  getPlatform: ->
-    os = require 'os'
-
-    if os.platform() is 'win32'
-      @which  = "where"
-      @prefix = "/"
-    else
-      @which  = "which"
-      @prefix = "-"


Remove no longer needed requires

Remove no longer needed requires

--- lib/main.coffee
+++ lib/main.coffee
@@ -21,9 +21,7 @@
     @subscriptions.dispose()

   provideLinter: ->
-    path = require 'path'
     helpers = require('atom-linter')
-    child_process = require 'child_process'
     provider =
       grammarScopes: ['text.html.php', 'source.php']
       scope: 'file'


:fire: unless destroy

:fire: unless destroy

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

   deactivate: ->
     @subscriptions.dispose()
-    @view?.destroy?()
     {@subscriptions, @view} = {}

   openConfig: ->


2015-05-07T16:22:00.543Z

--- lib/main.coffee
+++ lib/main.coffee
@@ -171,7 +171,6 @@
           "the commit message"
         ]

-
       recursive:

         type: "boolean"


allow user to define async events

allow user to define async events

--- lib/markem.iced
+++ lib/markem.iced
@@ -191,6 +191,11 @@
                 else
                   document.parent.files.push document

+            try
+              markemConf=require path.join process.cwd(),'markem.conf'
+            catch e
+            if markemConf? and markemConf.preRender?
+              await markemConf.preRender globals,defer err

             # render each documents
             for p,document of documents


code cleaning

code cleaning

--- lib/paas.coffee
+++ lib/paas.coffee
@@ -1,5 +1,5 @@
 haibu = require('haibu-api')
-HttpClient = require("../../request-json/main").JsonClient
+HttpClient = require("request-json").JsonClient
 MemoryManager = require("./memory").MemoryManager
 haibuUrl = "http://localhost:9002/"
 controllerClient = new HttpClient haibuUrl


Change the validation message

Change the validation message

--- lib/sifu.coffee
+++ lib/sifu.coffee
@@ -42,7 +42,7 @@
       args = ['validate', '-f', filePath.split(/\//).pop()] # Arguments to javac

       # Execute javac
-      atom.notifications.addInfo("Validating specification... it will take few seconds.")
+      atom.notifications.addInfo("validating specification... it should take a few seconds...")
       helpers.exec("sifu", args, {
         stream: 'stdout',
         cwd: wd,


BaseRepository#getById(): alias for get()

--- src/lib/base-repository.coffee
+++ src/lib/base-repository.coffee
@@ -108,6 +108,20 @@
         client ?= @client
         client.findById(id).then (obj) =>
             return @factory.createFromObject(obj)
+
+
+
+    ###*
+    alias for get()
+
+    @method getById
+    @public
+    @param {any} id
+    @param {ResourceClientInterface} [client=@client]
+    @return {Promise(Entity)} entity
+    ###
+    getById: (id, client) ->
+        @get(id, client)


only use callback if it's not undefined

only use callback if it's not undefined

--- src/scripts/daterangepicker/jquery-wrapper.coffee
+++ src/scripts/daterangepicker/jquery-wrapper.coffee
@@ -10,9 +10,10 @@
     @setupMainView()
     @setupEvents()

-    @view.dateRange.subscribe (newValue) =>
-      [startDate, endDate] = newValue
-      cb(startDate, endDate, @view.period())
+    if cb
+      @view.dateRange.subscribe (newValue) =>
+        [startDate, endDate] = newValue
+        cb(startDate, endDate, @view.period())

   setPosition: () ->
     parentOffset =


Fixed local date and time display in the date and time picker component (fixes #152).

--- src/scripts/models/DateTimePickerViewModel.coffee
+++ src/scripts/models/DateTimePickerViewModel.coffee
@@ -84,7 +84,7 @@
       # make sure we set the UTC Offset back to what the user selected
       @sourceDateTime().utcOffset @zoneValue(), true
       @sourceDateTime().date newMoment.date()
-      @sourceDateTime().month newMoment.utc().month()
+      @sourceDateTime().month newMoment.month()
       @sourceDateTime().year newMoment.year()
       @sourceDateTime.notifySubscribers()
       return


Added safeguard to prevent no dates being passed to Organization#generateReport

--- src/models/organization.coffee
+++ src/models/organization.coffee
@@ -86,11 +86,15 @@
       Logger.log "Project #{name} could not be found"
     generateReport: (start, end) ->
       deferred = Q.defer()
-      if @spreadsheet
-        @spreadsheet.generateReport(@users, start, end)
-        .done((numberDone) -> deferred.resolve(numberDone))
-      else
-        deferred.reject 'Spreadsheet was not loaded, report cannot be generated'
+      if not @spreadsheet
+        deferred.reject 'No spreadsheet is loaded, report cannot be generated'
+        return
+      else if not start or not end
+        deferred.reject 'No start or end date were passed as arguments'
+        return
+      Logger.log "Generating payroll from #{start.format('MMM Do, YYYY')} to #{end.format('MMM Do, YYYY')}"
+      @spreadsheet.generateReport(@users, start, end)
+      .done((numberDone) -> deferred.resolve(numberDone))
       deferred.promise
     resetHounding: () ->
       i = 0


pagination update method

pagination update method

--- src/modules/paginatable.coffee
+++ src/modules/paginatable.coffee
@@ -66,6 +66,10 @@
       collection: @getCollection()
       defaultState: @paginatable

-    @paginationContainer().append( @paginator.render().$el )
+    @paginator

-    @paginator
+  renderPaginationControl: ()->
+    @paginationControl()
+    @paginationContainer().append @paginationControl().render().$el
+
+


スタッフに割り当てたタスクを完了すると、完了リストの一番下に入ってしまうのを修正(一番上に変更) * 未割り当てのタスクは問題なかった

--- app/js/project/staff.js.coffee
+++ app/js/project/staff.js.coffee
@@ -104,7 +104,7 @@
         if !$('#show_trashes_check').attr('checked')
           ui.item.hide()

-        $('#tasks').append(ui.item)
+        $('#tasks').prepend(ui.item)

         unset_staff_new($(this).closest('.staff'))


Modify: soundmanager does not store sounds in mediator anymore

--- app/models/SoundManager.coffee
+++ app/models/SoundManager.coffee
@@ -26,37 +26,14 @@
   Start loading of level. The level to load is determined by the String LEVEL
   @note later change listener based to observer based event system
   ###
-  load: (LEVEL) =>
-    # @initializeSoundMap(LEVEL, mediator.levels[LEVEL].gMap)
-    # mapSounds = mediator.levels[LEVEL].sounds
-    # mediator.levels[LEVEL].soundCount =  mapSounds.sounds.length + mapSounds.backgroundSounds.length
-    # mediator.levels[LEVEL].soundLoadCount = 0
-    @loadSounds LEVEL, mapSounds
+  # load: (LEVEL) =>
+  #   @loadSounds LEVEL, mapSounds

   # ###
   # @param [String]
   # @param [map]
-  # Reads levelX.json file and to all sounds of the level
+  # Initializes soundMap for backgroundSounds
   # ###
-  # initializeSoundMap: (LEVEL, map) =>
-  #   currMapData = map.get 'currMapData'
-  #   mediator.levels[LEVEL].soundMap = []
-  #   for x in [0..map.numXTiles - 1]
-  #     mediator.levels[LEVEL].soundMap[x] = for y in [0..map.numYTiles - 1]
-  #       []
-
-  #   for layer in currMapData.layers
-  #     continue if layer.name isnt 'sound'
-
-  #     for tileID, tileIndex in layer.data
-  #       continue if tileID is 0
-
-  #       x = (tileIndex % map.numXTiles)
-  #       y = Math.floor(tileIndex / map.numXTiles)
-
-  #       mediator.levels[LEVEL].soundMap[x][y].push(layer.properties)
-
-
   getSoundMap: (map) =>
     soundMap = []
     for x in [0..map.width - 1]
@@ -77,30 +54,23 @@
     return soundMap

-  ###
-  @param [String]
-  @param [Array of String]
-  Load all sounds in soundMap
-  ###
-  loadSounds: (LEVEL, mapSounds) =>
+
+  loadSounds: (mapSounds, callback) =>
+    @soundCount = mapSounds.sounds.length + 1 + mapSounds.backgroundSounds.length
+    @soundList = {}
+    @backgroundSounds = {}
+    @themeSound = {}
+
     for sound in mapSounds.sounds
-      mediator.levels[LEVEL].soundList[sound] = new SoundObj
-      mediator.std.xhrGet @PATH+sound+'.mp3', @bufferSounds, 'arraybuffer', sound, mediator.levels[LEVEL].soundList, LEVEL
+      @soundList[sound] = new SoundObj
+      mediator.std.xhrGet mapSounds.prefix+sound, @bufferSounds, 'arraybuffer', sound, @soundList, callback

     for sound in mapSounds.backgroundSounds
-      mediator.levels[LEVEL].backgroundSounds[sound] = new SoundObj
-      mediator.std.xhrGet @PATH+sound+'.mp3', @bufferSounds, 'arraybuffer', sound, mediator.levels[LEVEL].backgroundSounds, LEVEL
+      @backgroundSounds[sound] = new SoundObj
+      mediator.std.xhrGet mapSounds.prefix+sound, @bufferSounds, 'arraybuffer', sound, @backgroundSounds, callback

-
-  loadSounds: (sounds) =>
-
-    for sound in mapSounds.sounds
-      mediator.levels[LEVEL].soundList[sound] = new SoundObj
-      mediator.std.xhrGet @PATH+sound+'.mp3', @bufferSounds, 'arraybuffer', sound, mediator.levels[LEVEL].soundList, LEVEL
-
-    for sound in mapSounds.backgroundSounds
-      mediator.levels[LEVEL].backgroundSounds[sound] = new SoundObj
-      mediator.std.xhrGet @PATH+sound+'.mp3', @bufferSounds, 'arraybuffer', sound, mediator.levels[LEVEL].backgroundSounds, LEVEL
+    @themeSound[mapSounds.theme] = new SoundObj
+    mediator.std.xhrGet mapSounds.prefix+mapSounds.theme, @bufferSounds, 'arraybuffer', mapSounds.theme, @themeSound, callback

@@ -114,17 +84,16 @@
     request = event.target
     sound = request.additionalAttributes[0]
     list = request.additionalAttributes[1]
-    LEVEL = request.additionalAttributes[2]
+    callback = request.additionalAttributes[2]

     buffer = @audioContext.createBuffer(request.response, false)
     list[sound].buffer = buffer

-    mediator.levels[LEVEL].soundLoadCount++
-    console.log sound+'.mp3 loaded'
+    console.log sound+' loaded'

-    if mediator.levels[LEVEL].soundLoadCount == mediator.levels[LEVEL].soundCount
-      console.log 'all sounds loaded'
-      @publishEvent 'soundsLoaded'
+    @soundCount--
+    if @soundCount <= 0
+      callback(@soundList, @backgroundSounds, @themeSound)

   ###
   @param [String]
@@ -166,8 +135,10 @@
       for name, sound of mediator.levels[mediator.activeLevel].soundList
         @stop name, mediator.levels[mediator.activeLevel].soundList

-      for name, sound of mediator.levels[mediator.activeLevel].backgroundSounds
-        @stop name, mediator.levels[mediator.activeLevel].backgroundSounds
+      for name, sound of mediator.levels[mediator.activeLevel].backgroundSoundList
+        @stop name, mediator.levels[mediator.activeLevel].backgroundSoundList
+
+      @stop mediator.activeLevel+'theme.mp3', mediator.levels[mediator.activeLevel].themeSound

     catch e
       console.log e.toString()
@@ -177,8 +148,8 @@
   Start all background sounds in backgroundSound list of active level with gain = 0, i.e. muted
   ###
   startBackgroundSounds: () =>
-    for name, sound of mediator.levels[mediator.activeLevel].backgroundSounds
-      @playSound name, mediator.levels[mediator.activeLevel].backgroundSounds, 0, true
+    for name, sound of mediator.getActiveLevel().backgroundSoundList
+      @playSound name, mediator.getActiveLevel().backgroundSoundList, 0, true
       sound.isPlaying = true

   ###
@@ -189,14 +160,14 @@
   updateBackgroundSounds: (PlayerPosition) =>
     @backgroundSoundsToPlay = []
     # TODO: not really elegant
-    for sound in mediator.levels[mediator.activeLevel].soundMap[Math.floor(PlayerPosition.x/32)][Math.floor(PlayerPosition.y/32)]
+    for sound in mediator.getActiveLevel().soundMap[Math.floor(PlayerPosition.x/32)][Math.floor(PlayerPosition.y/32)]
       # sound.type is the name of the sound here
-      @fade sound.type, mediator.levels[mediator.activeLevel].backgroundSounds, sound.intensity/100
-      @backgroundSoundsToPlay.push(sound.type)
+      @fade sound.type+'.mp3', mediator.getActiveLevel().backgroundSoundList, sound.intensity/100
+      @backgroundSoundsToPlay.push(sound.type+'.mp3')

-    for name, sound of mediator.levels[mediator.activeLevel].backgroundSounds
+    for name, sound of mediator.getActiveLevel().backgroundSoundList
       if @backgroundSoundsToPlay.indexOf(name) == -1
-        @fade name, mediator.levels[mediator.activeLevel].backgroundSounds, 0
+        @fade name, mediator.getActiveLevel().backgroundSoundList, 0

   ###
   @param [String]
@@ -210,7 +181,7 @@

   startAll: =>
-    @playSound(mediator.activeLevel+'theme',mediator.levels[mediator.activeLevel].soundList, 1, true)
+    @playSound(mediator.activeLevel+'theme.mp3',mediator.getActiveLevel().themeSound, 1, true)
     @startBackgroundSounds()
     @updateBackgroundSounds(mediator.player.position)


Remove commas from stats definition in user schema (#5244)

--- app/schemas/models/user.coffee
+++ app/schemas/models/user.coffee
@@ -203,20 +203,18 @@
     courseMiscPatches: c.int()
     courseEdits: c.int()
     concepts: {type: 'object', additionalProperties: c.int(), description: 'Number of levels completed using each programming concept.'}
-
     licenses: c.object {}, {
       usage: c.object {}, {
         used: c.int()
         total: c.int()
       }
-    },
-
+    }
     students: c.object {}, {
       count: c.object {}, {
         active: c.int()
         inactive: c.int()
       }
-    },
+    }

   earned: c.RewardSchema 'earned by achievements'
   purchased: c.RewardSchema 'purchased with gems or money'


added piece finder, cleaned up the king checking code in move func

--- app/scripts/chess/board.coffee
+++ app/scripts/chess/board.coffee
@@ -26,6 +26,14 @@

 	get: (x, y) ->
 		return @board[x][y]
+
+	find: (piece) ->
+		for j in [0..7]
+			for i in [0..7]
+				found = @get i, j
+				if found.isWhite == piece.isWhite && found.constructor.name == piece.constructor.name
+					return [i, j]
+		return [-1, -1]

 	# move 1 to 2
 	move: (from, to) ->
@@ -61,19 +69,13 @@
 		@reset x1, y1

 		# check if the king is in check
-		# first find the king
-		kx = -1
-		ky = -1
-		for j in [0..7]
-			for i in [0..7]
-				np = @get(i,j)
-				if np.isWhite == fromPiece.isWhite and np instanceof King
-					kx = i
-					ky = j
-
+		# first find the king coordinates
+		kC = @find new King fromPiece.isWhite
+		kx = kC[0]
+		ky = kC[1]

 		# now see if he's in check, if so revert the piece movement
-		if kx >= 0 and @inCheck kx, ky, fromPiece.isWhite
+		if kx >=0 and @inCheck kx, ky, fromPiece.isWhite
 			@set x1, y1, fromPiece
 			@set x2, y2, toPiece
 			return false


Refactor regional map/table code

Refactor regional map/table code

--- app/scripts/dcplotsctrl.coffee
+++ app/scripts/dcplotsctrl.coffee
@@ -52,7 +52,6 @@
     dimensions = []
     xfilter = null
     popData = null
-    map = null

     # what value to use for year 0
     YEAR_ZERO=1950
@@ -382,8 +381,8 @@

         dc.dataCount(".dc-data-count").dimension(xfilter).group(all)

-        initDataTable("#dc-data-table", wards)
-        initMap("#wpLocations", wards)
+        loadDataTable("#dc-data-table", wards)
+        loadMap("#wpLocations", wards)

         dc.renderAll()

@@ -529,19 +528,10 @@
           chart.rescale())
         .yAxis().ticks(4)

-    reloadTable = (datatable,dim) ->
-      alldata = dim.top(Infinity)
-      datatable.fnClearTable()
-      datatable.fnAddData(alldata)
-      datatable.fnDraw()
-
-    initDataTable = (selector, dim) ->
+    loadDataTable = (selector, dim) ->
       exists = $.fn.DataTable.fnIsDataTable($(selector))

-      if exists
-        datatable = $(selector).dataTable()
-        reloadTable(datatable,dim)
-      else
+      if !exists
         cols = $scope.fields.map((x) ->
           if x.endsWith("_year")
             mData: x
@@ -569,16 +559,30 @@
           aaData: dim.top(Infinity),
           bDestroy: true,
           aoColumns: cols
+      else
+        datatable = $(selector).dataTable()
+
+      reloadTable = () ->
+        alldata = dim.top(Infinity)
+        datatable.fnClearTable()
+        datatable.fnAddData(alldata)
+        datatable.fnDraw()

       dc.chartRegistry.list().forEach (chart) ->
         chart.on "filtered", () ->
           dc.events.trigger () ->
-            reloadTable(datatable,dim)
-
-    initMap = (container, dim) ->
-      # Have we already initialized the map?
-      if !map
-        id = "regionalDashMap"
+            reloadTable()
+
+      if exists
+        reloadTable()
+
+    # controller level map object
+    map = null
+
+    loadMap = (container, dim) ->
+      id = "regionalDashMap"
+
+      if not $('#' + id).length
         e = $('

')
         $(container).append(e)


remove mock callback

remove mock callback

--- app/scripts/robot/robot.coffee
+++ app/scripts/robot/robot.coffee
@@ -28,12 +28,6 @@
         else # assume object
           _.findWhere(_peripherals, properties)

-
-    radio.onReceive('mock', (channel, update) ->
-      updateLastContact()
-      findPeripheral(-1).update channel, update
-    )
-
     return {
       lastContact: ->
         return _lastContact


Make distance a local method

Make distance a local method

--- src/pane-container-view.coffee
+++ src/pane-container-view.coffee
@@ -112,6 +112,11 @@
     @nearestPaneInDirection('right')?.focus()

   nearestPaneInDirection: (direction) ->
+    distance: (pointA, pointB) ->
+      x = pointB.x - pointA.x
+      y = pointB.y - pointA.y
+      Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
+
     pane = @getActivePane()
     box = @boundingBoxForPane(pane)
     panes = @getPanes()
@@ -126,14 +131,10 @@
         boxA = @boundingBoxForPane(paneA)
         boxB = @boundingBoxForPane(paneB)
         switch direction
-          when 'left'
-            @distanceBetweenPoints(box.left, boxA.right) - @distanceBetweenPoints(box.left, boxB.right)
-          when 'right'
-            @distanceBetweenPoints(box.right, boxA.left) - @distanceBetweenPoints(box.right, boxB.left)
-          when 'above'
-            @distanceBetweenPoints(box.top, boxA.bottom) - @distanceBetweenPoints(box.top, boxB.bottom)
-          when 'below'
-            @distanceBetweenPoints(box.bottom, boxA.top) - @distanceBetweenPoints(box.bottom, boxB.top)
+          when 'left' then @distance(box.left, boxA.right) - @distance(box.left, boxB.right)
+          when 'right' then @distance(box.right, boxA.left) - @distance(box.right, boxB.left)
+          when 'above' then @distance(box.top, boxA.bottom) - @distance(box.top, boxB.bottom)
+          when 'below' then @distance(box.bottom, boxA.top) - @distance(box.bottom, boxB.top)

     panes[0]

@@ -144,8 +145,3 @@
     right: {x: boundingBox.right, y: boundingBox.top}
     top: {x: boundingBox.left, y: boundingBox.top}
     bottom: {x: boundingBox.left, y: boundingBox.bottom}
-
-  distanceBetweenPoints: (pointA, pointB) ->
-    x = pointB.x - pointA.x
-    y = pointB.y - pointA.y
-    Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));


CoffeeScript 1.1.3

CoffeeScript 1.1.3

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -21,7 +21,7 @@
   require.registerExtension '.coffee', (content) -> compile content

 # The current CoffeeScript version number.
-exports.VERSION = '1.1.3-pre'
+exports.VERSION = '1.1.3'

 # Words that cannot be used as identifiers in CoffeeScript code
 exports.RESERVED = RESERVED


Add more detailed logging

Add more detailed logging

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -24,7 +24,7 @@
   next()

 app.use(express.favicon())
-app.use(express.logger(':date - :method :url - :res[content-type]')) if not process.env.SILENT
+app.use(express.logger(':date :remote-addr - :method :url :status :res[content-length] - :response-time ms')) if not process.env.SILENT
 app.set('json spaces', 0) if app.get('env') is 'production'
 app.use(express.compress())
 app.use(express.methodOverride())


Fetch authors and roles for subject comment lists (#3678)

--- app/subjects/comment-list.cjsx
+++ app/subjects/comment-list.cjsx
@@ -1,4 +1,5 @@
 React = require 'react'
+apiClient = require 'panoptes-client/lib/api-client'
 talkClient = require 'panoptes-client/lib/talk-client'
 Comment = require '../talk/comment'
 Paginator = require '../talk/lib/paginator'
@@ -19,6 +20,8 @@

   getInitialState: ->
     meta: { }
+    authors: {}
+    author_roles: {}

   getComments: (page = @props.location.query.page) ->
     query =
@@ -29,8 +32,35 @@
       sort: '-created_at'

     talkClient.type('comments').get(query).then (comments) =>
+        author_ids = []
+        authors = {}
+        author_roles = {}
         meta = comments[0]?.getMeta() or { }
         @setState {comments, meta}
+        comments.map (comment) ->
+          author_ids.push comment.user_id
+        author_ids = author_ids.filter (id, i) -> author_ids.indexOf(id) is i
+
+        apiClient
+          .type 'users'
+          .get
+            id: author_ids
+          .then (users) =>
+            users.map (user) -> authors[user.id] = user
+            @setState {authors}
+
+        talkClient
+          .type 'roles'
+          .get
+            user_id: author_ids
+            section: ['zooniverse', @props.section]
+            is_shown: true
+            page_size: 100
+          .then (roles) =>
+            roles.map (role) ->
+              author_roles[role.user_id] ?= []
+              author_roles[role.user_id].push role
+            @setState {author_roles}

   render: ->
     return <Loading /> unless @state.comments
@@ -40,7 +70,16 @@

Comments:

           {for comment in @state.comments
-            <Comment {...@props} key={"comment-#{comment.id}"} data={comment} locked={true} linked={true} hideFocus={true} />}
+            <Comment
+              {...@props}
+              key={"comment-#{comment.id}"}
+              data={comment}
+              author={@state.authors[comment.user_id]}
+              roles={@state.author_roles[comment.user_id]}
+              locked={true}
+              linked={true}
+              hideFocus={true}
+            />}

         {if +@state.meta.page_count > 1


better sass build - handles multiple manifests

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -96,10 +96,15 @@
     sass:
       options:
         sourcemap: 'inline'
-        loadPath: '<%%= config.app %>/bower_components'
+        loadPath: [ '<%%= config.app %>/bower_components' ]
       server:
-        files:
-          '<%%= config.temp %>/styles/screen.css': [ '<%%= config.app %>/styles/screen.scss' ]<% } %><% if ( stylesLang === 'less' ) { %>
+        files: [
+          expand: true
+          cwd: '<%%= config.app %>/styles'
+          src: '**/*.{scss,sass}'
+          dest: '<%%= config.temp %>/styles'
+          ext: '.css'
+        ]<% } %><% if ( stylesLang === 'less' ) { %>

     less:
       compile:


add whitespace

add whitespace


add live reload, and support for static websites

--- app/templates/gruntfile.coffee
+++ app/templates/gruntfile.coffee
@@ -6,11 +6,11 @@
       dest: 'app'
       static : 'app/static'
       banner: """
-      /*
-        <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("d/m/yyyy") %>
+/*
+  <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("d/m/yyyy") %>

-        Author: <%= pkg.author.name %> - <%= pkg.author.email %>
-      */
+  Author: <%= pkg.author.name %> - <%= pkg.author.email %>
+*/

       """
     src:
@@ -19,18 +19,23 @@
         "src/coffee/*/*.coffee"]
       stylus : [
         "src/stylus/*.styl"]
-      jade   : [
-        "src/jade/app.jade"]
+      jade : "src/jade/web"
       jade_files : [
-        "src/jade/*.jade"]
+        "src/jade/**/*.jade"
+      ]
+
     jade :
       build:
         options:
-          data:
-            debug : true
-          pretty: true
-        files:
-          "<%=meta.dest%>/index.html" : "<%=src.jade%>"
+          data: debug: true
+          pretty : false
+        files : [
+          cwd    : "<%=src.jade%>"
+          src    : "**/*.jade"
+          dest   : "<%=meta.dest%>"
+          expand : true
+          ext    : ".html"
+        ]

     concat :
       coffee :
@@ -40,7 +45,7 @@
     coffee:
       build:
         files:
-          '<%=meta.static%>/js/<%=pkg.name%>.debug.js' : '.tmp/coffee/tmp.coffee'
+          '<%=meta.static%>/js/<%=pkg.name%>.debug.js' :'.tmp/coffee/tmp.coffee'

     stylus:
       tmp:
@@ -54,7 +59,9 @@
         banner: '<%= meta.banner %>'
       build:
         files:
-          '<%=meta.static%>/js/<%=pkg.name%>.js' : ['<%=meta.static%>/js/<%=pkg.name%>.debug.js']
+          '<%=meta.static%>/js/<%=pkg.name%>.js' : [
+            '<%=meta.static%>/js/<%=pkg.name%>.debug.js'
+          ]

     watch :
       main :
@@ -63,11 +70,23 @@
       coffee :
         files : ["<%=src.coffee%>"]
         tasks : ["concat","coffee", "uglify"]
+        options :
+          livereload : true
       stylus :
         files : ["<%=src.stylus%>"]
         tasks : ["stylus"]
+        options :
+          livereload : true
       jade :
         files : ["<%=src.jade%>","<%=src.jade_files%>"]
         tasks : ["jade"]
-
-  grunt.registerTask "default", [ "stylus", "concat", "coffee", "uglify", "jade" ]
+        options :
+          livereload : true
+
+  grunt.registerTask "default", [
+    "stylus"
+    "concat"
+    "coffee"
+    "uglify"
+    "jade"
+  ]


tmp removal of domready

tmp removal of domready

--- src/scripts/coffee/main.coffee
+++ src/scripts/coffee/main.coffee
@@ -18,7 +18,7 @@
       board_size = @board_size

       setTimeout (->
-        require ["app", "domReady!"], (_GoBoard)->
+        require ["app"], (_GoBoard)->
           lol = new _GoBoard(container, container_size, board_size)
           return
       ), 0


Updated slash commands to remove reserved Slack command

--- src/scripts/diagnostics.coffee
+++ src/scripts/diagnostics.coffee
@@ -32,9 +32,9 @@
     return channel is 'ibizan-diagnostics'

   # Org statistics
-  robot.router.post '/ibizan/diagnostics/status', (req, res) ->
+  robot.router.post '/ibizan/diagnostics/info', (req, res) ->
     body = req.body
-    if body.token is process.env.SLASH_STATUS_TOKEN
+    if body.token is process.env.SLASH_INFO_TOKEN
       res.status 200
       response = "#{Organization.name}'s Ibizan has been up since
                   #{Organization.initTime.toDate()}


move function upwards

move function upwards

--- src/opensong.coffee
+++ src/opensong.coffee
@@ -15,6 +15,22 @@

 # displays Opensong
 openSongLyrics = (domElem, lyrics) ->
+
+  replaceHeader = (abbr) ->
+    switch abbr
+      when "C"
+        "Chorus "
+      when "V"
+        "Verse "
+      when "B"
+        "Bridge "
+      when "T"
+        "Tag "
+      when "P"
+        "Pre-Chorus "
+      else
+        abbr
+
   # clear Html Element and add opensong class
   $(domElem).html("").addClass "opensong"

@@ -81,17 +97,3 @@
       else
         console.log "no support for :" + line

-  replaceHeader = (abbr) ->
-    switch abbr
-      when "C"
-        "Chorus "
-      when "V"
-        "Verse "
-      when "B"
-        "Bridge "
-      when "T"
-        "Tag "
-      when "P"
-        "Pre-Chorus "
-      else
-        abbr


fix #1745 -- syncdoc: to_str: Cannot read property 'to_str' of undefined

--- src/smc-util/syncstring.coffee
+++ src/smc-util/syncstring.coffee
@@ -589,15 +589,18 @@
         @_doc = value
         return

+    # Return underlying document, or undefined if document hasn't been set yet.
     get_doc: =>
         return @_doc

+    # Set this doc from its string representation.
     from_str: (value) =>
         @_doc = @_from_str(value)
         return

+    # Return string representation of this doc, or undefined if the doc hasn't been se yet.
     to_str: =>
-        return @_doc.to_str()
+        return @_doc?.to_str()

     # Used for internal debug logging
     dbg: (f) ->


Fix reactified array methods introduced by ObsArray

--- src/reactive.coffee
+++ src/reactive.coffee
@@ -290,7 +290,7 @@
         do (methName) ->
           meth = obj[methName]
           newMeth = (args...) ->
-            res = meth.call(obj, args...)
+            res = meth.call(obj, args...) if meth?
             arr[methName].call(arr, args...)
             res
           spec =


Added Perl to hook handler integration tests.

Tests hook handlers.

--- scripts/test-hook-handlers.coffee
+++ scripts/test-hook-handlers.coffee
@@ -80,6 +80,9 @@
   ,
     name: 'php-hooks-handler'
     repo: 'https://github.com/ddelnano/dredd-hooks-php.git'
+  ,
+    name: 'perl-hooks-handler'
+    repo: 'https://github.com/ungrim97/Dredd-Hooks.git'
 ]

 TRIGGER_KEYWORD = 'tests hook handlers' # inspired by https://help.github.com/articles/closing-issues-via-commit-messages/


zindex: Remove useless code.

zindex: Remove useless code.

--- Oghma/zindex.coffee
+++ Oghma/zindex.coffee
@@ -13,7 +13,6 @@
 # limitations under the License.

 Oghma = @Oghma ?= {}
-Oghma.Thingy ?= {}

 #
 #On local create, call acquire_top_index which provides a z index for the top of the heap.  Then create the thingy with that zindex.  Then call register.


Add unsupported project error

Add unsupported project error

--- services/web/app/coffee/Features/Errors/Errors.coffee
+++ services/web/app/coffee/Features/Errors/Errors.coffee
@@ -33,6 +33,13 @@
 	return error
 UnsupportedFileTypeError.prototype.__proto___ = Error.prototype

+UnsupportedProjectError = (message) ->
+	error = new Error(message)
+	error.name = "UnsupportedProjectError"
+	error.__proto__ = UnsupportedProjectError.prototype
+	return error
+UnsupportedProjectError.prototype.__proto___ = Error.prototype
+
 module.exports = Errors =
 	NotFoundError: NotFoundError
 	ServiceNotConfiguredError: ServiceNotConfiguredError


:new: Add specs for ignoreVCSIgnoredFiles

--- spec/config-spec.coffee
+++ spec/config-spec.coffee
@@ -1,6 +1,8 @@
 describe 'Linter Config', ->
   linter = null
   {getLinter, getMessage} = require('./common')
+  CP = require('child_process')
+  FS = require('fs')
   beforeEach ->
     waitsForPromise ->
       atom.packages.activatePackage('linter').then ->
@@ -29,3 +31,28 @@
       expect(linter.views.bottomContainer.status.count).toBe(0)
       atom.config.set('linter.statusIconScope', 'Project')
       expect(linter.views.bottomContainer.status.count).toBe(1)
+  describe 'ignoreVCSIgnoredFiles', ->
+    it 'ignores the file if its ignored by the VCS', ->
+      repoPath = "/tmp/linter_git_repo"
+      CP.execSync("rm -rf #{repoPath}")
+      CP.execSync("mkdir #{repoPath}")
+      CP.execSync("cd #{repoPath}; git init")
+      FS.writeFileSync("#{repoPath}/.gitignore", "/test.js\n")
+      FS.writeFileSync("#{repoPath}/test.js", "'use strict'\n")
+      atom.config.set('linter.ignoreVCSIgnoredFiles', true)
+      atom.project.addPath(repoPath)
+      linterProvider = getLinter()
+      spyOn(linterProvider, 'lint')
+      linter.addLinter(linterProvider)
+
+      waitsForPromise ->
+        atom.workspace.open("#{repoPath}/test.js").then ->
+          editor = atom.workspace.getActiveTextEditor()
+          editor.insertText("a")
+          editor.save()
+          expect(linterProvider.lint).not.toHaveBeenCalled()
+          atom.config.set('linter.ignoreVCSIgnoredFiles', false)
+          editor.insertText("a")
+          editor.save()
+          expect(linterProvider.lint).toHaveBeenCalled()
+          CP.execSync("rm -rf #{repoPath}")


Guard against missing sender.

Guard against missing sender.

When a tab is closing, Chrome sometimes doesn't set sender.tab.  To
reproduce, navigate (by entering the URLs directly) between www.bing.com
and www.google.com.

Fixes #1630.  Partially.  We still have a space leak.

--- background_scripts/main.coffee
+++ background_scripts/main.coffee
@@ -385,7 +385,8 @@
   chrome.browserAction.setIcon tabId: sender.tab.id, path: path

 handleUpdateScrollPosition = (request, sender) ->
-  updateScrollPosition(sender.tab, request.scrollX, request.scrollY)
+  # See note re. sender.tab at unregisterFrame.
+  updateScrollPosition sender.tab, request.scrollX, request.scrollY if sender.tab?

 updateScrollPosition = (tab, scrollX, scrollY) ->
   tabInfoMap[tab.id].scrollX = scrollX
@@ -598,8 +599,11 @@
   (frameIdsForTab[sender.tab.id] ?= []).push request.frameId

 unregisterFrame = (request, sender) ->
-  tabId = sender.tab.id
-  if frameIdsForTab[tabId]?
+  # When a tab is closing, Chrome sometimes passes on messages without sender.tab.  Therefore, we guard
+  # against this.
+  # FIXME(smblott) Consequently, we have a space leak in frameIdsForTab, tabInfoMap and urlForTab.
+  tabId = sender.tab?.id
+  if tabId? and frameIdsForTab[tabId]?
     if request.tab_is_closing
       updateOpenTabs sender.tab, true
     else


:art: improve spec helper to support capital letter with control combination e.g. Ctrl-R

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -69,12 +69,11 @@
       element.value += key
   dispatchKeyboardEvent(element, 'keyup', eventArgs...)

-_keystroke = (keys, {element}) ->
+_keystroke = (keys, event) ->
   if keys is 'escape'
-    keydown keys, {element}
+    keydown keys, event
   else
     for key in keys.split('')
-      event = {element}
       event.shift = true if key.match(/[A-Z]/)
       keydown key, event

@@ -352,8 +351,8 @@
             {editor, editorElement} = @vimState.searchInput.view
             editor.insertText(k.search)
             atom.commands.dispatch(editorElement, 'core:confirm')
-          when k.ctrl?  then keydown k.ctrl, {ctrl: true, element}
-          when k.raw?   then keydown k.raw, {raw: true, element}
+          when k.ctrl? then _keystroke(k.ctrl, {ctrl: true, element})
+          when k.raw? then _keystroke(k.raw, {raw: true, element})
     if mocked
       unmockPlatform(element)


[➠] Minor formatting.

[➠] Minor formatting.

--- vendor/assets/javascripts/jquery/plugins/detector.js.coffee
+++ vendor/assets/javascripts/jquery/plugins/detector.js.coffee
@@ -1,5 +1,6 @@
 # This is the manifest for “detector.js”.

 #= require ./browser
+
 #= require ./detector/detector
 #= require ./detector/plugin


LESS -> Less

LESS -> Less

--- build/tasks/prebuild-less-task.coffee
+++ build/tasks/prebuild-less-task.coffee
@@ -20,7 +20,7 @@
     rm(bootstrapLessPath)
     rm(path.join(appDir, 'node_modules', 'bootstrap', 'less'))

-  grunt.registerMultiTask 'prebuild-less', 'Prebuild cached of compiled LESS files', ->
+  grunt.registerMultiTask 'prebuild-less', 'Prebuild cached of compiled Less files', ->
     compileBootstrap()

     prebuiltConfigurations = [


add basic history management to debug console

--- scripts/console.coffee
+++ scripts/console.coffee
@@ -21,25 +21,66 @@
   obj

 class Console
+  KEY_RETURN = 13
+  KEY_LEFT   = 37
+  KEY_UP     = 38
+  KEY_RIGHT  = 39
+  KEY_DOWN   = 40
+
   constructor: (root, buffer, input) ->
     @root   = $(root)
     @buffer = if buffer? then $(buffer) else $('.console-buffer', @root)
     @input  = if input?  then $(input)  else $('.console-input',  @root)

-    @input.keypress (event) => @processInput() if event.charCode is 13
+    @history = [ ]
+    @history.limit = 100
+    @history.index = -1
+
+    @input.keydown (event) => @handleKey(event)
+
+  handleKey: (event) ->
+    if event.altKey
+      switch event.which
+        when KEY_UP   then @selectHistory +1; event.preventDefault()
+        when KEY_DOWN then @selectHistory -1; event.preventDefault()
+    else
+      switch event.which
+        when KEY_RETURN then @processInput(); event.preventDefault()

   processInput: ->
     try
       @dump CoffeeScript.eval @input.val(), bare: true
+      @pushHistory()
       @clearInput()
     catch ex
       @error ex
+      @input.select()

     @scrollToBottom()
     return

   clearInput: ->
     @input.val null
+    return
+
+  pushHistory: (command) ->
+    command ?= @input.val()
+    @history.unshift command
+    @history.pop() while @history.length > @history.limit
+    return
+
+  selectHistory: (offset) ->
+    index = @history.index + offset
+    console.log index
+    if index >= @history.length
+      undefined # TODO play donk sound or something
+    else if index < 0
+      @clearInput()
+      @history.index = -1
+    else
+      @history.index = index
+      command = @history[index]
+      @input.val(command).select()
     return

   scrollToBottom: ->


Include name in image listing

Include name in image listing

--- scripts/pudding.coffee
+++ scripts/pudding.coffee
@@ -252,9 +252,9 @@

 format_image = (image) ->
   if image.active
-    sprintf("%(image_id)s:  %(role)s (ACTIVE)\n", image)
+    sprintf("%(image_id)s: role=%(role)s\t%(name)s (ACTIVE)\n", image)
   else
-    sprintf("%(image_id)s:  %(role)s\n", image)
+    sprintf("%(image_id)s: role=%(role)s\t%(name)s\n", image)

 send_images_list_cb = (msg) ->
   return (err, images) ->


send message with empty log, refs #427

send message with empty log, refs #427

--- server/lib/logs.coffee
+++ server/lib/logs.coffee
@@ -37,7 +37,7 @@
             # missing
             fs.readFile logPath, (err, logContent) ->
                 content = logContent?.toString()
-                logContents[slug] = content.replace logs.colors, ''
+                logContents[slug] = content?.replace logs.colors, ''

                 next()
         , ->


Ensure tmp/ exists before running tests

Ensure tmp/ exists before running tests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -36,6 +36,8 @@
   grunt.loadNpmTasks 'grunt-mocha-cli'

   grunt.registerTask 'build', ['coffee']
-  grunt.registerTask 'test', ['mochacli', 'clean']
+
+  grunt.registerTask 'mktmpdir', -> grunt.file.mkdir 'tmp'
+  grunt.registerTask 'test', ['mktmpdir', 'mochacli', 'clean']

   grunt.registerTask 'default', ['build', 'test']


remove from default task

remove from default task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -258,13 +258,11 @@
   # Define custom composite tasks in terms of other tasks
   grunt.registerTask 'default', [
     'newer:jshint'
-    'mocha'
     'jst'
     'newer:stylus'
     'bake'
     'copy'
     'sails-linker'
-    'newer:rsync'
   ]
   grunt.registerTask 'release', ['jshint', 'uglify', 'concat']
   grunt.registerTask 'test', ['mocha']


Update Gruntfile to say Droplet instead of Ice Editor

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,7 +80,7 @@
     cssmin:
       options:
         banner: '''
-           /* ICE Editor stylesheet.
+           /* Droplet stylesheet.
            Copyright (c) 2014 Anthony Bau.
            MIT License.
            */'''
@@ -92,7 +92,7 @@
     concat:
       options:
         banner: '''
-           /* ICE Editor.
+           /* Droplet.
            Copyright (c) 2014 Anthony Bau.
            MIT License.
            */
@@ -109,8 +109,12 @@

     uglify:
       options:
-        banner: '/*! <%= pkg.name %> ' +
-                '<%= grunt.template.today("yyyy-mm-dd") %> */\n'
+        banner: '''
+          /* Droplet.
+          Copyright (c) 2014 Anthony Bau.
+          MIT License.
+          */
+        '''
         sourceMap: true

       build:


Adjust sauce tests

Adjust sauce tests

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -131,8 +131,6 @@

               # OS X 10.9
               ["OS X 10.9", "safari", "7"]
-              ["OS X 10.9", "chrome", "33"]
-              ["OS X 10.9", "firefox", "28"]

               # Linux
               ["Linux", "chrome", "38"]
@@ -141,13 +139,9 @@

               # Win 8.1
               ["Windows 8.1", "internet explorer", "11"]
-              ["Windows 8.1", "chrome", "38"]
-              ["Windows 8.1", "firefox", "33"]

               # Win 8
               ["Windows 8", "internet explorer", "10"]
-              ["Windows 8", "chrome", "38"]
-              ["Windows 8", "firefox", "33"]

               # Win 7
               ["Windows 7", "internet explorer", "9"]
@@ -157,9 +151,8 @@

               # Win XP
               ["Windows XP", "internet explorer", "8"]
-              ["Windows XP", "opera", "11"]
             ])
-            url: "https://s3.eu-central-1.amazonaws.com/infeedl/infeedl-js/jasmine/_SpecRunner.html"
+            url: "http://s3.eu-central-1.amazonaws.com/infeedl/infeedl-js/jasmine/_SpecRunner.html"
             framework: "jasmine"
             name: "Infeedl JS"
             concurrency: 5


Grunt: Run build target on test

Grunt: Run build target on test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -70,5 +70,5 @@

   # Our local tasks
   @registerTask 'build', ['noflo_manifest', 'noflo_browser', 'uglify']
-  @registerTask 'test', ['coffeelint', 'cafemocha']
+  @registerTask 'test', ['coffeelint', 'build', 'cafemocha']
   @registerTask 'default', ['test']


Enable tests to absolutely fail

Enable tests to absolutely fail

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,25 +29,25 @@
         src: '<%=yeoman.dist %>/forty-datetime.js'
         dest: '<%=yeoman.dist %>/forty-datetime.min.js'

-    # TODO Add testing
-    # mochaTest:
-    #   test:
-    #     options:
-    #       reporter: 'spec'
-    #       compilers: 'coffee:coffee-script'
-    #     src: ['test/**/*.coffee']
-    #
-    # karma:
-    #   options:
-    #     configFile: 'config/karma-conf.js'
-    #   unit:
-    #     singleRun: true
-    #
-    #
-    # grunt.loadNpmTasks('grunt-karma')
+    #TODO Add testing
+    mochaTest:
+       test:
+         options:
+           reporter: 'spec'
+           compilers: 'coffee:coffee-script'
+         src: ['test/**/*.coffee']
+
+    karma:
+       options:
+         configFile: 'config/karma-conf.js'
+       unit:
+         singleRun: true
+
+
+    grunt.loadNpmTasks('grunt-karma')

     grunt.registerTask 'default', [
-      #'mochaTest'
+      'mochaTest'
       'coffee'
       'uglify'
     ]


cleaning template gen task

cleaning template gen task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -65,14 +65,9 @@

   grunt.registerTask(
     'templates',
-    'Generate a coffeescript files containing the html templates.',
-    ->
-      tmpl = "templates =\n"
-      for f in glob.sync(html_sources)
-        name = (path.basename(f, '.html'))
-        content = fs.readFileSync(f)
-        tmpl += "  #{name}: \"\"\"#{content}\"\"\"\n"
-      fs.writeFileSync(templates_source, "templates =\n#{tmpl}\n")
+    'Generate a coffeescript files containing the html templates.', ->
+      tmpl = glob.sync(html_sources).map (f) -> "  #{path.basename(f, '.html')}: \"\"\"#{fs.readFileSync(f)}\"\"\"\n"
+      fs.writeFileSync(templates_source, "templates =\n" + tmpl.join('\n'))
       grunt.log.writeln("File #{templates_source} created.")
     )


Don't test for cyclomatic complexity

Don't test for cyclomatic complexity

The cyclomatic complexity test for coffeelint is currently wildly incorrect,
for example reporting a complexity of 22 for editable.coffee (actual complexity
of 9).

Therefore, remove this test until it becomes more accurate.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -96,9 +96,6 @@
         max_line_length:
           level: 'error'
           value: 120
-        cyclomatic_complexity:
-          level: 'error'
-          value: 22

       source: ['src/**/*.coffee']
       grunt: 'Gruntfile.coffee'


put back missing html files

put back missing html files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -135,6 +135,8 @@
 # Mapping of HTML files to the scripts they require
 dependencies =
   'site/index.html': [scripts.main]
+  'site/graphs.html': [libs.d3, scripts.graphs]
+  'site/settings.html': [scripts.settings]
   'site/start_job.html': [libs.blueimp, libs.bbs, scripts.start_job]
   'site/define_form.html': [scripts.define_form]
   'site/view_jobs.html': [scripts.view_jobs]


Separated out pushing the repo and pushing the tag

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -63,7 +63,9 @@
       tag_release:
         command: "git tag -a #{version_tag()} -m 'Version #{version()}'"
       push_repo:
-        commmand: "git push; git push --tags"
+        commmand: "git push"
+      push_tags:
+        commmand: "git push --tags"
         options:
           callback: (err, stdout, stderr, cb) ->
             if err
@@ -85,7 +87,7 @@
   grunt.registerTask 'build', ['coffee', 'concat', 'uglify']

   grunt.registerTask 'release', 'build, tag the current release, and push', () ->
-    grunt.task.run ['shell:with_clean_repo', 'shell:without_existing_tag', 'build', 'package_jquery', 'shell:tag_release', 'shell:push_repo']
+    grunt.task.run ['shell:with_clean_repo', 'shell:without_existing_tag', 'build', 'package_jquery', 'shell:tag_release', 'shell:push_repo', 'shell:push_tags']

   grunt.registerTask 'package_jquery', 'Generate a jquery.json manifest file from package.json', () =>
     src = "package.json"


Fix `karma:watch` task.

Fix `karma:watch` task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -196,6 +196,8 @@
         singleRun: true # `false` for debugging

       watch:
+        configFile: "<%= appConfig.test %>/karma.conf.coffee"
+        reporters: ["dots"]
         singleRun: false
         autoWatch: true


Add bump Grunt task.

Add bump Grunt task.

`grunt bump:patch/minor/major/build`

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -28,6 +28,14 @@
           layout: (type, component) -> type # Just the file.
           targetDir: './lib'
           verbose: yes
+    bump:
+      options:
+        files: [
+          'bower.json'
+          'package.json'
+        ]
+        commitFiles: ['.']
+        pushTo: 'origin'
     clean:
       dist: [
         'dist/*'


Fake data in tasks

Fake data in tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -21,6 +21,12 @@
             options:
                 compress: yes
             docs:
+                options:
+                    data: # TMP
+                        name: "demo test"
+                        id: "test"
+                        doc: "
here's the docs !
"
+                        demo: "
here's the demo !
"
                 files:
                     "docs/demo.html": "_docs/_pages/demo.jade" # TODO : change this
         copy:


Corrige tarefa de dist do grunt

Corrige tarefa de dist do grunt

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,7 +22,10 @@
 		environmentType: process.env['ENVIRONMENT_TYPE'] or 'stable'
 		versionName: -> [grunt.config('acronym'), grunt.config('environmentName'), grunt.config('buildNumber'),
 										 grunt.config('environmentType')].join('-')
-		clean: ['build']
+		clean:
+			main: ['build']
+			dist: ['build', 'dist']
+
 		copy:
 			main:
 				expand: true
@@ -51,16 +54,6 @@
 				cwd: 'spec/'
 				src: ['**', '!**/*.coffee']
 				dest: 'build/<%= relativePath %>/spec/'
-
-			dist:
-				expand: true
-				flatten: true
-				cwd: 'build/<%= relativePath %>/'
-				src: [
-					'js/vtex-i18n.js', 'js/vtex-i18n.min.js',
-					'js/vtex-locale-selector.js', 'js/vtex-locale-selector.min.js'
-				]
-				dest: 'dist/'

 		coffee:
 			main:
@@ -77,6 +70,13 @@
 				dest: 'build/<%= relativePath %>/spec/'
 				ext: '.js'

+			dist:
+				expand: true
+				cwd: 'src/coffee'
+				src: ['vtex-i18n.coffee', 'vtex-locale-selector.coffee']
+				dest: 'dist/<%= relativePath %>/'
+				ext: '.js'
+
 		less:
 			main:
 				files:
@@ -91,8 +91,8 @@
 		uglify:
 			dist:
 				files:
-					'build/js/vtex-i18n.min.js': ['build/js/vtex-i18n.js']
-					'build/js/vtex-locale-selector.min.js': ['build/js/vtex-locale-selector.js']
+					'dist/vtex-i18n.min.js': ['dist/vtex-i18n.js']
+					'dist/vtex-locale-selector.min.js': ['dist/vtex-locale-selector.js']

 		karma:
 			options:
@@ -186,7 +186,7 @@
 	grunt.registerTask 'tdd', ['dev', 'connect', 'remote', 'karma:unit', 'watch:test']

 	# Dist
-	grunt.registerTask 'dist', ['prod', 'copy:dist']
+	grunt.registerTask 'dist', ['clean', 'coffee:dist', 'uglify:dist']

 	# Tasks for deploy build
 	grunt.registerTask 'gen-commit', ['clean', 'copy:main', 'copy:test', 'coffee', 'less', 'copy:debug',


release fix

release fix

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -67,6 +67,7 @@
             if stdout.split("\n").indexOf( version_tag() ) >= 0
               throw 'This tag has already been committed to the repo.'
             else
+              grunt.task.run 'shell:tag_release'
               cb()

       tag_release:


require coffee:compile before publishing

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,4 +15,4 @@
       bucketName: 'ember-test'

     'npm-publish'
-      require: 'coffee'
+      require: 'coffee:compile'


putting the love back in js

putting the love back in js

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -71,6 +71,11 @@
 				'./.temp/'
 				'./dist/'
 			]
+			# Used for those that desire plain old JavaScript
+			jslove: [
+				'**/*.coffee'
+				'!**/node_modules/**'
+			]

 		# Compiles CoffeeScript (.coffee) files to JavaScript (.js)
 		coffee:
@@ -82,6 +87,18 @@
 				ext: '.js'
 				options:
 					sourceMap: true
+			# Used for those that desire plain old JavaScript
+			jslove:
+				files: [
+					cwd: './'
+					src: [
+						'**/*.coffee'
+						'!**/node_modules/**'
+					]
+					dest: './'
+					expand: true
+					ext: '.js'
+				]

 		# Lints CoffeeScript files
 		coffeelint:
@@ -461,9 +478,9 @@
 	# grunt build
 	grunt.registerTask 'build', [
 		'coffeelint'
-		'clean'
+		'clean:working'
 		'copy:app'
-		'coffee'
+		'coffee:app'
 		'template:dev'
 		'less'
 		'jade'
@@ -500,9 +517,9 @@
 	# grunt prod
 	grunt.registerTask 'prod', [
 		'coffeelint'
-		'clean'
+		'clean:working'
 		'copy:app'
-		'coffee'
+		'coffee:app'
 		'imagemin'
 		'hash:images'
 		'template:styles'


grunt dev runs test at start

grunt dev runs test at start

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -177,7 +177,7 @@
             # Linting according to http://contribute.jquery.org/style-guide/js/
             jshintrc: ".jshintrc"
         beforeConcat: [
-            "Gruntfile.js"
+            # "Gruntfile.js"
             "src/*.js"
             "3rd-party/**/jquery.fancytree.*.js"
             "test/unit/*.js"
@@ -289,14 +289,13 @@
                 "build/<%= pkg.name %>-all.min.js": ["<%= concat.all.dest %>"]

     watch:
-        # options:
-        #     atBegin: true
         less:
             files: "src/**/*.less"
             tasks: ["less:development"]
         jshint:
-            atBegin: true
-            files: "src/*.js"
+            options:
+                atBegin: true
+            files: ["src/*.js", "test/unit/*.js"]
             tasks: ["jshint:beforeConcat"]

   # ----------------------------------------------------------------------------


Remove non-banner comments from CSS

Remove non-banner comments from CSS

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -59,6 +59,7 @@
     cssmin:
       options:
         banner: '<%= meta.banner %>'
+        keepSpecialComments: 0
       compress:
         files:
           'static/css/styles.min.css' : ['css/bootstrap.css', 'css/isvalid.css'],


Add nuking of build artifacts

Add nuking of build artifacts

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -18,10 +18,15 @@
     exec:
       nuke_main:
         command: 'rm -rf ./components/*/'
+      nuke_main_built:
+        command: 'rm -rf ./browser'
       nuke_bower:
         command: 'rm -rf ./bower_components/*'
       nuke_preview:
         command: 'rm -rf ./components/*/'
+        cwd: 'preview'
+      nuke_preview_built:
+        command: 'rm -rf ./browser'
         cwd: 'preview'
       bower_install:
         command: './node_modules/.bin/bower install'
@@ -172,7 +177,7 @@
   @loadNpmTasks 'grunt-lint-inline'

   # Our local tasks
-  @registerTask 'nuke', ['exec:nuke_main', 'exec:nuke_bower', 'exec:nuke_preview']
+  @registerTask 'nuke', ['exec:nuke_main', 'exec:nuke_bower', 'exec:nuke_preview', 'exec:nuke_main_built', 'exec:nuke_preview_built']
   @registerTask 'build', ['exec:main_install', 'exec:bower_install', 'exec:main_build', 'exec:preview_install', 'exec:preview_build']
   @registerTask 'main_build', ['exec:main_install', 'exec:bower_install', 'exec:main_build']
   @registerTask 'main_rebuild', ['exec:nuke_main', 'exec:nuke_bower', 'main_build']


FIX: remove comment in Gruntfile

FIX: remove comment in Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -72,5 +72,4 @@
   grunt.loadNpmTasks 'grunt-contrib-jasmine'
   grunt.loadNpmTasks 'grunt-umd'

-  # grunt.registerTask 'default', ['sass', 'coffee', 'umd', 'jasmine', 'watch']
-  grunt.registerTask 'default', ['sass', 'coffee', 'umd', 'watch']
+  grunt.registerTask 'default', ['sass', 'coffee', 'umd', 'jasmine', 'watch']


Use built-in underscore from grunt.util

Use built-in underscore from grunt.util

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,7 +1,6 @@
 {spawn} = require 'child_process'
 fs = require 'fs'
 path = require 'path'
-_ = require 'underscore'

 BUILD_DIR = '/tmp/atom-build/atom-shell'
 APP_NAME = 'Atom.app'
@@ -11,10 +10,10 @@

 module.exports = (grunt) ->
   exec = (command, args, options, callback) ->
-    if _.isFunction(args)
+    if grunt.util._.isFunction(args)
       options = args
       args = []
-    if _.isFunction(options)
+    if grunt.util._.isFunction(options)
       callback = options
       options = undefined


fix($Gruntfile.coffee): Accept connections from non-local hosts

Ensure that the node.js connect test servers can accept connections
from any IP address (not just localhost). This enables testing on
virtual machines.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -335,11 +335,13 @@
             root_server:
                 options:
                     base: "."
+                    hostname: "0.0.0.0"
                     port: 9000
                     keepalive: true
             test_server:
                 options:
                     base: "test"
+                    hostname: "0.0.0.0"
                     port: 9001

         # Watching for changes


Separate resources and mac segments before joining

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -223,7 +223,7 @@
     cp 'static', path.join(APP_DIR, 'static'), filter: /.+\.less$/
     cp 'themes', path.join(APP_DIR, 'themes'), filter: /.+\.(cson|less)$/

-    grunt.file.recurse path.join('resources/mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
+    grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) ->
       unless /.+\.plist/.test(sourcePath)
         grunt.file.copy(sourcePath, path.resolve(APP_DIR, '..', subDirectory, filename))


Rename jade to pug.

Rename jade to pug.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -53,7 +53,7 @@
         expand: true
         ext: '.html'
         cwd: 'app/'
-        src: ['**/*.jade', '!**/include/**']
+        src: ['**/*.pug', '!**/include/**']
         dest: 'public/'

     markdown:
@@ -146,7 +146,7 @@
       options:
         logConcurrentOutput: true
       compile:
-        tasks: ['coffee', 'jade', 'markdown', 'stylus']
+        tasks: ['coffee', 'pug', 'markdown', 'stylus']

     cssmin:
       options:
@@ -240,9 +240,9 @@
       javascript:
         files: ['app/**/*.js']
         tasks: ['copy:js']
-      jade:
-        files: ['app/**/*.jade', 'test/**/*.jade']
-        tasks: ['jade']
+      pug:
+        files: ['app/**/*.pug', 'test/**/*.pug']
+        tasks: ['pug']
       stylus:
         files: ['app/**/*.styl']
         tasks: ['stylus']


fix gruntfile

fix gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,7 +15,7 @@
       main:
         files: [
             expand: true
-            cwd: 'src/'
+            cwd: 'scripts/'
             src: ['**/*.js']
             dest: 'out/'
         ]


Add clean:all, fix an issue with building ace (build was before bower)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -38,6 +38,7 @@

     clean:
       app: ['public']
+      all: ['public', 'bower_components', 'tmp']

     testem:
       basic:
@@ -214,7 +215,7 @@
   grunt.registerTask 'build-scripts', ['browserify:app']

   grunt.registerTask 'default', ['build']
-  grunt.registerTask 'build', ['clean', 'copy', 'bower', 'uglify:vendor', 'build-scripts', 'build-style', 'modernizr']
+  grunt.registerTask 'build', ['clean:app', 'bower', 'copy', 'uglify:vendor', 'build-scripts', 'build-style', 'modernizr']
   grunt.registerTask 'minify', ['build', 'uglify:app', 'cssmin']

   grunt.registerTask 'dev', ['checkDatabase', 'express:dev', 'build', 'watch']
@@ -223,4 +224,4 @@

   grunt.registerTask 'test:server', ['build', 'shell:mocha']
   grunt.registerTask 'test:client', ['browserify:tests', 'testem:ci:basic']
-  grunt.registerTask 'test', ['test:server', 'test:client']
+  grunt.registerTask 'test', ['clean:all', 'test:server', 'test:client']


Ignore folders that are called "Bundle"...

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -173,7 +173,8 @@

           # prepend bundle if needed
           for chunk in chunks
-            if chunk.indexOf('Bundle') != -1
+            index = chunk.indexOf('Bundle')
+            if index != -1 && index != 0
               newPathName = chunk.toLowerCase() + '.' + newPathName

           dest + newPathName


chat: do not override options object/prevent property shadowing

--- client/Social/Activity/views/privatemessage/inputwidget.coffee
+++ client/Social/Activity/views/privatemessage/inputwidget.coffee
@@ -1,18 +1,18 @@
 class PrivateMessageInputWidget extends ReplyInputWidget

-  create: ({body, clientRequestId}, callback) ->
+  create: (options = {}, callback) ->

     form = @getOption 'form'

     {autoComplete, inputs: {purpose}} = form

-    body       = @input.getValue()
-    purpose    = purpose.getValue()
-    recipients = (nickname for {profile: {nickname}} in autoComplete.getSelectedItemData())
+    options.body       = @input.getValue()
+    options.purpose    = purpose.getValue()
+    options.recipients = (nickname for {profile: {nickname}} in autoComplete.getSelectedItemData())

     {router, socialapi, windowController} = KD.singletons

-    socialapi.message.initPrivateMessage {body, recipients, purpose}, (err, channels) =>
+    socialapi.message.initPrivateMessage options, (err, channels) =>

       return KD.showError err  if err


AdminInvitationsView: Create Invite Someone button.

--- client/admin/lib/views/invitations/admininvitationsview.coffee
+++ client/admin/lib/views/invitations/admininvitationsview.coffee
@@ -1,6 +1,7 @@
 kd                      = require 'kd'
 KDView                  = kd.View
 KDTabView               = kd.TabView
+KDButtonView            = kd.ButtonView
 KDTabPaneView           = kd.TabPaneView
 InviteSomeoneView       = require './invitesomeoneview'
 PendingInvitationsView  = require './pendinginvitationsview'
@@ -36,3 +37,12 @@
     invite.addSubView   inviteView = new InviteSomeoneView {}, data

     tabView.showPaneByIndex 0
+
+  createInviteButton: ->
+
+    @addSubView @inviteButton = new KDButtonView
+      title    : 'INVITE SOMEONE'
+      cssClass : 'solid compact green invite'
+      callback : =>
+        @tabView.showPaneByName 'Invite'
+        @inviteButton.hide()


utilities/apitokens: add copy button

utilities/apitokens: add copy button

--- client/home/lib/utilities/components/apiaccess/apitoken.coffee
+++ client/home/lib/utilities/components/apiaccess/apitoken.coffee
@@ -5,12 +5,17 @@
 TeamFlux = require 'app/flux/teams'
 showError = require 'app/util/showError'
 remote = require('app/remote').getInstance()
+copyToClipboard = require 'app/util/copyToClipboard'

 module.exports = class ApiToken extends React.Component

   constructor: (props) ->

     super props
+
+  copyApiToken: ->
+
+    copyToClipboard @refs.token

   deleteApiToken: ->

@@ -33,7 +38,6 @@
               modal.destroy()

-
   render: ->

     { code, createdAt, username } = @props.apiToken.toJS()
@@ -44,16 +48,20 @@

-

+

           {code}

           {createdAt} by {username}

+      <CopyButton callback={@bound 'copyApiToken'} />
       <DeleteButton callback={@bound 'deleteApiToken'}/>

 DeleteButton = ({ callback }) ->
   DELETE
+
+CopyButton = ({ callback }) ->
+  COPY


Nav: removed top right links

Nav: removed top right links

--- client/landing/site.hackathon/coffee/core/topnavigation.coffee
+++ client/landing/site.hackathon/coffee/core/topnavigation.coffee
@@ -2,11 +2,8 @@

 module.exports = class TopNavigation extends KDCustomHTMLView

-  menu = [
-    { title : 'Koding University', href : 'http://learn.koding.com', name : 'about'}
-    { title : 'Features',          href : '/Features',               name : 'features'}
-    { title : 'SIGN IN',           href : '/Login',                  name : 'login'}
-  ]
+  menu = []
+
   if KD.userAccount.type isnt 'registered'
     menu.push
       title : 'SIGN IN'


Remove debug breakpoint

Remove debug breakpoint

--- app/src/wallet/transaction.coffee
+++ app/src/wallet/transaction.coffee
@@ -208,7 +208,6 @@
       @_isValidated = yes
       tx = ledger.bitcoin.decodeTransaction(@getSignedTransaction())
       $info("Raw TX: ", @getSignedTransaction())
-      debugger
       ledger.wallet.pathsToAddresses [@changePath], (addresses) =>
         changeAddress = _(addresses).chain().values().first().value()
         result = ledger.bitcoin.verifyRawTx(@getSignedTransaction(), @inputs, @amount, @fees, @recipientAddress, changeAddress)


Remove all Kite caches before logout

Remove all Kite caches before logout

--- client/Core/maincontroller.coffee
+++ client/Core/maincontroller.coffee
@@ -177,6 +177,8 @@
       mainView._logoutAnimation()
       KD.singletons.localSync.removeLocalContents()

+      KiteCache.clearAll()
+
       Cookies.expire "koding082014"
       Cookies.expire "useOldKoding"


Client:Login: add a separate handler for Reset route

--- client/Login/AppController.coffee
+++ client/Login/AppController.coffee
@@ -1,6 +1,14 @@
 class LoginAppsController extends AppController

   handler = (callback)->-> KD.singleton('appManager').open 'Login', callback
+
+  handleResetRoute = ({params:{token}})->
+    do handler (app)=>
+      if KD.isLoggedIn()
+        KD.getSingleton('router').handleRoute "/Account/Profile?focus=password&token=#{token}"
+      else
+        app.getView().setCustomDataToForm('reset', {recoveryToken:token})
+        app.getView().animateToForm('reset')

   KD.registerAppClass this,
     name                        : "Login"
@@ -9,11 +17,7 @@
       '/:name?/Redeem'          : handler (app)-> app.getView().animateToForm 'redeem'
       '/:name?/Register'        : handler (app)-> app.getView().animateToForm 'register'
       '/:name?/Recover'         : handler (app)-> app.getView().animateToForm 'recover'
-      '/:name?/Reset/:token'    : ({params:{token}})->
-        do handler (app)=>
-          app.getView().setCustomDataToForm('reset', {recoveryToken:token})
-          app.getView().animateToForm('reset')
-
+      '/:name?/Reset/:token'    : handleResetRoute
       '/:name?/ResendToken'     : handler (app)-> app.getView().animateToForm 'resendEmail'
     hiddenHandle                : yes
     behavior                    : 'application'


Verification: add email verification back into routers

--- client/Login/AppController.coffee
+++ client/Login/AppController.coffee
@@ -18,6 +18,15 @@
         app.getView().setCustomDataToForm('reset', {recoveryToken:token})
         app.getView().animateToForm('reset')

+  handleVerifyRoute = ({params:{token}})->
+    KD.singleton('appManager').open 'Login', (app) ->
+      if token
+        KD.remote.api.JPasswordRecovery.validate token, (err, isValid)=>
+          if err and err.short isnt 'redeemed_token'
+            KD.notify_ err.message
+          else if isValid
+            KD.notify_ "Thanks for confirming your email address"
+            KD.getSingleton('router').handleRoute "/Login"
   handleRedeemRoute = ({params:{name, token}})->
     token = decodeURIComponent token
     KD.remote.api.JInvitation.byCode token, (err, invite)->
@@ -78,6 +87,7 @@
       '/:name?/Reset'            : handler (app)-> app.getView().animateToForm 'reset'
       '/:name?/Reset/:token'     : handleResetRoute
       '/:name?/Confirm/:token'   : handleResetRoute
+      '/:name?/Verify/:token'    : handleVerifyRoute
       '/:name?/Redeem/:token'    : handleRedeemRoute
       '/:name?/ResendToken'      : handler (app)-> app.getView().animateToForm 'resendEmail'
     hiddenHandle                 : yes


Use confirmation dialog for expunge

Use confirmation dialog for expunge

--- client/app/components/menu.coffee
+++ client/app/components/menu.coffee
@@ -52,7 +52,7 @@
         onlyFavorites: true

     getStateFromStores: ->
-            isDrawerExpanded: LayoutStore.isDrawerExpanded()
+        isDrawerExpanded: LayoutStore.isDrawerExpanded()

     componentWillReceiveProps: (props) ->
         if not Immutable.is(props.selectedAccount, @props.selectedAccount)
@@ -266,7 +266,7 @@
                             'data-tooltip-direction': 'right'

                 if nbUnread > 0 and not progress
-                        span className: 'badge', nbUnread
+                    span className: 'badge', nbUnread

             if isSelected
                 ul
@@ -430,12 +430,12 @@

         e.preventDefault()

-        if window.confirm(t 'account confirm delbox')
+        doExpunge = ->
             mailbox =
                 accountID: accountID
                 mailboxID: mailboxID

-            AccountActionCreator.mailboxExpunge mailbox, (error) =>
+            AccountActionCreator.mailboxExpunge mailbox, (error) ->
                 if error?
                     # if user hasn't switched to another box, refresh display
                     if accountID is mailbox.accountID and
@@ -449,3 +449,14 @@
                 else
                     LayoutActionCreator.notify t("mailbox expunge ok"),
                         autoclose: true
+            LayoutActionCreator.hideModal()
+
+        modal =
+            title       : t 'account confirm delbox'
+            subtitle    : t 'account confirm delbox'
+            closeModal  : ->
+                LayoutActionCreator.hideModal()
+            closeLabel  : t 'app cancel'
+            actionLabel : t 'app confirm'
+            action      : doExpunge
+        LayoutActionCreator.displayModal modal


Fixed: typo in property name. event.controlKey should be event.ctrlKey

--- client/app/components/task.coffee
+++ client/app/components/task.coffee
@@ -117,9 +117,8 @@
     onKeyDown: (event) ->
         node = @refs['task-content'].getDOMNode()
         key = event.keyCode or event.charCode
-        ctrlPressed = event.controlKey or event.metaKey
-        comboKeyPressed = event.metaKey or event.controlKey or event.altKey
-
+        ctrlPressed = event.ctrlKey or event.metaKey
+        comboKeyPressed = event.metaKey or event.ctrlKey or event.altKey
         # neutral keys shouldn't add the tags list
         # backspace, space, tab, enter, top/bottom/left/right arrows
         neutralKeys = [


Fixed: 'done?' and 'todo?' were inverted. Close #23

--- client/app/components/task.coffee
+++ client/app/components/task.coffee
@@ -64,9 +64,9 @@
             if @state.buttonHover and isArchived
                 buttonText = t 'restore button?'
             else if @state.buttonHover and isDone
+                buttonText = t 'todo button?'
+            else if @state.buttonHover and not isDone
                 buttonText = t 'done button?'
-            else if @state.buttonHover and not isDone
-                buttonText = t 'todo button?'
             else if not @state.buttonHover and isDone
                 buttonText = t 'done button'
             else if not @state.buttonHover and not isDone


Mock out storage.

Mock out storage.

--- spec/about-spec.coffee
+++ spec/about-spec.coffee
@@ -49,6 +49,10 @@
   workspaceElement = null

   beforeEach ->
+    storage = {}
+    spyOn(localStorage, 'setItem').andCallFake (key, value) -> storage[key] = value
+    spyOn(localStorage, 'getItem').andCallFake (key) -> storage[key]
+
     spyOn(atom, 'isReleasedVersion').andReturn(true)

     workspaceElement = atom.views.getView(atom.workspace)


Fix spec desc

Fix spec desc

--- spec/about-spec.coffee
+++ spec/about-spec.coffee
@@ -79,7 +79,7 @@
         expect(aboutElement.querySelector('.app-up-to-date')).toBeVisible()
         expect(aboutElement.querySelector('.app-checking-for-updates')).not.toBeVisible()

-      it "shows the correct panels when the app checks for updates and an update is downloaded", ->
+      it "shows the correct panels and button states when the app checks for updates and an update is downloaded", ->
         expect(aboutElement.querySelector('.about-default-update-message')).toBeVisible()
         expect(aboutElement.querySelector('.about-update-action-button').disabled).toBe false
         expect(aboutElement.querySelector('.about-update-action-button').textContent).toBe 'Check for update'


Client: Dashboard: remove unused MemberAdded listener

--- client/dashboard/lib/index.coffee
+++ client/dashboard/lib/index.coffee
@@ -111,13 +111,6 @@

   membersViewAdded: (pane, view) ->
     group = view.getData()
-    # pane.on 'PaneDidShow', ->
-    #   view.refresh()  if pane.tabHandle.isDirty
-    #   pane.tabHandle.markDirty no
-    group.on 'MemberAdded', ->
-      kd.log 'MemberAdded'
-      # {tabHandle} = pane
-      # tabHandle.markDirty()

   loadSection: ({title}) ->
     @getView().nav.ready =>


Admin: Pistachio is good.

Admin: Pistachio is good.

I love pistachio.

--- client/admin/lib/views/integrations/adminconfiguredintegrationitemview.coffee
+++ client/admin/lib/views/integrations/adminconfiguredintegrationitemview.coffee
@@ -101,12 +101,12 @@

   pistachio: ->
-    data = @getData()
-    { title, summary, iconPath } = @getData().integration
+
+    { integration } = @getData()

     return """
-
-
#{title}

-      #{summary}
+
+      {p{ #(integration.title)}}
+      {span{ #(integration.summary)}}
       {{> @button}}
     """


Unsets the current user after the API call has succeeded

--- src/client/auth.coffee
+++ src/client/auth.coffee
@@ -32,10 +32,10 @@
     # @returns {Promise}
     # @TODO Misses a `dfd.fail`
     logout = (callback=->)->
-      app.core.setCurrentUser(false)
       api = app.sandbox.data.api;
       dfd = api('logout')
       dfd.done ->
+        app.core.setCurrentUser(false)
         api.model('me').clear()
         api.model('me').trigger('change')
         api.model.clearAll()


Try Out Quality Types

Try Out Quality Types

--- src/coffee/game.coffee
+++ src/coffee/game.coffee
@@ -1,18 +1,23 @@
 angular.module 'gameDefinition', ['qbn.edsl']
   .run (qbnEdsl) ->
-    {quality} = qbnEdsl
+    {quality, qualityType} = qbnEdsl

-    quality 'punchiness',
+    quality 'punchiness', qualityType.stat,
       'Punchiness'
       'One\'s capability for punching.'
       value: 2

-    quality 'deathWish',
+    quality 'deathWish', qualityType.unique,
       'Death Wish'
       'Has existence become a burden?'
       value: false

-    quality 'luck',
+    quality 'unsingableSong', qualityType.item,
+      'Unsingable Song'
+      'Because it is terrible or because it is forbidden?'
+      value: 3
+
+    quality 'luck', qualityType.stat,
       'Luck'
       'This text should never display.'
       value: -2


Fixes #311: -f can load package.json files

--- src/commandline.coffee
+++ src/commandline.coffee
@@ -190,6 +190,11 @@
     unless options.argv.noconfig
         if options.argv.f
             config = JSON.parse read options.argv.f
+
+            # If -f was specifying a package.json, extract the config
+            if config.coffeelintConfig
+                config =  config.coffeelintConfig
+
         else if (process.env.COFFEELINT_CONFIG and
         fs.existsSync(process.env.COFFEELINT_CONFIG))
             config = JSON.parse(read(process.env.COFFEELINT_CONFIG))


修正SubView装载多个JS或CSS时无法触发回调的BUG

修正SubView装载多个JS或CSS时无法触发回调的BUG

--- src/core/loader.coffee
+++ src/core/loader.coffee
@@ -80,6 +80,7 @@
 	context.suspendedInitFuncs = []

 	if htmlUrl
+
 		_loadHtml(targetDom, htmlUrl, context, {
 			complete: (success, result) ->
 				resourceLoadCallback(success, result, htmlUrl)


Consistent behavior across all browsers for zero-length lines: don't draw them.

--- src/core/shapes.coffee
+++ src/core/shapes.coffee
@@ -211,6 +211,10 @@
     @dash = args.dash or null

   draw: (ctx) ->
+    if @x1 == @x2 and @y1 == @y2
+      # browser behavior is not consistent for this case.
+      return
+
     ctx.lineWidth = @strokeWidth
     ctx.strokeStyle = @color
     ctx.lineCap = @capStyle


add isOrganizer-helper

add isOrganizer-helper

--- client.coffee
+++ client.coffee
@@ -24,4 +24,5 @@
   Template.foodrun.helpers
     isOpen: () ->
       this.state == "ongoing"
-
+    isOrganizer: () ->
+      this.organizer == Meteor.user()


Change repo used

Change repo used

--- buzzer.coffee
+++ buzzer.coffee
@@ -1,6 +1,6 @@
 gpio = require('gpio')
 Events = require('events').EventEmitter
-Cylon = require('cylon')
+Cylon = require('cylon-raspi')

 module.exports = class Buzzer extends Events
   interval: 200


Don’t implement ::isActive in terms of the theorist model behavior

--- src/pane.coffee
+++ src/pane.coffee
@@ -117,7 +117,8 @@
   onWillDestroyItem: (fn) ->
     @emitter.on 'will-destroy-item', fn

-  isActive: -> @active
+  isActive: ->
+    @container?.getActivePane() is this

   # Called by the view layer to indicate that the pane has gained focus.
   focus: ->


Terminal: scrollToBottom on tab changes

Terminal: scrollToBottom on tab changes

--- client/Terminal/webtermappview.coffee
+++ client/Terminal/webtermappview.coffee
@@ -121,6 +121,7 @@
   handleQuery:(query)->
     pane = @tabView.getActivePane()
     {webTermView} = pane.getOptions()
+    webTermView.terminal?.scrollToBottom()
     webTermView.once 'WebTermConnected', (remote)=>

       if query.command


update UTCOffsets ref

update UTCOffsets ref

--- client/controllers/sourceModal.coffee
+++ client/controllers/sourceModal.coffee
@@ -175,7 +175,7 @@
           daylightSavings = daylightSavings and moment.utc(
             date.year() + "-11-01") >= date
           tz = if daylightSavings then "EDT" else "EST"
-          date = date.utcOffset(utils.UTCOffsets[tz])
+          date = date.utcOffset(UTCOffsets[tz])
           templateInstance.$("#publishDateTZ").val(tz)
           templateInstance.$('#publishDate').data("DateTimePicker").date(date)
           templateInstance.$('#publishTime').data("DateTimePicker").date(date)


full basic account management

full basic account management

--- client/views/settings/settings.coffee
+++ client/views/settings/settings.coffee
@@ -1,2 +1,15 @@
 Template.settings.rendered = ->
   IonModal.open "signInModal" if not Meteor.loggingIn() and not Meteor.user()
+
+Template.settings.helpers
+  userEmail: ->
+    Meteor.user().emails[0].address
+
+Template.settings.events
+  'click #logout-button': ->
+    Meteor.logout()
+
+  'click #login-create-button': ->
+    IonModal.open('signInModal')
+
+


take out more logging

take out more logging

--- client/views/needs/listing.coffee
+++ client/views/needs/listing.coffee
@@ -16,7 +16,6 @@
       Needs.findOne({_id: @_id}).starUsers.length > 0 and
       Meteor.user()._id in Needs.findOne({_id: @_id}).starUsers
   hasBackground: ->
-    console.log @_id
     Needs.findOne({_id: @_id}) && Needs.findOne({_id: @_id}).backgroundId
   backgroundTitle: ->
     if Needs.findOne({_id: @_id}).backgroundId != null


Use `unless` instead of `if not`

Use `unless` instead of `if not`

--- coffeescript/rails.validations.coffee
+++ coffeescript/rails.validations.coffee
@@ -380,8 +380,7 @@
                     .removeData('notLocallyUnique')
                     .data('changed', true)

-          if(!valid)
-            return options.message
+          return options.message unless valid

   remote:
     uniqueness: (element, options) ->


Fix lint errors

Fix lint errors

--- spec/atom-environment-spec.coffee
+++ spec/atom-environment-spec.coffee
@@ -230,8 +230,8 @@
       atomEnv.initialize({
         window: {
           requestIdleCallback: (callback) -> idleCallbacks.push(callback),
-          addEventListener: () ->
-          removeEventListener: () ->
+          addEventListener: ->
+          removeEventListener: ->
         },
         document: document.implementation.createHTMLDocument()
       })
@@ -263,8 +263,8 @@
       atomEnv.initialize({
         window: {
           requestIdleCallback: (callback) -> idleCallbacks.push(callback),
-          addEventListener: () ->
-          removeEventListener: () ->
+          addEventListener: ->
+          removeEventListener: ->
         },
         document: document.implementation.createHTMLDocument()
       })


Adjust indentation to make coffeescript happy

--- cms/static/coffee/src/main.coffee
+++ cms/static/coffee/src/main.coffee
@@ -19,15 +19,15 @@
     if ajaxSettings.notifyOnError is false
       return
     if jqXHR.responseText
-        try
-          message = JSON.parse(jqXHR.responseText).error
-        catch
-          message = _.str.truncate(jqXHR.responseText, 300)
+      try
+        message = JSON.parse(jqXHR.responseText).error
+      catch
+        message = _.str.truncate(jqXHR.responseText, 300)
     else
-        message = gettext("This may be happening because of an error with our server or your internet connection. Try refreshing the page or making sure you are online.")
+      message = gettext("This may be happening because of an error with our server or your internet connection. Try refreshing the page or making sure you are online.")
     msg = new CMS.Views.Notification.Error(
-        "title": gettext("Studio's having trouble saving your work")
-        "message": message
+      "title": gettext("Studio's having trouble saving your work")
+      "message": message
     )
     msg.show()


Fixed: Esc key propagation stopped by implied return https://github.com/harvesthq/chosen/issues/390

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -91,6 +91,7 @@
         this.result_select(evt) if this.results_showing
       when 27
         this.results_hide() if @results_showing
+        return true
       when 9, 38, 40, 16, 91, 17
         # don't do anything on these keys
       else this.results_search()


Clear current item when returning to event list page

--- src/core/javascripts/controllers/event_list.js.coffee
+++ src/core/javascripts/controllers/event_list.js.coffee
@@ -63,12 +63,11 @@
     # has the event group been manually set (i.e. in the step before)
     $scope.event_group_manually_set = if !$scope.event_group_manually_set? and $scope.current_item.event_group? then true else false

-    # clear event data unless in summary mode
+    # clear current item unless in summary mode
     if $scope.current_item.event and $scope.mode != 0
-      delete $scope.current_item.event
-      delete $scope.current_item.event_chain
-      delete $scope.current_item.event_group if !$scope.event_group_manually_set
-      delete $scope.current_item.tickets
+      event_group = $scope.current_item.event_group
+      $scope.clearBasketItem()
+      $scope.current_item.setEventGroup(event_group) if $scope.event_group_manually_set

     promises = []


Event list: fix showDay logic to allow showing today's events

--- src/core/javascripts/controllers/event_list.js.coffee
+++ src/core/javascripts/controllers/event_list.js.coffee
@@ -277,7 +277,8 @@
       $scope.end_date = moment(date)
       $scope.loadEventData()
     else
-      new_date = date if !date.isSame($scope.selected_date, 'day')
+      new_date = date if !$scope.selected_date or !date.isSame($scope.selected_date, 'day')
+      console.log($scope.selected_date)

     if new_date
       $scope.selected_date = new_date


Return a *real* promise so that we can then/catch

--- apps/artwork_2/lib/inquire.coffee
+++ apps/artwork_2/lib/inquire.coffee
@@ -1,3 +1,4 @@
+Promise = require 'bluebird-q'
 User = require '../../../models/user.coffee'
 Artwork = require '../../../models/artwork.coffee'
 ArtworkInquiry = require '../../../models/artwork_inquiry.coffee'
@@ -8,8 +9,9 @@
   inquiry = new ArtworkInquiry notification_delay: 600
   artwork = new Artwork id: id

-  artwork.fetch().then ->
-    openInquiryQuestionnaireFor
-      user: user
-      artwork: artwork
-      inquiry: inquiry
+  Promise artwork.fetch()
+    .then ->
+      openInquiryQuestionnaireFor
+        user: user
+        artwork: artwork
+        inquiry: inquiry


Fix bug in the callback

Fix bug in the callback

--- lms/static/coffee/src/peer_grading/peer_grading_problem.coffee
+++ lms/static/coffee/src/peer_grading/peer_grading_problem.coffee
@@ -427,7 +427,7 @@

   setup_score_selection: (max_score) =>
     # And now hook up an event handler again
-    $("input[name='score-selection']").change @graded_callback
+    $("input[class='score-selection']").change @graded_callback


Fix the edit message (#6086)

Fix the edit message (#6086)

--- packages/rocketchat-ui/lib/chatMessages.coffee
+++ packages/rocketchat-ui/lib/chatMessages.coffee
@@ -341,10 +341,7 @@
 				if not event.shiftKey and not event.ctrlKey and not event.altKey and not event.metaKey # Enter without shift/ctrl/alt
 					event.preventDefault()
 					event.stopPropagation()
-					if this.editing.id
-						this.update(this.editing.id, rid, input)
-					else
-						this.send(rid, input)
+					this.send(rid, input)
 					return
 				else if not event.shiftKey
 					return input.value +='\n'
@@ -352,10 +349,7 @@
 				if event.shiftKey or event.ctrlKey or event.altKey or event.metaKey # Enter with shift/ctrl/alt
 					event.preventDefault()
 					event.stopPropagation()
-					if this.editing.id
-						this.update(this.editing.id, rid, input)
-					else
-						this.send(rid, input)
+					this.send(rid, input)
 					return


Write unit tests for postprocessor. Test type resolving.

--- test/postprocessor.test.coffee
+++ test/postprocessor.test.coffee
@@ -302,6 +302,48 @@

     describe 'types', ->
+        classesDefinition = null
+
+        beforeEach ->
+            classesDefinition = postprocessor.postprocess([
+                {
+                    type: 'object'
+                    rule: 'definition'
+                    title: 'Tweet'
+                    rules: [
+                        rule: 'attribute'
+                        attribute:
+                            name: 'author'
+                            type:
+                                type: 'object'
+                                title: 'User'
+                    ]
+                },
+                {
+                    type: 'object'
+                    rule: 'definition'
+                    title: 'User'
+                    rules: [
+                        rule: 'attribute'
+                        attribute:
+                            name: 'age'
+                            type:
+                                type: 'object'
+                                title: 'Integer'
+                    ]
+                }
+            ])
+
+        it 'should resolve defined types', ->
+            expect(classesDefinition.definitions).has.property('Tweet')
+            expect(classesDefinition.definitions).has.property('User')
+            expect(classesDefinition.definitions.Tweet.properties.author).has.property('$ref', '#/definitions/User')
+
+        it 'should specify unresolvable types directly', ->
+            expect(classesDefinition.definitions.User.properties.age).has.property('type', 'Integer')
+
+        it 'should collect unresolvable types in special hash', ->
+            expect(classesDefinition.unresolvedTypes).to.contain('Integer')

     describe 'meta definitions', ->


Bug Fix: Cannot select last battle.

Bug Fix: Cannot select last battle.

--- views/option-area.cjsx
+++ views/option-area.cjsx
@@ -18,16 +18,19 @@
   handleSelectPacket: ->
     index = parseInt(@refs.selectedIndex.getValue())
     return if index is NaN
-    if index == -1
+    # Use 'default' when selected packet out of range
+    if index < 0
       packet = @props.battlePackets[0]
       @props.toggleAutoShow true
+      @setState
+        selectedPacket: null
+      @props.updateBattleDetail packet
     else
       packet = @props.battlePackets[index]
       @props.toggleAutoShow false
-
-    @setState
-      selectedPacket: packet
-    @props.updateBattleDetail packet
+      @setState
+        selectedPacket: packet
+      @props.updateBattleDetail packet

   handleClickExport: ->
     isSuccessful = true


fixed coffeelint errors on workers/social/lib/social/models/log/index.coffee

--- workers/social/lib/social/models/log/index.coffee
+++ workers/social/lib/social/models/log/index.coffee
@@ -23,11 +23,11 @@
       ip            :
         type        : String
         required    : yes
-        default     : "127.0.0.1"
+        default     : '127.0.0.1'
       username      :
         type        : String
         required    : yes
-        default     : "guest"
+        default     : 'guest'
       createdAt     :
         type        : Date
         default     : -> new Date
@@ -42,7 +42,7 @@
         required    : no

-  @log = (data, callback = ->) ->
+  @log = (data, callback = -> ) ->

     log = new JLog data
     log.save (err) -> callback err
@@ -76,9 +76,9 @@
         head.push i
       return head

-    results = headUntil results, (el)-> el.success is true
+    results = headUntil results, (el) -> el.success is true
     # we dont need anything older than 5 minutes
-    results = headUntil results, (el)-> ((Date.now() - el.createdAt) / 1000 / 60) > TIME_LIMIT_IN_MIN
+    results = headUntil results, (el) -> ((Date.now() - el.createdAt) / 1000 / 60) > TIME_LIMIT_IN_MIN
     # if items length is lt CHECK_LIMIT return true
     callback results.length < TRY_LIMIT_FOR_BLOCKING

@@ -97,8 +97,8 @@
           sort        :
             createdAt : -1

-        JLog.some { username }, options, (err, results)->
-          checkRestrictions err, results, (res)->
+        JLog.some { username }, options, (err, results) ->
+          checkRestrictions err, results, (res) ->
             unless res then return callback res
             queue.next()

@@ -108,3 +108,5 @@
     ]

     daisy queue
+
+


bound: improve detection

bound: improve detection

--- core/KDObject.coffee
+++ core/KDObject.coffee
@@ -21,7 +21,8 @@
     @once 'ready', => @readyState = READY

   bound: (method)->
-    throw new Error "@bound: unknown method! #{method}"  unless @[method]?
+    unless 'function' is typeof @[method]
+      throw new Error "bound: unknown method! #{method}"
     boundMethod = "__bound__#{method}"
     boundMethod of this or Object.defineProperty(
       this, boundMethod, value: @[method].bind this