minor refactor of SubjectViewer

minor refactor of SubjectViewer

--- app/assets/javascripts/components/subject-viewer.cjsx
+++ app/assets/javascripts/components/subject-viewer.cjsx
@@ -269,28 +269,66 @@
               height = {@state.imageHeight} />
           </Draggable>

-          { if @props.subject.location.spec?.x?
+          { # HANDLE RECTANGLE TOOL HIGHLIGHTS
+
+            if @props.workflow.name is 'transcribe' and @props.subject.location.spec.toolName is 'rectangleTool'
             isPriorAnnotation = true # ?
             <g key={@props.subject.id} className="marks-for-annotation" data-disabled={isPriorAnnotation}>
               {
+                console.log '@props.subject.location.spec: ', @props.subject.location.spec
                 # Represent the secondary subject as a rectangle mark
                 # TODO Should really check the drawing tool used (encoded somehow in the 2ndary subject) and display a read-only instance of that tool. For now just defaulting to rect:
                 ToolComponent = markingTools['rectangleTool']
                 # TODO: Note that x, y, w h aren't scaled properly:
                 mark = {x: @props.subject.location.spec.x, y: @props.subject.location.spec.y, width: @props.subject.location.spec.width, height: @props.subject.location.spec.height}

-                <ToolComponent
-                  key={@props.subject.id}
-                  mark={mark}
-                  xScale={scale.horizontal}
-                  yScale={scale.vertical}
-                  disabled={isPriorAnnotation}
-                  selected={mark is @state.selectedMark}
-                  getEventOffset={@getEventOffset}
-                  ref={@refs.sizeRect}
-
-                  onSelect={@selectMark.bind this, @props.subject, mark}
-                />
+                <g>
+                  <rect
+                    className   = "mark-rectangle top"
+                    x           = 0
+                    y           = 0
+                    width       = { @state.imageWidth }
+                    height      = { mark.y }
+                    fill        = "rgba(0,0,0,0.6)"
+                  />
+                  <rect
+                    className   = "mark-rectangle bottom"
+                    x           = 0
+                    y           = { mark.y + mark.height }
+                    width       = { @state.imageWidth }
+                    height      = { @state.imageHeight - mark.y + mark.height }
+                    fill        = "rgba(0,0,0,0.6)"
+                  />
+                  <rect
+                    className   = "mark-rectangle left"
+                    x           = 0
+                    y           = { mark.y }
+                    width       = { mark.x }
+                    height      = { mark.height }
+                    fill        = "rgba(0,0,0,0.6)"
+                  />
+                  <rect
+                    className   = "mark-rectangle right"
+                    x           = { mark.x + mark.width}
+                    y           = { mark.y }
+                    width       = { @state.imageWidth - mark.width - mark.x }
+                    height      = { mark.height }
+                    fill        = "rgba(0,0,0,0.6)"
+                  />
+
+                  <ToolComponent
+                    key={@props.subject.id}
+                    mark={mark}
+                    xScale={scale.horizontal}
+                    yScale={scale.vertical}
+                    disabled={isPriorAnnotation}
+                    selected={mark is @state.selectedMark}
+                    getEventOffset={@getEventOffset}
+                    ref={@refs.sizeRect}
+
+                    onSelect={@selectMark.bind this, @props.subject, mark}
+                  />
+                </g>
               }
             </g>
           }
@@ -328,79 +366,9 @@
                     />
                   }
                 </g>
+
             }

-            { # ROW FOCUS TOOL -------------------------------------------
-              if @props.workflow.name is "transcribe" and @state.subject.location.spec.toolName is "textRowTool"
-                console.log 'ROW TOOL!'
-                markHeight = @state.subject.location.spec.yLower - @state.subject.location.spec.yUpper
-                <g>
-
-                  <rect
-                    className   = "mark-rectangle"
-                    x           = 0
-                    y           = { 0 }
-                    width       = { @state.imageWidth }
-                    height      = { @state.subject.location.spec.yUpper }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-
-                  <rect
-                    className   = "mark-rectangle"
-                    x           = 0
-                    y           = { @state.subject.location.spec.yLower }
-                    width       = { @state.imageWidth }
-                    height      = { @state.imageHeight - @state.subject.location.spec.yLower }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-                </g>
-            }
-
-
-            { # RECTANGLE FOCUS TOOL ------------------------------------------
-              if @props.workflow.name is "transcribe" and @state.subject.location.spec.toolName is "rectangleTool"
-                console.log 'RECTANGLE TOOL!'
-                markHeight = @state.subject.location.spec.yLower - @state.subject.location.spec.yUpper
-                <g>
-
-                  <rect
-                    className   = "mark-rectangle top"
-                    x           = 0
-                    y           = 0
-                    width       = { @state.imageWidth }
-                    height      = { @state.subject.location.spec.y }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-
-                  <rect
-                    className   = "mark-rectangle bottom"
-                    x           = 0
-                    y           = { @state.subject.location.spec.y + @state.subject.location.spec.height }
-                    width       = { @state.imageWidth }
-                    height      = { @state.imageHeight - @state.subject.location.spec.y + @state.subject.location.spec.height }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-
-                  <rect
-                    className   = "mark-rectangle left"
-                    x           = 0
-                    y           = { @state.subject.location.spec.y }
-                    width       = { @state.subject.location.spec.x }
-                    height      = { @state.subject.location.spec.height }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-
-                  <rect
-                    className   = "mark-rectangle right"
-                    x           = { @state.subject.location.spec.x + @state.subject.location.spec.width}
-                    y           = { @state.subject.location.spec.y }
-                    width       = { @state.imageWidth - @state.subject.location.spec.width - @state.subject.location.spec.x }
-                    height      = { @state.subject.location.spec.height }
-                    fill        = "rgba(0,0,0,0.6)"
-                  />
-
-                </g>
-            }

           </svg>


JPermissionSet: checkPermission: it should be a series check not parallel

cc/ @ozankasikci
https://github.com/koding/koding/commit/cf0a1eac50336e16ec77588edd262b6a5e9e6ebd

--- workers/social/lib/social/models/group/permissionset.coffee
+++ workers/social/lib/social/models/group/permissionset.coffee
@@ -116,16 +116,15 @@
     # permission checker helper, walks on the all required permissions
     # if one of them passes, breaks the loop and returns true
     kallback = (current, main) ->
-      hasPermission = no
-
-      queue = advanced.map ({ permission, validateWith, superadmin }) -> (fin) ->
+
+      queue = advanced.map ({ permission, validateWith, superadmin }) -> (next) ->

         if superadmin

           # if permission requires superadmin and current group is not 'koding'
           # or if somehow 'koding' group (main) not exists then pass ~ GG
           if currentGroup isnt MAIN_GROUP or not main
-            return fin()
+            return next()

           # if permission requires superadmin then do the permission check on
           # main group and permissionSet (which is 'koding' group) ~ GG
@@ -139,13 +138,14 @@
         validateWith ?= anyValidator

         validateWith.call target, client, group, permission, permissionSet, args,
-          (err, hasPermission_) ->
-            hasPermission = yes  if hasPermission_
-            fin err
-
-      async.parallel queue, (err) ->
+          (err, hasPermission) ->
+            if hasPermission
+            then callback null, yes
+            else next err
+
+      async.series queue, (err) ->
         return callback err  if err
-        callback null, hasPermission
+        callback null, no

     # set groupName from given target or client
     client.groupName = getGroupnameFrom target, client


social: add navigated defensive check for rendering loggedin home page

--- workers/social/lib/social/render/loggedin/kodinghome.coffee
+++ workers/social/lib/social/render/loggedin/kodinghome.coffee
@@ -11,8 +11,9 @@
   options.entryPoint = entryPoint

   prepareHTML = (scripts, socialApiData)->
-    if socialApiData
+    if socialApiData?.navigated?
       {navigated} = socialApiData
+
       {slug, data:{message}} = navigated

       if message


Remove the 1-week minimum restriction on Wiki Ed courses

We'll be doing an edit-a-thon pilot, and the restriction wasn't doing much good anyway.

--- app/assets/javascripts/utils/course_date_utils.coffee
+++ app/assets/javascripts/utils/course_date_utils.coffee
@@ -9,17 +9,13 @@

   # Returns an object of minDate and maxDate props for each date field of a course
   dateProps: (course, type) ->
-    minEnd = moment(course.start, 'YYYY-MM-DD')
-    #Wiki Ed's classroom program requires courses be at least a week long
-    type ?= course.type
-    if type == 'ClassroomProgramCourse'
-      minEnd.add(1, 'week')
+    startDate = moment(course.start, 'YYYY-MM-DD')

     props =
       end:
-        minDate: minEnd
+        minDate: startDate
       timeline_start:
-        minDate: moment(course.start, 'YYYY-MM-DD')
+        minDate: startDate
         maxDate: moment(course.timeline_end, 'YYYY-MM-DD')
       timeline_end:
         minDate: moment(course.timeline_start, 'YYYY-MM-DD')
@@ -27,9 +23,9 @@

     return props

-  # This method takes a previous version of a course and a new one where one of
-  # the dates has changed, and returns a course where all the dates are consistent
-  # with each other.
+  # This method takes a current version of a course and an updated key-value pair
+  # for changing one of the date fields and returns a course where all the dates
+  # are consistent with each other.
   updateCourseDates: (prevCourse, value_key, value) ->
     updatedCourse = $.extend({}, prevCourse);
     updatedCourse[value_key] = value


comment: add smooth item deletion for item view

--- client/Social/Activity/views/comments/listitemview.coffee
+++ client/Social/Activity/views/comments/listitemview.coffee
@@ -4,7 +4,8 @@

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

-    options.type = 'comment'
+    options.type     = 'comment'
+    options.cssClass = KD.utils.curry 'fade-out', options.cssClass

     super options, data

@@ -99,9 +100,15 @@
   showDeleteModal: ->

     modal = new CommentDeleteModal {}, @getData()
-    modal.once "DeleteClicked"   , @bound "hide"
-    modal.once "DeleteConfirmed" , @bound "destroy"
-    modal.once "DeleteError"     , @bound "show"
+    modal.once 'DeleteClicked'   , @bound 'hide'
+    modal.once 'DeleteConfirmed' , @bound 'delete'
+    modal.once 'DeleteError'     , @bound 'show'
+
+
+  hide          : ActivityListItemView::hide
+  show          : ActivityListItemView::show
+  delete        : ActivityListItemView::delete
+  whenSubmitted : ActivityListItemView::whenSubmitted

   createReplyLink: ->


slack: update calls

slack: update calls

--- client/admin/lib/views/invitations/slackinviteview.coffee
+++ client/admin/lib/views/invitations/slackinviteview.coffee
@@ -20,6 +20,8 @@

   SLACKBOT_ID = 'USLACKBOT'
   OAUTH_URL   = "#{location.origin}/api/social/slack/oauth"
+  CHANNELS_URL  = "#{location.origin}/api/social/slack/channels"
+  MESSAGING_URL = "#{location.origin}/api/social/slack/message"

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

@@ -46,14 +48,14 @@
   createInviterViews: ->

     $.ajax
-      type : 'GET'
-      url  : '/api/social/slack/channels'
+      method  : 'GET'
+      url     : CHANNELS_URL
       success : (res) =>
         @createChannelInviter res.channels.concat res.groups or []

     $.ajax
-      type : 'GET'
-      url  : '/api/social/slack/users'
+      method  : 'GET'
+      url     : USERS_URL
       success : (res) =>
         @createIndividualInviter res


parent max width

parent max width

--- components/article/client/view.coffee
+++ components/article/client/view.coffee
@@ -90,7 +90,7 @@
       .each (i, img) ->
         newWidth = ((img.width * window.innerHeight * 0.9) / img.height)
         if newWidth < 580
-          $(img).css('max-width', 580)
+          $(img).parent().css('max-width', 580)
         else if img.width < (img.height * .9)
           $(img).parent().addClass('portrait')
     @$('.article-section-artworks, .article-section-container[data-section-type=image]').addClass 'images-loaded'


fixed activity widget

fixed activity widget

--- components/menus/KDContextMenu.coffee
+++ components/menus/KDContextMenu.coffee
@@ -71,7 +71,9 @@
     item

   clickOnMenuItem:(source,event)=>
-
+
+    return if source.data?.disabled
+
     contextMenuDelegate = @getView().delegate #context menu tree view delegate (item clicked)
     if source.data.callback and "function" is typeof source.data.callback
       source.data.callback.call contextMenuDelegate, source, event
@@ -160,10 +162,8 @@
     super options,data

   partial:(data)->
-    partial = $ "

-
-          #{data.title}
-
"
+
+    "
#{data.title}
"

   mouseEnter:(event)->
     ###


Referral: add a magical function for checking and updating vm disk size

--- workers/social/lib/social/models/referral/index.coffee
+++ workers/social/lib/social/models/referral/index.coffee
@@ -36,6 +36,8 @@
         fetchTBCampaing:
           (signature Function)
         isCampaingValid:
+          (signature Function)
+        resetVMDefaults:
           (signature Function)
         fetchRedeemableReferrals:
           (signature Object, Function)
@@ -279,6 +281,19 @@
   CAMPAIGN_START_DATE             = new Date("Jan 29 2014 16:00:00")
   OLD_DISK_SIZE_IN_MB             = 250

+  # this functions checks default vm and updates disk size
+  # if required, when vm disk size is updated, returns hostname
+  @resetVMDefaults = secure (client, callback)->
+    JVM.fetchDefaultVm_ client, (err, vm)->
+      return callback err if err
+      return callback new Error "VM not found" unless vm
+      if vm.diskSizeInMB and vm.diskSizeInMB >= JVM.VMDefaultDiskSize
+        return callback null, no
+      else
+        JVM.resetDefaultVMLimits client, (er, vmName)->
+          return callback err if err
+          return callback null, yes, vmName
+
   @isCampaingValid = isCampaingValid = (callback)->
     fetchTBCampaing (err, campaign)->
       return callback err if err


Fixed higher-order use of a constrained method and added a test

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1183,7 +1183,6 @@
           # })"""
           (irFunction
             name: (ctx.definitionName() if ctx.isAtSimpleDefinition())
-            type: call.tea
             params: paramNames
             body: (join compiledWheres, [(jsReturn compiledBody)]))
             # (jsCall "λ#{paramNames.length}", [
@@ -1961,7 +1960,7 @@
       {
         id: ctx.declarationId symbol
         type: type
-        translation: nameTranslate ctx, atom, symbol, type
+        translation: nameTranslate ctx, atom, symbol, type, ctx.arity symbol
       }
     # Inside function only defer compilation if we don't know arity
     else if ctx.isInsideLateScope() and (ctx.isDeclared symbol) or contextType instanceof TempType
@@ -1971,7 +1970,7 @@
       {
         id: ctx.declarationId symbol
         type: type
-        translation: nameTranslate ctx, atom, symbol, type
+        translation: nameTranslate ctx, atom, symbol, type, ctx.arity symbol
       }
     else
       # log "deferring in rhs for #{symbol}"
@@ -1986,17 +1985,15 @@
       else
         (jsBinary "instanceof", exp, (validIdentifier symbol)))]

-nameTranslate = (ctx, atom, symbol, type) ->
+nameTranslate = (ctx, atom, symbol, type, arity) ->
   if atom.label is 'const'
     switch symbol
       when 'True' then 'true'
       when 'False' then 'false'
       else
         (jsAccess (validIdentifier symbol), "_value")
-  else if ctx.isMethod symbol, type
-    (irMethod type, symbol)
-  else
-    validIdentifier symbol
+  else
+    (irReference symbol, type, arity)

 numericalCompile = (ctx, symbol) ->
   translation = if symbol[0] is '~' then (jsUnary "-", symbol[1...]) else symbol
@@ -2155,11 +2152,34 @@
   finalType = substitute ctx.substitution, type
   # log op, (printType type), printType finalType
   classParams =
-    if op.ir is irMethodTranslate
+    if op.ir is irReferenceTranslate and ctx.isMethod op.name, op.type
       []
     else
       dictsForConstraint ctx, finalType.constraints
-  (jsCall (translateIr ctx, op), (join classParams, (translateIr ctx, args)))
+  op =
+    if op.ir is irReferenceTranslate and not ctx.isMethod op.name, op.type
+      validIdentifier op.name
+    else
+      translateIr ctx, op
+  (jsCall op, (join classParams, (translateIr ctx, args)))
+
+
+irReference = (name, type, arity) ->
+  {ir: irReferenceTranslate, name, type, arity}
+
+irReferenceTranslate = (ctx, {name, type, arity}) ->
+  if ctx.isMethod name, type
+    translateIr ctx, (irMethod type, name)
+  else
+    finalType = substitute ctx.substitution, type
+    classParams = dictsForConstraint ctx, finalType.constraints
+    if classParams.length > 0
+      (irFunctionTranslate ctx,
+        params: arity
+        body: [(jsReturn (jsCall (validIdentifier name), (join classParams, arity)))])
+    else
+      validIdentifier name
+

 irMethod = (type, name) ->
   {ir: irMethodTranslate, type, name}
@@ -4649,6 +4669,25 @@
     [x y] (f ["A" "B"])
   """
   """x""", "A"
+
+  'higher-order use of constrained function'
+  """
+    Show (class [a]
+      show (fn [x] (: (Fn a String))))
+
+    show-string (instance (Show String)
+      show (fn [x] x))
+
+    f (fn [pair]
+      [(show a) (show b)]
+      [a b] pair)
+
+    apply (fn [m to]
+      (m to))
+
+    [x y] (apply f ["A" "B"])
+  """
+  """x""", "A"
   # The following doesn't work because the Collection type class specifies
   # that the constructor takes only one argument.
   #


Type aliasing, resizing source and primitive type checking through Error

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -298,6 +298,7 @@
     scope.boundTypeVariables = newSet()
     scope.classes = newMap()
     scope.typeNames = newMap()
+    scope.typeAliases = newMap()
     scope

   newLateScope: ->
@@ -324,6 +325,15 @@
     for scope in (reverse @scopes)
       if kind = lookupInMap scope.typeNames, name
         return kind
+
+  addTypeAlias: (name, type) ->
+    addToMap @_scope().typeAliases, name, type
+
+  resolveTypeAliases: (name) ->
+    if alias = lookupInMap @_scope().typeAliases, name
+      alias
+    else
+      name

   bindTypeVariables: (vars) ->
     addAllToSet @_scope().boundTypeVariables, vars
@@ -838,7 +848,7 @@
 typeCompile = (ctx, expression) ->
   throw new Error "invalid typeCompile args" unless expression
   (if isAtom expression
-    typeConstantCompile
+    typeNameCompile
   else if isTuple expression
     typeTupleCompile
   else if isCall expression
@@ -850,21 +860,53 @@
 typesCompile = (ctx, expressions) ->
   typeCompile ctx, e for e in expressions

+typeNameCompile = (ctx, atom, expectedKind) ->
+  expanded = ctx.resolveTypeAliases atom.symbol
+  type =
+    if expanded is atom.symbol
+      kindOfType =
+        if isCapital atom
+          ctx.kindOfTypeName atom.symbol
+        else
+          expectedKind or star
+      if not kindOfType
+        # throw new Error "type name #{atom.symbol} was not defined" unless kind
+        malformed atom, "This type name has not been defined"
+      atomicType atom.symbol, kindOfType
+    else
+      expanded
+  finalKind = kind type
+  if finalKind instanceof KindFn
+    labelOperator atom
+  else
+    atom.label = 'typename'
+  if expectedKind and (not kindsEq expectedKind, finalKind)
+    malformed atom, "The kind of the type operator doesn't match the
+                  supplied number of arguments"
+  # log type
+  type
+
+typeTupleCompile = (ctx, form) ->
+  (labelOperator form)
+  elemTypes = _terms form
+  applyKindFn (tupleType elemTypes.length), (typesCompile ctx, elemTypes)...
+
 typeConstructorCompile = (ctx, call) ->
   op = _operator call
   args = _arguments call

   if isAtom op
-    (labelOperator op)
     name = op.symbol
     compiledArgs = typesCompile ctx, args
     if name is 'Fn'
+      (labelOperator op)
       typeFn compiledArgs...
     else
       arity = args.length
-      applyKindFn (atomicType name, kindFn arity), compiledArgs...
-  else
-    malformed op, 'Should use a type constructor here'
+      operatorType = typeNameCompile ctx, op, (kindFn arity)
+      applyKindFn operatorType, compiledArgs...
+  else
+    malformed op, 'Expected a type constructor instead'

 typeConstraintCompile = (ctx, expression) ->
   op = _operator expression
@@ -881,21 +923,6 @@
 typeConstraintsCompile = (ctx, expressions) ->
   filter ((t) -> t instanceof ClassConstraint),
     (typeConstraintCompile ctx, e for e in expressions)
-
-typeTupleCompile = (ctx, form) ->
-  (labelOperator form)
-  elemTypes = _terms form
-  applyKindFn (tupleType elemTypes.length), (typesCompile ctx, elemTypes)...
-
-typeConstantCompile = (ctx, atom) ->
-  atom.label = 'typename'
-  kind =
-    if isCapital atom
-      ctx.kindOfTypeName atom.symbol
-    else
-      star
-  throw new Error "type name #{atom.symbol} was not defined" unless kind
-  atomicType atom.symbol, kind

 # Inside definition, we call assignCompile with its RHS
 #   whether to call it and with what expression is left to the RHS expression
@@ -1136,11 +1163,12 @@
         else
           # Typing
           if body and not body.tea
-            throw new Error "Body not typed"
+            malformed body, 'Expression failed to type check'
+            #throw new Error "Body not typed"
           call.tea =
             if body
               new Constrained body.tea.constraints,
-                typeFn paramTypeVars..., body.tea.type
+                typeFn paramTypeVars..., body.tea?.type
             else
               freshInstance ctx, explicitType
           # """λ#{paramNames.length}(function (#{listOf paramNames}) {
@@ -1157,6 +1185,15 @@
             #     name: (ctx.definitionName() if ctx.isAtSimpleDefinition())
             #     params: paramNames
             #     body: (join compiledWheres, [(jsReturn compiledBody)]))])
+
+ms.type = ms_type = (ctx, call) ->
+  hasName = requireName ctx, 'Name required to declare new type alias'
+  alias = ctx.definitionName()
+  if not (isCapital symbol: alias)
+    malformed ctx.definitionPattern(), 'Type aliases must start with a capital letter'
+  [type] = _arguments call
+  ctx.addTypeAlias alias, typeCompile ctx, type
+  jsNoop()

   # data
   #   listing or
@@ -3735,6 +3772,7 @@
     toInject = collectRequiresFor moduleName
     ctx = injectedContext toInject
     {js, ast} = compileCtxAstToJs topLevel, ctx, (astFromSource "(#{source})", -1, -1)
+    checkTypes ctx
     replaceOrAddToMap compiledModules, moduleName,
       declared: (subtractContexts ctx, (injectedContext toInject)) # must recompute because ctx is mutated
       js: js
@@ -3748,9 +3786,16 @@
   ast = (astFromSource "(#{source})", -1, -1)
   [expression] = _terms ast
   {js} = compileCtxAstToJs topLevelExpression, ctx, expression
+  checkTypes ctx
   (attachPrintedTypes ctx, expression)
   js: library + immutable + (listOfLines map lookupJs, setToArray toInject) + js
   ast: ast
+
+# Primitive type checking for now
+checkTypes = (ctx) ->
+  failed = mapToArray filterMap ((name) -> name is 'could not unify'), ctx.substitution
+  if _notEmpty failed
+    throw new Error "Could not unify #{failed[0][0]} with #{failed[0][1]}!"

 lookupJs = (moduleName) ->
   js = (lookupInMap compiledModules, moduleName)?.js
@@ -4303,6 +4348,17 @@
       (Js.call (Js.access in "get") {key}))
   """
   """(at 5 data)""", 'b'
+
+  'type alias'
+  """
+    Point (type [Num Num])
+
+    x (fn [p]
+      (: (Fn Point Num))
+      first
+      [first second] p)
+  """
+  "(x [3 4]", 3

   'collections'
   """


Added explicit typing for JS values

Added explicit typing for JS values

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -1517,6 +1517,20 @@
     # TODO: support typing of expressions,
     #watch out of definition patterns without a name
     jsNoop()
+
+ms['::'] = ms_typed_expression = (ctx, call) ->
+  [type, constraintSeq, expression] = _arguments call
+  if isSeq constraintSeq
+    constraints = typeConstraintsCompile ctx, _terms constraintSeq
+  else
+    constraints = []
+    expression = constraintSeq
+  compiledType = new Constrained constraints, typeCompile ctx, type
+  if name = ctx.definitionName()
+    ctx.declare ctx.definitionName()
+    assignExplicitType ctx, compiledType
+  call.tea = compiledType
+  assignCompile ctx, call, (termCompile ctx, expression)

 ms.global = ms_global = (ctx, call) ->
   call.tea = toConstrained typeConstant 'JS'


remove a log complaining that an algorithm is a disaster

actually i don't think it needs to be optimized

--- workers/social/lib/social/models/group/permissionset.coffee
+++ workers/social/lib/social/models/group/permissionset.coffee
@@ -34,7 +34,6 @@
   constructor:->
     super
     # initialize the permission set with some sane defaults:
-    # TODO: this is a disaster!  Reimplement? - C.T.
     {permissionDefaultsByModule} = require '../../traits/protected'
     permissionsByRole = {}


Profile: avatar view href is related to group slug

--- client/Main/CommonViews/avatarviews/avatarview.coffee
+++ client/Main/CommonViews/avatarviews/avatarview.coffee
@@ -97,7 +97,10 @@

     @$('cite').addClass flags

-    @setAttribute "href", "/#{profile.nickname}"
+    KD.getSingleton("groupsController").ready =>
+      {slug} = KD.getSingleton("groupsController").getCurrentGroup()
+      href = if slug is "koding" then "/#{profile.nickname}" else "/#{slug}/#{profile.nickname}"
+      @setAttribute "href", href

     @showStatus()  if @getOptions().showStatus


LogsListController: add individual tabs for each scope

--- client/admin/lib/views/logs/logslistcontroller.coffee
+++ client/admin/lib/views/logs/logslistcontroller.coffee
@@ -15,7 +15,7 @@
     super options, data

-  fetchLogs: ->
+  fetchLogs: (options = {})->

     return if @isFetching

@@ -24,11 +24,11 @@

     @isFetching = yes

-    @fetchLogsFromAPI {}, (err, res) =>
+    @fetchLogsFromAPI options, (err, res) =>

       return  if showError err

-      { data: { logs }} = res
+      { data: { logs } } = res
       @listLogs logs

       @isFetching = no
@@ -38,9 +38,18 @@

     type     = 'GET'
     endPoint = '/-/api/logs'
+    args     = []
+
+    if (scope = @getOption 'scope') and scope isnt 'all'
+      args.push "scope=#{scope}"
+
+    if q = options.query
+      args.push "q=#{query}"
+
+    if args.length > 0
+      endPoint = "#{endPoint}?#{args.join ','}"

     doXhrRequest { endPoint, type }, (err, res) ->
-      console.log ">>>>>>", err, res
       callback err, res


GroupStackSettings: pre-fetch Ace app storage to load ace faster

--- client/admin/lib/views/groupstacksettings.coffee
+++ client/admin/lib/views/groupstacksettings.coffee
@@ -18,6 +18,8 @@
     @addSubView @scrollView

     @createInitialView()
+
+    kd.singletons.appStorageController.storage 'Ace', '1.0.1'

   createOnboardingView: (options = {}) ->


Fix display of next message on deletion, refs #456

--- client/app/actions/message_action_creator.coffee
+++ client/app/actions/message_action_creator.coffee
@@ -195,12 +195,10 @@
     currentMessage = MessageStore.getCurrentID() or 'not-null'
     conversationIDs = target.conversationIDs or [target.conversationID]
     currentConversation = MessageStore.getCurrentConversationID() or 'not-null'
-    if currentMessage in messageIDs
-        next = MessageStore.getNextOrPrevious false
-        # MessageActionCreator.setCurrent next.get('id'), true
-        window.cozyMails.messageDisplay next, false
-    else if currentConversation in conversationIDs
-        next = MessageStore.getNextOrPrevious true
+    isConv = currentMessage not in messageIDs
+    isConv = true
+    next = MessageStore.getNextOrPrevious isConv
+    if next?
         # MessageActionCreator.setCurrent next.get('id'), true
         window.cozyMails.messageDisplay next, false

@@ -260,6 +258,8 @@

             updated.push message.set('mailboxIDs', newMailboxIds).toJS()

+    _fixCurrentMessage target
+
     # immediately apply change to refresh UI
     # Update datastore
     AppDispatcher.handleViewAction
@@ -270,8 +270,6 @@
     AppDispatcher.handleViewAction
         type: ActionTypes.LAST_ACTION
         value: {actions}
-
-    _fixCurrentMessage target

     return updated

@@ -315,6 +313,8 @@
             newMailboxIds[trashMailbox] = -1
             updated.push(message.set('mailboxIDs', newMailboxIds).toJS())

+    _fixCurrentMessage target
+
     # immediately apply change to refresh UI
     # Update datastore
     AppDispatcher.handleViewAction
@@ -326,6 +326,4 @@
         type: ActionTypes.LAST_ACTION
         value: {actions}

-    _fixCurrentMessage target
-
     return updated


Magical refresh after loguout

Magical refresh after loguout

--- client/coffeescript/templates/session_bar.coffee
+++ client/coffeescript/templates/session_bar.coffee
@@ -7,5 +7,6 @@
   events: {
     'click a.logout_top_right': (event) ->
       Meteor.logout()
+      window.location = '#'
   }
 )


asdfghjk

asdfghjk

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -103,12 +103,15 @@
           else
             channel = bongo.mq.subscribe secretChannelId
             channel.bind 'pusher:subscription_succeeded', ->
-              log 'SUBSCRIPTION SUCCEEDED', secretChannelId
-              myMessageHandler = messageHandler.bind null, secretChannelId
-              channel.bind 'message', myMessageHandler
-              channel.bind 'error', myMessageHandler
-              channel.bind 'changeTransport', changeTransport.bind null, secretChannelId
-              callback channel
+              # join an extra channel here so that we can listen for the vacated webhook.
+              connChannel = bongo.mq.subscribe secretChannelId+'-conn'
+              connChannel.bind 'pusher:subscription_succeeded', ->
+                log 'SUBSCRIPTION SUCCEEDED', secretChannelId
+                myMessageHandler = messageHandler.bind null, secretChannelId
+                channel.bind 'message', myMessageHandler
+                channel.bind 'error', myMessageHandler
+                channel.bind 'changeTransport', changeTransport.bind null, secretChannelId
+                callback channel

       (options, callback)->
         @fetchKiteChannelName options.kiteName, (err, secretChannelId)->


test/virtualmachines: check always on taggle button

--- client/test/lib/dashboard/virtualmachines.coffee
+++ client/test/lib/dashboard/virtualmachines.coffee
@@ -68,3 +68,10 @@
       .click runningVMSelector
       .waitForElementVisible machineDetailSelector, 20000

+    # check for alwaysOnToggle
+    browser
+      .click alwaysOnToggleSelector
+      .pause 1000
+      .click alwaysOnToggleSelector
+      .pause 1000
+


update filename

update filename

--- src/experiments/mkts-tables.coffee
+++ src/experiments/mkts-tables.coffee
@@ -1,120 +0,0 @@
-
-
-
-############################################################################################################
-njs_path                  = require 'path'
-njs_fs                    = require 'fs'
-#...........................................................................................................
-CND                       = require 'cnd'
-rpr                       = CND.rpr
-badge                     = 'MK/TS/MACRO-ESCAPER/mkts-tables'
-log                       = CND.get_logger 'plain',     badge
-info                      = CND.get_logger 'info',      badge
-whisper                   = CND.get_logger 'whisper',   badge
-alert                     = CND.get_logger 'alert',     badge
-debug                     = CND.get_logger 'debug',     badge
-warn                      = CND.get_logger 'warn',      badge
-help                      = CND.get_logger 'help',      badge
-urge                      = CND.get_logger 'urge',      badge
-echo                      = CND.echo.bind CND
-XREGEXP                   = require 'xregexp'
-unified                   = require 'unified'
-remark_parse              = require 'remark-parse'
-stringify                 = require 'rehype-stringify'
-remark_to_rehype          = require 'remark-rehype'
-remark_to_html            = require 'remark-html'
-remark_grid_tables        = require 'remark-grid-tables'
-jr                        = JSON.stringify
-
-#-----------------------------------------------------------------------------------------------------------
-show_permutations = ->
-  columns = [ 'O', 'A', 'B', 'C', 'Z', ]
-  rows    = [ 'O', 'A', 'B', 'C', 'Z', ]
-
-  for column in columns
-    for row in rows
-      echo "      \\node[color=gray] at ( \\c#{column}, \\r#{row} ) {{\\mktsStyleCode{}#{column}#{row}}};"
-
-#-----------------------------------------------------------------------------------------------------------
-demo_tableparsing = ->
-  x = unified()
-    .use remark_parse
-    .use remark_grid_tables
-    # .use remark_to_rehype
-    .use stringify
-    .use remark_to_html
-
-  demo_1 = """
-  +-------+----------+------+
-  | Table Headings   | Here |
-  +-------+----------+------+
-  | Sub   | Headings | Too  |
-  +=======+==========+======+
-  | cell  | column spanning |
-  + spans +----------+------+
-  | rows  | normal   | cell |
-  +-------+----------+------+
-  | multi | cells can be    |
-  | line  | *formatted*     |
-  |       | **paragraphs**  |
-  | cells |                 |
-  | too   |                 |
-  +-------+-----------------+
-
-  """
-  debug x.stringify x.parse demo_1
-
-  demo_2 = """
-    +-------+----------+------+
-    | Table Headings   | Here |
-    +-------+----------+------+
-    | Sub   | Headings | Too  |
-    +-------+----------+------+
-    | Sub   | Headings | Too  |
-    +=======+==========+======+
-    | cell  | column spanning |
-    + spans +----------+------+
-    | rows  | normal   | cell |
-    +-------+----------+------+
-    | multi | cells can be    |
-    | line  | *formatted*     |
-    |       | **paragraphs**  |
-    | cells |                 |
-    | too   |                 |
-    +-------+-----------------+
-
-
-
-  """
-  debug x.stringify x.parse demo_2
-
-
-### ==================================================================================================== ###
-
-# debug EXCJSCC = require '../../src/experiments/exceljs-col-cache-module'
-EXCJSCC                   = require '../exceljs-spreadsheet-address-codec'
-
-
-debug '77762-1', EXCJSCC.l2n               'A'    ### letter_from_number ###
-debug '77762-2', EXCJSCC.l2n               'Z'
-debug '77762-3', EXCJSCC.l2n               'AA'
-# debug '77762-4', EXCJSCC.l2n               'zz' ### must be uppercase ###
-debug '77762-5', EXCJSCC.n2l               '1'    ### letter_from_number ###
-debug '77762-6', EXCJSCC.n2l               '256'
-debug '77762-7', EXCJSCC.n2l               256
-# debug '77762-8', EXCJSCC.validateAddress   '*+'
-debug '77762-9', EXCJSCC.validateAddress   'AA1'
-# debug '77762-10', EXCJSCC.validateAddress   'A:A'
-debug '77762-11', EXCJSCC.decodeAddress     'A1'  ### convert address string into structure ###
-debug '77762-12', EXCJSCC.decodeAddress     'AA1' ### like `{ address: 'A1', col: 1, row: 1, '$col$row': '$A$1' }` ###
-debug '77762-13', EXCJSCC.getAddress        'AA1' ### convert r,c into structure (if only 1 arg, assume r is address string) ###
-debug '77762-13', EXCJSCC.getAddress        '1', '2'
-debug '77762-13', EXCJSCC.getAddress        1, 2
-debug '77762-14', EXCJSCC.decode            'AA1' ### convert [address], [tl:br] into address structures ###
-debug '77762-14', EXCJSCC.decode            'A1:B2'
-# debug '77762-15', EXCJSCC.decodeEx          'AA1'
-debug '77762-16', EXCJSCC.encodeAddress     1, 1 ### convert row,col into address string ###
-debug '77762-16', EXCJSCC.encodeAddress     1234, 1234
-debug '77762-17', EXCJSCC.encode            1234, 1234 ### convert row,col into string address or t,l,b,r into range ###
-debug '77762-17', EXCJSCC.encode            1, 2, 3, 4
-


Problem js runs MathJax only over its own contents rather than full page

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -11,7 +11,8 @@
     $(selector, @el)

   bind: =>
-    MathJax.Hub.Queue ["Typeset", MathJax.Hub]
+    @el.find('.problem > div').each (index, element) =>
+      MathJax.Hub.Queue ["Typeset", MathJax.Hub, element]

     window.update_schematics()

@@ -267,7 +268,9 @@
               showMethod = @inputtypeShowAnswerMethods[cls]
               showMethod(inputtype, display, answers) if showMethod?

-        MathJax.Hub.Queue ["Typeset", MathJax.Hub]
+        @el.find('.problem > div').each (index, element) =>
+          MathJax.Hub.Queue ["Typeset", MathJax.Hub, element]
+
         @$('.show').val 'Hide Answer'
         @el.addClass 'showed'
         @updateProgress response


:fire: Remove useless temp var

:fire: Remove useless temp var

--- src/flux/stores/application.coffee
+++ src/flux/stores/application.coffee
@@ -13,18 +13,16 @@
     @listenToMany(Actions)

   onLoadView: (view) ->
-    temp = []
+    views = []
     view = [view] if typeof view != 'object'

     view.map (viewName) =>
       cache.views[viewName] ?= require "../../views/#{viewName}"

-      temp.push cache.views[viewName]
+      views.push cache.views[viewName]

     # Save actual views
     localStorage.setItem('zenit:last-view', view.join(','))
-
-    views = temp

     # Emit changes
     @trigger()


Tests: IDE: Workspace: Fixed createWorkspaceFromFileTree test.

--- tests/src/ide/workspace.coffee
+++ tests/src/ide/workspace.coffee
@@ -42,12 +42,13 @@
       .url (data) =>
         url    = data.value

-        vmName = url.split('/IDE/')[1].split('/')[0]
+        vmName   = url.split('/IDE/')[1].split('/')[0]
+        selector = '.activity-sidebar .kdlistitemview-main-nav.workspace'
+        path     = ' a[href="/IDE/' + vmName + '/' + folderData.name + '"]'

         browser
-          .waitForElementVisible  '.activity-sidebar .jtreeview-wrapper li' + ' a[href="/IDE/' + vmName + '/' + folderData.name + '"]', 20000 #Assertion
+          .waitForElementVisible  selector + path, 20000 #Assertion
           .pause                  3000
           .waitForElementVisible  '.vm-info', 20000
           .assert.containsText    '.vm-info', name # Assertion
           .end()
-


Fix LabelCategoryModel

Fix LabelCategoryModel

--- src/coffee/label_category/models/label_category.coffee
+++ src/coffee/label_category/models/label_category.coffee
@@ -47,9 +47,13 @@
           children.on(
             "destroy"
             =>
-              delete childrens[category_name]
-              unless _(childrens).keys().length || @get("label")
-                @destroy()
+              if _(children.get("childrens")).keys().length > 0
+                delete children.get("label")
+                children.set "label", undefined
+              else
+                delete childrens[category_name]
+                unless _(childrens).keys().length || @get("label")
+                  @destroy()
           )
           childrens[category_name] = children
           @set "childrens", childrens
@@ -62,8 +66,11 @@
             "change"
             (target)=>
               root = @root()
-              @destroy()
-              root.parse_labels_recursive_func target.get("name"), target
+              if target.get("name") != target.previous("name")
+                prev_name = target.previous("name")
+                removed_label = root.query(prev_name)
+                removed_label.destroy()
+                root.parse_labels_recursive_func target.get("name"), target
           )
           label.on(
             "remove"
@@ -75,6 +82,15 @@

         # rec
         childrens[category_name].parse_labels_recursive_func.call childrens[category_name], next_label_name, label
+
+      query: (label_name)->
+        # label_name example: "category/..."
+        category_name = if /\//.test(label_name) then label_name.match(/([^\/]*)\//)[1] else label_name
+        next_label_name = label_name.substring category_name.length + 1
+        unless /\//.test label_name
+          return @children(category_name)
+        else
+          return @children(category_name).query(next_label_name)

       set_label: (label)->
         @set "label", label


Add onShow() method

Add onShow() method

--- src/scripts/helpers/backbone/views/base.coffee
+++ src/scripts/helpers/backbone/views/base.coffee
@@ -3,6 +3,9 @@
   _ = require('underscore')
   Backbone = require('backbone')
   settings = require('cs!settings')
+
+  cleanObject = (obj) ->
+    delete obj[key] for key of obj

   class Region
     constructor: (@el, @parent) ->
@@ -21,6 +24,7 @@
       @views ?= []
       @views.push(view)
       view.setElement($("<#{type}>").appendTo(@$el)).render()
+      view.onShow()

     empty: () ->
       _.each @views, (view) ->
@@ -32,7 +36,7 @@

     close: () ->
       @empty()
-      delete @[key] for key of @
+      cleanObject()

   class Regions
     constructor: (regions = {}, $context) ->
@@ -70,20 +74,25 @@
       @_renderDom(data)

     render: () ->
-      @onBeforeRender?()
+      @onBeforeRender()
       @_render()
-      @onRender?()
+      @onRender()
       if @_rendered then @onDomRefresh?() else @_rendered = true

       return @

+    onShow: () -> # noop
+    onBeforeRender: () -> # noop
+    onRender: () -> # noop
+    onBeforeClose: () -> # noop
+
     close: () ->
-      @onBeforeClose?()
+      @onBeforeClose()

       _.each @regions, (region) ->
         region.close()

       @remove()
       @unbind()
-      delete @[key] for key of @
+      cleanObject()
       return @


Tidied the translate regular expression with non-matching groups so msg.match only returns the terms we're interested in.

--- src/hubot/scripts/translate.coffee
+++ src/hubot/scripts/translate.coffee
@@ -65,10 +65,10 @@
       return code if lang.toLowerCase() is language.toLowerCase()

 module.exports = (robot) ->
-  robot.respond /(translate)( me)?(( from) ([a-z]*))?(( (in)?to) ([a-z]*))? (.*)/i, (msg) ->
-    term   = "\"#{msg.match[10]}\""
-    origin = if msg.match[5] isnt undefined then getCode(msg.match[5], languages) else 'auto'
-    target = if msg.match[9] isnt undefined then getCode(msg.match[9], languages) else 'en'
+  robot.respond /(?:translate)(?: me)?(?:(?: from) ([a-z]*))?(?:(?: (?:in)?to) ([a-z]*))? (.*)/i, (msg) ->
+    term   = "\"#{msg.match[3]}\""
+    origin = if msg.match[1] isnt undefined then getCode(msg.match[1], languages) else 'auto'
+    target = if msg.match[2] isnt undefined then getCode(msg.match[2], languages) else 'en'

     msg.http("http://translate.google.com/translate_a/t")
       .query({
@@ -90,7 +90,7 @@
           language =languages[parsed[2]]
           parsed = parsed[0] && parsed[0][0] && parsed[0][0][0]
           if parsed
-            if msg.match[9] is undefined
+            if msg.match[2] is undefined
               msg.send "#{term} is #{language} for #{parsed}"
             else
               msg.send "The #{language} #{term} translates as #{parsed} in #{languages[target]}"


removePeer() tests for Node

removePeer() tests for Node

--- src/tests/spec/public/library/node.spec.coffee
+++ src/tests/spec/public/library/node.spec.coffee
@@ -135,6 +135,43 @@
 							return success
 						, 1000)

+			describe 'when removing a peer', ->
+				fakePeer = null
+
+				beforeEach ->
+					fakePeer = {
+						a: 1
+						b: 2
+						die: ->
+					}
+
+				it 'should get killed first', ->
+					fakePeer.die = jasmine.createSpy('die')
+					node.removePeer(fakePeer)
+					expect(fakePeer.die).toHaveBeenCalled()
+
+				it 'should get removed from the internal list', ->
+					spyOn(node._peers, 'remove')
+					node.removePeer(fakePeer)
+					expect(node._peers.remove.mostRecentCall.args).toEqual([
+							fakePeer
+						])
+
+				it 'should trigger the peer.removed event', ->
+					success = false
+					node.on('peer.removed', ( peer ) ->
+							success = true
+						)
+					node.removePeer(fakePeer)
+					waitsFor(->
+							return success
+						, 1000)
+
+				it 'should trigger the _triggerStaySuperNodeTimeout()', ->
+					spyOn(node, '_triggerStaySuperNodeTimeout')
+					node.removePeer(fakePeer)
+					expect(node._triggerStaySuperNodeTimeout).toHaveBeenCalled()
+
 			describe 'when entering network', ->

 				beforeEach ->


updating class to say className

updating class to say className

--- src/components/task-step/exercise-multiple-choice.cjsx
+++ src/components/task-step/exercise-multiple-choice.cjsx
@@ -53,7 +53,7 @@

     <Question model={question} answer_id={answer_id} correct_answer_id={correct_answer_id} feedback_html={feedback_html} onChange={@onAnswerChanged}>

{free_response}

-
Choose the best answer from the following:

+
Choose the best answer from the following:

     </Question>

   onAnswerChanged: (answer) ->


Ensure we only subscribe once

Ensure we only subscribe once

--- components/SendGraphChanges.coffee
+++ components/SendGraphChanges.coffee
@@ -5,6 +5,7 @@
     @runtime = null
     @graph = null
     @changes = []
+    @subscribed = false
     @inPorts = new noflo.InPorts
       runtime:
         datatype: 'object'
@@ -34,6 +35,7 @@
       do @subscribe

   subscribe: ->
+    return if @subscribed
     return if !@runtime or !@graph
     @graph.on 'endTransaction', @send
     @graph.on 'addNode', @addNode
@@ -47,6 +49,7 @@
     @graph.on 'removeInport', @removeInport
     @graph.on 'addOutport', @addOutport
     @graph.on 'removeOutport', @removeOutport
+    @subscribed = true

   unsubscribe: ->
     return unless @graph
@@ -62,6 +65,7 @@
     @graph.removeListener 'removeInport', @removeInport
     @graph.removeListener 'addOutport', @addOutport
     @graph.removeListener 'removeOutport', @removeOutport
+    @subscribed = false

     @outPorts.sent.disconnect()
     @outPorts.queued.disconnect()
@@ -142,7 +146,7 @@
       public: pub
       graph: @graph.properties.id

-  addOuport: (pub, priv) =>
+  addOutport: (pub, priv) =>
     @registerChange 'addoutport',
       public: pub
       node: priv.process


Reformat on medical_conditions.coffee file.

--- BHCDatabase/app/assets/javascripts/medical_conditions.coffee
+++ BHCDatabase/app/assets/javascripts/medical_conditions.coffee
@@ -3,27 +3,27 @@
 # You can use CoffeeScript in this file: http://coffeescript.org/

 ready = ->
-    btn = undefined
-    modal = undefined
-    span = undefined
-    modal = document.getElementById('modalcontainer')
-    btn = document.getElementById('openmodal')
-    span = document.getElementsByClassName('close')[0]
+  btn = undefined
+  modal = undefined
+  span = undefined
+  modal = document.getElementById('modalcontainer')
+  btn = document.getElementById('openmodal')
+  span = document.getElementsByClassName('close')[0]

-    btn.onclick = ->
-      modal.style.display = 'block'
-      return
+  btn.onclick = ->
+    modal.style.display = 'block'
+    return

-    span.onclick = ->
+  span.onclick = ->
+    modal.style.display = 'none'
+    return
+
+  window.onclick = (event) ->
+    if event.target == modal
       modal.style.display = 'none'
-      return
-
-    window.onclick = (event) ->
-      if event.target == modal
-        modal.style.display = 'none'
-      return
+    return

-  $(document).ready(ready)
-  $(document).on('turbolinks:load', ready)
-  $(document).on('page:load', ready)
+$(document).ready(ready)
+$(document).on('turbolinks:load', ready)
+$(document).on('page:load', ready)


mra template

mra template

--- templates/mira/app/assets/coffee/router.coffee
+++ templates/mira/app/assets/coffee/router.coffee
@@ -7,4 +7,4 @@
       console.log 'init:: Router -> AppRouter'

     appRoutes:
-      " "                   : 'index'
+      ""                   : 'index'


Improved tests.

Improved tests.

--- test/suite/CodepointPropertyReader.spec.coffee
+++ test/suite/CodepointPropertyReader.spec.coffee
@@ -19,29 +19,29 @@
     describe 'bidiClass()', ->

         it 'returns correct Bidi classes', ->
-            assert.strictEqual @subject.bidiClass(0x0000), CodepointPropertyReader.BIDI.BN    # BN
-            assert.strictEqual @subject.bidiClass(0x0009), CodepointPropertyReader.BIDI.OTHER # S
-            assert.strictEqual @subject.bidiClass(0x000A), CodepointPropertyReader.BIDI.OTHER # B
-            assert.strictEqual @subject.bidiClass(0x000C), CodepointPropertyReader.BIDI.OTHER # WS
-            assert.strictEqual @subject.bidiClass(0x0021), CodepointPropertyReader.BIDI.ON    # ON
-            assert.strictEqual @subject.bidiClass(0x0023), CodepointPropertyReader.BIDI.ET    # ET
-            assert.strictEqual @subject.bidiClass(0x002B), CodepointPropertyReader.BIDI.ES    # ES
-            assert.strictEqual @subject.bidiClass(0x002C), CodepointPropertyReader.BIDI.CS    # CS
-            assert.strictEqual @subject.bidiClass(0x0030), CodepointPropertyReader.BIDI.EN    # EN
-            assert.strictEqual @subject.bidiClass(0x0041), CodepointPropertyReader.BIDI.L     # L
-            assert.strictEqual @subject.bidiClass(0x0300), CodepointPropertyReader.BIDI.NSM   # NSM
-            assert.strictEqual @subject.bidiClass(0x05BE), CodepointPropertyReader.BIDI.R     # R
-            assert.strictEqual @subject.bidiClass(0x0600), CodepointPropertyReader.BIDI.AN    # AN
-            assert.strictEqual @subject.bidiClass(0x0608), CodepointPropertyReader.BIDI.AL    # AL
-            assert.strictEqual @subject.bidiClass(0x202A), CodepointPropertyReader.BIDI.OTHER # LRE
-            assert.strictEqual @subject.bidiClass(0x202B), CodepointPropertyReader.BIDI.OTHER # RLE
-            assert.strictEqual @subject.bidiClass(0x202C), CodepointPropertyReader.BIDI.OTHER # PDF
-            assert.strictEqual @subject.bidiClass(0x202D), CodepointPropertyReader.BIDI.OTHER # LRO
-            assert.strictEqual @subject.bidiClass(0x202E), CodepointPropertyReader.BIDI.OTHER # RLO
-            assert.strictEqual @subject.bidiClass(0x2066), CodepointPropertyReader.BIDI.OTHER # LRI
-            assert.strictEqual @subject.bidiClass(0x2067), CodepointPropertyReader.BIDI.OTHER # RLI
-            assert.strictEqual @subject.bidiClass(0x2068), CodepointPropertyReader.BIDI.OTHER # FSI
-            assert.strictEqual @subject.bidiClass(0x2069), CodepointPropertyReader.BIDI.OTHER # PDI
+            assert.strictEqual @subject.bidiClass(0x0000), CodepointPropertyReader.BIDI.BN, 'BN'
+            assert.strictEqual @subject.bidiClass(0x0009), CodepointPropertyReader.BIDI.OTHER, 'S'
+            assert.strictEqual @subject.bidiClass(0x000A), CodepointPropertyReader.BIDI.OTHER, 'B'
+            assert.strictEqual @subject.bidiClass(0x000C), CodepointPropertyReader.BIDI.OTHER, 'WS'
+            assert.strictEqual @subject.bidiClass(0x0021), CodepointPropertyReader.BIDI.ON, 'ON'
+            assert.strictEqual @subject.bidiClass(0x0023), CodepointPropertyReader.BIDI.ET, 'ET'
+            assert.strictEqual @subject.bidiClass(0x002B), CodepointPropertyReader.BIDI.ES, 'ES'
+            assert.strictEqual @subject.bidiClass(0x002C), CodepointPropertyReader.BIDI.CS, 'CS'
+            assert.strictEqual @subject.bidiClass(0x0030), CodepointPropertyReader.BIDI.EN, 'EN'
+            assert.strictEqual @subject.bidiClass(0x0041), CodepointPropertyReader.BIDI.L, 'L'
+            assert.strictEqual @subject.bidiClass(0x0300), CodepointPropertyReader.BIDI.NSM, 'NSM'
+            assert.strictEqual @subject.bidiClass(0x05BE), CodepointPropertyReader.BIDI.R, 'R'
+            assert.strictEqual @subject.bidiClass(0x0600), CodepointPropertyReader.BIDI.AN, 'AN'
+            assert.strictEqual @subject.bidiClass(0x0608), CodepointPropertyReader.BIDI.AL, 'AL'
+            assert.strictEqual @subject.bidiClass(0x202A), CodepointPropertyReader.BIDI.OTHER, 'LRE'
+            assert.strictEqual @subject.bidiClass(0x202B), CodepointPropertyReader.BIDI.OTHER, 'RLE'
+            assert.strictEqual @subject.bidiClass(0x202C), CodepointPropertyReader.BIDI.OTHER, 'PDF'
+            assert.strictEqual @subject.bidiClass(0x202D), CodepointPropertyReader.BIDI.OTHER, 'LRO'
+            assert.strictEqual @subject.bidiClass(0x202E), CodepointPropertyReader.BIDI.OTHER, 'RLO'
+            assert.strictEqual @subject.bidiClass(0x2066), CodepointPropertyReader.BIDI.OTHER, 'LRI'
+            assert.strictEqual @subject.bidiClass(0x2067), CodepointPropertyReader.BIDI.OTHER, 'RLI'
+            assert.strictEqual @subject.bidiClass(0x2068), CodepointPropertyReader.BIDI.OTHER, 'FSI'
+            assert.strictEqual @subject.bidiClass(0x2069), CodepointPropertyReader.BIDI.OTHER, 'PDI'

     describe 'isNonAsciiSpace()', ->


Added factory for no-yes buttons closes #112

--- wzk/ui/dialog/ButtonSet.coffee
+++ wzk/ui/dialog/ButtonSet.coffee
@@ -34,6 +34,15 @@
   ###*
     @return {wzk.ui.dialog.ButtonSet}
   ###
+  @createNoYes: ->
+    btns = new wzk.ui.dialog.ButtonSet()
+    btns.addButton goog.ui.Dialog.ButtonSet.DefaultButtons.NO, false, true
+    btns.addButton goog.ui.Dialog.ButtonSet.DefaultButtons.YES, true
+    btns
+
+  ###*
+    @return {wzk.ui.dialog.ButtonSet}
+  ###
   @createYesNoCancel: ->
     btns = new wzk.ui.dialog.ButtonSet()
     btns.addButton goog.ui.Dialog.ButtonSet.DefaultButtons.YES


Fix failing command line tests for select bindings.

--- tests/batman/view/select_rendering_test.coffee
+++ tests/batman/view/select_rendering_test.coffee
@@ -1,7 +1,7 @@
 helpers = if typeof require is 'undefined' then window.viewHelpers else require './view_helper'

 oldDeferEvery = Batman.DOM.IteratorBinding::deferEvery
-getSelections = (node) -> (c.selected for c in node[0].children when c.nodeType is window.Node.ELEMENT_NODE)
+getSelections = (node) -> node.find('option').map((i, node) -> !!node.selected).toArray()
 getContents = (node) -> node.find('option').map((i, node) -> node.innerHTML).toArray()

 QUnit.module 'Batman.View select bindings'
@@ -68,23 +68,25 @@

     QUnit.start()

-asyncTest 'it binds options created by a foreach and remains consistent when the set instance iterated over swaps', 4, ->
-  leo = new Batman.Object ({name: 'leo', id: 1})
-  mikey = new Batman.Object ({name: 'mikey', id: 2})
+asyncTest 'it binds options created by a foreach and remains consistent when the set instance iterated over swaps', ->
+  leo = Batman name: 'leo', id: 1
+  mikey = Batman name: 'mikey', id: 2

   context = Batman
     heroes: new Batman.Set(leo, mikey).sortedBy('id')
-    selected: mikey
-
-  helpers.render  '<select data-bind="selected.id">' +
+    selected: 1
+
+  helpers.render  '<select data-bind="selected">' +
                     '<option data-foreach-hero="heroes" data-bind-value="hero.id" data-bind="hero.name" />' +
                   '</selected>', context, (node) ->
-    deepEqual getContents(node), ['leo', 'mikey']
-    equal node[0].value, mikey.get('id')
-    context.set 'heroes', new Batman.Set(leo, mikey).sortedBy('id')
     delay ->
       deepEqual getContents(node), ['leo', 'mikey']
-      equal node[0].value, mikey.get('id')
+      equal node[0].value, "1"
+
+      context.set 'heroes', new Batman.Set(leo, mikey).sortedBy('id')
+      delay ->
+        deepEqual getContents(node), ['leo', 'mikey']
+        equal node[0].value, "1"

 asyncTest 'it binds the value of a multi-select box and updates the options when the bound value changes', ->
   context = new Batman.Object
@@ -112,8 +114,6 @@
   '''

   helpers.render source, context, (node) ->
-    getSelections = -> (c.selected for c in node[0].children when c.nodeType is window.Node.ELEMENT_NODE)
-
     deepEqual context.get('selected.names'), ['crono', 'link']
     deepEqual getSelections(node), []
     deepEqual getContents(node), []


trigger rebuild

trigger rebuild

--- app/assets/javascripts/admin/views/task/task_overview_view.coffee
+++ app/assets/javascripts/admin/views/task/task_overview_view.coffee
@@ -160,7 +160,7 @@

     teamSelectionView = new TeamSelectionView(
       collection: teamCollection
-      childViewOptions :
+      childViewOptions:
         modelValue: -> return "#{@model.get("name")}"
       name: "team"
       events:


Fixed bug with updated quantity with keyboard

--- app/assets/javascripts/sprangular/directives/quantityInput.coffee
+++ app/assets/javascripts/sprangular/directives/quantityInput.coffee
@@ -11,7 +11,8 @@
   controller: ($scope, Cart, Angularytics) ->
     $scope.$watch 'quantity', (newValue, oldValue)->
       if oldValue != newValue
-        Cart.updateItemQuantity($scope.variant.id, $scope.quantity) if $scope.updateCart
+        if $scope.updateCart
+          Cart.updateItemQuantity($scope.variant.id, $scope.quantity)

         if oldValue > newValue
           Angularytics.trackEvent("Cart", "Quantity decrease", oldValue - newValue)


if name field is pristine, set whole form to pristine when password form is closed

--- app/components/profile-settings-page/profile-settings-page.coffee
+++ app/components/profile-settings-page/profile-settings-page.coffee
@@ -45,6 +45,8 @@
       $scope.showPasswordFields = false
       _.each ['current_password', 'password', 'confirm_password'], (fieldName) ->
         $scope.accountDetailsForm[fieldName].$setPristine()
+      if $scope.accountDetailsForm.name.$pristine
+        $scope.accountDetailsForm.$setPristine()

     $scope.save = ->
       $scope.formSubmitted = true


account: code formatting

account: code formatting

--- client/account/lib/views/accountsshmachinelistitem.coffee
+++ client/account/lib/views/accountsshmachinelistitem.coffee
@@ -20,7 +20,7 @@
     active = state is Machine.State.Running

     @label = new KDLabelView
-      title    : label
+      title : label
     @switcher = new KDCheckBox
       defaultValue : active
       disabled     : not active


reactivity: chat input: a small peace of code cleanup

--- client/activity/lib/flux/stores/emojis/emojisstore.coffee
+++ client/activity/lib/flux/stores/emojis/emojisstore.coffee
@@ -13,6 +13,8 @@
 ###
 module.exports = class EmojisStore extends KodingFluxStore

+  @getterPath = 'EmojisStore'
+
   SKIPPED_EMOJIES = [
     'back'
     'black_medium_small_square'
@@ -25,8 +27,6 @@
     'white_small_square'
   ]

-  @getterPath = 'EmojisStore'
-
   getInitialState: ->

     emojiList = emojisKeywords.filter (emoji) ->


helpers for dealing with tags

helpers for dealing with tags

--- manager/app/assets/javascripts/models/host.js.coffee
+++ manager/app/assets/javascripts/models/host.js.coffee
@@ -16,6 +16,26 @@
     tags: ->
       return @get("tags").split(",")

+    has_tag: (tag) ->
+      return _.include(@tags(), tag)
+
+    is_new: ->
+      return @has_tag("new")
+
+    add_tag: (tag) ->
+      tags = @tags()
+      tags.push(tag)
+      @set_tags(tags)
+
+    remove_tag: (tag) ->
+      @set_tags _.reject(@tags(), (t) -> t == tag)
+
+    set_tags: (tags) ->
+      if ! _.isString(tags)
+        tags = tags.join(",")
+
+      @set("tags", tags, {silent: true})
+
   class exports.HostList extends Stark.Collection
     model: exports.Host
     url: ->


Teams: Creation: Don't add size attribute to domain input automatically while the form is showing

--- client/landing/site.landing/coffee/team/tabs/teamdomaintab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamdomaintab.coffee
@@ -48,10 +48,11 @@
     then teamName = slug
     else teamName = KD.utils.slugifyCompanyName team

-    @form.input.setValue teamName
-    @form.input.setAttribute 'size', teamName.length
+    { input } = @form

-    @form.input.emit 'ValidationFeedbackCleared'
+    input.setValue teamName
+    input.emit 'input'
+    input.emit 'ValidationFeedbackCleared'

   showError: (error) ->


Fix scroll issues

Fix scroll issues

--- framer/Components/NavComponent.coffee
+++ framer/Components/NavComponent.coffee
@@ -27,9 +27,6 @@
 			backgroundColor: "black"
 			visible: false

-		@background.onTap =>
-			@back()
-
 		# Screen.onEdgeSwipeLeftEnd (e) =>
 		# 	@back()

@@ -58,11 +55,13 @@

 		layer.point = Utils.pointZero()

-		scroll = ScrollComponent.wrap(layer)
+		scroll = new ScrollComponent
+		scroll.name = "scroll"
 		scroll.size = @size
 		scroll.backgroundColor = @backgroundColor
 		scroll.scrollHorizontal = layer.width > @width
 		scroll.scrollVertical = layer.height > @height
+		layer.parent = scroll.content

 		layer[NavComponentLayerScrollKey] = scroll

@@ -96,16 +95,17 @@

 		@_stack.push({layer:layer, transition:transition})

-	dialog: (layer)->
+	dialog: (layer) ->
 		@push(layer, Transitions.dialog, true, false)

-	modal: (layer)->
+	modal: (layer) ->
 		@push(layer, Transitions.modal, true, false)

-	back: ->
+	back: =>
 		return unless @previous
 		previous = @_stack.pop()
 		previous?.transition.back()
+		@emit("back")

 	@define "stack",
 		get: -> _.clone(@_stack)


When playlist is empty, this would have errored

--- frontend/app/js/player-service.coffee
+++ frontend/app/js/player-service.coffee
@@ -40,7 +40,7 @@
     else if data.Name
       now = data.Name
       sub = ""
-    else
+    else if data.file
       parts = data.file.split("/")
       now = parts[parts.length-1]
       sub = parts[0..parts.length-2].join("/")


ActivityTicker: touch everywhere

ActivityTicker: touch everywhere

--- workers/social/lib/social/models/activityticker.coffee
+++ workers/social/lib/social/models/activityticker.coffee
@@ -2,6 +2,7 @@
 {Relationship} = require "jraphical"

 {secure, daisy, dash, Base} = Bongo
+{uniq} = require 'underscore'

 module.exports = class ActivityTicker extends Base
   @share()
@@ -10,27 +11,29 @@
     sharedMethods :
       static      : ["fetch"]

-  relationshipNames = ["follower", "like", "member", "user", "reply", "author"]
-  constructorNames  = ["JAccount", "JNewApp", "JGroup", "JTag", "JNewStatusUpdate", "JComment"]
+  relationshipNames = ["follower", "like", "member", "user",  "author"]
+  constructorNames  = ["JAccount", "JNewApp", "JGroup", "JTag", "JNewStatusUpdate"]

   JAccount = require './account'

-  filterSources = (filters) =>
+  mapSourceNames = (filters)=>
     relationshipMap =
-      "follower" : ["JTag"]
+      "follower" : ["JAccount", "JTag"]
       "like"     : ["JAccount", "JNewStatusUpdate"]
       "member"   : ["JGroup"]
       "user"     : ["JApp"]

     sources = []
+    # iterate on filters and add required
+    # constructor names into sourceNames
     for filter in filters
       sources.concat relationshipMap[filter]

-    if sources.length is 0
-      return constructorNames
-    return sources
+    # if we couldnt find any applied filter
+    # it should be checked at calling side
+    return uniq sources

-  filterTargets = (filters) ->
+  mapTargetNames = (filters) ->
     validFilters = ["follower", "like", "member", "user"]
     targets = []
     # The only possible options are either returning only "JAccount" or
@@ -64,6 +67,11 @@

   decorateCommentEvent = (relationship, callback) ->
+    # it will be decorated as
+    # source is JAccount          -- doer
+    # target is JAccount          -- owner of the status update
+    # object is JComment          -- the actual comment
+    # subject is JNewStatusUpdate -- post that is commented on
     {source, target, as, timestamp} = relationship
     modifiedEvent =
       as        : as
@@ -71,7 +79,19 @@
       object    : target
       timestamp : timestamp

+    # rewrite this part without dash ~ C.S
     queue = [
+      ->
+        source.fetchTags (err, tags) ->
+          return callback err if err
+          modifiedEvent.subject.tags = tags
+          queue.fin()
+      # disable for now
+      # ->
+      #   target.fetchTags (err, tags) ->
+      #     return callback err if err
+      #     modifiedEvent.object.tags = tags
+      #     queue.fin()
       ->
         JAccount.one "_id": source.originId, (err, targetAccount) ->
           return callback err if err
@@ -114,6 +134,7 @@
         do kallback

   @fetch = secure (client, options = {}, callback) ->
+    console.log "hede"
     {connection: {delegate}} = client

     sources = constructorNames
@@ -121,14 +142,14 @@
     targets = constructorNames

     filters = []
-    if options?.filters
+    if options.filters
       for filter in options.filters  when filter in relationshipNames
         filters.push filter

     if filters.length > 0
-      sources = filterSources options.filters
-      as      = options.filters
-      targets = filterTargets options.filters
+      sources = mapSourceNames filters
+      as      = filters
+      targets = mapTargetNames filters

     from = options.from or +(new Date())
@@ -138,12 +159,12 @@
       targetName : "$in": targets
       timestamp : {"$lt" : new Date(from)}

-    options      =
+    relOptions      =
       # do not fetch more than 15 at once
       limit      : 10 # Math.min options.limit ? 15, 15
       sort       : timestamp  : -1

-    Relationship.some selector, options, (err, relationships) ->
+    Relationship.some selector, relOptions, (err, relationships) ->
       buckets = []
       return  callback err, buckets  if err
       daisy queue = relationships.map (relationship) ->


ActivityTicker: Status updates are fetched with their tags

--- workers/social/lib/social/models/activityticker.coffee
+++ workers/social/lib/social/models/activityticker.coffee
@@ -10,7 +10,7 @@
     sharedMethods :
       static      : ["fetch"]

-  relationshipNames = ["follower", "like", "member", "user", "reply"]
+  relationshipNames = ["follower", "like", "member", "user", "reply", "author"]
   constructorNames  = ["JAccount", "JApp", "JGroup", "JTag", "JStatusUpdate","JComment"]

   JAccount = require './account'
@@ -24,8 +24,20 @@
       decorateLikeEvent relationship, callback
     else if as is "reply"
       decorateCommentEvent relationship, callback
+    else if as is "author"
+      decorateStatusUpdateEvent relationship, callback
     else
       callback null, {source, target, as, timestamp}
+
+  decorateStatusUpdateEvent = (relationship, callback) ->
+    {source, target, as, timestamp} = relationship
+    source.fetchTags (err, tags) ->
+      return callback err if err
+      console.log 'Tags', tags
+      source.tags = tags
+      console.log 'Releysin', relationship
+      callback null, relationship
+

   decorateCommentEvent = (relationship, callback) ->
     {source, target, as, timestamp} = relationship


Fixed AutoCompleteSearchView test

Fixed AutoCompleteSearchView test

--- core/spec/javascripts/views/auto_complete/search_view_spec.coffee
+++ core/spec/javascripts/views/auto_complete/search_view_spec.coffee
@@ -28,6 +28,7 @@
       )
       expect(window.AutoCompleteSearchListView).to.have.been.calledWith(
         model: view.model
+        collection: view.search_collection
       )

   describe 'searchCollection', ->


adjusted trigger method, because uglifier doesn't seem to handle the arguments... splat in coffeescript

--- app/assets/javascripts/mercury/mercury_editor.js.coffee
+++ app/assets/javascripts/mercury/mercury_editor.js.coffee
@@ -192,9 +192,9 @@
     $(document).bind("mercury:#{eventName}", callback)

-  trigger: (eventName, arguments...) ->
-    Mercury.log(eventName, arguments)
-    $(document).trigger("mercury:#{eventName}", arguments)
+  trigger: (eventName, options) ->
+    Mercury.log(eventName, options)
+    $(document).trigger("mercury:#{eventName}", options)

   log: ->


Set a feed's unread entries count to zero if a 404 is received when trying to get the first page of entries.

--- app/assets/javascripts/ng-services/ng-ReadSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-ReadSvc.js.coffee
@@ -50,7 +50,9 @@
       entriesPaginationSvc.set_busy false
       if status == 404
         entriesPaginationSvc.set_more_entries_available false
-        timerFlagSvc.start 'error_no_entries' if entriesPaginationSvc.is_first_page()
+        if entriesPaginationSvc.is_first_page()
+          timerFlagSvc.start 'error_no_entries'
+          currentFeedSvc.get().unread_entries = 0
       else
         currentFeedSvc.unset()
         timerFlagSvc.start 'error_loading_entries'


fix change password error message

fix change password error message

--- app/coffee/modules/user-settings/change-password.coffee
+++ app/coffee/modules/user-settings/change-password.coffee
@@ -89,4 +89,4 @@
         link:link
     }

-module.directive("tgUserChangePassword", ["$tgResources", "$tgConfirm", "$tgLoading", UserChangePasswordDirective])
+module.directive("tgUserChangePassword", ["$tgResources", "$tgConfirm", "$tgLoading", "$translate", UserChangePasswordDirective])


Better handling of affiliation change submission.

--- services/web/public/coffee/main/affiliations/controllers/UserAffiliationsController.coffee
+++ services/web/public/coffee/main/affiliations/controllers/UserAffiliationsController.coffee
@@ -63,8 +63,11 @@
 					$scope.affiliationToChange.role,
 					$scope.affiliationToChange.department
 				)
-				.then () -> _getUserEmails()
-				.catch () -> $scope.ui.hasError = true
+				.then () ->
+					_reset()
+					_getUserEmails()
+				.catch () ->
+					$scope.ui.hasError = true

 		$scope.cancelAffiliationChange = (email) ->
 			$scope.affiliationToChange.email = ""


clicking on a contenteditable should not select text

--- www-client/src/www/lib/fora-editor/text.coffee
+++ www-client/src/www/lib/fora-editor/text.coffee
@@ -11,9 +11,9 @@
         if @binding.type is 'text' and not @binding.multiline
             @element.attr 'spellcheck', false

-        @element.click @onFocus
+        @element.mousedown @onClick
+        @element.bind 'touchstart', @onClick
         @element.focus @onFocus
-        @element.bind 'touch', @onFocus
         @element.blur @onBlur
         @element.keydown @onKeydown
         @element.keyup @onKeyup
@@ -30,12 +30,23 @@
             @evalControlState()

+
+    onClick: =>
+        @focusReason = 'click'
+
+

     onFocus: =>
-        @state.event = "focus"
-        @state.empty = @isEmpty()
-        @evalControlState()
-        @binding.events?.focus? this, arguments
+        console.log "focused:" + Date.now()
+        #if this is already the active element, pretend nothing happened.
+        #This happens when the user clicks the same element with a mouse.
+        if this isnt @editor.activeControl
+            @editor.activeControl = this
+            @state.event = "focus"
+            @state.empty = @isEmpty()
+            @evalControlState()
+            @binding.events?.focus? this, arguments
+            @focusReason = null

@@ -94,7 +105,9 @@
                     placeholder.addClass 'dim'
                     @setCaretPosition 'start'
             else
-                @selectText()
+                #If focus reason is click or touch, then we shouldn't select the text.
+                if @focusReason isnt 'click'
+                    @selectText()

         if @state.event is 'keypress'


Comment out tests for `-` that are failing due to `k`

I don’t want to delete these tests because they will be useful to have when `k` (and correspondingly `j`) is fixed. Otherwise, they would have to be rewritten at that time, or they would not get written and `-` and `+` would not be as well-tested.

The comments explaining why these tests are commented out and saying when they can be re-enabled should protect these tests from being left disabled when they shouldn’t be.

--- spec/motions-spec.coffee
+++ spec/motions-spec.coffee
@@ -544,7 +544,8 @@
         it "moves the cursor to the first character of the previous line", ->
           expect(editor.getCursorScreenPosition()).toEqual [0, 0]

-      describe "as a selection", ->
+      # commented out because this fails due to a bug in `k`; re-enable when `k` is fixed
+      xdescribe "as a selection", ->
         beforeEach ->
           keydown('d')
           keydown('-')
@@ -562,7 +563,8 @@
         it "moves to the first character of the previous line (directly above)", ->
           expect(editor.getCursorScreenPosition()).toEqual [1, 2]

-      describe "as a selection", ->
+      # commented out because this fails due to a bug in `k`; re-enable when `k` is fixed
+      xdescribe "as a selection", ->
         beforeEach ->
           keydown('d')
           keydown('-')
@@ -580,7 +582,8 @@
         it "moves the cursor to the first character of the previous line", ->
           expect(editor.getCursorScreenPosition()).toEqual [1, 2]

-      describe "as a selection", ->
+      # commented out because this fails due to a bug in `k`; re-enable when `k` is fixed
+      xdescribe "as a selection", ->
         beforeEach ->
           keydown('d')
           keydown('-')
@@ -602,7 +605,8 @@
         it "moves the cursor to the first character of that many lines previous", ->
           expect(editor.getCursorScreenPosition()).toEqual [1, 0]

-      describe "as a selection", ->
+      # commented out because this fails due to a bug in `k`; re-enable when `k` is fixed
+      xdescribe "as a selection", ->
         beforeEach ->
           keydown('d')
           keydown('3')
@@ -615,6 +619,7 @@
   describe "the + keybinding (which should also be equivalent to the enter keybinding)", ->
     # TODO copy the tests for the - keybinding into here, and then
     #  change the values to expect downwards instead of upwards movement
+    # to ease editing, copy the tests only after enabling all disabled tests for -

   describe "the gg keybinding", ->
     beforeEach ->


Composition#removeBlockAttributes

Composition#removeBlockAttributes

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -82,7 +82,7 @@
       attributes = block.getAttributes()
       if attributes.bullet or attributes.number
         if block.isEmpty()
-          @removeCurrentAttribute(key) for key of block.getAttributes()
+          @removeBlockAttributes()
         else
           @insertBlockBreak()
       else
@@ -90,7 +90,7 @@
         switch
           # Remove block attributes
           when block.isEmpty()
-            @removeCurrentAttribute(key) for key of block.getAttributes()
+            @removeBlockAttributes()
           # Break out of block after a newline (and remove the newline)
           when text.endsWithString("\n")
             @expandSelectionInDirection("backward")
@@ -156,6 +156,11 @@
       @document.removeTextAtLocationRange(locationRange)
       @setLocationRange(locationRange.collapse())

+  removeBlockAttributes: ->
+    range = @getLocationRange()
+    block = @document.getBlockAtIndex(range.end.index)
+    @removeCurrentAttribute(key) for key of block.getAttributes()
+
   # Current attributes

   hasCurrentAttribute: (attributeName) ->


Fix dragging selection

Fix dragging selection

--- src/trix/models/composition.coffee
+++ src/trix/models/composition.coffee
@@ -392,8 +392,8 @@
     @editingAttachment or @positionIsCursorTarget(@getPosition())

   getSelectedDocument: ->
-    if locationRange = @getLocationRange()
-      @document.getDocumentAtLocationRange(locationRange)
+    if selectedRange = @getSelectedRange()
+      @document.getDocumentAtRange(selectedRange)

   # Attachments


only sub on hash.type if it exists

only sub on hash.type if it exists

--- app/assets/javascripts/serializers/task_serializer.js.coffee
+++ app/assets/javascripts/serializers/task_serializer.js.coffee
@@ -1,7 +1,8 @@
 ETahi.TaskSerializer = DS.ActiveModelSerializer.extend ETahi.SerializesHasMany,
   normalize: (type, hash) ->
     hash.qualified_type = hash.type
-    hash.type = hash.type.replace(/.+::/, '')
+    if hash.type
+      hash.type = hash.type.replace(/.+::/, '')
     this._super.apply(this, arguments)

   serializeIntoHash: (data, type, record, options) ->


修复 日期下拉框Safari 下的bug

修复 日期下拉框Safari 下的bug

--- src/widget/edit/date-picker.coffee
+++ src/widget/edit/date-picker.coffee
@@ -586,7 +586,7 @@
 			@_dataGrid = dateGrid = new cola.DateGrid({
 				cellClick: (self, arg)=>
 					value = $fly(arg.element).attr("cell-date")
-					d = new Date(value + " 00:00:00")
+					d = new Date(value)
 					datePicker.close(d)
 			})
 			dateGrid.setCurrentDate(new Date())
@@ -948,7 +948,7 @@
 				cellClick: (self, arg)=>
 					datePicker.close(self.get("value"))
 			})
-
+
 			content = $.xCreate({
 				tagName: "div"
 				class: "month-picker"


Check if error is a string first. Fixes Term Pairs edit error

--- app/assets/javascripts/admin/admin_in_place_editor.js.coffee
+++ app/assets/javascripts/admin/admin_in_place_editor.js.coffee
@@ -392,11 +392,15 @@
         newParams[$(@).attr('data-resource')][params.name] = params.value
         return newParams
       error: ->
-        errors = JSON.parse(arguments[0].responseText).errors
+        errors = arguments[0].responseText
         errorsMessages = []
-        for k, v of errors
-          errorsMessages.push v
-        errorsMessages.join ', '
+        if typeof errors is 'string'
+          errorMessages = errors
+        else
+          errors = JSON.parse(errors).errors
+          for k, v of errors
+            errorsMessages.push v
+          errorsMessages.join ', '
       display: (item) ->
         # Hack around: https://github.com/vitalets/x-editable/issues/431
         if $(@).attr('data-type') == 'select'


Normalize accumulate exercise

Normalize accumulate exercise

* require the correct file
* reorder expectations

The expectations had `expected` and `actual` reversed, which causes
error messages to be very confusing.

--- accumulate/accumulate_test.spec.coffee
+++ accumulate/accumulate_test.spec.coffee
@@ -1,29 +1,29 @@
-require './example'
+require './accumulate'

 describe '[].accumulate()', ->

   it 'empty accumulation', ->
     accumulator = (e) -> e * e
-    expect([]).toEqual [].accumulate(accumulator)
+    expect([].accumulate(accumulator)).toEqual []

   xit 'accumulate squares', ->
     accumulator = (number) -> number * number
-    expect([1, 4, 9]).toEqual [1, 2, 3].accumulate accumulator
+    expect([1, 2, 3].accumulate accumulator).toEqual [1, 4, 9]

   xit 'accumulate upcases', ->
     accumulator = (word) -> word.toUpperCase()
     result      = 'hello world'.split(/\s/).accumulate accumulator

-    expect(['HELLO', 'WORLD']).toEqual result
+    expect(result).toEqual ['HELLO', 'WORLD']

   xit 'accumulate reversed strings', ->
     accumulator = (word) -> word.split('').reverse().join('')
     result      = 'the quick brown fox etc'.split(/\s/).accumulate accumulator

-    expect(["eht", "kciuq", "nworb", "xof", "cte"]).toEqual result
+    expect(result).toEqual ["eht", "kciuq", "nworb", "xof", "cte"]

   xit 'accumulate recursively', ->
     result = 'a b c'.split(/\s+/).accumulate (char)  ->
       '1 2 3'.split(/\s+/).accumulate (digit) -> "#{char}#{digit}"

-    expect([["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]]).toEqual result
+    expect(result).toEqual([["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]])


make sure @location is not undefined

make sure @location is not undefined

--- src/remote/gateway.coffee
+++ src/remote/gateway.coffee
@@ -76,7 +76,7 @@
     @apiEndpoint = config.apiEndpoint
     @trackingEndpoint = config.trackingEndpoint
     @identify = identify
-    @location = location
+    @location = location || {}
     @options = _.defaults({}, batching, { min:1, max:1, delay:2 })
     @queue = batchable @options.delay, (requests) -> @flush(requests)

@@ -84,8 +84,9 @@
     ident = {}
     ident['Hull-Bid'] = @identify.browser
     ident['Hull-Sid'] = @identify.session
-    ident['X-Track-Url'] = @location.url
-    ident['X-Track-Referer'] = @location.referer
+    if @location
+      ident['X-Track-Url'] = @location.url
+      ident['X-Track-Referer'] = @location.referer
     ident

   resetIdentify: ->


Instantiate After Freezing, Not Before

Instantiate After Freezing, Not Before

--- src/coffee/quality.coffee
+++ src/coffee/quality.coffee
@@ -6,8 +6,8 @@
     library = {}
     api =
       register: (id, name, options) ->
-        quality = instantiator new Quality()
-        library[id] = Object.freeze(quality)
+        quality = Object.freeze new Quality()
+        library[id] = instantiator quality
         return this # Allow Chaining
       resolve: (q) ->
         if q instanceof Quality


mouseover info

mouseover info

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -30,7 +30,7 @@
     .attr('width', 4)
     .attr('height', 4)
   .append('path')
-    .style('stroke', '#ccc')
+    .style('stroke', '#999')
     .style('stroke-width', 0.5)
     .style('shape-rendering', 'crispedges')
     .attr('d', 'M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2')
@@ -78,6 +78,17 @@
       count = -1 if count == 0
       console.log count, fill(count), fill.domain()
       fill(count)
+    # .datum((d) ->
+    #   name = d.properties.NAME
+    #   places.filter((p) -> p.key == name)[0])
+    .on 'mouseover', (d) ->
+      name = d.properties.NAME
+      place = places.filter((p) -> p.key == name)[0]
+      container = d3.select('.js-info').html(' ')
+      container.append('h2').text(name)
+
+
+

 mapDataToNeighborhoods = (data) ->
   nhoods = {}
@@ -104,9 +115,6 @@
       delete current[2010]

   places = d3.entries(nhoods).filter (nh) -> !shouldExcludeNeighborhood(nh.key)
-  for place in places
-    console.log place.key, place.value['Asian Population'][0]
-
   highlight 'White Population', 0

 loadCensusData = (callback) ->


Remove log.

Remove log.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -1272,7 +1272,6 @@
         siblings = selected.nextAll().slice(0,3).add(selected.prevAll().slice(0,3))
         siblings.each ->
           id = $(@).data('entry-id')
-          console.log id
           feedbin.preloadAssets(id)

     entriesLoading: ->


Fix positioning of member cards when dropped to "not selected" area

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -29,11 +29,9 @@

     $('.call-for-application.open .applicants').droppable
       drop: (event, ui) ->
-        $(@).append(ui.draggable)
-        ui.draggable.position
-          my: "top left"
-          at: "top left"
-          of: $(this)
+        $(@).append(ui.draggable.removeAttr('style'))
+        $(@).find('.member-card:even').removeClass('no-margin')
+        $(@).find('.member-card:odd').addClass('no-margin')

     $('.call-for-application.open .member-card-empty').droppable
       accept: (draggable) ->


update loading msg

update loading msg

--- app/assets/javascripts/users.js.coffee
+++ app/assets/javascripts/users.js.coffee
@@ -11,7 +11,7 @@
 	showLoading = ->
 		# replace current page with gif
 		loadingStatus = ''
-		text = 'Uploading your system information. Please wait... '
+		text = 'Please wait... '
 		img = "<img src='/assets/gs-loader.gif' alt='loading..'>"
 		loadingStatus += text
 		loadingStatus += img


Restore project-finished banner test

Restore project-finished banner test

--- app/pages/project/finished-banner.cjsx
+++ app/pages/project/finished-banner.cjsx
@@ -60,7 +60,7 @@
     dismissals = JSON.parse(localStorage.getItem 'finished-project-dismissals') ? {}
     recentlyDismissed = Date.now() - dismissals[@props.project.id] < @props.dismissFor

-    if false and (recentlyDismissed or not @state.projectIsComplete)
+    if recentlyDismissed or not @state.projectIsComplete
       null
     else
       <div className="successful project-announcement-banner">


Don't use query params in filtering for now

--- assets/scripts/app/controllers/dashboard/repositories.coffee
+++ assets/scripts/app/controllers/dashboard/repositories.coffee
@@ -1,5 +1,5 @@
 Controller = Ember.Controller.extend
-  queryParams: ['filter']
+  #queryParams: ['filter']
   filter: null

   filteredRepositories: (->


Default edgeStyles

Default edgeStyles

--- app/scripts/alchemy/defaultConf.coffee
+++ app/scripts/alchemy/defaultConf.coffee
@@ -78,6 +78,7 @@
             "captionColor": -> "#FFFFFF"
             "captionBackground": -> null
             "captionSize": 12
+
             "selected":
                 "color" : -> "#FFFFFF"
                 "borderColor": -> "#349FE3"
@@ -101,9 +102,16 @@
         "all":
             "width": -> 4
             "color": -> "#FFFFFF"
-            "opacity": -> 1
+            "opacity": -> 0.6
             "directed": -> true
             "curved": -> true
+
+            "selected":
+                "opacity": -> 0.8
+            "highlighted":
+                "opacity": -> 1
+            "hidden":
+                "opacity": 0
     edgeTypes: null
     curvedEdges: false
     edgeWidth: -> 4


client: ide: added user names to collab avatar menus

--- client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
+++ client/ide/lib/views/statusbar/idestatusbaravatarview.coffee
@@ -5,6 +5,7 @@
 IDEChatHeadWatchItemView    = require './idechatheadwatchitemview'
 IDEChatHeadReadOnlyItemView = require './idechatheadreadonlyitemview'
 IDELayoutManager            = require '../../workspace/idelayoutmanager'
+getFullnameFromAccount      = require 'app/util/getFullnameFromAccount'

 module.exports = class IDEStatusBarAvatarView extends AvatarView
@@ -51,6 +52,12 @@
     { frontApp }   = appManager
     { rtm }        = frontApp
     menuItems      = {}
+
+    do =>
+      disabled = 'disabled'
+      menuItems[title = getFullnameFromAccount @getData()] = { title, disabled }
+
+    menuItems.separator = type: 'separator'

     appManager.tell 'IDE', 'getCollaborationData', (data) =>


Activity: scroll issues part 1

Activity: scroll issues part 1

--- client/Social/Activity/views/privatemessage/pane.coffee
+++ client/Social/Activity/views/privatemessage/pane.coffee
@@ -21,11 +21,13 @@
     @createParticipantsView()
     @createAddParticipantForm()

-
-    # unfortunately until we make the listview
-    # more performant we need such hacks - SY
-    @on 'ListPopulated', ->
-      document.body.scrollTop = document.body.scrollHeight
+    @on 'ListPopulated', =>
+      KD.utils.defer @bound 'scrollDown'
+      KD.utils.wait 300, =>
+        @unsetClass 'translucent'
+        @input.input.setPlaceholder ''
+        @scrollDown()
+

     @listController.getListView().on 'ItemWasAdded', @bound 'messageAdded'
     @listController.getListView().on 'ItemWasRemoved', @bound 'messageRemoved'
@@ -34,26 +36,20 @@
     KD.singleton('windowController').on 'ScrollHappened', @bound 'handleScroll'

-  handleScroll: do ->
-
-    previous = 0
-
-    KD.utils.throttle ->
-      current = document.body.scrollTop
-      @listPreviousReplies()  if current < 20 and current < previous
-      previous = current
-    , 200
+
+

   setScrollTops: ->

-    @lastScrollTops.body   = document.body.scrollTop or document.body.scrollHeight
+    {body} = document
+    @lastScrollTops.body = body.scrollTop or body.scrollHeight

   applyScrollTops: ->

-    KD.utils.defer =>
-      document.body.scrollTop = @lastScrollTops.body
+    {body} = document
+    body.scrollTop = @lastScrollTops.body

   replaceFakeItemView: (message) ->
@@ -230,8 +226,8 @@
     # congestion. This function is overrides super function to render
     # all conversation messages to be displayed at the same time
     @appendMessage item
-    if i is total - 1
-      KD.utils.wait 50, => @emit 'ListPopulated'
+    @emit 'ListPopulated'  if i is total - 1
+

   populate: ->


Ticker: Add forgotten singleton name as `groupsController`

--- client/Social/Activity/ContentDisplays/ActivityTicker.coffee
+++ client/Social/Activity/ContentDisplays/ActivityTicker.coffee
@@ -33,7 +33,7 @@
     {constructorName, id} = member
     KD.remote.cacheable constructorName, id, (err, account)=>
       return console.error "account is not found", err if err or not account
-      source = KD.getSingleton().getCurrentGroup()
+      source = KD.getSingleton("groupsController").getCurrentGroup()
       @listController.addItem {as: "member", target: account, source  }, 0

   addFollow: (data)->


hackathon: added APITools as new prize

hackathon: added APITools as new prize

--- client/landing/site.hackathon/coffee/home/AppViewFull.coffee
+++ client/landing/site.hackathon/coffee/home/AppViewFull.coffee
@@ -483,6 +483,11 @@

Sprintly will offer an additional $1,000 to the team that 1) has a Sprintly account, 2) uses their API.

+

+

+
3Scale APITools will offer an additional $1,500 to the team that makes the best usage of any API on thier site and also uses APItools in the creation of thier project.

+

+


teams: join: /join route doesnt need a data flow

--- client/landing/site.landing/coffee/team/AppController.coffee
+++ client/landing/site.landing/coffee/team/AppController.coffee
@@ -14,12 +14,12 @@

 CREATION_FLOW = [ 'signup', 'domain', 'email-domains', 'invite'
                   'username', 'stacks', 'congrats' ]
-JOIN_FLOW     = [ 'welcome', 'join' ]
+# JOIN_FLOW     = [ 'welcome', 'join' ]

 getFlow = (step) ->

-  return JOIN_FLOW     if JOIN_FLOW.indexOf(step) > -1
+  # return JOIN_FLOW     if JOIN_FLOW.indexOf(step) > -1
   return CREATION_FLOW if CREATION_FLOW.indexOf(step) > -1
   return no


deselecting for selectionlist

deselecting for selectionlist

Former-commit-id: 681d33c719c68a1eb54c588772723566efde3e64 [formerly 87a3ed74db7496fb62b979e14eb748b72c45e34d]
Former-commit-id: 892307d13ee0d8fd6e16d4d343e9ec6ecf124ef4

--- js/msa/selection/selectionlist.coffee
+++ js/msa/selection/selectionlist.coffee
@@ -9,7 +9,9 @@
     addSelection: (sel) ->
       eId = sel.getId()
       if @_sels[eId]?
-        console.log "duplicate selection"
+        # duplicate selection, remove from selection
+        sel.deselect()
+        delete @_sels[eId]
       else
         @_sels[eId] = sel
         sel.select()


Tests: Fixed conflict.

Tests: Fixed conflict.

--- tests/src/activity/activity.coffee
+++ tests/src/activity/activity.coffee
@@ -72,18 +72,18 @@
       .end()

-  # likeComment: (browser) ->
-
-  #   helpers.postComment(browser)
-
-  #   comment         = helpers.getFakeText()
-  #   commentSelector = activitySelector + ' .comment-container .kdlistitemview-comment:first-child'
-
-  #   browser
-  #     .waitForElementVisible    commentSelector, 3000
-  #     .click                    commentSelector + ' [testpath=activity-like-link]'
-  #     .waitForElementVisible    commentSelector + ' .liked:not(.count)', 10000 # Assertion
-  #     .end()
+  likeComment: (browser) ->
+
+    helpers.postComment(browser)
+
+    comment         = helpers.getFakeText()
+    commentSelector = activitySelector + ' .comment-container .kdlistitemview-comment:first-child'
+
+    browser
+      .waitForElementVisible    commentSelector, 3000
+      .click                    commentSelector + ' [testpath=activity-like-link]'
+      .waitForElementVisible    commentSelector + ' .liked:not(.count)', 10000 # Assertion
+      .end()

   editComment: (browser) ->
@@ -141,16 +141,16 @@
       .end()

-  # searchActivity: (browser) ->
-
-  #   post     = helpers.postActivity(browser)
-  #   selector = '[testpath=activity-list] [testpath=ActivityListItemView]:first-child'
-
-  #   browser
-  #     .setValue                 '.kdtabhandlecontainer .search-input', post + '\n'
-  #     .pause                    5000
-  #     .assert.containsText      selector , post # Assertion
-  #     .end()
+  searchActivity: (browser) ->
+
+    post     = helpers.postActivity(browser)
+    selector = '[testpath=activity-list] [testpath=ActivityListItemView]:first-child'
+
+    browser
+      .setValue                 '.kdtabhandlecontainer .search-input', post + '\n'
+      .pause                    5000
+      .assert.containsText      selector , post # Assertion
+      .end()

   showMoreCommentLink: (browser) ->
@@ -172,14 +172,146 @@
     browser.end()

-  # topicFollow: (browser) ->
-
-  #   hashtag = helpers.sendHashtagActivity(browser)
-  #   selector = activitySelector + ' .has-markdown p a:first-child'
-
-  #   browser
-  #     .waitForElementVisible   selector, 5000
-  #     .click                   selector
-  #     .pause                   3000 # really required
-  #     .assert.containsText     '[testpath=channel-title]', hashtag # Assertion
-  #     .end()
+  topicFollow: (browser) ->
+
+    hashtag = helpers.sendHashtagActivity(browser)
+    selector = activitySelector + ' .has-markdown p a:first-child'
+
+    browser
+      .waitForElementVisible   selector, 5000
+      .click                   selector
+      .pause                   3000 # really required
+      .assert.containsText     '[testpath=channel-title]', hashtag # Assertion
+      .end()
+
+
+  postLongMessage: (browser) ->
+
+    helpers.beginTest(browser)
+
+    post = ''
+
+    for i in [1..6]
+
+      post += helpers.getFakeText()
+
+    helpers.doPostActivity(browser, post)
+    browser.end()
+
+
+  postLongComment: (browser) ->
+
+    helpers.beginTest(browser)
+    post = helpers.getFakeText()
+    helpers.doPostActivity(browser, post)
+    comment = ''
+
+    for i in [1..6]
+
+      comment += helpers.getFakeText()
+
+    helpers.doPostComment(browser, comment)
+    browser.end()
+
+
+  postMessageWithCode: (browser) ->
+
+    helpers.beginTest(browser)
+
+    timestamp = Date.now()
+    code      = "console.log(#{timestamp})"
+    post      = '```' + code + '```'
+    selector  = '[testpath=ActivityListItemView]:first-child .has-markdown code'
+
+    helpers.doPostActivity(browser, post, no)
+
+    browser
+      .assert.containsText selector, code # Assertion
+      .end()
+
+
+  postMessageWithImage: (browser) ->
+
+    helpers.beginTest(browser)
+
+    image = 'http://placehold.it/200x100'
+
+    browser
+      .click                  '[testpath="public-feed-link/Activity/Topic/public"]'
+      .waitForElementVisible  '[testpath=ActivityInputView]', 10000
+      .click                  '[testpath="ActivityTabHandle-/Activity/Public/Recent"]'
+      .click                  '[testpath=ActivityInputView]'
+      .setValue               '[testpath=ActivityInputView]', image
+      .click                  '[testpath=post-activity-button]'
+      .pause                  6000 # required
+
+    selector = activitySelector + ' .activity-content-wrapper .embed-image-view img'
+
+    browser
+      .waitForElementVisible selector, image # Assertion
+      .end()
+
+
+  postMessageWithLink: (browser) ->
+
+    helpers.beginTest(browser)
+
+    link = 'http://nightwatchjs.org/' # last '/' is the trick!
+    linkSelector = activitySelector + ' .activity-content-wrapper article a'
+
+    helpers.doPostActivity(browser, link)
+
+    browser.getAttribute linkSelector, 'href', (result) ->
+      href = result.value
+      assert.equal(link, href)
+
+    browser.end()
+
+
+  postMessageAndSeeIfItsPostedOnlyOnce: (browser) ->
+
+    post = helpers.getFakeText()
+
+    helpers.postActivity(browser)
+
+    secondPostSelector = '[testpath=activity-list] section:nth-of-type(1) [testpath=ActivityListItemView]:nth-of-type(2) article'
+    secondPost = browser.getText secondPostSelector
+
+    assert.notEqual(post, secondPost)
+
+    browser.end()
+
+
+  postCommentWithCode: (browser) ->
+
+    helpers.beginTest(browser)
+
+    post      = helpers.getFakeText()
+    timestamp = Date.now()
+    code      = "console.log(#{timestamp})"
+    comment   = '```' + code + '```'
+    selector  = '[testpath=ActivityListItemView]:first-child .comment-contents .has-markdown code'
+
+    helpers.doPostActivity(browser, post)
+    helpers.doPostComment(browser, comment, no)
+
+    browser
+      .pause 2000
+      .assert.containsText selector, code # Assertion
+      .end()
+
+
+  postCommentWithImage: (browser) ->
+
+    helpers.beginTest(browser)
+
+    post     = helpers.getFakeText()
+    image    = 'http://placehold.it/200x100'
+    selector = activitySelector + ' .comment-contents .comment-body-container .has-markdown a'
+
+    helpers.doPostActivity(browser, post)
+    helpers.doPostComment(browser, image)
+
+    browser
+      .assert.containsText selector, image # Assertion
+      .end()


Remove unused props

Remove unused props

--- app/assets/javascripts/rglossa/components/centre/search_inputs/cwb_search_inputs.jsx.coffee
+++ app/assets/javascripts/rglossa/components/centre/search_inputs/cwb_search_inputs.jsx.coffee
@@ -103,7 +103,6 @@
           return ([
             isMultilingual ? this.languageSelect(searchQuery) : null,
             <CwbMultiwordInput
-              hasPhoneticForm={corpus.has_phonetic}
               searchQuery={searchQuery}
               corpus={corpus}
               handleQueryChanged={handleQueryChanged.bind(null, index)}
@@ -126,7 +125,6 @@
           return ([
             isMultilingual ? this.languageSelect(searchQuery) : null,
             <CwbRegexInput
-              hasPhoneticForm={corpus.has_phonetic}
               searchQuery={searchQuery}
               handleQueryChanged={handleQueryChanged.bind(null, index)}
               handleSearch={handleSearch} />