use TutorLink and Routerless transistion store

--- tutor/src/components/buttons/back-button.cjsx
+++ tutor/src/components/buttons/back-button.cjsx
@@ -2,7 +2,9 @@
 BS = require 'react-bootstrap'
 _ = require 'underscore'

-{History, Link} = require 'react-router'
+Router = require '../../helpers/router'
+TutorLink = require '../link'
+
 {TransitionActions, TransitionStore} = require '../../flux/transition'

 BackButton = React.createClass
@@ -19,24 +21,21 @@
   getDefaultProps: ->
     bsStyle: 'default'

-  contextTypes:
-    router: React.PropTypes.func
-
   render: ->
     # Gets route to last path that was not the same as the current one
     # See TransitionStore for more detail.
-    historyInfo = TransitionStore.getPrevious(@context.router)
+    historyInfo = TransitionStore.getPrevious()
     {fallbackLink, className} = @props
     {text} = fallbackLink

-    backText = if historyInfo.name then "Back to #{historyInfo.name}" else fallbackLink.text
+    backText = if historyInfo?.name then "Back to #{historyInfo.name}" else fallbackLink.text

-    href =  historyInfo.path or @context.router.makeHref(
+    to =  historyInfo?.path or Router.makePathname(
       @props.fallbackLink.to, @props.fallbackLink.params
     )
-
-    <Link className={"btn btn-#{@props.bsStyle}"} to={href}>
+    console.log historyInfo, to
+    <TutorLink className={"btn btn-#{@props.bsStyle}"} to={to}>
       {backText}
-    </Link>
+    </TutorLink>

 module.exports = BackButton


Add updates panel specs

Add updates panel specs

--- spec/updates-panel-spec.coffee
+++ spec/updates-panel-spec.coffee
@@ -7,7 +7,7 @@
   beforeEach ->
     panel = new UpdatesPanel(new PackageManager)

-  it "Shows updates when updates are available", ->
+  it "shows updates when updates are available", ->
     pack =
       name: 'test-package'
       description: 'some description'
@@ -18,40 +18,42 @@
     panel.beforeShow(updates: [pack])
     expect(panel.updatesContainer.children().length).toBe(1)

-  it "Shows a message when updates are not available", ->
+  it "shows a message when updates are not available", ->
     panel.beforeShow(updates: [])
     expect(panel.updatesContainer.children().length).toBe(0)
     expect(panel.noUpdatesMessage.css('display')).not.toBe('none')

   describe "when the 'Update All' button is clicked", ->
-    it "attempts to update all packages and prompts to restart if at least one package updated successfully", ->
-      packA =
-        name: 'test-package-a'
-        description: 'some description'
-        latestVersion: '99.0.0'
-        version: '1.0.0'
-      packB =
-        name: 'test-package-b'
-        description: 'some description'
-        latestVersion: '99.0.0'
-        version: '1.0.0'
-      packC =
-        name: 'test-package-c'
-        description: 'some description'
-        latestVersion: '99.0.0'
-        version: '1.0.0'
+    packA =
+      name: 'test-package-a'
+      description: 'some description'
+      latestVersion: '99.0.0'
+      version: '1.0.0'
+    packB =
+      name: 'test-package-b'
+      description: 'some description'
+      latestVersion: '99.0.0'
+      version: '1.0.0'
+    packC =
+      name: 'test-package-c'
+      description: 'some description'
+      latestVersion: '99.0.0'
+      version: '1.0.0'

+    [cardA, cardB, cardC] = []
+    [resolveA, resolveB, resolveC, rejectA, rejectB, rejectC] = []
+
+    beforeEach ->
       # skip packman stubbing
       panel.beforeShow(updates: [packA, packB, packC])

       [cardA, cardB, cardC] = panel.getPackageCards()

-      [resolveA, rejectB, resolveC] = []
+      spyOn(cardA, 'update').andReturn(new Promise((resolve, reject) -> [resolveA, rejectA] = [resolve, reject]))
+      spyOn(cardB, 'update').andReturn(new Promise((resolve, reject) -> [resolveB, rejectB] = [resolve, reject]))
+      spyOn(cardC, 'update').andReturn(new Promise((resolve, reject) -> [resolveC, rejectC] = [resolve, reject]))

-      spyOn(cardA, 'update').andReturn(new Promise((resolve) -> resolveA = resolve))
-      spyOn(cardB, 'update').andReturn(new Promise((resolve, reject) -> rejectB = reject))
-      spyOn(cardC, 'update').andReturn(new Promise((resolve) -> resolveC = resolve))
-
+    it "attempts to update all packages and prompts to restart if at least one package updates successfully", ->
       expect(atom.notifications.getNotifications().length).toBe 0

       panel.updateAll()
@@ -73,3 +75,26 @@
         spyOn(atom, 'restartApplication')
         notifications[0].options.buttons[0].onDidClick()
         expect(atom.restartApplication).toHaveBeenCalled()
+
+    it 'disables the Update All button if all updates succeed', ->
+      expect(panel.updateAllButton.prop('disabled')).toBe false
+      panel.updateAll()
+
+      resolveA()
+      resolveB()
+      resolveC()
+
+      waits 0
+      runs ->
+        expect(panel.updateAllButton.prop('disabled')).toBe true
+
+    it 'keeps the Update All button enabled if not all updates succeed', ->
+      panel.updateAll()
+
+      resolveA()
+      rejectB('Error updating package')
+      resolveC()
+
+      waits 0
+      runs ->
+        expect(panel.updateAllButton.prop('disabled')).toBe false


check for a valid counter value in event log

--- services/real-time/app/coffee/EventLogger.coffee
+++ services/real-time/app/coffee/EventLogger.coffee
@@ -15,7 +15,7 @@
 		return if typeof(message_id) isnt 'string'
 		[key, count] = message_id.split("-", 2)
 		count = parseInt(count, 10)
-		if !count # ignore checks if counter is not present
+		if !(count >= 0)# ignore checks if counter is not present
 			return
 		# store the last count in a hash for each host
 		previous = EventLogger._storeEventCount(key, count)


Do not transition camera if camera angle didn't change.


Update checkFlag to support lists

Update checkFlag to support lists

--- core/KD.coffee
+++ core/KD.coffee
@@ -67,7 +67,15 @@

   isMine:(account)-> @whoami().profile.nickname is account.profile.nickname

-  checkFlag:(flag, account = KD.whoami())-> account.globalFlags and flag in account.globalFlags
+  checkFlag:(flagToCheck, account = KD.whoami())->
+    if account.globalFlags
+      if 'string' is typeof flagToCheck
+        return flagToCheck in account.globalFlags
+      else
+        for flag in flagToCheck
+          if flag in account.globalFlags
+            return yes
+    no

   requireLogin:(errMsg, callback)->


add null console logger for debugging

add null console logger for debugging

--- content.coffee
+++ content.coffee
@@ -1,4 +1,12 @@
-DOMAIN_RE = /^https?:\/\/(www\.)?(facebook.com|facebook.net|fbcdn.net)/i
+DEBUG = true
+
+if !DEBUG
+  console = {}
+  console.log = ->
+else
+  console = window.console
+
+

 SECRET = "" + Math.random()


Activity: messagePane removed unnecessary check

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -143,17 +143,15 @@

   glance: ->

-    data = @getData()
-    {id, typeConstant} = data
-    {socialapi, appManager} = KD.singletons
+    {socialapi, appManager}  = KD.singletons
+    {id, typeConstant, name} = @getData()

     app  = appManager.get 'Activity'
     item = app.getView().sidebar.selectedItem

     return  unless item?.count
-    # no need to send updatelastSeenTime or glance
-    # when checking publicfeeds
-    return  if typeConstant is 'group'
+    # no need to send updatelastSeenTime or glance when checking publicfeeds
+    return  if name is 'public'

     if typeConstant is 'post'
     then socialapi.channel.glancePinnedPost   messageId : id, @bound 'glanced'


activity/flux: dispatch correct action at load messages

--- client/activity/lib/flux/actions/message.coffee
+++ client/activity/lib/flux/actions/message.coffee
@@ -11,8 +11,8 @@
 ###
 loadMessages = (channelId) ->

-  { appManager, socialapi } = kd.singletons
-  { LOAD_MESSAGES_BEGIN, LOAD_MESSAGES_FAIL, CREATE_MESSAGE_SUCCESS } = actionTypes
+  { socialapi } = kd.singletons
+  { LOAD_MESSAGES_BEGIN, LOAD_MESSAGES_FAIL, LOAD_MESSAGE_SUCCESS } = actionTypes

   dispatch LOAD_MESSAGES_BEGIN, { channelId }

@@ -24,8 +24,9 @@
     # get the channel instance from cache and dispatch it.
     channel = socialapi.retrieveCachedItemById channelId

-    messages.forEach (message) ->
-      dispatch CREATE_MESSAGE_SUCCESS, { channelId, channel, message }
+    kd.singletons.reactor.batch ->
+      messages.forEach (message) ->
+        dispatch LOAD_MESSAGE_SUCCESS, { channelId, channel, message }

 ###*


Document writing plugins

Document writing plugins

--- assets/javascripts/plugins.js.coffee
+++ assets/javascripts/plugins.js.coffee
@@ -1,3 +1,29 @@
+# Each plugin is registered with
+#      * a name
+#      * a regex
+#      * a callback function
+#
+# Each message's body will be matched against the regex.
+# If it matches, then the callback function is run.
+#
+# The callback function must return the body of the message.
+# The body of the message can be modified if required (HTML allowed).
+#
+# The callback function is passed the following arguments:
+#
+#     * body of the message
+#     * type of message ("text", "paste" or "me")
+#     * a boolean variable which indicates if the message is live or history
+#
+# Use the third argument incase to check incase your plugin
+# should work on live messages only.
+#
+
+
+# The following plugin detects links and replaces them with anchor tags
+# It does it only for "me" and "text" messages
+# and only returns the body for "paste" messages.
+
 App.plugins.register "link", /(https?\:\S+)/g, (content, messageType, history)->
   if ["me", "text"].indexOf(messageType) != -1
     content.replace(/(https?\:\S+)/g, "$1")


Lazyload: Threshold logic taken to window

--- client/Main/maincontroller/mainviewcontroller.coffee
+++ client/Main/maincontroller/mainviewcontroller.coffee
@@ -6,10 +6,10 @@

     {repeat, killRepeat} = KD.utils

-    mainView       = @getView()
-    mainController = KD.getSingleton 'mainController'
-    appManager     = KD.getSingleton 'appManager'
-    @registerSingleton 'mainViewController', @, yes
+    mainView         = @getView()
+    mainController   = KD.getSingleton 'mainController'
+    appManager       = KD.getSingleton 'appManager'
+    @registerSingleton 'mainViewController', this, yes
     @registerSingleton 'mainView', mainView, yes

     mainController.on 'accountChanged.to.loggedIn', (account)=>
@@ -27,6 +27,25 @@
     then $('body').addClass 'super'
     else $('body').removeClass 'super'

+    mainViewController = this
+    window.onscroll = do ->
+      lastRatio = 0
+      threshold = 50
+      (event)->
+        el = document.body
+        {scrollHeight, scrollTop} = el
+
+        dynamicThreshold = if threshold > 1
+        then (scrollHeight - threshold) / scrollHeight
+        else threshold
+
+        ratio = (scrollTop + window.innerHeight) / scrollHeight
+
+        if dynamicThreshold < ratio > lastRatio
+          mainViewController.emit 'LazyLoadThresholdReached', {ratio}
+          log 'firlatingen', ratio
+
+        lastRatio = ratio

   loadView:(mainView)->


activity: fix reference error

activity: fix reference error

--- client/activity/lib/components/chatlist/index.coffee
+++ client/activity/lib/components/chatlist/index.coffee
@@ -72,6 +72,7 @@
   renderChildren: ->

     { messages, showItemMenu, channelName } = @props
+    { selectedMessageId } = @state

     lastDifferentOwnerId = null
     prevMessage = null


Add output via robot.logger instead

Add output via robot.logger instead

--- src/aws.coffee
+++ src/aws.coffee
@@ -93,7 +93,8 @@
     else
       ec2.describeInstances MaxResults: 500, (err, data) ->
         if err
-          console.log err, err.stack
+          robot.logger.error "Received error #{JSON.stringify err}"
+          robot.logger.error err.stack
         else
           instancesCache.data = data
           instancesCache.expiry = moment().add 5, 'seconds'
@@ -132,7 +133,7 @@
         if reportZero
           robot.messageRoom room, message
         else
-          console.log "#{room}: #{message}"
+          robot.logger.info "#{room}: #{message}"

   reportOnQuery = (room, query, duration, reportZero) ->
     reportZero = no if not reportZero?
@@ -162,7 +163,7 @@
         if reportZero
           robot.messageRoom room, message
         else
-          console.log "#{room}: #{message}"
+          robot.logger.info "#{room}: #{message}"

   robot.respond /aws jobs/, (msg) ->
     listJobs msg.message.room


Client: Activity: Added handleSetLastMessageEditMode to messagestore

--- client/activity/lib/flux/stores/messagesstore.coffee
+++ client/activity/lib/flux/stores/messagesstore.coffee
@@ -40,6 +40,7 @@
     @on actions.EDIT_MESSAGE_FAIL, @handleEditMessageFail
     @on actions.SET_MESSAGE_EDIT_MODE, @handleSetMessageEditMode
     @on actions.UNSET_MESSAGE_EDIT_MODE, @handleUnsetMessageEditMode
+    @on actions.SET_LAST_MESSAGE_EDIT_MODE, @handleSetLastMessageEditMode

     @on actions.REMOVE_MESSAGE_BEGIN, @handleRemoveMessageBegin
     @on actions.REMOVE_MESSAGE_SUCCESS, @handleRemoveMessageSuccess
@@ -215,6 +216,18 @@
     return messages = messages.setIn [messageId, '__isEditing'], no

+  ###*
+   * It sets last message editing mode
+   *
+   * @param {IMMessageCollection} messages
+   * @param {object} payload
+   * @param {string} payload.messageId
+   * @return {IMMessageCollection} nextState
+  ###
+  handleSetLastMessageEditMode: (messages, { accountId }) ->
+
+    return @setLastMessageEditMode(messages, { accountId, __isEditing: yes })
+
    * Handler for `CREATE_COMMENT_SUCCESS` action.
    * It first removes fake comment if it exists, and then pushes given comment
    * from payload.


fix locking up with long unread threads (will fix better soon)

--- vue/src/shared/models/discussion_model.coffee
+++ vue/src/shared/models/discussion_model.coffee
@@ -188,7 +188,8 @@
     (last(@readRanges) || [])[1]

   firstUnreadSequenceId: ->
-    (@unreadRanges()[0] || [])[0]
+    # (@unreadRanges()[0] || [])[0]
+    @firstMissing(@ranges, @readRanges)

   readSequenceIds: ->
     RangeSet.rangesToArray(@readRanges)


app-settings-menu: Updated ace notification position.

--- client/app/Applications/Ace.kdapplication/ace.coffee
+++ client/app/Applications/Ace.kdapplication/ace.coffee
@@ -280,8 +280,7 @@
     notification = new KDNotificationView
       title     : msg or "Something went wrong"
       type      : "mini"
-      cssClass  : "editor #{style}"
-      container : @parent
+      cssClass  : "#{style}"
       duration  : duration or if details then 5000 else 2500
       details   : details
       click     : ->


Add explicit assertion count for model test case tests

--- tests/batman/testing/model_test_case_test.coffee
+++ tests/batman/testing/model_test_case_test.coffee
@@ -13,21 +13,21 @@
     @foo = new @Foo
     @foo.set 'baz', 'baz'

-test 'assertValid asserts Batman Model is valid', ->
+test 'assertValid asserts Batman Model is valid', 1, ->
   @foo.set('title', 'foo')
   @testCase.assertValid @foo

-test 'assertNotValid asserts Batman Model is not valid', ->
+test 'assertNotValid asserts Batman Model is not valid', 1, ->
   @testCase.assertNotValid @foo

-test 'assertDecoders asserts full decoder list matches', ->
+test 'assertDecoders asserts full decoder list matches', 1, ->
   @testCase.assertDecoders @Foo, 'id', 'foo', 'bar'

-test 'assertEncoders asserts full encoder list matches', ->
+test 'assertEncoders asserts full encoder list matches', 1, ->
   @testCase.assertEncoders @Foo, 'foo', 'bar', 'baz'

-test 'assertEncoded asserts expected value for custom encoders', ->
+test 'assertEncoded asserts expected value for custom encoders', 1, ->
   @testCase.assertEncoded @foo, 'baz', '!baz!'

-test 'assertEncoded asserts function return value if expected value is a function', ->
+test 'assertEncoded asserts function return value if expected value is a function', 1, ->
   @testCase.assertEncoded @foo, 'baz', (baz) -> baz is '!baz!'


Improved syntax on title checking

Improved syntax on title checking

--- client/apps/edit/components/layout/index.coffee
+++ client/apps/edit/components/layout/index.coffee
@@ -187,16 +187,10 @@
         imageCount += 1
     @fullText.push('') for num in [imageCount..1]
     @fullText = @fullText.join(' ')
-
-    @yoastTitle = ''
-    if @article.get('thumbnail_title')
-      @yoastTitle = @article.get('thumbnail_title')
-    else
-      @yoastTitle = @article.get('title')

     yoastView = new YoastView
       contentField: @fullText
-      title: @yoastTitle
+      title: @article.get('thumbnail_title') or @article.get('title') or ''
       slug: @article.getSlug()

   onKeyup: =>


Autocomplete view reverted

Autocomplete view reverted

--- packages/emberella/lib/views/autocomplete.coffee
+++ packages/emberella/lib/views/autocomplete.coffee
@@ -111,6 +111,10 @@
   click: (e) ->
     @dispatch('selectMember', @, true)

+###############################################################################
+###############################################################################
+
+
 ###
   The `Emberella.AutocompleteView` combines a text field and a collection view
   to offer a list of suggested completions based on user input.
@@ -145,6 +149,11 @@
     @final
   ###
   isAutocomplete: true
+
+  ###
+    @property childViews
+  ###
+  childViews: ['inputView', 'listView']

   ###
     @property defaultTemplate
@@ -1025,11 +1034,6 @@
     get @, 'allSuggestions'
   , '_search', 'matcher', 'minLength', 'source', 'source.length'

-  setupChildViews: Ember.observer(->
-    @pushObject(@createChildView(get(@, 'inputView')))
-    @pushObject(@createChildView(get(@, 'listView')))
-  ).on 'init'
-
   ###
     @private


try running user as jenkins

try running user as jenkins

--- test/acceptance/scripts/settings.test.coffee
+++ test/acceptance/scripts/settings.test.coffee
@@ -28,7 +28,7 @@
 				HOME: "/tmp"
 			modem:
 				socketPath: false
-			user: "tex"
+			user: "111"
 			latexmkCommandPrefix: ["/usr/bin/time", "-v"]         # on Linux

 	internal:


Be explicit in version filter

Be explicit in version filter

--- services/docstore/app/coffee/DocManager.coffee
+++ services/docstore/app/coffee/DocManager.coffee
@@ -81,7 +81,7 @@
 						callback null, true, oldRev + 1 # rev will have been incremented in mongo by MongoManager.updateDoc

 	deleteDoc: (project_id, doc_id, callback = (error) ->) ->
-		DocManager.getDoc project_id, doc_id, {}, (error, doc) ->
+		DocManager.getDoc project_id, doc_id, { version: false }, (error, doc) ->
 			return callback(error) if error?
 			return callback new Errors.NotFoundError("No such project/doc to delete: #{project_id}/#{doc_id}") if !doc?
 			MongoManager.upsertIntoDocCollection project_id, doc_id, doc.lines, (error) ->


Disables the progression scoreboard while there is less than 2 teams on the scoreboard. Updated legend position.

--- picoCTF-web/web/coffee/progression_graphs.coffee
+++ picoCTF-web/web/coffee/progression_graphs.coffee
@@ -6,7 +6,7 @@
 topTeamsGraphOptions = {
   title: "Top Team Score Progression",
   legend: {
-    position: "none"
+    position: "top"
   },
   vAxis: {
     title: "Score"
@@ -90,7 +90,7 @@
   div = divFromSelector selector
   apiCall "GET", "/api/stats/top_teams/score_progression", {}
   .done (data) ->
-    if data.data.length > 0
+    if data.data.length >= 2

       scoreData = (team.score_progression for team in data.data)
       dataPoints = _.zip.apply _, progressionDataToPoints scoreData, 60, 100


Update license override for tweetnacl.

Update license override for tweetnacl.

--- build/tasks/license-overrides.coffee
+++ build/tasks/license-overrides.coffee
@@ -123,10 +123,10 @@
     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     """
-  'tweetnacl@0.13.2':
+  'tweetnacl@0.14.3':
     repository: 'https://github.com/dchest/tweetnacl-js'
     license: 'Public Domain'
-    source: 'https://github.com/dchest/tweetnacl-js/blob/2f328394f74d83564634fb89ea2798caa3a4edb9/README.md says public domain.'
+    source: 'https://github.com/dchest/tweetnacl-js/blob/1042c9c65dc8f1dcb9e981d962d7dbbcf58f1fdc/COPYING.txt says public domain.'
   'json-schema@0.2.2':
     repository: 'https://github.com/kriszyp/json-schema'
     license: 'BSD'


return more data from select2

return more data from select2

--- src/autocompleter/select2-autocompleter.coffee
+++ src/autocompleter/select2-autocompleter.coffee
@@ -10,7 +10,8 @@
     @_setupInput()

     @$input.on 'select2-selecting', (e)=>
-      @complete.call(@, name: e.object.text, value: e.object.id)
+      data = $.extend({}, { name: e.object.text, value: e.object.id }, e.object)
+      @complete.call(@, data)
       @$input.select2('destroy').remove()

     @$input.on 'select2-close', (e)=>


Search completion; some code touch up.

Search completion; some code touch up.

--- pages/vomnibar.coffee
+++ pages/vomnibar.coffee
@@ -8,9 +8,6 @@
   getUI: -> @vomnibarUI
   completers: {}

-  #
-  # Activate the Vomnibox.
-  #
   activate: (userOptions) ->
     options =
       completer: "omni"
@@ -18,19 +15,16 @@
       newTab: false
       selectFirst: false
     extend options, userOptions
-
-    options.refreshInterval =
-      if options.completer == "omni" then 100 else 0
-
-    name = options.completer
-    completer = @completers[name] ?= new BackgroundCompleter name
+    extend options, refreshInterval: if options.completer == "omni" then 100 else 0
+
+    completer = @completers[options.completer] ?= new BackgroundCompleter options.completer
     @vomnibarUI ?= new VomnibarUI()
     completer.refresh()
-    @vomnibarUI.setInitialSelectionValue(if options.selectFirst then 0 else -1)
-    @vomnibarUI.setCompleter(completer)
-    @vomnibarUI.setRefreshInterval(options.refreshInterval)
-    @vomnibarUI.setForceNewTab(options.newTab)
-    @vomnibarUI.setQuery(options.query)
+    @vomnibarUI.setInitialSelectionValue if options.selectFirst then 0 else -1
+    @vomnibarUI.setCompleter completer
+    @vomnibarUI.setRefreshInterval options.refreshInterval
+    @vomnibarUI.setForceNewTab options.newTab
+    @vomnibarUI.setQuery options.query
     @vomnibarUI.update true

   hide: -> @vomnibarUI?.hide()
@@ -43,17 +37,10 @@
     @initDom()

   setQuery: (query) -> @input.value = query
-
-  setInitialSelectionValue: (initialSelectionValue) ->
-    @initialSelectionValue = initialSelectionValue
-
-  setCompleter: (completer) ->
-    @completer = completer
-    @reset()
-
-  setRefreshInterval: (refreshInterval) -> @refreshInterval = refreshInterval
-
-  setForceNewTab: (forceNewTab) -> @forceNewTab = forceNewTab
+  setInitialSelectionValue: (@initialSelectionValue) ->
+  setRefreshInterval: (@refreshInterval) ->
+  setForceNewTab: (@forceNewTab) ->
+  setCompleter: (@completer) -> @reset()

   # The sequence of events when the vomnibar is hidden is as follows:
   # 1. Post a "hide" message to the host page.
@@ -76,7 +63,6 @@
     @completions = []
     window.clearTimeout @updateTimer if @updateTimer?
     @updateTimer = null
-    @mostRecentQuery = null
     @previousAutoSelect = null
     @previousInputValue = null
     @selection = @initialSelectionValue
@@ -166,7 +152,7 @@
       callback?()

   populateUiWithCompletions: (completions) ->
-    # update completion list with the new data
+    # Update completion list with the new suggestions.
     @completionList.innerHTML = completions.map((completion) -> "<li>#{completion.html}</li>").join("")
     @completionList.style.display = if completions.length > 0 then "block" else ""
     @selection = Math.min completions.length - 1, Math.max @initialSelectionValue, @selection
@@ -179,7 +165,7 @@
       @previousInputValue = null
       @previousAutoSelect = null
       @selection = -1
-    @update()
+    @update false

   update: (updateSynchronously = false, callback = null) =>
     if updateSynchronously
@@ -222,36 +208,31 @@

   # name is background-page completer to connect to: "omni", "tabs", or "bookmarks".
   constructor: (@name) ->
+    @port = chrome.runtime.connect name: "completions"
     @messageId = null
-    @port = chrome.runtime.connect name: "completions"
-    @port.onMessage.addListener (msg) => @messageHandler msg
     @reset()

-  reset: ->
-    # We only cache results for the duration of a single vomnibar activation.
-    @cache = new SimpleCache 1000 * 60 * 5
-
-  messageHandler: (msg) ->
-    # The result objects coming from the background page will be of the form:
-    #   { html: "", type: "", url: "" }
-    # Type will be one of [tab, bookmark, history, domain, search], or a custom search engine description.
-    for result in msg.results
-      result.performAction =
-        if result.type == "tab"
-          @completionActions.switchToTab.curry result.tabId
-        else
-          @completionActions.navigateToUrl.curry result.url
-
-    # Cache the results (but only if the background completer tells us that it's ok to do so).
-    if msg.callerMayCacheResults
-      console.log "cache set:", msg.query if @debug
-      @cache.set msg.query, msg.results
-    else
-      console.log "not setting cache:", msg.query if @debug
-
-    # We ignore messages which arrive too late.
-    if msg.id == @messageId
-      @mostRecentCallback msg.results
+    @port.onMessage.addListener (msg) =>
+      # The result objects coming from the background page will be of the form:
+      #   { html: "", type: "", url: "" }
+      # Type will be one of [tab, bookmark, history, domain, search], or a custom search engine description.
+      for result in msg.results
+        result.performAction =
+          if result.type == "tab"
+            @completionActions.switchToTab.curry result.tabId
+          else
+            @completionActions.navigateToUrl.curry result.url
+
+      # Cache the results (but only if the background completer tells us that it's ok to do so).
+      if msg.callerMayCacheResults
+        console.log "cache set:", msg.query if @debug
+        @cache.set msg.query, msg.results
+      else
+        console.log "not setting cache:", msg.query if @debug
+
+      # We ignore messages which arrive too late.
+      if msg.id == @messageId
+        @mostRecentCallback msg.results

   filter: (query, @mostRecentCallback) ->
     queryTerms = query.trim().split(/\s+/).filter (term) -> 0 < term.length
@@ -277,6 +258,11 @@
     # Inform the background completer that we have a new vomnibar activation.
     @port.postMessage name: @name, handler: "refresh"

+  reset: ->
+    # We only cache results for the duration of a single vomnibar activation.
+    @cache = new SimpleCache 1000 * 60 * 5
+    @mostRecentQuery = null
+
   cancel: ->
     # Inform the background completer that it may (should it choose to do so) abandon any pending query
     # (because the user is typing, and there'll be another query along soon).


Update cli executable.

Update cli executable.

--- src/cli.coffee
+++ src/cli.coffee
@@ -1,15 +1,14 @@
 #!/usr/bin/env coffee
-
-fs = require 'fs'
+fs        = require 'fs'
 requisite = require('../lib')

-writeBundle = (bundle, opts) ->
-  if opts.output?
-    fs.writeFileSync opts.output, bundle.toString opts, 'utf8'
-  else
-    console.log bundle.toString opts

-help = (code, message) ->
+error = (message) ->
+  console.log message
+  process.exit 1
+
+
+help = ->
   console.log """

   Usage: requisite path/to/entry-module [options]
@@ -28,10 +27,19 @@
     -v, --version                Display version
     -h, --help                   Display this help
   """
-  console.log '\n' + message if message
-  process.exit code
+  process.exit 0
+
+
+version = ->
+  console.log require('../package').version
+  process.exit 0
+
+
+args = process.argv.slice 2
+entry = args.shift()

 opts =
+  entry:   entry
   bare:    false
   include: []
   exclude: null
@@ -41,16 +49,11 @@
   prelude: null
   watch:   false

-args = process.argv.slice 2
-
-entry = args.shift()
-
 if (not entry?) or entry.charAt(0) == '-'
   if entry in ['-v', '--version']
-    console.log require('../package').version
-    process.exit 0
+    version()
   else
-    help 1
+    help()

 while opt = args.shift()
   switch opt
@@ -76,17 +79,26 @@
     when '-w', '--watch'
       opts.watch = true
     when '-h', '--help'
-      help 0
+      help()
     else
-      help 1
+      error 'Unknown argument'
+

 if opts.watch and not opts.output?
-  help 1, 'Output must be specified when using watch'
+  error 'Output must be specified when using watch'

-requisite.bundle entry, opts, (err, bundle) ->
-  writeBundle bundle, opts
+
+writeBundle = (bundle) ->
+  if opts.output?
+    fs.writeFileSync opts.output, bundle.toString opts, 'utf8'
+  else
+    console.log bundle.toString opts
+
+
+requisite.bundle opts, (err, bundle) ->
+  writeBundle bundle

   if opts.watch
-    requisite.watch bundle, (event, filename, mod) ->
+    requisite.watch opts, ->
       console.log "#{/\d{2}:\d{2}:\d{2}/.exec(new Date())[0]} - recompiling, #{filename} changed"
-      writeBundle bundle, opts
+      writeBundle bundle


bump Payment Font to 1.1.2

bump Payment Font to 1.1.2

--- snippets/pf.cson
+++ snippets/pf.cson
@@ -8,9 +8,15 @@
   'pf-american-express':
     'prefix': 'pf-american-express'
     'body': '<${1:span} class="pf pf-american-express">$2</${1:span}>'
+  'pf-apple-pay':
+    'prefix': 'pf-apple-pay'
+    'body': '<${1:span} class="pf pf-apple-pay">$2</${1:span}>'
   'pf-atm':
     'prefix': 'pf-atm'
     'body': '<${1:span} class="pf pf-atm">$2</${1:span}>'
+  'pf-bancontact-mister-cash':
+    'prefix': 'pf-bancontact-mister-cash'
+    'body': '<${1:span} class="pf pf-bancontact-mister-cash">$2</${1:span}>'
   'pf-bank-transfer':
     'prefix': 'pf-bank-transfer'
     'body': '<${1:span} class="pf pf-bank-transfer">$2</${1:span}>'
@@ -38,9 +44,15 @@
   'pf-cash-on-delivery':
     'prefix': 'pf-cash-on-delivery'
     'body': '<${1:span} class="pf pf-cash-on-delivery">$2</${1:span}>'
+  'pf-cash-on-pickup':
+    'prefix': 'pf-cash-on-pickup'
+    'body': '<${1:span} class="pf pf-cash-on-pickup">$2</${1:span}>'
   'pf-cash':
     'prefix': 'pf-cash'
     'body': '<${1:span} class="pf pf-cash">$2</${1:span}>'
+  'pf-cashcloud':
+    'prefix': 'pf-cashcloud'
+    'body': '<${1:span} class="pf pf-cashcloud">$2</${1:span}>'
   'pf-cb':
     'prefix': 'pf-cb'
     'body': '<${1:span} class="pf pf-cb">$2</${1:span}>'
@@ -56,6 +68,9 @@
   'pf-credit-card':
     'prefix': 'pf-credit-card'
     'body': '<${1:span} class="pf pf-credit-card">$2</${1:span}>'
+  'pf-dankort':
+    'prefix': 'pf-dankort'
+    'body': '<${1:span} class="pf pf-dankort">$2</${1:span}>'
   'pf-diners':
     'prefix': 'pf-diners'
     'body': '<${1:span} class="pf pf-diners">$2</${1:span}>'
@@ -65,6 +80,12 @@
   'pf-ec':
     'prefix': 'pf-ec'
     'body': '<${1:span} class="pf pf-ec">$2</${1:span}>'
+  'pf-elo-alt':
+    'prefix': 'pf-elo-alt'
+    'body': '<${1:span} class="pf pf-elo-alt">$2</${1:span}>'
+  'pf-elo':
+    'prefix': 'pf-elo'
+    'body': '<${1:span} class="pf pf-elo">$2</${1:span}>'
   'pf-eps':
     'prefix': 'pf-eps'
     'body': '<${1:span} class="pf pf-eps">$2</${1:span}>'
@@ -80,21 +101,24 @@
   'pf-flattr':
     'prefix': 'pf-flattr'
     'body': '<${1:span} class="pf pf-flattr">$2</${1:span}>'
+  'pf-gbp':
+    'prefix': 'pf-gbp'
+    'body': '<${1:span} class="pf pf-gbp">$2</${1:span}>'
   'pf-giropay':
     'prefix': 'pf-giropay'
     'body': '<${1:span} class="pf pf-giropay">$2</${1:span}>'
-  'pf-gittip':
-    'prefix': 'pf-gittip'
-    'body': '<${1:span} class="pf pf-gittip">$2</${1:span}>'
   'pf-google-wallet-alt':
     'prefix': 'pf-google-wallet-alt'
     'body': '<${1:span} class="pf pf-google-wallet-alt">$2</${1:span}>'
   'pf-google-wallet':
     'prefix': 'pf-google-wallet'
     'body': '<${1:span} class="pf pf-google-wallet">$2</${1:span}>'
-  'pf-gpb':
-    'prefix': 'pf-gpb'
-    'body': '<${1:span} class="pf pf-gpb">$2</${1:span}>'
+  'pf-gratipay-sign':
+    'prefix': 'pf-gratipay-sign'
+    'body': '<${1:span} class="pf pf-gratipay-sign">$2</${1:span}>'
+  'pf-gratipay':
+    'prefix': 'pf-gratipay'
+    'body': '<${1:span} class="pf pf-gratipay">$2</${1:span}>'
   'pf-ideal':
     'prefix': 'pf-ideal'
     'body': '<${1:span} class="pf pf-ideal">$2</${1:span}>'
@@ -104,6 +128,9 @@
   'pf-inr':
     'prefix': 'pf-inr'
     'body': '<${1:span} class="pf pf-inr">$2</${1:span}>'
+  'pf-interac':
+    'prefix': 'pf-interac'
+    'body': '<${1:span} class="pf pf-interac">$2</${1:span}>'
   'pf-invoice-sign-alt-o':
     'prefix': 'pf-invoice-sign-alt-o'
     'body': '<${1:span} class="pf pf-invoice-sign-alt-o">$2</${1:span}>'
@@ -143,15 +170,33 @@
   'pf-mastercard':
     'prefix': 'pf-mastercard'
     'body': '<${1:span} class="pf pf-mastercard">$2</${1:span}>'
+  'pf-mercado-pago-sign':
+    'prefix': 'pf-mercado-pago-sign'
+    'body': '<${1:span} class="pf pf-mercado-pago-sign">$2</${1:span}>'
+  'pf-mercado-pago':
+    'prefix': 'pf-mercado-pago'
+    'body': '<${1:span} class="pf pf-mercado-pago">$2</${1:span}>'
+  'pf-moip':
+    'prefix': 'pf-moip'
+    'body': '<${1:span} class="pf pf-moip">$2</${1:span}>'
+  'pf-multibanco':
+    'prefix': 'pf-multibanco'
+    'body': '<${1:span} class="pf pf-multibanco">$2</${1:span}>'
   'pf-ogone':
     'prefix': 'pf-ogone'
     'body': '<${1:span} class="pf pf-ogone">$2</${1:span}>'
+  'pf-pagseguro':
+    'prefix': 'pf-pagseguro'
+    'body': '<${1:span} class="pf pf-pagseguro">$2</${1:span}>'
   'pf-paybox':
     'prefix': 'pf-paybox'
     'body': '<${1:span} class="pf pf-paybox">$2</${1:span}>'
   'pf-paylife':
     'prefix': 'pf-paylife'
     'body': '<${1:span} class="pf pf-paylife">$2</${1:span}>'
+  'pf-paymill':
+    'prefix': 'pf-paymill'
+    'body': '<${1:span} class="pf pf-paymill">$2</${1:span}>'
   'pf-paypal-alt':
     'prefix': 'pf-paypal-alt'
     'body': '<${1:span} class="pf pf-paypal-alt">$2</${1:span}>'
@@ -161,6 +206,12 @@
   'pf-paysafecard':
     'prefix': 'pf-paysafecard'
     'body': '<${1:span} class="pf pf-paysafecard">$2</${1:span}>'
+  'pf-payshop':
+    'prefix': 'pf-payshop'
+    'body': '<${1:span} class="pf pf-payshop">$2</${1:span}>'
+  'pf-payu':
+    'prefix': 'pf-payu'
+    'body': '<${1:span} class="pf pf-payu">$2</${1:span}>'
   'pf-postepay':
     'prefix': 'pf-postepay'
     'body': '<${1:span} class="pf pf-postepay">$2</${1:span}>'
@@ -176,6 +227,18 @@
   'pf-rub':
     'prefix': 'pf-rub'
     'body': '<${1:span} class="pf pf-rub">$2</${1:span}>'
+  'pf-sage':
+    'prefix': 'pf-sage'
+    'body': '<${1:span} class="pf pf-sage">$2</${1:span}>'
+  'pf-sepa-alt':
+    'prefix': 'pf-sepa-alt'
+    'body': '<${1:span} class="pf pf-sepa-alt">$2</${1:span}>'
+  'pf-sepa':
+    'prefix': 'pf-sepa'
+    'body': '<${1:span} class="pf pf-sepa">$2</${1:span}>'
+  'pf-six':
+    'prefix': 'pf-six'
+    'body': '<${1:span} class="pf pf-six">$2</${1:span}>'
   'pf-skrill':
     'prefix': 'pf-skrill'
     'body': '<${1:span} class="pf pf-skrill">$2</${1:span}>'


Issue #46: Fetch all missed transactions for a user who has maxAlerts.

--- src/alerts.coffee
+++ src/alerts.coffee
@@ -110,20 +110,39 @@
 		else
 			callback err, results

+calcDateFromForUser = (user) ->
+	if user.maxAlerts is '1 per hour'
+		dateFrom = moment().subtract('hours', 1).toDate()
+	else if user.maxAlerts is '1 per day'
+		dateFrom = moment().startOf('day').toDate()
+	else
+		null
+
 userAlreadyReceivedAlert = (channelID, status, user, callback) ->
 	if not user.maxAlerts or user.maxAlerts is 'no max'
 		# user gets all alerts
 		callback null, false
 	else
-		if user.maxAlerts is '1 per hour'
-			dateFrom = moment().subtract('hours', 1).toDate()
-		else if user.maxAlerts is '1 per day'
-			dateFrom = moment().startOf('day').toDate()
-		else
-			callback "Unsupported option 'maxAlerts=#{user.maxAlerts}'"
+		dateFrom = calcDateFromForUser user
+		return callback "Unsupported option 'maxAlerts=#{user.maxAlerts}'" if not dateFrom

 		findOneAlert channelID, status, dateFrom, user.user, 'Completed', (err, alert) ->
 			callback err ? null, if alert then true else false
+
+# Setup the list of transactions for alerting.
+#
+# If a user is setup with maxAlerts, all transactions will be fetched
+# since the last time they received an alert.
+#
+# If the user ahs no maxAlerts limit, then the transactions object is returned as is.
+getTransactionsForAlert = (channelID, status, user, transactions, callback) ->
+	if not user.maxAlerts or user.maxAlerts is 'no max'
+		callback null, transactions
+	else
+		dateFrom = calcDateFromForUser user
+		return callback "Unsupported option 'maxAlerts=#{user.maxAlerts}'" if not dateFrom
+
+		findTransactionsMatchingStatus channelID, status, dateFrom, null, callback

 sendAlert = (channelID, status, user, transactions, contactHandler, done) ->
 	User.findOne { email: user.user }, (err, dbUser) ->
@@ -136,17 +155,18 @@

 			logger.info "Sending alert for user '#{user.user}' using method '#{user.method}'"

-			if user.method is 'email'
-				plainMsg = plainTemplate transactions
-				htmlMsg = htmlTemplate transactions
-				contactHandler 'email', user.user, 'OpenHIM Alert', plainMsg, htmlMsg, done
-			else if user.method is 'sms'
-				return done "Cannot send alert: MSISDN not specified for user '#{user.user}'" if not dbUser.msisdn
-
-				smsMsg = smsTemplate transactions
-				contactHandler 'sms', dbUser.msisdn, 'OpenHIM Alert', smsMsg, null, done
-			else
-				return done "Unknown method '#{user.method}' specified for user '#{user.user}'"
+			getTransactionsForAlert channelID, status, user, transactions, (err, transactionsForAlert) ->
+				if user.method is 'email'
+					plainMsg = plainTemplate transactionsForAlert
+					htmlMsg = htmlTemplate transactionsForAlert
+					contactHandler 'email', user.user, 'OpenHIM Alert', plainMsg, htmlMsg, done
+				else if user.method is 'sms'
+					return done "Cannot send alert: MSISDN not specified for user '#{user.user}'" if not dbUser.msisdn
+
+					smsMsg = smsTemplate transactionsForAlert
+					contactHandler 'sms', dbUser.msisdn, 'OpenHIM Alert', smsMsg, null, done
+				else
+					return done "Unknown method '#{user.method}' specified for user '#{user.user}'"

 # Actions to take after sending an alert
 afterSendAlert = (err, channelID, alert, user, transactions, skipSave, done) ->


sort answers by answer ids instead, make Answer component

--- src/components/question.cjsx
+++ src/components/question.cjsx
@@ -3,6 +3,55 @@
 ArbitraryHtml = require './html'

 idCounter = 0
+
+Answer = React.createClass
+  displayName: 'Answer'
+
+  render: ->
+    {answer, iter, qid, type, correct_answer_id, hasCorrectAnswer, chosen_answer, onChangeAnswer} = @props
+    qid ?= "auto-#{idCounter++}"
+
+    isChecked = answer.id in chosen_answer
+    isCorrect = answer.id is correct_answer_id
+
+    isCorrect = (answer.correctness is '1.0') if answer.correctness?
+
+    classes = ['answers-answer']
+    classes.push('answer-checked') if isChecked
+    classes.push('answer-correct') if isCorrect
+    classes = classes.join(' ')
+
+    unless (hasCorrectAnswer or type is 'teacher-review')
+      radioBox = <input
+        type='radio'
+        className='answer-input-box'
+        checked={isChecked}
+        id="#{qid}-option-#{iter}"
+        name="#{qid}-options"
+        onChange={onChangeAnswer(answer)}
+      />
+
+    if type is 'teacher-review'
+      percent = Math.round(answer.selected_count / answered_count * 100) or 0
+      selectedCount =

+

+
+
+

+      {selectedCount}
+      {radioBox}
+
+

+        <ArbitraryHtml className='answer-content' html={answer.content_html} />
+
+

+
+

 module.exports = React.createClass
   displayName: 'Question'
@@ -48,46 +97,21 @@
             block={true}/>

-    answers = _.map @props.model.answers, (answer, i) =>
-      isChecked = answer.id in [@props.answer_id, @state.answer_id]
-      isCorrect = answer.id is @props.correct_answer_id
+    questionAnswerProps =
+      qid: @props.model.id,
+      correct_answer_id: @props.correct_answer_id
+      hasCorrectAnswer: hasCorrectAnswer
+      chosen_answer: [@props.answer_id, @state.answer_id]
+      onChangeAnswer: @onChangeAnswer
+      type: type

-      isCorrect = (answer.correctness is '1.0') if answer.correctness?
-
-      classes = ['answers-answer']
-      classes.push('answer-checked') if isChecked
-      classes.push('answer-correct') if isCorrect
-      classes = classes.join(' ')
-
-      unless (hasCorrectAnswer or type is 'teacher-review')
-        radioBox = <input
-          type='radio'
-          className='answer-input-box'
-          checked={isChecked}
-          id="#{qid}-option-#{i}"
-          name="#{qid}-options"
-          onChange={@onChangeAnswer(answer)}
-        />
-
-      if type is 'teacher-review'
-        percent = Math.round(answer.selected_count / answered_count * 100) or 0
-        selectedCount =

-

-
-
-

-        {selectedCount}
-        {radioBox}
-
-

-          <ArbitraryHtml className='answer-content' html={answer.content_html} />
-
-

+    answers = _.chain(@props.model.answers)
+      .sortBy (answer) ->
+        parseInt(answer.id)
+      .map (answer, i) ->
+        answerProps = _.extend({}, {answer, iter: i}, questionAnswerProps)
+        <Answer {...answerProps}/>
+      .value()

     classes = ['question']
     classes.push('has-correct-answer') if hasCorrectAnswer
@@ -97,7 +121,7 @@
       <ArbitraryHtml className='question-stem' block={true} html={html} />
       {@props.children}

-        {answers.reverse()}
+        {answers}

       {feedback}

{@props.exercise_uid}


remove console.log

remove console.log

--- cleaner.coffee
+++ cleaner.coffee
@@ -3,5 +3,4 @@
 connect = require("mongodb").connect
 connect "mongodb://localhost/cu-test", (err, db) ->
   databaseCleaner.clean db, ->
-    console.log "done"
     db.close()


api.vm: Set the min/max ram values.

api.vm: Set the min/max ram values.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -407,6 +407,15 @@

   resourceSet = xapi.xo.getData(ref, 'resourceSet')

+  if 'memoryMin' of params
+    memoryMin = parseSize(params.memoryMin)
+    yield xapi.call 'VM.set_memory_static_min', ref, "#{memoryMin}"
+    yield xapi.call 'VM.set_memory_dynamic_min', ref, "#{memoryMin}"
+
+  if 'memoryMax' of params
+    memoryMax = parseSize(params.memoryMax)
+    yield xapi.call 'VM.set_memory_static_max', ref, "#{memoryMax}"
+
   # Memory.
   if 'memory' of params
     memory = parseSize(params.memory)
@@ -424,9 +433,7 @@
           "for a running VM"
       )

-    if memory < VM.memory.dynamic[0]
-      yield xapi.call 'VM.set_memory_dynamic_min', ref, "#{memory}"
-    else if memory > VM.memory.static[1]
+    if memory > VM.memory.static[1]
       yield xapi.call 'VM.set_memory_static_max', ref, "#{memory}"
     if resourceSet?
       yield @allocateLimitsInResourceSet({
@@ -519,6 +526,12 @@
   # Note: static_min ≤ dynamic_min ≤ dynamic_max ≤ static_max
   memory: { type: ['integer', 'string'], optional: true }

+  # Set static_min & dynamic_min
+  memoryMin: { type: ['integer', 'string'], optional: true }
+
+  # Set static_max
+  memoryMax: { type: ['integer', 'string'], optional: true }
+
   # Kernel arguments for PV VM.
   PV_args: { type: 'string', optional: true }


Don't invalidate sandbox add-sources when installing more deps

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -12,12 +12,12 @@
   'cabal sandbox add-source ../stack-ide/stack-ide-api'
 ])

-gulp.task 'install-deps', ['create-sandbox'], haskellCmds([
+gulp.task 'install-deps', haskellCmds([
   'cabal update'
   'cabal install --only-dependencies --enable-tests --ghcjs'
 ])

-gulp.task 'configure-haskell', [], haskellCmds([
+gulp.task 'configure-haskell', haskellCmds([
   'cabal configure --enable-tests --ghcjs'
 ])


use async save()

use async save()

--- lib/bridlensis.coffee
+++ lib/bridlensis.coffee
@@ -14,53 +14,52 @@
     scope  = editor.getGrammar().scopeName

     if script? and scope is "source.nsis" or scope is "source.nsis.bridle"
-      editor.save() if editor.isModified()
+      editor.save().then ->
+        bridleJar = atom.config.get("language-bridlensis.pathToJar")
+        defaultArguments = ["-jar", bridleJar]

-      bridleJar = atom.config.get("language-bridlensis.pathToJar")
-      defaultArguments = ["-jar", bridleJar]
+        if atom.config.get("language-bridlensis.customArguments").length > 0
+          customArguments = atom.config.get("language-bridlensis.customArguments").trim().split(" ")
+        else
+          customArguments = []

-      if atom.config.get("language-bridlensis.customArguments").length > 0
-        customArguments = atom.config.get("language-bridlensis.customArguments").trim().split(" ")
-      else
-        customArguments = []
+        if atom.config.get("language-bridlensis.nsisHome").length > 0 and customArguments.indexOf("-n") == -1
+          customArguments.push("-n")
+          customArguments.push(atom.config.get("language-bridlensis.nsisHome"))

-      if atom.config.get("language-bridlensis.nsisHome").length > 0 and customArguments.indexOf("-n") == -1
-        customArguments.push("-n")
-        customArguments.push(atom.config.get("language-bridlensis.nsisHome"))
-
-      customArguments.push(script)
-      args = defaultArguments.concat(customArguments)
-
-      try
-        consolePanel.clear()
-      catch
-        console.clear() if atom.config.get("language-bridlensis.clearConsole")
-
-      # Let's go
-      bridleCmd = spawn "java", args
-      hasError = false
-
-      bridleCmd.stdout.on "data", (data) ->
-        if data.indexOf("Exit Code:") isnt -1
-          hasError = true
+        customArguments.push(script)
+        args = defaultArguments.concat(customArguments)

         try
-          consolePanel.log(data.toString()) if atom.config.get("language-bridlensis.alwaysShowOutput")
+          consolePanel.clear()
         catch
-          console.log(data.toString())
+          console.clear() if atom.config.get("language-bridlensis.clearConsole")

-      bridleCmd.stderr.on "data", (data) ->
-        hasError = true
-        try
-          consolePanel.error(data.toString())
-        catch
-          console.error(data.toString())
+        # Let's go
+        bridleCmd = spawn "java", args
+        hasError = false

-      bridleCmd.on "close", ( errorCode ) ->
-        if errorCode is 0 and hasError is false
-          return notifyOnSucess() if atom.config.get("language-bridlensis.showBuildNotifications")
+        bridleCmd.stdout.on "data", (data) ->
+          if data.indexOf("Exit Code:") isnt -1
+            hasError = true

-        return atom.notifications.addError("Transpile failed", dismissable: false) if atom.config.get("language-bridlensis.showBuildNotifications")
+          try
+            consolePanel.log(data.toString()) if atom.config.get("language-bridlensis.alwaysShowOutput")
+          catch
+            console.log(data.toString())
+
+        bridleCmd.stderr.on "data", (data) ->
+          hasError = true
+          try
+            consolePanel.error(data.toString())
+          catch
+            console.error(data.toString())
+
+        bridleCmd.on "close", ( errorCode ) ->
+          if errorCode is 0 and hasError is false
+            return notifyOnSucess() if atom.config.get("language-bridlensis.showBuildNotifications")
+
+          return atom.notifications.addError("Transpile failed", dismissable: false) if atom.config.get("language-bridlensis.showBuildNotifications")
     else
       # Something went wrong
       atom.beep()


Minor fix.

Minor fix.

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -1006,7 +1006,7 @@
     res.end(format.response(0, vm.$id))
   catch e
     res.writeHead(500)
-    res.end(format.error(new GenericError(e.message)))
+    res.end(format.error(0, new GenericError(e.message)))

   return


update gulpfile

update gulpfile

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -76,6 +76,11 @@
   js_bkgVideo()
   js_sideNav()
   jscoffee()
+
+gulp.task "rev:css", ->
+  return gulp.src "build/css/main.css"
+          .pipe rev()
+          .pipe gulp.dest "build/css"

 gulp.task "newPost", ->
   knownOpts =


Bug fix

Bug fix

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -42,10 +42,10 @@
   return # promises workaround: https://github.com/gulpjs/gulp/issues/455

 gulp.task 'test', ['start-test-server', 'test-node', 'test-browsers-after-node'], ->
-  (require 'test/lib/start_server').server?.close()
+  (require './test/lib/start_server').server?.close()

 gulp.task 'start-test-server', (callback) ->
-  (require 'test/lib/start_server')(callback)
+  (require './test/lib/start_server')(callback)

 gulp.task 'zip', ['minify'], (callback) ->
   gulp.src(['*.js', 'node_modules/backbone-orm/*.js'])


fix css minification

fix css minification

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -63,7 +63,7 @@
     .pipe(stylus({set: ['include css']}))
     .pipe(prefix("last 1 version", "> 1%", "ie 8"))

-  styles.pipe(CSSmin()) if production
+  styles = styles.pipe(CSSmin()) if production

   styles.pipe gulp.dest paths.styles.destination
     .pipe livereload reloadServer


Build

Build

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -57,7 +57,7 @@
   (require './config/karma/run')({tags: tags}, callback)
   return # promises workaround: https://github.com/gulpjs/gulp/issues/455

-gulp.task 'test-node', ['build'], testNode
+gulp.task 'test-node', ['minify'], testNode
 gulp.task 'test-browsers', ['minify'], testBrowsers
 gulp.task 'test', ['minify'], (callback) ->
   Async.series [testNode, testBrowsers], (err) -> if err then process.exit(1) else callback(err)


Tests should be test

Tests should be test

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -5,7 +5,7 @@
 plumber = require "gulp-plumber"
 coffee = require "gulp-coffee"

-g.task "tests", ->
+g.task "test", ->
   g.src(
     "./tests/specs/**/*.coffee"
   ).pipe(
@@ -31,7 +31,7 @@
     coffeelint.reporter "failOnWarning"
   )

-g.task "compile", ["tests"], ->
+g.task "compile", ["test"], ->
   g.src(
     "./src/**/*.coffee"
   ).pipe(


Fixed images path.

Fixed images path.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -16,7 +16,7 @@
 notify      = require 'gulp-notify'

 config = {
-  imagesPattern: 'src/images/**/*.{png,gif,jpg,jpeg}'
+  imagesPattern: './assets/images/**/*.{png,gif,jpg,jpeg}'
   fontsPattern: './assets/bower_components/bootstrap-sass/assets/fonts/**/*.{eot,svg,ttf,woff,woff2,otf}'
 }


Require 'gulp-task-loader'

Require 'gulp-task-loader'

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1 +1,2 @@
-gulp = require 'gulp'+gulp = require 'gulp'
+gulpTaskLoader = require 'gulp-task-loader'


Rename gulp tasks

Rename gulp tasks

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -56,26 +56,27 @@
   runSequence ["generate-swift", "run-swift"]

 gulp.task "build", ->
-  runSequence ["sass", "coffee", "vendorJS"], "lintSass", "jekyll-build", "swift"
+  runSequence ["sass", "coffee", "vendor-js"], "list-scss", "jekyll-build", "swift"

 gulp.task "clean",
   del.bind(null, ["_site"])

-gulp.task "watch", ["sass", "coffee", "jekyll-serve"], ->
+gulp.task "watch", ["sass", "coffee", "jekyll-build-local"], ->
   gulp.watch "#{paths.sass}/**/*.scss", ["sass"]
   gulp.watch "#{paths.coffee}/**/*.coffee", ["coffee"]
-  gulp.watch "#{paths.coffee}/vendor.js", ["vendorJS"]
-  gulp.watch paths.jekyllFiles, ["jekyll-rebuild"]
+  gulp.watch "#{paths.coffee}/vendor.js", ["vendor-js"]
+  gulp.watch paths.jekyllFiles, ["jekyll-reload"]

-gulp.task "jekyll-serve", ->
+gulp.task "jekyll-build-local", ->
   run "bundle exec jekyll build --config _config.yml,_config.serve.yml", silent: true
+    .exec()
   browserSync.notify messages.jekyllBuild

 gulp.task "jekyll-build", ->
   run "bundle exec jekyll build", silent: true
     .exec()

-gulp.task "jekyll-rebuild", ["jekyll-serve"], ->
+gulp.task "jekyll-reload", ["jekyll-build-local"], ->
   browserSync.reload()

 gulp.task "doctor", ->
@@ -95,7 +96,7 @@
     .pipe gulp.dest(paths.styles)
     .pipe browserSync.reload(stream: true)

-gulp.task "lintSass", ->
+gulp.task "list-scss", ->
   gulp.src "#{paths.sass}/*.scss"
     .pipe cache paths.sass
     .pipe scssLint
@@ -115,7 +116,7 @@
     .pipe gulp.dest(paths.scripts)
     .pipe browserSync.reload(stream: true)

-gulp.task "vendorJS", ->
+gulp.task "vendor-js", ->
   gulp.src "#{paths.coffee}/vendor.js"
     .pipe include()
     .on "error", (error) -> gutil.log(error.message)


fix method for creating plan

fix method for creating plan

--- tutor/src/api/index.coffee
+++ tutor/src/api/index.coffee
@@ -57,8 +57,11 @@

   connectUpdate(TaskPlanActions, {data: TaskPlanStore.getChanged},
     (id, courseId) ->
-      url = if TaskPlanStore.isNew(id) then "courses/#{courseId}/plans" else "plans/#{id}"
-      {url}
+      if TaskPlanStore.isNew(id)
+        url: "courses/#{courseId}/plans"
+        method: 'POST'
+      else
+        url: "plans/#{id}"
   )

   connectRead(TaskPlanStatsActions, pattern: 'plans/{id}/stats')


Fixed UTF-8 issue

Fixed UTF-8 issue

--- node_modules_koding/koding-payment/lib/main.coffee
+++ node_modules_koding/koding-payment/lib/main.coffee
@@ -1,19 +1,10 @@
 xml2js   = require 'xml2js'
 https    = require 'https'

-punycode = require 'punycode'
-
 escape = (text)->
-  if typeof text is 'string'
-    punycode.encode text
-  else
-    ''
-
+  text
 unescape = (text)->
-  if typeof text is 'string'
-    punycode.decode text
-  else
-    ''
+  text

 createId = require 'hat'
 getUniqueId = -> createId 64
@@ -68,7 +59,7 @@
     auth     : API_KEY + ':'
     headers  :
       Accept           : 'application/xml'
-      'Content-Length' : data.length
+      'Content-Length' : Buffer.byteLength data, 'utf8'

   if data.length > 0
     options.headers['Content-Type'] = 'application/xml; charset=UTF-8'


Allow setting of app port from the command line. Fix #13

--- bin/www.coffee
+++ bin/www.coffee
@@ -23,8 +23,7 @@
 ###
   Get port from environment and store in Express.
 ###
-
-port = normalizePort process.env.PORT or '3000'
+port = normalizePort (process.argv.length > 2 and process.argv[2]) or process.env.PORT or '3000'
 app.set 'port', port

 ###


Grouped some lines together

Grouped some lines together

--- core/app/backbone/modules/notification_center.coffee
+++ core/app/backbone/modules/notification_center.coffee
@@ -16,14 +16,11 @@

   class NotificationCenter.Alert extends Backbone.Model
   class NotificationCenter.Alerts extends Backbone.Collection
+  window.alerts = new NotificationCenter.Alerts []

   FactlinkApp.addRegions
     alertsRegion: "#alerts"
-
-  window.alerts = new NotificationCenter.Alerts []
-  alertsView = new NotificationCenter.AlertsView collection: alerts
-
-  FactlinkApp.alertsRegion.show alertsView
+  FactlinkApp.alertsRegion.show new NotificationCenter.AlertsView collection: alerts

   NotificationCenter.info    = -> alerts.add new NotificationCenter.Alert message: message, type: 'info'
   NotificationCenter.success = -> alerts.add new NotificationCenter.Alert message: message, type: 'success'


Make link to proxy urls open in new window

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -67,6 +67,7 @@
         model: user
         time: @model().get('created_at')
         href: fact.get('proxy_open_url')
+        target: '_blank'
         activity_header_action: [
           _span ["feed-activity-description"],
             "commented on"
@@ -89,6 +90,7 @@
         model: user
         time: @model().get('created_at')
         href: fact.get('proxy_open_url')
+        target: '_blank'
         activity_header_action: [
           _span ["feed-activity-description"],
             "replied to"
@@ -135,7 +137,7 @@
         _a [href: user.link(), rel:"backbone"],
           _img ["feed-activity-user-avatar", alt:" ", src: user.avatar_url(48)]

-      _a ["feed-activity-container", href: @props.href, rel: 'backbone'],
+      _a ["feed-activity-container", href: @props.href, rel: 'backbone', target: @props.target],
         _div ["feed-activity-heading"],
           _div ["feed-activity-action"],
             _div ["feed-activity-time"],


Removed unneeded div

Removed unneeded div

--- core/app/backbone/views/react_feed_activities.coffee
+++ core/app/backbone/views/react_feed_activities.coffee
@@ -30,9 +30,8 @@

   render: ->
     _div [],
-      _div [],
-        @model().map (model) =>
-          ReactActivity model: model, key: model.id
+      @model().map (model) =>
+        ReactActivity model: model, key: model.id
       ReactLoadingIndicator model: @model()

 window.ReactFeedActivitiesFixed = React.createBackboneClass


Rename and hide links working

Rename and hide links working

--- client/code/view/view/tile.coffee
+++ client/code/view/view/tile.coffee
@@ -5,7 +5,9 @@
     href: "/dataset/#{@model.get('plugsInTo').get('box')}/view/#{@model.get 'box'}"

   events:
-    'click .hide': 'hideView'
+    'click .hide-view': 'hideView'
+    'click .dropdown-menu a': 'dropdownMenuItemClick'
+    'click .rename-view': 'renameViewClick'

   initialize: ->
     @model.on 'change', @render, this
@@ -22,12 +24,24 @@
       @$el.addClass 'source'
     @

-  hideView: (e) ->
+  dropdownMenuItemClick: (e) ->
     e.preventDefault()
     e.stopPropagation()
+
+  hideView: (e) ->
     @$el.slideUp()
     @model.set 'state', 'deleted'
     @model.get('plugsInTo').save {},
       error: (e) =>
         @$el.slideDown()
         console.warn 'View could not be deleted!'
+
+  renameViewClick: ->
+    # This is a bit of a hack, to avoid writing yet another rename widget.
+    # Hopefully it'll also teach people they can directly edit dataset names.
+    window.app.navigate "/dataset/#{@model.get('plugsInTo').get('box')}/view/#{@model.get('box')}", trigger: true
+    setTimeout ->
+      $('#subnav-path .editable').trigger('click')
+    , 300
+
+


Google API Client: Prevent initializing more than once

--- client/ide/GoogleApiClient.coffee
+++ client/ide/GoogleApiClient.coffee
@@ -68,6 +68,8 @@
       GoogleApiClient.readyState = yes
       GoogleApiClient.emit 'ready'

+    return window.handleGoogleApiLoaded()  if window.gapi
+
     identifier = 'google-api-client'
     url = 'https://apis.google.com/js/client.js?onload=handleGoogleApiLoaded'
     KodingAppsController.appendHeadElement 'script', {identifier, url}


Accept an explicit Repository object

Accept an explicit Repository object

--- lib/git-bridge.coffee
+++ lib/git-bridge.coffee
@@ -82,15 +82,6 @@
       repo = atom.project.getRepositories()[0]
     return repo

-  # Public: Return a filepath relative to the git repository that contains it.
-  #
-  # * `filepath` {String} Absolute path to the file.
-  #
-  # Returns the relative path as a {String}, or null if the path isn't within a known repository.
-  #
-  @repoRelativePath: (filepath) ->
-    @getActiveRepo(filepath)?.relativize filepath
-
   @_repoWorkDir: (filepath) -> @getActiveRepo(filepath).getWorkingDirectory()

   @_repoGitDir: (filepath) -> @getActiveRepo(filepath).getPath()
@@ -114,7 +105,7 @@

     return true

-  @withConflicts: (handler) ->
+  @withConflicts: (repo, handler) ->
     return unless @_checkHealth(handler)

     conflicts = []
@@ -140,7 +131,7 @@
     proc = @process({
       command: GitCmd,
       args: ['status', '--porcelain'],
-      options: { cwd: @_repoWorkDir() },
+      options: { cwd: repo.getWorkingDirectory() },
       stdout: stdoutHandler,
       stderr: stderrHandler,
       exit: exitHandler


Clears jumpy on mouse clicks.

Clears jumpy on mouse clicks.

- Closes #16.

--- lib/jumpy-view.coffee
+++ lib/jumpy-view.coffee
@@ -97,6 +97,9 @@
             activePane.find '.vertical-scrollbar'
                 .on 'scroll', (e) ->
                     that.clear()
+            atom.workspaceView.find '*'
+                .on 'click', (e) ->
+                    that.clear()

             isScreenRowVisible = (lineNumber) ->
                 return lineNumber > editorView.getFirstVisibleScreenRow() &&


#611 clipToMutationEnd when occurrence intersects original cursor position.

Partially fixed, but `d o j` case still issue since `d` doing cursor
restoration badly

--- spec/occurrence-spec.coffee
+++ spec/occurrence-spec.coffee
@@ -136,6 +136,34 @@
           OOO: xxx: |||: xxx: OOO:
           XXX: |||: OOO: OOO:
           """
+
+      describe "clip to mutation end behavior", ->
+        beforeEach ->
+          set
+            textC: """
+
+            oo|o:xxx:ooo:
+            xxx:ooo:xxx
+            \n
+            """
+        it "delete occurrence and cursor is at mutation end", ->
+          ensure "d o p",
+            textC: """
+
+            |:xxx::
+            xxx::xxx
+            \n
+            """
+        it "not clip if original cursor not intersects any occurence-marker", ->
+          ensure 'g o', occurrenceText: ['ooo', 'ooo', 'ooo'], cursor: [1, 2]
+          keystroke 'j', cursor: [2, 2]
+          ensure "d p",
+            textC: """
+
+            :xxx::
+            xx|x::xxx
+            \n
+            """

     describe "auto extend target range to include occurrence", ->
       textOriginal = "This text have 3 instance of 'text' in the whole text.\n"


Fixed up some spec stuff

Fixed up some spec stuff

--- spec/timekeeper-spec.coffee
+++ spec/timekeeper-spec.coffee
@@ -26,6 +26,23 @@

         # Setup the activation for the package
         activationPromise = atom.packages.activatePackage( "timekeeper" )
+
+        # Setup the current datetime object
+        currentDate = new Date()
+
+        # Setup the file path that will save time data for this spec run
+        saveFilePath = path.join(
+                            "#{atom.getConfigDirPath()}/.timekeeper",
+                            new Buffer( atom.project.getPath(), "utf8" ).toString( "base64" ),
+                            currentDate.getFullYear().toString(),
+                            ( currentDate.getMonth() + 1 ).toString(),
+                            currentDate.getDate() + ".json"
+                        )
+
+        # Check if we have a file from any previous runs, if yes, let us remove it
+        if fs.existsSync( saveFilePath )
+            # Remove the file, we don't want old data
+            fs.unlinkSync( saveFilePath )

         # Create the notepads object
         @timer = new Timer()
@@ -350,7 +367,7 @@
     describe "Timekeeper Abort", ->
         ### TEST ###
         # Test timerkeeper abort & resets everything on calling abort()
-        it "resets all data on calling reset() & is not actively tracking time", ->
+        it "discards all time tracking data on calling abort() & stops tracking time", ->
             # Wait for package to be activated and functional
             waitsForPromise =>
                 # Waits for the activation
@@ -364,18 +381,18 @@
                     @timer.start()

                 # Wait for the time to pass
-                waits( 5000 )
+                waits( 4000 )

                 # Verify that time tracking is running
                 runs =>
                     # Start timestamp and click should not be defaults
                     expect( @timer.startTimestamp ).not.toBeNull()
-                    expect( @timer.clock ).toBeGreaterThan( 3 )
+                    expect( @timer.clock ).toBeGreaterThan( 2 )

                     # Now let us issue an abort
-                    waitsFor =>
-                        # Call timer abort
-                        @timer.abort()
+                    #waitsFor =>
+                    # Call timer abort
+                    @timer.abort()

                     # We have effectively stopped time tracking so nothing should be going on now
                     runs =>
@@ -386,11 +403,11 @@
                         # Timer clock view should also be back to default
                         expect( atom.workspaceView.find( "#clock" ).html() ).toEqual( "00:00:00" )

-    ### TIMEKEEPER STOP ###
-    describe "Timekeeper Stop", ->
+    ### TIMEKEEPER FINISH ###
+    describe "Timekeeper Finish", ->
         ### TEST ###
         # Test timerkeeper saves time tracking data on calling start
-        it "saves tracked time data on calling stop()", ->
+        it "saves tracked time data on calling finish()", ->
             # Wait for package to be activated and functional
             waitsForPromise =>
                 # Waits for the activation
@@ -404,7 +421,7 @@
                     @timer.start()

                 # Wait for the time to pass
-                waits( 5000 )
+                waits( 1000 )

                 # Verify that time tracking is running
                 runs =>
@@ -412,12 +429,9 @@
                     @timer.previousStartTimestamp = @timer.startTimestamp

                     # Now let us finish up
-                    waitsFor =>
-                        # Call timer finish
-                        @timer.finish()
-
-                    # Wait a bit for it process all the data
-                    waits( 2000 )
+                    #waitsFor =>
+                    # Call timer finish
+                    @timer.finish()

                     # We have effectively stopped time tracking so nothing should be going on now
                     runs =>


commentrs in discussions, adjusted the models for discussion/tutorials, WIP for popup tutorial vids

--- components/inputs/KDInputViewWithPreview.coffee
+++ components/inputs/KDInputViewWithPreview.coffee
@@ -181,8 +181,6 @@
   setDomElement:(CssClass="")->
     super CssClass

-    # basically, here we can add aynthing like modals, overlays and the liek
-
     @$().after """

@@ -190,6 +188,8 @@

   viewAppended:->
     super
+
+    @$("div.preview_content").addClass("has-"+@options.preview.language)

     unless @showPreview
       @$("div.preview_content").addClass "hidden"
@@ -203,7 +203,10 @@
     @$("label").on "click",=>
       @$("input.checkbox").get(0).click()

-    @$("div.preview_content").addClass("has-"+@options.preview.language)
+
+    # hotfix for random display:none that can sometimes come up
+    @utils.wait  =>
+      @$("span.data").css display:"block"

     if @options.preview.mirrorScroll then @$().scroll (event)=>
       @setPreviewScrollPercentage @getEditScrollPercentage()


Requisite API changed.

Requisite API changed.

--- src/bundle.coffee
+++ src/bundle.coffee
@@ -54,7 +54,7 @@
     opts.before = (join base, src for src in @before or [])

     # Create bundler
-    requisite = require('requisite').createBundler opts
+    requisite = require('requisite')(opts)

     # Return compiler for middleware
     compiler = (cb) ->


#743 unfocus spec

#743 unfocus spec

--- spec/operator-activate-insert-mode-spec.coffee
+++ spec/operator-activate-insert-mode-spec.coffee
@@ -246,7 +246,7 @@
             3!!!!!!\n
             """

-  fdescribe "dontUpdateRegisterOnChangeOrSubstitute settings", ->
+  describe "dontUpdateRegisterOnChangeOrSubstitute settings", ->
     resultTextC = null
     beforeEach ->
       set


Add `bail` option to Webpack config to ensure Webpack errors fail CI builds.

--- build/config/webpack.config.coffee
+++ build/config/webpack.config.coffee
@@ -14,6 +14,11 @@
 # Export our Webpack configuration.
 module.exports =

+  # Disable error tolerance and cause Webpack to exit with a non-zero exit code
+  # on the first error. This is especially useful with CI systems which monitor
+  # exit codes to valid builds, etc.
+  # @see https://webpack.github.io/docs/configuration.html#bail
+  bail: true

   # The base directory for resolving the entry option.
   context: "#{__dirname}/../../"


Ensure proper handling of titles

Ensure proper handling of titles

--- scripts/models/content/inherits/container.coffee
+++ scripts/models/content/inherits/container.coffee
@@ -31,20 +31,28 @@
         return model.id is obj.id or model.cid is obj.id

     getTitle: (model) ->
-      return @findMatch(model)?.title
+      if model.unique
+        return model.get('title')
+
+      return @findMatch(model)?.title or model.get('title')

     setTitle: (model, title) ->
-      match = @findMatch(model)
+      if model.unique
+        model.set('title', title)
+      else
+        match = @findMatch(model)

-      if match
-        match.title = title
-      else
-        @titles.push
-          id: model.id or model.cid
-          mediaType: model.mediaType
-          title: title
+        if match
+          match.title = title
+        else
+          @titles.push
+            id: model.id or model.cid
+            mediaType: model.mediaType
+            title: title

-      model.trigger('change')
+        model.trigger('change')
+
+      return @

   return BaseModel.extend
     mediaType: 'application/vnd.org.cnx.folder'
@@ -111,13 +119,10 @@

       options = options || {}
       contents = attrs.contents or attrs.body
-
+
       if contents
         if not _.isArray(contents)
           contents = parseHTML(contents)
-
-        # If a medium should be unique, don't remember its title
-

         attrs.contents = @get('contents') or new Container()
         attrs.contents.titles = contents


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -36,7 +36,7 @@
       height: 2 * deadzone.y

     if objectCenter.x > centerRect.right
-      I.scroll.x = I.scroll.x.approach(centerI.maxVelocity.x)
+      I.scroll.x = I.scroll.x.approach(centerRect.RiI.maxVelocity.x)
     else if objectCenter.x < centerRect.left


Fix sendRPC error, add a send command

Fix sendRPC error, add a send command

--- src/client.coffee
+++ src/client.coffee
@@ -49,17 +49,15 @@
 		if (data.messages)
 			@messageHandler msg for msg in data.messages
 			return
-		console.log(data)
 		if (data.type == 'rpc')
 			@rpcHandlers[data.id]?(data.result)
 			delete @rpcHandlers[data.id]

 	messageHandler: (msg)->
 		switch msg.type
-			when 'ping' then sendRPC 'pong'
+			when 'ping' then @sendRPC 'pong'
 			else
 				console.log('Got this message', msg)
-

 	sendRPC: (name, args, callback)->
 		if (args == undefined)
@@ -73,6 +71,9 @@
 			id: rpcId
 			name: 'room.join'
 			args: args
+
+	send: (data)->
+		client.send data

 	joinRoom: (name, callback)->
 		@sendRPC('room.join', [name], callback)


Saving form changes the origDoc reference. Removed fields from modifiers which haven't been changed. Fixed bug causing form reset on save. Removed unnecessary logs.

--- src/Forms.coffee
+++ src/Forms.coffee
@@ -18,7 +18,6 @@
       onSubmit: (insertDoc, updateDoc, currentDoc) ->
         args = arguments
         template = getTemplate(@template)
-        console.debug 'onSubmit', args, @
         onSubmit = formArgs.onSubmit ? formArgs.hooks?.onSubmit
         result = onSubmit?.apply(@, args)
         callback = => template.settings.onSubmit?.apply(@, args)
@@ -32,12 +31,24 @@
       onSuccess: (operation, result) ->
         args = arguments
         template = getTemplate(@template)
-        console.debug 'onSuccess', args, @
-        AutoForm.resetForm(name)
+        Form.updateDocs(template)
+        Form.setUpDocs(template)
         onSuccess = formArgs.onSuccess ? formArgs.hooks?.onSuccess
         result = onSuccess?.apply(@, args)
         callback = => template.settings.onSuccess?.apply(@, args)
         deferCallback(result, callback)
+
+      before:
+        # Remove fields in the modifiers which haven't been changed.
+        update: (modifier) ->
+          $input = $(@autoSaveChangedElement)
+          changes = Form.getDocChanges(@template)
+          _.each ['$set', '$unset'], (propName) ->
+            fields = modifier[propName]
+            if fields?
+              _.each fields, (value, key) ->
+                unless changes[key]? then delete fields[key]
+          modifier

       onError: (operation, error) ->
         template = getTemplate(@template)
@@ -73,7 +84,7 @@
       formName: -> name
       # Without this a separate copy is passed across, which doesn't allow sharing data between
       # create method and form hooks.
-      doc: -> Form.getValues()
+      doc: -> Tracker.nonreactive -> Form.getValues()
       formTitle: -> Form.getFormTitle()
       formType: ->
         return if Form.isBulk()
@@ -85,7 +96,7 @@
       hasDoc: -> Form.hasDoc()
       isBulk: -> Form.isBulk()
       autosave: -> formArgs.autosave
-      resetOnSuccess: -> formArgs.resetOnSuccess
+      resetOnSuccess: -> formArgs.resetOnSuccess ? false

     ################################################################################################
     # EVENTS
@@ -94,7 +105,6 @@
     Form.events
       'click button.cancel': (e, template) ->
         e.preventDefault()
-        console.debug 'onCancel', arguments, @
         formArgs.onCancel?(template)
         formTemplate = getTemplate(template)
         formTemplate.settings.onCancel?()
@@ -105,14 +115,11 @@

     Form.created = ->
       @settings = @data.settings ? {}
-      @docs = Form.parseDocs()
-      @data.doc = Form.getValues()
-      @origDoc = Setter.clone(@data.doc)
+      Form.setUpDocs(@)
       @isSubmitting = false
       formArgs.onCreate?.apply(@, arguments)

     Form.rendered = ->
-      console.debug 'Rendered form', @, arguments
       # Move the buttons to the same level as the title and content to allow using flex-layout.
       $buttons = @$('.crud.buttons')
       $crudForm = @$('.flex-panel:first')
@@ -189,8 +196,8 @@
       # if Form.isBulk()
       #   Form.setUpBulkFields()

-      # if Form.isReactive()
-      #   Form.setUpReactivity()
+      if Form.isReactive()
+        Form.setUpReactivity()

       if @data.docPromise?
         Q.when(@data.docPromise).then => Form.mergeLatestDoc(@)
@@ -198,7 +205,6 @@
       formArgs.onRender?.apply(@, arguments)

     Form.destroyed = ->
-      console.debug 'Destroyed form', @, arguments
       template = @
       template.isDestroyed = true
       formArgs.onDestroy?.apply(@, arguments)
@@ -352,6 +358,22 @@
     # AUXILIARY
     ################################################################################################

+    Form.setUpDocs = (template) ->
+      template = getTemplate(template)
+      data = template.data
+      template.docs = Form.parseDocs(template)
+      data.doc = Form.getValues(template)
+      template.origDoc = Setter.clone(data.doc)
+
+    Form.updateDocs = (template) ->
+      template = getTemplate(template)
+      data = template.data
+      docs = _.map template.docs, (doc) -> Form.getCollection().findOne(doc._id)
+      template.docs = docs
+      data.doc = Form.getValues(template)
+      if data.docs?
+        data.docs = docs
+
     Form.parseDocs = (template) ->
       template = getTemplate(template)
       data = template.data


Fix CM/ShareJS adapter using old CM api

Fix CM/ShareJS adapter using old CM api

Fixes bug where deleting text in CM would throw an error in some
circumstances

--- services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
+++ services/web/public/coffee/ide/editor/sharejs/vendor/client/cm.coffee
@@ -12,21 +12,10 @@
   while i < delta.from.line
     startPos += editorDoc.lineInfo(i).text.length + 1   # Add 1 for '\n'
     i++
-
   startPos += delta.from.ch

-  if delta.to.line == delta.from.line &&
-     delta.to.ch == delta.from.ch # Then nothing was removed.
-    doc.insert startPos, delta.text.join '\n'
-  else
-    delLen = delta.to.ch - delta.from.ch
-    while i < delta.to.line
-      delLen += editorDoc.lineInfo(i).text.length + 1   # Add 1 for '\n'
-      i++
-    doc.del startPos, delLen
-    doc.insert startPos, delta.text.join '\n' if delta.text
-
-  applyCMToShareJS editorDoc, delta.next, doc if delta.next
+  doc.del startPos, delta.removed.join('\n').length if delta.removed
+  doc.insert startPos, delta.text.join('\n') if delta.text

 # Attach a CodeMirror editor to the document. The editor's contents are replaced
 # with the document's contents unless keepEditorContents is true. (In which case


filter discarded discussions from inbox

filter discarded discussions from inbox

--- vue/src/shared/services/thread_filter.coffee
+++ vue/src/shared/services/thread_filter.coffee
@@ -4,6 +4,7 @@

 export default (store, options) ->
   chain = store.discussions.collection.chain()
+  chain = chain.find(discardedAt: null)
   chain = chain.find(groupId: { $in: options.group.organisationIds() })      if options.group
   chain = chain.find(lastActivityAt: { $gt: options.from }) if options.from
   chain = chain.find(lastActivityAt: { $lt: options.to })   if options.to


Draw 2 elem clusters

Draw 2 elem clusters

--- web/source/javascripts/angular-app.js.coffee
+++ web/source/javascripts/angular-app.js.coffee
@@ -214,7 +214,10 @@
         hulls.selectAll('path.hull')
           .attr "d", (cluster) ->
             articles = _.at(nodes, cluster.members)
-            hullArticles = hullGeom(articles).reverse()
+            hullArticles = if articles.length > 2
+              hullGeom(articles).reverse()
+            else
+              articles
             polygon = ([a.x, a.y] for a in hullArticles)
             offsetPoly = polygonOffset(6, polygon)
             offsetPoly.forEach (p) ->
@@ -224,7 +227,10 @@
         hulls.selectAll('path.description-anchor-path')
           .attr "d", (cluster) ->
             articles = _.at(nodes, cluster.members)
-            hullArticles = hullGeom(articles).reverse()
+            hullArticles = if articles.length > 2
+              hullGeom(articles).reverse()
+            else
+              articles
             polygon = ([a.x, a.y] for a in hullArticles)
             offsetPoly = polygonOffset(8, polygon)
             offsetPoly.forEach (p) ->


Update to point to new home

Update to point to new home

--- assets/javascripts/mainMenu.coffee
+++ assets/javascripts/mainMenu.coffee
@@ -317,7 +317,7 @@
     @addInstructionText 'Credits', 400, 50, true

     @addCreditsText 'Copyright (c) 2014 Bryan Bibat.', 50, 100, "http://bryanbibat.net"
-    @addCreditsText 'Made with Phaser 2.0.7', 375, 100, "http://phaser.io"
+    @addCreditsText 'Made with Phaser 2.2.0', 375, 100, "http://phaser.io"
     @addCreditsText 'Sprites (c) 2002 Ari Feldman,', 50, 135, "www.widgetworx.com/spritelib/"
     @addCreditsText 'Sounds (c) 2013 dklon (Devin Watson),', 350, 135, "http://opengameart.org/users/dklon"
     @addCreditsText '"Airborne" font (c) 2005 Charles Casimiro Design,', 50, 170, "http://charlescasimiro.com/airborne.html"
@@ -329,7 +329,7 @@
     book = @addTypewriterText "Read a book on how to make", 570, 280, 22, 'both', "https://leanpub.com/html5shootemupinanafternoon"
     book2 = @addTypewriterText "HTML5 games like this:", 570, 305, 22, 'both', "https://leanpub.com/html5shootemupinanafternoon"

-    url = encodeURIComponent("http://datenshizero.github.io/gunner/")
+    url = encodeURIComponent("http://games.bryanbibat.net/habagat/")
     ogUrl = encodeURIComponent("<%= image_path 'opengraph.png' %>")
     text = encodeURIComponent("Play Gunner, a WWII themed HTML5 game where you destroy waves of enemy planes and ships")
     fullUrl = "https://twitter.com/intent/tweet?url=#{url}&text=#{text}"


Improve ipkg parser

Improve ipkg parser

Allow - and _ to be part of the package name in pkgs fields.
Fixes #242

--- lib/utils/ipkg.coffee
+++ lib/utils/ipkg.coffee
@@ -4,7 +4,7 @@

 optionsRegexp = /opts\s*=\s*\"([^\"]*)\"/
 sourcedirRegexp = /sourcedir\s*=\s*([a-zA-Z/0-9.]+)/
-pkgsRegexp = /pkgs\s*=\s*(([a-zA-Z/0-9., ]+\s{0,1})*)/
+pkgsRegexp = /pkgs\s*=\s*(([a-zA-Z/0-9., -_]+\s{0,1})*)/

 # Find all ipkg-files in a directory and returns
 # an observable of an array of files


Show breadcrumbs on all tasks

Show breadcrumbs on all tasks

--- src/components/task/index.cjsx
+++ src/components/task/index.cjsx
@@ -222,12 +222,11 @@
     taskClasses += " task-#{panelType}" if panelType?
     taskClasses += ' task-completed' if TaskStore.isTaskCompleted(id)

-    unless TaskStore.isSingleStepped(id)
-      breadcrumbs = <Breadcrumbs
-        id={id}
-        goToStep={@goToStep}
-        currentStep={@state.currentStep}
-        key="task-#{id}-breadcrumbs"/>
+    breadcrumbs = <Breadcrumbs
+      id={id}
+      goToStep={@goToStep}
+      currentStep={@state.currentStep}
+      key="task-#{id}-breadcrumbs"/>

     <PinnedHeaderFooterCard
       forceShy={true}


fix: determine sso login by method field not url

--- src/app/sessions/states/sign-in/sign-in.coffee
+++ src/app/sessions/states/sign-in/sign-in.coffee
@@ -37,15 +37,16 @@
   timeoutPromise = $timeout (-> $scope.waitingAWhile = true), 1500
   $http.get("#{DoubtfireConstants.API_URL}/auth/method").then ((response) ->

-    $scope.aafLogin = response.data.redirect_to || response.data || false
+    $scope.SSOLogin = response.data.method == "aaf" || response.data.method == "saml"

-    if $scope.aafLogin
+    if $scope.SSOLogin
+      $scope.SSOLoginUrl = response.data.redirect_to || response.data
       if $stateParams.authToken
         # This is AAF and we just got an auth_token? Must request to sign in
         $scope.signIn({ auth_token: $stateParams.authToken })
       else
         # We are AAF and no auth token so we can must redirect to AAF login provider
-        window.location.assign($scope.aafLogin)
+        window.location.assign($scope.SSOLoginUrl)
     else
       $scope.authMethodLoaded = true
       $timeout.cancel(timeoutPromise)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -2,8 +2,8 @@
 Returns a copy of the array without null and undefined values.

-   [1, 1, 1, 3, 4, 5].compact
-=> [1, 3, 4, 5]
+   [null, undefined, 3, 3, undefined, 5].compact()
+=> [3, 3, 5]


Muda tipo de client para cliente

Muda tipo de client para cliente

--- Operador-Interno/server/methods/cliente.coffee
+++ Operador-Interno/server/methods/cliente.coffee
@@ -12,7 +12,7 @@
 		Meteor.users.insert
 			_id: data.cpf
 			username: data.cpf
-			role: 'client'
+			role: 'cliente'
 			profile:
 				nome: data.nome


activity: remove message from pane by data value

--- client/Social/Activity/views/messagepane.coffee
+++ client/Social/Activity/views/messagepane.coffee
@@ -36,7 +36,7 @@

   removeMessage: (message) ->

-    @listController.removeItem message
+    @listController.removeItem null, message

   appendMessage: (message) ->


KiteSubscription: modify kite modal variable names.

--- client/Social/Apps/Views/createkitemodal.coffee
+++ client/Social/Apps/Views/createkitemodal.coffee
@@ -2,7 +2,7 @@

   constructor: (options = {}, data) ->

-    options.title             = "Create new Kite"
+    options.title             = "Create New Kite"
     options.overlay           = yes
     options.content           = ""
     options.cssClass          = "create-kite-modal"
@@ -103,16 +103,16 @@
     options.fields        =
       planId              :
         label             : "Plan Id"
-        name              : "planId"
+        name              : "userTag"
         cssClass          : "thin half"
         nextElement       :
           planName        :
             label         : "Plan Name"
-            name          : "planName"
+            name          : "title"
             cssClass      : "thin half"
       planprice           :
         label             : "Plan Price"
-        name              : "planPrice"
+        name              : "feeAmount"
         cssClass          : "thin half"
         nextElement       :
           planRecurring   :
@@ -129,7 +129,7 @@
             ]
       planDescription     :
         label             : "Plan Description"
-        name              : "planDescription"
+        name              : "description"
         type              : "textarea"

-    super options, data+    super options, data


payment: expose group plan fetch to client

--- client/app/lib/payment/paymentcontroller.coffee
+++ client/app/lib/payment/paymentcontroller.coffee
@@ -65,6 +65,11 @@
     @api().logOrder params, callback

+  fetchGroupPlan: (callback) ->
+
+    @api().fetchGroupPlan callback
+
+
   paypalReturn: (err) -> @emit 'PaypalRequestFinished', err
   paypalCancel: ->


ManagedHelpers: updateMachineData: to update queryString and ipAddress of a managed VM

--- client/app/lib/providers/managed/helpers.coffee
+++ client/app/lib/providers/managed/helpers.coffee
@@ -47,3 +47,15 @@
       ipAddress   : kite.ipAddress
       stack
     }, callback
+
+
+  updateMachineData: ({machine, kite}, callback)->
+
+    options       =
+      machineId   : machine._id
+      provider    : 'managed'
+      queryString : kite.queryString
+      ipAddress   : kite.ipAddress
+
+    remote = require('app/remote').getInstance()
+    remote.api.ComputeProvider.update options, callback


video/helper: add unsubscribeFromAudioChanges

--- client/app/lib/videocollaboration/helper.coffee
+++ client/app/lib/videocollaboration/helper.coffee
@@ -251,6 +251,14 @@
     else if activity and now - activity.timestamp > 1000
       callbacks.stopped()  if activity.talking
       activity = null
+
+
+###*
+ *
+ *
+ * @param {ParticipantType.Participant} participant
+###
+unsubscribeFromAudioChanges = (participant) -> participant.off 'audioLevelUpdated'

 ###*
@@ -391,6 +399,7 @@
   subscribeToStream
   createPublisher
   subscribeToAudioChanges
+  unsubscribeFromAudioChanges
   enableVideo
   disableVideo
   isVideoActive


pricing: revert subscription tag selector

--- client/Pricing/views/teamplan.coffee
+++ client/Pricing/views/teamplan.coffee
@@ -4,7 +4,7 @@
     @buyNow.showLoader()
     {paymentController, router} = KD.singletons

-    paymentController.fetchActiveSubscription ["team-plan"], (err, subscription) =>
+    paymentController.fetchActiveSubscription ["custom-plan"], (err, subscription) =>
       return KD.showError err  if err and err.code isnt "no subscription"
       size  = @plans[@planIndex].size
       @emit "CurrentSubscriptionSet", subscription  if subscription


return undefined lest xdm return our entire $.ajax object!

--- client/code/view/tool/content.coffee
+++ client/code/view/tool/content.coffee
@@ -109,6 +109,7 @@
             error: ->
               if typeof error is 'function'
                 error()
+          undefined # required to stop easyXDM calling the success callback
         reportingTag: (tagname, success, error) ->
           $.ajax
             type: 'POST'
@@ -121,6 +122,7 @@
             error: ->
               if typeof error is 'function'
                 error()
+          undefined # required to stop easyXDM calling the success callback

 class Cu.View.AppContent extends Cu.View.ToolContent


instructions book: fix vm context menu position

--- client/landing/app/MainApp/book/bookview.coffee
+++ client/landing/app/MainApp/book/bookview.coffee
@@ -332,11 +332,18 @@
       # click animation
       @clickAnimation()
       @utils.wait 1000, =>
+        chevron = @defaultVm.$ '.chevron'
         # open fileTree menu
-        @defaultVm.$('.chevron').click()
-        $('.jcontextmenu').offset(@defaultVm.$('.chevron').offset())
-        # destroy pointer
-        @destroyPointer()
+        chevron.click()
+        contextMenu = $ '.jcontextmenu'
+        contextMenu.addClass "hidden"
+
+        @utils.wait 500, =>
+          contextMenu.offset chevron.offset()
+          contextMenu.removeClass "hidden"
+
+          # destroy pointer
+          @destroyPointer()

     user = KD.nick()
     defaultVMName = KD.singletons.vmController.defaultVmName


Adding expression to deps for marking origin of deferredly typed dereferences

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1365,7 +1365,7 @@
       unresolvedNames = newMap()
       for name, binding of values bindings
         if canonicalType = ctx.finalType name
-          # log "already resolved", name, group
+          # log "already resolved", name, group, binding
           for type in binding.types
             # log "unify", (printType type.type), (printType canonicalType)
             unify ctx, type.type, (freshInstance ctx, canonicalType).type
@@ -1393,6 +1393,7 @@
           if ctx.isInTopScope()
             # TODO: error missing name
           else
+            # TODO: add expression
             ctx.addToDeferredNames
               name: name
               type: addConstraints canonicalType, allConstraints
@@ -2634,7 +2635,7 @@
           contextType instanceof TempType
       # Typing deferred, use an impricise type var
       type = toConstrained ctx.freshTypeVariable star
-      ctx.addToDeferredNames {name: symbol, type: type}
+      ctx.addToDeferredNames {name: symbol, type: type, expression: atom}
       nameTranslate ctx, atom, symbol, type
     else
       # log "deferring in rhs for #{symbol}", ctx.definitionName()


Yet another change to deferring, and it still isnt perfect, since I assume that every scope is late. I need to check this because top level match expressions will be broken.

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -603,7 +603,7 @@
   addToDeferredNames: (binding) ->
     @_deferrableDefinition().deferredBindings.push binding

-  addToDeferred: (binding) ->
+  addToDeferredBindings: (binding) ->
     @_scope().deferredBindings.push binding

   addToParentDeferred: (binding) ->
@@ -666,11 +666,9 @@
     "i#{@variableIndex++}"

   doDefer: (expression, dependencyName) ->
-    @_setDeferIn @_deferrableDefinition(), expression, dependencyName
-
-  _setDeferIn: (definition, expression, dependencyName) ->
+    definition = @_deferrableDefinition()
     definition._defer =
-      (@_deferReasonOf definition) or [expression, dependencyName]
+      (@_deferReasonOf definition) or [expression, dependencyName, @currentScopeIndex()]

   deferReason: ->
     @_deferReasonOf @_deferrableDefinition()
@@ -681,8 +679,8 @@
   _deferReasonOf: (definition) ->
     definition?._defer

-  addDeferredDefinition: ([expression, dependencyName, lhs, rhs]) ->
-    @_scope().deferred.push [expression, dependencyName, lhs, rhs]
+  addDeferredDefinition: ([expression, dependencyName, useScopeIndex, lhs, rhs]) ->
+    @_scope().deferred.push [expression, dependencyName, useScopeIndex, lhs, rhs]

   deferred: ->
     @_scope().deferred
@@ -1271,7 +1269,7 @@
     if deps.length > 0
       # log "adding top level lhs to deferred #{name}", deps
       currentType = type#substitute ctx.substitution, type
-      ctx.addToDeferred
+      ctx.addToDeferredBindings
         name: name
         scopeIndex: ctx.currentScopeIndex()
         type: currentType
@@ -1279,7 +1277,7 @@
         polymorphic: polymorphic
         deps: deps#(map (({name}) -> name), deps)
       # for dep in deps
-      #   ctx.addToDeferred {name: dep.name, type: dep.type, reversed: name}
+      #   ctx.addToDeferredBindings {name: dep.name, type: dep.type, reversed: name}
       if not ctx.isTyped name
         ctx.assignType name, (new TempType type)
     else
@@ -1469,8 +1467,9 @@
     deferredCount = 0
     while (_notEmpty ctx.deferred()) and deferredCount < ctx.deferred().length
       prevSize = ctx.deferred().length
-      [expression, dependencyName, lhs, rhs] = deferred = ctx.deferred().shift()
-      if (ctx.isDeclared dependencyName)
+      [expression, dependencyName, useScope, lhs, rhs] = deferred = ctx.deferred().shift()
+      if useScope isnt ctx.currentScopeIndex() and (ctx.isDeclared dependencyName) or
+          (ctx.isFinallyDeclared dependencyName)
         compiledPairs.push definitionPairCompile ctx, lhs, rhs
         deferredCount = 0
       else


name: pass error to callback

name: pass error to callback

--- workers/social/lib/social/models/name.coffee
+++ workers/social/lib/social/models/name.coffee
@@ -120,7 +120,7 @@
     fetchModels = (name, callback)->
       if 'string' is typeof name
         @one {name}, (err, nameObj)->
-          if err then next err
+          if err then callback err
           else if nameObj?
             fetchByNameObject nameObj, callback
           else


you can pass an offset. if you want. don't have to though.

--- vendor/assets/javascripts/nifty/dialog.coffee
+++ vendor/assets/javascripts/nifty/dialog.coffee
@@ -52,6 +52,9 @@
       insertedDialog.css('width', "#{options.width}px")
       insertedDialog.css('margin-left', "-#{options.width / 2}px")

+    if options.offset?
+      insertedDialog.css('margin-top', "#{options.offset}px")
+
     # Set the closing action for the inserted dialog to close dialog
     # and fade out the appropriate overlay
     insertedDialog.data 'closeAction', ->


Fix lint errors

Fix lint errors

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -997,7 +997,7 @@
   resolveProxy: (url) ->
     return new Promise (resolve, reject) =>
       requestId = @nextProxyRequestId++
-      disposable = @applicationDelegate.onDidResolveProxy (id, proxy) =>
+      disposable = @applicationDelegate.onDidResolveProxy (id, proxy) ->
         if id is requestId
           disposable.dispose()
           resolve(proxy)


Add JSDoc. Refs #17

Add JSDoc. Refs #17

--- src/coffee/services/product-projections.coffee
+++ src/coffee/services/product-projections.coffee
@@ -15,6 +15,10 @@
   ###
   @baseResourceEndpoint: '/product-projections'

+  ###*
+   * @private
+   * Reset default query/search params
+  ###
   _setDefaults: ->
     super()
     @_customParams =
@@ -26,7 +30,7 @@
         facet: []

   ###*
-   * Define to fetch only staged products
+   * Define whether to query for staged or current product projection.
    * @param Boolean [staged] true to query staged products (default). False to query published products
    * @return {ProductProjectionService} Chained instance of this class
   ###
@@ -35,18 +39,34 @@
     @_logger.debug @_customParams.query, 'Setting \'staged\' parameter'
     this

+  ###*
+   * Define the language tag used for searching product projection.
+   * @param {String} language An ISO language tag, used for search, for the 'lang' search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   lang: (language) ->
     throw new Error 'Language parameter is required for searching' unless language
     @_customParams.query.lang = language
     @_logger.debug @_customParams.query, 'Setting \'lang\' parameter'
     this

+  ###*
+   * Define the text to analyze and search.
+   * @param {String} [text] A string for the `text` search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   text: (text) ->
     return this unless text
     @_customParams.query.text = text
     @_logger.debug @_customParams.query, 'Setting \'text\' parameter'
     this

+  ###*
+   * Define a {Filter} used for filtering searched product projections.
+   * @link http://commercetools.de/dev/http-api-projects-products.html#search-filters
+   * @param {String} [filter] A {Filter} string for the `filter` search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   filter: (filter) ->
     return this unless filter
     encodedFilter = encodeURIComponent(filter)
@@ -54,6 +74,12 @@
     @_logger.debug @_customParams.query, 'Setting \'filter\' parameter'
     this

+  ###*
+   * Define a {Filter} (applied to query result) used for filtering searched product projections.
+   * @link http://commercetools.de/dev/http-api-projects-products.html#search-filters
+   * @param {String} [filter] A {Filter} string for the `filter.query` search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   filterByQuery: (filter) ->
     return this unless filter
     encodedFilter = encodeURIComponent(filter)
@@ -61,6 +87,12 @@
     @_logger.debug @_customParams.query, 'Setting \'filter.query\' parameter'
     this

+  ###*
+   * Define a {Filter} (applied to facet calculation) used for filtering searched product projections.
+   * @link http://commercetools.de/dev/http-api-projects-products.html#search-filters
+   * @param {String} [filter] A {Filter} string for the `filter.facets` search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   filterByFacets: (filter) ->
     return this unless filter
     encodedFilter = encodeURIComponent(filter)
@@ -68,6 +100,12 @@
     @_logger.debug @_customParams.query, 'Setting \'filter.facets\' parameter'
     this

+  ###*
+   * Define a {Facet} used for calculating statistical counts for searched product projections.
+   * @link http://commercetools.de/dev/http-api-projects-products.html#search-facets
+   * @param {String} [facet] A {Facet} string for the `facet` search parameter.
+   * @return {ProductProjectionService} Chained instance of this class
+  ###
   facet: (facet) ->
     return this unless facet
     encodedFacet = encodeURIComponent(facet)
@@ -77,7 +115,7 @@

   ###*
    * @private
-   * Extend the query string by staged param
+   * Build a query string from (pre)defined params and custom search params.
    * @return {String} the query string
   ###
   _queryString: ->
@@ -112,7 +150,7 @@
     _.compact([super()].concat(customQueryString)).join '&'

   ###*
-   * Fetch resource defined by _currentEndpoint with query parameters
+   * Search product projections with search parameters
    * @return {Promise} A promise, fulfilled with an {Object} or rejected with a {SphereError}
   ###
   search: ->


Fix negative number constants in macros

Fix negative number constants in macros

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1717,7 +1717,8 @@
 constantToSource = (value) ->
   switch typeof value
     when 'boolean' then (tokenize (if value then 'True' else 'False'))[0]
-    when 'number', 'string' then (tokenize "#{value}")[0]
+    when 'number' then (tokenize (if value < 0 then "~#{Math.abs value}" else "#{value}"))[0]
+    when 'string' then (tokenize "#{value}")[0]
     when 'object'
       kind = Object.prototype.toString.call(value).slice(8, -1)
       value


Working define command and two way down command

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -4792,6 +4792,7 @@
 exports.library = library

 exports.isForm = isForm
+exports.isCall = isCall
 exports.isAtom = isAtom


Fix users()

Fix users()

Was gettig `Cannot read property 'data' of undefined`, because @brain was never set

--- src/brain.coffee
+++ src/brain.coffee
@@ -51,7 +51,7 @@
   #
   # Returns an Array of User objects.
   users: ->
-    @brain.data.users
+    @data.users

   # Public: Get a User object given a unique identifier.
   #