Social: NotificationController is modified for showing Group join/leave notification trays

--- client/Main/notificationcontroller.coffee
+++ client/Main/notificationcontroller.coffee
@@ -112,13 +112,19 @@

   fetchTarget: (data)->
     new Promise (resolve, reject) =>
+      helper = (err, target) ->
+        return reject err  if err
+        data.target = target
+        resolve data
+
       {targetId, type} = data.contents
       switch type
         when "comment", "like"
-          KD.remote.api.SocialMessage.fetch id: targetId, (err, target) =>
-            return reject err  if err
-            data.target = target
-            resolve data
+          KD.remote.api.SocialMessage.fetch id: targetId, helper
+        when "follow"
+          KD.remote.api.JAccount.one socialApiId: targetId, helper
+        when "join", "leave"
+          KD.remote.api.JGroup.one socialApiChannelId: targetId, helper
         else
           resolve data

@@ -158,15 +164,17 @@
           setTitle "#{actorName} liked your status."
         when "follow"
           setTitle "#{actorName} started following you."
+        when "join"
+          setTitle "#{actorName} has joined #{target.title}."
+        when "leave"
+          setTitle "#{actorName} has left #{target.title}."
+
       # when "newMessage"
       #   @emit "NewMessageArrived"
       #   "#{actorName} sent you a #{subjectMap()[subject.constructorName]}."
       # when "groupInvited"
       #   "#{actorName} has invited you to #{subjectObj.title}."
-      # when "groupJoined"
-      #   "#{actorName} has joined #{subjectObj.title}."
-      # when "groupLeft"
-      #   "#{actorName} has left #{subjectObj.title}."
+

   prepareClick: (data, options) ->
     {target, contents} = data
@@ -177,6 +185,9 @@
           groupSlug = if target.group is "koding" then "" else "/#{target.group}"
           KD.getSingleton('router').handleRoute "#{groupSlug}/Activity/#{target.slug}", state:target
           view.destroy()
+        else if contents.type in ["join", "leave"]
+          KD.getSingleton('router').handleRoute "/#{target.slug}"
+          view.destroy()

   # NOTIFICATION SAMPLES
@@ -201,15 +212,6 @@
         .catch (err) ->
           warn err  if err

-    # @fetchActor contents, (err, data) =>
-    #   return warn err  if err
-    #   @fetchTarget data, (err, data) =>
-    #     return warn err  if err
-    #     @prepareMessage data, (err, options) =>
-    #       return warn err  if err
-    #       @prepareClick data, options
-    #       @notify options
-

   notify:(options  = {})->


WritingComponent cleaning

WritingComponent cleaning

--- urb/zod/pub/talk/src/js/components/WritingComponent.coffee
+++ urb/zod/pub/talk/src/js/components/WritingComponent.coffee
@@ -6,6 +6,68 @@
 StationActions  = require '../actions/StationActions.coffee'
 StationStore    = require '../stores/StationStore.coffee'
 Member          = require './MemberComponent.coffee'
+
+SHIPSHAPE = ///
+^~?(                              #preamble
+   [a-z]{3}                       # galaxy
+ | [a-z]{6}(-[a-z]{6}){0,3}       # star - moon
+ |   [a-z]{6}(-[a-z]{6}){3}       # comet
+   (--[a-z]{6}(-[a-z]{6}){3})+    #
+)$                                #postamble
+///
+PO = '''
+dozmarbinwansamlitsighidfidlissogdirwacsabwissib
+rigsoldopmodfoglidhopdardorlorhodfolrintogsilmir
+holpaslacrovlivdalsatlibtabhanticpidtorbolfosdot
+losdilforpilramtirwintadbicdifrocwidbisdasmidlop
+rilnardapmolsanlocnovsitnidtipsicropwitnatpanmin
+ritpodmottamtolsavposnapnopsomfinfonbanporworsip
+ronnorbotwicsocwatdolmagpicdavbidbaltimtasmallig
+sivtagpadsaldivdactansidfabtarmonranniswolmispal
+lasdismaprabtobrollatlonnodnavfignomnibpagsopral
+bilhaddocridmocpacravripfaltodtiltinhapmicfanpat
+taclabmogsimsonpinlomrictapfirhasbosbatpochactid
+havsaplindibhosdabbitbarracparloddosbortochilmac
+tomdigfilfasmithobharmighinradmashalraglagfadtop
+mophabnilnosmilfopfamdatnoldinhatnacrisfotribhoc
+nimlarfitwalrapsarnalmoslandondanladdovrivbacpol
+laptalpitnambonrostonfodponsovnocsorlavmatmipfap
+
+zodnecbudwessevpersutletfulpensytdurwepserwylsun
+rypsyxdyrnuphebpeglupdepdysputlughecryttyvsydnex
+lunmeplutseppesdelsulpedtemledtulmetwenbynhexfeb
+pyldulhetmevruttylwydtepbesdexsefwycburderneppur
+rysrebdennutsubpetrulsynregtydsupsemwynrecmegnet
+secmulnymtevwebsummutnyxrextebfushepbenmuswyxsym
+selrucdecwexsyrwetdylmynmesdetbetbeltuxtugmyrpel
+syptermebsetdutdegtexsurfeltudnuxruxrenwytnubmed
+lytdusnebrumtynseglyxpunresredfunrevrefmectedrus
+bexlebduxrynnumpyxrygryxfeptyrtustyclegnemfermer
+tenlusnussyltecmexpubrymtucfyllepdebbermughuttun
+bylsudpemdevlurdefbusbeprunmelpexdytbyttyplevmyl
+wedducfurfexnulluclennerlexrupnedlecrydlydfenwel
+nydhusrelrudneshesfetdesretdunlernyrsebhulryllud
+remlysfynwerrycsugnysnyllyndyndemluxfedsedbecmun
+lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes
+'''
+
+Audience = recl
+  displayName: "Audience"
+  onKeyDown: (e) ->
+    if e.keyCode is 13
+      e.preventDefault()
+      setTimeout () ->
+          $('#writing').focus()
+        ,0
+      return false
+  render: ->
+    div {
+          id:"audi"
+          className:"audi valid-#{@props.valid}"
+          contentEditable:true
+          @onKeyDown
+          onBlur:@props.onBlur
+        }, @props.audi.join(" ")

 module.exports = recl
   set: ->
@@ -26,20 +88,21 @@
     s.ludi = _.without s.ludi, window.util.mainStationPath window.urb.user
     s

-  getInitialState: -> @stateFromStore()
+  getInitialState: -> _.extend @stateFromStore(), length:0, lengthy: false

   typing: (state) ->
     if @state.typing[@state.station] isnt state
       StationActions.setTyping @state.station,state

-  _blur: ->
+  onBlur: ->
     @$writing.text @$writing.text()
     MessageActions.setTyping false
     @typing false

-  _focus: ->
+  onFocus: ->
     MessageActions.setTyping true
     @typing true
+    @cursorAtEnd

   addCC: (audi) ->
     listening = @state.config[window.util.mainStation(window.urb.user)].sources
@@ -62,22 +125,14 @@
       audi = @state.audi
     audi = @addCC audi
     MessageActions.sendMessage @$writing.text().trim(),audi
-    @$length.text "0/62"
     @$writing.text('')
+    @setState length:0
     @set()
     @typing false

-  _audiKeyDown: (e) ->
-    if e.keyCode is 13
-      e.preventDefault()
-      setTimeout () ->
-          $('#writing').focus()
-        ,0
-      return false
-
-  _writingKeyUp: (e) ->
+  onKeyUp: (e) ->
     if not window.urb.util.isURL @$writing.text()
-      @$length.toggleClass('valid-false',(@$writing.text().length > 62))
+      @setState lengthy: (@$writing.text().length > 62)
     # r = window.getSelection().getRangeAt(0).cloneRange()
     # @$writing.text @$writing.text()
     # setTimeout =>
@@ -87,7 +142,7 @@
     #     console.log r
     #   ,0

-  _writingKeyDown: (e) ->
+  onKeyDown: (e) ->
     if e.keyCode is 13
       txt = @$writing.text()
       e.preventDefault()
@@ -95,10 +150,10 @@
            window.urb.util.isURL @$writing.text() )
         @sendMessage()
       return false
-    @_input()
+    @onInput()
     @set()

-  _input: (e) ->
+  onInput: (e) ->
     text   = @$writing.text()
     length = text.length
     # geturl = new RegExp [
@@ -113,11 +168,10 @@
     #   for url in urls
     #     length -= url.length
     #     length += 10
-    @$length.text "#{length}/62"
-
-  _setFocus: -> @$writing.focus()
+    @setState {length}

   _validateAudiPart: (a) ->
+    a = a.trim()
     # if a[0] isnt "~"
     #   return false
     if a.indexOf("/") isnt -1
@@ -127,9 +181,9 @@
       ship = _a[0]
     else
       ship = a
-    if ship.length < 3
-      return false
-    return true
+
+    return (SHIPSHAPE.test ship) and
+      _.all (ship.match /[a-z]{3}/g), (a)-> -1 isnt PO.indexOf a

   _validateAudi: ->
     v = $('#audi').text()
@@ -138,23 +192,17 @@
       return true
     if v.length < 5 # zod/a is shortest
       return false
-    v = v.split " "
-    for a in v
-      a = a.trim()
-      valid = @_validateAudiPart(a)
-    valid
+    _.all (v.split /\ +/), @_validateAudiPart

   _setAudi: ->
     valid = @_validateAudi()
     StationActions.setValidAudience valid
     if valid is true
-      v = $('#audi').text()
-      if v.length is 0 then v = window.util.mainStationPath window.urb.user
-      v = v.split " "
-      for k,_v of v
-        if _v[0] isnt "~" then v[k] = "~#{_v}"
-      StationActions.setAudience v
-      v
+      stan = $('#audi').text() || window.util.mainStationPath window.urb.user
+      stan = (stan.split /\ +/).map (v)->
+        if v[0] is "~" then v else "~"+v
+      StationActions.setAudience stan
+      stan
     else
       false

@@ -178,12 +226,11 @@
     StationStore.addChangeListener @_onChangeStore
     MessageStore.addChangeListener @_onChangeStore
     @$el = $ @getDOMNode()
-    @$length = $('#length')
     @$writing = $('#writing')
     @$writing.focus()
     if @get()
       @$writing.text @get()
-      @_input()
+      @onInput()
     @interval = setInterval =>
         @$el.find('.time').text @getTime()
       , 1000
@@ -210,25 +257,14 @@
     div {className:k}, [
       (div {className:"attr"}, [
         (React.createElement Member, iden)
-        (div {
-          id:"audi"
-          className:"audi valid-#{@state.valid}"
-          contentEditable:true
-          onKeyDown: @_audiKeyDown
-          onBlur:@_setAudi
-          }, audi.join(" "))
+        (React.createElement Audience, {audi,valid:@state.valid, onBlur:@_setAudi})
         (div {className:"time"}, @getTime())
       ])
       (div {
           id:"writing"
           contentEditable:true
-          onFocus: @_focus
-          onBlur: @_blur
-          onInput: @_input
-          onPaste: @_input
-          onKeyDown: @_writingKeyDown
-          onKeyUp: @_writingKeyUp
-          onFocus: @cursorAtEnd
+          onPaste: @onInput
+          @onInput, @onFocus, @onBlur, @onKeyDown, @onKeyUp
         }, "")
-      div {id:"length"}, "0/62"
+       (div {id:"length",className:("valid-false" if @state.lengthy)}, "#{@state.length}/62")
       ]


fix point conversion error

fix point conversion error

--- framer/GestureInputRecognizer.coffee
+++ framer/GestureInputRecognizer.coffee
@@ -330,7 +330,7 @@

 		# Convert the point to the current context
 		eventPoint = Utils.convertPointFromContext(
-			@_getEventPoint(event), Framer.CurrentContext, true, false)
+			@_getEventPoint(event), Framer.CurrentContext, true, true)

 		_.extend event,
 			time: Date.now() # Current time √


use the first four subpods, lower char limit to 600

--- commands/wolfram.coffee
+++ commands/wolfram.coffee
@@ -46,14 +46,15 @@

             try
                 pods = root.children
+                npods = 0
                 for pod in pods
-                    continue if /Result|UnitConversion|:Expanded/.test pod.attributes.id
-                    if message.length > 800 then break
+                    if /Result|UnitConversion/.test pod.attributes.id then continue
+                    if npods > 4 or message.length > 600 then break
                     subpods = pod.children.filter (node) -> node.name is 'subpod'
                     for subpod in subpods
-                        if message.length > 800 then break
+                        if message.length > 600 then break
                         [text] = subpod.children.filter (node) -> node.name is 'plaintext'
                         message += "\n#{[text.firstChild().text]}"
             catch e

-        respond message
+            respond message


I'm really not getting it today

I'm really not getting it today

--- vendor/assets/javascripts/fustrate/components/modal.coffee
+++ vendor/assets/javascripts/fustrate/components/modal.coffee
@@ -28,8 +28,8 @@
     @settings.previousModal = $()

     @setTitle @constructor.title, icon: @constructor.icon
-    @setContent content, false
-    @setButtons @constructor.buttons, false
+    @setContent content, reload: false
+    @setButtons @constructor.buttons, reload: false

     @_reloadUIElements()
     @addEventListeners()


add back dep

add back dep

--- vendor/assets/javascripts/rolodex_angular/src/modal.coffee
+++ vendor/assets/javascripts/rolodex_angular/src/modal.coffee
@@ -5,7 +5,7 @@
 # work with the Rolodex styles. The rest remains true to the Bootstrap source including using their transition
 # animation library and not ngAnimate.

-angular.module("rolodex.modal", ["rolodex.transition"])
+angular.module("rolodex.modal", ["rolodex.transition", "templates"])

 # A helper, internal data structure that acts as a map but also allows getting / removing
 # elements in the LIFO order


Checkpointing

Checkpointing

--- src/maintainer.coffee
+++ src/maintainer.coffee
@@ -25,28 +25,61 @@
       @get_users,
       @user_application_mapping,
       @parallel_branch_and_tags,
-      @cleanup_leafs
+      @parallel_get_artifacts
+      # @cleanup_leafs
     ])

   ###
-  Usermapping
+  Parallel get_applications mapping.
+  @param {Array} users The list of users to map applications to
+  @param {Function} fn The callback function
   ###
   user_application_mapping: (users, fn) =>
-    async.map users, @get_applications
-    , (err, usermapping) =>
-      console.log usermapping
-      fn(err, usermapping)
+    async.map users, @get_applications, fn
+
+  ###
+  Parallel get_branch and get_tags mapping.
+  @param {Array} infolist The list of user/apps objects
+  @param {Function} fn The callback function
+  ###
+  parallel_branch_and_tags: (infolist, fn) =>
+    async.map infolist, (info, cb) =>
+      async.map info.apps, (app, cb2) =>
+        combined_info = { user: info.user, app: app }
+
+        async.parallel
+          branches: async.apply(@get_branches, combined_info)
+          tags: async.apply(@get_tags, combined_info)
+        , fn

   ###
   ###
-  parallel_branch_and_tags: (info, fn) =>
-    fn(null, "awesome")
+  parallel_get_artifacts: (artifactinfo_list, fn) =>
+    console.log artifactinfo_list
+
+    async.parallel
+      branches: async.map

   ###
   Cleanup leafs for archives and branches
   ###
-  cleanup_leafs: (info, fn) =>
+  cleanup_leafs: (leafinfo, fn) =>
+    console.log leafinfo
     fn(null, "cool")
+
+  ###
+  Sends a request to delete a given artifact.
+  @param {Object} artifactinfo The information to describe a single artifact
+  ###
+  cleanup_artifact: (artifactinfo, fn) =>
+    paths = [
+      artifactinfo.user,
+      artifactinfo.app,
+      artifactinfo.type,
+      artifactinfo.artifact
+    ]
+    request url: @app_url(paths), method: 'DELETE', json: true
+    , (err, resp, body) => fn(err, body)

   ###
   Helper function for the base app url.
@@ -120,7 +153,7 @@
   @param {Object} artifactinfo The user, app, type, and artifacts
   @param {Function} fn The callback function
   ###
-  get_archives: (artifactinfo fn) =>
+  get_archives: (artifactinfo, fn) =>
     @app_request artifactinfo.user
     , artifactinfo.app, artifactinfo.type, 'archives'
     , (err, body) =>


Refactor hints; add `userMightOverType`.

Previously, we set a variable `delay` and then did some logical
gymnastics to get the correct effect.

However, in fact, all we care about is whether the user might over-type
the links text.  So changing to using that as a Boolean flag greatly
simplifies the logic.  And we lose about 10 LoC.

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -361,14 +361,11 @@
     DomUtils.suppressEvent event

   updateVisibleMarkers: (hintMarkers, tabCount = 0) ->
-    keyResult = @markerMatcher.getMatchingHints hintMarkers, tabCount
-    linksMatched = keyResult.linksMatched
+    {linksMatched, userMightOverType} = @markerMatcher.getMatchingHints hintMarkers, tabCount
     if linksMatched.length == 0
       @deactivateMode()
     else if linksMatched.length == 1
-      keyResult.delay ?= 0
-      keyResult.waitForEnter ?= false
-      @activateLink linksMatched[0], keyResult
+      @activateLink linksMatched[0], userMightOverType ? false
     else
       @hideMarker marker for marker in hintMarkers
       @showMarker matched, @markerMatcher.hintKeystrokeQueue.length for matched in linksMatched
@@ -376,7 +373,7 @@
   #
   # When only one link hint remains, this function activates it in the appropriate way.
   #
-  activateLink: (linkMatched, {delay, waitForEnter} = {}) ->
+  activateLink: (linkMatched, userMightOverType=false) ->
     @removeHintMarkers()
     clickEl = linkMatched.clickableItem

@@ -391,12 +388,12 @@
         @linkActivator clickEl
         LinkHints.activateModeWithQueue() if @mode is OPEN_WITH_QUEUE

-    if waitForEnter? and waitForEnter
+    if userMightOverType and Settings.get "waitForEnterForFilteredHints"
       new WaitForEnter linkMatched.rect, linkActivator
-    else if delay? and 0 < delay
-      # Install a mode to block keyboard events if the user is still typing.  The intention is to prevent the
-      # user from inadvertently launching Vimium commands when typing the link text.
-      new TypingProtector delay, linkMatched?.rect, linkActivator
+    else if userMightOverType
+      # Block keyboard events while the user is still typing.  The intention is to prevent the user from
+      # inadvertently launching Vimium commands when (over-)typing the link text.
+      new TypingProtector 200, linkMatched?.rect, linkActivator
     else
       DomUtils.flashRect linkMatched.rect
       linkActivator()
@@ -546,18 +543,11 @@
     @filterLinkHints hintMarkers

   getMatchingHints: (hintMarkers, tabCount = 0) ->
-    delay = 0
-
     # At this point, linkTextKeystrokeQueue and hintKeystrokeQueue have been updated to reflect the latest
     # input. use them to filter the link hints accordingly.
     matchString = @hintKeystrokeQueue.join ""
     linksMatched = @filterLinkHints hintMarkers
     linksMatched = linksMatched.filter (linkMarker) -> linkMarker.hintString.startsWith matchString
-
-    if linksMatched.length == 1 && @hintKeystrokeQueue.length == 0 and 0 < @linkTextKeystrokeQueue.length
-      # In filter mode, people tend to type out words past the point needed for a unique match. Hence we
-      # should avoid passing control back to command mode immediately after a match is found.
-      delay = 200

     # Visually highlight of the active hint (that is, the one that will be activated if the user
     # types <Enter>).
@@ -566,11 +556,8 @@
     @activeHintMarker = linksMatched[tabCount]
     @activeHintMarker?.classList.add "vimiumActiveHintMarker"

-    {
-      linksMatched: linksMatched
-      delay: delay
-      waitForEnter: 0 < delay and Settings.get "waitForEnterForFilteredHints"
-    }
+    linksMatched: linksMatched
+    userMightOverType: @hintKeystrokeQueue.length == 0 and 0 < @linkTextKeystrokeQueue.length

   pushKeyChar: (keyChar, keydownKeyChar) ->
     # For filtered hints, we *always* use the keyChar value from keypress, because there is no obvious and


Fix a bug in IE support.

Fix a bug in IE support.

--- src/noreferrer.coffee
+++ src/noreferrer.coffee
@@ -94,7 +94,7 @@
             when '_self', window.name
               win = window
             else
-              doc = window.open(null, target)
+              win = window.open(null, target)
               # This may be an existing window, hence always call clear().
           doc = win.document
           doc.clear()


ChatPane: Create participant view if RTM is ready.

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -33,7 +33,9 @@

     channel
       .on 'RemovedFromChannel', (acc) => @removeParticipant acc.profile.nickname
-      .on 'AddedToChannel',     (acc) => @addParticipant acc.profile.nickname
+      .on 'AddedToChannel',     (acc) =>
+        if @rtm.isReady
+          @addParticipant acc.profile.nickname

   createElements: ->
@@ -114,6 +116,7 @@

   toggleButtons: (state) ->
+
     startButton = @startSession
     endButton   = @endSession


fire change event when setting an association

--- client/lanes/models/AssociationMap.coffee
+++ client/lanes/models/AssociationMap.coffee
@@ -91,6 +91,7 @@
             association[fn_name]( attributes )
             if Lanes.u.isModel(association)
                 model.set(this.pk(name), association.id) unless association.isNew()
+                model.trigger("change:#{name}", association, {}) unless options?.silent
                 association.isDirty = if value.isDirty? then value.isDirty else true
                 unless _.isObject(value)
                     association.clear()


Keep translation out of master branch until BOOK-788 is complete

--- src/core/javascripts/services/translate.js.coffee
+++ src/core/javascripts/services/translate.js.coffee
@@ -1,283 +0,0 @@
-angular.module('pascalprecht.translate').config ($translateProvider) ->
-
-  en_translations = {
-
-    CHANGE_LANG: 'Change language'
-    EN: 'English'
-    DE: 'Deutsch'
-    FR: 'Français'
-
-    PROGRESS_BAR_TITLE: 'Step'
-    PROGRESS_BAR_COMPLETE: 'Completed'
-    PROGRESS_BACK: 'Back'
-    PROGRESS_CLEAR: 'Clear'
-    PROGRESS_SEARCH: 'Search'
-    PROGRESS_SELECT: 'Select'
-    PROGRESS_CONFIRM: 'Confirm'
-    PROGRESS_NEXT: 'Continue'
-    PROGRESS_OK: 'OK'
-    PROGRESS_MOVE_BOOKING: 'Move booking'
-    PROGRESS_CANCEL_CANCEL: 'Do not cancel'
-    PROGRESS_CANCEL_BOOKING: 'Cancel booking'
-
-    MAP_TITLE: 'Search for a store to begin your booking.'
-    MAP_PLACEHOLDER: 'Enter a town, city, postcode or showroom'
-    GEOLOCATE: 'Use current location'
-    STORE_RESULT_TITLE: '{{number}} results for showrooms near {{address}}'
-    STORE_PHONE: 'Phone:'
-    SHOWROOM: 'Showroom'
-
-    ITEM_SERVICE: 'Service:'
-    ITEM_RESOURCE: 'Resource:'
-    ITEM_PERSON: 'Person:'
-    ITEM_PRICE: 'Price:'
-    ITEM_DATE: 'Date:'
-    ITEM_TIME: 'Time:'
-    ITEM_DURATION: 'Duration:'
-
-    SERVICE: 'Service'
-    SERVICE_NAME: '{{service}} at {{company}}'
-    SERVICE_CONFIRMATION: '{item} at'
-    DATE_TIME_CONFIRMATION: 'Date/Time'
-
-    SOLD_OUT_STATUS: 'Sold out'
-    SOLD_OUT_HIDE: 'Hide Sold Out Events'
-    SOLD_OUT_SHOW: 'Show Sold Out Events'
-
-    AVAIL_TITLE: 'Select a date and time for your Colour Consultation'
-    APPOINTMENT_DATE: 'Date'
-    APPOINTMENT_TIME: 'Time'
-    APPOINTMENT_DURATION: 'Duration'
-    AVAIL_DAY_PREVIOUS: 'Previous Day'
-    AVAIL_DAY_NEXT: 'Next Day'
-    AVAIL_MONTH_PREVIOUS: 'Previous Month'
-    AVAIL_MONTH_NEXT: 'Next Month'
-    AVAIL_MORNING: 'Morning'
-    AVAIL_AFTERNOON: 'Afternoon'
-    AVAIL_EVENING: 'Evening'
-    AVAIL_SLOTS: '{{number}} available'
-
-    PICK_A_DATE: 'Pick a date'
-    HOUR: 'hour'
-    MIN: 'min'
-    AND: 'and'
-
-    REVIEW_TITLE: 'Review your appointment details'
-    DETAILS_TITLE: 'Your details',
-    DETAILS_FIRST_NAME: 'First Name'
-    DETAILS_FIRST_NAME_VALIDATION_MSG: 'Please enter your first name'
-    DETAILS_LAST_NAME: 'Last Name'
-    DETAILS_LAST_NAME_VALIDATION_MSG: 'Please enter your last name'
-    DETAILS_EMAIL: 'E-mail'
-    DETAILS_EMAIL_VALIDATION_MSG: 'Please enter a valid email address'
-    DETAILS_PHONE: 'Mobile'
-    DETAILS_PHONE_VALIDATION_MSG: 'Please enter a valid mobile number'
-    DETAILS_TERMS: 'I agree to the terms and conditions'
-    DETAILS_VALIDATION_MSG: 'This field is required'
-    DETAILS_REQUIRED: 'Required'
-    DETAILS_OTHER_INFO: 'Other information'
-
-    CONFIRMATION_TITLE: 'Booking Confirmation'
-    CONFIRMATION_SUBHEADER: 'Thanks {{member_name}}. Your booking is now confirmed. We have emailed you the details below.'
-    PRINT: 'Print'
-    CALENDAR_EXPORT_TITLE: 'Export to calendar'
-    BOOKING_REFERENCE: 'Booking Reference'
-    CONFIRMATION_INSPIRATION_LINK: 'Visit our inspiration site'
-    CONFIRMATION_HOMEPAGE_LINK: 'Return to the homepage'
-
-    CANCEL_QUESTION: 'Are you sure you want to cancel this booking?'
-    WHEN: 'When'
-    WHERE: 'Where'
-    BOOKING_DETAILS: 'Booking Details'
-    CANCEL_CONFIRMATION: 'Your booking has been cancelled.'
-
-    ERROR: {
-      GENERIC: "Sorry, it appears that something went wrong. Please try again or call the business you're booking with if the problem persists."
-      LOCATION_NOT_FOUND: "Sorry, we don't recognise that location"
-      MISSING_LOCATION: "Please enter your location"
-      MISSING_POSTCODE: "Please enter a postcode"
-      INVALID_POSTCODE: "Please enter a valid postcode"
-      ITEM_NO_LONGER_AVAILABLE: "Sorry. The item you were trying to book is no longer available. Please try again."
-      FORM_INVALID: "Please complete all required fieldsssss"
-      GEOLOCATION_ERROR: "Sorry, we could not determine your location. Please try searching instead."
-      EMPTY_BASKET_FOR_CHECKOUT: "There are no items in the basket to proceed to checkout."
-    }
-  }
-
-
-  de_translations = {
-
-    CHANGE_LANG: 'Sprache ändern'
-
-    PROGRESS_BAR_TITLE: 'Schritt'
-    PROGRESS_BAR_COMPLETE: 'fertiggestellt'
-    PROGRESS_BACK: 'zurück'
-    PROGRESS_CLEAR: 'klar'
-    PROGRESS_SEARCH: 'Suche'
-    PROGRESS_SELECT: 'wählen'
-    PROGRESS_CONFIRM: 'bestätigen'
-    PROGRESS_NEXT: 'fortsetzen'
-    PROGRESS_OK: 'Ok'
-    PROGRESS_MOVE_BOOKING: 'Verschieben Buchungs'
-    PROGRESS_CANCEL_CANCEL: 'Stornieren Sie nicht'
-    PROGRESS_CANCEL_BOOKING: 'Reservierung stornieren'
-
-    MAP_TITLE: 'Suchen Sie nach einem Geschäft, um Ihre Reservierung zu beginnen.'
-    MAP_PLACEHOLDER: 'Geben Sie eine Stadt , Stadt, Postleitzahl oder Ausstellungsraum'
-    GEOLOCATE: 'Aktuellen Standort benutzen'
-    STORE_RESULT_TITLE: '{{number}} Ergebnisse für Geschäfte in der Nähe {{address}}'
-    STORE_PHONE: 'Telefon:'
-
-    ITEM_SERVICE: 'Service:'
-    ITEM_RESOURCE: 'Ressource:'
-    ITEM_PERSON: 'Person:'
-    ITEM_PRICE: 'Preis:'
-    ITEM_DATE: 'Datum:'
-    ITEM_TIME: 'Zeit:'
-    ITEM_DURATION: 'Dauer:'
-
-    SERVICE: 'Service'
-    SERVICE_NAME: '{{item}} bei {{company}}'
-    SERVICE_CONFIRMATION: '{{item}} at'
-    DATE_TIME_CONFIRMATION: 'Datum / Uhrzeit'
-
-    SOLD_OUT_STATUS: 'ausverkauft'
-    SOLD_OUT_HIDE: 'Ausblenden Ausverkauft Events'
-    SOLD_OUT_SHOW: 'Ausverkauft Events anzeigen'
-
-    AVAIL_TITLE: 'Wählen Sie ein Datum und die Uhrzeit für Ihre {{item}}'
-    APPOINTMENT_DATE: 'Datum'
-    APPOINTMENT_TIME: 'Zeite'
-    APPOINTMENT_DURATION: 'Dauer'
-    AVAIL_DAY_PREVIOUS: 'letzter Tag'
-    AVAIL_DAY_NEXT: 'Nächster Tag'
-    AVAIL_MONTH_PREVIOUS: 'Vorheriger Monat'
-    AVAIL_MONTH_NEXT: 'Nächster Monat'
-    AVAIL_MORNING: 'Morgen'
-    AVAIL_AFTERNOON: 'Nachmittag'
-    AVAIL_EVENING: 'Abend'
-    AVAIL_SLOTS: '{{number}} verfügbar'
-
-    PICK_A_DATE: 'Wählen Sie ein Datum'
-    HOUR: 'Stunde'
-    MIN: 'Minute'
-    AND: 'und'
-
-    REVIEW_TITLE: 'Überprüfen Sie Ihre Termindetails'
-    DETAILS_TITLE: 'Ihre Angaben',
-    DETAILS_FIRST_NAME: 'Vorname'
-    DETAILS_FIRST_NAME_VALIDATION_MSG: 'Bitte geben Sie Ihren Vornamen ein'
-    DETAILS_LAST_NAME: 'Nachname'
-    DETAILS_LAST_NAME_VALIDATION_MSG: 'Bitte geben Sie Ihren Nachnamen ein'
-    DETAILS_EMAIL: 'E-Mail-'
-    DETAILS_EMAIL_VALIDATION_MSG: 'Bitte geben Sie eine gültige E-Mail-Adresse'
-    DETAILS_PHONE: 'Handynummer'
-    DETAILS_PHONE_VALIDATION_MSG: 'Bitte geben Sie eine gültige Handynummer ein'
-    DETAILS_TERMS: 'Ich stimme den Geschäftsbedingungen'
-    DETAILS_VALIDATION_MSG: 'Dieses Feld ist erforderlich'
-    DETAILS_REQUIRED: 'erforderlich'
-    DETAILS_OTHER_INFO: 'sonstige Angaben'
-
-    CONFIRMATION_TITLE: 'Buchungsbestätigung'
-    CONFIRMATION_SUBHEADER: 'dank {{member_name}}. Ihre Buchung ist nun bestätigt. Leider können wir Ihnen per E-Mail die Details unten.'
-    PRINT: 'Druck'
-    CALENDAR_EXPORT_TITLE: 'Export, um den Kalender'
-    BOOKING_REFERENCE: 'Buchungsreferenz'
-    CONFIRMATION_INSPIRATION_LINK: 'Besuchen Sie unsere Website Inspiration'
-    CONFIRMATION_HOMEPAGE_LINK: 'Zurück zur Startseite'
-
-    CANCEL_QUESTION: 'Sind Sie sicher, Sie wollen diesen Buchung stornieren?'
-    WHEN: 'wann'
-    WHERE: 'wo'
-    BOOKING_DETAILS: 'Buchung Nähere'
-    CANCEL_CONFIRMATION: 'Ihre Buchung storniert wurde.'
-
-    ERROR: {
-      GENERIC: "Leider scheint es, dass etwas schief gelaufen ist. Bitte versuchen Sie es erneut oder rufen Sie das Unternehmen sind Sie bei der Buchung mit, wenn das Problem weiterhin besteht."
-      LOCATION_NOT_FOUND: "Leider haben wir nicht, dass die Lage erkennen"
-      MISSING_LOCATION: "Bitte geben Sie Ihren Standort"
-      MISSING_POSTCODE: "Bitte geben Sie eine Postleitzahl ein"
-      INVALID_POSTCODE: "Bitte geben Sie eine gültige Postleitzahl an"
-      ITEM_NO_LONGER_AVAILABLE: "Entschuldigung. Das Element, das Sie versuchten, zu buchen ist nicht mehr verfügbar. Bitte versuchen Sie es erneut."
-      FORM_INVALID: "Bitte füllen Sie alle Felder aus"
-      GEOLOCATION_ERROR: "Leider konnten wir Dein Ort wurde nicht festzustellen. Bitte versuchen Sie statt."
-      EMPTY_BASKET_FOR_CHECKOUT: "Es sind keine Artikel im Warenkorb zur Kasse gehen."
-    }
-
-  }
-
-  fr_translations = {
-    PROGRESS_BAR_TITLE: 'étape'
-  }
-
-  $translateProvider
-    .translations('en', en_translations)
-    .translations('de', de_translations)
-    .translations('fr', fr_translations)
-    .determinePreferredLanguage () ->
-      language = if navigator.languages then navigator.languages[0] else navigator.language or navigator.browserLanguage or navigator.systemLanguage or navigator.userLanguage or 'en'
-      language.substr(0,2)
-    .fallbackLanguage('en')
-    .useCookieStorage()
-
-
-angular.module('BB.Directives').directive 'bbTranslate', ($translate, $rootScope) ->
-  restrict: 'AE'
-  scope : false
-  link: (scope, element, attrs) ->
-
-    scope.languages = [{name: 'en', key: 'EN'}, {name: 'de', key: 'DE'}, {name: 'fr', key: 'FR'}]
-
-    $rootScope.connection_started.then ->
-      scope.selected_language = _.findWhere(scope.languages, {name: $translate.storage().get($translate.storageKey())})
-      moment.locale(scope.selected_language.name) if scope.selected_language
-
-    scope.changeLanguage = (language) ->
-      return if !language
-      scope.selected_language = language
-      moment.locale(language.name)
-      $translate.use(language.name)
-      # restart the widget
-      scope.clearBasketItem()
-      scope.emptyBasket()
-      scope.restart()
-
-
-angular.module('BB.Directives').directive 'bbTimePeriod', ($translate) ->
-  restrict: 'AE'
-  scope : true
-  link: (scope, element, attrs) ->
-
-    scope.duration = scope.$eval attrs.bbTimePeriod
-
-    return if !angular.isNumber scope.duration
-
-    $translate(['HOUR', 'MIN', 'AND']).then (translations) ->
-      hour_string = translations.HOUR
-      min_string  = translations.MIN
-      seperator   = translations.AND
-
-      val = parseInt(scope.duration)
-      if val < 60
-        scope.duration = "#{val} #{min_string}s"
-        return
-      hours = parseInt(val / 60)
-      mins = val % 60
-      if mins == 0
-        if hours == 1
-          scope.duration = "1 #{hour_string}"
-          return
-        else
-          scope.duration = "#{hours} #{hour_string}s"
-          return
-      else
-        str = "#{hours} #{hour_string}"
-        str += "s" if hours > 1
-        if mins == 0
-          scope.duration = str
-          return
-        else
-          str += " #{seperator}" if seperator.length > 0
-          str += " #{mins} #{min_string}s"
-          scope.duration = str
-          return


Don't overload var name

Don't overload var name

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -363,13 +363,13 @@
     lineEndingRegex = /\r\n|\r|\n/g
     lines = []
     lineEndings = []
-    lastIndex = 0
+    previousIndex = 0
     while result = lineEndingRegex.exec(newText)
-      lines.push(newText[lastIndex...result.index])
+      lines.push(newText[previousIndex...result.index])
       lineEndings.push(normalizedEnding ? result[0])
-      lastIndex = lineEndingRegex.lastIndex
-
-    lines.push(newText[lastIndex..]) if newText.length > lastIndex
+      previousIndex = lineEndingRegex.lastIndex
+
+    lines.push(newText[previousIndex..]) if newText.length > previousIndex

     # Update first and last line so replacement preserves existing prefix and suffix of oldRange
     lastIndex = lines.length - 1


Drop “experimental” warning from MarkerLayer APIs

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -816,7 +816,7 @@
   Section: Markers
   ###

-  # Public: *Experimental:* Create a layer to contain a set of related markers.
+  # Public: Create a layer to contain a set of related markers.
   #
   # * `options` An object contaning the following keys:
   #   * `maintainHistory` A {Boolean} indicating whether or not the state of
@@ -828,31 +828,25 @@
   #     maintained across the serialization boundary, allowing you to retrieve
   #     it via {::getMarkerLayer}.
   #
-  # This API is experimental and subject to change on any release.
-  #
   # Returns a {MarkerLayer}.
   addMarkerLayer: (options) ->
     layer = new MarkerLayer(this, String(@nextMarkerLayerId++), options)
     @markerLayers[layer.id] = layer
     layer

-  # Public: *Experimental:* Get a {MarkerLayer} by id.
+  # Public: Get a {MarkerLayer} by id.
   #
   # * `id` The id of the marker layer to retrieve.
-  #
-  # This API is experimental and subject to change on any release.
   #
   # Returns a {MarkerLayer} or `undefined` if no layer exists with the given
   # id.
   getMarkerLayer: (id) ->
     @markerLayers[id]

-  # Public: *Experimental:* Get the default {MarkerLayer}.
+  # Public: Get the default {MarkerLayer}.
   #
   # All marker APIs not tied to an explicit layer interact with this default
   # layer.
-  #
-  # This API is experimental and subject to change on any release.
   #
   # Returns a {MarkerLayer}.
   getDefaultMarkerLayer: ->


Mark addMarkerLayer options as optional

Mark addMarkerLayer options as optional

Since all keys have default values, and the `MarkerLayer` constructor
treats the `options` as optional, mark it as such in the documentation.

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -943,15 +943,15 @@

   # Public: Create a layer to contain a set of related markers.
   #
-  # * `options` An object contaning the following keys:
-  #   * `maintainHistory` A {Boolean} indicating whether or not the state of
-  #     this layer should be restored on undo/redo operations. Defaults to
-  #     `false`.
-  #   * `persistent` A {Boolean} indicating whether or not this marker layer
-  #     should be serialized and deserialized along with the rest of the
-  #     buffer. Defaults to `false`. If `true`, the marker layer's id will be
-  #     maintained across the serialization boundary, allowing you to retrieve
-  #     it via {::getMarkerLayer}.
+  # * `options` (optional) An object contaning the following keys:
+  #   * `maintainHistory` (optional) A {Boolean} indicating whether or not the
+  #     state of this layer should be restored on undo/redo operations. Defaults
+  #     to `false`.
+  #   * `persistent` (optional) A {Boolean} indicating whether or not this
+  #     marker layer should be serialized and deserialized along with the rest
+  #     of the buffer. Defaults to `false`. If `true`, the marker layer's id
+  #     will be maintained across the serialization boundary, allowing you to
+  #     retrieve it via {::getMarkerLayer}.
   #
   # Returns a {MarkerLayer}.
   addMarkerLayer: (options) ->


Enter on empty vomnibar a no-op.

Enter on empty vomnibar a no-op.

--- content_scripts/vomnibar.coffee
+++ content_scripts/vomnibar.coffee
@@ -112,6 +112,8 @@
       # google.
       if (@selection == -1)
         query = @input.value.trim()
+        # <Enter> on an empty vomnibar is a no-op.
+        return unless 0 < query.length
         @hide()
         chrome.extension.sendRequest({
           handler: if openInNewTab then "openUrlInNewTab" else "openUrlInCurrentTab"


clean up

clean up

--- components/Select.coffee
+++ components/Select.coffee
@@ -1,10 +0,0 @@
-noflo = require 'noflo'
-
-class Select extends noflo.Component
-  constructor: ->
-    @inPorts =
-      in: new noflo.Port
-    @outPorts =
-      out: new noflo.Port
-
-exports.getComponent = -> new Select


popup highlights option in MediaView

popup highlights option in MediaView

--- coffee/src/shells/highlights_shell.coffee
+++ coffee/src/shells/highlights_shell.coffee
@@ -55,6 +55,9 @@
   defaults: => _.extend super,
     # subshell will announce when ready, forward event
     readyOnRender: false
+
+    # option whether to show highlights or not. can be turned off externally.
+    popupHighlights: true

   events: => _.extend super,
@@ -135,7 +138,8 @@
       _.each @highlightViews, (highlightView) =>
         values = highlightView.values()
         if values.start <= progress <= values.end
-          highlightView.setActive true
+          if @options.popupHighlights
+            highlightView.setActive true
         else
           highlightView.setActive false

@@ -207,7 +211,8 @@
       offset = highlightView.$el.offset().left
       width = highlightView.$el.width()
       if offset <= event.clientX <= (offset + width)
-        highlightView.showNote()
+        if @options.popupHighlights
+          highlightView.showNote()
       else
         highlightView.hideNote()

@@ -344,15 +349,15 @@

     @timeRangeView.values @_clippingHighlight.values()
     @timeRangeView.$el.show()
-    # @remixMediaView.progressBarView.$el.hide()
     @mediaView.$('.clip-group-view').addClass('clipping')
+    @mediaView.options.popupHighlights = false

   onClipHighlightDone: =>
     @timeRangeView.$el.hide()
-    # @remixMediaView.progressBarView.$el.show()
     @mediaView.highlightsGroupView.softRender()
     @mediaView.$('.clip-group-view').removeClass('clipping')
+    @mediaView.options.popupHighlights = true

     @_clippingHighlight.clipping false
     @_clippingHighlight = undefined


test: bugfix: playerView test suite errored out

this test wasn't updated when the `acorn.player.withData` utility was
changed to validate the acornid. @jbenet

--- coffee/test/views/player_view.spec.coffee
+++ coffee/test/views/player_view.spec.coffee
@@ -10,7 +10,7 @@

   # model for PlayerView contruction
   model = acorn.Model.withData
-    acornid: 'thebestacornever'
+    acornid: 'agoodacorn'
     thumbnail: acorn.config.img.acorn
     title: 'The Best Title Ever'
     type: 'image'


Removes task for win32 update zip

Removes task for win32 update zip

--- tasks/pack.coffee
+++ tasks/pack.coffee
@@ -207,13 +207,6 @@
     # Remove leftovers
     applyPromise del, './build/win32/resources/app'

-    # Create the update archive
-    (callback) ->
-      gulp.src './build/win32/**/*'
-        .pipe zip manifest.name + '-' + manifest.version + '-win32-update.zip'
-        .pipe gulp.dest './dist'
-        .on 'end', callback
-
     # Create the installer
     (callback) ->
       signParams = [


Added Demi tests

Added Demi tests

--- test/demi_test.coffee
+++ test/demi_test.coffee
@@ -51,6 +51,40 @@
     it 'fails when `till` is invalid', ->
       expect(-> d.renderDateInterval('2016-02-01', 'foobar')).to.throw Error

+  describe '#renderDuration', ->
+    d = new Demi '2016-09-21'
+
+    it 'should be a valid function', ->
+      expect(d).to.respondTo 'renderDuration'
+
+    it 'compensates missing `till` for `now`', ->
+      r = d.renderDuration '2016-01-01'
+      expect(r).to.equal '9 Months'
+
+    it 'does not render if the dates are less than two months apart', ->
+      r = d.renderDuration '2016-01-01', '2016-02-01'
+      expect(r).to.be.undefined
+
+    it 'renders month counts when they are more than two month apart', ->
+      r = d.renderDuration '2016-01-01', '2016-03-01'
+      expect(r).to.equal '2 Months'
+
+    it 'renders month counts until dates are ~12 months apart', ->
+      r = d.renderDuration '2016-01-01', '2016-12-29'
+      expect(r).to.equal '12 Months'
+
+    it 'renders year and month counts when dates are more than a year apart', ->
+      r = d.renderDuration '2016-01-01', '2017-08-29'
+      expect(r).to.equal '1 Year 8 Months'
+
+    it 'renders year and months correctly when dates are few years apart', ->
+      r = d.renderDuration '2016-01-01', '2019-08-29'
+      expect(r).to.equal '3 Years 8 Months'
+
+    it 'renders year and months correctly when dates are few years apart', ->
+      r = d.renderDuration '2016-01-01', '2018-01-01'
+      expect(r).to.equal '2 Years'
+
   describe '#renderMarkdown', ->
     it 'correctly renders markdown', ->
       d = new Demi
@@ -93,13 +127,15 @@
       expect(out).to.not.deep.equal data

   describe '#render', ->
-    it 'renders dates and descriptions', ->
+    it 'renders dates, duration and descriptions', ->
       f = new Festus today: '2016-09-21'
       input =
         dates: from: '2015-06-15'
         description: '_wow_'
       known =
-        dates: 'Jun 2015 － Present'
+        dates:
+          interval: 'Jun 2015 － Present'
+          duration: '1 Year 3 Months'
         description: '
wow
\n'
       expect(f.render input).to.deep.equal known

@@ -109,11 +145,16 @@
         dates: from: '2015-06-15'
         foo: bar: dates:
           from: '2016-02-01'
-          till: '2016-03-01'
+          till: '2016-05-01'
         description: '_wow_'
       known =
-        dates: 'Jun 2015 － Present'
-        foo: bar: dates: 'Feb － Mar 2016'
+        dates:
+          interval: 'Jun 2015 － Present'
+          duration: '3 Years 3 Months'
+        foo: bar:
+          dates:
+            interval: 'Feb － May 2016'
+            duration: '3 Months'
         description: '
wow
\n'
       expect(f.render input).to.deep.equal known


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/expirable.coffee
+++ test/expirable.coffee
@@ -27,6 +27,6 @@
   5.times ->
     obj.update()

-  equals obj.I.alpha, 0.8 * (1 - ((obj.I.age / obj.I.duration))
+  equals obj.I.alpha, 0.8 * (1 - (obj.I.age / obj.I.duration)

 module()


payment: emit "SubscriptionTransitionCompleted" event if there is a subscription

--- client/Main/payment/paymentcontroller.coffee
+++ client/Main/payment/paymentcontroller.coffee
@@ -131,6 +131,7 @@
       .on 'DataCollected', (data) =>
         @transitionSubscription data, (err, subscription, rest...) ->
           return workflow.emit 'Failed', err  if err
+          workflow.emit 'SubscriptionTransitionCompleted', subscription  unless err
           workflow.emit 'Finished', data, err, subscription, rest...

       .on 'Finished', (data, err, subscription, rest...) =>


node unit tests complete

node unit tests complete

--- test/spec/node.coffee
+++ test/spec/node.coffee
@@ -1,5 +1,8 @@
 # the testing strategy needs to invoke 1 instance of alchemy!
 # or, properly scope the different instances...
+
+# currently, these tests all pass in the browser ("grunt test:keepalive")
+# but some fail in terminal ("grust test")

 do ->

@@ -34,6 +37,9 @@
                     arrayType = typeof []
                     edgesType.should.equal arrayType

+                it "should contain all and only edges with a relationship to @", ->
+                    testNode._adjacentEdges.toString().should.equal ["1-0-0", "2-0-0", "3-0-0", "4-0-0", "5-0-0"].toString()
+
         describe "_setNodeType", ->
             it "should set nodeType of @, if any", ->
                 testNode._setNodeType().should.equal "project"
@@ -45,7 +51,7 @@

         describe "getProperties", ->
             it "should return a list of the current node properties", ->
-                testNode.getProperties().should.equal testNode._properties
+                testNode.getProperties().should.equal testNode._properties
                 testNode.getProperties("caption").should.equal "AlchemyJS"

         describe "setProperty", ->
@@ -72,25 +78,13 @@

         describe "toggleHidden", ->
             it "should toggle the _state of the node (and its _adjacentEdges) between 'active' and 'hidden'", ->
+                testNode._adjacentEdges.pop()
                 testNode.toggleHidden()
                 testNode._state.should.equal "hidden"
+                testNode._adjacentEdges.should.not.contain "hidden"

-
-
-
-
-
-
-            # it "should contain ids of all connected edges", ->
-            #     testNode = alchemy._nodes[0]
-            #     edges = testNode._adjacentEdges
-            #     edgeIDsFromData = ["1-0", "2-0", "3-0", "4-0", "5-0"]
-
-            #     edges.should.eql edgeIDsFromData
-
-        # describe "outDegree()", ->
-        #     it "should return number of connections to node", ->
-        #         testNode = alchemy._nodes[0]
-        #         testNode.outDegree().should.equal 5
+        describe "outDegree", ->
+            it "should return the number of adjacentEdges", ->
+                testNode.outDegree().should.equal 5

     return


Finalized v1 of find and resize view.

Finalized v1 of find and resize view.

--- components/inputs/KDHitEnterInputView.coffee
+++ components/inputs/KDHitEnterInputView.coffee
@@ -7,7 +7,7 @@

 class KDHitEnterInputView extends KDInputView
-
+
   constructor:(options = {}, data)->

     options.type            or= "textarea"


added mock https server

added mock https server


add bindings for the recording api

add bindings for the recording api

--- lib/runtime.coffee
+++ lib/runtime.coffee
@@ -100,6 +100,9 @@
         "binopinstanceof": -> returns_ejsval_bool @module.getOrInsertExternalFunction "_ejs_op_instanceof",  types.EjsValue, [types.EjsValue, types.EjsValue]
         "binopin":         -> returns_ejsval_bool @module.getOrInsertExternalFunction "_ejs_op_in",          types.EjsValue, [types.EjsValue, types.EjsValue]

+        record_binop:      -> @module.getOrInsertExternalFunction "_ejs_record_binop",      types.void, [types.int32, types.string, types.EjsValue, types.EjsValue]
+        record_assignment: -> @module.getOrInsertExternalFunction "_ejs_record_assignment", types.void, [types.int32, types.EjsValue]
+
 exports.createInterface = (module) ->
         runtime =
                 module: module


Use semver when checking compatibility of dependencies

--- lib/updater.coffee
+++ lib/updater.coffee
@@ -160,7 +160,7 @@
     pkg = atom.packages.loadPackage(pkgName)
     currentVersion = pkg?.metadata.version

-    currentVersion isnt latestVersion
+    not semver.satisfies(currentVersion, latestVersion)

   _someDependencyIsMismatched: ->
     {packageDependencies} = require('../package.json')


use SVG transformation matrix to scale drawing marks

--- app/classifier/frame-annotator.cjsx
+++ app/classifier/frame-annotator.cjsx
@@ -54,12 +54,37 @@
     vertical = sizeRect?.height / @props.naturalHeight || 0
     {horizontal, vertical}

+  # transforms the event coordinates
+  # to points in the SVG coordinate system
+  eventCoordsToSVGCoords: (x, y) ->
+    svg = @refs.svgSubjectArea
+    newPoint = svg.createSVGPoint()
+    newPoint.x = x
+    newPoint.y = y
+    matrixForWindowCoordsToSVGUserSpaceCoords = @getMatrixForWindowCoordsToSVGUserSpaceCoords()
+    pointForSVGSystem = newPoint.matrixTransform(matrixForWindowCoordsToSVGUserSpaceCoords)
+    pointForSVGSystem
+
+  # find the original matrix for the SVG coordinate space
+  getMatrixForWindowCoordsToSVGUserSpaceCoords: ->
+    transformationContainer = @refs.transformationContainer
+    transformationContainer.getScreenCTM().inverse()
+
+  # transforms the difference between two event coordinates
+  # into the difference as represent in the SVG coordinate system
+  normalizeDifference: (e, d) ->
+    difference = {}
+    normalizedPoint1 = @eventCoordsToSVGCoords(e.pageX - d.x, e.pageY - d.y)
+    normalizedPoint2 = @eventCoordsToSVGCoords(e.pageX, e.pageY)
+    difference.x =  normalizedPoint2.x - normalizedPoint1.x
+    difference.y =  normalizedPoint2.y - normalizedPoint1.y
+    difference
+
+  # get the offset of event coordiantes in terms of the SVG coordinate system
   getEventOffset: (e) ->
-    scale = @getScale()
-    sizeRect = @getSizeRect()
-    x = (e.pageX - sizeRect?.left) / scale.horizontal || 0
-    y = (e.pageY - sizeRect?.top) / scale.vertical || 0
-    {x, y}
+    eventOffset = @eventCoordsToSVGCoords(e.clientX, e.clientY)
+    console.log "eventOffset", eventOffset
+    eventOffset

   render: ->
     taskDescription = @props.workflow.tasks[@props.annotation?.task]
@@ -109,19 +134,21 @@
       <div className="subject-area">
         {if BeforeSubject?
           <BeforeSubject {...hookProps} />}
-        <svg className="subject" style=svgStyle viewBox={createdViewBox} {...svgProps}>
-          <rect ref="sizeRect" width={@props.naturalWidth} height={@props.naturalHeight} fill="rgba(0, 0, 0, 0.01)" fillOpacity="0.01" stroke="none" />
-          {if type is 'image'
-            <Draggable onDrag={@props.panByDrag} disabled={@props.disabled}>
-              <SVGImage className={"pan-active" if @props.panEnabled} src={src} width={@props.naturalWidth} height={@props.naturalHeight} modification={@props.modification} />
-            </Draggable>
-          }
+        <svg ref="svgSubjectArea" className="subject" style=svgStyle viewBox={createdViewBox} {...svgProps}>
+          <g ref="transformationContainer" >
+            <rect ref="sizeRect" width={@props.naturalWidth} height={@props.naturalHeight} fill="rgba(0, 0, 0, 0.01)" fillOpacity="0.01" stroke="none" />
+            {if type is 'image'
+              <Draggable onDrag={@props.panByDrag} disabled={@props.disabled}>
+                <SVGImage className={"pan-active" if @props.panEnabled} src={src} width={@props.naturalWidth} height={@props.naturalHeight} modification={@props.modification} />
+              </Draggable>
+            }

-          {if InsideSubject? && !@props.panEnabled
-            <InsideSubject {...hookProps} />}
+            {if InsideSubject? && !@props.panEnabled
+              <InsideSubject {...hookProps} />}

-          {for anyTaskName, {PersistInsideSubject} of tasks when PersistInsideSubject?
-            <PersistInsideSubject key={anyTaskName} {...hookProps} />}
+            {for anyTaskName, {PersistInsideSubject} of tasks when PersistInsideSubject?
+              <PersistInsideSubject key={anyTaskName} {...hookProps} />}
+          </g>
         </svg>
         {@props.children}
         {if @state.alreadySeen


change crypto to always use utf-8 as it works with aws

--- app/coffee/DocArchiveManager.coffee
+++ app/coffee/DocArchiveManager.coffee
@@ -32,7 +32,7 @@
 			if err? || res.statusCode != 200
 				logger.err err:err, res:res, project_id:project_id, doc_id: doc._id, statusCode: res?.statusCode, "something went wrong archiving doc in aws"
 				return callback new Error("Error in S3 request")
-			md5lines = crypto.createHash("md5").update(JSON.stringify(doc.lines)).digest("hex")
+			md5lines = crypto.createHash("md5").update(JSON.stringify(doc.lines), "utf8").digest("hex")
 			md5response = res.headers.etag.toString().replace(/\"/g, '')
 			if md5lines != md5response
 				logger.err responseMD5:md5response, linesMD5:md5lines,  project_id:project_id, doc_id: doc?._id, "err in response md5 from s3"


Removed a console.log sentence

Removed a console.log sentence

--- app/coffee/directives/common.coffee
+++ app/coffee/directives/common.coffee
@@ -351,7 +351,6 @@

         scope.paginatorSetPage = (page) ->
             numPages = getNumPages()
-            console.log page, numPages
             if page <= numPages and page > 0
                 scope[setPageVar](page)


セーブ時にページ遷移するかどうかを確認してしまう不具合を修正

セーブ時にページ遷移するかどうかを確認してしまう不具合を修正

--- app/assets/javascripts/editor.js.coffee
+++ app/assets/javascripts/editor.js.coffee
@@ -3,6 +3,8 @@
 # You can use CoffeeScript in this file: http://coffeescript.org/

 $ ->
+  saving = false
+
   textEditor = ace.edit('text-editor')
   textEditor.setTheme('ace/theme/clouds')
   textEditor.setShowInvisibles(true)
@@ -28,6 +30,7 @@
       data =
         filename: filename
         source: session.getDocument().getValue()
+      saving = true
       location.href = '/editor/save_file?' + $.param(data)

   $('#load-button').click ->
@@ -60,7 +63,8 @@
   )

   window.onbeforeunload = (event) ->
-    if session.getDocument().getValue().length > 0
+    if !saving && session.getDocument().getValue().length > 0
       return '作成中のプログラムが消えてしまいます。'
     else
+      saving = false
       return


Make end_time's datepicker default to displaying the start_time.  Fixes #252

--- app/assets/javascripts/events.js.coffee
+++ app/assets/javascripts/events.js.coffee
@@ -17,3 +17,9 @@
       visible: true
       api: true).columns.adjust()
     return
+
+  $('#event_start_time').change ->
+    eventStartTime = $('#event_start_time').datetimepicker('getValue')
+    eventEndTime = $('#event_end_time').datetimepicker('getValue')
+    if (not eventEndTime?) or (eventStartTime > eventEndTime)
+      $('#event_end_time').datetimepicker('setOptions', {startDate: eventStartTime})


More line-height

More line-height

--- app/assets/javascripts/heroes.js.coffee
+++ app/assets/javascripts/heroes.js.coffee
@@ -33,7 +33,7 @@
       })
     else
       $('.hero-container').removeClass('loading')
-      $('.hero').html("
Unfortunately, we can't find this hero on GitHub. Are you sure you have the username right?
")
+      $('.hero').html("
Unfortunately, we can't find this hero on GitHub. Are you sure you have the username right?
")

   initEventHandlers: ->
     $('a.popup').live 'click', (e) =>


Reduce the animation duration more

Reduce the animation duration more

--- app/assets/javascripts/static.js.coffee
+++ app/assets/javascripts/static.js.coffee
@@ -13,7 +13,7 @@

     $this.counter = starting_point

-    tween = createjs.Tween.get($this).to( {counter: $target_count}, (1200 + index * 800) , createjs.Ease.quintOut)
+    tween = createjs.Tween.get($this).to( {counter: $target_count}, (1000 + index * 400) , createjs.Ease.quintOut)

     tween.addEventListener 'change', (event) ->
       # if the number is under 90 of the target, increment number by thousands


Store the datatable's current state clientside

--- app/assets/javascripts/suites.js.coffee
+++ app/assets/javascripts/suites.js.coffee
@@ -41,6 +41,7 @@
             this.data("table-state")
         fnStateSave: (settings, state) ->
             url = this.data("save-local-state-path")
+            this.data("current-state", state)
             window.Digilys.dataTable.saveState(state, url)
     )


Better autofocus logic for Choice component

--- app/classifier/tasks/survey/choice.cjsx
+++ app/classifier/tasks/survey/choice.cjsx
@@ -74,9 +74,11 @@
     canIdentify = answerProvided.every (answer) -> answer is true

   render: ->
+    hasFocus = false
     choice = @props.task.choices[@props.choiceID]

       {unless choice.images.length is 0
+        hasFocus = choice.images.length > 1
         <ImageFlipper images={@props.task.images[filename] for filename in choice.images} />}

{choice.label}

@@ -86,14 +88,19 @@

             Often confused with
             {' '}
-            {for otherChoiceID in choice.confusionsOrder
+            {for otherChoiceID, i in choice.confusionsOrder
               otherChoice = @props.task.choices[otherChoiceID]

-                <TriggeredModalForm className="survey-task-confusions-modal" trigger={
-
-                    {otherChoice.label}
-
-                } style={maxWidth: '60ch'}>
+                <TriggeredModalForm
+                  className="survey-task-confusions-modal"
+                  triggerProps={autoFocus: not hasFocus and i is 0}
+                  trigger={
+
+                      {otherChoice.label}
+
+                  }
+                  style={maxWidth: '60ch'}
+                >
                   <ImageFlipper images={@props.task.images[filename] for filename in otherChoice.images} />
                   <Markdown content={choice.confusions[otherChoiceID]} />

@@ -103,6 +110,7 @@

                 </TriggeredModalForm>
                 {' '}
+                {hasFocus = true}
               }

}

@@ -117,7 +125,7 @@
               'radio'

{question.label}

-              {for answerID in question.answersOrder
+              {for answerID, i in question.answersOrder
                 answer = question.answers[answerID]
                 isChecked = if question.multiple
                   answerID in (@state.answers[questionID] ? [])
@@ -126,10 +134,20 @@
                 isFocused = @state.focusedAnswer is "#{questionID}/#{answerID}"

-                    <input ref={questionID} name={questionID} type={inputType} checked={isChecked} onChange={@handleAnswer.bind this, questionID, answerID} onFocus={@handleFocus.bind this, questionID, answerID} onBlur={@handleFocus.bind this, null, null} />
+                    <input
+                      ref={questionID}
+                      name={questionID}
+                      type={inputType}
+                      autoFocus={not hasFocus and i is 0}
+                      checked={isChecked}
+                      onChange={@handleAnswer.bind this, questionID, answerID}
+                      onFocus={@handleFocus.bind this, questionID, answerID}
+                      onBlur={@handleFocus.bind this, null, null}
+                    />
                     {answer.label}

                   {' '}
+                  {hasFocus=true}
                 }

}

@@ -137,7 +155,7 @@

}

-        <button type="button" className="minor-button" onClick={@props.onCancel}>Cancel</button>
+        <button type="button" autoFocus={not hasFocus} className="minor-button" onClick={@props.onCancel}>Cancel</button>
         {' '}
         <button type="button" className="standard-button" disabled={not @checkFilledIn()} onClick={@handleIdentification}>
           Identify


try to fixe traceback bug

try to fixe traceback bug

--- app/controllers/index_controller.coffee
+++ app/controllers/index_controller.coffee
@@ -17,8 +17,8 @@
 action 'doctypes', ->

     #------INDEX SEVERAL ID FOR TEST
-    dataSystem.indexId "39bade34f76d6b32234c3974c8004ca9", ["description"]
-    dataSystem.indexId "39bade34f76d6b32234c3974c80059f0", ["description"]
+    #dataSystem.indexId "39bade34f76d6b32234c3974c8004ca9", ["description"]
+    #dataSystem.indexId "39bade34f76d6b32234c3974c80059f0", ["description"]

     #------PREPARE REQUESTS


SocialWorker: remove unused daisy

SocialWorker: remove unused daisy

--- workers/social/testhelper/models/computeproviders/credentialhelper.coffee
+++ workers/social/testhelper/models/computeproviders/credentialhelper.coffee
@@ -1,5 +1,4 @@
-{ daisy
-  expect
+{ expect
   withConvertedUser
   generateRandomEmail
   generateRandomString } = require '../../index'


Remove code that depended on the national dashboard filters

--- app/scripts/natdashboardctrl.coffee
+++ app/scripts/natdashboardctrl.coffee
@@ -109,8 +109,7 @@
         $scope.params.lga
         $scope.params.ward)

-    $scope.getStatus = (changed) ->
-      # the filtering has changed, reset the selected status
+    $scope.getStatus = () ->
       $scope.statusChoice = "all"

       modalSpinner.open()
@@ -146,12 +145,6 @@
           $scope.tiles = _.pairs(_.pick(statusMap,'functional','needs repair'))
           $scope.tiles.push([gettext('population cover'), popCover])
           modalSpinner.close()
-
-      if changed == 'region'
-        getLGA()
-        getWard()
-      if changed == 'lga'
-        getWard()

       getProblems()
       drawPlots()
@@ -189,10 +182,10 @@
           $scope.params.ward = null

         if newgf == "region"
-          $scope.getStatus("region")
+          $scope.getStatus()
         else
           $scope.params[groupField] = fieldVal
-          $scope.getStatus(groupField)
+          $scope.getStatus()
       ,0)

     barDblClick = (d) ->
@@ -275,9 +268,6 @@
         popData = data
         $scope.getStatus())

-      getRegion()
-      getLGA()
-      getWard()
       getProblems()

     initView()


Removed unused methods

Removed unused methods

--- core/app/backbone/models/notification.coffee
+++ core/app/backbone/models/notification.coffee
@@ -1,11 +1 @@
 class window.Notification extends Backbone.Model
-  channel: ->
-    return @_cached_channel if @cached_channel?
-    return null unless @channel_id()
-
-    @_cached_channel = new Channel(id: @channel_id())
-    @_cached_channel.fetch()
-    @_cached_channel
-
-  channel_id: -> @get('activity').to_channel_id
-


WHITESPACE

WHITESPACE

--- app/services/SolutionService.coffee
+++ app/services/SolutionService.coffee
@@ -8,7 +8,7 @@
       @goals.push g

   isSolution: (clickedCells) ->
-    return false unless clickedCells? and clickedCells.length >=3
+    return false unless clickedCells? and clickedCells.length >= 3
     solution = @getSolutionString clickedCells
     value = InputSolver.compute solution
     return false unless value in @goals


Added a pending spec for a lambda call.

Added a pending spec for a lambda call.

--- src/coffee/expression/inline.spec.coffee
+++ src/coffee/expression/inline.spec.coffee
@@ -190,6 +190,8 @@
                     if ex.reason
                         fail JSON.stringify ex, null, 4
                     else throw ex
+
+        xrun = (config) -> xit config.description, ->

         run
             description: "basic let"
@@ -385,4 +387,43 @@
                                         ends: 73
                                 ]
                         ]
+                ]
+
+        xrun
+            description: "basic inline lambda call"
+            input:  "(a b -> a * b + 3)(7, 5) - 9"
+            output:
+                call: "subtract"
+                starts: 25
+                ends: 25
+                with: [
+                        call: "add"
+                        starts: 14
+                        ends: 14
+                        with: [
+                                call: "multiply"
+                                starts: 10
+                                ends: 10
+                                with: [
+                                        primitive: "integer"
+                                        value: 7
+                                        starts: 19
+                                        ends: 19
+                                    ,
+                                        primitive: "integer"
+                                        value: 5
+                                        starts: 22
+                                        ends: 22
+                                ]
+                            ,
+                                primitive: "integer"
+                                value: 3
+                                starts: 16
+                                ends: 16
+                        ]
+                    ,
+                        primitive: "integer"
+                        value: 9
+                        starts: 27
+                        ends: 27
                 ]


require async button due to cherry pick mis match

--- src/components/task-step/step-mixin.cjsx
+++ src/components/task-step/step-mixin.cjsx
@@ -3,6 +3,7 @@

 {CardBody} = require '../pinned-header-footer-card/sections'
 Details = require '../task/details'
+AsyncButton = require '../async-button'
 {TaskStore} = require '../../flux/task'
 {TaskStepStore} = require '../../flux/task-step'


Fix generating website with new streaming api

--- docs/generate_website.coffee
+++ docs/generate_website.coffee
@@ -49,7 +49,11 @@
       delete attrs.height # used for pdf generation

       # create a PDF and run the example
-      doc = new PDFDocument
+      doc = new PDFDocument
+      f = "img/#{imageIndex++}"
+      file = fs.createWriteStream "#{f}.pdf"
+      doc.pipe file
+
       doc.translate doc.x, doc.y
       doc.scale 0.8
       doc.x = doc.y = 0
@@ -60,15 +64,17 @@

       delete attrs.title
       delete attrs.alt
-      attrs.href = "img/#{imageIndex}.png"
+      attrs.href = "#{f}.png"

       # write the PDF, convert to PNG using the mac `sips`
       # command line tool, and trim with graphicsmagick
-      do (f = "img/#{imageIndex++}") ->
-        doc.write "#{f}.pdf", ->
+      do (f) ->
+        file.on 'finish', ->
           exec "sips -s format png #{f}.pdf --out #{f}.png", ->
             fs.unlink "#{f}.pdf"
             exec "gm convert #{f}.png -trim #{f}.png"
+
+      doc.end()

 pages = []
 for file in files


Fixup event extension namespace helper.

Fixup event extension namespace helper.

--- src/js/jquery.extension.hlf.event.coffee
+++ src/js/jquery.extension.hlf.event.coffee
@@ -62,7 +62,7 @@

     # `attr` is an internal formatter for attribute names,
     # mainly those of jQuery data keys.
-    attr = (name='') -> "#{hoverIntent.toString 'attr'}#{name}"
+    attr = (name='') -> "#{hoverIntent.toString 'attr'}-#{name}"

     # `debugLog` is our internal logger. It's optimized to be a noop if
     # hover intent debugging is off.


Use app activate event instead of defunct activate-with-no-open-windows

This restores the ability to open a Window by single-clicking the dock
icon on macOS.

Signed-off-by: Max Brunsfeld <78036c9b69b887700d5846f26a788d53b201ffbb@github.com>

--- src/main-process/atom-application.coffee
+++ src/main-process/atom-application.coffee
@@ -250,9 +250,10 @@
       event.preventDefault()
       @openUrl({urlToOpen, @devMode, @safeMode})

-    @disposable.add ipcHelpers.on app, 'activate-with-no-open-windows', (event) =>
-      event?.preventDefault()
-      @emit('application:new-window')
+    @disposable.add ipcHelpers.on app, 'activate', (event, hasVisibleWindows) =>
+      unless hasVisibleWindows
+        event?.preventDefault()
+        @emit('application:new-window')

     @disposable.add ipcHelpers.on ipcMain, 'restart-application', =>
       @restart()


refactor band creation method

refactor band creation method

--- controllers/bands.coffee
+++ controllers/bands.coffee
@@ -62,21 +62,14 @@
 exports.create = (req, res) ->
   userId = req.user._id
   req.body.submitted_by = userId
-  new Band(req.body).save (err, result1) ->
+  new Band(req.body).save (err, result) ->
     return res.status(500).json error: err if err?
-    data =
-      parent: result1._id,
-      user_id: userId
-      user_ip_address: getIpAddress(req.session.ipAddress)
-    new Vote(data).save (err, result2) ->
-      return res.status(500).json error: err if err?
-      # this is really hacky; should be better way
-      result1.children = [result2._id]
-      result1.userHasVotedOn = true
-      return res.status(200).json partialResponse(result1)
+    req.params.id = result._id
+    # when creating a new band record simultaneously add a vote
+    return voteForBand(req, res)

 # POST /api/bands/:id/vote
-exports.vote = (req, res) ->
+exports.vote = voteForBand = (req, res) ->
   data =
     parent: req.params.id
     user_id: req.user._id


Cut log scrollback in half.

Cut log scrollback in half.

ArchiveBot is currently running with 31 concurrent jobs.  This is more
than has come before, but it is arguably a reasonable concurrency level.
With 512 entries per backlog, the dashboard eats up around a gigabyte of
RAM in Firefox and Chrome.  Hopefully, cutting down the scrollback will
bring that level down a bit.

--- dashboard/assets/javascripts/views.js.coffee
+++ dashboard/assets/javascripts/views.js.coffee
@@ -72,7 +72,7 @@

   tagName: 'section'

-  maxSize: 512
+  maxSize: 256

   frozenBinding: 'job.frozen'
   showIgnoresBinding: 'job.showIgnores'


Prevent deafult on keycommand to avoid autofocus issue when changing views. (Hack)

--- src/common/keyCommands/keyCommands.coffee
+++ src/common/keyCommands/keyCommands.coffee
@@ -56,20 +56,26 @@
     ## This is hacky, and prevents ":" from being used as an actual key binding.
     delim = attrs.ycKey.indexOf(":")

+    #parse yc-key attribute
     [key, desc] = if delim > 0
       [attrs.ycKey.substring(0,delim), attrs.ycKey.substring(delim+1)]
     else
       [attrs.ycKey, element.text()]

+    #parse key as an array if appropriate.
     if key.trim().indexOf("[") is 0
       key = scope.$eval(key)

+    preventDefault = true
+
     if attrs.ycKeyCommand?
-      cmd = () -> safeApply(scope, () ->
+      cmd = (e) -> safeApply(scope, () ->
+        e.preventDefault() if preventDefault
         scope.$eval(attrs.ycKeyCommand)
       )
     else if (element.prop('tagName')?.match /a/i)
-      cmd = () ->
+      cmd = (e) ->
+        e.preventDefault() if preventDefault
         if(element.prop('href')?.length > 0)
           safeApply scope, () ->
             $window.location.href = element.prop('href')
@@ -78,7 +84,10 @@
           element.triggerHandler 'click'
     else
       #keeping this out of $apply because ng-click calls apply.
-      cmd = () -> element.triggerHandler 'click'
+      cmd = (e) ->
+        e.preventDefault() if preventDefault
+        element.triggerHandler 'click'
+

     keyCommands.bind scope, key, cmd, desc
 )


Properly show user login on favorite click

--- resources/assets/coffee/react/beatmapset-page/header.coffee
+++ resources/assets/coffee/react/beatmapset-page/header.coffee
@@ -22,8 +22,8 @@
   toggleFavorite: (e) ->
     e.preventDefault()

-    if _.isEmpty currentUser
-      userLogin.show()
+    if !currentUser.id?
+      userLogin.show e.target
     else
       $.publish 'beatmapset:favorite:toggle'


dont need to falsely set changes on event plan load, prevents false showing of unsaved changes modal

--- src/components/task-plan/event/index.cjsx
+++ src/components/task-plan/event/index.cjsx
@@ -12,11 +12,6 @@
 EventPlan = React.createClass
   displayName: 'EventPlan'
   mixins: [PlanMixin]
-
-  componentWillMount: ->
-    {id} = @props
-    TaskPlanActions.setEvent(id)
-
   render: ->
     {id, courseId} = @props
     builderProps = _.pick(@state, 'isVisibleToStudents', 'isEditable', 'isSwitchable')


#511 Defocus any element when hovering the plane

--- app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.coffee
+++ app/assets/javascripts/oxalis/controller/viewmodes/plane_controller.coffee
@@ -125,7 +125,9 @@
           0
         ]

-      over : => @planeView.setActiveViewport( @activeViewport = planeId )
+      over : =>
+        $(':focus').blur()
+        @planeView.setActiveViewport( @activeViewport = planeId )

       scroll : @scrollPlanes
     }


invoke that function!

invoke that function!

--- app/assets/javascripts/md_simple_editor.js.coffee
+++ app/assets/javascripts/md_simple_editor.js.coffee
@@ -96,7 +96,7 @@
   txtarea.scrollTop = scrollPos

 initializeEditor = ->
-  md_simple_editor
+  md_simple_editor()
   $(document).off 'turbolinks:load page:load ready', initializeEditor
   $('.preview_md').click ->
     preview()


daterange

daterange

--- app/assets/javascripts/pt/tags/datetime.js.coffee
+++ app/assets/javascripts/pt/tags/datetime.js.coffee
@@ -30,3 +30,15 @@

         $el.data 'ptDateLoaded', true
         $el.datetimepicker 'show'
+  daterange:
+    event: 'click'
+    callback: ->
+      $el     = $ this
+      attr    = $el.ptAttr()
+
+      unless $el.data 'ptDateRangeLoaded'
+        $el.daterangepicker
+          format: 'MM/DD/YYYY'
+
+        $el.data 'ptDateRangeLoaded', true
+        $el.click()


Set multipart upload threshold to minimum value

--- app/assets/javascripts/uploadcare/settings.coffee
+++ app/assets/javascripts/uploadcare/settings.coffee
@@ -42,7 +42,7 @@
     previewUrlCallback: null
     # fine tuning
     imagePreviewMaxSize: 25 * 1024 * 1024
-    multipartMinSize: 25 * 1024 * 1024
+    multipartMinSize: 10 * 1024 * 1024
     multipartPartSize: 5 * 1024 * 1024
     multipartMinLastPartSize: 1024 * 1024
     multipartConcurrency: 4


removed unused UI references

removed unused UI references

--- app/assets/javascripts/dashboard/views/dashboard_view.coffee
+++ app/assets/javascripts/dashboard/views/dashboard_view.coffee
@@ -37,14 +37,6 @@
       </div>
     </div>
   """)
-
-  ui :
-    "tabDatasets" : "#tab-datasets"
-    "tabTasks" : "#tab-tasks"
-    "tabExplorative" : "#tab-explorative"
-    "tabTrackedTime" : "#tab-tracked-time"
-    "tabPane" : ".tab-pane"
-

   events :
     "click #tab-datasets" : "showDatasets"


When closing an entry, the viewport no longer "jumps" to the top of the page.

--- app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-OpenEntrySvc.js.coffee
@@ -27,7 +27,6 @@
   close: (entry)->
     index = $rootScope.open_entries.indexOf entry
     $rootScope.open_entries.splice index, 1 if index != -1
-    $location.hash('')

   #---------------------------------------------
   # Reset the entries open/close state. If the user has selected the "open all entries" option for


Exclude attacks when randomizing individual card unless attack card is specified.

--- assets/js/viewmodels/main.js.coffee
+++ assets/js/viewmodels/main.js.coffee
@@ -135,10 +135,15 @@
          @replaceSelectedCardsWithKingdom(result.kingdom) if result

       randomizeIndividualSelectedCard: =>
+         excludeTypes = []
+         if @dialog.selectedType() == Randomizer.Type.NONE and !@allowAttacks()
+            excludeTypes.push(Randomizer.Type.ATTACK)
+
          result = Randomizer.createKingdom(@dominionSets, {
             setIds: (ko.unwrap(set.id) for set in @dialog.sets when set.active())
             includeCardIds: @extractCardIds(@getUnselectedCards())
             excludeCardIds: @extractCardIds(@getSelectedCards())
+            excludeTypes: excludeTypes
             includeEventIds: (event.id for event in @kingdom.events)
             includeLandmarkIds: (landmark.id for landmark in @kingdom.landmarks)
             requiredType: @dialog.selectedType()


Update CampaignView width to fit volcano world

--- app/views/play/CampaignView.coco.coffee
+++ app/views/play/CampaignView.coco.coffee
@@ -927,7 +927,7 @@
     return if 0.2 < fraction < 0.8
     direction = if fraction < 0.5 then 1 else -1
     magnitude = 0.2 * bodyWidth * (if direction is -1 then fraction - 0.8 else 0.2 - fraction) / 0.2
-    portalsWidth = 2536  # TODO: if we add campaigns or change margins, this will get out of date...
+    portalsWidth = 2853  # TODO: if we add campaigns or change margins, this will get out of date...
     scrollTo = $portals.offset().left + direction * magnitude
     scrollTo = Math.max bodyWidth - portalsWidth, scrollTo
     scrollTo = Math.min 0, scrollTo


Update main.prod.coffee

Update main.prod.coffee

--- config/main.prod.coffee
+++ config/main.prod.coffee
@@ -212,7 +212,7 @@
       ports             :
         incoming        : "#{kontrol.port}"
       supervisord       :
-        command         : "#{GOBIN}/kontrol -region #{region} -machines #{etcd} -environment #{environment} -mongourl #{KONFIG.mongo} -port #{kontrol.port} -privatekey #{kontrol.privateKeyFile} -publickey #{kontrol.publicKeyFile} -artifactport #{kontrol.artifactPort} -storage etcd -postgres-dbname #{kontrolPostgres.dbname} -postgres-host #{kontrolPostgres.host} -postgres-port #{kontrolPostgres.port} -postgres-username #{kontrolPostgres.username} -postgres-password #{kontrolPostgres.password}"
+        command         : "#{GOBIN}/kontrol -region #{region} -machines #{etcd} -environment #{environment} -mongourl #{KONFIG.mongo} -port #{kontrol.port} -privatekey #{kontrol.privateKeyFile} -publickey #{kontrol.publicKeyFile} -artifactport #{kontrol.artifactPort} -storage postgres -postgres-dbname #{kontrolPostgres.dbname} -postgres-host #{kontrolPostgres.host} -postgres-port #{kontrolPostgres.port} -postgres-username #{kontrolPostgres.username} -postgres-password #{kontrolPostgres.password}"
       nginx             :
         websocket       : yes
         locations       : ["~^/kontrol/.*"]


default-apps: Changed event handler for open with menu items.

--- client/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
@@ -364,7 +364,7 @@
     {extensionToApp} = appsController
     possibleApps     = extensionToApp[fileExtension] or extensionToApp.txt
     for appName in possibleApps
-      items[appName] = action: "openFile"
+      items[appName] = action: "openFileWithApp"

     items["separator"]            = type     : "separator"
     items["Other Apps"]           = action   : "showOpenWithModal", separator : yes


Remove duplicates

Remove duplicates

--- client/landing/app/Applications/WebTerm.kdapplication/AppController.coffee
+++ client/landing/app/Applications/WebTerm.kdapplication/AppController.coffee
@@ -24,11 +24,12 @@
     preCondition :
       condition  : (options, cb)->
         {params} = options
-        (KD.getSingleton 'vmController').fetchDefaultVmName (defaultVmName)=>
+        vmController = KD.getSingleton 'vmController'
+        vmController.fetchDefaultVmName (defaultVmName)->
           vmName = params?.vmName or defaultVmName
           return cb no  unless vmName
-          KD.getSingleton("vmController").info vmName, (err, vm, info)=>
-            cb  if info?.state is 'RUNNING' then yes else no
+          vmController.info vmName, (err, vm, info)->
+            cb  info?.state is 'RUNNING'
       failure    : (options, cb)->
         KD.getSingleton("vmController").askToTurnOn 'WebTerm', cb


Remove Brunch config.

Remove Brunch config.

--- config.coffee
+++ config.coffee
@@ -1,36 +0,0 @@
-exports.config =
-
-	files:
-		javascripts:
-			joinTo:
-				'app.js': /^app/
-				'vendor.js': /^vendor/
-			order:
-				before: [
-					'vendor/jquery-1.9.1.js',
-					'vendor/underscore.js',
-					'vendor/json2.js'
-					'vendor/backbone.js',
-					'vendor/backbone.marionette.js',
-					'vendor/jqm-config.js',
-					'vendor/jquery.mobile-1.3.1.js',
-					'app/main.js'
-				]
-				after: [
-				]
-
-		stylesheets:
-			joinTo:
-				'app.css': /^(app|vendor)/
-			order:
-				before: [
-					'vendor/jquery.mobile-1.3.1.css'
-				]
-				after: []
-
-	conventions:
-		assets: /assets(\/|\\)/
-
-	modules:
-		wrapper: false
-		definition: false


Use new plugin config structure

Use new plugin config structure

--- config.coffee
+++ config.coffee
@@ -19,7 +19,8 @@
   modules:
     addSourceURLs: true

-  coffeelint:
-    options:
-      max_line_length:
-        level: "ignore"
+  plugins:
+    coffeelint:
+      options:
+        max_line_length:
+          level: "ignore"


[skip] Added Chase's phone number; typed realistically (10m)

--- app/assets/javascripts/globals.coffee
+++ app/assets/javascripts/globals.coffee
@@ -4,12 +4,29 @@
     "2": "L783461256"
     "3": "330893481"
     "4": "M718460675"
+    "5": "6185798636"
+
+  len = 1
+  interval = null
+  $el = null
+  typeItValue = null
+  timeout = 35
+
+  typeIt = ->
+    $el.val typeItValue.substr(0, len)
+    len = len + 1
+    if interval and len > typeItValue.length
+      console.log("clear interval")
+      window.clearInterval(interval)

   for key, value of shortcuts
     ( (value) ->
       Mousetrap.bind ["alt+#{key}"], (e) ->
         e.preventDefault() if e.preventDefault
-        $(':focus').val value
+        $el = $("input[name=q], input[type=tel]")
+        typeItValue = value
+        len = 1
+        interval = window.setInterval(typeIt, timeout)
     )(value)

   Mousetrap.bind ["alt+1"], (e) ->


Rename `show_unattached` param for custom curves

Ref quintel/etsource#2456

--- app/assets/javascripts/lib/app.coffee
+++ app/assets/javascripts/lib/app.coffee
@@ -238,7 +238,7 @@

       # Ajax request.
       req = $.ajax(
-        url: App.scenario.url_path() + '/custom_curves?show_unattached=true'
+        url: App.scenario.url_path() + '/custom_curves?include_unattached=true'
         method: 'GET'
         dataType: 'json'
       )


remove popover from main

remove popover from main

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -102,12 +102,6 @@
     placement: "top"
     title: 'There have been new comments since you last visited the group.'

-# adds bootstrap popovers to group activity indicators
-$ ->
-  $(".unread-group-activity").tooltip
-    placement: "top"
-    title: 'There have been new comments on this discussion since you last visited the group.'
-
 # character count for statement on discussion:show page
 pluralize_characters = (num) ->
   if(num == 1)


n-threshold

n-threshold

--- app/assets/javascripts/zvg/zvg.coffee
+++ app/assets/javascripts/zvg/zvg.coffee
@@ -348,32 +348,15 @@

   series_1_domain: (d) ->
     @accessor('series_1_domain', d)
-    #if d
-    #@_series_1_domain = d
-    #return @
-    #@_series_1_domain

   series_2_domain: (d) ->
     @accessor('series_2_domain', d)
-    #if d
-    #@_series_2_domain = d
-    #return @
-    #@_series_2_domain

   series_3_domain: (d) ->
     @accessor('series_3_domain', d)
-    #if d
-    #  @_series_3_domain = d
-    #  return @
-    #@_series_3_domain

   legend_labels: (d) ->
     @accessor('legend_labels', d)
-    #if d
-    #@_legend_labels = d
-    #return @
-    #@_legend_labels
-

   initializeSvg: (element = 'body') ->
     @outer = d3.select(@element).append('div').attr('class', 'zvg')
@@ -400,7 +383,9 @@
     @_render(args...)
     @afterRender()

-  beforeRender: -> null
+  beforeRender: ->
+    @_hide_columns_below_n(@_n_threshold)
+
   _render: -> null
   afterRender: ->
     @_adjust_svg_dimensions()
@@ -439,7 +424,6 @@
   # x offset for point chart scale on left
   x_offset: 0

-
   filter_data: (filters) ->
     if filters
       @_filters = filters
@@ -448,6 +432,11 @@
     else
       @data(@raw_data)

+  _hide_columns_below_n: (n) ->
+    n_value = @series_2_label_sum
+    select_columns_above = (n, data) ->
+      { key: data.key, values: data.values.filter((v) -> n_value(v) >= n) }
+    @_data = (d for d in (select_columns_above(n, d) for d in @_data) when d.values.length > 0)

   # pre-establishes indexes for the spacing and grouping of series 1 data
   # based on its contents (necessary because of the variable length of data within
@@ -648,8 +637,6 @@
   key_function: (data) -> data.key
   values_function: (data) -> data.values

-
-
   adjustSeries1EndLabels: ->
     # initial condition is horizontal labels with text-anchor: middle
     end_index = @series1LabelMap.length - 1


Less hacky retrieval of width

Less hacky retrieval of width

--- app/assets/js/components/queue.coffee
+++ app/assets/js/components/queue.coffee
@@ -81,7 +81,7 @@

   componentDidMount: () ->
     @setState({
-      width: document.querySelector('[data-reactid="' + @_rootNodeID + '"] svg').offsetWidth
+      width: @getDOMNode().querySelector('svg').offsetWidth
     })

   showToolTip: (datum) ->


Log loaded buckets

Log loaded buckets

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -18,6 +18,7 @@

   batchCount : 0
   roundTripTime : 0
+  loadedBucketList: []

   constructor : (@dataSetName, @cube, @dataLayerName, @testData) ->
@@ -139,10 +140,7 @@
               bucketData = @decode(responseBuffer.subarray(offset, offset += (@cube.BUCKET_LENGTH >> 1)))
             else
               bucketData = responseBuffer.subarray(offset, offset += @cube.BUCKET_LENGTH)
-            if @testData
-              id = bucket[0] + bucket[1] * 100 + bucket[2] * 10000
-              for i in [0...bucketData.length]
-                bucketData[i] = (id >> (8 * ((@cube.BIT_DEPTH >> 3) - 1 - (i % (@cube.BIT_DEPTH >> 3))))) % 256
+            @loadedBucketList.push(bucket)
             @cube.setBucketByZoomedAddress(bucket, bucketData)

         =>


Adds a title to the file preview iframe

Adds a title to the file preview iframe

This allows it to work better for screenreaders.  According to
acannon, this is practically the only place (frames and iframes)
where titles work for this.

fixes CNVS-18563

Test Plan:
  - Go to New Files
  - Use a screenreader to preview a file
  - The iframe with the preview in it should be read as "File
    Preview"

Change-Id: I9fc53e47a8c6a7edb33c62bbe2d8a93a88102ea8
Reviewed-on: https://gerrit.instructure.com/48887
Tested-by: Jenkins
Reviewed-by: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
QA-Review: Jahnavi Yetukuri <ed2b2672ccc2e59e56c0615df7f01b579202e014@instructure.com>
Product-Review: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>

--- app/coffeescripts/react_files/components/FilePreview.coffee
+++ app/coffeescripts/react_files/components/FilePreview.coffee
@@ -190,6 +190,7 @@
             @renderArrowLink('left') if @state.otherItems?.length > 0
             if @state.displayedItem
               iframe {
+                title: I18n.t('File Preview')
                 src: @state.displayedItem.get 'preview_url'
                 className: 'ef-file-preview-frame'
               }


Working "nothing here" action

Working "nothing here" action

--- app/controllers/subject_viewer.coffee
+++ app/controllers/subject_viewer.coffee
@@ -53,16 +53,14 @@

   onClassificationChange:  =>
     noAnnotations = @classification.annotations.length is 0
-    #TODO ignore this value from old model for now
-    #nothing = @classification.metadata.nothing
+    nothing = @classification.metadata.nothing
     isFavorite = !!@classification.favorite

     inSelection = @classification.metadata.inSelection
     @el.toggleClass 'no-annotations', noAnnotations
     @el.toggleClass 'favorite', isFavorite
-    #TODO ignoring this extra sense of nothingness for now
-    @finishButton.attr disabled: inSelection or noAnnotations
-    #@finishButton.attr disabled: inSelection or (noAnnotations and not nothing)
+
+    @finishButton.attr disabled: inSelection or (noAnnotations and not nothing)

   onClassificationAddSpecies: (classification, annotation) =>
     item = new AnnotationItem {@classification, annotation}
@@ -78,7 +76,7 @@
     @classification.updateAttribute 'favorite', false

   onChangeNothingCheckbox: ->
-    nothing = !!@nothingCheckbox.attr 'checked'
+    nothing = @nothingCheckbox.get(0).checked
     @classification.annotate {nothing}, true

   onClickFinish: ->
@@ -87,7 +85,7 @@
     @extraMessageContainer.hide() unless message

     @el.addClass 'finished'
-   # @classification.send() unless @classification.subject.metadata.empty
+    # @classification.send() unless @classification.subject.metadata.empty
     console?.log(@classification)

   onClickNext: ->


Sliderbar Component: Bugfix and code corrections

--- client/Framework/components/sliderbar/KDSliderBarView.coffee
+++ client/Framework/components/sliderbar/KDSliderBarView.coffee
@@ -1,3 +1,15 @@
+# example = new KDSliderBarView
+#   cssClass   : 'my-cute-slider'
+#   minValue   : 0
+#   maxValue   : 200
+#   interval   : 10
+#   width      : 500
+#   snap       : yes
+#   snapOnDrag : no
+#   drawBar    : yes - [0, 25, 50, 75, 100]
+#   showLabels : yes
+#   handles    : [100, 60]
+
 class KDSliderBarView extends KDCustomHTMLView
   constructor:(options = {}, data = {})->

@@ -8,6 +20,7 @@
     options.drawBar    ?= yes
     options.showLabels ?= yes
     options.snap       ?= yes
+    options.snapOnDrag ?= no
     options.width     or= 300

     super options, data
@@ -17,23 +30,22 @@

   createHandles:->
     for value in @getOption "handles"
-      @handles.push @addSubView handle = new KDSliderBarHandleView  {}, {value}
+      @handles.push @addSubView handle = new KDSliderBarHandleView {value}
+    sortRef = (a,b) ->
+      return -1 if a.options.value < b.options.value
+      return  1 if a.options.value > b.options.value
+      return  0

-    sortRef = (a,b) ->
-      return -1 if a.getData().value < b.getData().value
-      return  1 if a.getData().value > b.getData().value
-      return  0
-    @handles.sort sortRef
+    @handles.sort(sortRef)

   drawBar:->
     positions = []
-    for handle in @handles
-      positions.push handle.getRelativeX()
+    positions.push handle.getRelativeX() for handle in @handles

-    len       =  positions.length
-    left      = (parseInt(positions.first) if len>1) or 0
-    right     =  parseInt(positions.last)
-    diff      =  right - left
+    len       = positions.length
+    left      = (parseInt(positions.first) if len > 1) or 0
+    right     = parseInt(positions.last)
+    diff      = right - left

     unless @bar
       @addSubView @bar = new KDCustomHTMLView
@@ -42,59 +54,52 @@
     @bar.setWidth diff
     @bar.setX "#{left}px"

+  _ಠcreateLabel : (value) =>
+    {maxValue, minValue, interval, showLabels} = @getOptions()
+
+    pos = ((value - minValue) * 100) / (maxValue - minValue)
+    @labels.push @addSubView label = new KDCustomHTMLView
+      cssClass    : "sliderbar-label"
+      partial     : "#{value}"
+    label.setX "#{pos}%"
+
   addLabels:->
     {maxValue, minValue, interval, showLabels} = @getOptions()

-    createLabel = (value)=>
-      pos = ((value-minValue)*100)/(maxValue-minValue)
-      @labels.push @addSubView label = new KDCustomHTMLView
-        cssClass    : "sliderbar-label"
-        partial     : "#{value}"
-      label.setX "#{pos}%"
+    if Array.isArray showLabels
+    then @_ಠcreateLabel value for value in showLabels
+    else @_ಠcreateLabel value for value in [minValue..maxValue] by interval

-    if Array.isArray showLabels
-      for value in showLabels
-        createLabel value
-    else
-      for value in [minValue..maxValue] by interval
-        createLabel value
-
-  getValues:->
-    values = []
-    for handle in @handles
-      values.push handle.getData().value
-    return values
+  getValues:-> handle.getOptions().value for handle in @handles

   setLimits:->
     {maxValue, minValue, interval}      = @getOptions()

     if @handles.length is 1
-      @handles.first.data.leftLimit     = minValue
-      @handles.first.data.rightLimit    = maxValue
+      @handles.first.options.leftLimit  = minValue
+      @handles.first.options.rightLimit = maxValue
     else
-      for handle in @handles
-        i               = @handles.indexOf(handle)
-        data            = handle.data
-
-        data.leftLimit  = @handles[i-1]?.getData().value + interval or minValue
-        data.rightLimit = @handles[i+1]?.getData().value - interval or maxValue
+      for handle, i in @handles
+        options            = handle.getOptions()
+        options.leftLimit  = @handles[i-1]?.options.value + interval or minValue
+        options.rightLimit = @handles[i+1]?.options.value - interval or maxValue

   attachEvents:->
-    @on "click", (event)->
+    @on "click", (event) ->
       {maxValue, minValue} = @getOptions()
       sliderWidth          = @getWidth()
       clickedPos           = event.pageX - @getBounds().x
-      clickedValue         = ((maxValue-minValue)*clickedPos)/sliderWidth + minValue
-      snappedValue         = @handles.first.getSnappedValue value : clickedValue
-      closestHandle        = undefined
-      mindiff              = undefined
+      clickedValue         = ((maxValue - minValue) * clickedPos) / sliderWidth + minValue
+      snappedValue         = @handles.first.getSnappedValue value: clickedValue
+      closestHandle        = null
+      mindiff              = null

       for handle in @handles
-        value   = handle.getData().value
-        diff    = Math.abs(clickedValue-value)
-        if (diff < mindiff) or (mindiff is undefined)
-          mindiff       = diff
-          closestHandle = handle
+        {value}            = handle.getOptions()
+        diff               = Math.abs(clickedValue - value)
+        if (diff < mindiff) or not mindiff
+          mindiff          = diff
+          closestHandle    = handle

       closestHandle.setValue snappedValue

@@ -105,51 +110,48 @@
     @drawBar()   if @getOption('drawBar')
     @addLabels() if @getOption('showLabels')
     @attachEvents()
+    window.asd = @

 class KDSliderBarHandleView extends KDCustomHTMLView
-  constructor:(options = {}, data = {})->
+  constructor:(options = {})->
     options.tagName   = "a"
     options.cssClass  = "handle"
-    data.value       ?= 0
+    options.value    ?= 0
     options.draggable =
       axis            : "x"

-    super options, data
+    super options

   attachEvents:->
     {maxValue, minValue, width} = @parent.getOptions()
-    {value}                     = @getData()
+    value                       = @getOption "value"

     @on "DragStarted", ->
-      value = @getData().value
+      value = @getOption "value"
     @on "DragInAction", ->
-      relPos       = @dragState.position.relative.x
-      valueChange  = ((maxValue-minValue)*relPos)/width
-      @setValue value+valueChange
-    @on "DragFinished", ->
-      @snap() if @parent.getOption "snap"
+      relPos             = @dragState.position.relative.x
+      valueChange        = ((maxValue - minValue) * relPos) / width
+      @setValue value + valueChange
+      @snap() if @parent.getOption "snapOnDrag"
+    @on "DragFinished", -> @snap() if @parent.getOption "snap"

   getPosition:->
     {maxValue, minValue} = @parent.getOptions()
-    {value}              = @getData()
+    {value}              = @getOptions()
     sliderWidth          = @parent.getWidth()

-    percentage = ((value-minValue)*100)/(maxValue-minValue)
-    position   = (sliderWidth/100)*percentage
-
+    percentage = ((value - minValue) * 100) / (maxValue - minValue)
+    position   = (sliderWidth / 100) * percentage
     return "#{position}px"

+
   setValue:(value)->
-    {leftLimit, rightLimit} = @getData()
+    {leftLimit, rightLimit} = @getOptions()

-    if !(value<leftLimit) and !(value>rightLimit)
-      @data.value = value
-
-    else if value > rightLimit
-      @data.value = rightLimit
+    value = Math.min value, rightLimit if typeof rightLimit is "number"
+    value = Math.max value, leftLimit  if typeof leftLimit  is "number"

-    else if value < leftLimit
-      @data.value = leftLimit
+    @options.value = value

     @setX "#{@getPosition()}"
     @parent.drawBar() if @parent.getOption('drawBar')
@@ -158,28 +160,26 @@

   getSnappedValue:(value)->
     {interval}  = @parent.getOptions()
-    {value}     = value or @getData()
+    {value}     = value or @getOptions()

-    if interval isnt no
-      mod = value%interval
-      mid = interval/2
+    if interval
+      mod = value % interval
+      mid = interval / 2

       return value = switch
-        when mod <= mid then value-mod
-        when mod >  mid then value+(interval-mod)
+        when mod <= mid then value - mod
+        when mod >  mid then value + (interval - mod)
         else value

   snap:->
     {interval}  = @parent.getOptions()
     value       = @getSnappedValue()

-    if interval isnt no and @parent.getOption "snap"
+    if interval and @parent.getOption "snap"
       @setValue value
-      @setX "#{@getPosition()}"
       @parent.drawBar() if @parent.getOption('drawBar')

   viewAppended:->
     @setX "#{@getPosition()}"
     @attachEvents()
-    @snap() if @parent.getOption "snap"
-    +    @snap() if @parent.getOption "snap"


Client: Activity: Added search method for onChange event handling of input.

--- client/activity/lib/components/sidebarmodallist/index.coffee
+++ client/activity/lib/components/sidebarmodallist/index.coffee
@@ -44,6 +44,22 @@

+  search: (event) ->
+
+    { value }   = event.target
+    { channel } = ActivityFlux.actions
+
+    @setState { value, isSearching: yes }
+    @setState { value }
+
+    if value is '' then return @resetSearch()
+
+    value = value.slice(1)  if value[0] is '#'
+
+    channel.loadChannelsByQuery value
+    channel[@props.onThresholdAction] skip: @props.threads.size
+
+    @filter()

   renderHeader: ->
@@ -54,7 +70,9 @@
         <input
           className   = 'ChannelList-searchInput'
           placeholder = 'Search'
+          onChange    = { @bound 'search' }
           ref         = 'ChannelSearchInput'
+          value       = { @state.value }
         />
       </div>
     </div>


Activity: Fix CommentInputView::create parameter list

--- client/activity/lib/views/comments/commentinputwidget.coffee
+++ client/activity/lib/views/comments/commentinputwidget.coffee
@@ -92,13 +92,13 @@
     kd.utils.defer @bound 'focus'

-  create: ({body, clientRequestId}, callback) ->
+  create: ({body, payload, clientRequestId}, callback) ->

     { activity } = @getOptions()

     { appManager } = kd.singletons

-    appManager.tell 'Activity', 'reply', {activity, body, clientRequestId, payload}, (err, reply) =>
+    appManager.tell 'Activity', 'reply', {activity, body, payload, clientRequestId}, (err, reply) =>

       return showError err  if err

@@ -169,6 +169,3 @@
     @addSubView inputWrapper
     @addSubView @embedBox
     @addSubView @submitButton
-
-
-


Removed static func `fetchSnapshots`

Removed static func `fetchSnapshots`

It was basically an alias for a `JSnapshots.some` call, so rather than
place it in a helper file it was removed. Uses of this function were
replaced with `JSnapshot.some` calls (only one)

--- client/app/lib/providers/machinesettingssnapshotsview.coffee
+++ client/app/lib/providers/machinesettingssnapshotsview.coffee
@@ -24,18 +24,6 @@
     @snapshotsLimit()

     super options, data
-
-
-  ###*
-   * Fetch the list of snapshots from DB
-   *
-   * @param {Function(err:Error, snapshots:[JSnapshot]} callback
-  ###
-  @fetchSnapshots: (callback = kd.noop) ->
-
-    JSnapshot.some {}, {}, (err, snapshots) =>
-      return callback err  if err
-      callback err, snapshots

   ###*
@@ -122,7 +110,7 @@
   ###
   initList: ->

-    MachineSettingsSnapshotsView.fetchSnapshots (err, snapshots = []) =>
+    JSnapshot.some {}, {}, (err, snapshots = []) =>
       kd.warn err  if err
       @listController.lazyLoader.hide()
       @listController.replaceAllItems snapshots


hackathon: fixed error in text.

hackathon: fixed error in text.

--- client/landing/site.hackathon/coffee/home/AppViewFull.coffee
+++ client/landing/site.hackathon/coffee/home/AppViewFull.coffee
@@ -513,7 +513,7 @@

-
Mashape will reward an additional $1,000 to the team that makes the best use of their API.

+
Mashape will reward an additional $1,000 to the team that uses any API on Mashape.


perf(feed): remove btn-primary class for voted button

--- app/assets/javascripts/components/VoteButton.coffee
+++ app/assets/javascripts/components/VoteButton.coffee
@@ -19,7 +19,6 @@
         $btn.find('.text').text text
         $btn.data 'vote-count', ++count
         $btn.next('.js-social-count').text count
-        $btn.addClass('btn-primary').removeClass('btn-light')
       error: (res)->
         res = res.responseJSON
         $btn.prop 'disabled', false


Update nprogress.bandango.js.coffee

Update nprogress.bandango.js.coffee

--- app/assets/javascripts/nprogress.bandango.js.coffee
+++ app/assets/javascripts/nprogress.bandango.js.coffee
@@ -1,5 +1,3 @@
-$(document).ajaxStart ->
-  NProgress.start()
+$(document).ajaxStart -> NProgress.start()

-$(document).ajaxStop ->
-  NProgress.done()
+$(document).ajaxStop -> NProgress.done()


OX-687 fixed tab crashing issue caused by wrong zoom values

--- app/assets/javascripts/oxalis/model/flycam2d.coffee
+++ app/assets/javascripts/oxalis/model/flycam2d.coffee
@@ -35,6 +35,9 @@
     @rayThreshold = [10, 10, 10, 100]
     @spaceDirection = [1, 1, 1]
     @quality = 0        # offset of integer zoom step to the best-quality zoom level
+
+    # correct zoom values that are too high
+    @user.setValue("zoom", Math.min(@user.zoom, Math.floor(@getMaxZoomStep())))
     @setZoomStep(@user.zoom)

     @user.on({