postgres: mapping from account_id list to username info...

--- src/smc-hub/postgres.coffee
+++ src/smc-hub/postgres.coffee
@@ -999,8 +999,30 @@
                     cb()
         ], opts.cb)

+    ###
+    Querying for searchable information about accounts.
+    ###
     account_ids_to_usernames: (opts) =>
-        throw Error("NotImplementedError")
+        opts = defaults opts,
+            account_ids : required
+            cb          : required # (err, mapping {account_id:{first_name:?, last_name:?}})
+        if not @_validate_opts(opts) then return
+        if opts.account_ids.length == 0 # easy special case -- don't waste time on a db query
+            opts.cb(undefined, [])
+            return
+        @_query
+            query : 'SELECT account_id, first_name, last_name FROM accounts'
+            where : 'account_id = ANY($::UUID[])' : opts.account_ids
+            cb    : (err, result) =>
+                if err
+                    opts.cb(err)
+                else
+                    v = misc.dict(([r.account_id, {first_name:r.first_name, last_name:r.last_name}] for r in result.rows))
+                    # fill in unknown users (should never be hit...)
+                    for id in opts.account_ids
+                        if not v[id]?
+                            v[id] = {first_name:undefined, last_name:undefined}
+                    opts.cb(err, v)

     get_usernames: (opts) =>
         throw Error("NotImplementedError")


webapp/billing: dirty fix to prevent more than one subscription creation per minute (clearly, those wjp want to make more than one have to literally wait until this is done properly)

--- src/smc-webapp/billing.cjsx
+++ src/smc-webapp/billing.cjsx
@@ -1,4 +1,4 @@
-###############################################################################
+##############################################################################
 #
 # SageMathCloud: A collaborative web-based interface to Sage, IPython, LaTeX and the Terminal.
 #
@@ -37,6 +37,8 @@
         cb()
     else
         $.getScript("https://js.stripe.com/v2/").done(->cb()).fail(->cb('Unable to load Stripe support'))
+
+last_subscription_attempt = null

 actions = store = undefined
 # Create the billing actions
@@ -135,7 +137,14 @@
         @_action('cancel_subscription', 'Cancel a subscription', {subscription_id : id, cb : cb})

     create_subscription: (plan='standard') =>
-        @_action('create_subscription', 'Create a subscription', plan : plan)
+        {salvus_client} = require('./salvus_client')   # do not put at top level, since some code runs on server
+        lsa = last_subscription_attempt
+        if lsa? and lsa > misc.minutes_ago(1)
+            @setState(action:'', error: 'Too many subscription attempts in the last minute. Please check if you are already subscribed!')
+        else
+            @setState(error: '')
+            @_action('create_subscription', 'Create a subscription', plan : plan)
+            last_subscription_attempt = new Date()

     # Cancel all subscriptions, remove credit cards, etc. -- this is not a normal action, and is used
     # only when deleting an account.  We allow it a callback.


library: fix thumbnail

library: fix thumbnail

--- src/smc-webapp/library.cjsx
+++ src/smc-webapp/library.cjsx
@@ -118,7 +118,10 @@

     select_list_click: (doc) ->
-        @setState(show_thumb:false)  # we control the visibility of the thumbnail, because it would show to the old one until the new one is loaded
+        # ignore selection of the very same entry
+        return if doc.get('id') == @props.library_selected?.get('id')
+        # we control the visibility of the thumbnail, because it would show to the old one until the new one is loaded
+        @setState(show_thumb:false)
         @props.actions.setState(library_selected:doc)


Recalculate elapsed time on started_at change.

--- dashboard/assets/javascripts/controllers.js.coffee
+++ dashboard/assets/javascripts/controllers.js.coffee
@@ -34,7 +34,7 @@
     started = moment.unix @get('content.started_at')

     moment.duration(moment() - started).humanize()
-  ).property().volatile()
+  ).property('content.started_at').volatile()

   freeze: ->
     @get('content').addLogEntry Dashboard.FreezeUpdateEntry.create()


Use path.join for tmp file path

Use path.join for tmp file path

--- lib/atom-html-preview-view.coffee
+++ lib/atom-html-preview-view.coffee
@@ -99,7 +99,7 @@

   save: (callback) ->
     # Temp file path
-    outPath = path.resolve os.tmpdir() + @editor.getTitle() + ".html"
+    outPath = path.resolve path.join(os.tmpdir(), @editor.getTitle() + ".html")
     out = ""
     fileEnding = @editor.getTitle().split(".").pop()


Remove :shit: code

Remove :shit: code

--- src/app/select-list.coffee
+++ src/app/select-list.coffee
@@ -42,8 +42,6 @@
   setError: (message) ->
     @error.text(message)
     @error.show()
-    @error.show()
-    console.log "error"
     @addClass("error")

   populateList: ->


do not disconnect

do not disconnect

--- lib/build/build-state-view.coffee
+++ lib/build/build-state-view.coffee
@@ -64,6 +64,7 @@
       reconnection: true
       reconnectionDelay: 50
       reconnectionDelayMax: 12000
+      timeout: 3000

     @socket.on 'connect', =>
       console.log "socket connected. #{puzzleClient.access_token}"
@@ -96,7 +97,8 @@

   destroy: ->

-    @socket?.disconnect()
+    # @socket?.disconnect()
+
     if @task and @task.state != 'complete'
       puzzleClient.deleteTask @task.id
       .then ->


Updated tag autocomplete Added default value to label and value. Increased default delay to 800. Updated comments and added dependencies.

--- src/tag_autocomplete.coffee
+++ src/tag_autocomplete.coffee
@@ -3,8 +3,16 @@
 ##
 ## A directive for generating tag input with autocomplete support on text input
 ##
+## Dependencies
+## - jQuery UI autocomplete
+##
 ## Attributes
-## - mac-tag
+## - mac-tag-autocomplete-url:      url to fetch autocomplete dropdown list data
+## - mac-tag-autocomplete-value:    the value to be sent back upon selection
+## - mac-tag-autocomplete-label:    the label to display to the users
+## - mac-tag-autocomplete-selected: the list of elements selected by the user
+## - mac-tag-autocomplete-query:    the query parameter on GET command
+## - mac-tag-autocomplete-delay:    time delayed on fetching autocomplete data after keyup
 ##

 angular.module("Mac").directive "macTagAutocomplete", [
@@ -19,12 +27,11 @@

     compile: (element, attr) ->
       urlExp      = attr.macTagAutocompleteUrl
-      modelExp    = attr.macTagAutocompleteLocalModel
-      valueKey    = attr.macTagAutocompleteValue
-      labelKey    = attr.macTagAutocompleteLabel
+      valueKey    = attr.macTagAutocompleteValue      or "id"
+      labelKey    = attr.macTagAutocompleteLabel      or "name"
       selectedExp = attr.macTagAutocompleteSelected
       queryKey    = attr.macTagAutocompleteQuery      or "q"
-      delay       = attr.macTagAutocompleteDelay      or 100
+      delay       = attr.macTagAutocompleteDelay      or 800

       getUrl      = $parse urlExp
       getSelected = $parse selectedExp


Bugfix.

Bugfix.

--- src/views/navigation.coffee
+++ src/views/navigation.coffee
@@ -189,7 +189,7 @@
             @listenTo @searchState, 'change:input_query', (model, value, opts) =>
                 if opts.initial
                     @_open 'search'
-                unless value or opts.clearing or opts.keepOpen
+                else unless value or opts.clearing or opts.keepOpen
                     @_close 'search'
         onShow: ->
             @search.show new SearchInputView(@searchState, @searchResults)


support on issue updated

support on issue updated

--- src/scripts/fgb.coffee
+++ src/scripts/fgb.coffee
@@ -45,6 +45,26 @@
   """
   sendToChat robot, { room, message }

+onIssueUpdated = (robot, space, w, requests) ->
+  projectKey = w.project.projectKey
+  project = space.getProject projectKey
+  unless project?
+    robot.logger.warning 'hubot-fgb: unknown project key: ' + projectKey
+    return
+  issueKey = "#{project.getKey()}-#{w.content.key_id}"
+  issueUrl = "https://#{space.getId()}.backlog.jp/view/#{issueKey}"
+  summary = w.content.summary
+  description = w.content.description
+  username = w.createdUser.name
+  comment = w.content.comment.content
+  room = project.getRoom()
+  message = """
+  #{username} が課題「#{issueKey} #{summary}」を更新したみたい。
+  #{comment}
+  #{issueUrl}
+  """
+  sendToChat robot, { room, message }
+
 module.exports = (robot) ->
   robot.logger.debug 'hubot-fgb: load config: ' + JSON.stringify config
   space = newSpace config
@@ -53,6 +73,8 @@
     webhook = req.body
     if webhook.type is 1 # issue created
       onIssueCreated robot, space, webhook
+    else if webhook.type is 2 # issue updated
+      onIssueUpdated robot, space, webhook
     else
       return if webhook.type <= 17 # max webhook.type
       robot.logger.warning "hubot-fgb: unknown webhook type: #{webhook.type}"


Changed the messaging for the tfl script

--- src/scripts/tfl.coffee
+++ src/scripts/tfl.coffee
@@ -38,12 +38,12 @@
         if res.statusCode == 200
           line = JSON.parse(body)
           if _(good_service_statuses).include(line.line.status.status_description)
-            msg.send "Nope it's all good, no problems reported"
+            msg.send "It's all good, no problems reported"
           if _(average_statuses).include(line.line.status.status_description)
             msg.send "Well it's semi-fucked, TfL says: '#{line.line.status.status_details}'"
           if _(bad_statuses).include(line.line.status.status_description)
-            msg.send "Totally fuckeyed mate, best to avoid, Tfl says: '#{line.line.status.status_details}'"
+            msg.send "It's in a right state, best to avoid, Tfl says: '#{line.line.status.status_details}'"
           if _(closed_statuses).include(line.line.status.status_description)
-            msg.send "No chance, it's a planned closure, you should really pay attention to the TfL website..."
+            msg.send "Are you kidding?. It's a planned closure, you should really pay attention to the TfL website..."
         else
           msg.send "No line by that name sonny, you sure you even live in London?"


Remove useless console.log

Remove useless console.log

--- src/server/Room.coffee
+++ src/server/Room.coffee
@@ -35,7 +35,6 @@

 	addPlayer: (player) ->
 		@players.push(player);
-		console.log("I ADD A PLAYER", p.getId() for p in @players);
 		if (@players.length == 1)
 			@players[0].on('endOfTrack', @onEndOfTrack);
 			@playNextTrack()
@@ -51,11 +50,9 @@
 			@players.splice(idx, 1);

 	onPlayerDisconnect: (player) ->
-		console.log("PLAYER DISCONECT", player.getId());
 		idx = @players.indexOf(player);
 		if idx == -1 then return
 		@players.splice(idx, 1);
-		console.log("I REMOVE A PLAYER", p.getId() for p in @players);
 		if (idx == 0 and @players.length)
 			@players[0].on('endOfTrack', @onEndOfTrack);
 		if (@players.length == 0)


separate get json method

separate get json method

--- src/server/docs.coffee
+++ src/server/docs.coffee
@@ -24,20 +24,27 @@
   ###
   getJsonScheduler: (interval, cb) ->
     if process.env.NODE_ENV == 'production'
-      @getDocsJson cb
+      @getRemoteJson cb
     else if process.env.NODE_ENV == 'development'
-      @json = JSON.parse(fs.readFileSync(config.typedoc.path_to_json))
-      cb()
+      @getLocalJson cb
     console.log 'got json'
     setTimeout =>
       @getJsonScheduler interval, cb
     , interval * 1000

   ###*
+   * load json from directory
+   * @param  {Function} cb callback function on loaded
+  ###
+  getLocalJson: (cb) ->
+    @json = JSON.parse(fs.readFileSync(config.typedoc.path_to_json))
+    cb()
+
+  ###*
    * request json
    * @param  {Function} cb callback function on resolved request
   ###
-  getDocsJson: (cb) ->
+  getRemoteJson: (cb) ->
     options =
       url: 'https://raw.githubusercontent.com/jThreeJS/jThree/gh-pages/docs/develop.json'
       json: true
@@ -64,7 +71,6 @@
       if child.id == parseInt(file_id, 10)
         for gchild in child.children
           if gchild.id == parseInt(factor_id, 10)
-            console.log gchild.name
             return gchild
     return null

@@ -86,7 +92,7 @@
    * Costruct doc_data object formed for doc store
    * @param  {String|Number} file_id   id of child of doc root
    * @param  {String|Number} factor_id id of grandchild of doc root
-   * @return {Object}                  object of doc_data specifyed by id of file and factor
+   * @return {Object}                  object of doc_data specifyed by id of file and factor
   ###
   getDocDataById: (file_id, factor_id) ->
     data = @getGlobalClassById(file_id, factor_id)


Shift before appending to keep track of all logs

--- admin/static/coffee/app.coffee
+++ admin/static/coffee/app.coffee
@@ -137,10 +137,12 @@
             entry.set('machine_uuid',machine_uuid)

             machines.get(machine_uuid).get('log_entries').add entry
-            recent_log_entries.add entry
+

             if recent_log_entries.length > 3
                 recent_log_entries.shift()
+            recent_log_entries.add entry
+

             if parseFloat(json.timestamp) > recent_log_entries.min_timestamp
                 recent_log_entries.min_timestamp = Math.ceil parseFloat json.timestamp # /ajax/log juste compare integers


add todos

add todos

--- src/stackmat.js.coffee
+++ src/stackmat.js.coffee
@@ -148,7 +148,7 @@

   constructor: (options) ->
     if !@supported()
-      alert("You need a recent browser in order to connect your Stackmat Timer.")
+      alert("You need a recent browser in order to connect your Stackmat Timer.") # TODO plugin should probably not call alert()
       return

     @interval = options.interval || 1000 # control how often user wants to be messaged per second
@@ -158,7 +158,7 @@
     @onReset = options.onReset || ->
     @capturing = false

-    navigator.webkitGetUserMedia {audio: true}, (stream) =>
+    navigator.webkitGetUserMedia {audio: true}, (stream) => # TODO move to start()?
       microphone = @audioContext().createMediaStreamSource(stream)
       @device = new AudioHardware(microphone, @signalFetched)


test buffering during drainage

test buffering during drainage

--- src/test/buffer.coffee
+++ src/test/buffer.coffee
@@ -86,3 +86,31 @@

         stream.pipe buffer

+
+    drainage: (æ) ->
+        buffer = new BufferStream
+            size:'flexible'
+            disabled:yes
+        buffer.on 'data', (data) -> æ.equals results.shift(), data.toString()
+        buffer.on 'end', ->
+            æ.equals 0, results.length
+            æ.done()
+        results = [
+            "foo"
+            "barbaz"
+            "chaos"
+        ]
+
+        buffer.write "foo"
+        æ.equals 0, buffer.length
+        buffer.pause()
+        buffer.write "bar"
+        buffer.write "baz"
+        buffer.resume()
+        æ.equals 0, buffer.length
+        buffer.write "chaos"
+        æ.equals 0, buffer.length
+        buffer.end()
+
+
+


Updating places still using Buffer instead of TextBuffer

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -10,8 +10,8 @@

 # Private: Represents the contents of a file.
 #
-# The `Buffer` is often associated with a {File}. However, this is not always
-# the case, as a `Buffer` could be an unsaved chunk of text.
+# The `TextBuffer` is often associated with a {File}. However, this is not always
+# the case, as a `TextBuffer` could be an unsaved chunk of text.
 module.exports =
 class TextBuffer extends telepath.Model
   Emitter.includeInto(this)
@@ -672,7 +672,7 @@
       console.log row, line, line.length

   getDebugSnapshot: ->
-    lines = ['Buffer:']
+    lines = ['TextBuffer:']
     for row in [0..@getLastRow()]
       lines.push "#{row}: #{@lineForRow(row)}"
     lines.join('\n')


改进Form.fields属性的处理机制

改进Form.fields属性的处理机制

--- src/widget/edit/form.coffee
+++ src/widget/edit/form.coffee
@@ -36,15 +36,19 @@
 					usedCols = maxCols

 					for field in fields
+						propertyType = null
+						if field.dataType
+							propertyType = cola.DataType.defaultDataTypes[field.dataType]
+
 						if dataType
 							propertyDef = dataType.getProperty(field.property)
 						if propertyDef
 							caption = field.caption or propertyDef.get("caption") or field.property
-							propertyType = propertyDef.get("dataType")
+							if not propertyType
+								propertyType = propertyDef.get("dataType")
 							labelUserData = { captionSetted: true }
 						else
 							caption = field.caption or field.property
-							propertyType = null

 						if usedCols + (field.cols or defaultFieldCols) > maxCols
 							usedCols = 0


Mark deleteRows arguments in the description

Makes it so `startRow` is marked as the argument variable, and change
"end row" to `endRow` to match `startRow`.

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -904,8 +904,8 @@

   # Public: Delete the lines associated with the specified 0-indexed row range.
   #
-  # If the row range is out of bounds, it will be clipped. If the startRow is
-  # greater than the end row, they will be reordered.
+  # If the row range is out of bounds, it will be clipped. If the `startRow` is
+  # greater than the `endRow`, they will be reordered.
   #
   # * `startRow` A {Number} representing the first row to delete.
   # * `endRow` A {Number} representing the last row to delete, inclusive.


fix folds accidentally becoming unfolded

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -924,8 +924,8 @@
             fold.getBufferRange().translate([+insertDelta, 0])

         # Make sure the inserted text doesn't go into an existing fold
-        if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow - insertDelta)
-          rangesToRefold.push(fold.getBufferRange().translate([-(linesRange.getRowCount() - 1), 0]))
+        if fold = @displayBuffer.largestFoldStartingAtBufferRow(followingBufferRow)
+          rangesToRefold.push(fold.getBufferRange().translate([insertDelta - 1, 0]))
           fold.destroy()

         # Delete lines spanned by selection and insert them on the following correct buffer row


Less lint.

Less lint.

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -119,7 +119,7 @@

     buffer ?= new TextBuffer
     @displayBuffer ?= new DisplayBuffer({
-      buffer, tabLength, softWrapped, ignoreInvisibles: @mini || ignoreInvisibles, largeFileMode,
+      buffer, tabLength, softWrapped, ignoreInvisibles: @mini or ignoreInvisibles, largeFileMode,
       @config, @assert, @grammarRegistry, @packageManager
     })
     @buffer = @displayBuffer.buffer


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -1,4 +1,5 @@
-
+GameOver = (I={}) ->
+  self = GameState(I)
 drawTitleText = (canvas, titleText, subtitleText) ->
   canvas.fillColor 'white'
   canvas.font '24px Helvetica'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -13,7 +13,7 @@

   self = GameState(I).extend
     ###*
-    Set your velocity to follow another object.
+    Draw tex

     enemy.follow(player)


Rename oreRow -> oreRowID to clarify it's an index

As opposed to a reference to the row.

--- src/coffee/refining.coffee
+++ src/coffee/refining.coffee
@@ -11,12 +11,12 @@
   unless isNaN(quantity)
     itemName = $("#oreInput1").val()

-    oreRow = refiningTable.row((rowID) ->
+    oreRowID = refiningTable.row((rowID) ->
       return refiningTable.cell(rowID, 0).data() is itemName
     ).index()

-    if oreRow? # Found
-      oreCell = refiningTable.cell(oreRow, 1)
+    if oreRowID? # Found
+      oreCell = refiningTable.cell(oreRowID, 1)
       oreCell.data(oreCell.data() + quantity)
     else # Not found
       refiningTable.row.add([itemName, quantity])


[TERMINAL] Fixed cd with no arguments

[TERMINAL] Fixed cd with no arguments

--- src/coffee/terminal.coffee
+++ src/coffee/terminal.coffee
@@ -208,7 +208,7 @@
   cdCommand: (pathname) =>
     # on unix, cd with no arguments sends back to the user's home directory; there is no 'root' home dir so let's go to /
     if not pathname?
-      @directoryStack = [@directoryStack[0]]  # only keep ROOT directory (not root home); works if no other ref to this array
+      newDirectoryStack = [@directoryStack[0]]  # only keep ROOT directory (not root home); works if no other ref to this array
     else
       pathChain = pathname.split '/'
       newDirectoryStack = @directoryStack[..]  # work on stack copy in case it fails in the middle


Remove unused activity

Remove unused activity

--- src/config/activity.coffee
+++ src/config/activity.coffee
@@ -1,6 +0,0 @@
-exports.activity = {
-  _LABEL: "activity"
-  _CHILDREN: (world) ->
-    current = world.get('current')
-    current.get('actions')
-}


:bug: Use [...-1] instead of [1..] to splice

--- lib/views/status-list-view.coffee
+++ lib/views/status-list-view.coffee
@@ -1,7 +1,3 @@
-Os = require 'os'
-Path = require 'path'
-fs = require 'fs-plus'
-
 {$$, SelectListView} = require 'atom'

 git = require '../git'
@@ -14,7 +10,7 @@
     super
     @addClass 'overlay from-top'
     @branch = @data[0]
-    @setItems @parseData @data[1..]
+    @setItems @parseData @data[...-1]

     atom.workspaceView.append this
     @focusFilterEditor()


Fixed NWS Forecast update time display to show 12 rather then 0.

--- static/js/index.coffee
+++ static/js/index.coffee
@@ -49,8 +49,8 @@
     refreshed = dt.getMonth()+1 + "-" +
       dt.getDate() + "-" +
       dt.getFullYear() + " " +
-      dt.getHours() % 12 + ":" +
-      dt.getMinutes() + " " +
-      if dt.getHours() >= 12 then "PM" else "AM"
+      (if dt.getHours() % 12 == 0 then "12" else dt.getHours() % 12) +
+      ":" + dt.getMinutes() + " " +
+      (if dt.getHours() >= 12 then "PM" else "AM")

     source.text "Source: National Weather Service (updated " + refreshed + ")"


removing comments

removing comments

--- app/assets/coffee/entities/playlist.coffee
+++ app/assets/coffee/entities/playlist.coffee
@@ -98,7 +98,6 @@
           queuedtracks.forEach (t) ->
             t.set order: t.indexOf(@model) + 1
             t.save()
-            console.log t

     Swabcast.reqres.setHandler "entities:playlist", ->
       API.getPlaylistEntities()
@@ -109,8 +108,6 @@
     Swabcast.reqres.setHandler "episode:playlist", ->
       API.getPlaylistEntities()

-    # Swabcast.reqres.setHandler "playlist:first", ->
-    #   API.firstInPlaylist()

     Swabcast.reqres.setHandler "playlist:addtoqueue", (model) ->
       API.addToPlaylist model


add the joyride start as a coffeescript function

--- app/assets/javascripts/workflows.js.coffee
+++ app/assets/javascripts/workflows.js.coffee
@@ -1,6 +1,9 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
+@start_joyride = ->
+  $('#joyride').joyride {}
+  return

 @update_display = (id) ->
   request_job_data(id)


Also pass the routing options to before actions. The return value of the previous action is now the third param (breaking change).

--- src/chaplin/dispatcher.coffee
+++ src/chaplin/dispatcher.coffee
@@ -183,7 +183,7 @@
           @executeAction args...
           return

-        previous = method.call controller, params, previous
+        previous = method.call controller, params, options, previous

         # Detect a CommonJS promise  in order to use pipelining below,
         # otherwise execute next method directly


Get rid of extraneous comment

Get rid of extraneous comment

--- app/assets/volume/showInvestigators.coffee
+++ app/assets/volume/showInvestigators.coffee
@@ -14,9 +14,6 @@
     link: ($scope, $element, $attrs) ->
       $scope.title = $attrs.title

-      # 0 and 1000 have no significance except that 1000 is bigger than all the permissions
-      # and 0 is smaller than all of them.
-
       maxPerm = constants.permission[$attrs.maxlevel] ? constants.permission.length
       minPerm = constants.permission[$attrs.minlevel] ? 0
       $scope.investigators = $scope.volume.access.filter (i) ->


fixing some touch event listeners to use hammer tap

--- src/coffeescripts/Main.coffee
+++ src/coffeescripts/Main.coffee
@@ -39,7 +39,7 @@
         , false

-  # Init function
+  # Init function
   # Adds event listeners and checks if EULA has been accepted
   init: ->
     LoggrUtil.log "init"
@@ -62,7 +62,7 @@
     @search = new Search()
     @search.addEventListener Search.SELECT, (id) =>
       @showHash "#detail?id=#{id}"
-
+
     # Check EULA
     @checkEULA =>
       @launch()
@@ -70,7 +70,7 @@
       $(document).on 'online', => @launch() if not @isLaunchComplete

-  # Check EULA and launch the app once EULA is accepted.
+  # Check EULA and launch the app once EULA is accepted.
   # `callback` Callback function invoked when accepted
   checkEULA: (callback) ->
     LoggrUtil.isEulaAccepted (val) ->
@@ -96,7 +96,7 @@
       isOnline = SFHybridApp.deviceIsOnline()
     if not isOnline
       LoggrUtil.logConnectionError()
-    else
+    else
       LoggrUtil.log 'device is online. Trying authentication'
       # Initiate authentication if a container app, else directly initialize the app.
       if cordova?
@@ -107,7 +107,7 @@
       else
         @run()

-  # Authenticate User
+  # Authenticate User
   # `callback` Callback invoked when authentication is complete
   authenticate: (callback) ->
     if ContainerAuth?
@@ -161,7 +161,7 @@
     $('body div#app').empty().show()
     .append(new Hogan.Template(T.home).render({isRetina: UI.isRetina()}))

-    $('#mainNav li').on "touchstart", (event) =>
+    $('#mainNav li').hammer(UI.buttonHammerOptions).on "tap", (event) =>
       event.preventDefault()
       switch event.currentTarget.id
         when "recentNav" then @showHash '#recent' + Model.getLastType() + 's'
@@ -182,8 +182,8 @@
           LoggrUtil.log "Initial hash: #{hash}"
           @showHash if location.hash is '' then hash else location.hash

-  # Swtch content
-  # `hash` Hash of the content
+  # Swtch content
+  # `hash` Hash of the content
   # `state` Optional state when invoked from history.back
   showHash: (hash, state) ->
     LoggrUtil.log 'showHash ' + hash + ' current: ' + location.hash
@@ -238,7 +238,7 @@
         else
           throw new Error 'Unknown hash: ' + hash

-
+

   # `partial` list, isAccount, isContact, isEmpty
   renderList: (partial, objectSettings) ->
@@ -311,7 +311,7 @@
       $('#Leads').addClass 'selectedType'

     $('#types li').hammer(UI.buttonHammerOptions)
-    .on 'touchstart', (event) =>
+    .on 'tap', (event) =>
       #LoggrUtil.tagEvent 'Switch type', {type:event.currentTarget.id}
       newHash = '#recent' + event.currentTarget.id
       if location.hash is newHash
@@ -328,10 +328,10 @@
       else
         @showHash newHash

-  # Shows a list of accounts or contacts.
-  # Tries to get data from memory cache or loads it up when not available.
-  # `type` Record type
-  # `reset` Flag if the list should be cleared before loading the new one.
+  # Shows a list of accounts or contacts.
+  # Tries to get data from memory cache or loads it up when not available.
+  # `type` Record type
+  # `reset` Flag if the list should be cleared before loading the new one.
   showList: (type, reset = false) ->
     Model.setLastType type

@@ -352,7 +352,7 @@
         if object.checked
           firstEnabled = object
         if object.id is type
-          objectEnabled = object.checked
+          objectEnabled = object.checked

       if not objectEnabled
         if firstEnabled
@@ -380,4 +380,4 @@
 # global sign-in function used for web version.
 window?.signIn = ->
   LoggrUtil.log 'signIn'
-  location.href = window.location.protocol + '//' + window.location.host + '/authenticate'
+  location.href = window.location.protocol + '//' + window.location.host + '/authenticate'


Add the implementation for darwin

Add the implementation for darwin

--- src/default-network.coffee
+++ src/default-network.coffee
@@ -61,6 +61,16 @@
       result.defaultGateway = config.defaultGateway
       callback(null, result)

+getOnDarwin = (callback) ->
+  exec 'route -n get default', (error, stdout, stderr) ->
+    return callback(error) if error
+    defaultGateway = (stdout.match(/gateway:\s*(.+)\s*\n/) || [])[1]
+    defaultInterface = (stdout.match(/interface:\s*(.+)\s*\n/) || [])[1]
+    data = {}
+    data.defaultGateway = defaultGateway if defaultGateway?
+    data.defaultInterface = defaultInterface if defaultInterface?
+    callback(null, data)
+
 get = (callback) ->
   platform = require('os').platform()
   collector = switch platform


fix(macMenu): Fixed menu index bi-directional binding not working properly

--- src/directives/menu.coffee
+++ src/directives/menu.coffee
@@ -11,7 +11,6 @@
         - `index` - {Integer} Item index
 @param {Object} mac-menu-style Styles apply to the menu
 @param {Expression} mac-menu-index Index of selected item
-        - `index` - {Integer} Item index
 ###

 angular.module("Mac").directive "macMenu", [
@@ -24,6 +23,7 @@
       items:  "=macMenuItems"
       style:  "=macMenuStyle"
       select: "&macMenuSelect"
+      pIndex: "=macMenuIndex"

     link: ($scope, element, attrs, ctrls) ->
       $scope.selectItem = (index) ->
@@ -32,8 +32,11 @@
       $scope.setIndex = (index) ->
         $scope.index = index

-      $scope.$watch "index", (value) ->
-        getter = $parse attrs.macMenuIndex
-        if getter.assign? and value
-          getter.assign $scope.$parent, value
+        if attrs.macMenuIndex?
+          $scope.pIndex = parseInt(index)
+
+      # sync local index with user index
+      if attrs.macMenuIndex?
+        $scope.$watch "pIndex", (value) ->
+          $scope.index = parseInt(value)
 ]


configs again

configs again

--- configs.coffee
+++ configs.coffee
@@ -23,7 +23,7 @@
 		configs.mongoURL = process.env.MONGOLAB_URI
 		configs.url = "http://www.vault.tk"
 		configs.port = "80"
-		configs.host = null
+		configs.host = 'www.vault.tk'
 		configs.clef =
 			app_id: process.env.CLEF_APP_ID
 			app_secret: process.env.CLEF_APP_SECRET


Fix to call transport::end from durable channel

--- src/durable-channel.coffee
+++ src/durable-channel.coffee
@@ -234,6 +234,6 @@

   end: ->
     clearTimeout @timeoutMonitor
-    @queue.end(!@isTransportShared)
+    @transport.end(!@isTransportShared)

 module.exports = DurableChannel


fixing all day events

fixing all day events

--- src/google-calendar.coffee
+++ src/google-calendar.coffee
@@ -67,6 +67,9 @@
   # TODO: robot.respond /gcal whereabouts/ # where is everyone
   # TODO: link to hangout
   # TODO: link to event on google calendar web
+  # TODO: for recurring, make sure I'm showing the next occurrence, and all after that
+  # TODO: remove locations for undefined locations
+  # TODO: figure out why times are wrong....

   robot.respond /gcal me/i, (msg)->
     moment = require('moment')
@@ -91,9 +94,15 @@
       callback: (err, data)->
         return console.log(err) if err
         items = data.items.map((item)->
-          start = moment(item.start.dateTime).format('M/D h:mm')
-          end = moment(item.end.dateTime).format('M/D h:mm')
-          "[#{start}-#{end}] '#{item.summary}' (#{item.location})"
+          start = item.start.date || item.start.dateTime
+          end = item.end.date || item.end.dateTime
+          format = 'M/D'
+          format += ' h:mm' if start.dateTime
+          start = moment(start).format(format)
+          end = moment(end).format(format)
+          entry =  "[#{start}-#{end}] "
+          entry += "'#{item.summary}'"
+          entry += "(#{item.location})" if item.location
         ).join("\n")
         console.log items
         message = if items.length > 0


Show loginscreen when clicked Click here to start tab in Guest Mode

--- core/KD.coffee
+++ core/KD.coffee
@@ -114,7 +114,7 @@
           type     : 'mini'
           cssClass : 'error'
           title    : onFailMsg
-          duration : 5000
+          duration : 3500

       onFail?()


appsUri

appsUri

--- core/KD.coffee
+++ core/KD.coffee
@@ -41,17 +41,9 @@
   subscriptions   : []
   classes         : {}

-  apiUri: switch KD.env
-    when 'beta'
-      'https://api.koding.com'
-    else
-      'https://dev-api.koding.com'
+  apiUri: switch KD.config.apiUri

-  appsUri: switch KD.env
-    when 'beta'
-      'http://app.koding.com'
-    else
-      'http://dev-app.koding.com'
+  appsUri: switch KD.config.appsUri

   whoami:-> KD.getSingleton('mainController').userAccount


support reverse direction scroll with infinite-scroll-reverse directive

--- src/infinite-scroll.coffee
+++ src/infinite-scroll.coffee
@@ -204,3 +204,206 @@
       $interval.cancel checkInterval
     )
 ]
+
+mod.directive 'infiniteScrollReverse', ['$rootScope', '$window', '$interval', 'THROTTLE_MILLISECONDS', \
+                                  ($rootScope, $window, $interval, THROTTLE_MILLISECONDS) ->
+  scope:
+    infiniteScrollReverse: '&'
+    infiniteScrollContainer: '='
+    infiniteScrollDistance: '='
+    infiniteScrollDisabled: '='
+    infiniteScrollUseDocumentBottom: '=',
+    infiniteScrollListenForEvent: '@'
+
+  link: (scope, elem, attrs) ->
+    windowElement = angular.element($window)
+
+    scrollDistance = null
+    scrollEnabled = null
+    checkWhenEnabled = null
+    container = null
+    immediateCheck = true
+    useDocumentBottom = false
+    unregisterEventListener = null
+    checkInterval = false
+
+    height = (elem) ->
+      elem = elem[0] or elem
+
+      if isNaN(elem.offsetHeight) then elem.document.documentElement.clientHeight else elem.offsetHeight
+
+    offsetTop = (elem) ->
+      if not elem[0].getBoundingClientRect or elem.css('none')
+        return
+
+      elem[0].getBoundingClientRect().top + pageYOffset(elem)
+
+    pageYOffset = (elem) ->
+      elem = elem[0] or elem
+
+      if isNaN(window.pageYOffset) then elem.document.documentElement.scrollTop else elem.ownerDocument.defaultView.pageYOffset
+
+    # infinite-scroll specifies a function to call when the window,
+    # or some other container specified by infinite-scroll-container,
+    # is scrolled within a certain range from the bottom of the
+    # document. It is recommended to use infinite-scroll-disabled
+    # with a boolean that is set to true when the function is
+    # called in order to throttle the function call.
+    handler = ->
+      if container == windowElement
+        containerBottom = height(container) + pageYOffset(container[0].document.documentElement)
+        elementBottom = offsetTop(elem) + height(elem)
+      else
+        containerBottom = height(container)
+        containerTopOffset = 0
+        if offsetTop(container) != undefined
+          containerTopOffset = offsetTop(container)
+        elementBottom = offsetTop(elem) - containerTopOffset + height(elem)
+
+      if(useDocumentBottom)
+        elementBottom = height((elem[0].ownerDocument || elem[0].document).documentElement)
+
+      remaining = elementBottom - containerBottom
+      shouldScroll = remaining <= height(container) * scrollDistance + 1
+
+      if shouldScroll
+        checkWhenEnabled = true
+
+        if scrollEnabled
+          if scope.$$phase || $rootScope.$$phase
+            scope.infiniteScrollReverse()
+          else
+            scope.$apply(scope.infiniteScrollReverse)
+      else
+        if checkInterval then $interval.cancel checkInterval
+        checkWhenEnabled = false
+
+    # The optional THROTTLE_MILLISECONDS configuration value specifies
+    # a minimum time that should elapse between each call to the
+    # handler. N.b. the first call the handler will be run
+    # immediately, and the final call will always result in the
+    # handler being called after the `wait` period elapses.
+    # A slimmed down version of underscore's implementation.
+    throttle = (func, wait) ->
+      timeout = null
+      previous = 0
+      later = ->
+        previous = new Date().getTime()
+        $interval.cancel(timeout)
+        timeout = null
+        func.call()
+
+      return ->
+        now = new Date().getTime()
+        remaining = wait - (now - previous)
+        if remaining <= 0
+          $interval.cancel(timeout)
+          timeout = null
+          previous = now
+          func.call()
+        else timeout = $interval(later, remaining, 1) unless timeout
+
+    if THROTTLE_MILLISECONDS?
+      handler = throttle(handler, THROTTLE_MILLISECONDS)
+
+    scope.$on '$destroy', ->
+      container.unbind 'scroll', handler
+      if unregisterEventListener?
+        unregisterEventListener()
+        unregisterEventListener = null
+
+    # infinite-scroll-distance specifies how close to the bottom of the page
+    # the window is allowed to be before we trigger a new scroll. The value
+    # provided is multiplied by the container height; for example, to load
+    # more when the bottom of the page is less than 3 container heights away,
+    # specify a value of 3. Defaults to 0.
+    handleInfiniteScrollDistance = (v) ->
+      scrollDistance = parseFloat(v) or 0
+
+    scope.$watch 'infiniteScrollDistance', handleInfiniteScrollDistance
+    # If I don't explicitly call the handler here, tests fail. Don't know why yet.
+    handleInfiniteScrollDistance scope.infiniteScrollDistance
+
+    # infinite-scroll-disabled specifies a boolean that will keep the
+    # infnite scroll function from being called; this is useful for
+    # debouncing or throttling the function call. If an infinite
+    # scroll is triggered but this value evaluates to true, then
+    # once it switches back to false the infinite scroll function
+    # will be triggered again.
+    handleInfiniteScrollDisabled = (v) ->
+      scrollEnabled = !v
+      if scrollEnabled && checkWhenEnabled
+        checkWhenEnabled = false
+        handler()
+
+    scope.$watch 'infiniteScrollDisabled', handleInfiniteScrollDisabled
+    # If I don't explicitly call the handler here, tests fail. Don't know why yet.
+    handleInfiniteScrollDisabled scope.infiniteScrollDisabled
+
+    # use the bottom of the document instead of the element's bottom.
+    # This useful when the element does not have a height due to its
+    # children being absolute positioned.
+    handleInfiniteScrollUseDocumentBottom = (v) ->
+      useDocumentBottom = v
+
+    scope.$watch 'infiniteScrollUseDocumentBottom', handleInfiniteScrollUseDocumentBottom
+    handleInfiniteScrollUseDocumentBottom scope.infiniteScrollUseDocumentBottom
+
+    # infinite-scroll-container sets the container which we want to be
+    # infinte scrolled, instead of the whole window. Must be an
+    # Angular or jQuery element, or, if jQuery is loaded,
+    # a jQuery selector as a string.
+    changeContainer = (newContainer) ->
+      if container?
+        container.unbind 'scroll', handler
+
+      container = newContainer
+      if newContainer?
+        container.bind 'scroll', handler
+
+    changeContainer windowElement
+
+    if scope.infiniteScrollListenForEvent
+      unregisterEventListener = $rootScope.$on scope.infiniteScrollListenForEvent, handler
+
+    handleInfiniteScrollContainer = (newContainer) ->
+      # TODO: For some reason newContainer is sometimes null instead
+      # of the empty array, which Angular is supposed to pass when the
+      # element is not defined
+      # (https://github.com/sroze/ngInfiniteScroll/pull/7#commitcomment-5748431).
+      # So I leave both checks.
+      if (not newContainer?) or newContainer.length == 0
+        return
+
+      if newContainer.nodeType && newContainer.nodeType == 1
+        newContainer = angular.element newContainer
+      else if typeof newContainer.append == 'function'
+        newContainer = angular.element newContainer[newContainer.length - 1]
+      else if typeof newContainer == 'string'
+        newContainer = angular.element document.querySelector newContainer
+
+      if newContainer?
+        changeContainer newContainer
+      else
+        throw new Error("invalid infinite-scroll-container attribute.")
+
+    scope.$watch 'infiniteScrollContainer', handleInfiniteScrollContainer
+    handleInfiniteScrollContainer(scope.infiniteScrollContainer or [])
+
+    # infinite-scroll-parent establishes this element's parent as the
+    # container infinitely scrolled instead of the whole window.
+    if attrs.infiniteScrollParent?
+      changeContainer angular.element elem.parent()
+
+    # infinte-scoll-immediate-check sets whether or not run the
+    # expression passed on infinite-scroll for the first time when the
+    # directive first loads, before any actual scroll.
+    if attrs.infiniteScrollImmediateCheck?
+      immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck)
+
+    checkInterval = $interval (->
+      if immediateCheck
+        handler()
+      $interval.cancel checkInterval
+    )
+]


Added collectionName to methods

Added collectionName to methods

--- counter.coffee
+++ counter.coffee
@@ -9,8 +9,8 @@
   getRawMongoCollection(collectionName)

-callCounter = (method, args...) ->
-  Counters = getCounterCollection()
+callCounter = (method, collectionName, args...) ->
+  Counters = getCounterCollection(collectionName)
   if Meteor._wrapAsync?
     Meteor._wrapAsync(_.bind(Counters[method], Counters))(args...)
   else
@@ -19,13 +19,14 @@
     future.wait()

-_deleteCounters = ->
-  callCounter('remove', {}, {safe: true})
+_deleteCounters = (collectionName) ->
+  callCounter('remove', collectionName, {}, {safe: true})

-_incrementCounter = (counterName, amount = 1) ->
+_incrementCounter = (collectionName, counterName, amount = 1) ->
   newDoc = callCounter(
     'findAndModify',
+    collectionName,
     {_id: counterName},         # query
     null,                       # sort
     {$inc: {seq: amount}},      # update
@@ -34,13 +35,14 @@
   return newDoc.seq

-_decrementCounter = (counterName, amount = 1) ->
-  _incrementCounter(counterName, -amount)
+_decrementCounter = (collectionName, counterName, amount = 1) ->
+  _incrementCounter(collectionName, counterName, -amount)

-_setCounter = (counterName, value) ->
+_setCounter = (collectionName, counterName, value) ->
   callCounter(
     'update',
+    collectionName,
     {_id: counterName},
     {$set: {seq: value}}
   )


Updated tooltip attributes to follow naming convention.

--- src/directives/tooltip.coffee
+++ src/directives/tooltip.coffee
@@ -5,10 +5,10 @@
 @description
 Tooltip directive

-@param {String} mac-tooltip Text to show in tooltip
-@param {String} mac-tooltip-direction Direction of tooltip
-@param {String} mac-tooltip-trigger How tooltip is triggered
-@param {Boolean} mac-tooltip-inside Should the tooltip be appended inside element
+@param {String}  mac-tooltip Text to show in tooltip
+@param {String}  direction   Direction of tooltip
+@param {String}  trigger     How tooltip is triggered
+@param {Boolean} inside      Should the tooltip be appended inside element
 ###

 angular.module("Mac").directive "macTooltip", ->
@@ -20,8 +20,8 @@
     enabled = false

     showTip = (event) ->
-      inside    = attrs.macTooltipInside?
-      direction = attrs.macTooltipDirection or "top"
+      inside    = attrs.inside?
+      direction = attrs.direction or "top"
       tip       = if inside then element else $(document.body)
       tooltip   = $("""

#{text}

""")
       tip.append tooltip
@@ -71,7 +71,7 @@
         text = value

         unless enabled
-          trigger = attrs.macTooltipTrigger or "hover"
+          trigger = attrs.trigger or "hover"
           unless trigger in ["hover", "click"]
             return console.error "Invalid trigger"


Fixed a bug where sometimes scripts setting playback would be overridden by the surface resetting playback after casting is done.

--- app/lib/surface/Surface.coffee
+++ app/lib/surface/Surface.coffee
@@ -281,6 +281,7 @@

   onSetPlaying: (e) ->
     @playing = (e ? {}).playing ? true
+    @setPlayingCalled = true
     if @playing and @currentFrame >= (@world.totalFrames - 5)
       @currentFrame = 0
     if @fastForwarding and not @playing
@@ -352,6 +353,7 @@
     @casting = true
     @wasPlayingWhenCastingBegan = @playing
     Backbone.Mediator.publish 'level-set-playing', { playing: false }
+    @setPlayingCalled = false # don't overwrite playing settings if they changed by, say, scripts

     if @coordinateDisplay?
       @surfaceTextLayer.removeChild @coordinateDisplay
@@ -370,7 +372,7 @@

     # This has a tendency to break scripts that are waiting for playback to change when the level is loaded
     # so only run it after the first world is created.
-    Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan } unless event.firstWorld
+    Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan } unless event.firstWorld or @setPlayingCalled

     fastForwardTo = null
     if @playing


Use the Backbone.Form constructor for modal forms

Rather than use the root form's constructor, which may have properties
like `fields` or `template` defined on it, use the base Backbone.Form
constructor to create modal forms.

Closes #2, #7

--- src/editors/extra/list.coffee
+++ src/editors/extra/list.coffee
@@ -362,7 +362,7 @@
       @itemToString value
     openEditor: ->
       self = this
-      ModalForm = @form.constructor
+      ModalForm = Backbone.Form
       form = @modalForm = new ModalForm(
         schema: @nestedSchema
         data: @value)


Form

Form

- New parameter "render_as_grid", set "true" to enable
  grid style rendering in forms. "grid" is read as fraction
  in field's form options "grid" key.

--- src/elements/Form/Form.coffee
+++ src/elements/Form/Form.coffee
@@ -38,6 +38,10 @@
 				check: Boolean
 			render_as_block:
 				check: Boolean
+			render_as_grid:
+				default: false
+				mandatory: true
+				check: Boolean
 			top: {}
 			bottom: {}

@@ -97,6 +101,10 @@
 			@checkbox = null

 		@__verticalLayout = new VerticalLayout(vl_opts)
+
+		if @_render_as_grid
+			@__verticalLayout.addClass("cui-form--grid")
+
 		@__verticalLayout

@@ -596,6 +604,7 @@

 			field = fields[field_idx]
 			hint_div = null
+			grid = field._form?.grid

 			if field._form and (not isNull(field._form.hint) or field._form.right)

@@ -618,6 +627,7 @@
 					# append deprecated stuff to the hint div
 					# you should use ".hint" instead
 					append(get_append(field._form.right), hint_div)
+

 			if field instanceof Form and field.renderAsBlock()
@@ -711,6 +721,9 @@
 				# used to set row visibility
 				DOM.data(tr, "data-field", field)

+				if grid
+					tr.setAttribute("data-cui-grid", grid)
+
 				table.appendChild(tr)
 			else
 				row = CUI.DOM.element("DIV", class: "cui-form-row", "data-for-field": field.getUniqueId())
@@ -720,6 +733,9 @@

 				# used to set row visibility
 				DOM.data(row, "data-field", field)
+
+				if grid
+					row.setAttribute("data-cui-grid", grid)

 				table.appendChild(row)


Fix clean directory detection on git 2.9.x

--- src/lib/GitCommands.coffee
+++ src/lib/GitCommands.coffee
@@ -11,7 +11,7 @@
 env = process.env
 env.GIT_MERGE_AUTOEDIT = 'no'

-GIT_CLEAN_REGEX = /^nothing to commit,? \(?working directory clean\)?$/m
+GIT_CLEAN_REGEX = /^nothing to commit,? \(?working (directory|tree) clean\)?$/m
 AVH_EDITION_REGEX = /AVH Edition/

 class GitCommands


Add RequestCreateCtrl

Add RequestCreateCtrl

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -36,3 +36,13 @@
           if status == 200 and data._status == 'ERR'
             for field, message of data._issues
               flash.error = "#{field}: #{message}"
+  .controller 'RequestCreateCtrl', ($scope, Request, RequestForm, flash) ->
+    $scope.formTemplate = RequestForm 'wps001'
+    # FIXME: Should not hardcode the service code here
+    $scope.form =
+      service_code: "wps001"
+    $scope.save = () ->
+      Request.save $scope.form, (request) ->
+        console.log "Successfully created request", request
+        if request._status == 'OK'
+          flash.success = 'Request successfully created!'


Remove sass:compressed task

Remove sass:compressed task

--- app/templates/Gruntfile.coffee
+++ app/templates/Gruntfile.coffee
@@ -19,11 +19,6 @@
           '../htdocs/common/css/basic.css': '../htdocs/_scss/basic.scss'
         options:
           style: 'expanded'
-
-      compressed:
-        files: '<%= sass.default.files %>'
-        options:
-          style: 'compressed'

     connect:
       livereload:


js has nothing to do with coffee and therefore can not be bitter

--- app/templates/_Gulpfile.coffee
+++ app/templates/_Gulpfile.coffee
@@ -105,7 +105,7 @@
 #*------------------------------------*\
 #   $CONTRIB-UGLIFY
 #*------------------------------------*/
-gulp.task "bitter", () ->
+gulp.task "uglifyJs", () ->
 	gulp.src [conf.path.js + "/main.js"]
 	.pipe uglify()
 	.pipe rename({suffix: '.min'})
@@ -302,7 +302,7 @@
 # #*------------------------------------*/
 # grunt.registerTask 'default', ['watch']
 # grunt.registerTask 'optim', ['imageoptim', 'svgmin']
-gulp.task "build", ["bitter", "minify"]
+gulp.task "build", ["uglifyJs", "minify"]
 # grunt.registerTask 'depcheck', ['devUpdate:check']
 # grunt.registerTask 'depask', ['devUpdate:ask']
 # grunt.registerTask 'depup', ['devUpdate:up']


test(discover): Test for invalid responses

--- test/anvilClientSpec.coffee
+++ test/anvilClientSpec.coffee
@@ -243,6 +243,40 @@
         failure.should.have.been.called

+    describe 'with an invalid response', ->
+
+      beforeEach (done) ->
+        data = issuer: anvil.issuer
+        anvil = new AnvilConnect config
+
+        nock(anvil.issuer)
+          .get('/.well-known/openid-configuration')
+          .reply(200, 'This isn\'t JSON!')
+
+        success = sinon.spy ->
+          done()
+        failure = sinon.spy ->
+          done()
+        promise = anvil.discover()
+          .then(success)
+          .catch(failure)
+
+      after ->
+        nock.cleanAll()
+
+      it 'should return a promise', ->
+        promise.should.be.instanceof Promise
+
+      it 'should not provide the openid configuration', ->
+        success.should.not.have.been.called
+
+      it 'should not set configuration', ->
+        expect(anvil.configuration).to.be.undefined
+
+      it 'should not catch an error', ->
+        failure.should.have.been.called
+
+

   describe 'jwks', ->


Don't echo empty commands

Don't echo empty commands

--- apps/grepo_base/static/coffee/grepo.coffee
+++ apps/grepo_base/static/coffee/grepo.coffee
@@ -56,7 +56,7 @@
   [name, argv...] = command.split(/\s+/)

   if name isnt "grepo"
-    term.error "command not found: #{name}"
+    term.error "command not found: #{name}" if name
   else
     $.getJSON "/opster/", argv: argv, (xhr) ->
       return term.error(xhr.stderr) if xhr.stderr


GameObject.Effect

GameObject.Effect

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -23,7 +23,7 @@

   @name fadeOut
   @methodOf GameObject#
-  @param {Number} [duration=30] How m
+  @param {Number} [duration=30] Time to fade out in
   @param {Function} [callback=null] The function to execute when the sprite has finished fading.
   ###
   fadeOut: (duration, callback) ->


module renames

module renames

--- src/game_state.cameras.coffee
+++ src/game_state.cameras.coffee
@@ -1,33 +0,0 @@
-GameState.Cameras = (I, self) ->
-  cameras = [Camera()]
-
-  self.bind 'update', (elapsedTime) ->
-    self.cameras().invoke 'trigger', 'update', elapsedTime
-
-  self.bind 'afterUpdate', (elapsedTime) ->
-    self.cameras().invoke 'trigger', 'afterUpdate', elapsedTime
-
-  self.bind 'draw', (canvas) ->
-    self.cameras().invoke 'trigger', 'draw', canvas, self.objects()
-
-  self.bind 'overlay', (canvas) ->
-    self.cameras().invoke 'trigger', 'overlay', canvas, self.objects()
-
-  return {
-    addCamera: (data) ->
-      cameras.push(Camera(data))
-    ###*
-    Returns the array of camera objects.
-
-    @name cameras
-    @methodOf Engine#
-    @returns {Array}
-    ###
-    cameras: (newCameras) ->
-      if newCameras
-        cameras = newCameras
-
-        return self
-      else
-        return cameras
-  }


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state_cameras.coffee
+++ src/game_state_cameras.coffee
@@ -2,7 +2,7 @@
   cameras = [Camera()]

   self.bind 'update', (elapsedTime) ->
-    self.cameras().invoke 'trigger', 'update',
+    self.cameras().invoke 'trigger', 'update', elapsedTime

   self.bind 'afterUpdate', ->
     self.cameras().invoke 'trigger', 'afterUpdate'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -25,6 +25,6 @@
     x: 4
     y: 20

-  equals engine.first('type=testObj').l, obj1
+  equals engine.first('type=testObj').length, obj1

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -32,6 +32,6 @@
     x: 9
     y: 10

-  equals engine.first('.type=testOb')
+  equals engine.first('.type=testObj'),

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -17,7 +17,6 @@

   equals engine.find('.findable').length, 2

-
 test "#first", ->
   engine = Engine()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -25,7 +25,7 @@

   indexSum = 0

-  engine.each '', (obj, index) ->
+  engine.each '.rad=A', (obj, index) ->
     indexSum += index

     if index is 0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -21,7 +21,7 @@
   indexSum = 0

   engine.each (obj, index) ->
-    indexSum +=
+    indexSum += index

     if index is 0
       equals obj, obj1


Change coding style

Change coding style

--- assets/javascripts/game.coffee
+++ assets/javascripts/game.coffee
@@ -45,17 +45,17 @@

       totalCosts: (key) ->
         @data.costs[key] ?= 0
-        return @data.costs[key] * @data["farmSize"]
+        return @data.costs[key] * @data.farmSize
       labor: (key) ->
         @data.hours[key] ?= 0
-        return (@data["farmSize"] * @data["wages"] / 8) * @data.hours[key]
+        return (@data.farmSize * @data.wages / 8) * @data.hours[key]

       states:
         startGame:
           input: (line) ->
             response = @getYesNo(line)
             unless @errorFlag
-              @data["skipIntro"] = response
+              @data.skipIntro = response
               if response
                 @transition "mainIntro"
               else
@@ -258,13 +258,13 @@
             return

           input: (line) ->
-            @data["farmSize"] = @getNumber(@data["farmSize"], line)
-            unless @errorFlag
-              if @data["farmSize"] >= 5
+            @data.farmSize = @getNumber(@data.farmSize, line)
+            unless @errorFlag
+              if @data.farmSize >= 5
                 @message = "THAT'S PRETTY LARGE FOR A PEASANT IN LUZON."
               else
                 @message = ""
-              unless @data["runCount"] > 1
+              unless @data.runCount > 1
                 @transition "setWages"
               else
                 @transition "startProcessing"
@@ -276,15 +276,15 @@
             return

           input: (line) ->
-            @data["wages"] = @getNumber(@data["wages"], line)
-            unless @errorFlag
-              if @data["wages"] <= 100
+            @data.wages = @getNumber(@data.wages, line)
+            unless @errorFlag
+              if @data.wages <= 100
                 @message = "Those are terrible wages - you cheapskate!!!\n"
-              else if @data["wages"] > 139
+              else if @data.wages > 139
                 @message = "Very high wages - I doubt you can afford them\n"
               else
                 @message = ""
-              unless @data["runCount"] > 1
+              unless @data.runCount > 1
                 @transition "setSeedType"
               else
                 @transition "startProcessing"
@@ -296,9 +296,9 @@
             return

           input: (line) ->
-            @data["seedType"] = @getResponse("LOCAL,IR64", @data["seedType"], line)
-            unless @errorFlag
-              unless @data["runCount"] > 1
+            @data.seedType = @getResponse("LOCAL,IR64", @data.seedType, line)
+            unless @errorFlag
+              unless @data.runCount > 1
                 @transition "setSeedCost"
               else
                 @transition "startProcessing"
@@ -314,14 +314,14 @@
             unless @errorFlag
               if @data.costs["seeds"] > 680
                 @message = "expensive seed - it may not be good - too old\n"
-                @data["seedDefectFactor"] = 0.9
+                @data.seedDefectFactor = 0.9
               else if @data.costs["seeds"] <= 560
                 @message = "cheap seed - why waste time with it - poor germination\n"
-                @data["seedDefectFactor"] = 0.9
+                @data.seedDefectFactor = 0.9
               else
                 @message = ""
-                @data["seedDefectFactor"] = 1.0
-              unless @data["runCount"] > 1
+                @data.seedDefectFactor = 1.0
+              unless @data.runCount > 1
                 @transition "setPesticide"
               else
                 @transition "startProcessing"
@@ -336,10 +336,10 @@
             response = @getYesNo(line)
             unless @errorFlag
               if response
-                @data["pesticideYieldFactor"] = 1.15
+                @data.pesticideYieldFactor = 1.15
                 @data.costs["earlyPestControl"] = 390
                 @data.hours["earlyPestControl"] = 1.5 * 8
-                unless @data["runCount"] > 1
+                unless @data.runCount > 1
                   @transition "setFertilizer"
                 else
                   @transition "startProcessing"
@@ -358,12 +358,12 @@
             response = @getYesNo(line)
             unless @errorFlag
               if response
-                @data["pesticideYieldFactor"] = 1.15
-                @data.costs["early_pest_control"] = 390
-                @data.hours["early_pest_control"] = 1.5 * 8
-              else
-                @data["pesticideYieldFactor"] = 1
-              unless @data["runCount"] > 1
+                @data.pesticideYieldFactor = 1.15
+                @data.costs["earlyPestPontrol"] = 390
+                @data.hours["earlyPestControl"] = 1.5 * 8
+              else
+                @data.pesticideYieldFactor = 1
+              unless @data.runCount > 1
                 @transition "setFertilizer"
               else
                 @transition "startProcessing"
@@ -375,16 +375,16 @@
             return

           input: (line) ->
-            @data["fertApplRate"] = @getNumber(@data["fertApplRate"], line)
-            unless @errorFlag
-              if @data["fertApplRate"] <= 0
-                @data["fertApplRate"] = 0
+            @data.fertApplRate = @getNumber(@data.fertApplRate, line)
+            unless @errorFlag
+              if @data.fertApplRate <= 0
+                @data.fertApplRate = 0
                 @transition "setLandPrep"
               else
-                if @data["fertApplRate"] > 400
+                if @data.fertApplRate > 400
                   @message = """

-                    #{@data["fertApplRate"]} KG. OF UREA CONTAINS #{@data.fertNContent()} KG. OF NITROGEN.
+                    #{@data.fertApplRate} KG. OF UREA CONTAINS #{@data.fertNContent()} KG. OF NITROGEN.
                     YOUR CROP CAN NOT USE THAT MUCH.  YOU MAY CAUSE SOME
                     DAMAGE TO THE ECOSYSTEMS IN NEARBY STREAMS.  AT THE
                     VERY LEAST, YOU HAVE WASTED MANY PESOS.
@@ -402,16 +402,16 @@
             return

           input: (line) ->
-            @data["fertCost"] = @getNumber(@data["fertCost"], line)
-            unless @errorFlag
-              if @data["fertCost"] <= 6.8
+            @data.fertCost = @getNumber(@data.fertCost, line)
+            unless @errorFlag
+              if @data.fertCost <= 6.8
                 @message = "You can not buy it so CHEAPLY - PLEASE REPEAT\n"
-              else if @data["fertCost"] > 7.4
+              else if @data.fertCost > 7.4
                 @message = "THAT IS EXPENSIVE FERTILIZER - PLEASE REPEAT\n"
               else
-                @data.costs["fertilizer"] = @data["fertApplRate"] * @data["fertCost"]
-                @data.hours["fertilizer"] = (1.5 + @data["fertApplRate"]/50) * 8 # Hours of labor needed
-                unless @data["runCount"] > 1
+                @data.costs["fertilizer"] = @data.fertApplRate * @data.fertCost
+                @data.hours["fertilizer"] = (1.5 + @data.fertApplRate/50) * 8 # Hours of labor needed
+                unless @data.runCount > 1
                   @transition "setLandPrep"
                 else
                   @transition "startProcessing"
@@ -425,16 +425,16 @@
             return

           input: (line) ->
-            @data["prepMethod"] = @getResponse("CARABAO,TRACTOR", @data["prepMethod"], line)
-            unless @errorFlag
-              if @data["prepMethod"] is "CARABAO"
+            @data.prepMethod = @getResponse("CARABAO,TRACTOR", @data.prepMethod, line)
+            unless @errorFlag
+              if @data.prepMethod is "CARABAO"
                 @transition "setCarabaoOwnership"
               else
-                @data["ownOrHire"] = "HIRE"
+                @data.ownOrHire = "HIRE"
                 @message = "\nTHE CURRENT RATE FOR HIRED TRACTOR PLUS DRIVER IS\n" + @TRACTOR_RATE + " PESOS/HOUR.\n"
                 @data.costs["seedbedPrep"] = @CARABAO_RATE * @SEEDBED_PREP_HOURS
                 @data.costs["landPrep"] = @TRACTOR_RATE * @TRACTOR_HOURS
-                unless @data["runCount"] > 1
+                unless @data.runCount > 1
                   @transition "setWeeding"
                 else
                   @transition "startProcessing"
@@ -446,9 +446,9 @@
             return

           input: (line) ->
-            @data["ownOrHire"] = @getResponse("OWN,HIRE", @data["ownOrHire"], line)
-            unless @errorFlag
-              if @data["ownOrHire"] is "HIRE"
+            @data.ownOrHire = @getResponse("OWN,HIRE", @data.ownOrHire, line)
+            unless @errorFlag
+              if @data.ownOrHire is "HIRE"
                 @message = "\nTHE CURRENT RATE FOR CARABAO PLUS HANDLER IS " + @CARABAO_RATE + " PESOS/\n" + "HOUR.\n"
                 @data.costs["seedbedPrep"] = @CARABAO_RATE * @SEEDBED_PREP_HOURS
                 @data.costs["landPrep"] = @CARABAO_RATE * @CARABAO_HOURS
@@ -457,7 +457,7 @@
                 @data.costs["landPrep"] = 2.00/6*(@SEEDBED_PREP_HOURS + @CARABAO_HOURS) # (2 pesos/day)/(6 hours/day)x(total hours)
                 @data.hours["landPrep"] = @CARABAO_HOURS

-              unless @data["runCount"] > 1
+              unless @data.runCount > 1
                 @transition "setWeeding"
               else
                 @transition "startProcessing"
@@ -478,10 +478,10 @@
             return

           input: (line) ->
-            @data["weedingMethod"] = @getResponse(@VALID_WEEDING(), @data["weedingMethod"], line)
-            unless @errorFlag
-              @data.costs["weeding"] = @WEEDING_EFFECTS[@data["weedingMethod"]][2]
-              @data.hours["weeding"] = @WEEDING_EFFECTS[@data["weedingMethod"]][1]
+            @data.weedingMethod = @getResponse(@VALID_WEEDING(), @data.weedingMethod, line)
+            unless @errorFlag
+              @data.costs["weeding"] = @WEEDING_EFFECTS[@data.weedingMethod][2]
+              @data.hours["weeding"] = @WEEDING_EFFECTS[@data.weedingMethod][1]
               @transition "setMolluscicide"
             return

@@ -495,7 +495,7 @@
             response = @getYesNo(line)
             unless @errorFlag
               if response
-                @data["snailAttacks"] = 0
+                @data.snailAttacks = 0
                 @data.costs["molluscicide"] = 315
                 @data.hours["molluscicide"] = 18
                 @transition "setWeedingStart"
@@ -526,8 +526,8 @@

           input: (line) ->
             @data.weedingDaysStart = @getNumber(@data.weedingDaysStart, line)
-            @transition "setThreshing"  unless @errorFlag or @data["runCount"] > 1
-            @transition "startProcessing" if @data["runCount"] > 1
+            @transition "setThreshing"  unless @errorFlag or @data.runCount > 1
+            @transition "startProcessing" if @data.runCount > 1

             return

@@ -537,9 +537,9 @@
             return

           input: (line) ->
-            @data["threshingMethod"] = @getResponse("HAND,MACHINE", @data["threshingMethod"], line)
-            @transition "setMarketValue" unless @errorFlag or @data["runCount"] > 1
-            @transition "startProcessing" if @data["runCount"] > 1
+            @data.threshingMethod = @getResponse("HAND,MACHINE", @data.threshingMethod, line)
+            @transition "setMarketValue" unless @errorFlag or @data.runCount > 1
+            @transition "startProcessing" if @data.runCount > 1
             return

         setMarketValue:
@@ -548,9 +548,9 @@
             return

           input: (line) ->
-            @data["price"] = @getNumber(@data["price"], line)
-            unless @errorFlag
-              if @data["price"] > 6.35
+            @data.price = @getNumber(@data.price, line)
+            unless @errorFlag
+              if @data.price > 6.35
                 @message = "\nTHAT'S WAY ABOVE THE LATEST REPORTED MARKET PRICE.\n" + "PLEASE REPEAT THE PRICE PER KILOGRAM.\n"
               else
                 @transition "startProcessing"
@@ -558,43 +558,43 @@

         startProcessing:
           _onEnter: ->
-            @data["riceYield"] =
-              @data["pesticideYieldFactor"] * @data["seedDefectFactor"] *
-              @WEEDING_EFFECTS[@data["weedingMethod"]][0] *
+            @data.riceYield =
+              @data.pesticideYieldFactor * @data.seedDefectFactor *
+              @WEEDING_EFFECTS[@data.weedingMethod][0] *
               ( (@data.ureaFactor1() * Math.pow(@data.fertNContent(), 2)) +
                 (@data.ureaFactor2() * @data.fertNContent()) + 1 ) *
               @data.seedYield()
-            @data["riceYield"] = 0 if @data["riceYield"] < 0
+            @data.riceYield = 0 if @data.riceYield < 0

             @message = "\n\n\nrandomizing events...\n    calculating costs and yields... DONE! \n\n"
-            if @data["prepMethod"] is "TRACTOR"
-              @data["riceYield"] *= 1.07
+            if @data.prepMethod is "TRACTOR"
+              @data.riceYield *= 1.07
               @message += """

                 YOU DID A BEAUTIfUL JOB PREPARING THE FIELDS.

                 """

-            if @data["weedingDaysStart"] >= 20
+            if @data.weedingDaysStart >= 20
               @message += """

                     YOU WAITED TOO LONG TO START THE WEEDING. YOU HAVE
                     WASTED YOUR TIME AND YOUR LABOR.  TOUGH LUCK!

                 """
-              @data["riceYield"] *= 0.7
-            else if @data["weedingDaysStart"] >= 14
+              @data.riceYield *= 0.7
+            else if @data.weedingDaysStart >= 14
               @message += """

                     YOU HAVE WAITED SO LONG TO START THE WEEDING THAT IT IS
                     ONLY PARTLY SUCCESSFUL.  BETTER LUCK NEXT TIME.

                 """
-              @data["riceYield"] *= 0.85
+              @data.riceYield *= 0.85

             @message += "\n"

-            if @data["seedType"] is "IR64"
+            if @data.seedType is "IR64"
               r = Math.floor(Math.random() * 4)/100.0    # IR64 IS QUITE RESISTANT
               @message += "LOOKS AS if BACTERIAL BLIGHT AND BLAST HAVE CAUSED ABOUT\n"
             else
@@ -603,7 +603,7 @@

             @message += "#{100 * r} PERCENT LOSS OF POTENTIAL YIELD.\n"

-            @data["riceYield"] *= (1 - r)
+            @data.riceYield *= (1 - r)

             @message += """

@@ -624,7 +624,7 @@
         processing2:
           _onEnter: ->
             @message = ""
-            if @data["snailAttacks"] >= 1
+            if @data.snailAttacks >= 1
               @message += """

                 You didn't get rid of the snails last year. Too bad - they have
@@ -632,13 +632,13 @@

                 """
               r4 = Math.floor(12 + (Math.random() * 6))
-              @data["riceYield"] *= (1 - 0.01 * r4)
+              @data.riceYield *= (1 - 0.01 * r4)

             @message += """

                 THE FARM SPECIALIST ESTIMATES YOUR CROP AS IT NOW STANDS
-                AT #{Math.round(10 * @data["riceYield"] / 50) / 10} CAVANS PER HECTARE.
+                AT #{Math.round(10 * @data.riceYield / 50) / 10} CAVANS PER HECTARE.

                 Do you think that SHE is correct?

@@ -656,7 +656,7 @@
                        W E L L   DON'T COUNT ON THAT FOR HARVEST!!!!

               """
-            unless @data["skipIntro"]
+            unless @data.skipIntro

               @message += """

@@ -680,7 +680,7 @@

                 """
               @insectAttack = Math.floor(Math.random() * 5) + 1
-              if @data["seedType"] is "IR64"
+              if @data.seedType is "IR64"
                 @insectAttack /= 2
               else if @insectAttack <= 0
                 @message += """
@@ -688,7 +688,7 @@
                   """
             else
               @insectAttack = Math.floor(Math.random() * 10) + 1
-              @insectAttack /= 2 if @data["seedType"] is"IR64"         # IR64 resists insect attack.
+              @insectAttack /= 2 if @data.seedType is"IR64"         # IR64 resists insect attack.

             @message += """
               THE DEGREE OF INSECT ATTACK THIS YEAR IS #{@insectAttack}
@@ -708,14 +708,14 @@
                 @data.costs["insecticide"] = 0
                 @data.hours["insecticide"] = 0
                 @insectAttack *= 2
-              @data["riceYield"] *= (1.0 - 0.015 * @insectAttack)
+              @data.riceYield *= (1.0 - 0.015 * @insectAttack)
               @transition "processing4"
             return

         processing4:
           _onEnter: ->
             @message = ""
-            unless @data["skipIntro"]
+            unless @data.skipIntro

               @message += """

@@ -758,15 +758,15 @@

                   """

-              @data["riceYield"] *= (1.0 - 0.015 * r3)
+              @data.riceYield *= (1.0 - 0.015 * r3)
               @transition "processing5"
             return

         processing5:
           _onEnter: ->
-            @data["typhoonLevel"] = Math.floor(Math.random() * 10)
-
-            if @data["typhoonLevel"] <= 4
+            @data.typhoonLevel = Math.floor(Math.random() * 10)
+
+            if @data.typhoonLevel <= 4
               @message = "\nYour prayer has worked. The typhoon missed your area!\n"
             else
               @message = "\nSECOND TYPHOON WARNING. IT'S COMING YOUR WAY.\n"
@@ -782,7 +782,7 @@
           input: (line) ->
             response = @getYesNo(line)
             unless @errorFlag
-              @data["ratDamage"] = Math.abs(Math.sqrt(-2 * Math.log(Math.random()))*Math.sin(2 * Math.PI * Math.random()))     # Rat loss
+              @data.ratDamage = Math.abs(Math.sqrt(-2 * Math.log(Math.random()))*Math.sin(2 * Math.PI * Math.random()))     # Rat loss
               @message = ""
               if response
                 @transition "raticides"
@@ -795,7 +795,7 @@
         raticides:
           _onEnter: ->
             @message = ""
-            unless @data["skipIntro"]
+            unless @data.skipIntro
               @message += """

                 ACUTE RATICIDES GIVE SPECTACULAR RESULTS BUT IN THE LONG
@@ -812,11 +812,11 @@
             return

           input: (line) ->
-            @data["poison"] = @getResponse("CHRONIC,ACUTE", @data["poison"], line)
+            @data.poison = @getResponse("CHRONIC,ACUTE", @data.poison, line)
             unless @errorFlag
               @message = ""
-              if @data["poison"] is "CHRONIC"
-                unless @data["skipIntro"]
+              if @data.poison is "CHRONIC"
+                unless @data.skipIntro
                   @message = """

                     YOU HAVE CHOSEN WARFARIN IN .5% CONCENTRATION. YOU MUST
@@ -828,7 +828,7 @@
                 @data.costs["rodenticide"] = 67.5   # cost of warfarin /ha
                 @data.hours["rodenticide"] = 14 * 3 # Hours of labor to apply warfaran/ha.
               else
-                unless @data["skipIntro"]
+                unless @data.skipIntro
                   @message = """

                     YOU WILL USE ZINC PHOSPHIDE. MIX IT WITH 150 PARTS OF
@@ -850,32 +850,32 @@

             #      COMPUTE CROP LOSS WITH RATICIDE
             if @data.costs["rodenticide"] > 0
-              @data["ratDamage"] = Math.floor(@data["ratDamage"] *5)
+              @data.ratDamage = Math.floor(@data.ratDamage *5)

               @message += """

                 YOU HAVE CUT YOUR POTENTIAL RAT LOSS DRAMATICALLY.
-                CONGRATULATIONS! ONLY #{@data["ratDamage"]} PERCENT OF THE CROP WAS LOST.
+                CONGRATULATIONS! ONLY #{@data.ratDamage} PERCENT OF THE CROP WAS LOST.

                 """

             #      COMPUTE CROP LOSS WITHOUT RATICIDE
             else
-              @data["ratDamage"] = Math.floor(@data["ratDamage"] * 15)
+              @data.ratDamage = Math.floor(@data.ratDamage * 15)

-              if @data["ratDamage"] > 24
+              if @data.ratDamage > 24
                 @message = """

                   THE PALAY WAS DOING WELL - BUT, MAN - DID YOU GET HIT BY
                   RATS!! 10 DAYS BEFORE HARVEST THEY DESCENDED LIKE THE PLAGUE
-                  AND RUINED #{@data["ratDamage"]} PERCENT OF YOUR STAND.  EGAD!!
+                  AND RUINED #{@data.ratDamage} PERCENT OF YOUR STAND.  EGAD!!

                   """
-              else if @data["ratDamage"] > 12
+              else if @data.ratDamage > 12
                 @message = """

                   CROP LOOKED GOOD BUT IN THE WEEK BEFORE HARVEST MANY RATS
-                  CONVERGED ON THE FIELDS AND DESTROYED #{@data["ratDamage"]} PERCENT OF
+                  CONVERGED ON THE FIELDS AND DESTROYED #{@data.ratDamage} PERCENT OF
                   THE STANDING CROP.  BETTER LUCK NEXT YEAR!!

                   """
@@ -883,37 +883,37 @@
                 @message = """

                   YOU WERE VERY LUCKY THIS SEASON, PALAY GREW WELL AND YOU
-                  LOST ONLY #{@data["ratDamage"]} PERCENT OF THE CROP TO RATS.
+                  LOST ONLY #{@data.ratDamage} PERCENT OF THE CROP TO RATS.

                   """

-            @data["riceYield"] *= (1 - 0.01 * @data["ratDamage"])  # Total crop x (1-destroyed by rats)
+            @data.riceYield *= (1 - 0.01 * @data.ratDamage)  # Total crop x (1-destroyed by rats)

             #
             #      ***** TYPHOON *****
             #
-            if @data["typhoonLevel"] > 8
+            if @data.typhoonLevel > 8
               @message += """

                 TOUGH LUCK. THE TYPHOON PASSED THROUGH YOUR PROVINCE AND
                 THE CROP IS IN A TERRIBLE MESS.

                 """
-              @data["riceYield"] *= 0.65           # 35% yield loss
-            else if @data["typhoonLevel"] > 4
+              @data.riceYield *= 0.65           # 35% yield loss
+            else if @data.typhoonLevel > 4
               @message += """

                 THE TYPHOON PASSED CLOSE BY. HIGH WIND AND HEAVY RAIN CAUSED
                 GREAT LODGING BUT MUCH OF THE CROP CAN BE SAVED.

                 """
-              @data["riceYield"] *= 0.80           # 20% yield loss
+              @data.riceYield *= 0.80           # 20% yield loss

             #
             #
             #                  **** LOW WAGE LOSS ****
             #
-            if @data["wages"] < 90
+            if @data.wages < 90
               @message += """

@@ -922,8 +922,8 @@

                 """

-              @data["riceYield"] *= 0.75           # 35% yield loss
-            else if @data["wages"] <= 100
+              @data.riceYield *= 0.75           # 35% yield loss
+            else if @data.wages <= 100
               @message += """

@@ -931,7 +931,7 @@
                 workers.  WHAT A SHAME!     !

                 """
-              @data["riceYield"] *= 0.88           # 20% yield loss
+              @data.riceYield *= 0.88           # 20% yield loss

             @calculateFinalDetails()

@@ -962,9 +962,9 @@

-              RICE TYPE: #{@data["seedType"]}
-
-              FARM SIZE: #{@data["farmSize"]} HECTARES
+              RICE TYPE: #{@data.seedType}
+
+              FARM SIZE: #{@data.farmSize} HECTARES
               TOTAL YIELD: #{Math.round(@totalYield)} KILOGRAMS

                             (#{Math.round(10*@totalYield/50.0)/10} CAVANS PER HA.)
@@ -998,7 +998,7 @@

             if @totalCosts("fertilizer") > 0
               @message += """
-                    FERTILIZER (UREA) @#{ @data["fertCost"] } PESOS/KG.
+                    FERTILIZER (UREA) @#{ @data.fertCost } PESOS/KG.
                 #{sprintf(@FMT2, "  ", @totalCosts("fertilizer"))}

                 """
@@ -1027,7 +1027,7 @@

               SEEDBED PREPARATION -- 4 DAYS WORKER AND\n
               """
-            if @data["ownOrHire"] is "HIRE"
+            if @data.ownOrHire is "HIRE"
               @message += """
                     CARABAO AT #{6 * @CARABAO_RATE} PESOS PER DAY
                 #{sprintf(@FMT2, "  ", @totalCosts("seedbedPrep"))}
@@ -1051,15 +1051,15 @@
               LAND PREPARATION -- PLOWING AND HARROWING

               """
-            if @data["prepMethod"] is "TRACTOR"
-              @message += """
-                    #{@data["farmSize"] * @TRACTOR_HOURS / 8} TRACTOR DAYS @#{8 * @TRACTOR_RATE} PESOS/DAY
+            if @data.prepMethod is "TRACTOR"
+              @message += """
+                    #{@data.farmSize * @TRACTOR_HOURS / 8} TRACTOR DAYS @#{8 * @TRACTOR_RATE} PESOS/DAY
                 #{sprintf(@FMT2, "  ", @totalCosts("landPrep"))}
                 """
             else
-              if @data["ownOrHire"] is "HIRE"
+              if @data.ownOrHire is "HIRE"
                 @message += """
-                     #{@data["farmSize"] * @CARABAO_HOURS / 6} DAYS FOR CARABAO
+                     #{@data.farmSize * @CARABAO_HOURS / 6} DAYS FOR CARABAO
                       AT #{6 * @CARABAO_RATE} PESOS PER DAY
                   #{sprintf(@FMT2, "  ", @totalCosts("landPrep"))}
                   """
@@ -1088,7 +1088,7 @@
             if @totalCosts("fertilizer") > 0
               @message += """
                 COSTS OF APPLYING FERTILIZER
-                    #{@data["farmSize"] * @data.hours["fertilizer"] / 8} PERSON DAYS @#{@data["wages"]} PESOS/DAY
+                    #{@data.farmSize * @data.hours["fertilizer"] / 8} PERSON DAYS @#{@data.wages} PESOS/DAY
                 #{sprintf(@FMT2, "  ", @labor("fertilizer"))}
                 """

@@ -1109,12 +1109,12 @@
               @message += """

                 APPLICATION OF PESTICIDE
-                    2 DAYS PER HA. AT #{@data["wages"]} PESOS PER DAY
+                    2 DAYS PER HA. AT #{@data.wages} PESOS PER DAY
                 #{sprintf(@FMT2, "  ", @labor("insecticide"))}\n
                 """
-            unless @data["weedingMethod"] is "0"
-              @message += """
-                WEEDING COSTS -- METHOD #{@data["weedingMethod"]}
+            unless @data.weedingMethod is "0"
+              @message += """
+                WEEDING COSTS -- METHOD #{@data.weedingMethod}
                     #{@data.hours["weeding"]} HOURS PER HECTARE
                 #{sprintf(@FMT2, "  ", @labor("weeding"))}\n
                 """
@@ -1141,7 +1141,7 @@
               THRESHING

               """
-            if @data["threshingMethod"] is "HAND"
+            if @data.threshingMethod is "HAND"
               @message += sprintf(@FMT2, "RATE ABOUT 40 KG/HR", @labor("threshing")/2) + "\n"
             else
               @message += sprintf(@FMT2, "COST BY MACHINE - 5% OF YIELD", @totalCosts("threshing")) + "\n"
@@ -1199,7 +1199,7 @@
         summary:
           _onEnter: ->
             totalOperatingCosts = (@operatingCosts + @totalCosts("irrigation") + @totalCosts("landTax") + @mortgage)
-            profit = (@totalYield * @data["price"]) - totalOperatingCosts
+            profit = (@totalYield * @data.price) - totalOperatingCosts
             profit = Math.round(profit * 100) / 100

             @message = """
@@ -1207,8 +1207,8 @@
               S U M M A R Y
                                                       LAND REFORM OWNER
                 TOTAL VALUE OF #{Math.round(@totalYield * 100) / 100} KILOGRAMS
-                OF PALAY AT #{ @data["price"] }
-                PESOS PER KILOGRAM                        #{Math.round(@totalYield * @data["price"] * 100) / 100}
+                OF PALAY AT #{ @data.price }
+                PESOS PER KILOGRAM                        #{Math.round(@totalYield * @data.price * 100) / 100}

                 LESS TOTAL EXPENSES                       #{Math.round(totalOperatingCosts * 100) / 100}
                                                    --------------------
@@ -1219,14 +1219,14 @@

               """
-            if profit < @data["farmSize"] * 2800
+            if profit < @data.farmSize * 2800
               @message += """
                 YOU ARE ONE HECK OF A POOR FARMER - YOUR KIDS WILL STARVE
                 AT THAT RATE.  YOU MADE AT LEAST ONE terrible DECISION.
                 YOU HAD BETTER CORRECT IT BEFORE NEXT SEASON.

                 """
-            else if profit < @data["farmSize"] * 4500
+            else if profit < @data.farmSize * 4500
               @message += """
                 THAT IS NOT A GOOD YEAR AT ALL. BETTER IMPROVE YOUR TECHNIQUE
                 OR YOU WILL LOSE THE LAND YOU WORKED SO HARD TO EARN
@@ -1244,7 +1244,7 @@
         runAgain:
           _onEnter: ->
             @message = "\n\n"
-            unless @data["skipIntro"]
+            unless @data.skipIntro
               @message += """
                 YOU WILL NOW HAVE THE OPTION OF RERUNNING THE PROGRAM.
                 YOU MAY KEEP THE SAME RICE TYPE OR YOU MAY CHANGE.
@@ -1271,7 +1271,7 @@

         changeFactors:
           _onEnter: ->
-            @data["runCount"]++
+            @data.runCount++
             @message = """
               WHICH FACTOR DO YOU WISH TO CHANGE

@@ -1383,7 +1383,7 @@
         #
         #  HARVESTING
         #
-        @data.hours["harvesting"] = 0.0311 * @data["riceYield"] # .0311=number of hours to harvest 1kg.
+        @data.hours["harvesting"] = 0.0311 * @data.riceYield # .0311=number of hours to harvest 1kg.

         #
         #
@@ -1391,17 +1391,17 @@
         #
         @data.costs["threshing"] = @data.hours["threshing"] = 0

-        if @data["threshingMethod"] is "MACHINE"
-          @data["riceYield"] *= 0.97                 # 3% extra loss for machine use
-          @data.costs["threshing"] = 0.05 * @data["riceYield"] * @data["price"]   # 5% of crop goes to machine owner
+        if @data.threshingMethod is "MACHINE"
+          @data.riceYield *= 0.97                 # 3% extra loss for machine use
+          @data.costs["threshing"] = 0.05 * @data.riceYield * @data.price   # 5% of crop goes to machine owner
         else
-          @data.hours["threshing"] = 0.05 * @data["riceYield"]
+          @data.hours["threshing"] = 0.05 * @data.riceYield

         #
         #
         #  HAULING
         #
-        @data.costs["hauling"] = 0.012 * @data["riceYield"]
+        @data.costs["hauling"] = 0.012 * @data.riceYield

         #
         #      ******************************
@@ -1429,12 +1429,12 @@
         subtotal += @labor("rodenticide")              # Application of rodenticide
         subtotal += @labor("harvesting")               # Harvesting cost
         subtotal += @totalCosts("threshing")          # Cost for machine threshing
-        subtotal += @labor("threshing")                # Cost for hand threshing
+        subtotal += @labor("threshing")               # Cost for hand threshing
         subtotal += @totalCosts("hauling")            # Hauling cost
         @operatingCosts = subtotal                    # Total operating costs

-        @totalYield = @data["riceYield"] * @data["farmSize"]
-        @mortgage = (@totalYield * @data["price"]) * 4 * 0.07
+        @totalYield = @data.riceYield * @data.farmSize
+        @mortgage = (@totalYield * @data.price) * 4 * 0.07

     )


Keep the correct state for watching files so the appropriate one is recompiled

--- command_line.coffee
+++ command_line.coffee
@@ -58,10 +58,10 @@
 # Compiles the source CoffeeScript, returning the desired JavaScript, tokens,
 # or JSLint results.
 compile_scripts: ->
-  return unless source: sources.shift()
-  fs.readFile source, (err, code) ->
-    compile_script(source, code)
-    compile_scripts()
+  compile: (source) ->
+    fs.readFile source, (err, code) -> compile_script(source, code)
+  compile(source) for source in sources
+

 # Compile a single source script, containing the given code, according to the
 # requested options. Both compile_scripts and watch_scripts share this method.
@@ -83,10 +83,11 @@
 # Watch a list of source CoffeeScript files, recompiling them every time the
 # files are updated.
 watch_scripts: ->
-  for source in sources
+  watch: (source) ->
     process.watchFile source, {persistent: true, interval: 500}, (curr, prev) ->
       return if curr.mtime.getTime() is prev.mtime.getTime()
       fs.readFile source, (err, code) -> compile_script(source, code)
+  watch(source) for source in sources

 # Write out a JavaScript source file with the compiled code.
 write_js: (source, js) ->


Add tests for .get() call w/o second argument

--- test/testJsonpointer.coffee
+++ test/testJsonpointer.coffee
@@ -65,6 +65,38 @@
       expect(evaluate(t)).to.throw Error for t in invalidTargets

+    describe "call w/o second argument", () ->
+
+      it "should return function if first argument is valid JSON", () ->
+        validJSON = "null"
+
+        actual = jsonpointer.get validJSON
+        actual.should.to.be.a "function"
+
+
+      it "should throw an exception if first argument is not JSON", () ->
+        invalidJSON = "invalid"
+        curryGet = (json) -> () -> jsonpointer.get json
+
+        expect(curryGet(invalidJSON)).to.throw Error
+
+
+    describe "result of call w/o second argument", () ->
+
+      it "should evaluate given pointer on previously passed document", () ->
+        target =
+          foo: "bar"
+        targetAsString = JSON.stringify target
+        evaluate = jsonpointer.get targetAsString
+        expectedResults =
+          "/foo": target.foo
+          "/baz": undefined
+
+        check = (pointer, expected) ->
+          expect(evaluate pointer).to.be.deep.equal expected
+        check(pointer, expected) for pointer, expected of expectedResults
+
+
     describe "pointer validation", () ->
       target =
         foo: "bar"


Fixes #405

Fixes #405

--- src/languages/nunjucks.coffee
+++ src/languages/nunjucks.coffee
@@ -1,4 +1,3 @@
-console.log('loading nunjucks')

 module.exports = {


Rearrange default metadata.

Rearrange default metadata.

--- base-site/_default.meta.coffee
+++ base-site/_default.meta.coffee
@@ -1,5 +1,13 @@
 allowed_extensions= ['txt','pdf','html','htm','gif','jpg','jpeg','ico','tif','png','tiff','bmp']
 module.exports=
+  # An object named 'malifi' will be added to req.  That object contains references
+  # to the current metadata, a method to look up other metadata, the site stack
+  # and decoded path, url and host strings.  If _malifi_alias is defined in the
+  # metadata, a second reference to the same object will be added to req using
+  # that name.  Assuming the default of '_', the metadata can be accessed either
+  # as req.malifi.meta or req._.meta
+  _malifi_alias: '_'
+
   # The module that will serve reroute requests.
   # Reroute is an internal redirect.  The request will be served as if it were
   # for the new URL rather than the original one.  Rerouted requests may access
@@ -23,14 +31,6 @@
   # be 404ed unless there is a *.post.js, *.post.coffee or other such post-
   # specific handler
   _getOnly: true
-
-  # An object named 'malifi' will be added to req.  That object contains references
-  # to the current metadata, a method to look up other metadata, the site stack
-  # and decoded path, url and host strings.  If _malifi_alias is defined in the
-  # metadata, a second reference to the same object will be added to req using
-  # that name.  Since the default is '_', the metadata can be accessed either as
-  # req.malifi.meta or req._.meta
-  _malifi_alias: '_'

   # Valid URLs may include the following extensions.
   # Any other extensions at the end of a URL will be disallowed.


stage

stage

--- config/stage.coffee
+++ config/stage.coffee
@@ -45,6 +45,7 @@
     login       : 'stage'
     password    : '#[85_[*zh7%4;4l6T]F!'
     vhost       : 'stage'
+    pidFile     : '/var/run/broker.pid'
   kites:
     disconnectTimeout: 3e3
   email         :


hub: increase max size for published documents to 5MB

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2476,7 +2476,7 @@
                     return
                 project.read_file
                     path    : mesg.path
-                    maxsize : 1000000  # restrict to 1MB -- for now
+                    maxsize : 5000000  # restrict to 5MB -- for now
                     cb      : (err, data) =>
                         if err
                             @error_to_client(id:mesg.id, error:err)


update Report.create test

update Report.create test

--- server/test/units/report.coffee
+++ server/test/units/report.coffee
@@ -4,7 +4,8 @@

 suite('Report')
-test('.create', ->
+test('.create', (done) ->
+  Report = require '../../models/report'
   report_attributes =
     title: 'Lovely Report'
     brief: 'Gtta b brf n dis dscrption'
@@ -14,10 +15,12 @@
     conclusion: 'This report is lovely afterall.'

   Report.create(report_attributes).success( ->
-    Report.findAndCountAll().
+    Report.count().
       success((count)->
         assert.equal 1, count
-      ).failure( ->
+        done()
+      ).failure( (err) ->
+        throw err
         throw 'Failed to find Reports'
       )
   ).


More simple errorHandling

More simple errorHandling

--- src/octonode/client.coffee
+++ src/octonode/client.coffee
@@ -39,6 +39,12 @@
     uri+= "?access_token=#{@token}" if @token
     return uri

+  errorHandle: (res, body, callback) ->
+    # TODO: Unprocessable entity
+    if res.statusCode is 422 then throw new Error body.message
+    if res.statusCode in [400, 401, 404] then throw new Error body.message
+    callback res.statusCode, body
+
   # Github api GET request
   get: (path, callback) ->
     request
@@ -46,11 +52,7 @@
       method: 'GET'
     , (err, res, body) ->
       if err then throw err
-      body = JSON.parse body
-      # TODO: Unprocessable entity
-      if res.statusCode is 422 then throw new Error body.message
-      if res.statusCode in [400, 401, 404] then throw new Error body.message
-      callback res.statusCode, body
+      @errorHandle res, JSON.parse(body), callback

   # Github api POST request
   post: (path, content={}, callback) ->
@@ -62,11 +64,7 @@
         'Content-Type': 'application/json'
     , (err, res, body) ->
       if err then throw err
-      body = JSON.parse body
-      # TODO: Unprocessable entity
-      if res.statusCode is 422 then throw new Error body.message
-      if res.statusCode in [400, 401, 404] then throw new Error body.message
-      callback res.statusCode, body
+      @errorHandle res, JSON.parse(body), callback

   # Github api PUT request
   put: (path, content={}, callback) ->
@@ -78,11 +76,7 @@
         'Content-Type': 'application/json'
     , (err, res, body) ->
       if err then throw err
-      body = JSON.parse body
-      # TODO: Unprocessable entity
-      if res.statusCode is 422 then throw new Error body.message
-      if res.statusCode in [400, 401, 404] then throw new Error body.message
-      callback res.statusCode, body
+      @errorHandle res, JSON.parse(body), callback

   # Github api DELETE request
   del: (path, content={}, callback) ->
@@ -94,11 +88,7 @@
         'Content-Type': 'application/json'
     , (err, res, body) ->
       if err then throw err
-      body = JSON.parse body
-      # TODO: Unprocessable entity
-      if res.statusCode is 422 then throw new Error body.message
-      if res.statusCode in [400, 401, 404] then throw new Error body.message
-      callback res.statusCode, body
+      @errorHandle res, JSON.parse(body), callback

 # Export modules
 module.exports = Client


Fix bug in script

Fix bug in script

--- script/get.coffee
+++ script/get.coffee
@@ -27,6 +27,7 @@
 require(argv.w).init(manager) if argv.w?

 unpack_fields = (array, fields) ->
+  return array unless array
   if _.isArray array[0]
     unpack_fields(elem, fields) for elem in array
   else


remove debug

remove debug

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -7,8 +7,6 @@
       $(el).addClass 'bot'

 botAdder = (arg1, msg) ->
-  console.log "botifying}"
-  console.log msg
   botify($('[data-ts="' + msg.ts + '"]'))

 botifyMessages = =>


Failed login now shows message.

Failed login now shows message.

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -103,9 +103,12 @@

   pwgAjax.on401 ->
-    $scope.loggedInUser = null
-    $scope.redirectToSegment "login"
-    $scope.flashAfterRouteChange = "Session timeout. Please log in again."
+    if $scope.loggedInUser
+      $scope.loggedInUser = null
+      $scope.redirectToSegment "login"
+      $scope.flashAfterRouteChange = "Session timeout. Please log in again."
+    else
+      pwgFlash.error "Login failure."

   $scope.templateURL = (path) ->
     window.angularTemplateURL(path)
@@ -269,6 +272,7 @@
       $scope.redirectToSegment 'search'

     handleFailure = (data) ->
+      console.log data
       # Nothing to do.
       return


BF-111 fixed scaling for 3D-cube canvas

BF-111 fixed scaling for 3D-cube canvas

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -263,12 +263,15 @@
     @rendererxy.setSize( WIDTH, HEIGHT )
     @rendereryz.setSize( WIDTH, HEIGHT )
     @rendererxz.setSize( WIDTH, HEIGHT )
+    @rendererPrev.setSize( WIDTH, HEIGHT)
     @cameraxy.aspect  = WIDTH / HEIGHT
     @cameraxy.updateProjectionMatrix()
     @camerayz.aspect  = WIDTH / HEIGHT
     @camerayz.updateProjectionMatrix()
     @cameraxz.aspect  = WIDTH / HEIGHT
     @cameraxz.updateProjectionMatrix()
+    @cameraPrev.aspect = WIDTH / HEIGHT
+    @cameraPrev.updateProjectionMatrix()
     @draw()

 ############################################################################


inverted controls

inverted controls

--- app/assets/javascripts/view.coffee
+++ app/assets/javascripts/view.coffee
@@ -239,11 +239,11 @@
 			cam.move [0,0,-moveValueStrafe]

 		#Rotate up
+		if keyboard.isKeyDown(KEY_DOWN)
+			cam.pitch moveValueRotate
+
+		#Rotate down
 		if keyboard.isKeyDown(KEY_UP)
-			cam.pitch moveValueRotate
-
-		#Rotate down
-		if keyboard.isKeyDown(KEY_DOWN)
 			cam.pitch -moveValueRotate

 		#Rotate right


Use jqmData() instead of data() method

Use jqmData() instead of data() method

--- src/opac/search_result.coffee
+++ src/opac/search_result.coffee
@@ -154,7 +154,7 @@
 		maxTab = 0

 		@plugin('search_settings')
-		.data 'settings',
+		.jqmData 'settings',
 			default_class: 'keyword'
 			term: ''
 			item_type: ''
@@ -171,7 +171,7 @@
 		# Define a function to try searching the public catalogue given a request object.
 		trySearching = (request, direction) ->

-			#request = $.extend {}, $('.search_settings').data('settings'), request
+			#request = $.extend {}, $('.search_settings').jqmData('settings'), request
 			#FIXME: the following object is empty and overrides default settings.
 			#{
 			#depth:    current_depth
@@ -184,8 +184,8 @@
 			# and proceed only if they differ.
 			# Our method of comparison is to first stringify the objects into JSON format
 			# and then check if the text strings are equal.
-			return if @length and JSON.stringify(request) is JSON.stringify(@data 'request')
-			@data 'request', request
+			return if @length and JSON.stringify(request) is JSON.stringify(@jqmData 'request')
+			@jqmData 'request', request

 			$this = @html(content)

@@ -194,7 +194,7 @@

 				# Upon success or not,
 				# we will cache the result object and publish it to other plugins.
-				@data 'result', result
+				@jqmData 'result', result
 				@publish 'opac.result', [result]

 				# If there are no results,
@@ -301,8 +301,8 @@
 		# informaiton to show details of the title and to prepare for a
 		# possible request to create a hold of the title.
 		@on 'click', 'li`', (e) =>
-			request = @data 'request'
-			result = @data 'result'
+			request = @jqmData 'request'
+			result = @jqmData 'result'
 			[id, $img, posn] = title_details $(e.currentTarget)
 			if id and request
 				# >FIXME: could the main js file load the required modules?
@@ -317,8 +317,8 @@

 		# Upon the plugin receiving an ID (and a possible direction) on *title*
 		@subscribe 'opac.title', (title_id, direction) ->
-			request = @data 'request'
-			result = @data 'result'
+			request = @jqmData 'request'
+			result = @jqmData 'result'
 			total =  result.count
 			actual = result.ids.length
 			offset = Number request.offset
@@ -356,7 +356,7 @@
 		# we will extend the recent request with an author search term at zero page offset.
 		# We will publish it and try searching the public catalogue with it.
 		@on 'click', 'a.author', (e) =>
-			request = @data 'request'
+			request = @jqmData 'request'
 			author = $('div.author', $(e.currentTarget).closest('li')).text()

 			if author and request
@@ -388,7 +388,7 @@

 			# * Otherwise, extend the current request object with a new scope
 			# and publish it on *search*
-			if request = @data 'request'
+			if request = @jqmData 'request'
 				request = $.extend {}, request,
 					org_unit: ou.id
 					org_name: ou.name