remove unnecessary parenthesis and such

remove unnecessary parenthesis and such

--- test/bacon.model.spec.coffee
+++ test/bacon.model.spec.coffee
@@ -73,32 +73,26 @@
     expect(values).to.deep.equal([1, 2, 4, 8])
   it "Applies given async stream of functions to the Model", (done) ->
     b = lib.Model(1)
-    b.onValue((val) ->
-      if (val == 2) then done()
-    )
-    b.apply(Bacon.fromBinder((sink) ->
+    b.onValue (val) ->
+      if val == 2 then done()
+    b.apply Bacon.fromBinder (sink) ->
       setTimeout((->
         sink(twice)
       ), 500)
       ->
-    ))
   it "Applies given node-stream stream of functions to the Model", (done) ->
     b = lib.Model(1)
-    b.onValue((val) ->
-      if (val == 3) then done()
-    )
-    b.apply(Bacon.fromBinder((sink) ->
+    b.onValue (val) ->
+      if val == 3 then done()
+    b.apply Bacon.fromBinder (sink) ->
       S = require('stream').Transform
       s = new S(objectMode: true)
-      s.on('readable', ->
-        mult = parseInt(s.read())
-        f = (x) -> x * mult
-        sink(f)
-      )
+      s.on 'readable', ->
+        mult = parseInt s.read()
+        sink((x) -> x * mult)
       s.push(3)
       s.push(null)
       ->
-    ))

 describe "Model.addSource", ->
   it "connects new input stream", ->


revert travis info

revert travis info

--- test/bin/test-command.coffee
+++ test/bin/test-command.coffee
@@ -9,9 +9,8 @@
   it 'with defaults', (done) ->

     cwd = process.cwd()
-    moduleOnlyDir = resolve 'test', 'helpers', 'module-only'
-    console.log "cwd = [#{cwd}]  chdir = [#{moduleOnlyDir}]"
-    process.chdir moduleOnlyDir
+
+    process.chdir join 'test', 'helpers', 'module-only'

     check = (error) ->
       if error? then return done error


better comment for preloading changes

better comment for preloading changes

--- src/javascripts/player.coffee
+++ src/javascripts/player.coffee
@@ -142,9 +142,11 @@

   play: () ->
     return unless @media.paused
-    # set src on first playback
+
+    # set src on first playback to prevent IE from preloading the audio file
     unless @media.src
       @media.src = @src
+
     if @media.readyState < 2 # can play current position
       @app.theme.addLoadingClass()
     if @media.readyState < 1 # has metadata available


remove crossOrigin attribute from audio tag

this prevents playing audio files from other domains

fixes #20

--- src/javascripts/player.coffee
+++ src/javascripts/player.coffee
@@ -9,7 +9,6 @@
     self = this
     self.media = elem
     self.media.preload = "metadata"
-    self.media.crossOrigin = true
     @loadFile()
     @setInitialTime()
     @setCurrentTime()


add "preserve last search" config specs

add "preserve last search" config specs

--- spec/fuzzy-finder-spec.coffee
+++ spec/fuzzy-finder-spec.coffee
@@ -679,6 +679,33 @@
           expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor1.getPath()
           expect(atom.workspace.getActiveTextEditor().getCursorBufferPosition()).toEqual [3, 4]

+  describe "Preserve last search", ->
+    it "does not preserve last search by default", ->
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe true
+      projectView.filterEditorView.getModel().insertText('this should not show up next time we open finder')
+
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe false
+
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe true
+      expect(projectView.filterEditorView.getText()).toBe ''
+
+    it "preserves last search when config is set", ->
+      atom.config.set("fuzzy-finder.preserveLastSearch", true)
+
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe true
+      projectView.filterEditorView.getModel().insertText('this should show up next time we open finder')
+
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe false
+
+      dispatchCommand('toggle-file-finder')
+      expect(atom.workspace.panelForItem(projectView).isVisible()).toBe true
+      expect(projectView.filterEditorView.getText()).toBe 'this should show up next time we open finder'
+
   describe "Git integration", ->
     [projectPath, gitRepository, gitDirectory] = []


guard against possibly missing API fields

--- src/foursquare-locator.coffee
+++ src/foursquare-locator.coffee
@@ -161,7 +161,7 @@
           msg.send "#{user_name} is nowhere to be found."

         timeFormatted = moment(new Date(checkin.createdAt*1000)).fromNow()
-        msg.send "#{user_name} was at #{checkin.venue.name}, #{checkin.venue.location.city}, #{checkin.venue.location.state} #{timeFormatted}"
+        msg.send "#{user_name} was at #{checkin.venue.name} #{checkin.venue.location.city || ''} #{checkin.venue.location.state || ''} #{timeFormatted}"

     else
       # Nothing stored. Do simple looping instead
@@ -176,7 +176,7 @@
           user_name_match = user_name.toLowerCase()
           if ~user_name_match.indexOf searchterm
             timeFormatted = moment(new Date(checkin.createdAt*1000)).fromNow()
-            msg.send "#{user_name} was at #{checkin.venue.name}, #{checkin.venue.location.city}, #{checkin.venue.location.state} #{timeFormatted}"
+            msg.send "#{user_name} was at #{checkin.venue.name} #{checkin.venue.location.city || ''} #{checkin.venue.location.state || ''} #{timeFormatted}"
             found++

         # If loop failed to come up with a result, tell them
@@ -190,7 +190,7 @@
       for own key, checkin of response.recent
         user_name = formatName checkin.user
         timeFormatted = moment(new Date(checkin.createdAt*1000)).fromNow()
-        msg.send "#{user_name} was at #{checkin.venue.name}, #{checkin.venue.location.city}, #{checkin.venue.location.state} #{timeFormatted}"
+        msg.send "#{user_name} was at #{checkin.venue.name} #{checkin.venue.location.city || ''} #{checkin.venue.location.state || ''} #{timeFormatted}"

   # Check for required config
   missingEnvironmentForApi = (msg) ->


Tests for caseSensitive option

Tests for caseSensitive option

--- spec/search-model-spec.coffee
+++ spec/search-model-spec.coffee
@@ -31,3 +31,13 @@
       it 'returns only literal matches when off', ->
         subject.search('items.', regex: false)
         expect(subject.regex.test('items;')).toEqual(false)
+
+    describe "caseSensitive option", ->
+      it 'matches only same case when on', ->
+        subject.search('Items', caseSensitive: true)
+        expect(subject.regex.test('Items')).toEqual(true)
+        expect(subject.regex.test('items')).toEqual(false)
+
+      it 'matches diff case when off', ->
+        subject.search('Items', caseSensitive: false)
+        expect(subject.regex.test('items')).toEqual(true)


Update class name used in specs

Update class name used in specs

--- spec/themes-panel-spec.coffee
+++ spec/themes-panel-spec.coffee
@@ -66,7 +66,7 @@

       themeView = null
       waitsFor ->
-        themeView = panel.find('.package-view').view()
+        themeView = panel.find('.available-package-view').view()
         themeView?

       runs ->
@@ -88,7 +88,7 @@

       themeView = null
       waitsFor ->
-        themeView = panel.find('.package-view').view()
+        themeView = panel.find('.available-package-view').view()
         themeView?

       runs ->


Get billing information if it's missing.

--- client/landing/app/Applications/Environments.kdapplication/views/DomainRegisterModalFormView.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/DomainRegisterModalFormView.coffee
@@ -125,14 +125,34 @@

     {nickname, firstName, lastName} = KD.whoami().profile

+    paymentController = KD.getSingleton('paymentController')
+    group             = KD.getSingleton("groupsController").getCurrentGroup()
+
     super
       navigable                       : no
       goToNextFormOnSubmit            : no
       hideHandleContainer             : yes
       forms                           :
         "Domain Address"              :
-          callback                    : @bound "registerDomain"
           buttons                     :
+            billingButton             :
+              title                   : "Billing Info"
+              style                   : "cupid-green hidden"
+              type                    : "submit"
+              loader                  :
+                color                 : "#ffffff"
+                diameter              : 24
+              callback                : =>
+                form = @forms["Domain Address"]
+                {createButton, billingButton} = form.buttons
+
+                billingButton.hideLoader()
+
+                paymentController.setBillingInfo 'user', group, (success)->
+                  if success
+                    billingButton.hide()
+                    createButton.show()
+
             createButton              :
               title                   : "Add Domain"
               style                   : "cupid-green"
@@ -140,6 +160,7 @@
               loader                  :
                 color                 : "#ffffff"
                 diameter              : 24
+              callback                : @bound "registerDomain"
             close                     :
               title                   : "Close"
               style                   : "cupid-green hidden"
@@ -169,16 +190,19 @@
                     @suggestionBox?.show()
                     domains.hide()
                     regYears.show()
+                    @needBilling yes
                     "#{KD.utils.slugify firstName}s-new-domain.com"
                   when "existing"
                     @suggestionBox?.hide()
                     domains.hide()
                     regYears.hide()
+                    @needBilling no
                     "#{KD.utils.slugify firstName}s-existing-domain.com"
                   when "subdomain"
                     @suggestionBox?.hide()
                     domains.show()
                     regYears.hide()
+                    @needBilling no
                     "#{KD.utils.slugify firstName}s-subdomain"
             domainName                :
               placeholder             : "#{KD.utils.slugify firstName}s-new-domain.com"
@@ -212,8 +236,31 @@

     form = @forms["Domain Address"]
     {createButton} = form.buttons
+
     form.on "FormValidationFailed", createButton.bound 'hideLoader'
     @on "DomainCreationCancelled", createButton.bound 'hideLoader'
+
+  needBilling:(paymentRequired)->
+    form = @forms["Domain Address"]
+    {createButton, billingButton} = form.buttons
+
+    unless paymentRequired
+      createButton.show()
+      billingButton.hide()
+      return
+
+    paymentController = KD.getSingleton('paymentController')
+    group             = KD.getSingleton("groupsController").getCurrentGroup()
+
+    paymentController.getBillingInfo 'user', group, (err, account)->
+      console.log arguments
+      need = err or not account or not account.cardNumber
+      if need
+        billingButton.show()
+        createButton.hide()
+      else
+        createButton.show()
+        billingButton.hide()

   registerDomain:->
     form = @forms["Domain Address"]


added component time cycle functions snippets

--- snippets/ts-react.cson
+++ snippets/ts-react.cson
@@ -94,3 +94,67 @@
           mapDispatchToProps,
       )(${1:ComponentName});
     '''
+  'Typescript-React: componentDidMount':
+    'prefix': 'cdm'
+    'body': '''
+      public componentDidMount() {
+        ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentDidMount (4 spaces indent)':
+    'prefix': 'cdm4'
+    'body': '''
+      public componentDidMount() {
+          ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentDidUnmount':
+    'prefix': 'cdu'
+    'body': '''
+      public componentDidUnmount() {
+        ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentDidUnmount (4 spaces indent)':
+    'prefix': 'cdu4'
+    'body': '''
+      public componentDidUnmount() {
+          ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentWillMount':
+    'prefix': 'cwm'
+    'body': '''
+      public componentWillMount() {
+        ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentWillMount (4 spaces indent)':
+    'prefix': 'cwm4'
+    'body': '''
+      public componentWillMount() {
+          ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentWillUnmount':
+    'prefix': 'cwu'
+    'body': '''
+      public componentWillUnmount() {
+        ${1}
+      }
+      ${2}
+    '''
+  'Typescript-React: componentWillUnmount (4 spaces indent)':
+    'prefix': 'cwu4'
+    'body': '''
+      public componentWillUnmount() {
+          ${1}
+      }
+      ${2}
+    '''


Query string parameter semantics should be in place for DELETE as well as GET. Oops.

--- src/main/coffee/NR.API.coffee
+++ src/main/coffee/NR.API.coffee
@@ -69,7 +69,7 @@
             else
                 queryString = queryString + encodeURIComponent(k.toString()) + "=" + encodeURIComponent(v.toString()) + "&"

-        if method == "GET"
+        if method == "GET" || method == "DELETE"
             if url.indexOf("?") >= 0
                 url = url + "&" + queryString
             else
@@ -114,7 +114,7 @@
             document.dispatchEvent event

         this._ajax.open method, NR.API._rootURL + url, true
-        if method != "GET"
+        if method != "GET" && method != "DELETE"
             this._ajax.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
             this._ajax.send(queryString)
         else


selection tool button

selection tool button

--- src/coffee/bokeh_views.coffee
+++ src/coffee/bokeh_views.coffee
@@ -723,7 +723,7 @@
       @_drag_mapper(ymap, ydiff)

-class SelectionToolView extends PlotWidget
+class SelectionToolView_ extends PlotWidget
   initialize : (options) ->
     super(options)
     @selecting = false
@@ -866,6 +866,54 @@
     @_render_shading()
     @render_end()
     return null
+
+class SelectionToolView extends SelectionToolView_
+  """this version only works via the toolbar button   """
+  initialize : (options) ->
+    super(options)
+    @selecting = false
+    @select_button_clicked = false
+
+
+  bind_events : (plotview) ->
+    console.log("SelectionToolView bind_events")
+    @plotview = plotview
+    @plotview.mousedownCallbacks.push((e, x, y) =>
+      if @button_selecting
+        if not @selecting
+          @_start_selecting(e, x, y)
+        else
+          @_stop_selecting())
+
+    @plotview.moveCallbacks.push((e, x, y) =>
+      if @button_selecting and @selecting
+          @_selecting(e, x, y)
+          e.preventDefault()
+          e.stopPropagation())
+    select_button = $('<button> Selction Tool </button>')
+    @plotview.$el.find('.button_bar').append(select_button)
+    select_button.click(=>
+      if @button_selecting
+        @stop_selecting()
+      else
+        @button_selecting = true)
+  _stop_selecting : () ->
+    @mset(
+      start_x : null
+      start_y : null
+      current_x : null
+      current_y : null
+    )
+    @plotview.$el.removeClass("shading")
+    for renderer in @mget('renderers')
+      @model.resolve_ref(renderer).get_ref('data_source').set('selecting', false)
+      @model.resolve_ref(renderer).get_ref('data_source').save()
+    @selecting = false
+    @button_selecting = false
+    if @shading
+      @shading.remove()
+      @shading = null
+

 class OverlayView extends PlotWidget
   initialize : (options) ->


Add method comments.

Add method comments.

--- src/coffee/stockexport.coffee
+++ src/coffee/stockexport.coffee
@@ -4,6 +4,10 @@
 CommonUpdater = require('sphere-node-sync').CommonUpdater
 InventoryUpdater = require('sphere-node-sync').InventoryUpdater

+/**
+ * Elastic.io component exporting inventory items from Sphere.io.
+ * For each inventory item a XML file will be written.
+ */
 class StockExport extends CommonUpdater
   constructor: (options = {}) ->
     super options
@@ -12,9 +16,22 @@
     @rest = @inventoryUpdater.rest
     @

+  /**
+   * Fetch inventory items from Sphere and export xml files for all inventory items.
+   * @param {string} msg The message to process.
+   * @param {hash} cfg The component's configuration.
+   * @param {function} next The callback function to be invoked when the component finished its work.
+   * @param {string} snapshot The optional snapshot parameter which is used by a component for deduplication.
+   * @return The result of the callback.
+   */
   elasticio: (msg, cfg, next, snapshot) ->
     @returnResult false, 'Not yet implemented', next

+  /**
+   * Fetch inventory items from Sphere and create xml for all inventory items.
+   * @param {function} callback The callback function to be invoked when the method finished its work.
+   * @return The result of the callback.
+   */
   run: (callback) ->
     @inventoryUpdater.allInventoryEntries(@rest).then (allInventoryEntries) =>
       xml = @createXml()
@@ -27,6 +44,10 @@
     .fail (msg) ->
       @returnResult false, msg, callback

+  /**
+   * Creates an XML instance.
+   * @return {number} The result of adding num1 and num2.
+   */
   createXml: () ->
     ops =
       version: '1.0'
@@ -36,6 +57,12 @@
     xml.e('xsdVersion').t '0.1'
     xml

+  /**
+   * Adds inventory data to given xml instance.
+   * @param {xml} xml The xml instance to be used.
+   * @param {inventory} entry The inventory data to be used in the template.
+   * @return {xml} The xml with inventory data.
+   */
   createXmlEntry: (xml, entry) ->
     xe = xml.e('entry')
     @add xe, entry, 'id'
@@ -43,6 +70,11 @@
     @add xe, entry, 'sku'
     @add xe, entry, 'quantityOnStock'

+  /**
+   * Writes an xml file..
+   * @param {xml} xml The xml file to be written.
+   * @return {promise} The promise for writing the xml file.
+   */
   writeOutput: (xml) ->
     deffered = Q.defer()
     content = xml.end(pretty: true, indent: '  ', newline: "\n")
@@ -53,6 +85,14 @@
         deffered.resolve @output
     deffered.promise

+  /**
+   * Adds an element to given xml instance.
+   * @param {xml} xml The xml instance to be used.
+   * @param {element} elem The xml element to be added.
+   * @param {string} attr The name of the element.
+   * @param {string} xAttr ???
+   * @return {xml} The xml with added element.
+   */
   add: (xml, elem, attr, xAttr) ->
     xAttr = attr unless xAttr
     value = elem[attr]


add PointText default x, y

add PointText default x, y

--- src/drawable/PointText.coffee
+++ src/drawable/PointText.coffee
@@ -13,7 +13,7 @@
 	----------------------
 	for example: text is in the right top of the point, then align = "+-"
 	###
-	constructor: (@text, @x, @y) ->
+	constructor: (@text, @x = 0, @y = 0) ->
 		super()
 		@type = 'PointText'
 		@strokeStyle = null # no stroke by default


inject-cloud-resources reports how many files it processed

--- tasks/fresh/steroids-setup-cloud-resources.coffee
+++ tasks/fresh/steroids-setup-cloud-resources.coffee
@@ -16,6 +16,7 @@
         src: 'dist/app/**/*.html'
   }
   grunt.registerMultiTask 'steroids-inject-cloud-resources-js', ->
+    count = 0
     @files.forEach (pair) ->
       pair.src.forEach (src) ->
         grunt.file.copy src, src,
@@ -25,7 +26,9 @@
                 <!-- Cloud resource definition file for Supersonic Data -->
                 <script src="/cloud-resources.js" />
             """
-        console.log src
+        grunt.log.debug "Injected cloud-resources.js to #{src}"
+        count++
+    grunt.log.ok "Processed #{count} files"

   ###
   # 1: convert config/cloud-resources.raml to dist/cloud-resources.js


Update for new featured shows options

Update for new featured shows options

--- apps/home/client/index.coffee
+++ apps/home/client/index.coffee
@@ -55,7 +55,7 @@

   renderFeaturedShows: ->
     new PartnerShows().fetchSetItemsByKey 'homepage:featured-shows', success: (shows) ->
-      $('#home-featured-shows').html featuredShowsTemplate(shows: shows.take(10), crop: crop)
+      $('#home-featured-shows').html featuredShowsTemplate(shows: shows.take(8), displayLocation: true)

 module.exports.init = ->
   new HomeView el: $('body')


Polygon constructor accepts array.

Polygon constructor accepts array.

--- unitary.coffee
+++ unitary.coffee
@@ -213,7 +213,10 @@
     if (not (@ instanceof Polygon))
       throw new Error('Constructor cannot be called as a function.')
     super()
-    @.points = points
+    if (Object.prototype.toString.call(points[0]) == '[object Array]')
+      @.points = points[0]
+    else
+      @.points = points
   equals: () -> false
   move: (dx, dy) ->
     points = []


Support perfect result in cf gym

Support perfect result in cf gym

--- server/testSystems/codeforces/CodeforcesSubmitDownloader.coffee
+++ server/testSystems/codeforces/CodeforcesSubmitDownloader.coffee
@@ -183,6 +183,8 @@
             time = moment(time, "MMM/DD/YYYY HH:mm", true).subtract(3, 'hours').toDate()
             if outcome == "In queue" or outcome.startsWith("Running")
                 outcome = "CT"
+            if outcome.startsWith("Perfect result")
+                outcome = "OK"
             if outcome == "Вы уже отправляли этот код"
                 outcome = "DP"
             if user.toLowerCase() != @username.toLowerCase()
@@ -205,4 +207,4 @@
                     system: "codeforces"
                     username: @username
             )
-        return result+        return result


fix logging

fix logging

--- services/web/app/coffee/Features/Editor/EditorController.coffee
+++ services/web/app/coffee/Features/Editor/EditorController.coffee
@@ -71,7 +71,7 @@
 		Metrics.inc "editor.add-folder"
 		ProjectEntityUpdateHandler.addFolder project_id, folder_id, folderName, (err, folder, folder_id)=>
 			if err?
-				logger.err err:err, project_id:project_id, source:source,  "could not add folder"
+				logger.err {err, project_id, folder_id, folderName, source}, "could not add folder"
 				return callback(err)
 			EditorController._notifyProjectUsersOfNewFolder project_id, folder_id, folder, (err) ->
 				return callback(err) if err?
@@ -81,7 +81,7 @@
 		logger.log project_id:project_id, path:path, "making directories if they don't exist"
 		ProjectEntityUpdateHandler.mkdirp project_id, path, (err, newFolders, lastFolder)=>
 			if err?
-				logger.err err:err, project_id:project_id, "could not mkdirp"
+				logger.err err:err, project_id:project_id, path:path, "could not mkdirp"
 				return callback(err)

 			EditorController._notifyProjectUsersOfNewFolders project_id, newFolders, (err) ->


test(search): new tests for ios

test(search): new tests for ios

--- test/spec/directives/search.coffee
+++ test/spec/directives/search.coffee
@@ -4,15 +4,91 @@

   scope   = null
   element = null
+  children= null
+  $bgLeft = $bgRight = $search = $placeholder = $cancel = $tapLayer = null

   beforeEach inject ($rootScope, $compile) ->
     scope   = $rootScope.$new()
-    element = $compile('<bp-search><input type="search" /></bp-search>') scope
+    element = $compile('<bp-search><input type="search"/></bp-search>') scope
+    children = element.children()
+    $bgLeft = children.eq 0
+    $bgRight = children.eq 1
+    $search = children.eq 2
+    $placeholder = children.eq 3
+    $cancel = children.eq 4
+    $tapLayer = children.eq 5
     scope.$apply()

   describe 'element', ->
-    it 'should transclude the input field"', ->
-      expect(element.has('input').length).toBe 1
-    it 'should have a cancel button', ->
-      expect(element.has('bp-action.bp-button[bp-tap]').length).toBe 1
-      expect(element.find('bp-action').text()).toBe 'Cancel'
+    it 'should have ARIA role', ->
+      expect(element.attr 'role').toBe 'search'
+
+    it 'should have child elements', ->
+      expect($bgLeft.is 'bp-search-bg-left').toBe true
+      expect($bgRight.is 'bp-search-bg-right').toBe true
+      expect($search.is 'input').toBe true
+      expect($placeholder.is 'bp-search-placeholder').toBe true
+      expect($cancel.is 'bp-action').toBe true
+      expect($tapLayer.is 'bp-search-tap').toBe true
+
+    it 'should calculate dimensions', inject ($timeout) ->
+      $timeout.flush()
+      expect($bgLeft.attr('style')).toMatch /width/
+      expect($bgRight.attr('style')).toMatch /width/
+      expect($search.attr('style')).toMatch /width/
+      expect($search.attr('style')).toMatch /padding-left/
+
+  describe 'events', ->
+    it 'should bind and unbind events', inject ($window) ->
+      windowEvents = $._data($window).events
+      searchEvents = $._data($search.get 0 ).events
+      cancelEvents = $._data($cancel.get 0 ).events
+      tapLayerEvents = $._data($tapLayer.get 0 ).events
+
+      expect(windowEvents.resize?).toBe true
+      expect(windowEvents.orientationchange?).toBe true
+      expect(searchEvents.blur?).toBe true
+      expect(cancelEvents.tap?).toBe true
+      expect(tapLayerEvents.tap?).toBe true
+      expect(tapLayerEvents.click?).toBe true
+      expect(tapLayerEvents.touchstart?).toBe true
+      expect(tapLayerEvents.touchmove?).toBe true
+      expect(tapLayerEvents.touchend?).toBe true
+
+      scope.$destroy()
+
+      expect(windowEvents.resize?).toBe false
+      expect(windowEvents.orientationchange?).toBe false
+      expect(searchEvents.blur?).toBe false
+      expect(cancelEvents.tap?).toBe false
+      expect(tapLayerEvents.tap?).toBe false
+      expect(tapLayerEvents.click?).toBe false
+      expect(tapLayerEvents.touchstart?).toBe false
+      expect(tapLayerEvents.touchmove?).toBe false
+      expect(tapLayerEvents.touchend?).toBe false
+
+    it 'should execute handlers correctly', inject ($timeout) ->
+      childScope = $cancel.scope()
+
+      $bgLeft.css 'width', ''
+      childScope.onResize()
+      expect($bgLeft.attr 'style').toMatch /width/
+
+      childScope.onFocus()
+      $timeout.flush()
+      expect(element.hasClass 'focus').toBe true
+
+      childScope.onBlur()
+      expect(element.hasClass 'focus').toBe false
+
+      e =
+        stopPropagation: ->
+        stopImmediatePropagation: ->
+
+      spyOn e, 'stopPropagation'
+      spyOn e, 'stopImmediatePropagation'
+
+      childScope.stopPropagation e
+
+      expect(e.stopPropagation).toHaveBeenCalled()
+      expect(e.stopImmediatePropagation).toHaveBeenCalled()


Fix expect on undefined

Fix expect on undefined


Adapt to ngnvd3 migration.

Adapt to ngnvd3 migration.

--- test/unit/modelingChartSpec.coffee
+++ test/unit/modelingChartSpec.coffee
@@ -15,6 +15,8 @@
             average: 1.4
           fxrKTrans:
             average: 1.3
+          deltaKTrans:
+            average: 1.35
           vE:
             average: 0.2
           tauI:
@@ -28,6 +30,8 @@
             average: 1.3
           fxrKTrans:
             average: 1.1
+          deltaKTrans:
+            average: 1.2
           vE:
             average: 0.3
           tauI:
@@ -44,58 +48,94 @@
         ]

       describe 'Chart Configuration', ->
-
-        dates = null
-
-        beforeEach ->
-          dates = mock.map (mdlResult) ->
-            mdlResult.modeling.session.date
-
-        describe 'kTrans', ->
+        describe 'Ktrans', ->
           config = null

           beforeEach ->
-            config = ModelingChart.configureD3(mock, KTrans.CHART_DATA_SERIES_CONFIG)
+            config = ModelingChart.configure(mock, 'kTrans')

           it 'should configure two data series', ->
             expect(config.data, "The configuration data is missing").to.exist
             expect(config.data.length, "The configuration data series count is incorrect")
               .to.equal(2)
+
+          describe 'FXL', ->

-          it 'should set the X axis values to the session acquisition dates', ->
-            expect(config.xValues, "The configuration X values are incorrect")
-              .to.eql(dates)
+            data = null

-          it 'should set the Y axis values to the Ktrans values', ->
-            expect(config.xValues, "The configuration Y values are incorrect")
-              .to.eql(dates)
+            beforeEach ->
+              data = _.find(config.data, key : 'FXL Ktrans')

-        describe 'vE', ->
+            it 'should capture the FXL KTrans results', ->
+              expect(data, "The FXL Ktrans data series is missing").to.exist
+              expect(data.values, "The FXL Ktrans data values are missing").to.exist
+              expect(data.values.length, "The FXL Ktrans results length is incorrect")
+               .to.equal(2)
+              expect(data.values, "The FXL Ktrans results are incorrect")
+               .to.eql(_.map(mock, 'fxlKTrans'))
+
+          describe 'FXR', ->
+
+            data = null
+
+            beforeEach ->
+              data = _.find(config.data, key : 'FXR Ktrans')
+
+            it 'should capture the FXR KTrans results', ->
+              expect(data, "The FXR Ktrans data series is missing").to.exist
+              expect(data.values, "The FXR Ktrans data values are missing").to.exist
+              expect(data.values.length, "The FXL Ktrans results length is incorrect")
+               .to.equal(2)
+              expect(data.values, "The FXR Ktrans results are incorrect")
+               .to.eql(_.map(mock, 'fxrKTrans'))
+
+        describe 'v_e', ->
           config = null

           beforeEach ->
-            config = ModelingChart.configureD3(mock, VE.CHART_DATA_SERIES_CONFIG)
+            config = ModelingChart.configure(mock, 'vE')

-          it 'should configure two data series', ->
+          it 'should configure one data series', ->
             expect(config.data, "The configuration data is missing").to.exist
             expect(config.data.length, "The configuration data series count is incorrect")
               .to.equal(1)
+            expect(config.data[0].key, "The v_e data series key is incorrect")
+              .to.equal('v_e')

-          it 'should set the X axis values to the session acquisition dates', ->
-            expect(config.xValues, "The configuration X values are incorrect")
-              .to.eql(dates)
+          it 'should capture the v_e results', ->
+            values = config.data[0].values
+            expect(values, "The v_e data values are missing").to.exist
+            expect(values.length, "The v_e results length is incorrect")
+             .to.equal(2)
+            expect(values, "The v_e results are incorrect")
+             .to.eql(_.map(mock, 'vE'))

-        describe 'tauI', ->
-          config = null
-
-          beforeEach ->
-            config = ModelingChart.configureD3(mock, TauI.CHART_DATA_SERIES_CONFIG)
-
-          it 'should configure two data series', ->
-            expect(config.data, "The configuration data is missing").to.exist
-            expect(config.data.length, "The configuration data series count is incorrect")
-              .to.equal(1)
-
-          it 'should set the X axis values to the session acquisition dates', ->
-            expect(config.xValues, "The configuration X values are incorrect")
-              .to.eql(dates)
+        # describe 'vE', ->
+        #   config = null
+        #
+        #   beforeEach ->
+        #     config = ModelingChart.configure(mock, 'vE')
+        #
+        #   it 'should configure two data series', ->
+        #     expect(config.data.values, "The configuration data is missing").to.exist
+        #     expect(config.data.values.length, "The configuration data series count is incorrect")
+        #       .to.equal(1)
+        #
+        #   it 'should set the X axis values to the session acquisition dates', ->
+        #     expect(config.xValues, "The configuration X values are incorrect")
+        #       .to.eql(dates)
+        #
+        # describe 'tauI', ->
+        #   config = null
+        #
+        #   beforeEach ->
+        #     config = ModelingChart.configure(mock, 'tauI')
+        #
+        #   it 'should configure two data series', ->
+        #     expect(config.data.values, "The configuration data is missing").to.exist
+        #     expect(config..values.length, "The configuration data series count is incorrect")
+        #       .to.equal(1)
+        #
+        #   it 'should set the X axis values to the session acquisition dates', ->
+        #     expect(config.xValues, "The configuration X values are incorrect")
+        #       .to.eql(dates)


revert to just writting payload

revert to just writting payload

--- plugins/root.coffee
+++ plugins/root.coffee
@@ -12,7 +12,7 @@
     method: 'POST'
     path: '/'
     handler: (request, reply) ->
-      console.log request.payload.question.answers
+      console.log request.payload
       reply()
   next()


cb_es is undefined

cb_es is undefined

--- lib/org_unit_provisioning.coffee
+++ lib/org_unit_provisioning.coffee
@@ -5,7 +5,6 @@
 qs        = require 'qs'
 sanitize  = require 'sanitize-arguments'
 async     = require 'async'
-retry     = require 'retry'

 class OrgUnit extends GoogleAPIAdminSDK
   # TODO: possibly make customer_id an option of OrgUnit
@@ -134,7 +133,7 @@

         # Google interface: `name` requires no slash, parentOrgUnitPath requires a slash
         that.insert customer_id, { name: level, parentOrgUnitPath: parent }, (err, body) =>
-          return cb_es "Unable to create org unit #{full_path}: #{JSON.stringify err}" if err?
+          return cb_wf "Unable to create org unit #{full_path}: #{JSON.stringify err}" if err?
           # cache[full_path] = body
           parent = full_path
           cb_wf null, { body, parent }


Use Workspace::eachEditor

Use Workspace::eachEditor

--- lib/project/results-model.coffee
+++ lib/project/results-model.coffee
@@ -18,7 +18,7 @@
     @useRegex = state.useRegex ? false
     @caseSensitive = state.caseSensitive ? false

-    atom.project.eachEditor (editSession) =>
+    atom.workspace.eachEditor (editSession) =>
       editSession.on 'contents-modified', => @onContentsModified(editSession)

     @clear()


Resolves #34 while maintaining backwards compatibility with Atom < 1.21.x

--- lib/simple-drag-drop-text.coffee
+++ lib/simple-drag-drop-text.coffee
@@ -43,7 +43,11 @@
     if @selBufferRange.isEmpty() then return

     inSelection = no
-    $(@lines).find('.highlights .highlight.selection .region').each (__, ele) =>
+
+    @highlights = $(@lines).find('.highlights')
+    @highlights = if 0 == @highlights.length then @views.querySelector '.highlights' else @highlights
+
+    $(@highlights).find('.highlight.selection .region').each (__, ele) =>
       {left, top, right, bottom} = ele.getBoundingClientRect()
       if left <= e.pageX < right and
           top <= e.pageY < bottom


theme names will have to be manually entered... meh

--- lib/sunset.coffee
+++ lib/sunset.coffee
@@ -1,31 +1,31 @@
 {CompositeDisposable} = require 'atom'
+
+cached_themes =
+  ui: []
+  syntax: []

 module.exports = Sunset =
   config:
     daytime_syntax_theme:
       title: 'Daytime Theme (Syntax)'
       description: 'What syntax theme should I use during daylight?'
-      default: ''
       type: 'string'
-      enum: ['']
+      default: 'atom-light-syntax'
     daytime_ui_theme:
       title: 'Daytime Theme (UI)'
       description: 'What UI theme should I use during daylight?'
-      default: ''
       type: 'string'
-      enum: ['']
+      default: 'atom-light-ui'
     nighttime_syntax_theme:
       title: 'Nighttime Theme (Syntax)'
       description: 'What syntax theme should I use during nighttime?'
-      default: ''
       type: 'string'
-      enum: ['']
+      default: 'atom-dark-syntax'
     nighttime_ui_theme:
       title: 'Nighttime Theme (UI)'
       description: 'What UI theme should I use during nighttime?'
-      default: ''
       type: 'string'
-      enum: ['']
+      default: 'atom-dark-ui'
     when_does_it_get_dark:
       title: 'When does nighttime start?'
       description: 'When (in 24hr format) does the sun set?'
@@ -52,20 +52,17 @@
     @bindEvents()
     @tick()

-    syntax_themes = atom.themes.getLoadedThemes().filter((theme) -> theme.metadata.theme is 'syntax').map((theme) -> theme.name)
-    ui_themes = atom.themes.getLoadedThemes().filter((theme) -> theme.metadata.theme is 'ui').map((theme) -> theme.name)
+    atom.themes.getLoadedThemes().forEach (theme) ->
+      cached_themes.ui.push(theme.name) if theme.metadata.theme is 'ui'
+      cached_themes.syntax.push(theme.name) if theme.metadata.theme is 'syntax'

-    # hack, but populate the settings so people can easily pick themes to change between
-    @config.daytime_syntax_theme.enum =
-      @config.nighttime_syntax_theme.enum = syntax_themes
+    @config.daytime_syntax_theme.description =
+      @config.nighttime_syntax_theme.description =
+        'Values can be `' + cached_themes.syntax.join(', ') + '`'

-    @config.daytime_ui_theme.enum =
-      @config.nighttime_ui_theme.enum = ui_themes
-
-    @config.daytime_ui_theme.default =
-      @config.nighttime_ui_theme.default = ui_themes[0]
-    @config.daytime_syntax_theme.default =
-      @config.nighttime_syntax_theme.default = syntax_themes[0];
+    @config.daytime_ui_theme.description =
+      @config.nighttime_ui_theme.description =
+        'Values can be `' + cached_themes.ui.join(', ') + '`'

     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
     @subscriptions = new CompositeDisposable


Support node.js environment

Support node.js environment

--- src/app.coffee
+++ src/app.coffee
@@ -37,11 +37,11 @@
     Version string for Rubic
   @readonly
   ###
-  @classProperty("version", value: chrome.runtime.getManifest?()["version"] or VER_EMULATION)
+  @classProperty("version", value: chrome?.runtime?.getManifest?()["version"] or VER_EMULATION)

   LOG = (type, verbosity, args) ->
     return if Preferences.logVerbosity < verbosity
-    fn = window.console[type].bind(window.console)
+    fn = global.console[type].bind(global.console)
     return fn(sprintf(args...)) if typeof(args[0]) == "string"
     return fn(args...)

@@ -54,7 +54,7 @@
   @error        = -> return LOG("error", 1, arguments)
   @error.detail = -> return LOG("error", 2, arguments)

-  @info("Rubic/%s %s", @version, window.navigator.userAgent)
+  @info("Rubic/%s %s", @version, window?.navigator.userAgent)

   ###*
   @static


limiting the number of chats to 20 i guess

--- src/app.coffee
+++ src/app.coffee
@@ -969,6 +969,8 @@
     renderPlaylistButtons()
   mpd.on 'chat', (chat_object) ->
     chats.push(chat_object)
+    chats_limit = 20
+    chats.splice(0, chats.length - chats_limit) if chats.length > chats_limit
     renderChat()
   mpd.on 'connect', ->
     mpd_alive = true


Simplify pagination.

Simplify pagination.

--- src/app.coffee
+++ src/app.coffee
@@ -89,16 +89,14 @@
             unit_list.setFilter 'service', service.id
             unit_list.setFilter 'only', 'name,location,root_services'

-            unit_list.on 'sync', (unit_list, response, options) =>
-                @units.add unit_list.toArray()
-                unless response.next
-                    @units.trigger 'finished'
-
             opts =
                 # todo: re-enable
                 #spinner_target: spinner_target
                 success: =>
-                    unit_list.fetchNext opts
+                    has_more = unit_list.fetchNext opts
+                    @units.add unit_list.toArray()
+                    unless has_more
+                        @units.trigger 'finished'

             unit_list.fetch opts


removed unused tests

removed unused tests

--- test/hpub_reader.test.coffee
+++ test/hpub_reader.test.coffee
@@ -4,19 +4,6 @@

 describe 'hpub', ->
   describe 'reader', ->
-    # it 'should throw an Error when file is missing', ->
-    #     fun = ->
-    #         new hpub.Reader "./test/unknownbook.hpub"
-
-    #     assert.throws (->
-    #         fun()
-    #     )
-
-    # it 'should throw Error when file is corrupted', ->
-    #     reader = new hpub.Reader "./test/corruptedbook.hpub"
-    #     assert.throws (->
-    #         throw new Error("Invalid or unsupported hpub format.")
-    #     ), Error

     it 'should read file', ->
         new hpub.Reader "./test/hpub_samples/book1.hpub", (err, reader) ->


There is no bot_message anymore

There is no bot_message anymore

--- src/bot.coffee
+++ src/bot.coffee
@@ -132,10 +132,6 @@
         @robot.logger.debug "Received message: '#{text}' in channel: #{channel.name}, from: #{user.name}"
         @receive new TextMessage(user, text, message.ts)

-      when 'bot_message'
-        @robot.logger.debug "#{bot.name} has joined #{channel.name}"
-        @receive new Message bot
-
       when 'channel_join', 'group_join'
         @robot.logger.debug "#{user.name} has joined #{channel.name}"
         @receive new EnterMessage user


add count to prom

add count to prom

--- libraries/metrics/metrics.coffee
+++ libraries/metrics/metrics.coffee
@@ -58,6 +58,14 @@

 	count : (key, count, sampleRate = 1)->
 		statsd.count buildKey(key), count, sampleRate
+		key = Metrics.buildPromKey(key)
+		if !promMetrics[key]?
+			promMetrics[key] = new prom.Counter({
+				name: key,
+				help: key,
+				labelNames: ['name','host']
+			})
+		promMetrics[key].inc({name: name, host: hostname}, count)

 	timing: (key, timeSpan, sampleRate)->
 		statsd.timing(buildKey(key), timeSpan, sampleRate)


better log formatting

better log formatting

--- library/exposure/policies.coffee
+++ library/exposure/policies.coffee
@@ -126,5 +126,6 @@
         add q for q in entity?.qualify?() or Array()
         aliased = envelope and envelope is container
         _.extend envelope, container unless aliased
-        logger.debug message, qualifiers.join ", "
+        squalifiers = try _.map qualifiers, "bold"
+        logger.debug message, squalifiers.join ", "
         assert not _.isEmpty qualifiers; qualifiers


Fix minor misnomer, yet another one

Fix minor misnomer, yet another one

--- library/membrane/validate.coffee
+++ library/membrane/validate.coffee
@@ -87,7 +87,7 @@
     # run all the validator contexsts in parallel and wait for the
     # completion. If no validation mistakes found, run continuation.
     # If some mistakes are found, however, run `@renderValidation`.
-    continueWithValidation: (request, response, continuation) ->
+    validateAndContinue: (request, response, continuation) ->
         notRequest = "a #{request} is not a request"
         notResponse = "a #{response} is not a response"
         notContinuation = "a #{continuation} is not function"


updated test to check for title too

updated test to check for title too

--- test/unit/controller/error-controller-spec.coffee
+++ test/unit/controller/error-controller-spec.coffee
@@ -7,6 +7,7 @@
     $routeParams = null
     $scope = null
     createController = null
+    pageTitleFactory = null

     beforeEach module cfg.ngApp

@@ -14,6 +15,7 @@
       $controller = $injector.get '$controller'
       $rootScope = $injector.get '$rootScope'
       $routeParams = $injector.get '$routeParams'
+      pageTitleFactory = $injector.get 'pageTitleFactory'

       $scope = $rootScope.$new()

@@ -22,12 +24,17 @@
           $scope: $scope
           $routeParams: $routeParams

-    it 'should change the $scope.code according to $routeParams.code', () ->
+    it 'should change the $scope.code and title according to $routeParams.code', () ->
       code = '123'
+      cb =
+        handleTitle: (event, newTitle) ->
+          expect(newTitle).toContain code
+          expect($scope.code).toBe code
+
+      pageTitleFactory.addListener cb.handleTitle
+
       $routeParams.code = code
       controller = createController()
-
-      expect($scope.code).toBe code

     it 'should default $scope.code to 404 if there is no $routeParams.code', () ->
       controller = createController()


Make codio command runnable with cli

Make codio command runnable with cli

--- src/cli.coffee
+++ src/cli.coffee
@@ -107,6 +107,12 @@
           if err then return logError err
           console.log chalk.green("#{manifest.username}/#{manifest.name}/#{manifest.version} successfully uploaded")

+  codio: (argv) ->
+    codio = require('./codio')
+    codio (err) ->
+      if err then return logError err
+      console.log chalk.green '.codio created'
+
 command = argv._.shift() || 'help'
 if typeof commands[command] != 'function'
   console.log 'Unknown command:', command


Add timezone to timeago tooltip

Add timezone to timeago tooltip

--- resources/assets/coffee/_classes/tooltip-default.coffee
+++ resources/assets/coffee/_classes/tooltip-default.coffee
@@ -118,9 +118,21 @@
       .text time.format('LL')
     $timeEl = $('<span>')
       .addClass 'tooltip-default__time'
-      .text time.format('LT')
+      .text "#{time.format('LT')} #{@tzString(time)}"

     $('<span>')
       .append $dateEl
       .append ' '
       .append $timeEl
+
+
+  tzString: (time) ->
+    offset = time.utcOffset()
+
+    offsetString =
+      if offset % 60 == 0
+        "#{if offset >= 0 then '+' else ''}#{offset / 60}"
+      else
+        time.format('Z')
+
+    "UTC#{offsetString}"


Send stop/kill signal to process group

Send stop/kill signal to process group

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- deployment/supervisord.coffee
+++ deployment/supervisord.coffee
@@ -52,6 +52,8 @@
     startretries            : 5
     stopsignal              : 'TERM'
     stopwaitsecs            : 10
+    stopasgroup             : yes
+    killasgroup             : yes
     redirect_stderr         : yes
     stdout_logfile          : "%(ENV_KONFIG_SUPERVISORD_LOGDIR)s/#{app}.log"
     stdout_logfile_maxbytes : '1MB'


Fixing absolute integration tests (still passing)

--- test/AbsoluteIntegration.coffee
+++ test/AbsoluteIntegration.coffee
@@ -7,27 +7,31 @@
 app.use assets src: path.join(__dirname, 'assets')
 app.listen 3590

-# exports['Compiled stylesheets work from absolute options.src'] = (test) ->
-#   cssTag = "<link rel='stylesheet' href='/css/button.css'>"
-#   test.equals css('button'), cssTag
-#   test.done()
+exports['Compiled stylesheets work from absolute options.src'] = (test) ->
+  cssTag = "<link rel='stylesheet' href='/css/button.css'>"
+  test.equals css('button'), cssTag

-#   request 'http://localhost:3588/css/button.css', (err, res, body) ->
-#     throw err if err
-#     expectedBody = '''
-#     .button {
-#       -webkit-border-radius: 5px;
-#       -moz-border-radius: 5px;
-#       border-radius: 5px;
-#     }\n
-#     '''
-#     test.equals body, expectedBody
-#     test.done()
+  request 'http://localhost:3590/css/button.css', (err, res, body) ->
+    throw err if err
+    expectedBody = '''
+    .button {
+      -webkit-border-radius: 5px;
+      -moz-border-radius: 5px;
+      border-radius: 5px;
+    }\n
+    '''
+    test.equals body, expectedBody
+    test.done()

 exports['JS dependencies work from absolute options.src'] = (test) ->
   jsTags = """<script src='/js/subdir/nested/hobbits.js'></script>
   <script src='/js/tree-dependent.js'></script>
   """
   test.equals js('tree-dependent'), jsTags
-  test.done()
-  app.close()+
+  request 'http://localhost:3590/js/tree-dependent.js', (err, res, body) ->
+    throw err if err
+    expectedBody = '(function() {\n\n}).call(this);\n'
+    test.equals body, expectedBody
+    test.done()
+    app.close()


Update commentary

Update commentary

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -125,6 +125,7 @@
 			end = change.end
 			# Check that this change was made by us, not a collaborator
 			# (Cursor is still one place behind)
+			# NOTE: this is also the case when a user backspaces over a highlighted region
 			if end.row == cursorPosition.row and end.column == cursorPosition.column + 1
 				if change.action == "insert"
 					range = new Range(end.row, 0, end.row, end.column)
@@ -134,16 +135,16 @@
 					if commandFragment? and commandFragment.length > 2
 						if commandFragment.startsWith('\\label{')
 							# console.log ">> LABEL IS HERE"
-							# TODO: trigger re-scan of document
 							@labelsManager.scheduleLoadLabelsFromOpenDoc()
 						setTimeout () =>
 							@editor.execCommand("startAutocomplete")
 						, 0
 			else
+				# Check if user has backspaced/deleted a highlighted region of text
+				# and see if that contains a `\label{}`
 				if change.action == 'remove'
 					if _.any(change.lines, (line) -> line.match(/\\label{.*}/))
 						# console.log ">> a label has been removed"
-						# TODO: trigger removal of label
 						@labelsManager.scheduleLoadLabelsFromOpenDoc()

 		monkeyPatchAutocomplete: () ->


Do bulk rejects in reverse order.

Do bulk rejects in reverse order.

--- services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.coffee
@@ -220,6 +220,8 @@
 		rejectChangeIds: (change_ids) ->
 			changes = @rangesTracker.getChanges(change_ids)
 			return if changes.length == 0
+			changes.sort((a, b) -> b.op.p - a.op.p)
+
 			session = @editor.getSession()
 			for change in changes
 				if change.op.d?


activity: defensive control structures for `JGroup#description` reference usages

--- client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
+++ client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
@@ -103,7 +103,9 @@

       @innerContaner = new KDCustomHTMLView cssClass : "right-block-box"

-      hasBody = group.body.trim() isnt ""
+      {body}  = group
+      body   ?= ""
+      hasBody = Boolean body.trim().length
       isAdmin = "admin" in KD.config.roles

       edit = new CustomLinkView
@@ -121,7 +123,7 @@

       @bodyView = new KDCustomHTMLView
         tagName   : "p"
-        pistachio : "{{ #(body)}}"
+        pistachio : "{{ #(body) or ''}}"
         cssClass  : "group-description"
       , group


teams: login: correctly prefill the email address

--- client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamlogintab.coffee
@@ -39,8 +39,8 @@
     @form.button.setClass 'TeamsModal-button TeamsModal-button--green'

     if location.search isnt '' and location.search.search('username=') > 0
-      username = location.search.split('username=').last.replace(/\&.+/, '')
-      @form.username.input.setValue username
+      username = location.search.split('username=').last.replace(/\&.+/, '') # trim the rest params if any
+      @form.username.input.setValue decodeURIComponent username  # decode in case it is an email
       @form.username.inputReceivedKeyup()

     @invitationLink = new CustomLinkView


Using local entries in log.get_duration and log.get_activity_duration functions.

--- src/log.coffee
+++ src/log.coffee
@@ -99,10 +99,10 @@

     get_duration: (index, entries=@entries)->
         duration = 0
-        first = log.entries[index]
+        first = entries[index]
         if not first then return 0
         for i in [index...entries.length]
-            e = log.entries[i]
+            e = entries[i]
             if not e.pause? and ((e.task != first.task) or (e.active != first.active)) then break
             duration += @_get_segment_duration i, false, entries

@@ -113,7 +113,7 @@
         first = entries[index]
         if not first then return 0
         for i in [index...entries.length]
-            e = log.entries[i]
+            e = entries[i]
             if not e.pause? and e.active != first.active then break
             duration += @_get_segment_duration i, false, entries


group dashboard: wip button placement scroll indicator

--- client/app/Applications/Groups.kdapplication/Views/permissionview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/permissionview.coffee
@@ -120,7 +120,7 @@
     @buttonHeight    = @permissions.$('.formline.button-field').outerHeight(yes)
     @subHeaderHeight = @getDelegate().parent.parent.$('.sub-header').outerHeight(yes)
     @visibleHeight   = delegate.$().outerHeight(yes)
-    @contentHeight   = delegate.$('.group-content').outerHeight(yes)
+    @contentHeight   = @getDelegate().parent.parent.$('.group-content').outerHeight(yes)

     @setButtonPosition @calculateButtonPosition()


Demos: Updated teamwork code on demos page.

--- client/landing/app/Applications/Demos.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Demos.kdapplication/AppController.coffee
@@ -20,7 +20,7 @@
       joinModalTitle     : "Join a coding session"
       joinModalContent   : "
Paste the session key that you received and start coding together.
"
       shareSessionKeyInfo: "
This is your session key, you can share this key with your friends to work together.
"
-      firebaseInstances  : "teamwork-local"
+      firebaseInstance   : "teamwork-local"
       # firebaseInstances  : ["kd-prod-1", "kd-prod-2", "kd-prod-3", "kd-prod-4", "kd-prod-5"]
       enableChat         : yes
       panels             : [


Require pry manually.

Require pry manually.

--- src/pry.coffee
+++ src/pry.coffee
@@ -6,6 +6,7 @@
     @it = "(#{@_pry.toString()}).call(this)"

   _pry: ->
+    pry = require('pryjs')
     _ = null
     pry.open ((input) ->
       _ = eval(input)


Build vararg ActiveX methods

Build vararg ActiveX methods

--- src/objects/file.proto.coffee
+++ src/objects/file.proto.coffee
@@ -31,14 +31,20 @@
   else
     mtime

+# Call ActiveX method with arbitrary arguments
+varargs = (name, path, args)->
+  s = ""
+  for a, i in args
+    s += ",a[#{i}]"
+  (new Function 'x,f,a', "return x.#{name}(f#{s})") fs, path, args
+
+
 # fs.OpenTextFile
 # args: IOMode, Create, Format
-exports.open = (args...)->
-  args.unshift @
-  fs.OpenTextFile.apply fs, args
+exports.open = ->
+  varargs 'OpenTextFile', @, arguments

 # fs.CreateTextFile
 # args: Overwrite, Unicode
-exports.open = (args...)->
-  args.unshift @
-  fs.CreateTextFile.apply fs, args
+exports.create = ->
+  varargs 'CreateTextFile', @, arguments


fix: remove leading 00: from duration if episode is >1hr

--- src/javascripts/player.coffee
+++ src/javascripts/player.coffee
@@ -126,7 +126,10 @@

   setDuration: =>
     if @app.episode.duration
-      @app.episode.humanDuration = Utils.secondsToHHMMSS(_.clone(@app.episode.duration))
+      humanDuration = Utils.secondsToHHMMSS(_.clone(@app.episode.duration))
+      if @app.episode.duration < 3600
+        humanDuration = humanDuration.replace(/^00:/, '')
+      @app.episode.humanDuration = humanDuration
       @duration = @app.episode.duration
       return


updating teaspoon to reflect class name changes

--- test/javascripts/integration/admin_journal_user_roles_test.js.coffee
+++ test/javascripts/integration/admin_journal_user_roles_test.js.coffee
@@ -140,8 +140,8 @@
 test 'admin adds a role for user', ->
   visit "/admin/journals/#{TahiTest.journalId}"
   .then -> ok exists 'tr.user-row'
-  fillIn '.admin-search-input', TahiTest.query
-  click '.admin-search-button'
+  fillIn '.admin-user-search-input', TahiTest.query
+  click '.admin-user-search-button'
   click '.assign-role-button'
   .then -> $('.add-role-input').typeahead 'val', 'Edit'
   .then -> click '.tt-suggestion'
@@ -149,8 +149,8 @@

 test 'admin removes a role for user', ->
   visit "/admin/journals/#{TahiTest.journalId}"
-  fillIn '.admin-search-input', TahiTest.query
-  click '.admin-search-button'
+  fillIn '.admin-user-search-input', TahiTest.query
+  click '.admin-user-search-button'
   click '.assign-role-button'
   .then -> $('.add-role-input').typeahead 'val', 'Edit'
   .then -> click '.tt-suggestion'
@@ -159,8 +159,8 @@

 test 'autocomplete does not give roles the user is already assigned to', ->
   visit "/admin/journals/#{TahiTest.journalId}"
-  fillIn '.admin-search-input', TahiTest.query
-  click '.admin-search-button'
+  fillIn '.admin-user-search-input', TahiTest.query
+  click '.admin-user-search-button'
   click '.assign-role-button'
   .then -> $('.add-role-input').typeahead 'val', 'Edit'
   .then -> click '.tt-suggestion'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera_fade.coffee
+++ src/camera_fade.coffee
@@ -41,6 +41,7 @@
   ###
   fadeIn: (options={}) ->
     {duration, intensity} = Object.reverseMerge(options, defaultParams)
+
     configureFade(duration, color, 0)

   ###*


assign the key._id in the key function, fix error when snapping a key with another on same time, close #16

--- src/scripts/graph/Keys.coffee
+++ src/scripts/graph/Keys.coffee
@@ -93,7 +93,11 @@
         self.onKeyUpdated.dispatch()

       propValue = (d,i,j) -> d.keys
-      propKey = (d, k) -> d.time
+      propKey = (d, k) ->
+        # Add a unique id for SelectionManager.removeDuplicates
+        # and use it as key.
+        if !d._id then d._id = Utils.guid()
+        return d._id
       keys = properties.select('.line-item__keys').selectAll('.key').data(propValue, propKey)

       # selectKey is triggered by dragstart event
@@ -123,12 +127,7 @@
       key_grp = keys.enter()
         .append('g')
         .attr('class', 'key')
-        # Add a unique id for SelectionManager.removeDuplicates
-        .call((d) ->
-          d.each (item) ->
-            item._id =  Utils.guid()
-        )
-        # Use the unique id added above for the dom element id
+        # Use the unique id added in propKey above for the dom element id.
         .attr('id', (d) -> d._id)
         .on('mousedown', () ->
           # Don't trigger mousedown on linescontainer else


Fixed setting factlinkBaseUrl incorrectly in ProfileController.

--- core/app/backbone/controllers/profile_controller.coffee
+++ core/app/backbone/controllers/profile_controller.coffee
@@ -120,7 +120,7 @@
     channelCollectionView.setActive('profile')

   makePermalinkEvent: ->
-    FactlinkApp.factlinkBaseUrl = baseUrl
+    FactlinkApp.factlinkBaseUrl = null
     @permalink_event = @bindTo FactlinkApp.vent, 'factlink_permalink_clicked', =>
       @last_profile_status =
         view: @profile_views.currentView()


Update ajaxify_rails.js.coffee

Update ajaxify_rails.js.coffee

--- vendor/assets/javascripts/ajaxify_rails.js.coffee
+++ vendor/assets/javascripts/ajaxify_rails.js.coffee
@@ -36,7 +36,7 @@
 # Example:
 #   <%= link_to 'test', test_path, :method => :delete %>
 rails_ujs_fix = ->
-  return false if jQuery['rails'] == undefined
+  return false if 'rails' of jQuery

   jQuery.rails.handleMethod = (link) ->
     href = $.rails.href(link)


split types on space

split types on space

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/_module.coffee
+++ vendor/assets/javascripts/fustrate/_module.coffee
@@ -79,7 +79,7 @@
       .prop('class', "label #{type}#{text}".toLowerCase())

   @icon: (types) ->
-    classes = ("fa-#{type}" for type in types).join(' ')
+    classes = ("fa-#{type}" for type in types.split(' ')).join(' ')

     "<i class=\"fa #{classes}\"></i>"


Change Deps to Tracker

Change Deps to Tracker

--- sample.coffee
+++ sample.coffee
@@ -109,7 +109,7 @@

    # Set up an autorun to keep the X-Auth-Token cookie up-to-date and
    # to update the subscription when the userId changes.
-   Deps.autorun () ->
+   Tracker.autorun () ->
       userId = Meteor.userId()
       Meteor.subscribe 'allData', userId
       Meteor.subscribe 'allJobs', userId


use constants

use constants

--- workers/social/lib/social/models/log/index.coffee
+++ workers/social/lib/social/models/log/index.coffee
@@ -8,8 +8,7 @@

   TRY_LIMIT_FOR_BLOCKING = 5
-  @TIME_LIMIT_IN_MIN = 5
-  TIME_LIMIT_IN_MS = @TIME_LIMIT_IN_MIN * 60000 #TIME_LIMIT_IN_MIN * 60sec * 1000ms
+  TIME_LIMIT_IN_MIN = 5

   @set
     softDelete      : yes
@@ -69,7 +68,7 @@

     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 > 5
+    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


Collaboration: Fetch workspaces by machines of user

--- workers/social/lib/social/models/workspace.coffee
+++ workers/social/lib/social/models/workspace.coffee
@@ -1,4 +1,6 @@
 {Module} = require 'jraphical'
+
+JMachine = require './computeproviders/machine'

 module.exports = class JWorkspace extends Module

@@ -33,6 +35,7 @@
         deleteById : signature String, Function
         deleteByUid: signature String, Function
         update     : signature String, Object, Function
+        fetchByMachines: signature Function
       instance     :
         delete     : signature Function
     sharedEvents   :
@@ -99,6 +102,19 @@
     JWorkspace.some query, limit: 30, callback

+  @fetchByMachines$ = secure (client, callback) ->
+
+    client.connection.delegate.fetchUser (err, user) ->
+      query = 'users.id': user.getId()
+
+      JMachine.some query, {}, (err, machines) ->
+        return callback err  if err
+
+        machineUIds = machines.map (machine) -> machine.uid
+        JWorkspace.some machineUId: $in: machineUIds, {}, (err, workspaces) ->
+          callback err, workspaces
+
+
   @deleteById = secure (client, id, callback)->

     selector   =


Tests: Added editComment test.

Tests: Added editComment test.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -87,3 +87,27 @@
       .click                    commentSelector + ' [testpath=activity-like-link]'
       .waitForElementVisible    commentSelector + ' .liked:not(.count)', 10000 # Assertion
       .end()
+
+
+  editComment: (browser) ->
+
+    helpers.postComment(browser)
+
+    selector        = '[testpath=activity-list] section:nth-of-type(1) [testpath=ActivityListItemView]:first-child'
+    commentSelector = selector + ' .comment-container button.comment-menu'
+    post        =  faker.Lorem.paragraph().replace(/(?:\r\n|\r|\n)/g, '')
+
+    browser
+      .waitForElementPresent    commentSelector, 3000
+      .click                    commentSelector
+      .waitForElementVisible    '.kdcontextmenu .edit-comment', 5000
+      .click                    '.kdcontextmenu .edit-comment'
+      .clearValue               selector + ' .comment-container .comment-input-widget [testpath=CommentInputView]'
+      .setValue                 selector + ' .comment-container .comment-input-widget [testpath=CommentInputView]', post + '\n'
+      .pause                    3000
+
+    browser
+      .assert.containsText selector + ' .comment-container', post # Assertion
+      .end()
+
+


Minor refactoring.

Minor refactoring.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -16,12 +16,12 @@

     createAndSubmitForm = ->
       action = $link.attr("href")
-      $form = $("<form method='post' action='#{action}'></form>")
+      $form = $("<form />").attr("method", "post").attr("action", action)

       method = $link.data("method")
-      metadata_input = "<input name='_method' value='#{method}' type='hidden' />"
+      $metadataInput = $("<input />").attr("type", "hidden").attr("name", "_method").val(method)

-      $form.hide().append(metadata_input).appendTo("body");
+      $form.hide().append($metadataInput).appendTo("body");
       $form.submit()

     confirmationMessage = $link.data("confirm")


Allow to pass non-asynchronous data to GitLabDropdown

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -83,15 +83,19 @@
     search_fields = if @options.search then @options.search.fields else [];

     if @options.data
-      # Remote data
-      @remote = new GitLabDropdownRemote @options.data, {
-        dataType: @options.dataType,
-        beforeSend: @toggleLoading.bind(@)
-        success: (data) =>
-          @fullData = data
-
-          @parseData @fullData
-      }
+      # If data is an array
+      if _.isArray @options.data
+        @parseData @options.data
+      else
+        # Remote data
+        @remote = new GitLabDropdownRemote @options.data, {
+          dataType: @options.dataType,
+          beforeSend: @toggleLoading.bind(@)
+          success: (data) =>
+            @fullData = data
+
+            @parseData @fullData
+        }

     # Init filiterable
     if @options.filterable
@@ -204,7 +208,12 @@
     else
       selected = if @options.isSelected then @options.isSelected(data) else false
       url = if @options.url then @options.url(data) else "#"
-      text = if @options.text then @options.text(data) else ""
+
+      if @options.text?
+        text = @options.text(data)
+      else
+        text = data.text if data.text?
+
       cssClass = "";

       if selected


Remove unused variable

Remove unused variable

--- resources/assets/coffee/react/beatmap-discussions/nominations.coffee
+++ resources/assets/coffee/react/beatmap-discussions/nominations.coffee
@@ -328,8 +328,6 @@

   nominatorsList: =>
-    showHype = @props.beatmapset.can_be_hyped
-
     return null unless @props.beatmapset.status in ['wip', 'pending', 'ranked', 'qualified']

     nominators = []


Fixed bad reference to jasmine-jquery

Fixed bad reference to jasmine-jquery

--- app/assets/javascripts/jasminerice.js.coffee
+++ app/assets/javascripts/jasminerice.js.coffee
@@ -1,7 +1,7 @@
 #=require jasmine
 #=require jasmine-html
 #=require boot
-#=require jasmine-jquery-1.11.0.js
+#=require jasmine-jquery-1.5.8.js

 (->
   execJasmine = ->


Dim base layers when drawing

Dim base layers when drawing

Ref #905

--- app/assets/javascripts/map_display.js.coffee
+++ app/assets/javascripts/map_display.js.coffee
@@ -103,8 +103,8 @@
     @map.setView(center.latLon, center.zoom) if center.latLon
     return

-  addLayers: (opts = {}) ->
-    baseLayers = {}
+  addLayers: (opts = {}) =>
+    @baseLayers = {}
     for tileServer, idx in $("#map-tiles").data("tileservers")
       continue if (tileServer.url == "" or tileServer.name == "")
       options = jQuery.parseJSON(tileServer.options)
@@ -112,13 +112,13 @@
          tileLayer = L.tileLayer.wms(tileServer.url, options)
       else
          L.tileLayer(tileServer.url, options)
-      baseLayers[tileServer.name] = tileLayer
+      @baseLayers[tileServer.name] = tileLayer
       tileLayer.addTo(@map) if idx == 0
     additionalLayers = @remoteJSONLayer
     additionalLayers['Collisions'] = @collisionLayer if @collisionLayer
     additionalLayers['Photos'] = @photoLayer if @photoLayer

-    L.control.layers(baseLayers, additionalLayers).addTo(@map) unless opts.hidelayers
+    L.control.layers(@baseLayers, additionalLayers).addTo(@map) unless opts.hidelayers
     @map.addLayer @photoLayer if opts.photoselect

   buildPhotoLayer: (photoselect) =>
@@ -296,6 +296,8 @@
     $('.leaflet-draw-edit-edit')[0].click()

   addDraw: (feature) =>
+    layer.setOpacity(0.7) for _, layer of @baseLayers
+
     @drawnItems = new L.FeatureGroup()
     $('.icon-save').toggle()


fixed bootstrap-slider dep

fixed bootstrap-slider dep

--- app/assets/javascripts/require_config.coffee
+++ app/assets/javascripts/require_config.coffee
@@ -8,7 +8,7 @@
     "jquery"              : "../bower_components/jquery/jquery"
     "underscore"          : "../bower_components/lodash/dist/lodash"
     "bootstrap"           : "../bower_components/bootstrap/docs/assets/js/bootstrap"
-    "slider"              : "../bower_components/bootstrap-slider/bootstrap-slider"
+    "slider"              : "../bower_components/seiyria-bootstrap-slider/js/bootstrap-slider"
     "coffee-script"       : "../bower_components/coffee-script/extras/coffee-script"
     "backbone.marionette" : "../bower_components/backbone.marionette/lib/backbone.marionette"
     "backbone.paginator"  : "../bower_components/backbone.paginator/dist/backbone.paginator"


Comment

Comment

--- app/assets/javascripts/site_search.js.coffee
+++ app/assets/javascripts/site_search.js.coffee
@@ -64,7 +64,7 @@
           _gaq.push(['_trackEvent', 'SearchClickCategory', GAAction,  GALabel])

     # Override user agents and scroll to search box on narrow devices
-    window.scrollTo(0, 0) # restore the mess we make below on next page load
+    window.scrollTo(0, 0) # restore the mess we make below on next search page load
     $searchField.focus ->
       $("body").css("min-height", $(document).height() + $searchField.offset().top)
       y = $searchField.offset().top - 6


fix media track/caption XSS vulnerability, fixes CNVS-17659

ensure locale and other fields are properly sanitized when building
<track /> tags

test plan: see repro steps on CNVS-17659

Change-Id: I8016d05ce12854166ed467f06a2769c5f1bfa979
Reviewed-on: https://gerrit.instructure.com/46288
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Cody Cutrer <c4fe2b1d90ef8f2548c8aeabfa633434316f0305@instructure.com>
QA-Review: Caleb Guanzon <54fa1f30d451e4f3c341a8f3c47c9215fd0be9c6@instructure.com>
Product-Review: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>

--- app/coffeescripts/jquery/mediaComment.coffee
+++ app/coffeescripts/jquery/mediaComment.coffee
@@ -6,7 +6,8 @@
   'vendor/mediaelement-and-player'
   'jquery'
   'compiled/util/kalturaAnalytics'
-], (I18n, _, pubsub, mejs, $, kalturaAnalytics) ->
+  'str/htmlEscape'
+], (I18n, _, pubsub, mejs, $, kalturaAnalytics, htmlEscape) ->

   VIDEO_WIDTH = 550
   VIDEO_HEIGHT = 448
@@ -47,11 +48,11 @@
       # this 'when ...' is because right now in canvas, none of the mp3 urls actually work.
       # see: CNVS-12998
       sources = for source in data.media_sources when source.content_type isnt 'audio/mp3'
-        "<source type='#{source.content_type}' src='#{source.url}' title='#{source.width}x#{source.height} #{Math.floor(source.bitrate / 1024)} kbps' />"
+        "<source type='#{htmlEscape source.content_type}' src='#{htmlEscape source.url}' title='#{htmlEscape source.width}x#{htmlEscape source.height} #{htmlEscape(Math.floor(source.bitrate / 1024))} kbps' />"

       tracks = _.map data.media_tracks, (track) ->
         languageName = mejs.language.codes[track.locale] || track.locale
-        "<track kind='#{track.kind}' label='#{languageName}' src='#{track.url}' srclang='#{track.locale}' />"
+        "<track kind='#{htmlEscape track.kind}' label='#{htmlEscape languageName}' src='#{htmlEscape track.url}' srclang='#{htmlEscape track.locale}' />"

       types = _.map data.media_sources, (source) -> source.content_type
       dfd.resolve {sources, tracks, types, can_add_captions: data.can_add_captions}
@@ -62,7 +63,8 @@
     st_tags = sourcesAndTracks.sources.concat(sourcesAndTracks.tracks).join('')

     getElement = (tagType) ->
-      $("<#{tagType} #{if mediaType is 'video' then "width='#{width}' height='#{height}'" else ''} controls>#{st_tags}</#{tagType}>")
+      html = "<#{tagType} #{if mediaType is 'video' then "width='#{width}' height='#{height}'" else ''} controls>#{st_tags}</#{tagType}>"
+      $(html)

     willPlayAudioInFlash = ->
       opts = $.extend({mode: 'auto'}, mejs.MediaElementDefaults, mejs.MepDefaults,mediaPlayerOptions)


moved test around

moved test around


check for duplicates on rename

check for duplicates on rename

--- services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
+++ services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
@@ -457,16 +457,16 @@
 			return callback(error) if error?
 			ProjectEntityHandler.getAllEntitiesFromProject project, (error, oldDocs, oldFiles) =>
 				return callback(error) if error?
-				projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (error, entity, entPath)=>
+				projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (error, entity, entPath, parentFolder)=>
 					return callback(error) if error?
-					ProjectEntityHandler.checkElementName entity, newName, (err) =>
-						return callback(err) if err?
+					# check if the new name already exists in the current folder
+					ProjectEntityHandler.checkElementName parentFolder, newName, (error) =>
+						return callback(error) if error?
 						endPath = path.join(path.dirname(entPath.fileSystem), newName)
 						conditions = {_id:project_id}
 						update = "$set":{}
 						namePath = entPath.mongo+".name"
 						update["$set"][namePath] = newName
-						# FIXME check if this would create a duplicate file!
 						tpdsUpdateSender.moveEntity({project_id:project_id, startPath:entPath.fileSystem, endPath:endPath, project_name:project.name, rev:entity.rev})
 						Project.findOneAndUpdate conditions, update, { "new": true}, (error, newProject) ->
 							return callback(error) if error?


Use try to prevent crash on wrong lifecycle function

--- views/services/plugin-manager.cjsx
+++ views/services/plugin-manager.cjsx
@@ -434,7 +434,10 @@
         plugin.handleClick = ->
           plugin.pluginWindow.show()
     # Lifecycle
-    plugin.pluginDidLoad() if typeof plugin.pluginDidLoad is 'function'
+    try
+      plugin.pluginDidLoad() if typeof plugin.pluginDidLoad is 'function'
+    catch error
+      console.log error
     @emitReload()

   # unload one plugin
@@ -442,7 +445,10 @@
   unloadPlugin: (plugin) ->
     return if !plugin?
     # Lifecycle
-    plugin.pluginWillUnload() if typeof plugin.pluginWillUnload is 'function'
+    try
+      plugin.pluginWillUnload() if typeof plugin.pluginWillUnload is 'function'
+    catch error
+      console.log error
     # Destroy window
     windowManager.closeWindow(plugin.pluginWindow) if plugin.pluginWindow?
     @emitReload()


fix closing a user position doesn’t navigate you back

Former-commit-id: b9809d9f43b0c92cb8eab8651c3784cf06f59606 [formerly 0c074323d8781474e945a67f0b48de5d8a61536b] [formerly 5eedf740d7fe0bb01286787f0323058a14e6fffa [formerly 5eedf740d7fe0bb01286787f0323058a14e6fffa [formerly 14005bf1e1ba8ba31adb37128f03f3de144bba6b]]]
Former-commit-id: 4d948e2b9a0588897e809b33dfa32b11e017ba9d [formerly 900c2bea54eae325ae4e31c7577a3ad59104d0c2]
Former-commit-id: 7536cf0a4992ee71b929ccb72a6e5419dd0f6e07
Former-commit-id: 48515b7636db7cb4bcdf5c71528b0b476991cb71

--- app/assets/javascripts/backbone/apps/franklin/position/position_controller.coffee
+++ app/assets/javascripts/backbone/apps/franklin/position/position_controller.coffee
@@ -11,7 +11,7 @@
       @dialog_overlay = @getOverlay view

       @listenTo @dialog_overlay, 'close', ->
-        #TODO: navigate back
+        App.request 'nav:back:crumb'

     setupView : (view) ->


updated target chart area use units. Closes #1094

--- app/assets/javascripts/d3/target_bar.coffee
+++ app/assets/javascripts/d3/target_bar.coffee
@@ -15,7 +15,7 @@
     ],
     area_targets: [
       {key: 'onshore_land',           unit: 'km2'},
-      {key: 'onshore_coast',          unit: 'km2'},
+      {key: 'onshore_coast',          unit: 'km'},
       {key: 'offshore',               unit: 'km2'}
     ]


Removed localstorage comment

Removed localstorage comment

--- app/assets/javascripts/lib/core/base.coffee
+++ app/assets/javascripts/lib/core/base.coffee
@@ -60,7 +60,7 @@
             onAdd : ->
               window.setTimeout( ( => $('div.js-cookie-compliance').addClass('is-open')), 1)
         msg = new Msg(args)
-        # LocalStore.set('cookie-compliance', true)
+        LocalStore.set('cookie-compliance', true)

     addNavTracking: ->
       $('#js-primary-nav').on 'click', '.js-nav-item', ->


Without mtl.ready

Without mtl.ready

--- app/assets/javascripts/mtl/data_href.coffee
+++ app/assets/javascripts/mtl/data_href.coffee
@@ -1,6 +1,5 @@
-MTL.onReady ->
-  $(document).on 'click', '[data-mtl-href]', (ev) ->
-    window.location.href = $(ev.currentTarget).data 'mtl-href'
+$(document).on 'click', '[data-mtl-href]', (ev) ->
+  window.location.href = $(ev.currentTarget).data 'mtl-href'

-  $(document).on 'click', '[data-mtl-href] a', (ev) ->
-    ev.stopPropagation()
+$(document).on 'click', '[data-mtl-href] a', (ev) ->
+  ev.stopPropagation()


Fix focus when going back to root

Fix focus when going back to root

--- src/views/service-tree.coffee
+++ src/views/service-tree.coffee
@@ -242,14 +242,14 @@
             data

         onDomRefresh: ->
-            $target = null
-            if @collection.chosenServiceNode
-                $target = @$el.find('li.service-node.parent.header-item')
-            else
-                $target = @$el.find('li.service-node').first()
-            _.defer =>
-                $target
-                .focus()
-                .addClass('autofocus')
-                .on 'blur', () ->
-                    $target.removeClass('autofocus')
+            target = null
+            #if @collection.chosenServiceNode
+            #    $target = @$el.find('li.service-node.parent.header-item')
+            #else
+            #    $target = @$el.find('li.service-node').first()
+            #_.defer =>
+            #    $target
+            #    .focus()
+            #    .addClass('autofocus')
+            #    .on 'blur', () ->
+            #        $target.removeClass('autofocus')


Fix polls graphs percentage bar lengths

Fix polls graphs percentage bar lengths

--- static/coffee/v2_polls.coffee
+++ static/coffee/v2_polls.coffee
@@ -242,11 +242,13 @@
       data = []
       for category in segment.categories
         categories.push(category.label)
+        cat_percentage = if segment.set > 0 then Math.round(category.count / segment.set * 100) else 0
+
         data.push({
           name: category.label
-          y: category.count
-          weight: category.count
-          percent: if segment.set > 0 then Math.round(category.count / segment.set * 100) else 0
+          y: cat_percentage
+          weight: cat_percentage
+          percent: cat_percentage
         })

       # ignore states that aren't included


added hourly events to week view

added hourly events to week view

--- app/coffee/drm-calendar/drm-calendar.coffee
+++ app/coffee/drm-calendar/drm-calendar.coffee
@@ -572,8 +572,15 @@
             calendarInner = @calendar.find "div.#{@calendarInnerClass}"
             if @view is 'month'
                 calendarItem = calendarInner.find "td[data-date=#{dates}]"
-            else if @view is 'week'
+            else if @view is 'week' and (events.allDayEvent is true or !events.time?)
                 calendarItem = calendarInner.find "td[data-date=#{dates}][data-hour='All Day Event']"
+            else if @view is 'week' and events.allDayEvent is false
+                # find hour td element
+                console.log events.time
+                re = new RegExp '[0:]','gi'
+                hour = events.time.replace(re, '')
+                console.log hour
+                calendarItem = calendarInner.find "td[data-date=#{dates}][data-hour=#{hour}]"
             eventList = calendarItem.find "ul.#{@eventClass}"
             length = eventList.length

@@ -1262,8 +1269,8 @@
         name: "One Time Event"
         month: "May"
         year: 2014
-        time: '1:00'
-        eventDate: 2
+        time: '1:00pm'
+        eventDate: 4
         type: "test"
         recurrance: "none"
         allDayEvent: false


Revert "Fix #795: Close lightbox only if buttons, ESC key or the X icon is pressed"

This reverts commit 54cbfd9c921f5397b228bdb0195dba4707de2902.

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -31,12 +31,18 @@
             lightbox.addClass('open')
         ), 70

+        lightbox.on 'click', (e) =>
+            target = angular.element(e.target)
+            if target[0] == lightbox[0]
+                @close(lightbox)
+
         $(document)
             .on 'keydown.lightbox', (e) =>
                 code = if e.keyCode then e.keyCode else e.which

                 if code == 27
                     @close(lightbox)
+

     close: (lightbox) ->
         $(document).off('.lightbox')
@@ -46,7 +52,6 @@
             .removeClass('open')

 module.service("lightboxService", LightboxService)
-

 LightboxDirective = (lightboxService) ->
     link = ($scope, $el, $attrs) ->


Remove console.log...

Remove console.log...

--- app/assets/javascripts/admin/views/election/propositions/propositions_list/sub_sub_tag_view.js.coffee
+++ app/assets/javascripts/admin/views/election/propositions/propositions_list/sub_sub_tag_view.js.coffee
@@ -45,5 +45,4 @@
     params = text: event.target.text.value
     params['tagIds'] = @subSubTag.id
     params['candidacyId'] = @candidacy.id
-    console.log params
     @propositions.create {proposition: params }, url: '/api/v1/propositions', type: 'POST'


changes due to refactoring of sphere class

--- src/app.coffee
+++ src/app.coffee
@@ -1,15 +1,16 @@
-threenorama = require('./sphere.coffee')
+SphereSegment = require('./components/sphereSegment/SphereSegment.coffee')
 fabric = require('../bower_components/fabric.js/dist/fabric.require.js')

-fullSphereCamera =
-  panMin: 0         # 0°
-  panMax: 2*Math.PI # 306°
-  tiltMin: 0        # 0°
-  tiltMax: Math.PI  # 180°
+panSegment =
+  min: 1.5*Math.PI   # 0°
+  max: 2*Math.PI # 306°
+tiltSegment =
+  min: 0 # 90°
+  max: Math.PI  # 180°
 pictureSize =
   pan: Math.PI/4    # 45° =>  8*45° = 360°
   tilt: Math.PI/10  # 18° => 10*18° = 180°
-sphere = new threenorama.Sphere(fullSphereCamera, pictureSize)
+sphere = new SphereSegment(tiltSegment, panSegment, pictureSize)
 canvas = new fabric.StaticCanvas('c')
 canvas.setHeight(600)
       .setWidth(1400)
@@ -26,7 +27,7 @@
       originY: 'center'
       left: panToCanvas(col.pan)
       top: tiltToCanvas(col.tilt)
-      height: tiltToCanvas(sphere.pictureSize.tilt)
+      height: tiltToCanvas(row.pictureSize.tilt)
       # width: panToCanvas(pictureSize.pan*2*Math.PI/row.panArc)
       width: panToCanvas(row.pictureSize.pan)
       fill: 'red'


Change name of created_at field in views for grammar

--- src/app.coffee
+++ src/app.coffee
@@ -40,6 +40,9 @@
       # pass
     else if Buffer.isBuffer(json[k])
       # already handled
+    else if k == 'created_at'
+      # change the name of this key for display purposes
+      fields.props['created'] = moment(v).fromNow()
     else if v instanceof Date
       fields.props[k] = moment(v).fromNow()
     else
@@ -61,6 +64,9 @@
       # pass
     else if v instanceof Date
       fields.props[k] = moment(v).fromNow()
+    else if k == 'created_at'
+      # change the name of this key for display purposes
+      fields.props['created'] = v
     else
       fields.props[k] = if v? then v else 'not present'


Fix AMD and Phaser integration.

Fix AMD and Phaser integration.

--- src/app.coffee
+++ src/app.coffee
@@ -11,6 +11,9 @@
   baseUrl: 'lib'
   paths:
     app: '../release'
+  shim:
+    phaser:
+      exports: 'Phaser'
 requirejs [
   'phaser'
   'app/game'


restructured people views to fit the new model

--- src/app.coffee
+++ src/app.coffee
@@ -372,8 +372,7 @@
       that = @
       require ["backgrid"], (Backgrid) ->
         myCell = Backgrid.Cell.extend
-                viewingPerson: ->
-                  console.log "clicked"
+                viewingPerson: (e) ->
                   e.preventDefault();
                   return router.navigate "view/" + this.model.get("id"),
                     trigger: true
@@ -443,78 +442,53 @@

   PersonSingleView = Backbone.View.extend(
     el: "#page"
-    fuzzyDateRenderer: (type, date1, date2) ->
-
-      #takes the HL way of representing fuzzy dates, and returns a formatted string.
-      switch type
-        when "exactly"
-          return date1
-        when "between"
-          return "between " + ((if isset(date1) then date1 else "unknown")) + " and " + ((if isset(date2) then date2 else "unknown"))
-        when "unknown", null
-          "unknown"
+    initialize: ->
+      @listenTo @model, 'change', @.render
+
+      @model.fetch
+        dataType: "jsonp"

     render: (options) ->
-      that = this
+      that = @
       require ["text!../templates/view.html.tpl"], (template) ->
         that.$el.html _.template(template)
         return
-
-      person = new Person(id: options.id)
-      person.fetch
-        dataType: "jsonp"
-        success: (person) ->
+      # render the person partial
+      require ["text!../templates/view_details.html.tpl"], (template) ->
+        $("#person-details").html _.template(template,
+          person: that.model
+        )
+        return

-          # use some render helpers to add to the model some text to format fuzzy date types
-          person.set
-            birthdate: that.fuzzyDateRenderer(person.get("birthtype"), person.get("birthdate1"), person.get("birthdate2"))
-            deathdate: that.fuzzyDateRenderer(person.get("deathtype"), person.get("deathdate1"), person.get("deathdate2"))
-
+      # now we have a name, add this to the breadcrumb
+      template = _.template($("#breadcrumb-template").html(),
+        breadcrumbs: [
+          url: "#view/" + @model.get("id")
+          title: @model.get("forenames") + " " + @model.get("surname")
+        ]
+      )
+      $("#breadcrumb").html template
+
+
+      if @model.get("relationships").models.length > 0
+        require ["text!../templates/view_relationships.html.tpl"], (template) ->
+          $("#relationships").html _.template template,
+            relationships: that.model.get("relationships").models

-          # render the person partial
-          require ["text!../templates/view_details.html.tpl"], (template) ->
-            $("#person-details").html _.template(template,
-              person: person
-            )
-            return
-
-
-          # now we have a name, add this to the breadcrumb
-          template = _.template($("#breadcrumb-template").html(),
-            breadcrumbs: [
-              url: "#view/" + person.id
-              title: person.get("forenames") + " " + person.get("surname")
-            ]
-          )
-          $("#breadcrumb").html template
-          return
-
-
-      #create a new relationships collection for this person
-      relationships = new Relationships(id: options.id)
-
-      #fetch the relationships collection with an ajax call
-      relationships.fetch
-        dataType: "jsonp"
-        success: (relationships) ->
-          template = undefined
-
-          #evaluate whether this person has any relationships in the collection to display
-          if relationships.models.length > 0
-            require ["text!../templates/view_relationships.html.tpl"], (template) ->
-              $("#relationships").html _.template(template,
-                relationships: relationships.models
-              )
-              return
-
-          else
-
-            # can't name the person because it is loaded syncronously.
-            template = "No relationships were found."
-            $("#relationships").html template
-          return
-
-      return
+      else
+        # can't name the person because it is loaded syncronously.
+        template = "No relationships were found for " + @model.get("forenames") + "."
+        $("#relationships").html template
+
+      if @model.get("occupations").models.length > 0
+        require ["text!../templates/view_occupation.html.tpl"], (template) ->
+          $("#bibliographical-details").append _.template template,
+            occupations: that.model.get("occupations").models
+      if @model.get("addresses").models.length > 0
+        require ["text!../templates/view_address.html.tpl"], (template) ->
+          $("#bibliographical-details").append _.template template,
+            addresses: that.model.get("addresses").models
+
   )
   Router = Backbone.Router.extend(routes:
     "": "home"
@@ -538,8 +512,10 @@
     return

   router.on "route:viewPerson", (id) ->
-    personSingleView = new PersonSingleView()
-    personSingleView.render id: id
+    if person = Person.findModel {id: id}
+    else
+      person = new Person id: id
+    personSingleView = new PersonSingleView({model: person})
     return

   router.on "route:viewPersonResults", (querystring) ->


fix: loxgraph now works in firefox

fix: loxgraph now works in firefox

--- script.coffee
+++ script.coffee
@@ -76,7 +76,11 @@
   @parseXML: (sourceId, xmlDoc) =>
       ractive.set 'progress', ((ractive.get 'current') / (ractive.get 'total') * 100).toFixed(0)
       parsedData = $(xmlDoc).find('S').map ->
-        timestamp = new Date($(this).attr('T').replace(' ', 'T'))
+        dateString = $(this).attr('T')
+        # ugly hack to fix date parsing differences between chrome & firefox
+        dateString = dateString.replace(' ', 'T') if /Firefox/.test(navigator.userAgent)
+        # /hack
+        timestamp = new Date(dateString)
         value = parseFloat($(this)[0].attributes[1].value)
         [[timestamp, value]]
       .toArray()


add stack to log of uncaught exceptions logging

--- server.coffee
+++ server.coffee
@@ -9,6 +9,7 @@

 process.on 'uncaughtException', (err) ->
     console.log err
+    console.log err.stack
     setTimeout ->
         process.exit 1
     , 1000


Disable log messages

Disable log messages

--- server.coffee
+++ server.coffee
@@ -68,7 +68,7 @@

 # Get background location from player.
 exports.onGeoloc = (userId, geoloc) ->
-	log '[onGeoloc()] Geoloc from ' + Plugin.userName(userId) + '('+userId+'): ', JSON.stringify(geoloc)
+	#log '[onGeoloc()] Geoloc from ' + Plugin.userName(userId) + '('+userId+'): ', JSON.stringify(geoloc)
 	#Store lat and lng for other uses
 	Db.personal(userId).set 'location', 'latitude', geoloc.latitude
 	Db.personal(userId).set 'location', 'longitude', geoloc.longitude
@@ -81,7 +81,6 @@
 			if (parseInt(beacon.peek('owner'),10) != parseInt(getTeamOfUser(userId),10)) and !found
 				if distance(geoloc.latitude, geoloc.longitude, beacon.peek('location', 'lat'), beacon.peek('location', 'lng')) < beaconRadius
 					found= true;
-					log 'beacon comparison: ', beacon.key() is Db.personal(userId).peek('lastNotification', 'beaconNumber')
 					if beacon.key() isnt Db.personal(userId).peek('lastNotification', 'beaconNumber')
 						#send notifcation
 						Event.create
@@ -631,7 +630,7 @@
 		if maxScore < team.peek('teamScore')
 			teamMax = team.key()
 			maxScore = team.peek('teamScore')
-	log "[getFirstTeam()] teamMax: " + teamMax
+	#log "[getFirstTeam()] teamMax: " + teamMax
 	return teamMax

 # Modify user and team scores by adding "points" to the current value
@@ -662,12 +661,10 @@
 	teamMax = getFirstTeam()
 	newLead = false;
 	newLead = teamMax isnt Db.shared.peek('game', 'firstTeam')
-
-	log "[checkNewLead()] newLead: " + newLead + " "
-
 	# create score event
 	# To Do: personalize for team members or dubed players
 	if newLead
+		log "[checkNewLead()] newLead: " + newLead + " "
 		addEvent {
 			timestamp: new Date()/1000
 			type: "score"


Move data at end of game or when game is reset

GameState indicates whether the game has ended or user decided to remake

--- server.coffee
+++ server.coffee
@@ -58,6 +58,7 @@
 # Config changes (by admin or plugin adder)
 exports.onConfig = (config) ->
 	if config.restart
+		moveData()
 		log '[onConfig()] Restarting game'
 		initializeGame()

@@ -111,7 +112,9 @@
 #==================== Client calls ====================
 # Restarts game
 exports.client_restartGame = () ->
-	#TODO store old game in Db
+	#Store old game data in history
+	moveData()
+	#Reset game database
 	initializeGame()

 # Add a beacon (during setup phase)
@@ -603,7 +606,7 @@
 # function called everytime scores are modified to check wheter there is a new leading team or not
 checkNewLead = ->
 	teamMax = getFirstTeam()
-	newLead = false
+	newLead = false;
 	newLead = teamMax isnt Db.shared.peek('game', 'firstTeam')

 	log "[checkNewLead()] newLead: " + newLead + " "
@@ -644,3 +647,12 @@
     	unit: 'toRest'
     	exclude: members
     	text: message
+
+#Move all data to history tab
+moveData = ->
+	current = Db.shared.peek('gameNumber')
+	Db.shared.set 'history', current,'game', Db.shared.peek('game')
+	Db.shared.set 'history', current, 'gameState', Db.shared.peek('gameState')
+
+
+


Properly use a zero indexed array

Properly use a zero indexed array

--- server.coffee
+++ server.coffee
@@ -201,7 +201,7 @@

             if rank
                 if self in [1..3]
-                    for i in [1..3]
+                    for i in [0..2]
                         if results[self][1] is results[i][1]
                             diff = 0


Optimization.

Optimization.

--- server.coffee
+++ server.coffee
@@ -118,7 +118,8 @@
   updateSource: (id, fields) =>
     if _.isEmpty fields
       fields._id = id
-    else
+    # TODO: Not completely correct when @fields contain multiple fields from same subdocument (they will be counted only once) - because Meteor always passed whole subdocuments we could count only top-level fields in @fields
+    else if _.size(fields) isnt @fields.length
       referenceFields =
         _id: 1 # To make sure we do not pass empty set of fields
       for f in @fields
@@ -132,6 +133,8 @@
       unless fields
         fields =
           _id: id
+    else
+      fields._id = id

     [selector, sourceValue] = @generator fields


Changes for data collection

Changes for data collection

--- server.coffee
+++ server.coffee
@@ -14,7 +14,6 @@
 	initializeColors()
 	initializeGame()
 	registerPlugin()
-	Db.backend.set('history', 'groupCode', Plugin.groupCode())

 # Game update
 exports.onUpgrade = ->
@@ -29,11 +28,6 @@
 		newVersion = 10
 		initializeColors()
 		Db.shared.remove 'history'
-
-	if version < 11
-		newVersion = 11
-		Db.backend.set('history', 'groupCode', Plugin.groupCode())
-
 	if version < 12
 		newVersion = 12
 		Db.backend.remove('collectionRegistered')
@@ -124,7 +118,7 @@
 		if MD5.externmd5(request.data) is Config.onHTTPKey
 			moveData()
 			request.respond 200, JSON.stringify(Db.backend.peek('history'))
-			log '[onHTTP()] succesfully sent database'
+			log '[onHTTP()] succesfully sent database, id='+Plugin.groupCode()
 			return 0
 	request.respond 200, 'wrong key'
 	log '[onHTTP()] failed attempt to sent database'
@@ -699,7 +693,7 @@

 # Adds event to the eventlist
 addEvent = (eventArgs) ->
-	maxId = Db.shared.peek 'game', 'eventlist', 'maxId'
+	maxId = Db.shared.peek('game', 'eventlist', 'maxId')
 	log "[addEvent()] Event: " + eventArgs.type + " id: " + maxId
 	Db.shared.set 'game', 'eventlist', maxId, eventArgs
 	Db.shared.modify 'game', 'eventlist', 'maxId', (v) -> v + 1
@@ -729,6 +723,8 @@

 #Move all data to history tab
 moveData = ->
+	if not (Db.backed.peek('history', 'groupCode')?)
+		Db.backed.set 'history', 'groupCode', Plugin.groupCode()
 	current = Db.shared.peek('gameNumber')
 	if current?
 		Db.backend.set 'history', current,'game', Db.shared.peek('game')


We have to know in observeChanges if we are in a reactive context for reactive publish.

Not clear why this would be necessary. See
https://github.com/Diggsey/meteor-reactive-publish/commit/a1bed70a44cd4c
9680ce817503282cbfaa8a7cb1

--- server.coffee
+++ server.coffee
@@ -34,8 +34,7 @@
   initializing = false

 MeteorCursor::observeChanges = (options) ->
-  handle = Tracker.nonreactive =>
-    originalObserveChanges.call @, options
+  handle = originalObserveChanges.call @, options
   if Tracker.active and @_isReactive()
     Tracker.onInvalidate =>
       handle.stop()


Moved general /:thing/ to bottom of routes.

Other endpoints where not working since it will match anything.

--- server.coffee
+++ server.coffee
@@ -41,7 +41,7 @@
   res.header 'Access-Control-Allow-Methods', 'GET, OPTIONS'
   res.header 'Access-Control-Allow-Headers', 'Content-Type'
   next()
-
+
 app.use(app.router)
 app.use(express.static('./public'))
 app.use (req, res) ->
@@ -128,11 +128,6 @@
   subtitle = "- #{req.params.from}"
   dooutput(res, message, subtitle)

-app.get '/:thing/:from', (req, res) ->
-  message = "Fuck #{req.params.thing}."
-  subtitle = "- #{req.params.from}"
-  dooutput(res, message, subtitle)
-
 app.get '/flying/:from', (req, res) ->
   message = "I don't give a flying fuck. - #{req.params.from}."
   subtitle = "- #{req.params.from}"
@@ -143,8 +138,13 @@
   subtitle = "- #{req.params.from}"
   dooutput(res, message, subtitle)

+app.get '/:thing/:from', (req, res) ->
+  message = "Fuck #{req.params.thing}."
+  subtitle = "- #{req.params.from}"
+  dooutput(res, message, subtitle)
+
 operations(app)

-port = process.env.PORT || 5000
+port = process.env.PORT || 5000
 app.listen port
 console.log "FOAAS Started on port #{port}"


recommendables storage is a set now, objects/item are stored in the 'hash' property of the set

--- server.coffee
+++ server.coffee
@@ -1,26 +1,19 @@
 class Server

-  @recommendables = []
-
+  set = require './Set'
+  @recommendables = set.createSet()
+
   http = require 'http'
-
-  # helper function that responds to the client
-  @respond: (res, code, contentType, data) ->
-      res.writeHead code,
-          'Content-Type': contentType
-          'Content-Length': data.length
-      res.write data
-      res.end()

   @setup: =>
     @server = http.createServer (request, response) =>
-      console.log "Request: #{request.method} #{request.url}"
+      #console.log "Request: #{request.method} #{request.url}"
       data = @testJSON()
       request.on 'data', (chunk) -> data += chunk
       request.on 'end', () =>
-        console.log "Data: #{data}"
+       # console.log "Data: #{data}"
         requestObject = JSON.parse(decodeURIComponent(data.trim()))
-        console.log requestObject
+        #console.log requestObject
         switch requestObject.msg
           when 'feedback'
             @processFeedback(requestObject)
@@ -42,7 +35,7 @@

   @saveItem: (item) ->
     console.log 'Save the item. Recommendable items now are:'
-    @recommendables.push item
+    @recommendables.add item
     console.log @recommendables

   @makeRecommendation: (requestObject) ->
@@ -50,6 +43,14 @@

   @processError: (requestObject) ->
     console.log 'Just received an error'
+
+  # helper function that responds to the client
+  @respond: (res, code, contentType, data) ->
+      res.writeHead code,
+          'Content-Type': contentType
+          'Content-Length': data.length
+      res.write data
+      res.end()

   @start: ->
     @server.listen process.env.C9_PORT


fix exceptions dumping

fix exceptions dumping

--- server.coffee
+++ server.coffee
@@ -49,5 +49,6 @@
 setInterval gameController.manageEnergy, constants.energy.frequencyMs
 setInterval gameController.assignTeams, 30000

-process.on 'uncaughtException', (err) ->
-    console.error err+# log errors
+process.on 'uncaughtException', (err) ->
+    console.error err.stack