client-side failover for tellKite

client-side failover for tellKite

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -45,6 +45,20 @@
       changeTransport = (channelId, {transport})->
         log 'CHANGING TRANSPORT: ', transport
         transports[channelId] = transport
+
+      resetTransport = (channelId)->
+        delete transports[channelId]
+
+      getCommandFailover =(secretChannelId, options, callback)->
+        kallback = __utils.getCancellableCallback callback
+        kanceller = __utils.getCancellableCallback =>
+          kallback.cancel()
+          resetTransport(secretChannelId)
+          @tellKite options, callback
+        setTimeout kanceller, 5000
+        ->
+          kanceller.cancel()
+          kallback arguments...

       sendScrubbedCommand =(channelId, url, options)->
         transport = transports[channelId]
@@ -113,9 +127,10 @@
                 callback channel

       (options, callback)->
+        account = @
         @fetchKiteChannelName options.kiteName, (err, secretChannelId)->
           fetchChannel secretChannelId, (channel)->
             options.secretChannelId = secretChannelId
             options.withArgs or= {}
-            args = [options, callback]
+            args = [options, getCommandFailover.call account, secretChannelId, options, callback]
             sendCommand secretChannelId, options.kiteName, args


Feature: cookie class

Feature: cookie class

- Developed a class to handle cookies.

This class was developed to keep track of when users send messages to me. I
don't want the same guy blasting me with emails daily just because he forgot
that he already contacted me.

--- assets/javascripts/contact.coffee
+++ assets/javascripts/contact.coffee
@@ -2,6 +2,48 @@
 ---
 class ContactFormValidation
   constructor: (@form, @flashDiv) ->
+    class Cookie
+      # Class methods
+      constructor: (@name, value, expiryInDays = 0) ->
+        @expiry = ''
+        if expiryInDays
+          date = new Date()
+          date.setTime date.getTime() + (expiryInDays * 24 * 60 * 60 * 1000)
+          @expiry = date.toGMTString()
+
+        @value = switch
+          when typeof value is 'string' or typeof value is 'number' then value
+          when typeof value is 'object' then JSON.stringify value
+
+      @find = (name) ->
+        return cookie for cookie in document.cookie.split ';' when !!~cookie.indexOf name
+
+      # Public methods
+      unserializedValue: =>
+        cookieStr = Cookie.find @name
+
+        if not cookieStr
+          return false
+
+        escapedValue = cookieStr
+        .split('=')[1]
+        unescapedValue = unescape escapedValue
+        try
+          JSON.parse unescapedValue
+        catch
+          unescapedValue
+
+      save: =>
+        document.cookie = "#{@name}=#{escape @value}; expires=#{@expiry}; path=/"
+
+      del: =>
+        document.cookie = "#{@name}=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path="
+
+    if not Cookie.find 'state'
+      object =
+    	  'didContact':true
+      cookie = new Cookie 'state', object, 7
+
     $ @form
       .find 'button[type="submit"]'
         .click (event) =>
@@ -58,6 +100,12 @@
           data: data
           method: 'POST'
           error: (jqXHR, textStatus, errorThrown) ->
+            message = switch
+              when textStatus is 'abort' then 'The operation has been cancelled.'
+              when textStatus is 'error' then 'Unknown error.'
+              when textStatus is 'parsererror' then 'Failed parsing the response.'
+              when textStatus is 'timeout' then 'The operation timed-out.'
+
             $ submitButton
             	.prop 'disabled', false
             $ flashDiv
@@ -66,6 +114,9 @@
             $ flashDiv
               .find '.title'
               .text 'Error'
+            $ flashDiv
+              .find '.message'
+              .text message
           success: (data, textStatus, jqXHR) ->
             $ flashDiv
               .removeClass 'alert-success'
@@ -73,10 +124,10 @@
             $ flashDiv
               .find '.title'
               .text 'Success!'
-          complete: (data, textStatus, jqXHR) ->
             $ flashDiv
               .find '.message'
-              .text textStatus
+              .text 'Message successfully sent.'
+          complete: (jqXHR, textStatus) ->
             # Show the flash `<div>` and then hide it after 7 seconds
             $ flashDiv
               .show 'slow'


fixes #303

fixes #303

--- resource/translators/translator.coffee
+++ resource/translators/translator.coffee
@@ -467,6 +467,10 @@
         fields.push({ name: @CSLtoBibTeX[name], value: value.value })
       else
         Translator.debug('Unmapped CSL field', name, '=', value.value)
+      continue
+
+    if value.value.trim() == ''
+      @remove(name)
       continue

     switch name


Group: better error message for .expose()

--- src/Group.coffee
+++ src/Group.coffee
@@ -50,7 +50,7 @@
     new Buffer options

   expose: (exposedName, bufferPath) ->
-    throw new Error 'Already exposed a buffer with that name' if @_exposedBuffers[exposedName]?
+    throw new Error "Already exposed a buffer with name #{exposedName}" if @_exposedBuffers[exposedName]?
     @_exposedBuffers[exposedName] = @_getBufferByPath(bufferPath)

   _getBufferByPath: (bufferPath) ->


Overlays are special and are independent from routing.

--- client/base.coffee
+++ client/base.coffee
@@ -48,13 +48,13 @@
   Session.set 'currentSearchLimit', INITIAL_SEARCH_LIMIT
   Session.set 'searchActive', false
   Session.set 'searchFocused', false
-  Session.set 'importOverlayActive', false
-  Session.set 'loginOverlayActive', false
   Session.set 'adminActive', false
   Session.set 'currentPublicationId', null
   Session.set 'currentPublicationSlug', null
   Session.set 'currentPublicationProgress', null
   Session.set 'currentPersonSlug', null
+
+  # Overlays are special and we do not clear them while routing

 # TODO: Use real parser (arguments can be listed multiple times, arguments can be delimited by ";")
 parseQuery = (qs) ->


:shirt:

:shirt:

--- lib/markdown-preview-view.coffee
+++ lib/markdown-preview-view.coffee
@@ -140,7 +140,7 @@
           Promise.reject("#{@file.getBaseName()} could not be found")
         else
           Promise.resolve(source)
-      .catch (reason) => Promise.reject(reason)
+      .catch (reason) -> Promise.reject(reason)
     else if @editor?
       Promise.resolve(@editor.getText())
     else


"Correct code correctly prune oldest plunks"

--- lib/plunker/stores/memory.coffee
+++ lib/plunker/stores/memory.coffee
@@ -44,7 +44,7 @@
     @plunks.on "reset add", ->
       # Defer to next tick or something around then
       delay 1, ->
-        if self.plunks.length > self.options.size
+        while self.plunks.length > self.options.size
           self.plunks.remove self.plunks.at(self.plunks.length - 1)


Update clock.coffee

Update clock.coffee

Updating setTime minute interval timing

--- clock.coffee
+++ clock.coffee
@@ -106,7 +106,7 @@
     # start ticking, update all contexts, colors every second
     window.setInterval ( => @setTime('all') ), 1000
     # start ticking, update minute context
-    window.setInterval ( => @setTime('minute') ), 100
+    window.setInterval ( => @setTime('minute') ), 20
     # save configuration every 10 seconds
     window.setInterval ( => @saveConfig() ), 10000


don't log out when doc lines are [] as this is a new empty doc

--- services/docstore/app/coffee/DocManager.coffee
+++ services/docstore/app/coffee/DocManager.coffee
@@ -16,7 +16,8 @@
 					if docFromDocCollection?
 						return callback null, docFromDocCollection, mongoPath
 					else if doc?
-						logger.warn project_id:project_id, doc_id:doc_id, "doc just used from project collection, why?"
+						if doc?.lines?.length > 0
+							logger.warn project_id:project_id, doc_id:doc_id, "doc just used from project collection, why?"
 						return callback null, doc, mongoPath
 					else
 						return callback new Errors.NotFoundError("No such doc: #{project_id}")


Removed buggy Model/Base.createWithData update functionality for existing models.

--- lib/ST/Model/Base.coffee
+++ lib/ST/Model/Base.coffee
@@ -96,14 +96,9 @@
           modelClass.createWithData data, options
         else
           null
-      # If object with uuid already exists, update object and return it
+      # If object with uuid already exists, return existing object
       else if data.uuid && ST.Model._byUuid[data.uuid]
-        object = ST.Model._byUuid[data.uuid]
-        attributes = object._class._attributes
-        for attribute, details of attributes
-          if attributes.hasOwnProperty attribute
-            object.set attribute, data[attribute] if data[attribute]?
-        object
+        ST.Model._byUuid[data.uuid]
       # Otherwise, create a new object
       else
         object = new this


Avoid loading custom packages' names

Avoid loading custom packages' names

--- src/lib/nodejs/ComponentLoader.coffee
+++ src/lib/nodejs/ComponentLoader.coffee
@@ -29,6 +29,7 @@

     # Handle sub-modules
     _.each moduleDef.dependencies, (def) =>
+      return done() unless def.name?
       return done() unless @checked.indexOf(def.name) is -1
       @getModuleComponents def, (depComponents) ->
         return done() if _.isEmpty depComponents


fix a compilation issue with views/quest/completed

--- www/src/views/quest/completed.coffee
+++ www/src/views/quest/completed.coffee
@@ -1,5 +1,5 @@
 define ["underscore", "views/proto/common", "models/current-user", "text!templates/quest-completed.html"], (_, Common, currentUser, html) ->
-    Common.extend
+    class extends Common
         template: _.template(html)
         events:
             "click .btn-primary": "stop"


Inline form now removes input, resolves #182

--- wzk/ui/inlineform/DynamicForm.coffee
+++ wzk/ui/inlineform/DynamicForm.coffee
@@ -8,6 +8,12 @@
 goog.require 'wzk.ui.inlineform.ConfigHandler'

 class wzk.ui.inlineform.DynamicForm
+
+  ###*
+    @enum {string}
+  ###
+  @SELECTORS:
+    FILE_INPUT: "input[type=file]"

   ###*
     @constructor
@@ -53,6 +59,8 @@
   ###
   handleRowDelete: (e) =>
     el = (`/** @type {Element} */`) e.target
+    for fileInput in @dom.all wzk.ui.inlineform.DynamicForm.SELECTORS.FILE_INPUT, el
+      @dom.removeNode fileInput
     goog.style.setElementShown el, false
     @removeDutyFromHiddenInputs el


Fix progressbar with stack

Fix progressbar with stack

--- lib/cabal-process.coffee
+++ lib/cabal-process.coffee
@@ -50,6 +50,10 @@
         continue unless msg?
         if msg.uri?
           hasError = true
+      match = data.toString().match /\[\s*([\d]+)\s+of\s+([\d]+)\s*\]/
+      if match?
+        [_, progress, total] = match
+        onProgress?(progress / total)
       onMsg? msgs

     proc.on 'close', (code, signal) =>


measure time between uiReadyPromise and schemaUpdatePromise

--- content/better-bibtex.coffee
+++ content/better-bibtex.coffee
@@ -260,6 +260,7 @@
   # oh FFS -- datadir is async now
   yield Zotero.uiReadyPromise
   CACHE.init()
+  bench('Zotero.uiReadyPromise')

   # Zotero startup is a hot mess; https://groups.google.com/d/msg/zotero-dev/QYNGxqTSpaQ/uvGObVNlCgAJ
   yield Zotero.Schema.schemaUpdatePromise


OX-103 Code Review: Replacing hard-coded integer with Constant, using flycam.getIndices()

--- app/assets/javascripts/controller.coffee
+++ app/assets/javascripts/controller.coffee
@@ -295,9 +295,8 @@
     intersects = ray.intersectObjects(@sceneController.skeleton.nodes)

     if intersects.length > 0 and intersects[0].distance >= 0
-      intersectsCoord = [intersects[0].point.y, intersects[0].point.x, intersects[0].point.z]
+      intersectsCoord = [intersects[0].point.x, intersects[0].point.y, intersects[0].point.z]
       globalPos = @flycam.getGlobalPos()
-      clickCoords = [globalPos[1], globalPos[0], globalPos[2]]

       # console.log clickCoords[2 - plane]
       # console.log intersectsCoord[2 - plane]
@@ -305,7 +304,8 @@
       # console.log Math.abs(clickCoords[2 - plane] - intersectsCoord[2 - plane])

       # make sure you can't click nodes, that are clipped away (one can't see)
-      if plane == 3 or (Math.abs(clickCoords[2 - plane] - intersectsCoord[2 - plane]) < @cameraController.getRouteClippingDistance())
+      ind = @flycam.getIndices(plane)
+      if plane == VIEW_3D or (Math.abs(globalPos[ind[2]] - intersectsCoord[ind[2]]) < @cameraController.getRouteClippingDistance())
       # intersects[0].object.material.color.setHex(Math.random() * 0xffffff)
         vertex = intersects[0].object.geometry.vertices[intersects[0].vertex]
       # set the active Node to the one that has the ID stored in the vertex


Escape key to hide edit dialog

Escape key to hide edit dialog

--- app/assets/javascripts/devices.js.coffee
+++ app/assets/javascripts/devices.js.coffee
@@ -215,3 +215,8 @@

   buttons.append(ok).append(' ').append(cancel)
   return false
+
+$(document).keydown((e) ->
+  if e.keyCode == 27
+    $('.modal-wrapper').remove()
+)


fix binding on event handler

fix binding on event handler

--- app/assets/javascripts/erd/erd.js.coffee
+++ app/assets/javascripts/erd/erd.js.coffee
@@ -91,7 +91,7 @@
     $('form.add_column_form').on 'submit', @handle_add_column
     $('#changes_form').on 'submit', @handle_save

-  handle_save: ->
+  handle_save: =>
     j = '['
     rows = ($(tr).find('td') for tr in $('#changes > tbody > tr'))
     $(rows).each (i, row) ->
@@ -100,7 +100,7 @@
     j += ']'
     $('#changes_form').find('input[name=changes]').val(j)

-  handle_add_column: (ev) ->
+  handle_add_column: (ev) =>
     ev.preventDefault()
     target = $(ev.target)
     name = target.find('input[name=name]').val()
@@ -108,7 +108,7 @@

     model = target.find('input[name=model]').val()
     type  = target.find('input[name=type]').val()
-    upsert_change 'add_column', model, "#{name}(#{type})", '', ''
+    @upsert_change 'add_column', model, "#{name}(#{type})", '', ''

     name_span = $("<span/>", class: 'column_name_text')
       .append(name)
@@ -131,7 +131,7 @@
       .find('a.add_column')
       .show()

-  handle_change_column_type: (ev) ->
+  handle_change_column_type: (ev) =>
     ev.preventDefault()
     target = $(ev.target)
     to = target.find('input[name=to]').val()
@@ -141,14 +141,14 @@
     column = target.find('input[name=column]').val()
     type   = target.find('input[name=type]').val()
     if to != type
-      upsert_change 'alter_column', model, column, type, to
+      @upsert_change 'alter_column', model, column, type, to

     target.hide()
       .siblings('.column_type_text')
       .text(to)
       .show()

-  handle_rename_column: (ev) ->
+  handle_rename_column: (ev) =>
     ev.preventDefault()
     target = $(ev.target)
     to = target.find('input[name=to]').val()
@@ -157,14 +157,14 @@
     model = target.find('input[name=model]').val()
     column = target.find('input[name=column]').val()
     if to != column
-      upsert_change 'rename_column', model, column, column, to
+      @upsert_change 'rename_column', model, column, column, to

     target.hide()
       .siblings('.column_name_text')
       .text(to)
       .show()

-  handle_rename_model: (ev) ->
+  handle_rename_model: (ev) =>
     ev.preventDefault()
     target = $(ev.target)
     to = target.find('input[name=to]').val()
@@ -172,16 +172,16 @@

     model = target.find('input[name=model]').val()
     if to != model
-      upsert_change 'rename_model', model, '', model, to
+      @upsert_change 'rename_model', model, '', model, to

     target.hide()
       .siblings('.model_name_text')
       .text(to)
       .show()

-  handle_add_column_click: (ev) ->
-    ev.preventDefault()
-    target = $(@)
+  handle_add_column_click: (ev) =>
+    ev.preventDefault()
+    target = $(ev.currentTarget)

     m = target.parents('div.model')
     if m.hasClass('noclick')
@@ -198,8 +198,8 @@
       .val('')
       .focus()

-  handle_text_elem_click: (ev) ->
-    target = $(@)
+  handle_text_elem_click: (ev) =>
+    target = $(ev.currentTarget)
     text = target.text()

     m = target.parents('div.model')


Rename method for better understanding

Rename method for better understanding

--- app/assets/javascripts/gl_crop.js.coffee
+++ app/assets/javascripts/gl_crop.js.coffee
@@ -27,9 +27,9 @@

     # Ensure needed elements are jquery objects
     # If selector is provided we will convert them to a jQuery Object
-    @filename = @$(@filename)
-    @previewImage = @$(@previewImage)
-    @pickImageEl = @$(@pickImageEl)
+    @filename = @getElement(@filename)
+    @previewImage = @getElement(@previewImage)
+    @pickImageEl = @getElement(@pickImageEl)

     # Modal elements usually are outside the @form element
     @modalCrop = if _.isString(@modalCrop) then $(@modalCrop) else @modalCrop
@@ -40,7 +40,7 @@

     @bindEvents()

-  $: (selector) ->
+  getElement: (selector) ->
     $(selector, @form)

   bindEvents: ->


Wire up escape to close overlay

Wire up escape to close overlay

--- app/assets/javascripts/overlay.js.coffee
+++ app/assets/javascripts/overlay.js.coffee
@@ -2,16 +2,23 @@
   constructor: (selector, options) ->
     @selector = selector
     @options = options
-    $(@selector).click @open
-    @self = this
+    self = this
+    @overlay = $("

")

-  open: (event) ->
-    event.preventDefault()
-    overlayContent = $(this).find('.js-overlay').remove()
+    $(@selector).click (event) ->
+      event.preventDefault()
+      self.open(this)

-    overlay = $("

")
-    overlay.append(overlayContent)
-    $('body').append(overlay)
+    $(document).on 'keyup', (event) ->
+      return unless event.which == 27
+      self.close()
+
+
+  open: (target) ->
+    overlayContent = $(target).find('.js-overlay').remove()
+
+    @overlay.append(overlayContent)
+    $('body').append(@overlay)

     maxWidth = $(window).width() - 20
     maxHeight = $(window).height() - 20
@@ -43,11 +50,15 @@

     top = window.scrollY + 10

-    overlay.css('left', margin)
-    overlay.css('top', top)
-    overlay.width(width)
-    overlay.height(height)
+    @overlay.css('left', margin)
+    @overlay.css('top', top)
+    @overlay.width(width)
+    @overlay.height(height)

     $('body').addClass('scroll-lock')

     overlayContent.show()
+
+  close: ->
+    @overlay.hide()
+    $('body').removeClass('scroll-lock')


Fix broken form style

Fix broken form style

--- app/assets/javascripts/routing.js.coffee
+++ app/assets/javascripts/routing.js.coffee
@@ -20,6 +20,7 @@
   if modal
     modal_url = "/#{locale}/modals.fragment?modal=#{modal}"
     cached.getDOM modal_url, (error, dom) ->
+      $("select, input:checkbox, input:radio, input:file, input:text, textarea, submit", dom).uniform()
       dom.appendTo modalWrap.show()

   return true
@@ -46,4 +47,4 @@
   History.pushState null, null, url
   crossroads.parse url

-$(init_routing)+$(init_routing)


minor changes

minor changes

* remove unused dependecy (eyes)
* change termination message

--- cleandb.coffee
+++ cleandb.coffee
@@ -1,5 +1,4 @@
 server = require './server'
-eyes = require 'eyes'

 # Small script to remove all data for Cozy home.

@@ -10,7 +9,7 @@
              console.log "Cleaning Applications failed."
              process.exit(0)
          else
-             console.log "All applications are removed."
+             console.log "All applications and users are removed."
              process.exit(0)

 destroyUsers = ->


append the bars

append the bars

--- src/packages/editor-stats/src/editor-stats-view.coffee
+++ src/packages/editor-stats/src/editor-stats-view.coffee
@@ -57,6 +57,23 @@
     h = @.height()
     [pt, pl, pb, pr] = [0,0,0,0]

+    data = d3.entries @eventlog
+    max  = d3.max data, (d) -> d.value
+
+    x.rangeRoundBands [0, w - pl - pr], 0.1
+    y.domain([0, max]).range [h, 0]
+
+    vis = d3.select('svg g')
+
+    bars = vis.selectAll('g.bar')
+      .data(data)
+    .enter().append('g')
+      .attr('transform', (d, i) -> "translate(#{x(i)}, 0)")
+
+    bars.append('rect')
+      .attr('width', x.rangeBand())
+      .attr('height', (d, i) -> y(d.value))
+

   track: =>
     date = new Date


auto increment markdown list number

auto increment markdown list number

--- app/coffee/modules/common/wisiwyg.coffee
+++ app/coffee/modules/common/wisiwyg.coffee
@@ -72,15 +72,14 @@
                 keepDefault: false
                 replaceWith: (data) ->
                     lastLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").pop()
-
                     match = lastLine.match /^(\s*- ).*/
                     return "\n#{match[1]}" if match

                     match = lastLine.match /^(\s*\* ).*/
                     return "\n#{match[1]}" if match

-                    match = lastLine.match /^(\s*1\. ).*/
-                    return "\n#{match[1]}" if match
+                    match = lastLine.match /^(\d+)\./
+                    return "\n#{parseInt(match[1], 10) + 1}. " if match

                     return "\n"


Catch errors parsing apm ls output

Catch errors parsing apm ls output

Not 100% how these are happening but stdout being empty definitely
causes this to fail previously.

Closes #53

--- lib/user-utilities.coffee
+++ lib/user-utilities.coffee
@@ -81,7 +81,10 @@
         stdout: (oneLine) -> data.push(oneLine)
         exit: =>
           stdout = data.join('\n')
-          packages = JSON.parse(stdout)
+          try
+            packages = JSON.parse(stdout)
+          catch error
+            packages = {}
           resolve
             dev: @filterActivePackages(packages.dev)
             user: @filterActivePackages(packages.user)


Test 5.

Test 5.

--- app/assets/javascripts/app/controllers/dashboard.coffee
+++ app/assets/javascripts/app/controllers/dashboard.coffee
@@ -93,16 +93,13 @@
     @$(".tab-content.#{target}").removeClass('hidden')

 class DashboardRouter extends App.ControllerPermanent
-  #requiredPermission: ['*']
+  requiredPermission: ['*']

   constructor: (params) ->
     super

     # check authentication
-    #@authenticateCheckRedirect()
-    if !@authenticateCheck()
-      @navigate '#login'
-      return
+    @authenticateCheckRedirect()

     App.TaskManager.execute(
       key:        'Dashboard'


Fix javascript error on students page

Fix javascript error on students page

--- app/assets/javascripts/components/students/student.cjsx
+++ app/assets/javascripts/components/students/student.cjsx
@@ -24,10 +24,11 @@
   buttonClick: (e) ->
     e.stopPropagation()
     @openDrawer()
-  _studentRole: 0
+
   _shouldShowRealName: ->
+    studentRole = 0
     return false unless @props.student.real_name?
-    @props.current_user? && (@props.current_user.admin? || @props.current_user.role > @_studentRole())
+    @props.current_user? && (@props.current_user.admin? || @props.current_user.role > studentRole)

   render: ->


Sort repos explicitly on dashboard

Sort repos explicitly on dashboard

--- app/routes/dashboard/repositories.coffee
+++ app/routes/dashboard/repositories.coffee
@@ -14,6 +14,13 @@
     }).then (response) ->
       response.repositories.sortBy('last_build.finished_at').filter( (repo) ->
         repo.last_build
+      ).sort( (a, b) ->
+        if !a.last_build.finished_at || a.last_build.finished_at > b.last_build.finished_at
+          return -1
+        else if !b.last_build.finished_at || b.last_build.finished_at > a.last_build.finished_at
+          return 1
+        else
+          return 0
       ).map( (repo) ->
         Ember.Object.create(repo)
       )


Clean trailing spaces and semicolons in edate.coffee

--- src/edate.coffee
+++ src/edate.coffee
@@ -1,4 +1,4 @@
-defineGetter = (constructor, propName, getter) ->
+defineGetter = (constructor, propName, getter) ->
   if Object.defineProperty
     Object.defineProperty(constructor::, propName, {
       get: getter
@@ -6,7 +6,7 @@
   else
     constructor::.__defineGetter__(propName, getter)

-defineProperty = (object, propName, propValue) ->
+defineProperty = (object, propName, propValue) ->
   if Object.defineProperty
     Object.defineProperty(object, propName, {
       value: propValue
@@ -20,21 +20,21 @@
 defineGetter(Number, 'second',  secondsGetter)
 defineGetter(Number, 'seconds', secondsGetter)

-minutesGetter = -> this * 60.seconds
-defineGetter(Number, 'minute',  minutesGetter);
-defineGetter(Number, 'minutes', minutesGetter);
+minutesGetter = -> this * 60.seconds
+defineGetter(Number, 'minute',  minutesGetter)
+defineGetter(Number, 'minutes', minutesGetter)

 hoursGetter = -> this * 60.minutes
-defineGetter(Number, 'hour',  hoursGetter);
-defineGetter(Number, 'hours', hoursGetter);
+defineGetter(Number, 'hour',  hoursGetter)
+defineGetter(Number, 'hours', hoursGetter)

 daysGetter = -> this * 24.hours
-defineGetter(Number, 'day',  daysGetter);
-defineGetter(Number, 'days', daysGetter);
+defineGetter(Number, 'day',  daysGetter)
+defineGetter(Number, 'days', daysGetter)

 weeksGetter = -> this * 7.days
-defineGetter(Number, 'week',  weeksGetter);
-defineGetter(Number, 'weeks', weeksGetter);
+defineGetter(Number, 'week',  weeksGetter)
+defineGetter(Number, 'weeks', weeksGetter)

 # Before/After calculations
 defineGetter(Number, 'ago', -> new Date(Date.now() - this))
@@ -42,8 +42,8 @@
 defineGetter(Number, 'fromNow', -> new Date(Date.now() + this))

 afterFunction = (date) -> new Date(+date + this)
-defineProperty(Number::, 'after', afterFunction);
-defineProperty(Number::, 'since', afterFunction);
+defineProperty(Number::, 'after', afterFunction)
+defineProperty(Number::, 'since', afterFunction)

 defineProperty(Number::, 'before', (date) -> new Date(+date - this))

@@ -61,13 +61,13 @@
 # Date boundary functions
 #

-numberOrDate2Date = (numberOrDate) ->
+numberOrDate2Date = (numberOrDate) ->
   switch numberOrDate.constructor
     when Date then numberOrDate
     when Number then new Date(numberOrDate)
     else throw new Error('Argument should be a Number or Date')

-atTheBeginningOfHour = ->
+atTheBeginningOfHour = ->
   date = numberOrDate2Date(this)
   date.setMinutes(0)
   date.setSeconds(0)


Minor refactoring.

Minor refactoring.

--- spec/javascripts/app/directives_spec.js.coffee
+++ spec/javascripts/app/directives_spec.js.coffee
@@ -57,10 +57,9 @@
     beforeEach module("app/assets/templates/filter.html")

     beforeEach inject ($rootScope, $compile) ->
-      element = angular.element('<filter value="filterValue" placeholder="Enter database name"></filter>')
+      $scope = $rootScope

-      $scope = $rootScope
-      $compile(element)($scope)
+      element = $compile('<filter value="filterValue" placeholder="Enter database name"></filter>')($scope)
       $scope.$digest()

     it "sets the valid placeholder", ->