removed `my feed` title

removed `my feed` title

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -338,11 +338,6 @@

     {activityController} = KD.singletons
     {slug, socialApiChannelId} = KD.getGroup()
-
-    @wrapper.addSubView new KDCustomHTMLView
-      tagName  : 'h3'
-      cssClass : 'sidebar-title'
-      partial  : 'MY FEED'

     @wrapper.addSubView @public = new CustomLinkView
       title    : 'Public Feed'


activity: cleanup threadpane logic to remove scroll business

--- client/activity/lib/mixins/threadpanelifecycle.coffee
+++ client/activity/lib/mixins/threadpanelifecycle.coffee
@@ -9,27 +9,14 @@
 ###
 module.exports = ThreadPaneLifecycleMixin = (reset) ->

-  componentDidMount = ->
-
-    reset @props, @state, =>
-      scrollTop              = @state.channelThread.getIn [ 'flags', 'scrollPosition' ]
-      scroller               = React.findDOMNode @refs.pane.refs.chatPane.refs.scrollContainer
-      scroller.scrollTop     = scrollTop  if scrollTop
-      scroller.style.opacity = 1
-
+  componentDidMount = -> reset @props, @state

   componentWillReceiveProps = (nextProps) -> reset nextProps, @state

-
   componentWillUnmount = ->

-    # terrifying drill - SY
-    scroller            = React.findDOMNode @refs.pane.refs.chatPane.refs.scrollContainer
-    { scrollTop }       = scroller
-    { channel, thread } = ActivityFlux.actions
+    { thread } = ActivityFlux.actions

-    scroller.style.opacity = 0
-    channel.setScrollPosition (@state.channelThread.getIn [ 'channel', 'id' ]), scrollTop
     thread.changeSelectedThread null

@@ -38,3 +25,5 @@
     componentWillReceiveProps
     componentWillUnmount
   }
+
+


KodingListController: Define filterStates before calling super

--- client/app/lib/kodinglist/kodinglistcontroller.coffee
+++ client/app/lib/kodinglist/kodinglistcontroller.coffee
@@ -43,16 +43,16 @@
         cssClass  : 'no-item-found'
         partial   : "<cite>#{options.noItemFoundText}</cite>"

-    super options, data
-
-    if not options.fetcherMethod and not options.model
-      return  throw new Error 'Model or fetcherMethod should be given!'
-
     @filterStates =
       skip        : 0
       busy        : no
       query       : {}
       page        : 0
+
+    super options, data
+
+    if not options.fetcherMethod and not options.model
+      return  throw new Error 'Model or fetcherMethod should be given!'

     @bindEvents()


Age in seconds

Age in seconds


Comment typo.

Comment typo.


Account Settings: Credentials: Add "User Input" option as default item to filter list.

--- client/account/lib/accountcredentiallistwrapper.coffee
+++ client/account/lib/accountcredentiallistwrapper.coffee
@@ -40,7 +40,8 @@
       title : 'Filter by provider type'

     selectOptions = [
-      { title : 'All',  value : ''  } # Firstly set default option
+      { title : 'All',        value : ''  } # Firstly set default option
+      { title : 'User Input', value : 'userInput' }
     ]

     providers = @getValidProviders()


ActivitySidebar: Minor refactor.

ActivitySidebar: Minor refactor.

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -665,13 +665,13 @@
       @sharedMachinesList.updateList data.shared.concat data.collaboration

-  invalidateWorkspaces: (machine)->
-
-    return  unless machine?
-
-    remote.api.JWorkspace.deleteByUid machine.uid, (err)=>
-
-      return kd.warn err  if err?
+  invalidateWorkspaces: (machine) ->
+
+    return  unless machine
+
+    remote.api.JWorkspace.deleteByUid machine.uid, (err) =>
+
+      return kd.warn err  if err

   removeMachineNode: (machine) ->


Make TinyMCE more tolerant about HTML elements

STUD-1565


add event suppression to the destroy all command

--- config/initializers/tools.coffee
+++ config/initializers/tools.coffee
@@ -7,8 +7,9 @@
         switch process.argv[3]
             when 'cleandb'
                 Alarm.destroyAll ->
-                    console.log 'All data cleaned'
-                    process.exit 0
+                    Event.destroyAll ->
+                        console.log 'All data cleaned'
+                        process.exit 0
                 break
             else
                 console.log 'Usage: compound database [cleandb]'


correct typo

correct typo

--- workers/social/lib/social/models/name.coffee
+++ workers/social/lib/social/models/name.coffee
@@ -21,7 +21,7 @@
   @release =(name, callback=->)->
     @remove {name}, callback

-  @validateName =(candiate)->
+  @validateName =(candidate)->
     3 < candidate.length < 26 and /^[a-z0-9][a-z0-9-]+$/.test value

   @claimNames = secure (client, callback=->)->


Do intersection testing on mouseup

Do intersection testing on mouseup

--- app/js/main.coffee
+++ app/js/main.coffee
@@ -51,20 +51,18 @@
         dragTarget.target = null
     mouseup: (e) ->
       (poly.hit = false for poly in polys)
+      # explicit iteration to compare all polys
+      for i in [0..(polys.length-1)]
+        poly = polys[i]
+        for other in polys.slice(i+1)
+          if poly.intersects other
+            poly.hit = true
+            other.hit = true
     mousemove: (e) ->
       if @dragging and dragTarget.target
         where = [@mouse.x, @mouse.y]
         dragTarget.target.pos = Vec.add where, dragTarget.offset
-    keydown: (e) ->
-      if e.keyCode is 32 # space

-        # explicit iteration to compare all polys
-        for i in [0..(polys.length-1)]
-          poly = polys[i]
-          for other in polys.slice(i+1)
-            if poly.intersects other
-              poly.hit = true
-              other.hit = true

 class Polygon
   hit: false
@@ -95,7 +93,6 @@

     # check axis-aligned bounding box first:
     if mouseX < minX or mouseY < minY or mouseX > maxX or mouseY > maxY
-      @hit = false
       return false

     # then do ray cast (poly/line intersection)
@@ -104,11 +101,7 @@
     for segment in Utils.pairs points
       count += 1 if Utils.linesIntersect testLine, segment

-    if count % 2 is 1 # odd number of line crossings is inside the polygon
-      @hit = true
-    else
-      @hit = false
-    @hit
+    count % 2 is 1 # odd number of line crossings is inside the polygon

   intersects: (other) ->
     for axis in @normalAxes().concat other.normalAxes()


revert fix since sinan fixed it in the framework

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -502,7 +502,7 @@
                   color           : "#444444"
                   diameter        : 12
                 callback          : =>
-                  unless newAppModal.modalTabs.forms.form.inputs.name.validate().valid
+                  unless newAppModal.modalTabs.forms.form.inputs.name.validate()
                     newAppModal.modalTabs.forms.form.buttons.Create.hideLoader()
                     return
                   name        = newAppModal.modalTabs.forms.form.inputs.name.getValue()


Disable pricing check

Disable pricing check


Add dismissable option

Add dismissable option


VM Settings: Snapshot: Fix activated state problem of "Create your VM" button

--- client/app/lib/providers/computeplansmodalpaid.coffee
+++ client/app/lib/providers/computeplansmodalpaid.coffee
@@ -77,7 +77,6 @@
       name          : 'snapshots'
       selectOptions : [ title: 'None', value: "" ]
       callback      : (snapshotId) =>
-        sliderValue = @storageSlider.handles.first.value

         # If the selected snapshot has a value (not empty), disable the
         # region selector, and set it's value to the snapshot region.
@@ -89,13 +88,13 @@
           @regionSelector.makeDisabled()
         else
           @regionSelector.makeEnabled()
+          @createVMButton.enable()

         # Update the usage text with the value of the slider
         #
         # Note that @getValues() just returns the value *option* of the
         # handles, so we're getting the value directly.
-        @updateSnapshotUsageText  sliderValue
-        @updateCreateVMBtnEnabled sliderValue
+        @updateSnapshotUsageText @storageSlider.handles.first.value

     content.addSubView storageContainer = new KDView
       cssClass : "storage-container"
@@ -131,7 +130,8 @@
     @updateStorageUsageText 5, usage, limits
     @updateSnapshotUsageText 5
     @updateCreateVMBtnEnabled 5
-    @storageSlider.on "ValueIsChanging", (val)=>
+
+    @storageSlider.on 'ValueIsChanging', (val) =>
       @updateStorageUsageText val, usage, limits
       @updateSnapshotUsageText val
       @updateCreateVMBtnEnabled val


get working dir name in windows properly

--- prompts.coffee
+++ prompts.coffee
@@ -1,5 +1,5 @@
 __homeDir = process.env.HOME or process.env.HOMEPATH or process.env.USERPROFILE
-__workingDirName = process.cwd().split("/").pop()
+__workingDirName = process.cwd().split("/").pop().split("\\").pop()
 __userName = __homeDir and __homeDir.split("/").pop() or "root"
 getUser = (->
   user = undefined


--- src/components/student-dashboard/progress-guide.cjsx
+++ src/components/student-dashboard/progress-guide.cjsx
@@ -10,7 +10,7 @@
 LearningGuideSection = require '../learning-guide/section'
 LearningGuideColorKey = require '../learning-guide/color-key'
 PracticeButton = require '../learning-guide/practice-button'
-WeakerSections = require '../learning-guide/weaker-sections'
+Section = require '../learning-guide/section'

 # Number of sections to display
 NUM_SECTIONS = 4
@@ -31,6 +31,9 @@
   render: ->
     courseId = @props.courseId
     guide = LearningGuide.Student.store.get(courseId)
+    sections = LearningGuide.Helpers.recentSections(
+      LearningGuide.Student.store.getAllSections(courseId)
+    )

Performance Forecast

@@ -56,10 +59,11 @@
     courseId: React.PropTypes.string.isRequired
     sampleSizeThreshold: React.PropTypes.number.isRequired

+  mixins: [ChapterSectionMixin]
   viewGuide: ->
     @context.router.transitionTo('viewGuide', {courseId: @props.courseId})

-  renderEmpty: ->
+  renderEmpty: (sections) ->

Performance Forecast

@@ -70,10 +74,16 @@

             This area will fill in with topics as you complete your assignments

+

+
{sections.length} sections were returned by the performance forecast

+          { for section in sections
+
{@sectionFormat(section.chapter_section)} section.title
 }
+

   render: ->
+<<<<<<< HEAD
     return @renderEmpty() unless LearningGuide.Helpers.canPractice({
       @props, sections:LearningGuide.Student.store.getAllSections(@props.courseId)
     })
@@ -81,14 +91,29 @@
     sections = LearningGuide.Helpers.weakestSections(
       LearningGuide.Student.store.getAllSections(@props.courseId), @props.sampleSizeThreshold
     )
+=======
+    sections = LearningGuide.Student.store.getAllSections(@props.courseId)
+    recent = LearningGuide.Helpers.recentSections(sections)
+    return @renderEmpty(sections) if _.isEmpty(recent)
+
+    if LearningGuide.Helpers.canPractice({sections})
+      practiceButton = <PracticeButton title='Practice my weakest topics'
+            courseId={@props.courseId} sections={sections} />
+>>>>>>> Show recent sections, regardless of strength

+<<<<<<< HEAD
         <ProgressGuide {...@props} />

         <PracticeButton title='Practice my weakest topics'
           {...@props} sections={sections} />
+=======
+        <ProgressGuide sections={recent} courseId={@props.courseId} />
+
+        {practiceButton}
+>>>>>>> Show recent sections, regardless of strength

         <BS.Button
           onClick={@viewGuide}


Section save typo

Section save typo


use guard instead of if

use guard instead of if

--- core/app/backbone/collections/search_collection.coffee
+++ core/app/backbone/collections/search_collection.coffee
@@ -25,9 +25,10 @@
   encodedQuery: -> encodeURIComponent @query

   addNewItem: (compare_field) ->
-    if @shouldShowNewItem(compare_field)
-      @add @getNewItem()
-      @trigger 'sync'
+    return unless @shouldShowNewItem(compare_field)
+
+    @add @getNewItem()
+    @trigger 'sync'

   shouldShowNewItem: (compare_field) ->
     @query != '' and not (@query.toLowerCase() in @newItemFields(compare_field))


Set up mathjax_preprocessor

Set up mathjax_preprocessor

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -6,6 +6,7 @@
     @element_id = @el.attr('id')
     @url = @el.data('url')
     @render()
+    @mathjax_preprocessor = false

   $: (selector) ->
     $(selector, @el)
@@ -300,12 +301,12 @@

     if jax = MathJax.Hub.getAllJax(target)[0]
       eqn = $(element).val()
-      if window.mathjax_preprocessor
-        eqn = window.mathjax_preprocessor(eqn)
+      if @mathjax_preprocessor
+        eqn = @mathjax_preprocessor(eqn)

       MathJax.Hub.Queue ['Text', jax, eqn],
         [@updateMathML, jax, element]
-
+
   updateMathML: (jax, element) =>
     try
       $("##{element.id}_dynamath").val(jax.root.toMathML '')
@@ -323,7 +324,7 @@
   inputtypeSetupMethods:

     textinputdynamath: (element) =>
-      @mathjax_preprocessor = window.mathjax_preprocessor
+      @mathjax_preprocessor = window.mathjax_preprocessor

     javascriptinput: (element) =>


Only refresh when waitfor is an attribute.

  Silly workaround for the fact that tests set the answers, but not the inputs,
  so refreshAnswers changes the test result even though it shouldn't.

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -136,7 +136,7 @@
   # invalid. To do so, the callee must throw an exception named "Waitfor
   # Exception". This and any other errors or exceptions that arise from the
   # callee are rethrown and abort the submission.
-  #
+  #
   # In order to use this feature, add a 'data-waitfor' attribute to the input,
   # and specify the function to be called by the check button before sending
   # off @answers
@@ -145,13 +145,13 @@
       if ($(inp).is("input[waitfor]"))
         try
           $(inp).data("waitfor")()
+          @refreshAnswers()
         catch e
           if e.name == "Waitfor Exception"
             alert e.message
           else
             alert "Could not grade your answer. The submission was aborted."
           throw e
-    @refreshAnswers()

   ###
@@ -163,7 +163,6 @@
   ###
   check_fd: =>
     Logger.log 'problem_check', @answers
-

     # If there are no file inputs in the problem, we can fall back on @check
     if $('input:file').length == 0


DEMO: add demo for unbordered navigational buttonbar-group

--- demo/src/demos/ButtonDemo.coffee
+++ demo/src/demos/ButtonDemo.coffee
@@ -833,6 +833,19 @@
 				new CUI.Button(icon: "fa-angle-right", group: "navi")
 			]
 		)
+
+		@__addButtonbar("navigation toolbar with no border and number input",
+			class: "cui-demo-buttonbar-navigation"
+			buttons: [
+				new CUI.Button(icon: "fa-angle-left", group: "navi")
+				new CUI.Input(
+					class: "cui-number-input"
+					data: value: ""
+					group: "navi"
+				)
+				new CUI.Button(icon: "fa-angle-right", group: "navi")
+			]
+		)

 		@__addButtonbar("navigation toolbar with border",
 			class: "cui-demo-buttonbar-navigation-border"


Fix bug: remove should be removeChild

Fix bug: remove should be removeChild

--- core/app/backbone/views/popovers/react_popover.coffee
+++ core/app/backbone/views/popovers/react_popover.coffee
@@ -55,6 +55,6 @@
     @_tether.destroy()
     React.unmountComponentAtNode @_tooltipElement
     if @_tooltipElement.parentNode
-      @_tooltipElement.parentNode.remove(@_tooltipElement)
+      @_tooltipElement.parentNode.removeChild(@_tooltipElement)

   render: -> _span()


Render: Update (disabled) error message.

--- engine/render.coffee
+++ engine/render.coffee
@@ -538,8 +538,8 @@
             #         '1282': 'INVALID_OPERATION',
             #         '1205': 'OUT_OF_MEMORY'
             #     }
-            #     console.log ('GL Error ' + errcodes[error] + ' when drawing ' + mesh.name +
-            #            ' (' + mesh.mesh_name + ') with ' + mesh.material_names[submesh_idx])
+            #     console.error ('GL Error ' + errcodes[error] + ' when drawing ' + mesh.name +
+            #             ' (' + mesh.mesh_name + ') with ' + mat.name)

             # @meshes_drawn += 1
             # @triangles_drawn += num_indices * 0.33333333333333333


change connection-close response type

change connection-close response type

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -485,6 +485,7 @@
             xhr.responseType = "arraybuffer"

             xhr.send()
+
             @_url = null
             @_connId = null
             super()


add up some signalling, just in case

add up some signalling, just in case


Fix issue with incorrect bug links when multiple bug numbers given The "bug" variable was not closed over in the for loop. Fix is to move it out into a separate function.

--- scripts/bugzilla.coffee
+++ scripts/bugzilla.coffee
@@ -22,6 +22,28 @@
   makePath = (bug) ->
     '/bugzilla/show_bug.cgi?id=' + bug

+  fetchBug = (msg, bug) ->
+    params =
+      id: bug
+
+    msg.http(domain)
+      .path(makePath(bug))
+      .header('Content-Type', 'application/x-www-form-urlencoded')
+      .post(QS.stringify(params)) (err, res, body) ->
+        if err
+          msg.send 'Failed to query bug status. ' + err
+          return
+
+        if res.statusCode != 200
+          msg.send 'Failed to query bug status. Got status ' + res.statusCode
+          return
+
+        $ = Cheerio.load body
+        title = $('title').text()
+
+        if title != 'Invalid Bug ID'
+          msg.send "#{title} #{domain}#{makePath(bug)}"
+
   robot.hear /bug #?(\d+)/ig, (msg) ->
     notUniq = {}
     for b in msg.match
@@ -30,23 +52,4 @@
         continue
       notUniq[bug] = true

-      params =
-        id: bug
-
-      msg.http(domain)
-        .path(makePath(bug))
-        .header('Content-Type', 'application/x-www-form-urlencoded')
-        .post(QS.stringify(params)) (err, res, body) ->
-          if err
-            msg.send 'Failed to query bug status. ' + err
-            return
-
-          if res.statusCode != 200
-            msg.send 'Failed to query bug status. Got status ' + res.statusCode
-            return
-
-          $ = Cheerio.load body
-          title = $('title').text()
-
-          if title != 'Invalid Bug ID'
-            msg.send "#{title} #{domain}#{makePath(bug)}"
+      fetchBug msg, bug


Add .preview class for img attachments, since we display a preview

--- src/trix/views/image_attachment_view.coffee
+++ src/trix/views/image_attachment_view.coffee
@@ -24,7 +24,7 @@
     [image]

   getClassName: ->
-    [super, "image"].join(" ")
+    [super, "image preview"].join(" ")

   refresh: (image) ->
     image ?= @findElement()?.querySelector("img")


disable specs to test travis phantomjs

disable specs to test travis phantomjs

It crashes on something in there, but works locally

--- exercises/test/all-tests.coffee
+++ exercises/test/all-tests.coffee
@@ -1,2 +1,6 @@
-testsContext = require.context("./", true, /\.spec\.(cjsx|coffee)$/)
-testsContext.keys().forEach(testsContext)
+require './components/app.spec'
+require './components/exercise/controls.spec'
+# require './components/exercise.spec'
+
+# testsContext = require.context("./", true, /\.spec\.(cjsx|coffee)$/)
+# testsContext.keys().forEach(testsContext)


fix input

fix input

--- mechanics/001-binary-stars/csmain.coffee
+++ mechanics/001-binary-stars/csmain.coffee
@@ -21,7 +21,7 @@
         ps['m1'] = 1 if not ps['m1']
         ps['m2'] = 1 if not ps['m2']
         ps['a'] = 4 if not ps['a']
-        ps['e'] = 0 if not ps['e']
+        ps['theta'] = 0 if not ps['theta']

         m1 = parseFloat(ps['m1'])
         m2 = parseFloat(ps['m2'])


added on method to block

added on method to block

--- app/assets/javascripts/block.coffee
+++ app/assets/javascripts/block.coffee
@@ -20,8 +20,10 @@
 		$elem.e_name = e_name
 		$elem.b_name = @name
 		$elem
-
-
+
+	on: () ->
+		@$node.on.apply(@$node, arguments)
+
 	_addEvents: (element)->
 		for event_name, handler of @events
 			p = event_name.split(' ')
@@ -29,8 +31,8 @@
 				handler = decl.methods[handler]
 			@$node.on p[0], p[1], handler.bind(@)

-	_trigger: (event) ->
-		@$node.trigger event
+	_trigger: (event, data) ->
+		@$node.trigger event, data

 	_elementClass: (e_name) ->
 		$$.elementClass(@name, e_name)


Draw the initial game state - fill in the counters that have already been played

--- app/assets/javascripts/c4.js.coffee
+++ app/assets/javascripts/c4.js.coffee
@@ -7,11 +7,11 @@
     @context = @canvas[0].getContext '2d'
     @overlayContext = @overlayCanvas[0].getContext '2d'
     @reset()
-    this.initialiseGameState() if @options.grid && @options.moves
     @overlayCanvas.click $.proxy(this.clicked, this)
     @overlayCanvas.mousemove $.proxy(this.mouseMove, this)
     @overlayCanvas.mouseleave $.proxy(this.mouseLeave, this)
     @drawFrame()
+    this.initialiseGameState() if @options.grid && @options.moves

   reset: ->
     @nextColour = 0
@@ -23,6 +23,7 @@
   initialiseGameState: ->
     @grid = @options.grid
     @moves = @options.moves
+    @drawInitialMoves()

   activate: (active) ->
     @active = active
@@ -39,7 +40,7 @@
   highlight: (column) ->
     if @highlightColumn > -1 && @highlightColumn < @options.gridWidth
       @context.globalCompositeOperation = 'copy'
-      @context.fillStyle = 'rgba(0,0,0,0)'
+      @context.fillStyle = 'rgba(0, 0, 0, 0)'
       @clearEllipse(((@highlightColumn + 1) * @options.cellSize) - (@options.cellSize/2),
         ((@options.gridHeight - @grid[@highlightColumn].length) * @options.cellSize) - (@options.cellSize/2),
         (@options.cellSize * 4/10) + 1)
@@ -71,6 +72,17 @@
         @overlayContext.lineTo((x - 1) * @options.cellSize, (y - 1) * @options.cellSize)
         @overlayContext.closePath()
         @overlayContext.fill()
+
+  drawInitialMoves: ->
+    column = 0
+    for counters in @grid
+      row = 0
+      for counter in counters
+        @context.fillStyle = counter
+        this.fillEllipse(((column + 1) * @options.cellSize) - (@options.cellSize/2),
+          ((7 - row) * @options.cellSize) - (@options.cellSize/2), (@options.cellSize * 4/10))
+        row++
+      column++

   fillEllipse: (x, y, radius) ->
     @context.beginPath()


remove event listener on media statistics before turbolinks caching

--- app/assets/javascripts/media.coffee
+++ app/assets/javascripts/media.coffee
@@ -427,4 +427,5 @@
   $(document).off 'click', '#editMediumTags'
   $(document).off 'click', '#cancelMediumTags'
   $(document).off 'click', '.triggerDownload'
+  $(document).off 'click', '#showMediaStatistics'
   return


Add alternate snippet to insert docblocks in JS

--- user/snippets.cson
+++ user/snippets.cson
@@ -54,6 +54,9 @@
 	Comment:
 		prefix: "/"
 		body: "/** $1 */"
+	DocBlock:
+		prefix: "/*"
+		body: "/**\n * $1\n */"
 	Babel:
 		prefix: "babel"
 		body: '"use babel";\n"use strict";\n\n'


use setViewValue to trigger wiki watchers

--- app/coffee/modules/wiki/main.coffee
+++ app/coffee/modules/wiki/main.coffee
@@ -159,9 +159,6 @@
             return if not wikiPage
             render(wikiPage)

-        $scope.$on "wiki:edit", (event, wikiPage) ->
-            render(wikiPage)
-
         $scope.$on "$destroy", ->
             $el.off()

@@ -215,8 +212,7 @@
                 if not wiki.id?
                     $analytics.trackEvent("wikipage", "create", "create wiki page", 1)

-                $model.$modelValue = wikiPage
-                $scope.$broadcast("wiki:edit", wikiPage)
+                $model.$setViewValue wikiPage

                 $confirm.notify("success")
                 switchToReadMode()


Add header for disable pagination by default.

--- app/coffee/services/resource.coffee
+++ app/coffee/services/resource.coffee
@@ -15,7 +15,10 @@
 ResourceProvider = ($http, $q, $gmStorage, $gmUrls, $model, config) ->
     service = {}
     headers = ->
-        return {"X-SESSION-TOKEN": $gmStorage.get('token')}
+        return {
+            "X-SESSION-TOKEN": $gmStorage.get('token')
+            "X-DISABLE-PAGINATION": "true"
+        }

     queryMany = (name, params, options) ->
         defauts = {method: "GET", headers:  headers()}


`util/search`: support for `--products all`


[fix] Don't assume contacts have an email address

Test plan:

Removed the email address from mg's contact and tried to autocomplete it.  No contact appeared, and no errors were generated.
```
_.find(angular.element("input").injector().get("$contacts").list(), function(e){return e.email=="mg@inboxapp.com"}).email = undefined
```

--- public/js/baobab.controller.compose.coffee
+++ public/js/baobab.controller.compose.coffee
@@ -30,8 +30,8 @@
         if _.isEmpty(search)
           return []
         $contacts.list().filter (contact) ->
-          contact.email.toLowerCase().indexOf(search.toLowerCase()) == 0 ||
-          !_.isEmpty(contact.name) && contact.name.toLowerCase().indexOf(search.toLowerCase()) == 0
+          !_.isEmpty(contact.email) && (contact.email.toLowerCase().indexOf(search.toLowerCase()) == 0 ||
+          !_.isEmpty(contact.name) && contact.name.toLowerCase().indexOf(search.toLowerCase()) == 0)
       parse: (text) ->
         candidates = $contacts.list().filter (contact) ->
           contact.email.toLowerCase() == text.toLowerCase()


small refactoring

small refactoring

--- frontend/app/scripts/app.coffee
+++ frontend/app/scripts/app.coffee
@@ -1,5 +1,6 @@
 'use strict'
 App = angular.module('app', ['ionic', 'ngResource']).run () ->
+

   App.config [


Adds subject link to tag search

Adds subject link to tag search

--- app/talk/tags.cjsx
+++ app/talk/tags.cjsx
@@ -49,12 +49,22 @@
       {if @state.tags?.length > 0

-            Your search returned {resourceCount @state.meta.count, 'tags'}.
+            Your search returned {resourceCount @state.meta.count, 'subjects'}.

             {for tag in @state.tags

+

+                  <Link to="project-talk-subject"
+                    {...@props}
+                    params={
+                      owner: @props.params.owner
+                      name: @props.params.name
+                      id: tag.subject.id}>
+                    Subject {tag.subject.id}
+                  </Link>
+

                 <SubjectViewer subject={tag.subject} user={@props.user} project={@props.project}/>

                   {for subjectTag in tag.subjectTags


show hit rate to 2dp only

show hit rate to 2dp only

--- app/coffee/ASpell.coffee
+++ app/coffee/ASpell.coffee
@@ -38,7 +38,7 @@
 			for k, v of addToCache
 				cache.set(k, v)

-			logger.log hits: hits, total: words.length, hitrate: hits/words.length, "cache hit rate"
+			logger.log hits: hits, total: words.length, hitrate: (hits/words.length).toFixed(2), "cache hit rate"
 			callback null, results

 	getSuggestions: (language, output) ->


Add todo

Add todo

--- framer/LayerDraggable.coffee
+++ framer/LayerDraggable.coffee
@@ -23,6 +23,16 @@
 Events.DidEndAnimation       = "didendanimation"
 Events.DidStartLockDirection = "didstartlockdirection"

+"""
+TODO:
+
+- Check if all events are thrown correctly
+- Deal with nested draggables
+	- Event propagation (click vs move)
+	- Multiple scroll moves
+	- Multiple lock directions
+
+"""

 class exports.LayerDraggable extends BaseClass

@@ -31,8 +41,6 @@

 	@define "horizontal", @simpleProperty "horizontal", true, true
 	@define "vertical", @simpleProperty "vertical", true, true
-
-	# @define "lockDirection", @simpleProperty "lockDirection", false, true

 	@define "constraints",
 		get: -> @_constraints


Changing helpUrls to pasteUrls in triggers

--- scripts/triggers.coffee
+++ scripts/triggers.coffee
@@ -15,8 +15,8 @@
   '!dataja'       : "Don't ask to ask, Just ask!"
   '!tryit'        : "Try it and see™. You learn much more by experimentation than by asking without having even tried."
   '!explain'      : "Explain your business goals, such as what website feature you're making, instead of the technical problem you're facing. We can give you much better answers this way."
-  '!help'         : "Before asking for help, use #{helpUrl} to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
-  '!helpme'       : "Before asking for help, use #{helpUrl} to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
+  '!help'         : "Before asking for help, use #{pasteUrl} to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
+  '!helpme'       : "Before asking for help, use #{pasteUrl} to provide us the information we need to help you - Laravel version, expected/actual behavior, and all relevant code. Paste the link here when done. Thanks!"
   '!paste'        : "You may paste your code at #{pasteUrl}.  Please avoid using pastebin.com"
   '!dontspam'     : "Please don't paste your code here. Instead, use #{pasteUrl}"
   '!debug'        : "Open your <project>/app/config/app.php and set the debug key to true"


refs #311 逆順表示のスイッチをリファクタリング

refs #311 逆順表示のスイッチをリファクタリング

--- app/assets/javascripts/components/sheets/version_checkbox.js.cjsx
+++ app/assets/javascripts/components/sheets/version_checkbox.js.cjsx
@@ -28,12 +28,9 @@
   onChangeReverse: =>
     params = getQueryParams location.search
     url = location.origin + location.pathname
-    if @state.reverse is true
-      delete params.reverse_sheet
-      history.pushState '', '', mergeQueryParams url, params
-    else
-      params.reverse_sheet = true
-      history.pushState '', '', mergeQueryParams url, params
+    # parameterの付与/削除
+    if @state.reverse is true then delete params.reverse_sheet else params.reverse_sheet = true
+    history.replaceState '', '', mergeQueryParams url, params
     EnvironmentActionCreators.changeReverse !@state.reverse

   renderVersionCheckbox: ->


clean up code in marking-tool

clean up code in marking-tool

--- app/controllers/marking-tool.coffee
+++ app/controllers/marking-tool.coffee
@@ -8,12 +8,14 @@

   visible: true
   markType: null
+  size: if !!~navigator.userAgent.indexOf 'iO' then 40 else 20
+  color: null

-  lines = []
+  @hr1: null
+  @hr2: null
+  @vr1: null
+  @vr2: null
   circle: null
-
-  size: if !!~navigator.userAgent.indexOf 'iO' then 40 else 20
-  color: 'rgb(200, 0, 0)'

   cursors:
     circle: 'move'
@@ -41,19 +43,14 @@

   render: ->
     return if not @visible
+    svgLines = [@hr1, @hr2, @vr1, @vr2]

-    # update colors
-    if @markType is 'asteroid'
-      @vr1.attr stroke: 'rgb(200,200,0)'
-      @vr2.attr stroke: 'rgb(200,200,0)'
-      @hr1.attr stroke: 'rgb(200,200,0)'
-      @hr2.attr stroke: 'rgb(200,200,0)'
-    else
-      @vr1.attr stroke: 'rgb(200,0,0)', transform: 'rotate(45)'
-      @vr2.attr stroke: 'rgb(200,0,0)', transform: 'rotate(45)'
-      @hr1.attr stroke: 'rgb(200,0,0)', transform: 'rotate(45)'
-      @hr2.attr stroke: 'rgb(200,0,0)', transform: 'rotate(45)'
-
+    for line in svgLines
+      if @markType is 'asteroid'
+        line.attr stroke: 'rgb(255,215,0)'
+      else
+        line.attr stroke: 'rgb(200,0,20)', transform: 'rotate(45)'
+
     # @hr.attr strokeWidth: 1 / @surface.zoomBy
     # @vr.attr strokeWidth: 1 / @surface.zoomBy
     scale = @surface.el.offsetWidth


cover different scenarios

cover different scenarios

--- test/collections/artwork_collections.coffee
+++ test/collections/artwork_collections.coffee
@@ -23,12 +23,13 @@
       @collections.first().url().should.include '/api/v1/collection/saved-artwork?user_id=' + @user.id
       @collections.first().artworks.url.should.include '/api/v1/collection/saved-artwork/artworks'

-    it 'triggers destroy:artwork when a collection artwork is destroyed', ->
-      @collections.on 'destroy:artwork', spy = sinon.spy()
-      @collections.add { id: 'saved-artwork' }
-      @collections.first().artworks.add fabricate 'artwork'
-      @collections.first().artworks.first().destroy()
-      spy.called.should.be.ok
+    it 'triggers {event}:artwork when a collection artwork is acted on', ->
+      for e in ['destroy', 'remove', 'add']
+        @collections.on e + ':artwork', spy = sinon.spy()
+        @collections.add { id: 'saved-artwork' }
+        @collections.first().artworks.add fabricate 'artwork'
+        @collections.first().artworks.first().destroy()
+        spy.called.should.be.ok

     it 'fetches artworks by recently saved', ->
       @collections.add { id: 'saved-artwork' }


Update dateCompleted and modified when completing task

--- astroid-web.coffee
+++ astroid-web.coffee
@@ -8,8 +8,12 @@

   Template.todos.events
     'click a.completeBox': (e) ->
+      now = moment()
       Tasks.update this._id,
-        $set: completed: not @completed
+        $set:
+          completed: not @completed
+          dateCompleted: now
+          modified: now

   Template.newTaskForm.events
     'submit #new-task, click #addTaskButton': (e) ->


Fix bug preventing people from removing themselves

--- public/src/queue.cjsx
+++ public/src/queue.cjsx
@@ -12,7 +12,7 @@
     onClick = =>
       @props.removeSpot @props.spot

-    if @props.ownsQueue or @props.holdsSpot @props.spot
+    if @props.ownsQueue or util.holdsSpot window.user, @props.spot
       doneButton = <DoneButton onClick={onClick} />

     waitTime = "has been waiting for #{moment(@props.spot.createdAt).fromNow(true)}"
@@ -99,10 +99,9 @@
     queueCount = util.queueCountToString @state.Spots.length

     ownsQueue = util.isInOwners window.user, @state.Owners
-    holdsSpot = (spot) => util.holdsSpot window.user.id, spot
     spots = @state.Spots.map (spot, idx, arr) =>
-      <Spot key={spot.key} spot={spot}
-        holdsSpot={holdsSpot} ownsQueue={ownsQueue} removeSpot={@removeSpot} />
+      <Spot key={spot.key} spot={spot} ownsQueue={ownsQueue}
+          removeSpot={@removeSpot} />

     if ownsQueue
       addOwnerButton = <AddOwnerButton addOwner={@addOwner} />


converted spaces to tabs in test

converted spaces to tabs in test

--- test/integration/tcpIntegrationTests.coffee
+++ test/integration/tcpIntegrationTests.coffee
@@ -67,7 +67,7 @@
 		roles: [ "test" ]
 		passwordHash: ""
 		cert: (fs.readFileSync "test/resources/client-tls/cert.pem").toString()
-
+
 	sendTCPTestMessage = (port, callback) ->
 		client = new net.Socket()
 		client.connect port, 'localhost', -> client.write testMessage
@@ -78,7 +78,7 @@
 	sendTLSTestMessage = (port, callback) ->
 		options =
 			cert: fs.readFileSync "test/resources/client-tls/cert.pem"
-			key:  fs.readFileSync "test/resources/client-tls/key.pem"
+			key:	fs.readFileSync "test/resources/client-tls/key.pem"
 			ca: [ fs.readFileSync "tls/cert.pem" ]

 		client = tls.connect port, 'localhost', options, -> client.write testMessage
@@ -102,15 +102,15 @@
 	afterEach (done) -> server.stop done

 	it "should route TCP messages", (done) ->
-    incrementTransactionCountSpy = sinon.spy stats, 'incrementTransactionCount' # check if the method was called
-    measureTransactionDurationSpy = sinon.spy stats, 'measureTransactionDuration' # check if the method was called
+		incrementTransactionCountSpy = sinon.spy stats, 'incrementTransactionCount' # check if the method was called
+		measureTransactionDurationSpy = sinon.spy stats, 'measureTransactionDuration' # check if the method was called

-    server.start null, null, null, null, 7787, null, ->
-      sendTCPTestMessage 4000, (data) ->
-        data.should.be.exactly 'OK'
-        incrementTransactionCountSpy.calledOnce.should.be.true
-        incrementTransactionCountSpy.getCall(0).args[0].authorisedChannel.should.have.property 'name', 'TCPIntegrationChannel1'
-        measureTransactionDurationSpy.calledOnce.should.be.true
+		server.start null, null, null, null, 7787, null, ->
+			sendTCPTestMessage 4000, (data) ->
+				data.should.be.exactly 'OK'
+				incrementTransactionCountSpy.calledOnce.should.be.true
+				incrementTransactionCountSpy.getCall(0).args[0].authorisedChannel.should.have.property 'name', 'TCPIntegrationChannel1'
+				measureTransactionDurationSpy.calledOnce.should.be.true
 				done()

 	it "should route TLS messages", (done) ->


Added pretty json and toolbar-icons

Added pretty json and toolbar-icons

--- atom/packages.cson
+++ atom/packages.cson
@@ -19,9 +19,11 @@
   "minimap"
   "minimap-bookmarks"
   "package-sync"
+  "pretty-json"
   "project-manager"
   "sort-lines"
   "todo-show"
   "tool-bar"
   "tool-bar-main"
+  "toolbar-iconshortcuts"
 ]


add jsonlint and scss-lint

add jsonlint and scss-lint

--- atom/packages.cson
+++ atom/packages.cson
@@ -27,6 +27,8 @@
   "linter"
   "linter-coffeescript"
   "linter-cpplint"
+  "linter-jsonlint"
+  "linter-scss-lint"
   "linter-csslint"
   "linter-doiuse"
   "linter-erb"


:train::dancers: Updated in browser at strd6.github.io/editor

--- pixie.cson
+++ pixie.cson
@@ -10,7 +10,7 @@
   "grid-gen": "distri/grid-gen:v0.2.0"
   "hotkeys": "distri/hotkeys:v0.2.0"
   "jquery-utils": "distri/jquery-utils:v0.2.0"
-  postmaster: "distri/postmaster:v0.2.0"
+  postmaster: "distri/postmaster:v0.2.3"
   runtime: "distri/runtime:v0.3.0"
   "touch-canvas": "distri/touch-canvas:v0.3.1"
   "undo": "distri/undo:v0.2.0"


remove browserfy workaround now that webpack is used

--- app/pages/project/stats/charts.cjsx
+++ app/pages/project/stats/charts.cjsx
@@ -1,10 +1,6 @@
 React = require 'react'
 ChartistGraph = require 'react-chartist'
 moment = require 'moment'
-
-# hack to make browserify and rc-slider play nice when using window shim
-document.createElement ?= (input) ->
-  style: {}
 Rcslider = require 'rc-slider'

 Progress = React.createClass


client: home: try-on-koding-imagepath-fix

--- client/home/lib/utilities/components/tryonkoding/container.coffee
+++ client/home/lib/utilities/components/tryonkoding/container.coffee
@@ -32,7 +32,7 @@
     if team
       value = """

-
+

           """


replace bare "section" word with label

replace bare "section" word with label

--- tutor/src/components/cc-dashboard/help.cjsx
+++ tutor/src/components/cc-dashboard/help.cjsx
@@ -19,6 +19,8 @@
     glprops = {lowercase:true, courseId: courseId}
     section =
       <CourseGroupingLabel lowercase {...glprops} />
+    uCaseSection =
+      <CourseGroupingLabel {...glprops} />

         Welcome to Concept Coach™
@@ -36,20 +38,20 @@
                 Click your name in the top right corner and select “Course Settings and Roster”

-                Click the blue “Add Section” link and follow the prompts to create your section.
-                If you have more than one section, repeat this step as needed.
+                Click the blue “Add {uCaseSection}” link and follow the prompts to create your {section}.
+                If you have more than one {section}, repeat this step as needed.

             Get student enrollment code and send to your students.

-                Select your first section and click the blue “Get Student Enrollment Code” link.
+                Select your first {section} and click the blue “Get Student Enrollment Code” link.

-                Copy the example message and share this message with your students in that section.
+                Copy the example message and share this message with your students in that {section}.

-                Repeat this step for each section of your course (every section has a different enrollment code).
+                Repeat this step for each {section} of your course (every {section} has a different enrollment code).

             Assign Concept Coach.


for some reason, stop listening is not available for views

--- lib/assets/javascripts/appitude.js.coffee
+++ lib/assets/javascripts/appitude.js.coffee
@@ -22,7 +22,7 @@

 unloadView = (name)->
   if view = getView(name)
-    view.stopListening();
+    view.undelegateEvents();
     delete views[name]

 autoloadViews = ()->


Move editor model references to attached callback

I think this fixes the deserialization issue, curious if an editor
needs to be attached to the dom before the model can be referenced?

--- lib/changes/commit-message-element.coffee
+++ lib/changes/commit-message-element.coffee
@@ -31,17 +31,17 @@
     # Elements
     @innerHTML    = BaseTemplate
     @messageNode  = @querySelector('atom-text-editor')
-    @messageModel = @messageNode.getModel()

     @branchNode   = @querySelector('.branch-name')
     @buttonNode   = @querySelector('.btn')

+    @stagedCount = 0
+
+  attachedCallback: ->
+    @messageModel = @messageNode.getModel()
     @messageModel.setSoftWrapped(true)
     @messageModel.setPlaceholderText(PlaceholderText)

-    @stagedCount = 0
-
-  attachedCallback: ->
     # Model events
     @disposables.add @changesView.model.gitIndex.onDidUpdateRepository(@update.bind(this))
     @disposables.add @messageModel.onDidChange(@updateCommitButton.bind(this))


move mpld3 script outside

move mpld3 script outside

--- lib/markdown-preview-enhanced-view.coffee
+++ lib/markdown-preview-enhanced-view.coffee
@@ -1150,14 +1150,29 @@
               if $g.attr('id')?.match(/fig\_/)
                 $g.html('')

+          ss = $('.output-div > script', $codeChunk)
+          if ss
+            # load d3.min.js and mpld3.min.js
+            ###
+            requires = options.require or []
+            requires.push(path.resolve(__dirname, '../dependencies/mpld3/d3.v3.min.js'))
+            requires.push(path.resolve(__dirname, '../dependencies/mpld3/mpld3.v0.3.min.js'))
+            options.require = requires
+            ###
+
+            for s in ss
+              $s = $(s)
+              c = $s.html()
+              $s.remove()
+              jsCode += (c + '\n')
+
       if options.element
         $codeChunk.append("
#{options.element}
")

-      if cmd == 'javascript'
+      if options.require # cmd == 'javascript'
         requires = options.require or []
         if typeof(requires) == 'string'
           requires = [requires]
-
         requiresStr = ""
         for requirePath in requires
           # TODO: css


Cleanup product integration after each spec

--- src/spec/integration/integration-product.spec.coffee
+++ src/spec/integration/integration-product.spec.coffee
@@ -1,12 +1,13 @@
 _ = require 'underscore'
 Q = require 'q'
+_.mixin require('sphere-node-utils')._u
 Logger = require '../../lib/logger'
 ProductSync = require '../../lib/sync/product-sync'
 Config = require('../../config').config
 product = require '../../models/product.json'

-# Increase timeout
-jasmine.getEnv().defaultTimeoutInterval = 10000
+uniqueId = (prefix) ->
+  _.uniqueId "#{prefix}#{new Date().getTime()}_"

 getProductFromStaged = (product) ->
   p = product.masterData.staged
@@ -27,7 +28,6 @@
         levelFile: 'error'
     @client = @sync._client

-    @unique = new Date().getTime()
     pt =
       name: 'myType'
       description: 'foo'
@@ -35,7 +35,7 @@
       name:
         en: 'Foo'
       slug:
-        en: "foo-#{@unique}"
+        en: uniqueId 'foo'
       productType:
         typeId: 'product-type'
       # without all these default the syncer does not work!
@@ -60,7 +60,22 @@
       @newProduct.id = product.id
       done()
     .fail (msg) -> done(msg)
-
+  , 10000 # 10sec
+
+  afterEach (done) ->
+    @client.products.perPage(0).fetch()
+    .then (payload) =>
+      Q.all _.map payload.body.results, (product) =>
+        @client.products.byId(product.id).delete(product.version)
+    .then (results) =>
+      @client.productTypes.perPage(0).fetch()
+    .then (payload) =>
+      Q.all _.map payload.body.results, (productType) =>
+        @client.productTypes.byId(productType.id).delete(productType.version)
+    .then (results) =>
+      done()
+    .fail (error) -> done(_.prettify(error))
+  , 60000 # 1min

   it 'should not update minimum product', (done) ->
     data = @sync.buildActions @newProduct, @oldProduct


Update Atom config

Update Atom config

--- etc/atom/config.cson
+++ etc/atom/config.cson
@@ -32,3 +32,4 @@
     js_jslint_happy: true
   linter:
     lintOnChange: false
+  "linter-jshint": {}


Tweak behaviour

Tweak behaviour

--- etc/atom/config.cson
+++ etc/atom/config.cson
@@ -18,3 +18,7 @@
   ]
   'projectHome': '/Users/stillers/workspace'
 'markdown-preview': {}
+'whitespace':
+  'removeTrailingWhitespace': false
+'bracket-matcher':
+  'autocompleteBrackets': false


Slightly faster height calculation

Slightly faster height calculation

--- resources/assets/coffee/sync-height.coffee
+++ resources/assets/coffee/sync-height.coffee
@@ -31,7 +31,7 @@

     for reference in @references
       id = reference.getAttribute('data-sync-height-target')
-      heights[id] = reference.getBoundingClientRect().height
+      heights[id] = reference.offsetHeight

     for target in @targets
       height = heights[target.getAttribute('data-sync-height-id')]


More documentation and refactoring for learnsets.

--- shared/learnsets.coffee
+++ shared/learnsets.coffee
@@ -10,8 +10,12 @@
   minGeneration = Math.min(unportableGenerations...)
   throw new Error("Gen. '#{generation}' must be greater than #{minGeneration}.")

-# Loop through the learnsets for this pokemon and all prevolutions.
-# This takes all available generations into count.
+# A helper method to loop through the learnsets for this pokemon and all
+# prevolutions, and then runs an iterator function (that you give) on those
+# learnsets. The iterator function takes one parameter, `learnset`, the learnset
+# for a given generation for the Pokemon's current forme.
+#
+# All generations that can be used are taken into consideration.
 loopLearnsets = (SpeciesData, FormeData, pokemon, forGeneration, iterator) ->
   minimumGeneration = getMinimumGeneration(forGeneration)
   [name, formeName] = [pokemon.name, pokemon.forme]
@@ -30,6 +34,10 @@
 # Returns an array of moves that this Pokemon can learn for a given generation.
 self.learnableMoves = (pokemon, forGeneration) ->

+# Checks the moveset of a given Pokemon for a given generation, with the given
+# species and forme data for all Pokemon.
+#
+# Returns true if the moveset is valid, false otherwise.
 self.checkMoveset = (SpeciesData, FormeData, pokemon, generation, moves) ->
   looper = loopLearnsets.bind(null, SpeciesData, FormeData, pokemon, generation)

@@ -37,25 +45,27 @@
   # through level-up, tutoring, machines, or Sketch.
   leftoverMoves = (m  for m in moves when !checkMove(looper, pokemon, m))

+  # TODO: form-change learnsets should actually be considered part of the normal
+  # moveset, but only for that forme.
+
   # If there are no leftover moves, then we're done, the moveset is valid.
   return true  if leftoverMoves.length == 0

+  # Each Pokemon has certain learnset groups. Standard ones are listed below.
   standardGroups = [ "level-up", "tutor", "machine", "egg" ]

-  # There are certain ways specific Pokemon can learn a move.
   # Check non-standard groups:
   # "form-change", "light-ball-egg", "stadium-surfing-pikachu"
   return true  if looper (learnset) ->
-    # Check to see if special moves can completely overlap these moves
-    learnsetGroups = (g  for g of learnset when g not in standardGroups)
-    for groupName in learnsetGroups
-      total = 0
-      for move in leftoverMoves
-        total += (if move of learnset[groupName] then 1 else 0)
-
-      # If one group completely overlaps the leftover moves, we're good.
+    nonstandardGroups = (g  for g of learnset when g not in standardGroups)
+    # If a non-standard group completely overlaps the leftover moves, the
+    # moveset is valid.
+    for group in nonstandardGroups
+      total = (m  for m in leftoverMoves when m of learnset[group]).length
       return true  if total == leftoverMoves.length

+  # This makes it so if the remaining moves are all egg moves, the moveset is
+  # valid. That's false, but it's permissive. Later, factor in chain-breeding.
   # TODO: Check chain-breeding.
   eggMoves = []
   looper (learnset) ->
@@ -67,6 +77,8 @@

   return false

+# Checks a single move to see if the Pokemon can learn it through level-up,
+# tutors, machines, Sketch, or pre-evolutions.
 checkMove = (looper, pokemon, move) ->
   {level} = pokemon
   valid = false


Update radio_input.cjsx (#9)

Update radio_input.cjsx (#9)

Adds the disabled attribute to <RadioInput />.

--- modules/components/forms/radio_input.cjsx
+++ modules/components/forms/radio_input.cjsx
@@ -19,6 +19,7 @@
             name={@props.name}
             checked={@state.value?.toString() is option.value.toString()}
             value={option.value}
+            disabled={option.disabled}
             type='radio'
             onChange={@handleChange} />
           {option.name}<br/>


Add warning logging when unusual things happen

--- js-library/app/js/jail_iframe/util/annotated_site_receiver.coffee
+++ js-library/app/js/jail_iframe/util/annotated_site_receiver.coffee
@@ -8,7 +8,9 @@
     window.FACTLINK_ON_CORE_LOAD?()

   openModalOverlay: ->
-    return if modalOpen
+    if modalOpen
+      console.error 'trying to open an already open modal: bug?'
+      return
     FactlinkJailRoot.$modalFrame.addClass 'factlink-control-visible'
     modalOpen = true
     FactlinkJailRoot.trigger 'modalOpened'
@@ -20,7 +22,9 @@
   closeModal_noAction: ->
     FactlinkJailRoot.$modalFrame.removeClass 'factlink-control-visible'
     setTimeout( ->
-      return unless modalOpen
+      if !modalOpen
+        console.error 'trying to close an already closed modal: bug?'
+        return
       modalOpen = false
       FactlinkJailRoot.trigger 'modalClosed'
     , 300)


general check for existence

general check for existence

--- lib/assets/javascripts/turbograft/initializers.coffee
+++ lib/assets/javascripts/turbograft/initializers.coffee
@@ -51,7 +51,7 @@
   document.addEventListener eventType, (ev) ->
     target = ev.target

-    while target != document && (typeof target != "undefined") && (target != null)
+    while target != document && target?
       if target.nodeName == "A" || target.nodeName == "BUTTON"
         isNodeDisabled = nodeIsDisabled(target)
         ev.preventDefault() if isNodeDisabled


Change timeout to 15 seconds

Change timeout to 15 seconds

--- app/config.coffee
+++ app/config.coffee
@@ -12,7 +12,7 @@
     image: "arbiter"
     networkDisabled: true
     memory: 50e6
-    timeout: 1e5
+    timeout: 15e3
     maxLength: 1e3
     volumes:
       share: "/opt/share"


Add mogo.

Add mogo.

--- assets/scripts/data/sponsors.coffee
+++ assets/scripts/data/sponsors.coffee
@@ -42,6 +42,7 @@
   { type: 'silver', link: "Pivotal Labs"}
   { type: 'silver', link: "Fiksu"}
   { type: 'silver', link: "Sauce Labs"}
+  { type: 'silver', link: "MogoNever be embarrassed by a visually broken site again."}
 ]

 @Travis.WORKERS = {


Add BusyConf as sponsor.

Add BusyConf as sponsor.

--- assets/scripts/data/sponsors.coffee
+++ assets/scripts/data/sponsors.coffee
@@ -43,6 +43,7 @@
   { id: '40', type: 'silver', link: "Fiksu"}
   { id: '41', type: 'silver', link: "Sauce Labs"}
   { id: '42', type: 'silver', link: "Mogotest: Never be embarrassed by a visually broken site again."}
+  { id: '43', type: 'silver', link: "BusyConf: Conferences and Events Made Easy"}
 ]

 @Travis.WORKERS = {


Groups: activity selector is added

Groups: activity selector is added

--- server/lib/crawler/staticpages/feed.coffee
+++ server/lib/crawler/staticpages/feed.coffee
@@ -27,13 +27,14 @@
     return callback new Error "Unknown content type.", null

   pageContent = ""
-  model.count group: "koding", (error, count)=>
+  selector = group : "koding"
+  model.count selector, (error, count)=>
     return callback error, null  if error
     return callback null, getEmptyPage contentType  if count is 0
-    model.some group: "koding", options, (err, contents)=>
+    model.some selector, options, (err, contents)=>
       return callback err, null  if err
       return callback null, getEmptyPage contentType  if count is 0
-      queue = [0..contents.length - 1].map (index)=>=>
+      queue = [0..contents.length].map (index)=>=>
         queue.pageContent or= ""

         content = contents[index]


ModalView: code styling.

ModalView: code styling.

--- src/components/modals/modalview.coffee
+++ src/components/modals/modalview.coffee
@@ -5,7 +5,7 @@

 module.exports = class KDModalView extends KDView

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

     options.overlay       ?= no            # a Boolean
     options.overlayClick  ?= yes           # a Boolean
@@ -63,11 +63,12 @@
     $(window).one "keydown.modal",(e)=>
       @cancel() if e.which is 27

-    @on "childAppended", @setPositions.bind @
+    @on "childAppended", @setPositions.bind this

     @listenWindowResize()

-  setDomElement:(cssClass)->
+
+  setDomElement: (cssClass) ->
     {helpContent, helpTitle} = @getOptions()

     if helpContent
@@ -86,11 +87,13 @@

     """

-  addSubView:(view, selector = ".kdmodal-content", shouldPrepend = no)->
+
+  addSubView: (view, selector = ".kdmodal-content", shouldPrepend = no) ->
     selector = null if @$(selector).length is 0
     super view, selector, shouldPrepend

-  setButtons:(buttonDataSet, destroyExists = no)->
+
+  setButtons: (buttonDataSet, destroyExists = no) ->
     @buttons or= {}
     @setClass "with-buttons"
     defaultFocusTitle = null
@@ -103,10 +106,12 @@

     @buttons[defaultFocusTitle]?.setFocus()  if not focused and defaultFocusTitle

-  destroyButtons:->
+
+  destroyButtons: ->
     button.destroy()  for own _key, button of @buttons

-  click:(e)->
+
+  click:(e) ->
     @cancel() if $(e.target).is(".closeModal")
     if $(e.target).is(".showHelp")
       {helpContent} = @getOptions()
@@ -123,15 +128,18 @@
   # keyUp:(e)->
   #   @cancel() if e.which is 27

-  setTitle:(title)->
+
+  setTitle: (title) ->
     @$().find(".kdmodal-title").removeClass('hidden').html("#{title}")
     @modalTitle = title

-  setSubtitle:(subtitle)->
+
+  setSubtitle: (subtitle) ->
     @$().find(".kdmodal-title").append("#{subtitle}")
     @modalSubtitle = subtitle

-  setModalHeight:(value)->
+
+  setModalHeight: (value) ->
     if value is "auto"
       # @$().css "min-height","100px"
       @$().css "height","auto"
@@ -140,12 +148,14 @@
       @$().height value
       @modalHeight = value

-  setModalWidth:(value)->
+
+  setModalWidth: (value) ->
     # if isNaN value
     @modalWidth = value
     @$().width value

-  setPositions:->
+
+  setPositions: ->
     @utils.defer =>
       {top, right, bottom, left} = @getOptions().position
       newRules = {}
@@ -157,7 +167,8 @@
       newRules.opacity = 1
       @$().css newRules

-  _windowDidResize:->
+
+  _windowDidResize: ->
     @setPositions()
     {innerHeight} = window
     @$('.kdmodal-content').css maxHeight : innerHeight - 120
@@ -175,32 +186,36 @@
     @overlay.once 'click', @bound 'destroy'  if overlayClick

-  createButton:(title, buttonOptions)->
+  createButton: (title, buttonOptions) ->
     buttonOptions.title    = title
-    buttonOptions.delegate = @
+    buttonOptions.delegate = this
     itemClass = buttonOptions.itemClass
     delete buttonOptions.itemClass
     @buttonHolder.addSubView button = new (itemClass or KDButtonView) buttonOptions
     button.on 'KDModalShouldClose', => @emit 'KDModalShouldClose'
     button

-  setContent:(content)->
+
+  setContent: (content) ->
     @modalContent = content
     @getDomElement().find(".kdmodal-content").html content

-  display:->
+
+  display: ->
     if @getOptions().fx
       @utils.defer =>
         @setClass "active"

-  cancel:->
+
+  cancel: ->
     return unless @getOptions().cancelable
     @emit 'ModalCancelled'
     @destroy()

-  destroy:->
+
+  destroy: ->
     $(window).off "keydown.modal"
-    uber = KDView::destroy.bind @
+    uber = KDView::destroy.bind this

     if @options.fx
       @unsetClass "active"
@@ -214,17 +229,20 @@

   ### STACK HELPERS ###

-  @createStack: (options)->
+  @createStack: (options) ->
     @stack or= new KDModalViewStack options

-  @addToStack: (modal)->
+
+  @addToStack: (modal) ->
     @stack.addModal modal
+

   @destroyStack: ->
     @stack.destroy()
     delete @stack

-  @confirm = (options)->
+
+  @confirm = (options) ->
     noop = -> modal.destroy()

     { ok, cancel, title, content, description } = options
@@ -250,3 +268,5 @@
           callback  : cancel.callback or noop
     modal.addSubView options.subView  if options.subView
     modal
+
+


PUBDEV-5204: Allow user to specify explicit list of interactions in GLM

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -16,6 +16,12 @@
           if actual_value then actual_value.name else null
         when 'VecSpecifier'
           if actual_value then actual_value.column_name else null
+        when 'StringPair[]'
+          if actual_value
+            pairs = actual_value.map (pair) -> pair.a + ':' + pair.b
+            join pairs, ', '
+          else
+            null
         when 'string[]', 'byte[]', 'short[]', 'int[]', 'long[]', 'float[]', 'double[]'
           if actual_value then join actual_value, ', ' else null
         else


Change scores styling

Change scores styling

Removed area around the items to make everything clickable.
Make sure that the colored square aligns to the top when a lot of users
are shown when the item is clicked.

--- client.coffee
+++ client.coffee
@@ -161,6 +161,8 @@

 scoresContent = ->
 	Ui.list !->
+		Dom.style
+			padding: '0'
 		Db.shared.observeEach 'game', 'teams', (team) !->
 			teamColor = Db.shared.get('colors', team.n, 'hex')
 			teamName = Db.shared.get('colors', team.n, 'name')
@@ -168,15 +170,18 @@
 			# list of teams and their scores
 			expanded = Obs.create(false)
 			Ui.item !->
+				Dom.style
+					padding: '14px'
+					minHeight: '71px'
 				Dom.div !->
 					Dom.style
 						width: '70px'
 						height: '70px'
-						marginRight: '10px'
 						background: teamColor
 						backgroundSize: 'cover'
-				Dom.div !->
-					Dom.style Flex: 1, fontSize: '100%'
+						position: 'absolute'
+				Dom.div !->
+					Dom.style Flex: 1, fontSize: '100%', paddingLeft: '80px'
 					Dom.text "Team " + teamName + " scored " + teamScore + " points"
 					# To Do expand voor scores
 					if expanded.get()


Fix bug in voting layout for real

Fix bug in voting layout for real

--- client.coffee
+++ client.coffee
@@ -301,12 +301,11 @@
             Dom.div !->
                 Dom.style
                     Box: 'center middle'
-                    display: 'inline-block !important'
                     position: 'relative'
                     padding: '6px 4px'
                     margin: '1px'
                     borderRadius: '2px'
-                    width: size+'px'
+                    flexDirection: 'column'

                 Ui.avatar Plugin.userAvatar(user.key()),
                     size: size


Minor changes

Minor changes

--- client.coffee
+++ client.coffee
@@ -10,6 +10,8 @@
 Form = require 'form'
 {tr} = require 'i18n'
 Util = require 'util'
+Social = require 'social'
+Event = require 'event'

 exports.render = ->
 	if Page.state.get(0) is 'advanced'
@@ -82,11 +84,17 @@
 						Ui.avatar Plugin.userAvatar(i)
 						Dom.text Plugin.userName(i)

+	Social.renderComments round_no
+
 renderRoundList = ->
 	renderRoundItem = (round) ->
 		Ui.item ->
 			if !round.get('finished') and !Db.shared.get('votes', Plugin.userId())
 				Icon.render data: 'new', style: { display: 'block', margin: '0 10 0 0' }, size: 34
+
+			if unread = Social.newComments(round.key())
+				Dom.div !->
+				Ui.unread unread, null, {marginLeft: '4px'}

 			Dom.h2 Util.stringToQuestion(round.get('question'))
 			Dom.onTap -> Page.nav ['round', round.key()]


Rewrite OBJECT literal spec

Rewrite OBJECT literal spec

--- spec/Parser-spec.coffee
+++ spec/Parser-spec.coffee
@@ -131,18 +131,18 @@
     it 'should support OBJECT literal', ->
       parser.parse """
       x = 1
-      point = x: x
+      point = x: x * x
       point.x = 2
       """
       expectEqualRefs parser, new Range([0, 0], [0, 1]),
         new Range([1, 11], [1, 12]),
-        new Range([2, 6], [2, 7])
+        new Range([1, 15], [1, 16])
       expectEqualRefs parser, new Range([1, 11], [1, 12]),
         new Range([0, 0], [0, 1]),
-        new Range([2, 6], [2, 7])
-      expectEqualRefs parser, new Range([2, 6], [2, 7]),
-        new Range([0, 0], [0, 1]),
-        new Range([1, 11], [1, 12])
+        new Range([1, 15], [1, 16])
+      expectEqualRefs parser, new Range([1, 11], [1, 12]),
+        new Range([1, 15], [1, 16]),
+        new Range([0, 0], [0, 1])

     it 'should find ref from outer in ARRAY', ->
       parser.parse """


Adds dependency injection to event sourceable via Space.eventSourcing.Router

--- source/server/infrastructure/router.coffee
+++ source/server/infrastructure/router.coffee
@@ -1,21 +1,29 @@
-class Space.eventSourcing.Router extends Space.messaging.Controller
-
+class Space.eventSourcing.Router extends Space.Object
   @type 'Space.eventSourcing.Router'

+  @mixin [
+    Space.messaging.CommandHandling
+    Space.messaging.EventSubscribing
+    Space.messaging.EventPublishing
+  ]
+
+  _commandHandlers: {}
+
   dependencies: {
+    repository: 'Space.eventSourcing.Repository'
+    injector: 'Injector'
     configuration: 'configuration'
-    repository: 'Space.eventSourcing.Repository'
-    commitStore: 'Space.eventSourcing.CommitStore'
+    commandBus: 'Space.messaging.CommandBus'
+    eventBus: 'Space.messaging.EventBus'
     log: 'log'
   }

-  @ERRORS: {
-
+  ERRORS: {
     managedEventSourcableNotSpecified: 'Please specify a Router::eventSourceable
     class to be managed by the router.'

     missingInitializingMessage: 'Please specify Router::initializingMessage
-    (an event or command class) that will be used to create new instanes of
+    (an event or command class) that will be used to create new instances of
     the managed eventSourceable.'

     missingEventCorrelationProperty: 'Please specify Process::eventCorrelationProperty
@@ -27,8 +35,8 @@

   eventSourceable: null
   initializingMessage: null
+  routeCommands: null
   routeEvents: null
-  routeCommands: null
   eventCorrelationProperty: null

   constructor: ->
@@ -41,27 +49,79 @@
     if @routeEvents.length > 0 and not @eventCorrelationProperty?
       throw new Error Router.ERRORS.missingEventCorrelationProperty
     @routeCommands ?= []
-    super

   onDependenciesReady: ->
-    super
     @_setupInitializingMessage()
     @_routeEventToEventSourceable(eventType) for eventType in @routeEvents
     @_routeCommandToEventSourceable(commandType) for commandType in @routeCommands

   _setupInitializingMessage: ->
     if @initializingMessage.isSubclassOf(Space.messaging.Event)
-      @eventBus.subscribeTo @initializingMessage, (event) =>
-        @log.info("#{this}: Creating new #{@eventSourceable} with event
-                  #{event.typeName()}\n", event)
-        eventSourceable = @_handleDomainErrors(-> new @eventSourceable event)
-        @repository.save(eventSourceable) if eventSourceable?
+      @_initializingMessageEventHandler(@initializingMessage)
     else if @initializingMessage.isSubclassOf(Space.messaging.Command)
-      @commandBus.registerHandler @initializingMessage, (cmd) =>
-        @log.info("#{this}: Creating new #{@eventSourceable} with command
-                  #{cmd.typeName()}\n", cmd)
-        eventSourceable = @_handleDomainErrors(-> new @eventSourceable cmd)
-        @repository.save(eventSourceable) if eventSourceable?
+      @_initializingMessageCommandHandler(@initializingMessage)
+
+  _initializingMessageEventHandler: (event) ->
+    @eventBus.subscribeTo event, (cmd) =>
+      @log.info("#{this}: Creating new #{@eventSourceable} with event
+                #{event.typeName()}\n", event)
+
+      eventSourceable = @_handleDomainErrors(->
+        instance = new @eventSourceable(event.sourceId)
+        @_injectDependencies(instance)
+        instance.handle(event)
+        return instance
+      )
+      @repository.save(eventSourceable) if eventSourceable?
+
+  _initializingMessageCommandHandler: (command) ->
+    @commandBus.registerHandler command, (cmd) =>
+      @log.info("#{this}: Creating new #{@eventSourceable} with command
+                #{cmd.typeName()}\n", cmd)
+
+      eventSourceable = @_handleDomainErrors(->
+        instance = new @eventSourceable(cmd.targetId)
+        @_injectDependencies(instance)
+        instance.handle(cmd)
+        return instance
+      )
+      @repository.save(eventSourceable) if eventSourceable?
+
+  _genericEventHandler: (event) =>
+    # Only route this event if the correlation property exists
+    unless event.meta? and event.meta[this.eventCorrelationProperty]?
+      return
+
+    correlationId = event.meta[@eventCorrelationProperty]
+    @log.info(@_logMsg("Handling event #{event.typeName()} for
+                       #{@eventSourceable}<#{correlationId}>\n"), event)
+
+    eventSourceable = @repository.find(@eventSourceable, correlationId)
+    unless eventSourceable?
+      throw Router.ERRORS.cannotHandleMessage(event)
+
+    @_injectDependencies(eventSourceable)
+    eventSourceable = @_handleDomainErrors(->
+      eventSourceable.handle event
+    )
+    @repository.save(eventSourceable) if eventSourceable?
+
+  _genericCommandHandler: (command) =>
+    if not command?
+      return
+
+    @log.info(@_logMsg("Handling command #{command.typeName()} for
+                       #{@eventSourceable}<#{command.targetId}>"), command)
+
+    eventSourceable = @repository.find @eventSourceable, command.targetId
+    unless eventSourceable?
+      throw Router.ERRORS.cannotHandleMessage(command)
+
+    @_injectDependencies(eventSourceable)
+    eventSourceable = @_handleDomainErrors(->
+      eventSourceable.handle(command)
+    )
+    @repository.save(eventSourceable) if eventSourceable?

   _routeEventToEventSourceable: (eventType) ->
     @eventBus.subscribeTo eventType, @_genericEventHandler
@@ -69,37 +129,21 @@
   _routeCommandToEventSourceable: (commandType) ->
     @commandBus.registerHandler commandType, @_genericCommandHandler

-  _genericEventHandler: (event) =>
-    # Only route this event if the correlation property exists
-    return unless event.meta? and event.meta[this.eventCorrelationProperty]?
-    correlationId = event.meta[this.eventCorrelationProperty]
-    @log.info(@_logMsg("Handling event #{event.typeName()} for
-                       #{@eventSourceable}<#{correlationId}>\n"), event)
-    eventSourceable = @repository.find @eventSourceable, correlationId
-    throw Router.ERRORS.cannotHandleMessage(event) if !eventSourceable?
-    eventSourceable = @_handleDomainErrors(-> eventSourceable.handle event)
-    @repository.save(eventSourceable) if eventSourceable?

-  _genericCommandHandler: (command) =>
-    if not command? then return
-    @log.info(@_logMsg("Handling command #{command.typeName()} for
-                       #{@eventSourceable}<#{command.targetId}>"), command)
-    eventSourceable = @repository.find @eventSourceable, command.targetId
-    throw Router.ERRORS.cannotHandleMessage(command) if !eventSourceable?
-    eventSourceable = @_handleDomainErrors(-> eventSourceable.handle command)
-    @repository.save(eventSourceable) if eventSourceable?
+  _injectDependencies: (eventSourceable) ->
+    @injector.injectInto(eventSourceable)

   _logMsg: (message) -> "#{@configuration.appId}: #{this}: #{message}"

   _handleDomainErrors: (fn) ->
     try
-      return fn.call(this)
+      return fn.call(@)
     catch error
       if error instanceof Space.Error
-        this.publish(new Space.domain.Exception({
+        @publish(new Space.domain.Exception({
           thrower: @eventSourceable.toString(),
           error: error
         }))
         return null
       else
-        throw error
+        throw error


:new: Add specs for tempFile Copied from @andystanton 's POC

--- spec/helper-spec.coffee
+++ spec/helper-spec.coffee
@@ -221,6 +221,57 @@
         ).then (result) ->
           expect(result).toBe(1)

+  describe '::tempFiles', ->
+    it 'cries when arguments are invalid', ->
+      expect ->
+        helpers.tempFiles()
+      .toThrow()
+      expect ->
+        helpers.tempFiles(null, null)
+      .toThrow()
+      expect ->
+        helpers.tempFiles('', null)
+      .toThrow()
+      expect ->
+        helpers.tempFiles('', '')
+      .toThrow()
+      expect ->
+        helpers.tempFiles(null, '')
+      .toThrow()
+      expect ->
+        helpers.tempFiles([], '')
+      .toThrow()
+      expect ->
+        helpers.tempFiles([], null)
+      .toThrow()
+    it 'works and accepts a callback and returns a promise and its promise value is that returned by the callback', ->
+      filePaths = null
+      waitsForPromise ->
+        helpers.tempFiles(
+          [
+            {
+              'name': 'foo.js',
+              'contents': 'Foo!'
+            },
+            {
+              'name': 'bar.js',
+              'contents': 'Bar!'
+            }
+          ], (filepaths) ->
+            filePaths = filepaths
+            expect(filePaths[0].indexOf('atom-linter_')).not.toBe(-1)
+            expect(path.basename(filePaths[0])).toBe('foo.js')
+            expect(fs.existsSync(filePaths[0])).toBe(true)
+            expect(fs.readFileSync(filePaths[0]).toString()).toBe('Foo!')
+
+            expect(filePaths[1].indexOf('atom-linter_')).not.toBe(-1)
+            expect(path.basename(filePaths[1])).toBe('bar.js')
+            expect(fs.existsSync(filePaths[1])).toBe(true)
+            expect(fs.readFileSync(filePaths[1]).toString()).toBe('Bar!')
+            return filePaths
+          ).then (result) ->
+            expect(result.length).toBe(2)
+
   describe '::createElement', ->
     it 'works', ->
       clicked = false


keep tikzexternalize files

keep tikzexternalize files

--- services/clsi/app/coffee/ResourceWriter.coffee
+++ services/clsi/app/coffee/ResourceWriter.coffee
@@ -78,6 +78,8 @@
 					should_delete = true
 					if path.match(/^output\./) or path.match(/\.aux$/) or path.match(/^cache\//) # knitr cache
 						should_delete = false
+					if path.match(/^output-.*/) # Tikz cached figures
+						should_delete = false
 					if path == "output.pdf" or path == "output.dvi" or path == "output.log" or path == "output.xdv"
 						should_delete = true
 					if path == "output.tex" # created by TikzManager if present in output files


P1 DONE: operator-transform-string #270

P1 DONE: operator-transform-string #270

--- spec/operator-transform-string-spec.coffee
+++ spec/operator-transform-string-spec.coffee
@@ -1,7 +1,8 @@
+# SPEC_MIGRATION: P1 DONE #270
 {getVimState, dispatch} = require './spec-helper'
 settings = require '../lib/settings'

-describe "Operator TransformString", ->
+ffdescribe "Operator TransformString", ->
   [set, ensure, keystroke, editor, editorElement, vimState] = []

   beforeEach ->
@@ -33,27 +34,27 @@
         cursor: [[0, 2], [1, 2]]

     it 'takes a count', ->
-      ensure '4~',
+      ensure '4 ~',
         text: 'AbC\nxYz'
         cursor: [[0, 2], [1, 2]]

     describe "in visual mode", ->
       it "toggles the case of the selected text", ->
         set cursorBuffer: [0, 0]
-        ensure 'V~', text: 'AbC\nXyZ'
+        ensure 'V ~', text: 'AbC\nXyZ'

     describe "with g and motion", ->
       it "toggles the case of text, won't move cursor", ->
         set cursorBuffer: [0, 0]
-        ensure 'g~2l', text: 'Abc\nXyZ', cursor: [0, 0]
+        ensure 'g ~ 2 l', text: 'Abc\nXyZ', cursor: [0, 0]

       it "g~~ toggles the line of text, won't move cursor", ->
         set cursorBuffer: [0, 1]
-        ensure 'g~~', text: 'AbC\nXyZ', cursor: [0, 1]
+        ensure 'g ~ ~', text: 'AbC\nXyZ', cursor: [0, 1]

       it "g~g~ toggles the line of text, won't move cursor", ->
         set cursorBuffer: [0, 1]
-        ensure 'g~g~', text: 'AbC\nXyZ', cursor: [0, 1]
+        ensure 'g ~ g ~', text: 'AbC\nXyZ', cursor: [0, 1]

   describe 'the U keybinding', ->
     beforeEach ->
@@ -62,39 +63,39 @@
         cursorBuffer: [0, 0]

     it "makes text uppercase with g and motion, and won't move cursor", ->
-      ensure 'gUl', text: 'ABc\nXyZ', cursor: [0, 0]
-      ensure 'gUe', text: 'ABC\nXyZ', cursor: [0, 0]
+      ensure 'g U l', text: 'ABc\nXyZ', cursor: [0, 0]
+      ensure 'g U e', text: 'ABC\nXyZ', cursor: [0, 0]
       set cursorBuffer: [1, 0]
-      ensure 'gU$', text: 'ABC\nXYZ', cursor: [1, 0]
+      ensure 'g U $', text: 'ABC\nXYZ', cursor: [1, 0]

     it "makes the selected text uppercase in visual mode", ->
-      ensure 'VU', text: 'ABC\nXyZ'
+      ensure 'V U', text: 'ABC\nXyZ'

     it "gUU upcase the line of text, won't move cursor", ->
       set cursorBuffer: [0, 1]
-      ensure 'gUU', text: 'ABC\nXyZ', cursor: [0, 1]
+      ensure 'g U U', text: 'ABC\nXyZ', cursor: [0, 1]

     it "gUgU upcase the line of text, won't move cursor", ->
       set cursorBuffer: [0, 1]
-      ensure 'gUgU', text: 'ABC\nXyZ', cursor: [0, 1]
+      ensure 'g U g U', text: 'ABC\nXyZ', cursor: [0, 1]

   describe 'the u keybinding', ->
     beforeEach ->
       set text: 'aBc\nXyZ', cursorBuffer: [0, 0]

     it "makes text lowercase with g and motion, and won't move cursor", ->
-      ensure 'gu$', text: 'abc\nXyZ', cursor: [0, 0]
+      ensure 'g u $', text: 'abc\nXyZ', cursor: [0, 0]

     it "makes the selected text lowercase in visual mode", ->
-      ensure 'Vu', text: 'abc\nXyZ'
+      ensure 'V u', text: 'abc\nXyZ'

     it "guu downcase the line of text, won't move cursor", ->
       set cursorBuffer: [0, 1]
-      ensure 'guu', text: 'abc\nXyZ', cursor: [0, 1]
+      ensure 'g u u', text: 'abc\nXyZ', cursor: [0, 1]

     it "gugu downcase the line of text, won't move cursor", ->
       set cursorBuffer: [0, 1]
-      ensure 'gugu', text: 'abc\nXyZ', cursor: [0, 1]
+      ensure 'g u g u', text: 'abc\nXyZ', cursor: [0, 1]

   describe "the > keybinding", ->
     beforeEach ->
@@ -110,7 +111,7 @@

       describe "when followed by a >", ->
         it "indents the current line", ->
-          ensure '>>',
+          ensure '> >',
             text: "12345\nabcde\n  ABCDE"
             cursor: [2, 2]

@@ -120,13 +121,13 @@

       describe "when followed by a >", ->
         it "indents the current line", ->
-          ensure '>>',
+          ensure '> >',
             text: "  12345\nabcde\nABCDE"
             cursor: [0, 2]

       describe "when followed by a repeating >", ->
         beforeEach ->
-          keystroke '3>>'
+          keystroke '3 > >'

         it "indents multiple lines at once", ->
           ensure
@@ -140,7 +141,7 @@
     describe "in visual mode", ->
       beforeEach ->
         set cursor: [0, 0]
-        keystroke 'V>'
+        keystroke 'V >'

       it "indents the current line and exits visual mode", ->
         ensure
@@ -157,7 +158,7 @@
         set cursor: [0, 0]

       it "indents the currrent selection and exits visual mode", ->
-        ensure 'vj>',
+        ensure 'v j >',
           mode: 'normal'
           cursor: [1, 0]
           text: """
@@ -166,7 +167,7 @@
           ABCDE
           """
       it "when repeated, operate on same range when cursor was not moved", ->
-        ensure 'vj>',
+        ensure 'v j >',
           mode: 'normal'
           cursor: [1, 0]
           text: """
@@ -183,7 +184,7 @@
           ABCDE
           """
       it "when repeated, operate on relative range from cursor position with same extent when cursor was moved", ->
-        ensure 'vj>',
+        ensure 'v j >',
           mode: 'normal'
           cursor: [1, 0]
           text: """
@@ -191,7 +192,7 @@
             abcde
           ABCDE
           """
-        ensure 'l.',
+        ensure 'l .',
           mode: 'normal'
           cursor: [1, 2]
           text: "  12345\n    abcde\n  ABCDE"
@@ -202,13 +203,13 @@

     describe "when followed by a <", ->
       it "indents the current line", ->
-        ensure '<<',
+        ensure '< <',
           text: "12345\n  abcde\nABCDE"
           cursor: [0, 0]

     describe "when followed by a repeating <", ->
       beforeEach ->
-        keystroke '2<<'
+        keystroke '2 < <'

       it "indents multiple lines at once", ->
         ensure
@@ -221,7 +222,7 @@

     describe "in visual mode", ->
       it "indents the current line and exits visual mode", ->
-        ensure 'V<',
+        ensure 'V <',
           mode: 'normal'
           text: "12345\n  abcde\nABCDE"
           selectedBufferRange: [[0, 0], [0, 0]]
@@ -247,14 +248,14 @@

       describe "when followed by a =", ->
         beforeEach ->
-          keystroke '=='
+          keystroke '= ='

         it "indents the current line", ->
           expect(editor.indentationForBufferRow(1)).toBe 0

       describe "when followed by a repeating =", ->
         beforeEach ->
-          keystroke '2=='
+          keystroke '2 = ='

         it "autoindents multiple lines at once", ->
           ensure text: "foo\nbar\nbaz", cursor: [1, 0]
@@ -270,14 +271,14 @@
         cursorBuffer: [0, 0]

     it "CamelCase text and not move cursor", ->
-      ensure 'gc$', text: 'vimMode\natom-text-editor\n', cursor: [0, 0]
-      ensure 'jgc$', text: 'vimMode\natomTextEditor\n', cursor: [1, 0]
+      ensure 'g c $', text: 'vimMode\natom-text-editor\n', cursor: [0, 0]
+      ensure 'j g c $', text: 'vimMode\natomTextEditor\n', cursor: [1, 0]

     it "CamelCase selected text", ->
-      ensure 'Vjgc', text: 'vimMode\natomTextEditor\n', cursor: [0, 0]
+      ensure 'V j g c', text: 'vimMode\natomTextEditor\n', cursor: [0, 0]

     it "gcgc CamelCase the line of text, won't move cursor", ->
-      ensure 'lgcgc', text: 'vimMode\natom-text-editor\n', cursor: [0, 1]
+      ensure 'l g c g c', text: 'vimMode\natom-text-editor\n', cursor: [0, 1]

   describe 'SnakeCase', ->
     beforeEach ->
@@ -289,14 +290,14 @@
           'g _': 'vim-mode-plus:snake-case'

     it "SnakeCase text and not move cursor", ->
-      ensure 'g_$', text: 'vim_mode\natom-text-editor\n', cursor: [0, 0]
-      ensure 'jg_$', text: 'vim_mode\natom_text_editor\n', cursor: [1, 0]
+      ensure 'g _ $', text: 'vim_mode\natom-text-editor\n', cursor: [0, 0]
+      ensure 'j g _ $', text: 'vim_mode\natom_text_editor\n', cursor: [1, 0]

     it "SnakeCase selected text", ->
-      ensure 'Vjg_', text: 'vim_mode\natom_text_editor\n', cursor: [0, 0]
+      ensure 'V j g _', text: 'vim_mode\natom_text_editor\n', cursor: [0, 0]

     it "g_g_ SnakeCase the line of text, won't move cursor", ->
-      ensure 'lg_g_', text: 'vim_mode\natom-text-editor\n', cursor: [0, 1]
+      ensure 'l g _ g _', text: 'vim_mode\natom-text-editor\n', cursor: [0, 1]

   describe 'DashCase', ->
     beforeEach ->
@@ -305,14 +306,14 @@
         cursorBuffer: [0, 0]

     it "DashCase text and not move cursor", ->
-      ensure 'g-$', text: 'vim-mode\natom_text_editor\n', cursor: [0, 0]
-      ensure 'jg-$', text: 'vim-mode\natom-text-editor\n', cursor: [1, 0]
+      ensure 'g - $', text: 'vim-mode\natom_text_editor\n', cursor: [0, 0]
+      ensure 'j g - $', text: 'vim-mode\natom-text-editor\n', cursor: [1, 0]

     it "DashCase selected text", ->
-      ensure 'Vjg-', text: 'vim-mode\natom-text-editor\n', cursor: [0, 0]
+      ensure 'V j g -', text: 'vim-mode\natom-text-editor\n', cursor: [0, 0]

     it "g-g- DashCase the line of text, won't move cursor", ->
-      ensure 'lg-g-', text: 'vim-mode\natom_text_editor\n', cursor: [0, 1]
+      ensure 'l g - g -', text: 'vim-mode\natom_text_editor\n', cursor: [0, 1]

   describe 'surround', ->
     beforeEach ->
@@ -334,22 +335,23 @@
           , 100

       it "surround text object with ( and repeatable", ->
-        ensure ['ysiw', char: '('],
+        # [FIXME] OLD STYLE
+        ensure ['y s i w', char: '('],
           text: "(apple)\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: "(apple)\n(pairs): [brackets]\npairs: [brackets]\n( multi\n  line )"
       it "surround text object with { and repeatable", ->
-        ensure ['ysiw', char: '{'],
+        ensure ['y s i w', char: '{'],
           text: "{apple}\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: "{apple}\n{pairs}: [brackets]\npairs: [brackets]\n( multi\n  line )"
       it "surround linewise", ->
-        ensure ['ysys', char: '{'],
+        ensure ['y s y s', char: '{'],
           text: "{\napple\n}\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
           cursor: [0, 0]
-        ensure '3j.',
+        ensure '3 j .',
           text: "{\napple\n}\n{\npairs: [brackets]\n}\npairs: [brackets]\n( multi\n  line )"

       describe 'charactersToAddSpaceOnSurround setting', ->
@@ -360,11 +362,11 @@

         it "add additional space inside pair char when surround", ->
           settings.set('charactersToAddSpaceOnSurround', ['(', '{', '['])
-          ensure ['ysiw', char: '('], text: "( apple )\norange\nlemmon"
+          ensure ['y s i w', char: '('], text: "( apple )\norange\nlemmon"
           keystroke 'j'
-          ensure ['ysiw', char: '{'], text: "( apple )\n{ orange }\nlemmon"
+          ensure ['y s i w', char: '{'], text: "( apple )\n{ orange }\nlemmon"
           keystroke 'j'
-          ensure ['ysiw', char: '['], text: "( apple )\n{ orange }\n[ lemmon ]"
+          ensure ['y s i w', char: '['], text: "( apple )\n{ orange }\n[ lemmon ]"

     describe 'map-surround', ->
       beforeEach ->
@@ -385,16 +387,16 @@
           'atom-text-editor.vim-mode-plus.visual-mode':
             'm s':  'vim-mode-plus:map-surround'
       it "surround text for each word in target case-1", ->
-        ensure ['msip', char: '('],
+        ensure ['m s i p', char: '('],
           text: "\n(apple)\n(pairs) (tomato)\n(orange)\n(milk)\n"
           cursor: [1, 0]
       it "surround text for each word in target case-2", ->
         set cursor: [2, 1]
-        ensure ['msil', char: '<'],
+        ensure ['m s i l', char: '<'],
           text: '\napple\n<pairs> <tomato>\norange\nmilk\n'
           cursor: [2, 0]
       it "surround text for each word in visual selection", ->
-        ensure ['vipms', char: '"'],
+        ensure ['v i p m s', char: '"'],
           text: '\n"apple"\n"pairs" "tomato"\n"orange"\n"milk"\n'
           cursor: [1, 0]

@@ -406,13 +408,13 @@
         set cursor: [1, 8]

       it "delete surrounded chars and repeatable", ->
-        ensure ['ds', char: '['],
+        ensure ['d s', char: '['],
           text: "apple\npairs: brackets\npairs: [brackets]\n( multi\n  line )"
-        ensure 'jl.',
+        ensure 'j l .',
           text: "apple\npairs: brackets\npairs: brackets\n( multi\n  line )"
       it "delete surrounded chars expanded to multi-line", ->
         set cursor: [3, 1]
-        ensure ['ds', char: '('],
+        ensure ['d s', char: '('],
           text: "apple\npairs: [brackets]\npairs: [brackets]\n multi\n  line "
       it "delete surrounded chars and trim padding spaces", ->
         set
@@ -421,8 +423,8 @@
             {  orange   }\n
             """
           cursor: [0, 0]
-        ensure ['ds', char: '('], text: "apple\n{  orange   }\n"
-        ensure ['jds', char: '{'], text: "apple\norange\n"
+        ensure ['d s', char: '('], text: "apple\n{  orange   }\n"
+        ensure ['j d s', char: '{'], text: "apple\norange\n"
       it "delete surrounded for multi-line but dont affect code layout", ->
         set
           cursor: [0, 34]
@@ -432,7 +434,7 @@
               hello: world
             }
             """
-        ensure ['ds', char: '{'],
+        ensure ['d s', char: '{'],
           text: [
               "highlightRanges @editor, range, "
               "  timeout: timeout"
@@ -455,14 +457,14 @@
             """
           cursorBuffer: [0, 1]
       it "change surrounded chars and repeatable", ->
-        ensure ['cs', char: '(['],
+        ensure ['c s', char: '(['],
           text: """
             [apple]
             (grape)
             <lemmon>
             {orange}
             """
-        ensure 'jl.',
+        ensure 'j l .',
           text: """
             [apple]
             [grape]
@@ -470,14 +472,14 @@
             {orange}
             """
       it "change surrounded chars", ->
-        ensure ['jjcs', char: '<"'],
+        ensure ['j j c s', char: '<"'],
           text: """
             (apple)
             (grape)
             "lemmon"
             {orange}
             """
-        ensure ['jlcs', char: '{!'],
+        ensure ['j l c s', char: '{!'],
           text: """
             (apple)
             (grape)
@@ -494,7 +496,7 @@
               hello: world
             }
             """
-        ensure ['cs', char: '{('],
+        ensure ['c s', char: '{('],
           text: """
             highlightRanges @editor, range, (
               timeout: timeout
@@ -509,16 +511,16 @@
             'y s w': 'vim-mode-plus:surround-word'

       it "surround a word with ( and repeatable", ->
-        ensure ['ysw', char: '('],
+        ensure ['y s w', char: '('],
           text: "(apple)\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: "(apple)\n(pairs): [brackets]\npairs: [brackets]\n( multi\n  line )"
       it "surround a word with { and repeatable", ->
-        ensure ['ysw', char: '{'],
+        ensure ['y s w', char: '{'],
           text: "{apple}\npairs: [brackets]\npairs: [brackets]\n( multi\n  line )"
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: "{apple}\n{pairs}: [brackets]\npairs: [brackets]\n( multi\n  line )"

     describe 'delete-surround-any-pair', ->
@@ -538,14 +540,14 @@
             'd s': 'vim-mode-plus:delete-surround-any-pair'

       it "delete surrounded any pair found and repeatable", ->
-        ensure 'ds',
+        ensure 'd s',
           text: 'apple\n(pairs: brackets)\n{pairs "s" [brackets]}\n( multi\n  line )'
         ensure '.',
           text: 'apple\npairs: brackets\n{pairs "s" [brackets]}\n( multi\n  line )'

       it "delete surrounded any pair found with skip pair out of cursor and repeatable", ->
         set cursor: [2, 14]
-        ensure 'ds',
+        ensure 'd s',
           text: 'apple\n(pairs: [brackets])\n{pairs "s" brackets}\n( multi\n  line )'
         ensure '.',
           text: 'apple\n(pairs: [brackets])\npairs "s" brackets\n( multi\n  line )'
@@ -554,7 +556,7 @@

       it "delete surrounded chars expanded to multi-line", ->
         set cursor: [3, 1]
-        ensure 'ds',
+        ensure 'd s',
           text: 'apple\n(pairs: [brackets])\n{pairs "s" [brackets]}\n multi\n  line '

     describe 'delete-surround-any-pair-allow-forwarding', ->
@@ -570,13 +572,13 @@
           ___(inner)
           ___(inner)
           """
-        ensure 'ds',
+        ensure 'd s',
           text: """
           ___inner
           ___(inner)
           """
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: """
           ___inner
           ___inner
@@ -599,9 +601,9 @@
             'c s': 'vim-mode-plus:change-surround-any-pair'

       it "change any surrounded pair found and repeatable", ->
-        ensure ['cs', char: '<'], text: "<apple>\n(grape)\n<lemmon>\n{orange}"
-        ensure 'j.', text: "<apple>\n<grape>\n<lemmon>\n{orange}"
-        ensure 'jj.', text: "<apple>\n<grape>\n<lemmon>\n<orange>"
+        ensure ['c s', char: '<'], text: "<apple>\n(grape)\n<lemmon>\n{orange}"
+        ensure 'j .', text: "<apple>\n<grape>\n<lemmon>\n{orange}"
+        ensure 'j j .', text: "<apple>\n<grape>\n<lemmon>\n<orange>"

     describe 'change-surround-any-pair-allow-forwarding', ->
       beforeEach ->
@@ -616,13 +618,13 @@
           ___(inner)
           ___(inner)
           """
-        ensure ['cs', char: '<'],
+        ensure ['c s', char: '<'],
           text: """
           ___<inner>
           ___(inner)
           """
           cursor: [0, 0]
-        ensure 'j.',
+        ensure 'j .',
           text: """
           ___<inner>
           ___<inner>
@@ -649,7 +651,7 @@
       set register: 'a': text: 'A register', type: 'character'

     it "replace selection with regisgter's content", ->
-      ensure 'viw',
+      ensure 'v i w',
         selectedText: 'aaa'
       ensure '_',
         mode: 'normal'
@@ -657,19 +659,19 @@

     it "replace text object with regisgter's content", ->
       set cursor: [1, 6]
-      ensure '_i(',
+      ensure '_ i (',
         mode: 'normal'
         text: originalText.replace('parenthesis', 'default register')

     it "can repeat", ->
       set cursor: [1, 6]
-      ensure '_i(j.',
+      ensure '_ i ( j .',
         mode: 'normal'
         text: originalText.replace(/parenthesis/g, 'default register')

     it "can use specified register to replace with", ->
       set cursor: [1, 6]
-      ensure ['"', char: 'a', '_i('],
+      ensure ['"', char: 'a', '_ i ('],
         mode: 'normal'
         text: originalText.replace('parenthesis', 'A register')

@@ -693,15 +695,15 @@
       set register: 'a': text: 'A register', type: 'character'

     it "swap selection with regisgter's content", ->
-      ensure 'viw', selectedText: 'aaa'
-      ensure 'gp',
+      ensure 'v i w', selectedText: 'aaa'
+      ensure 'g p',
         mode: 'normal'
         text: originalText.replace('aaa', 'default register')
         register: '"': text: 'aaa'

     it "swap text object with regisgter's content", ->
       set cursor: [1, 6]
-      ensure 'gpi(',
+      ensure 'g p i (',
         mode: 'normal'
         text: originalText.replace('111', 'default register')
         register: '"': text: '111'
@@ -713,14 +715,14 @@
         here (default register)
         here (111)
         """
-      ensure 'gpi(j.',
+      ensure 'g p i ( j .',
         mode: 'normal'
         text: updatedText
         register: '"': text: '222'

     it "can use specified register to swap with", ->
       set cursor: [1, 6]
-      ensure ['"', char: 'a', 'gpi('],
+      ensure ['"', char: 'a', 'g p i ('],
         mode: 'normal'
         text: originalText.replace('111', 'A register')
         register: 'a': text: '111'
@@ -751,7 +753,7 @@

     it 'toggle comment for textobject for indent and repeatable', ->
       set cursor: [2, 0]
-      ensure 'g/ii',
+      ensure 'g / i i',
         text: """
           class Base
             constructor: (args) ->
@@ -765,7 +767,7 @@

     it 'toggle comment for textobject for paragraph and repeatable', ->
       set cursor: [2, 0]
-      ensure 'g/ip',
+      ensure 'g / i p',
         text: """
           # class Base
           #   constructor: (args) ->


More decoupling of constructing controls - Add hooks for adding conditions - Values are not filtered relative to the context

--- cilantro/static/cilantro/scripts/coffeescript/forms/controls.coffee
+++ cilantro/static/cilantro/scripts/coffeescript/forms/controls.coffee
@@ -35,8 +35,10 @@
         initialize: (options) ->
             @options = options

+            @deferredEvents = $.Deferred()
+
             # Subscribe to edits for nodes relating to this DataField
-            mediator.subscribe "datafield/#{ @model.id }/edit", (node) =>
+            mediator.subscribe "datacontext/#{ @model.id }/edit", (node) =>
                 if node is @node then return
                 @set(node)

@@ -51,6 +53,15 @@
             @$operator = @$ '[name=operator]'
             @$controls = @$ '.form-actions'

+            @$el.append formActionsTemplate()
+
+            @$include = @$ '[name=include]'
+            @$exclude = @$ '[name=exclude]'
+
+            @deferredEvents.then @loadOperators
+            @deferredEvents.then @loadValues
+
+        loadOperators: =>
             for [operator, text] in (operators = @model.get 'operators')
                 if operator.charAt(0) is '-'
                     NEGATION_OPERATORS[operator.substr(1)] = operator
@@ -61,14 +72,10 @@
             if @$operator.children().length is 1
                 @$operator.hide()

-            @$el.append formActionsTemplate()
-
-            @$include = @$ '[name=include]'
-            @$exclude = @$ '[name=exclude]'
-
+        loadValues: =>

         get: (options) ->
-            id = @node.id
+            id = @model.id
             operator = @getOperator options
             value = @getValue options

@@ -87,6 +94,18 @@

         preventDefault: (event) ->
             event.preventDefault()
+
+        showControls: (event) ->
+            @$controls.fadeTo 200, 1
+
+        hideControls: (event) ->
+            @$controls.fadeTo 400, 0.3
+
+        toggleControls: (event) ->
+            if NEGATION_OPERATORS[@$operator.val()]
+                @$exclude.prop 'disabled', false
+            else
+                @$exclude.prop 'disabled', true

         getValue: (options) ->
             @$value.val()
@@ -106,23 +125,24 @@

         submitInclude: (event) ->
             event.preventDefault()
-            @node.set @get()
+            @submit @get()

         submitExclude: (event) ->
             event.preventDefault()
-            @node.set @get negated: true
-
-        showControls: (event) ->
-            @$controls.fadeTo 200, 1
-
-        hideControls: (event) ->
-            @$controls.fadeTo 400, 0.3
-
-        toggleControls: (event) ->
-            if NEGATION_OPERATORS[@$operator.val()]
-                @$exclude.prop 'disabled', false
-            else
-                @$exclude.prop 'disabled', true
+            @submit @get negated: true
+
+        submit: (data) ->
+            if @node
+                @node.set data
+            else
+                mediator.publish 'datacontext/add', data
+            @clear()
+
+        clear: ->
+            delete @node
+            @setValue()
+            @setOperator()
+

     class StringControl extends Control
@@ -176,14 +196,15 @@
             @$value2 = @$('[name=value-2]').hide()

         getValue: ->
+            value = parseFloat @$value.val()
             if /range/.test @getOperator()
-                [@$value.val(), @$value2.val()]
-            else
-                @$value.val()
-
-        setValue: ->
-            value = @node.get 'value'
-            if /range/.test @node.get 'operator'
+                value2 = parseFloat @$value2.val()
+                [value, value2]
+            else
+                value
+
+        setValue: (value) ->
+            if /range/.test @getOperator()
                 @$value.val value[0]
                 @$value2.val value[1]
             else
@@ -212,23 +233,27 @@
             @setElement @template
                 label: @model.get('alt_name') or @model.get('name')
                 help: @model.get 'description'
-
-            super
-
-        setup: ->
-            super
-            @loadValues()
-
-
-        loadValues: ->
+            super
+
+        loadOperators: =>
+            NEGATION_OPERATORS['-in'] = 'in'
+            for [operator, text] in (operators = @model.get 'operators')
+                if operator is 'in'
+                    @$operator.append "<option value=#{ operator }>#{ text }</option>"
+                    break
+            @$operator.hide()
+
+        loadValues: =>
             @$el.addClass 'loading'

             Backbone.ajax
                 url: environ.absolutePath @model.get('links').values.href
+                data:
+                    context: null
                 success: (resp) =>
                     for obj in resp
                         @$value.append "<option value=#{obj.value}>#{obj.name} (#{obj.count})</option>"
-                    #@$value.select2()
+                    #@$value.select2().css 'width', 'auto'
                     @$el.removeClass 'loading'


activity: update public channel notification setting route

--- client/activity/lib/routes/publicchannelnotificationsettings.coffee
+++ client/activity/lib/routes/publicchannelnotificationsettings.coffee
@@ -10,7 +10,7 @@

   constructor: ->

-    @path = '/Channels/:channelName/NotificationSettings'
+    @path = 'NotificationSettings'

   getComponent: (state, callback) -> callback null, NotificationSettingsModal
@@ -18,11 +18,7 @@

   onEnter: (nextState, replaceState, done) ->

-    { channelName } = nextState.params
+    channel = channelByName nextState.params.channelName

-    channel   = channelByName channelName
-    channelId = channel.id
+    NotificationSettingsFlux.actions.channel.load(channel.id).then -> done()

-    NotificationSettingsFlux.actions.channel.load channelId
-      .then -> done()
-