Fix up the @choose dsl

Fix up the @choose dsl

--- library/schema/domain.coffee
+++ library/schema/domain.coffee
@@ -118,14 +118,15 @@

 # Create a reference to the JSON enumeration. An enumeration defines
 # what values are valid for this data type. Everything else beyond
-# those values are considered invalid. This version takes an array
-# of values as array object, which is usefull for creating enums in
-# a programatic fashion. Makes sure all values are plain strings.
-module.exports.choose = choose = (description, values) ->
+# those values are considered invalid. This version may take array
+# of values as array object, which is useful for creating enums in
+# a programatic fashion as well as collect the trailing arguments.
+# Makes sure all values are plain strings anyway. Errors out if not.
+module.exports.choose = choose = (description, values...) ->
     noValues = "all values must be primitive strings"
     noDescription = "no description has been given"
-    noArray = "values argument should be an array"
-    assert _.isArray(values), noArray
+    values = head if _.isArray head = _.head values
+    assert _.isArray(values), "values are not array"
     assert _.isString(description), noDescription
     assert _.all(values, _.isString), noValues
     @description = description.toString()


stop old observers on autorun

stop old observers on autorun

--- lib/reactive-publish.coffee
+++ lib/reactive-publish.coffee
@@ -63,12 +63,17 @@
 Meteor.reactivePublish = (name, f) ->
 	Meteor.publish name, (args...) ->
 		oldRecords = {}
-		depends = []
+		handles = []
 		isPublishing = false

 		handle = Deps.autorun =>
 			newRecords = {}

+			for cHandle in handles
+				cHandle.stop()
+
+			handles = []
+
 			addCursor = (cursor) =>
 				if cursor
 					collectionName = cursor._cursorDescription.collectionName
@@ -80,7 +85,7 @@
 					newRecords[collectionName] = record
 					oldRecord = oldRecords[collectionName] ? {ids: {}}

-					cursor.observeChanges
+					handles.push cursor.observeChanges
 						added: (id, fields) =>
 							record.ids[id] = true
 							if id of oldRecord.ids


Change semantics of node() a little to allow for string path or multiple arguments

--- lib/layervault/organization.coffee
+++ lib/layervault/organization.coffee
@@ -2,9 +2,13 @@
   constructor: (org, @api) ->
     @orgName = if typeof org is "object" then org.name else org

-  node: (path, cb) ->
+  node: (path..., cb) ->
     @api.get @apiPath(path), {}, cb

   apiPath: (path) ->
-    path = path.path || path.name if typeof path is "object"
-    "/#{encodeURIComponent(@orgName)}/#{path}"+    compiledPath = []
+    for p in path
+      p = p.path || p.name if typeof p is "object"
+      compiledPath.push p
+
+    "/#{encodeURIComponent(@orgName)}/#{compiledPath.join('/')}"


[Fixes #27824499] eden precious -- json input for later, precious

--- lib/ephemeris.coffee
+++ lib/ephemeris.coffee
@@ -157,8 +157,11 @@
   run: (stream) ->
     # However, Ephemeris can't always be `@run` nor it is necessarily desirable.
     if @settings.precious is false
+      # Note: this isn't fit for rerun as `@settings` are being changed...
       if _.isString(@settings.out) and @settings.out isnt "json"
         @settings.out = ["json", @settings.out]
+      # Clean-up the `@settings` for later use.
+      delete @settings[key] for key in ['root', 'data', 'precious']
       if _.isArray @settings.out
         massage = new Massage @settings.out
         massage.write   JSON.stringify(@settings), stream


Remove comment about suspected bug (which is actually a js feature)

Semantics of 'this' makes abstraction over positionForCharacterIndex method impossible without a .bind(textBuffer).

--- lib/list-edit.coffee
+++ lib/list-edit.coffee
@@ -75,13 +75,6 @@

     listElements = TextManipulation.getListElements bufferText, textBuffer.characterIndexForPosition cursorPos

-    # Atom bug?
-    # fn = textBuffer.positionForCharacterIndex
-    # console.log (textBuffer.positionForCharacterIndex)
-    # console.log (fn)
-    # console.log (textBuffer.positionForCharacterIndex 2)
-    # console.log (fn 2 ) #fails
-
     bufferRanges = _.map listElements, (elt) ->
       TextManipulation.getRangeForIxRange textBuffer, [elt.eltStart, elt.eltEnd]


more mongo log fixes

more mongo log fixes

--- lib/mongo-log.coffee
+++ lib/mongo-log.coffee
@@ -14,8 +14,11 @@
       when mongo.KillCursorCommand  then 'kill_cursor'
       when mongo.QueryCommand       then 'query'
       when mongo.UpdateCommand      then 'update'
+      else command

   mongo.Connection.prototype.write = (db_command, callback) ->
+    return unless db_command
+
     output = collectionName: db_command.collectionName
     for k in [ 'query', 'documents', 'spec', 'document', 'selector', \
                'returnFieldSelector', 'numberToSkip', 'numberToReturn' ]


Make Cancel the secondary button option

Make Cancel the secondary button option

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -412,10 +412,10 @@
           "Move to Trash": ->
             for selectedPath in selectedPaths
               shell.moveItemToTrash(selectedPath)
-          "Cancel": null
           "Delete": =>
             for selectedPath in selectedPaths
               @removeSync(selectedPath)
+          "Cancel": null

   removeSync: (pathToRemove) ->
     try


Set callNow to false when observing config keypaths

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -42,16 +42,21 @@
       else
         @selectActiveFile()

-    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', => @selectActiveFile()
+    @subscribe atom.workspaceView, 'pane-container:active-pane-item-changed', =>
+      @selectActiveFile()
     @subscribe atom.project, 'path-changed', => @updateRoot()
-    @observeConfig 'tree-view.hideVcsIgnoredFiles', => @updateRoot()
-    @observeConfig 'tree-view.hideIgnoredNames', => @updateRoot()
-    @observeConfig 'core.ignoredNames', =>
+    @observeConfig 'tree-view.hideVcsIgnoredFiles', callNow: false, =>
+      @updateRoot()
+    @observeConfig 'tree-view.hideIgnoredNames', callNow: false, =>
+      @updateRoot()
+    @observeConfig 'core.ignoredNames', callNow: false, =>
       @updateRoot() if atom.config.get('tree-view.hideIgnoredNames')

-    if @root
+    @updateRoot()
+    if @root?
       @selectEntry(@root)
       @root.deserializeEntryExpansionStates(state.directoryExpansionStates)
+
     @selectEntryForPath(state.selectedPath) if state.selectedPath
     @focusAfterAttach = state.hasFocus
     @scrollTopAfterAttach = state.scrollTop if state.scrollTop


Comment out disturbing bug

Comment out disturbing bug

Need fix in later

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -238,7 +238,12 @@
     @editorElement.addEventListener('mouseup', checkSelection)
     @subscriptions.add new Disposable =>
       @editorElement.removeEventListener('mouseup', checkSelection)
-    @subscriptions.add atom.commands.onWillDispatch(preserveCharacterwise)
+
+    # [FIXME]
+    # Hover position get wired when focus-change between more than two pane.
+    # commenting out is far better than introducing Buggy behavior.
+    # @subscriptions.add atom.commands.onWillDispatch(preserveCharacterwise)
+
     @subscriptions.add atom.commands.onDidDispatch(checkSelection)

   resetNormalMode: ({userInvocation}={}) ->


docs(fix): fix a couple broken links

docs(fix): fix a couple broken links

--- docs/src/client/scripts/constants/build-types-constant.coffee
+++ docs/src/client/scripts/constants/build-types-constant.coffee
@@ -69,7 +69,7 @@
 		info: 'run when developing'
 		tasks: [
 			label: 'Run default build.'
-			info: 'see default build'
+			info: 'see default build'
 		,
 			label: 'Fireup the file watchers.'
 			info: 'on saving a file, the browser will refresh'
@@ -119,7 +119,7 @@
 		info: 'see your prod build in the browser'
 		tasks: [
 			label: 'Run prod build.'
-			info: 'see prod build'
+			info: 'see prod build'
 		,
 			label: 'Start the server.'
 		,
@@ -145,7 +145,7 @@
 		,
 			label: 'Copy server test scripts to dist/server/.'
 		,
-			label: 'Run client tests in
+			label: 'Run server tests in
 					jasmine.'
 		]
 		example:


Remove unused operation tests

Remove unused operation tests

--- test/model/operation.spec.coffee
+++ test/model/operation.spec.coffee
@@ -1,6 +0,0 @@
-helpers = require('../helpers')
-AWS = helpers.AWS
-
-describe 'AWS.Model.Operation', ->
-  describe 'basic attributes', ->
-    it


Fix failing dispatcher tests.

Fix failing dispatcher tests.

--- test/spec/dispatcher_spec.coffee
+++ test/spec/dispatcher_spec.coffee
@@ -305,14 +305,15 @@
       mediator.subscribe '!router:changeURL', spy

       path = 'my-little-path'
-      options = create {path}
-      mediator.publish 'router:match', route1, params, options
+      routeA = create route1, {path}
+      options = {}
+      mediator.publish 'router:match', routeA, params, options

       loadTest1Controller ->
         expect(spy).was.calledOnce()
         args = spy.firstCall.args
         expect(args[0]).to.be path
-        expect(args[1]).to.eql create(options, addedOptions)
+        expect(args[1]).to.eql addedOptions

         mediator.unsubscribe '!router:changeURL', spy

@@ -338,16 +339,17 @@
       spy = sinon.spy()
       mediator.subscribe '!router:changeURL', spy

-      options =
-        path: 'my-little-path', queryString: '?foo=bar',
-        controller: 'test1', action: 'show'
-      mediator.publish 'router:match', route1, params, options
+      path = 'my-little-path'
+      query = 'foo=bar'
+
+      routeB = create route1, {path, query}
+      mediator.publish 'router:match', routeB, params, options

       loadTest1Controller ->
         expect(spy).was.calledOnce()
         args = spy.firstCall.args
-        expect(args[0]).to.be "#{options.path}?#{options.queryString}"
-        expect(args[1]).to.eql  create(options, addedOptions)
+        expect(args[0]).to.be "#{path}?#{query}"
+        expect(args[1]).to.eql addedOptions

         mediator.unsubscribe '!router:changeURL', spy


chore(test/uploader-demo-jquery.coffee): fix indent

--- test/uploader-demo-jquery.coffee
+++ test/uploader-demo-jquery.coffee
@@ -1,89 +1,89 @@
 $ ->
-  errorHandler = (event, id, fileName, reason, xhr) ->
-    qq.log "id: #{id}, fileName: #{fileName}, reason: #{reason}"
+    errorHandler = (event, id, fileName, reason, xhr) ->
+        qq.log "id: #{id}, fileName: #{fileName}, reason: #{reason}"

-  $('#basicUploadSuccessExample').fineUploader(
-    debug: true
-    request:
-      endpoint: "/upload/receiver"
-      paramsInBody: true
-    chunking:
-      enabled: true
-    resume:
-      enabled: true
-    retry:
-      enableAuto: true
-      showButton: true
-    deleteFile:
-      enabled: true
-      endpoint: '/upload/receiver'
-      forceConfirm: true
-      params:
-        foo: "bar"
-    display:
-      fileSizeOnSubmit: true
-    paste:
-      targetElement: $(document)
-  )
-    .on('error', errorHandler)
-    .on "upload", (event, id, filename) ->
-      $(this).fineUploader 'setParams', {"hey": "ho"}, id
+    $('#basicUploadSuccessExample').fineUploader(
+        debug: true
+        request:
+            endpoint: "/upload/receiver"
+            paramsInBody: true
+        chunking:
+            enabled: true
+        resume:
+            enabled: true
+        retry:
+            enableAuto: true
+            showButton: true
+        deleteFile:
+            enabled: true
+            endpoint: '/upload/receiver'
+            forceConfirm: true
+            params:
+                foo: "bar"
+        display:
+            fileSizeOnSubmit: true
+        paste:
+            targetElement: $(document)
+    )
+        .on('error', errorHandler)
+        .on "upload", (event, id, filename) ->
+            $(this).fineUploader 'setParams', {"hey": "ho"}, id

-  $('#manualUploadModeExample').fineUploader(
-    autoUpload: false
-    debug: true
-    uploadButtonText: "Select Files"
-    request:
-      endpoint: "/upload/receiver"
-    display:
-      fileSizeOnSubmit: true
-  )
-    .on 'error', errorHandler
+    $('#manualUploadModeExample').fineUploader(
+        autoUpload: false
+        debug: true
+        uploadButtonText: "Select Files"
+        request:
+            endpoint: "/upload/receiver"
+        display:
+            fileSizeOnSubmit: true
+    )
+        .on 'error', errorHandler

-  $('#triggerUpload').click ->
-    $('#manualUploadModeExample').fineUploader "uploadStoredFiles"
+    $('#triggerUpload').click ->
+        $('#manualUploadModeExample').fineUploader "uploadStoredFiles"

-  $('#basicUploadFailureExample').fineUploader(
-    request:
-      endpoint: "/upload/receiver"
-      params: "generateError": "true"
-    debug: true
-    failedUploadTextDisplay:
-      mode: 'custom'
-      maxChars: 5
-    retry:
-      enableAuto: true
-      showButton: true
-  )
-    .on 'error', errorHandler
+    $('#basicUploadFailureExample').fineUploader(
+        request:
+            endpoint: "/upload/receiver"
+            params: "generateError": "true"
+        debug: true
+        failedUploadTextDisplay:
+            mode: 'custom'
+            maxChars: 5
+        retry:
+            enableAuto: true
+            showButton: true
+    )
+        .on 'error', errorHandler

-  $('#uploadWithVariousOptionsExample').fineUploader(
-    multiple: false
-    request:
-      endpoint: "/upload/receiver"
-    debug: true
-    validation:
-      allowedExtensions: ['jpeg', 'jpg', 'txt']
-      sizeLimit: 50000
-      minSizeLimit: 2000
-    text:
-      uploadButton: "Click Or Drop"
-    display:
-      fileSizeOnSubmit: true
-  )
-    .on 'error', errorHandler
+    $('#uploadWithVariousOptionsExample').fineUploader(
+        multiple: false
+        request:
+            endpoint: "/upload/receiver"
+        debug: true
+        validation:
+            allowedExtensions: ['jpeg', 'jpg', 'txt']
+            sizeLimit: 50000
+            minSizeLimit: 2000
+        text:
+            uploadButton: "Click Or Drop"
+        display:
+            fileSizeOnSubmit: true
+    )
+        .on 'error', errorHandler

-  $('#fubExample').fineUploader(
-    uploaderType: 'basic'
-    multiple: false
-    debug: true
-    autoUpload: false
-    button: $("#fubUploadButton")
-    request:
-      endpoint: "/upload/receiver"
-  )
-    .on 'error', errorHandler
+    $('#fubExample').fineUploader(
+        uploaderType: 'basic'
+        multiple: false
+        debug: true
+        autoUpload: false
+        button: $("#fubUploadButton")
+        request:
+            endpoint: "/upload/receiver"
+    )
+        .on 'error', errorHandler


Social: createSocialApiId readded

Social: createSocialApiId readded

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -430,6 +430,21 @@
     @notifyOriginWhen 'PrivateMessageSent', 'FollowHappened'
     @notifyGroupWhen 'FollowHappened'

+  createSocialApiId:(callback)->
+    return callback null, @socialApiId  if @socialApiId
+    {createAccount} = require './socialapi/requests'
+    createAccount @getId(), (err, account)=>
+      return callback err if err
+      return callback {message: "Account is not set, malformed response from social api"} unless account?.id
+      @update $set: socialApiId: account.id, (err)->
+        # check for error
+        if err
+          console.error "Error while creating account on social api", err
+          return callback { message: "Couldnt create/register Account"}
+        # check account
+        # return account id from social api
+        return callback null, account.id
+
   checkGroupMembership: secure (client, groupName, callback)->
     {delegate} = client.connection
     JGroup = require "./group"


Set position of all newly created assets to ruler right

There is an argument that non-timeseries assets shouldn't get this
treatment, but it doesn't seem like the worst default.  Fixes #2949

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -146,8 +146,7 @@
       true

     $scope.selectAll = (event, c) ->
-      range = c.segment
-      ruler.selection = range
+      ruler.selection = range = c.segment
       editExcerpt()
       if range && isFinite(range.l) && !range.contains(ruler.position)
         seekOffset(range.l)
@@ -172,7 +171,7 @@
           new Segment(endPos, startPos)
         else if startPos = endPos
           new Segment(startPos)
-      editExcerpt() if event.type != 'mousemove'
+      editExcerpt() if up.type != 'mousemove'
       return

     removed = (track) ->
@@ -199,6 +198,11 @@
         updateRange(@segment = new Segment(asset.segment))
         select(this) if `asset.id == target.asset`
         return
+
+      fillData: ->
+        super()
+        if !@asset
+          @data.position = if ruler.range.uBounded then ruler.range.u else 0

       Object.defineProperty @prototype, 'id',
         get: -> @asset?.id


Added missing `>` to default template

Added missing `>` to default template

--- src/bread_crumbs_component.coffee
+++ src/bread_crumbs_component.coffee
@@ -1,6 +1,6 @@
 defaultTemplate = """
 {{#each crumb in breadCrumbs}}
-<li {{bind-attr class="crumb.isCurrent:current:"}}
+<li {{bind-attr class="crumb.isCurrent:current:"}}>
   {{#if crumb.linkable}}
     {{#link-to crumb.path}}
       {{crumb.name}}


Fix removing caches for entier repository

It seems that sometimes repository_id is not present on cache object.
I'll investigate that later, but we have a repo object on the caches
controller anyway, so I'll just use it instead.

--- app/controllers/caches.coffee
+++ app/controllers/caches.coffee
@@ -17,7 +17,7 @@
         deletingDone = => @set('isDeleting', false)

         repo = @get('repo')
-        Ajax.ajax("/repos/#{@get('cache.repository_id')}/caches", "DELETE").then(deletingDone, deletingDone).then =>
+        Ajax.ajax("/repos/#{@get('repo.repository_id')}/caches", "DELETE").then(deletingDone, deletingDone).then =>
           @set('model', {})

 `export default Controller`


Enabled some extra markdown features.

Enabled some extra markdown features.

--- app/controllers/editor.coffee
+++ app/controllers/editor.coffee
@@ -64,6 +64,10 @@
             return hljs.highlightAuto(code).value
         catch error
           code
+      tables: true
+      sanitize: true
+      smartLists: true
+      smartypants: true
     }
     @controls = $("#editorcontrols")
     @controls.find('#bold').click @formatBold


feat(funnel): add issues names to logs

feat(funnel): add issues names to logs

--- scripts/gitter-activity-notifier.coffee
+++ scripts/gitter-activity-notifier.coffee
@@ -164,16 +164,15 @@
                 if !error and response.statusCode == 200
                     fbResponse = JSON.parse(body)
                     branchName = fbResponse.head.ref
-                    user = fbResponse.head.user.login
+                    user = fbResponse.user.login
                     demourl = 'http://fgpv.cloudapp.net/demo/users/' + user + '/' + branchName + '/samples/index-one.html'
-                    console.log demourl
+                    console.log 'Demo url', demourl, m[2], m[2].indexOf('/fgpv-vpgf')

                     # demo urls are only constructed for fgpv-vpgf repos
-                    if v.indexOf('/fgpv-vpgf') != -1
+                    if m[2].indexOf('/fgpv-vpgf') != -1
                         m.push demourl
                 else
                     console.log 'Got an error: ', error, ', status code: ', response.statusCode
-                return

                 store[roomid].prDelay[m[4]] = m
                 console.log(m)
@@ -249,18 +248,18 @@
         for commentid, parts of store[roomid].comment
             console.log(commentid, parts)
             parts[0] = andify parts[0]
-            messages.push(commentimg + vsprintf('%1$s commented on issue %2$s#%4$s', parts))
+            messages.push(commentimg + vsprintf('`%1$s` commented on the "__%3$s__" issue: %2$s#%4$s', parts))

         # construct comment messages
         for issueid, parts of store[roomid].issue
             console.log(issueid, parts)
             parts[1] = andify parts[1]
-            messages.push(infoimg + vsprintf('%1$s %2$s an issue: %3$s#%5$s', parts))
+            messages.push(infoimg + vsprintf('`%1$s` %2$s the "__%4$s__" issue: %3$s#%5$s', parts))

         for prid, parts of store[roomid].pr
             console.log(prid, parts)
             parts[1] = andify parts[1]
-            messages.push(primg + vsprintf('%1$s %2$s a Pull Request: %3$s#%5$s; [Reviewable %5$s](https://reviewable.io/reviews/%3$s/%5$s)', parts))
+            messages.push(primg + vsprintf('`%1$s` %2$s the "__%4$s__" Pull Request: %3$s#%5$s; [Reviewable %5$s](https://reviewable.io/reviews/%3$s/%5$s)', parts))

         for travisid, parts of store[roomid].travis
             console.log(travisid, parts)
@@ -268,7 +267,7 @@
             prDelayParts = store[roomid].prDelay[travisid]
             if prDelayParts
                 parts.push(prDelayParts[0])
-                messages.push(primg + vsprintf('%1$s %2$s a Pull Request: %3$s#%5$s; [Reviewable %5$s](https://reviewable.io/reviews/%3$s/%5$s)', prDelayParts))
+                messages.push(primg + vsprintf('`%1$s` %2$s the "__%4$s__" Pull Request: %3$s#%5$s; [Reviewable %5$s](https://reviewable.io/reviews/%3$s/%5$s); [Demo](%6$s)', prDelayParts))
                 store[roomid].prDelay[travisid] = undefined # null delayed pr message
             else
                 parts.push('Someone')


Fix larger button.

Fix larger button.

--- src/js/directives/mapping-box/index.coffee
+++ src/js/directives/mapping-box/index.coffee
@@ -42,7 +42,7 @@
                     mappingBox.scaleY -= 0.1
                     stage.update()

-            scope.large = () ->
+            scope.larger = () ->
                 if mappingBox
                     mappingBox.scaleX += 0.1
                     mappingBox.scaleY += 0.1


Move pinned comment above compose box

Move pinned comment above compose box

--- resources/assets/coffee/react/_components/comments.coffee
+++ resources/assets/coffee/react/_components/comments.coffee
@@ -46,16 +46,16 @@
           osu.trans('comments.title')
           span className: 'comments__count', osu.formatNumber(uiState.comments.total)

+        if pinnedComments.length > 0
+          div className: "comments__items comments__items--pinned",
+            @renderComments pinnedComments, true
+
         div className: 'comments__new',
           el CommentEditor,
             commentableType: @props.commentableType
             commentableId: @props.commentableId
             focus: false
             modifiers: @props.modifiers
-
-        if pinnedComments.length > 0
-          div className: "comments__items comments__items--pinned",
-            @renderComments pinnedComments, true

         div className: 'comments__items comments__items--toolbar',
           el CommentsSort,


Add bulk method from range tracker.

Add bulk method from range tracker.

--- services/document-updater/app/coffee/RangesTracker.coffee
+++ services/document-updater/app/coffee/RangesTracker.coffee
@@ -100,6 +100,21 @@
 			change = @getChange(change_id)
 			return if !change?
 			@_removeChange(change)
+
+		removeChangeIds: (change_to_remove_ids) ->
+			return if !change_to_remove_ids?.length > 0
+			i = @changes.length
+			remove_change_id = {}
+			for change_id in change_to_remove_ids
+				remove_change_id[change_id] = true
+
+			while (i--)
+				if remove_change_id[@changes[i].id]
+					delete remove_change_id[@changes[i].id]
+					removed_change = @changes.splice(i, 1)[0]
+					@_markAsDirty removed_change, "change", "removed"
+					if Object.keys(remove_change_id).length == 0
+						break

 		validate: (text) ->
 			for change in @changes


Atualiza eventos do Minicart

Atualiza eventos do Minicart

--- src/coffee/portal-minicart.coffee
+++ src/coffee/portal-minicart.coffee
@@ -33,16 +33,18 @@
 	bindEvents: =>
 		@hoverContext.on 'mouseover', ->
 			$(window).trigger "minicartMouseOver"
+			@context.trigger 'vtex.cart.mouseOver'

 		@hoverContext.on 'mouseout', ->
 			$(window).trigger "minicartMouseOut"
+			@context.trigger 'vtex.cart.mouseOut'

-		$(window).on "minicartMouseOver", =>
+		$(window).on "vtex.minicart.mouseOver", =>
 			if @cartData.items?.length > 0
 				$(".vtexsc-cart").slideDown()
 				clearTimeout @timeoutToHide

-		$(window).on "minicartMouseOut", =>
+		$(window).on "vtex.minicart.mouseOut", =>
 			clearTimeout @timeoutToHide
 			@timeoutToHide = setTimeout ->
 				$(".vtexsc-cart").stop(true, true).slideUp()
@@ -65,8 +67,8 @@
 			else
 				@updateCart()

-		$(window).on 'productAddedToCart', =>
-			@updateCart()
+		$(window).on 'productAddedToCart', @updateCart
+		$(window).on 'vtex.cart.productAdded', @updateCart

 	updateCart: =>
 		@context.addClass 'amount-items-in-cart-loading'
@@ -81,6 +83,7 @@
 		})
 		.done =>
 			@context.removeClass 'amount-items-in-cart-loading'
+			@context.trigger 'vtex.minicart.updated'
 		.success (data) =>
 			@cartData = data
 			@prepareCart()
@@ -132,7 +135,10 @@
 			contentType: "application/json; charset=utf-8"
 			type: "POST"
 		})
+		.done =>
+			@context.trigger 'vtex.minicart.updated'
 		.success (data) =>
+			@context.trigger 'vtex.cart.productRemoved'
 			@cartData = data
 			@prepareCart()
 			@render()


Bump the version to 1.0

Bump the version to 1.0

--- src/coffeescripts/toast.js.coffee
+++ src/coffeescripts/toast.js.coffee
@@ -8,7 +8,7 @@
 OPTIONS = ['duration', 'content', 'id', 'className', 'animate', 'allowHTML']

 class window.Toast
-  @VERSION: '0.1'
+  @VERSION: '1.0'

   constructor: (options = {}) ->
     @tc = new TransitionCallbacks(clearAll: true, timeout: 500)


Tests:activity:others: Disabled search activity test.

--- tests/src/activity/others.coffee
+++ tests/src/activity/others.coffee
@@ -8,19 +8,19 @@
 module.exports =

-  searchActivity: (browser) ->
+  # searchActivity: (browser) ->

-    post     = helpers.postActivity(browser)
-    selector = '[testpath=activity-list] [testpath=ActivityListItemView]:first-child'
-    word     = post.split(' ')[0]
+  #   post     = helpers.postActivity(browser)
+  #   selector = '[testpath=activity-list] [testpath=ActivityListItemView]:first-child'
+  #   word     = post.split(' ')[0]

-    browser
-      .setValue                 '.kdtabhandlecontainer .search-input', word + '\n'
-      .waitForElementVisible    '.kdtabpaneview.search', 25000
-      .waitForElementVisible    selector, 25000
-      .pause                    25000
-      .assert.containsText      selector, word # Assertion
-      .end()
+  #   browser
+  #     .setValue                 '.kdtabhandlecontainer .search-input', word + '\n'
+  #     .waitForElementVisible    '.kdtabpaneview.search', 25000
+  #     .waitForElementVisible    selector, 25000
+  #     .pause                    25000
+  #     .assert.containsText      selector, word # Assertion
+  #     .end()

   showMoreCommentLink: (browser) ->


more tests for sampledBy

more tests for sampledBy

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -1077,6 +1077,17 @@
         combined.onValue(->)
         src.sampledBy(combined, add)
       ["aa", "bb", "cc"])
+  it "skips samplings that occur before the property gets its first value", ->
+    expectStreamEvents(
+      ->
+        p = series(5, [1]).toProperty()
+        p.sampledBy(series(3, [0]))
+      [])
+    expectStreamEvents(
+      ->
+        p = series(5, [1, 2]).toProperty()
+        p.sampledBy(series(3, [0, 0, 0, 0]))
+      [1, 1, 2])

 describe "Property.sampledBy(property)", ->
   it "samples property at events, resulting to a Property", ->


Give runtimes even more time to load components over the slow Sauce Connect tunnel

--- spec/EditGraph.coffee
+++ spec/EditGraph.coffee
@@ -43,15 +43,18 @@
       chai.expect(search).to.be.an 'object'
       chai.expect(search.classList.contains('overlay')).to.equal true
     it 'when clicked it should show the search input', (done) ->
+      @timeout 10000
       breadcrumb = search.shadowRoot.querySelector '#breadcrumb'
       chai.expect(breadcrumb).to.be.an 'object'
-      Syn.click breadcrumb
       setTimeout ->
-        chai.expect(search.classList.contains('overlay')).to.equal false
-        done()
-      , 500
+        Syn.click breadcrumb
+        setTimeout ->
+          chai.expect(search.classList.contains('overlay')).to.equal false
+          done()
+        , 500
+      , 5000
     it 'should initially show results', (done) ->
-      @timeout 10000
+      @timeout 20000
       if search.results.length
         chai.expect(search.results.length).to.be.above 10
         done()
@@ -60,7 +63,7 @@
       setTimeout ->
         chai.expect(search.results.length).to.be.above 10
         done()
-       , 8000
+       , 10000
     it 'should narrow them down when something is written', (done) ->
       @timeout 10000
       input = search.shadowRoot.querySelector '#search'


Fix spec

Fix spec

--- spec/tags-spec.coffee
+++ spec/tags-spec.coffee
@@ -9,10 +9,10 @@
     atom.project.setPaths([path])

     pkg = atom.packages.loadPackage('ide-haskell-hasktags')
-    pkg.activateNow()
+    try pkg.activateNow()

     waitsFor 'tags', ->
-      tags = pkg.mainModule.tags
+      tags = pkg?.mainModule?.tags

     waitsFor ->
       not tags.inProgress
@@ -22,16 +22,20 @@
       expect(tags.listTags()).toContain
         tag : 'main'
         uri : "#{path}test1.hs"
+        context : 'main '
         line : 2
       expect(tags.listTags()).not.toContain
         tag : 'main'
         uri : "#{path}test1.hs"
+        context : 'main '
         line : 3
       expect(tags.listTags()).toContain
         tag : 'main'
         uri : "#{path}test2.hs"
+        context : 'main '
         line : 2
       expect(tags.listTags()).toContain
         tag : 'main'
         uri : "#{path}test2.hs"
+        context : 'main '
         line : 21


Use more native conditional

Use more native conditional

Per @cavvia, this commit favors a nicer Coffee syntax.

--- models/search_result.coffee
+++ models/search_result.coffee
@@ -50,8 +50,8 @@
       else if @get('model') is 'partnershow'
         'show'
       else if @get('model') is 'profile'
-        isInstitution = _.contains(['PartnerInstitution', 'PartnerInstitutionalSeller'], @get('owner_type'))
-        if isInstitution then 'institution' else 'gallery'
+        institutionTypes = ['PartnerInstitution', 'PartnerInstitutionalSeller']
+        if @get('owner_type') in institutionTypes then 'institution' else 'gallery'
       else @get('model')

     _s.capitalize model


Minor style change

Minor style change

--- src/Bottleneck.coffee
+++ src/Bottleneck.coffee
@@ -295,7 +295,7 @@

   wrap: (fn) ->
     schedule = @schedule
-    wrapped = (args...) -> schedule fn.bind(this), args...
+    wrapped = (args...) -> schedule fn.bind(@), args...
     wrapped.withOptions = (options, args...) => schedule options, fn, args...
     wrapped


coffeescript lint - unnecessary double arrow

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -398,7 +398,7 @@

       # Clear and replace the hash fragment after the content has loaded
       # to force the browser window to find the intended content (as a side effect)
-      jumpToHash = () =>
+      jumpToHash = () ->
         if currentPage.get('loaded') and window.location.hash
           linksHelper.offsetHash()


Simplify code

Simplify code

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -17,7 +17,7 @@

       content: () ->
         if @model.isBook()
-          return @model.get('currentPage').get('content')
+          return @model.get('currentPage.content')

         return @model.get('content')


remove cache for permission manager

remove cache for permission manager

--- src/server/permission_manager/index.coffee
+++ src/server/permission_manager/index.coffee
@@ -2,11 +2,12 @@
 CacheManager           = require('./cache_manager')
 SystemPermissions      = require('./system_permissions')

-class UserPermissionManager extends CacheManager
+# the permission might be changed by other workers.
+# caching is disabled
+class UserPermissionManager
     collectionName = "Permissions"

     constructor : (@mongoInterface, callback) ->
-        super(SystemPermissions)
         @dbOperation('addIndex', null, {_email:1},
             (err) =>
                 callback(err,this)
@@ -34,19 +35,14 @@
                 callback(null, null)
             else callback(null, sysPermRec)

-        # If entry is in cache, use cache entry
-        sysPermRec = @find(user)
-        if sysPermRec then filterOnPermission(sysPermRec)
-
-        # Else, hit the DB
-        else Async.waterfall [
+        Async.waterfall [
             (next) =>
                 @dbOperation('findUser', user, null, next)
             (dbRecord, next) =>
                 if not dbRecord then next(null, null)
                 else
                     # Add to cache
-                    sysPermRec = @add(user, dbRecord.permission)
+                    sysPermRec = new SystemPermissions(user, dbRecord.permission)
                     for mountPoint, app of dbRecord.apps
                         appPerm = sysPermRec.addItem(mountPoint, app.permission)
                         if app.appInstances
@@ -70,7 +66,7 @@
                 if not sysPermRec
                     @dbOperation 'addUser', user, null, (err) =>
                         # Add to cache
-                        next(err, @add(user))
+                        next(err, new SystemPermissions(user))
                 else next(null, sysPermRec)
             (sysPermRec, next) =>
                 @setSysPerm
@@ -92,7 +88,7 @@
                 if sysPermRec
                     @dbOperation 'removeUser', user, null, (err) ->
                         # Remove from cache
-                        next(err, @remove(user))
+                        next(err, null)
                 else next(null, null)
         ], callback

@@ -124,7 +120,6 @@

     addAppPermRec : (options) ->
         {user, mountPoint, permission, callback} = options
-
         setPerm = (callback) =>
             @setAppPerm
                 user        : user


markdown editors rename

markdown editors rename

--- src/smc-webapp/markdown-editor/editor.cjsx
+++ src/smc-webapp/markdown-editor/editor.cjsx
@@ -15,14 +15,14 @@
 EDITOR_SPEC =
     cm        :
         short     : 'Code'
-        name      : 'Code'
+        name      : 'Source Code'
         icon      : 'code'
         component : CodemirrorEditor
         buttons   : set(['print', 'decrease_font_size', 'increase_font_size', 'save', 'time_travel', 'replace', 'find', 'goto_line', \
                          'cut', 'paste', 'copy', 'undo', 'redo', 'reload'])
     markdown :
         short     : 'View'
-        name      : 'View'
+        name      : 'Rendered View'
         icon      : 'eye'
         component : RenderedMarkdown
         buttons   : set(['print', 'decrease_font_size', 'increase_font_size', 'save', 'time_travel', 'reload'])


fixed ui helper bug

fixed ui helper bug

--- public/dreamcatcher/controllers/application_controller.coffee
+++ public/dreamcatcher/controllers/application_controller.coffee
@@ -114,10 +114,10 @@
     parentEl = @element if not parentEl?
     $('textarea', parentEl).each (i, el) ->
       fitToContent $(this).attr('id'), 0
+    $('.select-menu', parentEl).each (i, el) =>
+      UiHelper.registerSelectMenu $(el)
     $('.tooltip', parentEl).each (i, el) =>
       UiHelper.registerTooltip $(el)
-    $('.select-menu', parentEl).each (i, el) =>
-      UiHelper.registerSelectMenu $(el)

   #- appearance (bedsheet, scroll  & theme) change


no scroll frame for eigen on fair browse (closes #2596)

--- apps/fair/client/index.coffee
+++ apps/fair/client/index.coffee
@@ -71,7 +71,8 @@
       profile: profile

     # Links in the browse section keep your scroll position
-    scrollFrame '#fair-browse a'
+    # unless the browser is Eigen
+    scrollFrame('#fair-browse a') unless sd.EIGEN

   # Checks to see if the href is an internal link to:
   # an artwork, artist, show, or post


meh... pend

meh... pend

--- apps/shows/test/routes.coffee
+++ apps/shows/test/routes.coffee
@@ -27,7 +27,7 @@

     it 'fetches the cities & featured shows and renders the index template', ->
       routes.index {}, @res
-        .then =>
+        .then =>
           Backbone.sync.args[2][1].id.should.equal '530ebe92139b21efd6000071'
           Backbone.sync.args[2][1].item_type.should.equal 'PartnerShow'
           Backbone.sync.args[2][2].url.should.containEql 'api/v1/set/530ebe92139b21efd6000071/items'
@@ -40,7 +40,7 @@

   describe '#city', ->
     beforeEach ->
-      @req =
+      @req =
         params: { city: 'new-york-ny-usa' }
         query: { page: 1 }
       @res = { render: sinon.stub() }
@@ -55,7 +55,7 @@
         .then =>
           @next.called.should.be.true()

-    it 'fetches the cities & shows and renders the city template', ->
+    xit 'fetches the cities & shows and renders the city template', ->
       @upcomingShow = new PartnerShow fabricate('show', start_at: moment().add(5, 'days').format(), end_at: moment().add(15, 'days').format())
       @openingShow = new PartnerShow fabricate('show', start_at: moment().add(1, 'days').format(), end_at: moment().add(10, 'days').format())
       @currentShow = new PartnerShow fabricate('show', start_at: moment().subtract(5, 'days').format(), end_at: moment().add(5, 'days').format())


don tneed to track is completed in ui settings for intro panel viewing

--- tutor/src/flux/task-panel.coffee
+++ tutor/src/flux/task-panel.coffee
@@ -12,7 +12,6 @@
   placement:
     taskId: ''
     stepId: ''
-  is_completed: false

 # old key, keep backwards compatibility
 TWO_STEP_VIEWED_KEY = 'has-viewed-two-step-help'
@@ -28,7 +27,7 @@

   panels = StepPanel.getPanelsWithStatus(stepId)

-  _.extend({panels}, _.pick(step, 'id', 'type', 'is_completed'))
+  _.extend({panels}, _.pick(step, 'id', 'type'))

 makeUiSettings = (initial) ->
   deepMerge({}, ONE_TIME_CARD_DEFAULTS, initial)
@@ -44,7 +43,6 @@
 stepMapOneTimeCard = (condition, type, settingKey, task, step, stepIndex) ->
   if hasBeenPlaced(settingKey)
     if isPlacedHere(settingKey, step)
-      console.info(settingKey)
       makeStep(task, {type}, stepIndex)
   else if condition(task, step, stepIndex)
     placement =


Form not submitting after error fixed

Form not submitting after error fixed

--- src/javascripts/contact_us.coffee
+++ src/javascripts/contact_us.coffee
@@ -7,23 +7,28 @@
     $form = $("#contact-us-form-template").clone()
     $form.removeAttr("id")

-    # Post the form w/ Ajax on submit
-    $form.submit (event) ->
-      event.preventDefault()
-      $form.find("input[type=submit]").val("Skickar meddelande ...").attr("disabled", "disabled")
-      $.ajax
-        type: "POST"
-        url: $trigger.attr('data-action')
-        data: $form.serialize() + "&contactid=#{$trigger.attr('data-contact-id')}"
-        success: (data) ->
-          $form.replaceWith(data)
-        error: (x, y, z) ->
-          # Server error or timeout, nothing to do
-          # FIXME: Uncomment the real error message
-          $form.after('
Ett fel inträffade, vänligen försök senare eller skicka ditt meddelande till nedanstående e-postadress.
')
-
     # Replace the trigger w/ the form
     $trigger.replaceWith($form.show())
+
+    $(document).on "click", ".write-to-us-form input[type=submit]", ->
+      $form = $(@).closest("form")
+      $form.submit (event) ->
+        event.preventDefault()
+        $form.find("input[type=submit]").val("Skickar meddelande ...").attr("disabled", "disabled")
+        $.ajax
+          type: "POST"
+          url: $trigger.attr('data-action')
+          data: $form.serialize() + "&contactid=#{$trigger.attr('data-contact-id')}"
+          success: (data) ->
+            console.log("success")
+            console.log($form)
+            $form.replaceWith(data)
+            $('.contact-us').find('form:first').show()
+          error: (x, y, z) ->
+            # Server error or timeout, nothing to do
+            # FIXME: Uncomment the real error message
+            console.log("error")
+            $form.after('
Ett fel inträffade, vänligen försök senare eller skicka ditt meddelande till nedanstående e-postadress.
')

     # Scroll to top of form
     $('html, body').animate


only prefetch selected

only prefetch selected

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -33,6 +33,7 @@

     next_index:    0
     reset:   (world) -> world.call 'fetch_program', 'run'
+
     reset_index:   (world) -> world.put 'next_index', 0
     advance_index: (world) ->
       next_index = world.get 'next_index'
@@ -55,6 +56,7 @@
       world.call 'perform', action.get 'value' if action

     prefetch: (world) ->
+      return unless world.get 'selected'
       console.log 'prefetch', "#{sprite} #{sprite.get 'running'} #{world.get('find_action')} -> #{sprite.get('interrupt')}"
       if interrupt = sprite.get('interrupt')
         key = world.call 'find_interrupt', interrupt


clean up index/action methods

clean up index/action methods

--- src/layout/mixins/programs.coffee
+++ src/layout/mixins/programs.coffee
@@ -32,36 +32,48 @@
     selected: (world) -> world.label() == sprite.get 'running'
     editable: (world) -> world.label() == sprite.get 'editing'

+    # Index
+
     next_index:    0
-    next_action:   (world) ->
-      actions = world.find_child('actions').find_children()
-      actions[world.get('next_index')]
     reset_index:   (world) -> world.put 'next_index', 0
     advance_index: (world) ->
       next_index = world.get 'next_index'
       world.put 'next_index', next_index + 1

+    # Action
+
+    find_action:   (world) ->
+      actions = world.find_child('actions').find_children()
+      actions[world.get('next_index')]
+    next_action: (world) ->
+      action = world.call 'find_action'
+      world.call 'advance_index'
+      action
+
     fetch: (world, args) ->
       return unless world.get 'selected'
-      action = null
-
-      if interrupt = sprite.get('interrupt')
-        key = world.call 'find_interrupt', interrupt
-        action = world.call 'fetch_program', key
-      if !action && action = world.get 'next_action'
-        world.call 'advance_index'
-      action ?= world.call 'fetch_program', 'first'
+      action = world.call 'fetch_action'

       world.call 'perform', action.get 'value' if action

+    fetch_action: (world) ->
+      if interrupt = sprite.get('interrupt')
+        key = world.call 'find_interrupt', interrupt
+        sprite.put 'interrupt', null
+        return world.call('fetch_program', key)
+      if action = world.get 'next_action'
+        return action
+
+      world.call 'fetch_program', 'run'
+
     fetch_program: (world, key) ->
-      world.up.find_child(sprite.get 'running').call 'reset_index'
       sprite.put 'running', key
       p = world.up.find_child(key)
       p.call 'reset_index'
       p.call 'next_action'

     find_interrupt: (world, key) ->
+      console.log 'find_interrupt', key
       # TODO: put real logic here
       'interrupt'


Fixes #1261

Fixes #1261

--- admin/static/coffee/sidebar.coffee
+++ admin/static/coffee/sidebar.coffee
@@ -110,6 +110,11 @@
                 _m = machines.get(m.get('id'))
                 if _m and _m.get('datacenter_uuid') and _m.get('datacenter_uuid') isnt universe_datacenter.get('id')
                     dc_visible.push _m.get('datacenter_uuid')
+            # Also add empty datacenters to visible -- we define them
+            # as reachable.
+            datacenters.each (dc) =>
+                if DataUtils.get_datacenter_machines(dc.get('id')).length is 0
+                    dc_visible.push dc.get('id')
             dc_visible = _.uniq(dc_visible)
             conn =
                 datacenters_active: dc_visible.length
@@ -167,7 +172,7 @@
             # Get a list of all other issues (non-critical)
             other_issues = issues.filter (issue) -> not issue.get('critical')
             other_issues = _.groupBy other_issues, (issue) -> issue.get('type')
-
+

             reduced_issues = _.map(critical_issues, (issues, type) ->
                 json = {}
@@ -178,7 +183,7 @@
             if reduced_issues.length > 0
                 reduced_issues[0].is_first = true

-
+
             reduced_other_issues = _.map(other_issues, (issues, type) ->
                 json = {}
                 json[type] = true


remove unnecessary try

remove unnecessary try

--- src/background.coffee
+++ src/background.coffee
@@ -38,7 +38,7 @@
 #
 launch_unique_popout = (request, sender) ->
   if popouts[name request]
-    try chrome.windows.remove popouts[name request], -> launch_popout(request, sender)
+    chrome.windows.remove popouts[name request], -> launch_popout(request, sender)
   else
     launch_popout(request, sender)


Cancel ticker timeout when pausing

Cancel ticker timeout when pausing

--- app/assets/javascripts/live.coffee
+++ app/assets/javascripts/live.coffee
@@ -22,6 +22,7 @@
   ticker_remaining: 0
   ticker_elapsed: 0
   ticker_over: 0
+  ticker_timeout: 0
   current_cue: $()
   live_indicator: $()
 # ------------
@@ -50,6 +51,7 @@
   else
     ppbutton.removeClass('play')
     ppbutton.html('play_circle_filled')
+    clearTimeout(host.ticker_timeout)
 # ------------

 # --- Go-To functions. Jump to next/prev/custom cue ---
@@ -81,7 +83,8 @@
 schedule_tick = (tick_f) ->
   actual = (new Date().getTime()) - host.ticker_start
   # Next tick in 1 second +/- correction of any accumulated error
-  setTimeout tick_f, 1000 - (actual - host.ticker_elapsed)
+  host.ticker_timeout =
+    setTimeout tick_f, 1000 - (actual - host.ticker_elapsed)

 tick = -> # Process tick
   if host.play
@@ -107,8 +110,6 @@
   set_play off
   actual = (new Date().getTime()) - host.ticker_start
   host.ticker_over = actual - host.ticker_elapsed
-  # TODO: Fix: If quickly paused and then resumed again, the old ticker "thread"
-  # stays alive. Kill it when pausing!
 # ------------

 # --- Load. Executed when the page is loaded (via Tubolinks or otherwise) ---


demonstrate map cell shadinig in maps.coffee

--- app/assets/javascripts/maps.coffee
+++ app/assets/javascripts/maps.coffee
@@ -8,6 +8,14 @@
 	// alert("Yes I exist")

 	$('#big-map').mapster({
+		areas: [
+			{
+				key: 'Province36',
+				fillColor: '00ff00',
+				staticState: true,
+				stroke: true
+			},
+		],
 		mapKey: 'prov-key',
 		singleSelect: true,
 		onClick: function(e){
@@ -40,8 +48,6 @@
     });

-	//on select of area, retrieve its info (json?)
-	//on mouseover, if none selected, retrieve its info
 	//on tab select, reveal info

 	//some kind of control bar and masking - multiselect and cannot deselect perhaps?


AutoComplete: do not auto populate if the input value is same

--- src/components/autocomplete/autocompletecontroller.coffee
+++ src/components/autocomplete/autocompletecontroller.coffee
@@ -342,6 +342,8 @@

     return @hideDropdown() if value is ''

+    return if @active and value is @dropdownPrefix
+
     @dropdownPrefix = value
     @showFetching()


Add key for collections role iterator

Add key for collections role iterator

--- app/collections/collaborators.cjsx
+++ app/collections/collaborators.cjsx
@@ -217,7 +217,7 @@

               {for roleSet in roleSets
                 continue if owner.id is roleSet.links.owner.id
-                <RoleRow roleSet={roleSet} onRemove={@handleCollaboratorChange} />}
+                <RoleRow key={roleSet.id} roleSet={roleSet} onRemove={@handleCollaboratorChange} />}

}


Rewrite URLs in subject metadata as links (#4769)

Rewrite URL values in subject metadata as markdown links that open in a new tab.

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -295,6 +295,8 @@

           {for key, value of @props.subject?.metadata when key.charAt(0) not in @props.metadataFilters and key[...2] isnt '//'
+            if (value.startsWith('http'))
+              value = "[#{value}](+tab+#{value})"

               {key.replace(///^(#{@props.metadataPrefixes.join('|')})///, '')}
               <Markdown tag="td" content={value} inline />


Add a custom scrubber

Add a custom scrubber

--- app/components/subject-viewer.cjsx
+++ app/components/subject-viewer.cjsx
@@ -47,9 +47,17 @@
     loading: true
     playing: false
     frame: @props.frame ? 0
+    time: 0

   componentDidMount: ->
-    @refs.videoPlayer?.addEventListener 'canplaythrough', @handleLoad
+    player = @refs.videoPlayer?.getDOMNode()
+    player?.addEventListener 'canplaythrough', @handleLoad
+    player?.addEventListener 'ended', @endVideo
+    player?.addEventListener 'timeupdate', @updateScrubber
+
+  componentDidUpdate: ->
+    player = @refs.videoPlayer?.getDOMNode()
+    player?.currentTime = @state.time

   render: ->
     {type, format, src} = getSubjectLocation @props.subject, @state.frame
@@ -78,15 +86,19 @@
               </button>}

       when 'video'
-
-          {if @state.playing
-            <button type="button" className="secret-button" aria-label="Pause" onClick={@playVideo.bind this, false}>
-
-            </button>
-          else
-            <button type="button" className="secret-button" aria-label="Play" onClick={@playVideo.bind this, true}>
-
-            </button>}
+
+
+            {if @state.playing
+              <button type="button" className="secret-button" aria-label="Pause" onClick={@playVideo.bind this, false}>
+
+              </button>
+            else
+              <button type="button" className="secret-button" aria-label="Play" onClick={@playVideo.bind this, true}>
+
+              </button>}
+
+          <progress ref="videoScrubber" value="0" min="0" value={@state.time} style={width: '80%'} onClick={@seekVideo}>
+          </progress>

@@ -102,7 +114,7 @@

-        {tools}
+        {tools}
         {if @props.subject?.locations.length >= 2

@@ -185,6 +197,27 @@
       player.play()
     else
       player.pause()
+      time = player.currentTime
+      @setState {time}
+
+  seekVideo: (e) ->
+    player = @refs.videoPlayer?.getDOMNode()
+    scrubber = @refs.videoScrubber?.getDOMNode()
+    pos = (e.pageX  - (scrubber.offsetLeft + scrubber.offsetParent.offsetLeft)) / scrubber.offsetWidth
+    time = pos * player.duration
+    @setState {time}
+
+  endVideo: (e) ->
+    player = @refs.videoPlayer?.getDOMNode()
+    playing = false
+    time = 0
+    @setState {playing, time}
+
+  updateScrubber: (e) ->
+    player = @refs.videoPlayer?.getDOMNode()
+    scrubber = @refs.videoScrubber?.getDOMNode()
+    scrubber.setAttribute 'max', player.duration unless scrubber.getAttribute 'max'
+    scrubber.value = player.currentTime

   handleLoad: (e) ->
     @setState loading: false


Only validate offline for now.

Only validate offline for now.

--- src/coffee/run.coffee
+++ src/coffee/run.coffee
@@ -22,7 +22,7 @@
     console.error "Problems on reading file '#{argv.csv}': " + err
     process.exit 2
   validator.parse content, (data, count) ->
-    errors = validator.validate data
+    errors = validator.validateOffline data
     process.exit 0 if validator.errors.length is 0
     console.log validator.errors
     process.exit 1


Add extra height to bottom of document

Add extra height to bottom of document

--- src/controller.coffee
+++ src/controller.coffee
@@ -3606,7 +3606,7 @@
       bounds.unite @view.getViewNodeFor(record.block).getBounds()

     @mainScrollerStuffing.style.width = "#{bounds.right()}px"
-    @mainScrollerStuffing.style.height = "#{bounds.bottom()}px"
+    @mainScrollerStuffing.style.height = "#{bounds.bottom() + (@options.extraBottomHeight ? @fontSize)}px"

   hook 'redraw_palette', 0, ->
     bounds = new @draw.NoRectangle()


Fix rememberedSocket indexing error when floating blocks are deleted

--- src/controller.coffee
+++ src/controller.coffee
@@ -1098,6 +1098,11 @@
             @session.cursor.document -= 1

           @session.floatingBlocks.splice i, 1
+
+          for socket in @session.rememberedSockets
+            if socket.socket.document > i
+              socket.socket.document -= 1
+
           break
   else if container?
     # No document, so try to remove from container if it was supplied


完善Model的getProperty和getDataType方法 完善对数据和DataType的检查 改进cola.util.update的错误信息提示

--- src/core/model.coffee
+++ src/core/model.coffee
@@ -1036,6 +1036,7 @@
 				dataType = @_rootDataType.getProperty(path1)?.get("dataType")
 			else
 				dataType = @_rootDataType
+
 			if dataType instanceof cola.EntityDataType
 				return dataType.getProperty(path2)
 			else
@@ -1316,9 +1317,10 @@
 			if path.substring(0, i) is @alias
 				dataType = if @_itemData instanceof cola.Entity or @_itemData instanceof cola.EntityList then @_itemData.dataType else null
 				dataType ?= @dataType
-				if dataType
-					property = dataType.getProperty(path.substring(i + 1))
-				return property
+				if dataType instanceof cola.EntityDataType
+					return dataType.getProperty(path.substring(i + 1))
+				else
+					return null
 			else
 				return @parent.getProperty(path)
 		else if path is @alias


add tooltips. resolve #12

add tooltips. resolve #12

--- src/renderer/components/doc-factor-table-component.coffee
+++ src/renderer/components/doc-factor-table-component.coffee
@@ -36,13 +36,13 @@
               {
                 flags = []
                 if child.flags.isPrivate
-                   flags.push
+                   flags.push
                 else if child.flags.isProtected
-                   flags.push
+                   flags.push
                 else
-                   flags.push
+                   flags.push
                 if child.flags.isStatic
-                   flags.push
+                   flags.push
                 flags
               }


Need to set the blueprint on the item.

Need to set the blueprint on the item.

--- app/controllers/create-item.coffee
+++ app/controllers/create-item.coffee
@@ -2,7 +2,11 @@

   actions:
     create: ->
-      item = @get('model').item
+      model = @get 'model'
+      item = model.item
+      blueprint = model.blueprint
+
+      item.set 'blueprint', blueprint
       item.save()

 `export default CreateItemController`


insert the container on regular refreshes and on page:load

--- app/assets/javascripts/flasher/flasher.js.coffee
+++ app/assets/javascripts/flasher/flasher.js.coffee
@@ -1,6 +1,13 @@
 jQuery ->
   $('

').prependTo 'body'
+
+  $(document).on 'page:load', ->
+    $('

').prependTo 'body'
+
   Flasher.show()
+
+  $(document).on 'ajaxComplete', (event, request) ->
+    Flasher.show()

 Flasher = (->
   hideFlash = (flash) ->
@@ -33,5 +40,4 @@

 )()

-$(document).on 'ajaxComplete', (event, request) ->
-  Flasher.show()
+


use collapse instead of modal and fix link copy

--- app/assets/javascripts/share_buttons/copy.coffee
+++ app/assets/javascripts/share_buttons/copy.coffee
@@ -2,27 +2,27 @@
   constructor: (@$button) ->
     @clipboard = new ZeroClipboard(@$button[0])
     @clipboard.on('load', => @_initializeClipboardHandlers())
-    @$modal = $(@$button.data('target'))
+    @$collapse = $(@$button.data('target'))
     @$button.on('click', => @_buttonClicked())

   _initializeClipboardHandlers: ->
     @clipboard.on('complete', => @_linkCopied())

   _buttonClicked: ->
-    if @$modal.hasClass('in') then @_openModal() else @_closeModal()
+    if @$collapse.hasClass('in') then @_openCollapse() else @_closeCollapse()

-  _closeModal: ->
-      @$modal.modal('hide')
+  _closeCollapse: ->
+      @$collapse.collapse('hide')

-  _openModal: ->
-      @$modal.modal('show')
+  _openCollapse: ->
+      @$collapse.collapse('show')

   _linkCopied: ->
-    @$modal.find('.copy-alert-text').hide(0)
+    @$collapse.find('.copy-alert-text').hide(0)

-    @$modal.one 'shown.bs.modal', =>
-      @$modal.find('.copied-alert-text').collapse('show')
+    @$collapse.one 'shown.bs.collapse', =>
+      @$collapse.find('.copied-alert-text').collapse('show')

-    @_openModal()
+    @_openCollapse()

 ShareButtons.register('copy', ShareButtons.CopyButton)


Remove old code

Remove old code

--- app/assets/javascripts/stores/block_store.coffee
+++ app/assets/javascripts/stores/block_store.coffee
@@ -75,7 +75,6 @@
   if fromWeekId != toWeek.id
     toWeekBlocks = BlockStore.getBlocksInWeek(toWeek.id)
     toWeekBlocks.forEach (b, i) ->
-      if b == block
       b.order = i
       setBlock b, true
   BlockStore.emitChange()


scroll to top before opening post action panels

--- app/assets/javascripts/storytime/posts.js.coffee
+++ app/assets/javascripts/storytime/posts.js.coffee
@@ -29,8 +29,9 @@
       $(".scroll-panel-header").fadeOut
         duration: 'fast'
         easing: 'swing'
+      $(".scroll-panel-body").scrollTop(0)
       $(".scroll-panel-body").animate({top: "0"}, 'fast', 'swing')
-
+
     $(".post-action-panel").on "shown.bs.collapse", ->
       $(".chosen-select").trigger("chosen:updated")


minor refactoring of transfer controller

--- app/js/controllers/transfer.coffee
+++ app/js/controllers/transfer.coffee
@@ -1,14 +1,14 @@
 angular.module("app").controller "TransferController", ($scope, $stateParams, $modal, $q, Wallet, WalletAPI, Blockchain, Utils, Info, Growl) ->
     Info.refresh_info()
     $scope.utils = Utils
-    $scope.account_from_name = account_from_name = $stateParams.from
-    account = $scope.$parent?.$parent?.acount
+    $scope.balances = []
     $scope.show_from_section = true
-    if $scope.$parent.$parent?.account_name
+    if $scope.account_name
         $scope.show_from_section = false
-        $scope.account_from_name = account_from_name = $scope.$parent.$parent?.account_name
+        $scope.account_from_name = account_from_name = $scope.account_name
+    else
+        $scope.account_from_name = account_from_name = $stateParams.from
     $scope.gravatar_account_name = null
-    $scope.balances = Wallet.balances[$scope.account_from_name]
     $scope.transfer_info = { memo: '', symbol: Info.symbol}
     $scope.memo_size_max = 19
     $scope.addr_symbol = null
@@ -18,10 +18,12 @@
         Wallet.get_current_or_first_account().then (account)->
             $scope.account_from_name = account_from_name = account.name
             Wallet.refresh_account(account_from_name).then (acct)->
-                $scope.transfer_info.symbol=Object.keys(Wallet.balances[account_from_name])[0]
+                $scope.balances = Wallet.balances[account_from_name]
+                $scope.transfer_info.symbol = Object.keys($scope.balances)[0]
     else
         Wallet.refresh_account(account_from_name).then (acct)->
-            $scope.transfer_info.symbol=Object.keys(Wallet.balances[account_from_name])[0]
+            $scope.balances = Wallet.balances[account_from_name]
+            $scope.transfer_info.symbol = Object.keys($scope.balances)[0]

     Blockchain.get_config().then (config) ->
         $scope.memo_size_max = config.memo_size_max


show current speed from cordova geolocation

--- app/js/features/track/track.coffee
+++ app/js/features/track/track.coffee
@@ -1,4 +1,4 @@
-angular.module("logbuch").factory "Track", (StorageService, Log) ->
+angular.module("logbuch").factory "Track", (StorageService, Log, DebugLog) ->
   class Track
     @attributes = ['lat', 'long', 'waypoints', 'distanceKm', 'distanceNm', 'speedKmh', 'speedKn']

@@ -18,6 +18,9 @@

     @convertKmtoNm = (km) ->
       parseFloat(km) * 0.539956803456
+
+    @convertMsToKmh = (speed) ->
+      (speed > 0 && speed * 3.6) || 0

     @fromStorage = ->
       stored = StorageService.getObject('currentTrack')
@@ -58,10 +61,13 @@
     updateCurrentPosition: (position) ->
       @lat      = position.latLng.lat
       @long     = position.latLng.lng
+      @speedKmh = Track.convertMsToKmh(position.coords.speed)
+      @speedKn  = Track.convertKmtoNm(@speedKmh)
+      new DebugLog("Track Speed: #{position.coords.speed}", "km/h: #{@speedKmh}").save()

       @calculateTotalDistance()
       @calculateCurrentDistance()
-      @calculateCurrentSpeed()
+      #@calculateCurrentSpeed()

       true


abort support

abort support

--- web/scripts/referenceUI.coffee
+++ web/scripts/referenceUI.coffee
@@ -111,6 +111,7 @@
     closeClick = () ->
         $(backFade).remove()
         $(refContainer).remove()
+        codeland.doppioAPI.abort()
     clClick = () ->
         $(this).unbind()

@@ -172,7 +173,7 @@
             msg = ''
             finished_cb = ->
                 jQuery('#abortCode').hide(500 , -> jQuery('#runCode').show())
-            codeland.doppioAPI.run sandBoxEditor.getStudentCode() finished_cb
+            codeland.doppioAPI.run(sandBoxEditor.getStudentCode(),'', finished_cb)

             e.preventDefault()
             return


Anonymous user id now displayed in user info tooltip

--- src/renderer/scripts/views/main/comment/collectionView.coffee
+++ src/renderer/scripts/views/main/comment/collectionView.coffee
@@ -68,8 +68,16 @@
                 @$el.animate {scrollTop: @el.scrollHeight}, 10

             if comment.isPostByAnonymous()
+                $user = $ """
+

+
#{comment.get("user.id")}

+

+                """
+
                 $comment.find(".NcoComments_item_thumbnail")
                     .attr("src", "./images/anonymous_user.png")
+                    .data("powertipjq", $user)
+                    .powerTip({placement: "e", mouseOnToPopup: true})

                 return

@@ -77,7 +85,7 @@
             .then (user) ->
                 $user = $(tooltipTemplate({user}))

-                $tip = $comment.find(".NcoComments_item_thumbnail")
+                $comment.find(".NcoComments_item_thumbnail")
                     .attr("src", user.get("thumbnailURL"))
                     .data("powertipjq", $user)
                     .powerTip({placement: "e", mouseOnToPopup: true})


moved definitions to be inside the fixtures

--- test/specs/integration/migrate_test.coffee
+++ test/specs/integration/migrate_test.coffee
@@ -14,7 +14,6 @@
 SailsIntegration = rek("lib/sails-migrations/sails_integration.coffee")
 ourAdapter = rek("lib/sails-migrations/adapter.coffee")
 migrationsPath = path.resolve('test/example_app/db/migrations')
-definitionsPath = path.resolve('test/example_app/db/migrations/definitions')

 cleanupMigrationFiles = (migrationsPath)->
   rmdirp.sync(migrationsPath)
@@ -26,12 +25,10 @@
   )

 copyFixturesToMigrationsPath = (scope)->
-  mkdirp.sync(definitionsPath) #This should create the db/migrations + db/migrations/definitions dirs in the example_app
+  mkdirp.sync(migrationsPath) #This should create the db/migrations + db/migrations/definitions dirs in the example_app
   fixturePath = path.resolve('test/specs/fixtures/migrations')
   migrationFixtures = glob.sync("#{fixturePath}/*#{scope}*.js", {})
-  definitionFixtures = glob.sync("#{fixturePath}/definitions/*#{scope}*.js", {})
   copy(migrationFixtures, migrationsPath)
-  copy(definitionFixtures, definitionsPath)

 describe 'db:migrate', ->
   beforeEach (done)->
@@ -57,28 +54,24 @@

   it 'should be able to run a migration', (done)->
     tableName = 'one_migration'
-    migrationDefinitionPath = path.resolve("test/specs/fixtures/migrations/definitions/one_migration.js")
-    migrationDefinition = require(migrationDefinitionPath)
     copyFixturesToMigrationsPath(tableName)
     Migrator.migrate(@adapter, migrationsPath, null, (err)=>
       return done(err) if err
       @ourAdapter.describe(tableName, (err, definition)->
         return done(err) if err
-        assert.equal(_.keys(definition).length, _.keys(migrationDefinition).length)
+        assert.equal(_.keys(definition).length, 3)
         done()
       )
     )

   it 'should be able to run 2 migrations', (done)->
     tableName = 'two_migrations'
-    migrationDefinitionPath = path.resolve("test/specs/fixtures/migrations/definitions/two_migrations.js")
-    migrationDefinition = require(migrationDefinitionPath)
     copyFixturesToMigrationsPath(tableName)
     Migrator.migrate(@adapter, migrationsPath, null, (err)=>
       return done(err) if err
       @ourAdapter.describe(tableName, (err, definition)->
         return done(err) if err
-        assert.equal(_.keys(definition).length, _.keys(migrationDefinition).length+1)
+        assert.equal(_.keys(definition).length, 4)
         done()
       )
     )


Server: check apitoken availability and use new error interface

--- servers/lib/server/handlers/api/createssotoken.coffee
+++ servers/lib/server/handlers/api/createssotoken.coffee
@@ -1,8 +1,14 @@
-{ argv } = require 'optimist'
-KONFIG   = require('koding-config-manager').load("main.#{argv.c}")
+{ argv }  = require 'optimist'
+KONFIG    = require('koding-config-manager').load("main.#{argv.c}")

 { daisy }                          = require 'bongo'
 { checkAuthorizationBearerHeader } = require '../../helpers'
+{ sendApiError
+  sendApiResponse
+  checkApiTokenAvailability }      = require './helpers'
+
+apiErrors = require './errors'
+

 module.exports = createSsoToken = (req, res, next) ->

@@ -10,7 +16,7 @@

   # validating req params
   { error, token, username } = validateRequest req
-  return res.status(error.statusCode).send(error.message)  if error
+  return sendApiError res, error  if error

   account  = null
   apiToken = null
@@ -20,16 +26,21 @@
     ->
       # checking if token is valid
       JApiToken.one { code : token }, (err, apiToken_) ->
-        return res.status(500).send 'an error occurred'  if err
-        return res.status(400).send 'invalid token!'     unless apiToken_
+        return sendApiError res, apiErrors.internalError    if err
+        return sendApiError res, apiErrors.invalidApiToken  unless apiToken_
         apiToken = apiToken_
+        queue.next()
+
+    ->
+      checkApiTokenAvailability { apiToken }, (err) ->
+        return sendApiError res, err  if err
         queue.next()

     ->
       # checking if user exists
       JAccount.one { 'profile.nickname' : username }, (err, account_) ->
-        return res.status(500).send 'an error occurred'  if err
-        return res.status(400).send 'invalid username!'  unless account_
+        return sendApiError res, apiErrors.internalError    if err
+        return sendApiError res, apiErrors.invalidUsername  unless account_
         account = account_
         queue.next()

@@ -37,8 +48,8 @@
       # checking if user is a member of the group of api token
       client = { connection : { delegate : account } }
       account.checkGroupMembership client, apiToken.group, (err, isMember) ->
-        return res.status(500).send 'an error occurred'  if err
-        return res.status(400).send 'invalid request'    unless isMember
+        return sendApiError res, apiErrors.internalError    if err
+        return sendApiError res, apiErrors.notGroupMember   unless isMember
         queue.next()

     ->
@@ -53,7 +64,7 @@
       port       = if publicPort in ['80', '443'] then '' else ":#{publicPort}"

       loginUrl = "#{protocol}://#{host}#{port}/#{URL}"
-      return res.status(200).send { token, loginUrl }
+      return sendApiResponse res, { token, loginUrl }

   ]

@@ -66,19 +77,11 @@
   token        = null
   { username } = req.body

-  errors =
-    invalidRequest      :
-      statusCode        : 400
-      message           : 'invalid request'
-    unauthorizedRequest :
-      statusCode        : 401
-      message           : 'unauthorized request'
-
   unless username
-    return { error : errors.invalidRequest }
+    return { error : apiErrors.invalidUsername }

   unless token = checkAuthorizationBearerHeader req
-    return { error : errors.unauthorizedRequest }
+    return { error : apiErrors.unauthorizedRequest }

   return { error : null, token, username }


this signal-tracker never worked right

this signal-tracker never worked right

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -24,16 +24,3 @@

 # Botify the currently-viewed channel
 botifyMessages()
-
-#replaceDispatch(eventManager)
-if false
-  for key in Object.keys(TS)
-    for subkey in Object.keys(TS[key])
-      if subkey.match(/_sig$/)
-        ( ->
-          console.log "breaking dispatch on #{key}.#{subkey}"
-          foo = key
-          bar = subkey
-          unless "#{key}.#{subkey}" in whitelist
-            TS[foo][bar].dispatch = =>
-              console.log "calling #{foo}.#{bar}")()


Don't trigger peek:update unless peek is enabled

--- app/assets/javascripts/peek.coffee
+++ app/assets/javascripts/peek.coffee
@@ -5,8 +5,10 @@
 getRequestId = ->
   if requestId? then requestId else $('#peek').data('request-id')

+peekEnabled = ->
+  $('#peek').length
+
 updatePerformanceBar = (results) ->
-  peekResults = $('#peek-results')
   for key of results.data
     for label of results.data[key]
       $("[data-defer-to=#{key}-#{label}]").text results.data[key][label]
@@ -53,11 +55,15 @@
 $(document).on 'pjax:end', (event, xhr, options) ->
   if xhr?
     requestId = xhr.getResponseHeader('X-Request-Id')
-  $(this).trigger 'peek:update'
+
+  if peekEnabled()
+    $(this).trigger 'peek:update'

 # Also listen to turbolinks page change event
 $(document).on 'page:change', ->
-  $(this).trigger 'peek:update'
+  if peekEnabled()
+    $(this).trigger 'peek:update'

 $ ->
-  $(this).trigger 'peek:update'
+  if peekEnabled()
+    $(this).trigger 'peek:update'


Hide subscribe menu button for subscribers

--- app/views/play/CampaignView.coffee
+++ app/views/play/CampaignView.coffee
@@ -1125,7 +1125,8 @@

   shouldShow: (what) ->
     isStudent = me.get('role') in ['student']
-
+    isIOS = me.get('iosIdentifierForVendor') || application.isIPadApp
+
     if features.codePlay and what in ['clans', 'settings']
       return false

@@ -1156,8 +1157,11 @@
     if what in ['back-to-classroom']
       return isStudent

-    if what in ['buy-gems', 'premium']
-      return not (me.get('iosIdentifierForVendor') || application.isIPadApp) && !me.freeOnly() && !isStudent
+    if what in ['buy-gems']
+      return not (isIOS or me.freeOnly() or isStudent)
+
+    if what in ['premium']
+      return not (me.isPremium() or isIOS or me.freeOnly() or isStudent)

     return true


Fix priority fee for transfers

Fix priority fee for transfers

--- app/js/controllers/transfer.coffee
+++ app/js/controllers/transfer.coffee
@@ -73,7 +73,7 @@
         my_transfer_form.amount.error_message = null
         my_transfer_form.payto.error_message = null
         Blockchain.get_asset(0).then (v)->
-            priority_fee = Utils.formatAsset(Utils.asset(Info.info.priority_fee, v))
+            priority_fee = Utils.formatAsset(Utils.asset(Wallet.info.priority_fee.amount, v))
             $modal.open
                 templateUrl: "dialog-confirmation.html"
                 controller: "DialogConfirmationController"


Update Simulator to new, simple way of making UserCodeMap

--- app/lib/simulator/Simulator.coffee
+++ app/lib/simulator/Simulator.coffee
@@ -114,7 +114,6 @@
     catch error
       console.log "Failed to form task results:", error
       return @cleanupAndSimulateAnotherTask()
-    console.log 'Processing results:', taskResults
     humanSessionRank = taskResults.sessions[0].metrics.rank
     ogreSessionRank = taskResults.sessions[1].metrics.rank
     if @options.headlessClient and @options.simulateOnlyOneGame
@@ -377,78 +376,24 @@
       return 1

   generateSpellsObject: ->
-    @currentUserCodeMap = @task.generateSpellKeyToSourceMap()
-    @spells = {}
-    for thang in @level.attributes.thangs
-      continue if @thangIsATemplate thang
-      @generateSpellKeyToSourceMapPropertiesFromThang thang
-    @spells
-
-  thangIsATemplate: (thang) ->
-    for component in thang.components
-      continue unless @componentHasProgrammableMethods component
-      for methodName, method of component.config.programmableMethods
-        return true if @methodBelongsToTemplateThang method
-
-    return false
-
-  componentHasProgrammableMethods: (component) -> component.config? and _.has component.config, 'programmableMethods'
-
-  methodBelongsToTemplateThang: (method) -> typeof method is 'string'
-
-  generateSpellKeyToSourceMapPropertiesFromThang: (thang) =>
-    for component in thang.components
-      continue unless @componentHasProgrammableMethods component
-      for methodName, method of component.config.programmableMethods
-        spellKey = @generateSpellKeyFromThangIDAndMethodName thang.id, methodName
-
-        @createSpellAndAssignName spellKey, methodName
-        @createSpellThang thang, method, spellKey
-        @transpileSpell thang, spellKey, methodName
-
-  generateSpellKeyFromThangIDAndMethodName: (thang, methodName) ->
-    spellKeyComponents = [thang, methodName]
-    spellKeyComponents[0] = _.string.slugify spellKeyComponents[0]
-    spellKey = spellKeyComponents.join '/'
-    spellKey
-
-  createSpellAndAssignName: (spellKey, spellName) ->
-    @spells[spellKey] ?= {}
-    @spells[spellKey].name = spellName
-
-  createSpellThang: (thang, method, spellKey) ->
-    @spells[spellKey].thangs ?= {}
-    @spells[spellKey].thangs[thang.id] ?= {}
-    spellTeam = @task.getSpellKeyToTeamMap()[spellKey]
-    playerTeams = @task.getPlayerTeams()
-    useProtectAPI = true
-    if spellTeam not in playerTeams
-      useProtectAPI = false
-    else
-      spellSession = _.filter(@task.getSessions(), {team: spellTeam})[0]
-      unless codeLanguage = spellSession?.submittedCodeLanguage
-        console.warn 'Session', spellSession.creatorName, spellSession.team, 'didn\'t have submittedCodeLanguage, just:', spellSession
-    @spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method, useProtectAPI, codeLanguage ? 'javascript'
-
-  transpileSpell: (thang, spellKey, methodName) ->
-    slugifiedThangID = _.string.slugify thang.id
-    generatedSpellKey = [slugifiedThangID,methodName].join '/'
-    source = @currentUserCodeMap[generatedSpellKey] ? ''
-    aether = @spells[spellKey].thangs[thang.id].aether
-    #unless _.contains(@task.spellKeysToTranspile, generatedSpellKey)
-    try
-      aether.transpile source
-    catch e
-      console.log "Couldn't transpile #{spellKey}:\n#{source}\n", e
-      aether.transpile ''
-
-  createAether: (methodName, method, useProtectAPI, codeLanguage) ->
-    aetherOptions = createAetherOptions functionName: methodName, codeLanguage: codeLanguage, skipProtectAPI: not useProtectAPI
-    return new Aether aetherOptions
+    spells = {}
+    for {hero, team} in [{hero: 'Hero Placeholder', team: 'humans'}, {hero: 'Hero Placeholder 1', team: 'ogres'}]
+      sessionInfo = _.filter(@task.getSessions(), {team: team})[0]
+      fullSpellName = _.string.slugify(hero) + '/plan'
+      submittedCodeLanguage = sessionInfo?.submittedCodeLanguage ? 'javascript'
+      submittedCode = LZString.decompressFromUTF16 sessionInfo?.submittedCode?[_.string.slugify(hero)]?.plan ? ''
+      aether = new Aether createAetherOptions functionName: 'plan', codeLanguage: submittedCodeLanguage, skipProtectAPI: false
+      try
+        aether.transpile submittedCode
+      catch e
+        console.log "Couldn't transpile #{fullSpellName}:\n#{submittedCode}\n", e
+        aether.transpile ''
+      spells[fullSpellName] = name: 'plan', team: team, thang: {thang: {id: hero}, aether: aether}
+    spells
+

 class SimulationTask
   constructor: (@rawData) ->
-    @spellKeyToTeamMap = {}

   getLevelName: ->
     levelName = @rawData.sessions?[0]?.levelID
@@ -476,30 +421,4 @@

   getSessions: -> @rawData.sessions

-  getSpellKeyToTeamMap: -> @spellKeyToTeamMap
-
-  getPlayerTeams: -> _.pluck @rawData.sessions, 'team'
-
   setWorld: (@world) ->
-
-  generateSpellKeyToSourceMap: ->
-    # TODO: we always now only have hero-placeholder/plan vs. hero-placeholder-1/plan on humans vs. ogres, always just have to retranspile for Esper, and never need to transpile for NPCs or other methods, so we can get rid of almost all of this stuff.
-    playerTeams = _.pluck @rawData.sessions, 'team'
-    spellKeyToSourceMap = {}
-    for session in @rawData.sessions
-      teamSpells = session.teamSpells[session.team]
-      allTeams = _.keys session.teamSpells
-      for team in allTeams
-        for spell in session.teamSpells[team]
-          @spellKeyToTeamMap[spell] = team
-      teamCode = {}
-
-      for thangName, thangSpells of session.submittedCode
-        for spellName, spell of thangSpells
-          fullSpellName = [thangName, spellName].join '/'
-          if _.contains(teamSpells, fullSpellName)
-            teamCode[fullSpellName] = LZString.decompressFromUTF16 spell
-
-      _.merge spellKeyToSourceMap, teamCode
-
-    spellKeyToSourceMap


Expose Owner in Researcher List

Expose Owner in Researcher List

--- app/pages/lab/project-details.cjsx
+++ app/pages/lab/project-details.cjsx
@@ -36,7 +36,7 @@

   componentWillMount: ->
     @props.project.get('project_roles', page_size: 100).then (roles) =>
-      scientists = for role in roles when 'scientist' in role.roles
+      scientists = for role in roles when 'scientist' or 'owner' in role.roles
         role.links.owner.id
       apiClient.type('users').get(scientists).then (researchers) =>
         @setState({ researchers })
@@ -44,7 +44,7 @@
     avatar = @props.project.get('avatar')
     background = @props.project.get('background')
     Promise.all([avatar, background])
-      .then ([avatar, background]) =>
+      .then ([avatar, background]) =>
         @setState {avatar, background}
       .catch (error) =>
         @setState {error}


Fixed some indentation.

Fixed some indentation.

--- app/views/editor/thang/edit.coffee
+++ app/views/editor/thang/edit.coffee
@@ -50,13 +50,13 @@
     @thangType.saveBackups = true

     @listenToOnce(@thangType, 'error', =>
-        @hideLoading()
-
-        # Hack: editor components appear after calling insertSubView.
-        # So we need to hide them first.
-        $(@$el).find('.main-content-area').children('*').not('#error-view').remove()
-
-        @insertSubView(new ErrorView())
+      @hideLoading()
+
+      # Hack: editor components appear after calling insertSubView.
+      # So we need to hide them first.
+      $(@$el).find('.main-content-area').children('*').not('#error-view').remove()
+
+      @insertSubView(new ErrorView())
     )

     thangRes = @supermodel.addModelResource(@thangType, 'thang_type')


Adding tests specifically for basic source behavior.

--- tests/batman/property/property_test.coffee
+++ tests/batman/property/property_test.coffee
@@ -12,6 +12,39 @@
 # class Batman.Object
 #   constructor: (obj) ->
 #     @[key] = val for own key, val of obj if obj
+
+QUnit.module 'Batman.Property basics'
+  setup: ->
+    @class = class extends Batman.Object
+    @object = new @class
+
+test "calling an accessor from inside an accessor adds a new source", ->
+  @class.accessor 'foo', -> @get('bar')
+  @object.get('foo')
+
+  deepEqual @object.property('foo').sources.toArray(), [@object.property('bar')]
+
+test "calling accessors with sources adds appropriate sources", ->
+  @class.accessor 'foo', -> @get('bar')
+  @class.accessor 'bar', -> @get('baz')
+  @object.get('foo')
+
+  deepEqual @object.property('foo').sources.toArray(), [@object.property('bar')]
+  deepEqual @object.property('bar').sources.toArray(), [@object.property('baz')]
+
+test "calling multiple accessors adds all properties as sources", ->
+  @class.accessor 'foo', -> @get('bar'); @get('baz')
+  @object.get('foo')
+
+  expected = [@object.property('bar'), @object.property('baz')]
+  deepEqual @object.property('foo').sources.toArray(), expected
+
+test "calling mutators from inside an accessor does not add a new source", ->
+  @class.accessor 'foo', -> @set('bar', 1234); @unset('baz')
+  @object.get('foo')
+
+  deepEqual @object.property('foo').sources.toArray(), []
+

 QUnit.module 'Batman.Property',
   setup: ->


Scrollbar support

Scrollbar support

--- web/scripts/gameSelection.coffee
+++ web/scripts/gameSelection.coffee
@@ -4,7 +4,7 @@
         $("#acelne").remove()
         tmp = document.createElement("div")
         cont = $(tmp)
-        $(tmp).css(height:'100%', overflow:'auto',position:'relative','font-size':"20" + 'px')
+        $(tmp).css({'height':'100%', 'overflow-y':'scroll','width':'95%','position':'relative','font-size':"20px"})
         $(tmp).attr("id","gameSelection")
         @div.append(tmp)
         return


Re-export OnigRegExp via a lazy property

--- src/first-mate.coffee
+++ src/first-mate.coffee
@@ -2,4 +2,8 @@
   ScopeSelector: require './scope-selector'
   GrammarRegistry: require './grammar-registry'
   Grammar: require './grammar'
-  OnigRegExp: require('oniguruma').OnigRegExp
+
+# This allows this file to be processed with `electron-link`
+Object.defineProperty(module.exports, 'OnigRegExp', {
+  get: -> require('oniguruma').OnigRegExp
+})


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -1,4 +1,3 @@
-#TODO: Work in progress
 GameState = (I={}) ->
   # Set some default properties
   Object.reverseMerge I,


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_state.coffee
+++ src/game_state.coffee
@@ -63,7 +63,7 @@

     I.updating = false

-  self.include GameState.Cameras
-  self.include GameState.SaveState
+  self.include "GameState.Cameras"
+  self.include "GameState.SaveState"

   return self


remove querystring from google avatars before adding our own

--- src/googleplus.coffee
+++ src/googleplus.coffee
@@ -20,7 +20,8 @@

     getStreamUrl: -> @streamUrl

-    getAvatar: -> "#{@avatar}?sz=48"
+    getAvatar: ->
+        @avatar.replace /(\?.*)?$/, '?sz=48'

     @from: (data) ->
         {actor, url, published, object, id} = data


get last task and the state of it

get last task and the state of it

--- SingularityUI/app/views/tail.coffee
+++ SingularityUI/app/views/tail.coffee
@@ -58,6 +58,8 @@
         $('#global-zeroclipboard-html-bridge').css 'top', '1px'

     renderLines: ->
+        [..., task] = @model.get('taskUpdates')
+        console.log(task.taskState)
         # So we want to either prepend (fetchPrevious) or append (fetchNext) the lines
         # Well, or just render them if we're starting fresh
         $firstLine = @$linesWrapper.find '.line:first-child'


Short term fix for an out of control ModelObjectMother

--- kanban/KanbanAppSpec.coffee
+++ kanban/KanbanAppSpec.coffee
@@ -73,7 +73,7 @@
         wip: 1
       'In-Progress':
         wip: 2
-
+
     @createApp({columns: Ext.JSON.encode(columnSettings), groupByField: 'ScheduleState'}).then =>

       columns = @app.down('rallycardboard').getColumns()
@@ -176,14 +176,14 @@
     @createApp(query: query).then =>
       filterInfo = @app.down('rallyfilterinfo')
       expect(filterInfo.getQuery()).toBe query
-
+
   it 'should show plan estimate when plan estimate field is enabled', ->
-    @ajax.whenQuerying('userstory').respondWithCount(1)
+    @ajax.whenQuerying('userstory').respondWithCount(1, createImmediateSubObjects: true)
     @createApp(cardFields: "Name,Discussion,Tasks,Defects,PlanEstimate").then =>
       expect(@app.getEl().down('.PlanEstimate')).not.toBeNull()

   it 'should not show plan estimate when plan estimate field is disabled', ->
-    @ajax.whenQuerying('userstory').respondWithCount(1)
+    @ajax.whenQuerying('userstory').respondWithCount(1, createImmediateSubObjects: true)
     @createApp(cardFields: "Name,Discussion,Tasks,Defects").then =>
       expect(@app.getEl().down('.PlanEstimate')).toBeNull()


Reduced strictness of mark as complete behavior

In some cases, the queue state manager is transitioning from waiting to in progress when an item is completed. This would cause the queue to get stuck on the given item and never move it to a completed state.

--- packages/emberella/lib/mixins/mq_mixin.coffee
+++ packages/emberella/lib/mixins/mq_mixin.coffee
@@ -123,7 +123,7 @@
     retry: (manager) ->
       manager.invokeQueueCallback 'willRetryQueueItem'
       manager.incrementProperty('retries')
-      manager.transitionTo('queued')
+      Ember.run -> manager.transitionTo('queued')

     didError: (manager) ->
       manager.transitionTo('error')
@@ -596,15 +596,13 @@
       Ember.warn "Item to mark as complete was not a queueable item."
       return @

-    unless get item, 'isQueueItemInProgress'
-      Ember.warn "Item to mark as complete was not active or in progress."
-      return @
-
     isPaused = get @, 'isPaused'

     markAsCompleteFn = ->
       return if get @, 'isPaused'
       @removeObserver 'isPaused', @, markAsCompleteFn
+      unless get item, 'isQueueItemInProgress'
+        Ember.warn "Item to mark as complete was not active or in progress."
       item.send 'finish'

     if isPaused


Remove obsolete, commented out tests

Remove obsolete, commented out tests

--- bokehjs/test/core/has_props.coffee
+++ bokehjs/test/core/has_props.coffee
@@ -62,34 +62,6 @@
       obj = new SubclassWithMultipleMixins()
       expect(_.keys(obj.properties)).to.be.deep.equal _.keys(_.extend mixins.line(""), mixins.text("bar_"))

-  # it "should support computed properties", ->
-  #   model = new TestObject({'a': 1, 'b': 1})
-  #   model.define_computed_property 'c', ->
-  #   @get('a') + @get('b')
-  #   model.add_dependencies('c', model, ['a', 'b'])
-
-  #   expect(model.get "c").to.equal 2
-
-  # describe "cached properties", ->
-  #   model = null
-  #   before ->
-  #     model = new TestObject({a: 1, b: 1})
-  #     model.define_computed_property 'c', ->
-  #         @get('a') + @get('b')
-  #       , true
-  #     model.add_dependencies('c', model, ['a', 'b'])
-
-  #   it "should be computed", ->
-  #     expect(model.get "c").to.equal(2)
-
-  #   it "should store computed values in cache", ->
-  #     expect(model._computed["c"].cache).to.not.be.undefined
-
-  #   it "should invalidate cached values on changes", ->
-  #     model.set('a', 10)
-
-  #     expect(model.get "c").to.equal(11)
-
   # describe "arrays of references", ->
   #   [model1, model2, model3, model4, doc] = [null, null, null, null, null]
   #   before ->


hub: in single machine mode make the host of all projects localhost

--- src/smc-hub/compute-client.coffee
+++ src/smc-hub/compute-client.coffee
@@ -1238,10 +1238,7 @@
         dbg = @dbg("open")
         dbg()
         if @_dev or @_single
-            if @_dev
-                host = 'localhost'
-            else
-                host = os.hostname()
+            host = 'localhost'
             async.series([
                 (cb) =>
                     if not @host


properly pass the network option

properly pass the network option

--- src/smc-hub/compute-server.coffee
+++ src/smc-hub/compute-server.coffee
@@ -279,6 +279,7 @@
                 args.push('--ephemeral_disk')
             if @_network
                 args.push('--network')
+                args.push(1)

         args.push(@project_id)
         dbg("args=#{misc.to_safe_str(args)}")


add a `featureToggle` property to ide.

add a `featureToggle` property to ide.

--- services/web/public/coffee/ide.coffee
+++ services/web/public/coffee/ide.coffee
@@ -124,5 +124,8 @@
 		catch err
 			console.error err

+		# User can append ?ft=somefeature to url to activate a feature toggle
+		ide.featureToggle = location?.search?.match(/^\?ft=(\w+)$/)?[1]
+

 	angular.bootstrap(document.body, ["SharelatexApp"])


Fix crash of sortieDangerousCheck

Fix crash of sortieDangerousCheck

--- views/services/sortieDangerousCheck.coffee
+++ views/services/sortieDangerousCheck.coffee
@@ -1,9 +1,12 @@
-window.addEventListener 'game.response',
+const __ = i18n.main.__.bind(i18n.main)
+
+window.addEventListener 'game.response',
   ({detail: {path, body, postBody}}) ->
     if path in ['/kcsapi/api_req_map/start', '/kcsapi/api_req_map/next']
       state = getStore()
       {sortieStatus, escapedPos} = state.sortie
       {fleets, ships, equips} = state.info
+      {$equips} = state.const
       damagedShips = []
       for deckId in [0..3]
         continue unless sortieStatus[deckId]
@@ -17,7 +20,7 @@
           safe = false
           for slotId in ship.api_slot.concat(ship.api_slot_ex || -1)
             continue if slotId == -1
-            safe = true if equips[slotId]?.api_type[3] == 14
+            safe = true if $equips[equips[slotId]?.api_id].api_type[3] == 14
           if !safe
             damagedShips.push("Lv. #{ship.api_lv} - #{ship.api_name}")
       if damagedShips.length > 0


Make ctrl-Z/ctrl-y invoke 'core:redo' instead of 'corey:redo'

--- keymaps/default.cson
+++ keymaps/default.cson
@@ -43,8 +43,8 @@
   'meta-w': 'core:close'
   'meta-ctrl-f': 'window:toggle-full-screen'
   'meta-z': 'core:undo'
-  'meta-Z': 'corey:redo'
-  'meta-y': 'corey:redo'
+  'meta-Z': 'core:redo'
+  'meta-y': 'core:redo'
   'meta-x': 'core:cut'
   'meta-c': 'core:copy'
   'meta-v': 'core:paste'


fix bugarooski

fix bugarooski

--- app/assets/javascripts/views/tabs.js.coffee
+++ app/assets/javascripts/views/tabs.js.coffee
@@ -6,7 +6,6 @@
     'click ul.tabs a': 'tabToggle'

   initialize: ->
-    $("section.tabbed").first().addClass('active')
     if window.location.hash
       s = window.location.hash.replace('#','')
       $("section.#{s}, ul.tabs a[data-section='#{s}']").addClass('active')


Tweak to Winston output handler

Tweak to Winston output handler

--- src/io/tty/log.coffee
+++ src/io/tty/log.coffee
@@ -9,4 +9,7 @@
 		json: no

 exports.log = (level, params...) ->
-	winston.log level, (params.map util.inspect)...+	if params[0] instanceof Error
+		winston.log level, params[0], params[1..]...
+
+	else winston.log level, (params.map util.inspect)...


fix bug with multi conditions on show and survey report

--- app/assets/javascripts/hanuman/conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/conditional-logic.js.coffee
@@ -107,10 +107,15 @@
   checkConditionsAndHideShow: (conditions, ancestorId, $ruleElement, $container, inRepeater, matchType) ->
     conditionMetTracker = []
     $.each conditions, (index, condition) ->
-      $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control')
       if inRepeater
         $conditionElement = $container.closest(".form-container-repeater").find("[data-question-id=" + condition.question_id + "]").find('.form-control')
-      #TODO this checkbox condition still needs to be fixed for relativity
+        if $conditionElement.length < 1
+          $conditionElement = $container.closest(".form-container-repeater").find("[data-question-id=" + condition.question_id + "]").find('.form-control-static')
+      else
+        $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control')
+        if $conditionElement.length < 1
+          $conditionElement = $("[data-question-id=" + condition.question_id + "]").find('.form-control-static')
+
       if $conditionElement.is(":checkbox")# || $triggerElement.is(":radio"))
         # limit binding of each checkbox if data-label-value and answer are the same-kdh
         $conditionElement = $conditionElement.closest('.form-container-entry-item').find(".form-control[data-label-value=" + condition.answer.replace("/","\\/") + "]")


refactor variable name and checking for hidden before running logic

--- app/assets/javascripts/hanuman/conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/conditional-logic.js.coffee
@@ -7,43 +7,42 @@
       $(rule.conditions).each ->
         bindConditions(rule.hidden, rule.question_id, this.question_id, this.operator, this.answer)

+  #todo when implementing d
   #bind conditions to question
   bindConditions = (hidden, ancestor_id, question_id, operator, answer) ->
     $("[data-question-id=" + question_id + "]").on "change", ->
-      logic_result = false
+      hide_questions = true
       i = 0
       while i < 2
         if i < 1
           switch operator
             when "is equal to"
-              if $(this).val() == answer then logic_result = true
+              if $(this).val() == answer then hide_questions = false
             when "is not equal to"
-              if $(this).val() != answer then logic_result = true
+              if $(this).val() != answer then hide_questions = false
             when "is empty"
-              if $(this).val().length < 1 then logic_result = true
+              if $(this).val().length < 1 then hide_questions = false
             when "is not empty"
-              if $(this).val().length > 0 then logic_result = true
+              if $(this).val().length > 0 then hide_questions = false
             when "is greater than"
               if $.isNumeric($(this).val())
-                if $(this).val() > answer then logic_result = true
+                if $(this).val() > answer then hide_questions = false
             when "is less than"
               if $.isNumeric($(this).val())
-                if $(this).val() < answer then logic_result = true
+                if $(this).val() < answer then hide_questions = false
             when "starts with"
-              if $(this).val().slice(0, answer.length) == answer then logic_result = true
+              if $(this).val().slice(0, answer.length) == answer then hide_questions = false
             when "contains"
-              if $(this).val().indexOf(answer) > -1 then logic_result = true
+              if $(this).val().indexOf(answer) > -1 then hide_questions = false
         else
-          hideShowQuestions(hidden, logic_result, ancestor_id)
+          hideShowQuestions(hidden, hide_questions, ancestor_id)
         i++

   #hide or show questions
-  hideShowQuestions = (hidden, logic_result, ancestor_id) ->
+  # when hide_questions is true we don't want to hide
+  hideShowQuestions = (hidden, hide_questions, ancestor_id) ->
     selection = $("[data-question-id=" + ancestor_id + "],[data-ancestor=" + ancestor_id + "]").closest(".form-entry-item-container")
-    if hidden
-      if logic_result then selection.addClass("conditional-logic-hidden") else selection.removeClass("conditional-logic-hidden")
-    else
-      if logic_result then selection.removeClass("conditional-logic-hidden") else selection.addClass("conditional-logic-hidden")
+    if hide_questions then selection.addClass("conditional-logic-hidden") else selection.removeClass("conditional-logic-hidden")

   #call findDataRules on document ready
   findDataRules()


trakttv: getEpisodeData: return errors

trakttv: getEpisodeData: return errors

--- src/js/trakttv.coffee
+++ src/js/trakttv.coffee
@@ -150,6 +150,8 @@
       result.number = response.number

       trakttv.searchEpisode episodeID, (err, response) ->
+        return callback(err) if err?
+
         result.overview = response.episode.overview
         result.images = response.episode.images

@@ -160,6 +162,8 @@
         events.emit 'update', 'episode', episode: result

         callback null, result
+    (response, status, req) ->
+      callback status

 #   {
 #     "type":"episode",


Movde errors plugin to core docpad code

Movde errors plugin to core docpad code

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -3,6 +3,7 @@

 # Necessary
 pathUtil = require('path')
+fsUtil = require('fs')
 _ = require('underscore')
 caterpillar = require('caterpillar')
 CSON = require('cson')
@@ -104,6 +105,32 @@
 	getEvents: ->
 		@events

+	errors:
+		'400': 'Bad Request'
+		'401': 'Unauthorized'
+		'402': 'Payment Required'
+		'403': 'Forbidden'
+		'404': 'Not Found'
+		'405': 'Method Not Allowed'
+		'406': 'Not Acceptable'
+		'407': 'Proxy Authentication Required'
+		'408': 'Request Timeout'
+		'409': 'Conflict'
+		'410': 'Gone'
+		'411': 'Length Required'
+		'412': 'Precondition Failed'
+		'413': 'Request Entity Too Large'
+		'414': 'Request-URI Too Long'
+		'415': 'Unsupported Media Type'
+		'416': 'Requested Range Not Satisfiable'
+		'417': 'Expectation Failed'
+		'500': 'Internal Server Error'
+		'501': 'Not Implemented'
+		'502': 'Bad Gateway'
+		'503': 'Service Unavailable'
+		'504': 'Gateway Timeout'
+		'505': 'HTTP Version Not Supported'
+
 	# ---------------------------------
 	# Collections

@@ -328,10 +355,6 @@
 		# Extend Server
 		# Whether or not we should extend the server with extra middleware and routing
 		extendServer: true
-
-		# Enable Custom Error Pages
-		# A flag to provide an entry to handle custom error pages
-		customErrors: false

 		# Port
 		# The port that the server should use
@@ -2321,6 +2344,32 @@
 		config = @config
 		server = null

+		# Error Handling Stuff
+		errorFiles = {}
+		# read the out directory for available files
+		fileList = fsUtil.readdirSync(config.outPath)
+
+		for file in fileList
+			if file.match /^[0-9x]{3}\.html/
+				code = file.match /^([0-9x]{3})\.html/
+				errorFiles[code[1]] = pathUtil.join(config.outPath, file)
+
+		# Find the closest matching error page
+		findClosest = (code) ->
+			code = code.toString()
+			match = null
+			count = 2
+
+			while match == null
+				if errorFiles.hasOwnProperty(code)
+					match = code
+				else
+					code = code.substr(0, count)
+					while code.length < 3
+						code += 'x'
+					count -= 1
+			return match
+
 		# Handlers
 		complete = (err) ->
 			return next(err)  if err
@@ -2432,10 +2481,27 @@
 							server.use(express.static config.outPath)

 						# 404 Middleware
-						server.use (req,res,next) ->
-							if config.customErrors
-								return next()
-							return res.send(404)
+						server.use (req, res, next) ->
+							notFound = 404
+							code = findClosest(notFound)
+							if errorFiles.hasOwnProperty(code) && pathUtil.existsSync errorFiles[code]
+								data = fsUtil.readFileSync(errorFiles[code], 'utf8')
+							else
+								data = notFound + ' ' + errorCodes[notFound]
+
+							return res.send(data, notFound)
+
+						# 500 Middleware
+						server.use (err, req, res, next) ->
+							# Assume 500 for now
+							serverError = 500
+							code = findClosest(serverError)
+							if errorFiles.hasOwnProperty(code) && pathUtil.existsSync errorFiles[code]
+								data = fsUtil.readFileSync(errorFiles[code], 'utf8')
+							else
+								data = serverError + ' ' + errorCodes[serverError]
+
+							return res.send(datam serverError)

 				# Start the Server
 				startServer()