Simplify mode_normal.coffee

Simplify mode_normal.coffee

--- content_scripts/mode_normal.coffee
+++ content_scripts/mode_normal.coffee
@@ -11,17 +11,20 @@
       @setKeyMapping items.normalModeKeyStateMapping

     chrome.storage.onChanged.addListener (changes, area) =>
-      if area == "local" and changes.normalModeKeyStateMapping?.newValue
+      if area == "local" and changes.normalModeKeyStateMapping and changes.normalModeKeyStateMapping.newValue
         @setKeyMapping changes.normalModeKeyStateMapping.newValue
+    return

   commandHandler: ({command: registryEntry, count}) ->
     count *= registryEntry.options.count ? 1
-    count = 1 if registryEntry.noRepeat
+    if registryEntry.noRepeat
+      count = 1

     if registryEntry.repeatLimit? and registryEntry.repeatLimit < count
-      return unless confirm """
+      result = confirm """
         You have asked Vimium to perform #{count} repetitions of the command: #{registryEntry.description}.\n
         Are you sure you want to continue?"""
+      return unless result

     if registryEntry.topFrame
       # We never return to a UI-component frame (e.g. the help dialog), it might have lost the focus.
@@ -32,6 +35,7 @@
       chrome.runtime.sendMessage {handler: "runBackgroundCommand", registryEntry, count}
     else
       NormalModeCommands[registryEntry.command] count, {registryEntry}
+    return

 enterNormalMode = (count) ->
   mode = new NormalMode()
@@ -47,28 +51,30 @@
   scrollToBottom: ->
     Marks.setPreviousPosition()
     Scroller.scrollTo "y", "max"
+    return
   scrollToTop: (count) ->
     Marks.setPreviousPosition()
     Scroller.scrollTo "y", (count - 1) * Settings.get("scrollStepSize")
-  scrollToLeft: -> Scroller.scrollTo "x", 0
-  scrollToRight: -> Scroller.scrollTo "x", "max"
-  scrollUp: (count) -> Scroller.scrollBy "y", -1 * Settings.get("scrollStepSize") * count
-  scrollDown: (count) -> Scroller.scrollBy "y", Settings.get("scrollStepSize") * count
-  scrollPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1/2 * count
-  scrollPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1/2 * count
-  scrollFullPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1 * count
-  scrollFullPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1 * count
-  scrollLeft: (count) -> Scroller.scrollBy "x", -1 * Settings.get("scrollStepSize") * count
-  scrollRight: (count) -> Scroller.scrollBy "x", Settings.get("scrollStepSize") * count
+    return
+  scrollToLeft: -> Scroller.scrollTo "x", 0; return
+  scrollToRight: -> Scroller.scrollTo "x", "max"; return
+  scrollUp: (count) -> Scroller.scrollBy "y", -1 * Settings.get("scrollStepSize") * count; return
+  scrollDown: (count) -> Scroller.scrollBy "y", Settings.get("scrollStepSize") * count; return
+  scrollPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1/2 * count; return
+  scrollPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1/2 * count; return
+  scrollFullPageUp: (count) -> Scroller.scrollBy "y", "viewSize", -1 * count; return
+  scrollFullPageDown: (count) -> Scroller.scrollBy "y", "viewSize", 1 * count; return
+  scrollLeft: (count) -> Scroller.scrollBy "x", -1 * Settings.get("scrollStepSize") * count; return
+  scrollRight: (count) -> Scroller.scrollBy "x", Settings.get("scrollStepSize") * count; return

   # Tab navigation: back, forward.
-  goBack: (count) -> history.go(-count)
-  goForward: (count) -> history.go(count)
+  goBack: (count) -> history.go(-count); return
+  goForward: (count) -> history.go(count); return

   # Url manipulation.
   goUp: (count) ->
     url = window.location.href
-    if (url[url.length - 1] == "/")
+    if (url.endsWith("/"))
       url = url.substring(0, url.length - 1)

     urlsplit = url.split("/")
@@ -87,20 +93,24 @@
       else
         url = "view-source:" + url
       chrome.runtime.sendMessage {handler: "openUrlInNewTab", url}
+    return

   copyCurrentUrl: ->
     chrome.runtime.sendMessage { handler: "getCurrentTabUrl" }, (url) ->
       HUD.copyToClipboard url
       url = url[0..25] + "...." if 28 < url.length
       HUD.showForDuration("Yanked #{url}", 2000)
+    return

   openCopiedUrlInNewTab: (count) ->
     HUD.pasteFromClipboard (url) ->
       chrome.runtime.sendMessage { handler: "openUrlInNewTab", url, count }
+    return

   openCopiedUrlInCurrentTab: ->
     HUD.pasteFromClipboard (url) ->
       chrome.runtime.sendMessage { handler: "openUrlInCurrentTab", url }
+    return

   # Mode changes.
   enterInsertMode: ->
@@ -123,14 +133,22 @@
     new FindMode()

   # Find.
-  performFind: (count) -> FindMode.findNext false for [0...count] by 1
-  performBackwardsFind: (count) -> FindMode.findNext true for [0...count] by 1
+  performFind: (count) ->
+    for [0...count] by 1
+      FindMode.findNext false
+    return
+
+  performBackwardsFind: (count) ->
+    for [0...count] by 1
+      FindMode.findNext true
+    return

   # Misc.
   mainFrame: -> focusThisFrame highlight: true, forceFocusThisFrame: true
   showHelp: (sourceFrameId) -> HelpDialog.toggle {sourceFrameId, showAllCommandDetails: false}

   passNextKey: (count, options) ->
+    # TODO(philc): OK to remove return statement?
     if options.registryEntry.options.normal
       enterNormalMode count
     else
@@ -138,7 +156,7 @@

   goPrevious: ->
     previousPatterns = Settings.get("previousPatterns") || ""
-    previousStrings = previousPatterns.split(",").filter( (s) -> s.trim().length )
+    previousStrings = previousPatterns.split(",").filter((s) -> s.trim().length)
     findAndFollowRel("prev") || findAndFollowLink(previousStrings)

   goNext: ->
@@ -151,11 +169,13 @@
     # the currently-focused element highlighted specially. Tabbing will shift focus to the next input element.
     # Pressing any other key will remove the overlays and the special tab behavior.
     resultSet = DomUtils.evaluateXPath textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE
-    visibleInputs =
-      for i in [0...resultSet.snapshotLength] by 1
-        element = resultSet.snapshotItem i
-        continue unless DomUtils.getVisibleClientRect element, true
-        { element, index: i, rect: Rect.copy element.getBoundingClientRect() }
+    visibleInputs = []
+
+    for i in [0...resultSet.snapshotLength] by 1
+      element = resultSet.snapshotItem i
+      unless DomUtils.getVisibleClientRect element, true
+        continue
+      visibleInputs.push({ element, index: i, rect: Rect.copy element.getBoundingClientRect() })

     visibleInputs.sort ({element: element1, index: i1}, {element: element2, index: i2}) ->
       # Put elements with a lower positive tabIndex first, keeping elements in DOM order.
@@ -181,15 +201,15 @@
     # to be the key-mappings input.  Arguably, this is the input that the user is most likely to use.
     recentlyFocusedElement = lastFocusedInput()

-    selectedInputIndex =
-      if count == 1
-        # As the starting index, we pick that of the most recently focused input element (or 0).
-        elements = visibleInputs.map (visibleInput) -> visibleInput.element
-        Math.max 0, elements.indexOf recentlyFocusedElement
-      else
-        Math.min(count, visibleInputs.length) - 1
-
-    hints = for tuple in visibleInputs
+
+    if count == 1
+      # As the starting index, we pick that of the most recently focused input element (or 0).
+      elements = visibleInputs.map (visibleInput) -> visibleInput.element
+      selectedInputIndex = Math.max 0, elements.indexOf recentlyFocusedElement
+    else
+      selectedInputIndex = Math.min(count, visibleInputs.length) - 1
+
+    hints = visibleInputs.map((tuple) =>
       hint = DomUtils.createElement "div"
       hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"

@@ -199,7 +219,7 @@
       hint.style.width = tuple.rect.width + "px"
       hint.style.height = tuple.rect.height + "px"

-      hint
+      hint)

     new FocusSelector hints, visibleInputs, selectedInputIndex

@@ -240,7 +260,8 @@
     "(" + textInputTypes.map((type) -> '@type="' + type + '"').join(" or ") + "or not(@type))" +
     " and not(@disabled or @readonly)]",
     "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']"]
-  DomUtils?.makeXPath(inputElements)
+  if DomUtils?
+    DomUtils.makeXPath(inputElements)
 )()

 # used by the findAndFollow* functions.
@@ -281,14 +302,16 @@
     linkMatches = false
     for linkString in linkStrings
       if link.innerText.toLowerCase().indexOf(linkString) != -1 ||
-          0 <= link.value?.indexOf? linkString
+          (link.value && link.value.includes && link.value.includes(linkString))
         linkMatches = true
         break
-    continue unless linkMatches
+    unless linkMatches
+      continue

     candidateLinks.push(link)

-  return if (candidateLinks.length == 0)
+  if (candidateLinks.length == 0)
+    return

   for link in candidateLinks
     link.wordCount = link.innerText.trim().split(/\s+/).length
@@ -327,6 +350,7 @@
       if (element.hasAttribute("rel") && element.rel.toLowerCase() == value)
         followLink(element)
         return true
+  return

 class FocusSelector extends Mode
   constructor: (hints, visibleInputs, selectedInputIndex) ->


Правки тултипов на русском языке

Правки тултипов на русском языке

--- app/assets/javascripts/sitemplate_wysihat_local.js.coffee
+++ app/assets/javascripts/sitemplate_wysihat_local.js.coffee
@@ -13,12 +13,12 @@
     toolbar:
       undo: 'Отменить'
       redo: 'Повторить'
-      bold: 'Жирный'
+      bold: 'Полужирный'
       italic: 'Курсив'
       underline: 'Подчеркнутый'
-      h1: 'Заголовок 1'
-      h2: 'Заголовок 2'
-      h3: 'Заголовок 3'
+      h1: 'Заголовок первого уровня'
+      h2: 'Заголовок второго уровня'
+      h3: 'Заголовок третьего уровня'
       align:
         left: 'Выравнивание по левому краю'
         right: 'Выравнивание по правому краю'
@@ -26,7 +26,7 @@
         justify: 'Выравнивание по ширине'
       ul: 'Маркированный список'
       ol: 'Нумерованный список'
-      image: 'Вставка изображения'
+      image: 'Вставить изображение'
       inline:
         left: 'Врезка изображения слева'
         right: 'Врезка изображения справа'


Change trigger flag

Change trigger flag

--- app/assets/javascripts/uploadcare/widget/widget.js.coffee
+++ app/assets/javascripts/uploadcare/widget/widget.js.coffee
@@ -36,10 +36,9 @@

         @reloadInfo()

-      setValue: (value) ->
+      setValue: (value, changed = true) ->
         @element.val(value)
-
-        @reloadInfo()
+        @element.trigger('change') if changed

       reloadInfo: ->
         id = utils.uuidRegex.exec @element.val()
@@ -50,22 +49,22 @@

           if id
             info = uploads.fileInfo(id, @settings)
-            @__setLoaded(info)
+            @__setLoaded(info, false)
           else
             @__reset()

       __changed: (e) =>
         @reloadInfo()

-      __setLoaded: (infoPr) ->
+      __setLoaded: (infoPr, changed = true) ->
         $.when(infoPr)
           .fail(@__fail)
           .done (info) =>
             if @settings.imagesOnly && !uploads.isImage(info)
               return @__fail('image')
             @template.setFileInfo(info)
-            @setValue info.fileId
             @template.loaded()
+            @setValue(info.fileId, changed)

       __fail: (type) =>
         @__cancel()
@@ -81,7 +80,7 @@

       __cancel: =>
         @__reset()
-        @setValue ''
+        @setValue('')

       __setupWidget: ->
         # Initialize the file browse button


Environment: VM logic moved from EnvironmentMainScene to MachineContainer

--- client/Environments/views/scene/environmentmachinecontainer.coffee
+++ client/Environments/views/scene/environmentmachinecontainer.coffee
@@ -7,6 +7,26 @@
     options.title      = 'Virtual Machines'

     super options, data
+
+    # Plus button on machinesContainer uses the vmController
+    @on 'PlusButtonClicked', =>
+      return unless KD.isLoggedIn()
+        new KDNotificationView
+          title: "You need to login to create a new machine."
+
+      @addVmModal = new KDModalView
+        title        : 'Add Virtual Machine'
+        cssClass     : 'add-vm-modal'
+        view         : @getVmSelectionView()
+        width        : 786
+        buttons      :
+          create     :
+            title    : "Create"
+            style    : "modal-clean-green"
+            callback : =>
+              @addVmModal.destroy()
+              KD.singleton("vmController").createNewVM (err) ->
+                KD.showError err

   loadItems:->
     super
@@ -18,6 +38,7 @@
         @emit "DataLoaded"
         return warn "Failed to fetch VMs", err  if err
       addedCount = 0
+
       vms.forEach (vm)=>
         @addItem
           title     : vm
@@ -25,4 +46,52 @@
           memUsage  : KD.utils.getRandomNumber 100
           activated : yes
         addedCount++
-        @emit "DataLoaded"  if addedCount is vms.length+        @emit "DataLoaded"  if addedCount is vms.length
+
+  getVmSelectionView: ->
+
+    addVmSelection = new KDCustomHTMLView
+      cssClass   : "new-vm-selection"
+
+    addVmSelection.addSubView addVmSmall = new KDCustomHTMLView
+      cssClass    : "add-vm-box selected"
+      partial     :
+        """
+
Small 1x

+

+
1 CPU

+
1GB RAM

+
4GB Storage

+

+        """
+
+    addVmSelection.addSubView addVmLarge = new KDCustomHTMLView
+      cssClass    : "add-vm-box passive"
+      partial     :
+        """
+
Large 2x

+

+
2 CPU

+
2GB RAM

+
8GB Storage

+

+        """
+
+    addVmSelection.addSubView addVmExtraLarge = new KDCustomHTMLView
+      cssClass    : "add-vm-box passive"
+      partial     :
+        """
+
Extra Large 3x

+

+
4 CPU

+
4GB RAM

+
16GB Storage

+

+        """
+
+    addVmSelection.addSubView comingSoonTitle = new KDCustomHTMLView
+      cssClass     : "coming-soon-title"
+      tagName      : "h5"
+      partial      : "Coming soon..."
+
+    return addVmSelection


Hackathon: Subscribe: endpoint updated

Hackathon: Subscribe: endpoint updated

--- client/landing/site.hackathon/coffee/home/subscribeformview.coffee
+++ client/landing/site.hackathon/coffee/home/subscribeformview.coffee
@@ -50,7 +50,7 @@
     data = @serializeFormData()

     jQuery.ajax
-      url         : "/-/marketing/subscribe"
+      url         : "/-/emails/subscribe"
       type        : 'POST'
       data        : data
       success     : => @emit 'subscribeSuccess'


:bug: Fix typo

:bug: Fix typo

--- lib/minimap-bookmarks-binding.coffee
+++ lib/minimap-bookmarks-binding.coffee
@@ -10,7 +10,7 @@

     # https://github.com/atom/bookmarks/blob/master/lib/main.coffee#L38
     id = atom.packages.packageStates?.bookmarks?[@editor.id]?.markerLayerId
-    markerLayer = markerLayerId && @editor.getMarkerLayer(id)
+    markerLayer = id && @editor.getMarkerLayer(id)
     if markerLayer?
       @subscriptions.add markerLayer.onDidCreateMarker (marker) =>
         @handleMarker(marker)


clenup

clenup

--- lib/operator-transform-string.coffee
+++ lib/operator-transform-string.coffee
@@ -576,21 +576,18 @@
   @extend(false)
   wise: 'linewise'
   trim: false
-  input: null
   target: "MoveToRelativeLineMinimumOne"
-
-  getPattern: (trim=false) ->
-    if trim
-      /\r?\n[ \t]*/g
-    else
-      /\r?\n/g

   initialize: ->
     @focusInput(10) if @isRequireInput()
     super

   getNewText: (text) ->
-    text.trimRight().replace(@getPattern(@trim), @input) + "\n"
+    if @trim
+      pattern = /\r?\n[ \t]*/g
+    else
+      pattern = /\r?\n/g
+    text.trimRight().replace(pattern, @input) + "\n"

 class JoinWithKeepingSpace extends JoinBase
   @extend()
@@ -604,11 +601,11 @@
   requireInput: true
   trim: true

-class JoinByInputWithKeepingSpace extends JoinBase
+class JoinByInputWithKeepingSpace extends JoinByInput
   @extend()
   @registerToSelectList()
   @description: "Join lines without padding space between each line"
-  requireInput: true
+  trim: false

 # -------------------------
 # String suffix in name is to avoid confusion with 'split' window.


Better getTitle

Better getTitle

--- lib/restructured-preview-view.coffee
+++ lib/restructured-preview-view.coffee
@@ -1,6 +1,7 @@
 {TextEditorView} = require 'atom'
 {ScrollView} = require 'atom-space-pen-views'
 url = require 'url'
+path = require 'path'

 module.exports =
 class RestructuredPreviewView extends ScrollView
@@ -19,4 +20,6 @@
       @div()

     getTitle: ->
-      return "Restructured Preview"
+      title = url.parse(@filePath)
+      title = path.basename(title.pathname)
+      "Restructured Preview - #{title}"


fix(route): use protractor.config for CS e2e tests

Looks like I missed updating CoffeeScript e2e tests to use
Protractor config instead of loading build.config.

This aligns CoffeeScript with the other languages.

--- lib/route/templates/page_test.coffee
+++ lib/route/templates/page_test.coffee
@@ -1,9 +1,7 @@
 ###global describe, beforeEach, it, browser<% if (e2eTestFramework === 'jasmine') { %>, expect<% } %> ###
 'use strict'

-buildConfigFile = require('findup-sync') 'build.config.js'
-buildConfig = require buildConfigFile<% if (e2eTestFramework === 'mocha') { %>
-chai = require 'chai'
+<% if (e2eTestFramework === 'mocha') { %>chai = require 'chai'
 chaiAsPromised = require 'chai-as-promised'
 expect = chai.expect<% } %>
 <%= upperCamel %>PagePo = require './<%= hyphenName %>.po'<% if (e2eTestFramework === 'mocha') { %>
@@ -15,7 +13,7 @@

   beforeEach ->
     <%= lowerCamel %>Page = new <%= upperCamel %>PagePo
-    browser.driver.get buildConfig.host + ':' + buildConfig.port + '/#<%= url %>'
+    browser.get '/#<%= url %>';

   it 'should say <%= ctrlName %>', ->
     expect(<%= lowerCamel %>Page.heading.getText()).<% if (e2eTestFramework === 'mocha') { %>to.eventually.equal<% } else { %>toEqual<% } %> '<%= lowerCamel %>'


fix unnecessary extra lines

fix unnecessary extra lines

--- src/marktab.coffee
+++ src/marktab.coffee
@@ -314,7 +314,7 @@
 						break
 				line += "|"
 				@lines.push(line)
-			@lines.push("")
+			@lines.push("") if someLeft
 		@lines

 	# simply returns @lines concatenated into a string with newlines (\n)


deleting coffee directory

deleting coffee directory

--- common/static/coffee/spec/discussion/discussion_router_spec.coffee
+++ common/static/coffee/spec/discussion/discussion_router_spec.coffee
@@ -1,220 +0,0 @@
-describe 'DiscussionRouter', ->
-  beforeEach ->
-    DiscussionSpecHelper.setUpGlobals()
-    DiscussionSpecHelper.setUnderscoreFixtures()
-    appendSetFixtures("""
-
-      """)
-
-    @threads = [
-      DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "1",
-        title: "Thread1",
-        votes: {up_count: '20'},
-        pinned: true,
-        comments_count: 1,
-        created_at: '2013-04-03T20:08:39Z',
-      }),
-      DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "2",
-        title: "Thread2",
-        votes: {up_count: '42'},
-        comments_count: 2,
-        created_at: '2013-04-03T20:07:39Z',
-      }),
-      DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "3",
-        title: "Thread3",
-        votes: {up_count: '12'},
-        comments_count: 3,
-        created_at: '2013-04-03T20:06:39Z',
-      }),
-      DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "4",
-        title: "Thread4",
-        votes: {up_count: '25'},
-        comments_count: 0,
-        pinned: true,
-        created_at: '2013-04-03T20:05:39Z',
-      }),
-    ]
-
-    @other_threads = {
-      '12': DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "12",
-        title: "Thread12",
-        votes: {up_count: '74'},
-        comments_count: 0,
-        pinned: false,
-        created_at: '2015-04-03T20:05:39Z',
-      }),
-      '15': DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "15",
-        title: "Thread15",
-        votes: {up_count: '92'},
-        comments_count: 0,
-        pinned: true,
-        created_at: '2015-05-01T19:4:03Z',
-      }),
-      '112': DiscussionViewSpecHelper.makeThreadWithProps({
-        id: "112",
-        title: "Thread112",
-        votes: {up_count: '1'},
-        comments_count: 2,
-        pinned: false,
-        created_at: '2015-06-09T20:05:39Z',
-      })
-    }
-
-    spyOn(DiscussionUtil, 'makeWmdEditor')
-    @discussion = new Discussion(_.map(@threads, (thread_spec) -> new Thread(thread_spec)), {pages: 2, sort: 'date'})
-    @course_settings = new DiscussionCourseSettings({
-          "category_map": {
-            "children": ["Topic", "General"],
-            "entries": {
-              "Topic": {"is_cohorted": false, "id": "topic"},
-              "General": {"is_cohorted": false, "id": "general"}
-            }
-          },
-          "allow_anonymous": false,
-          "allow_anonymous_to_peers": false,
-          "is_cohorted": true,
-          "cohorts": [
-            {"id": 1, "name": "Cohort1"},
-            {"id": 2, "name": "Cohort2"}
-          ]
-        })
-    @router = new DiscussionRouter({discussion: @discussion, course_settings: @course_settings})
-
-  describe 'showThread', ->
-    existingCheck = (thread_id) ->
-      it "shows thread #{thread_id}, which is already in threads collection", ->
-        DiscussionSpecHelper.makeAjaxSpy(() -> )
-        spyOn(@router, 'renderThreadView')
-        # precondition check - thread is in router's collection
-        expect(@router.discussion.get(thread_id)).not.toBeUndefined()
-
-        @router.showThread("irrelevant forum name", thread_id)
-        expect($.ajax).not.toHaveBeenCalled()
-        expect(@router.renderThreadView).toHaveBeenCalled()
-        expect(@router.thread.id).toBe(thread_id)
-
-    missingCheck = (forum_name, thread_id) ->
-      it "requests thread #{thread_id} in forum #{forum_name} if not already in collection", ->
-
-        DiscussionSpecHelper.makeAjaxSpy(
-          (params) =>
-            expect(params.url.path()).toBe(DiscussionUtil.urlFor('retrieve_single_thread', forum_name, thread_id))
-            params.success({content: @other_threads[thread_id]})
-        )
-        spyOn(@router, 'renderThreadView')
-        # precondition check - thread is not in router's collection
-        expect(@router.discussion.get(thread_id)).toBeUndefined()
-
-        @router.showThread(forum_name, thread_id)
-        expect(@router.renderThreadView).toHaveBeenCalled()
-        expect(@router.thread.id).toBe(thread_id)
-
-
-    existingCheck('1')
-    existingCheck('2')
-    existingCheck('3')
-    existingCheck('4')
-
-    missingCheck('forum1', '12')
-    missingCheck('forum2', '15')
-    missingCheck('forum3', '112')
-


Refactored waiting to use system date rather than asynchronous timers to prevent locking up during synchronous timers.

--- src/TaskRunner.coffee
+++ src/TaskRunner.coffee
@@ -21,16 +21,31 @@
     if @pauseDf
       @pauseDf.resolve()
       @pauseDf = null
+    @status = 'running'

-    @status = 'running'
-    @_deferWait()
+    # NOTE: Uses system time instead of timers to prevent locking up during synchronous tasks.
+    startTime = null
+    runsTime = 0
+    lastTaskIsWait = false
     runNext = Meteor.bindEnvironment =>
+      if startTime? && !lastTaskIsWait
+        currentTime = new Date().getTime()
+        timeDiff = currentTime - startTime
+        runsTime += timeDiff
+        if runsTime >= @options.runDuration
+          Logger.debug('Task runner ran for', runsTime, 'ms')
+          @_wait()
+          lastTaskIsWait = true
+          runsTime = 0
+      else
+        lastTaskIsWait = false
       if @bufferQueue.length == 0
         Logger.debug('Task runner complete')
         @runDf.resolve()
         @reset()
         return
       callback = @bufferQueue.shift()
+      startTime = new Date().getTime()
       @runQueue.add(callback).fin(runNext)

     # Running on an empty queue will reset the promise so we need to store a refernece to the
@@ -40,20 +55,16 @@
     runNext()
     promise

-  _deferWait: ->
-    runDuration = @options.runDuration
-    Logger.debug('Task runner running for ' + runDuration + 'ms...')
-    @waitHandle = setTimeout (Meteor.bindEnvironment => @_wait()), @options.runDuration
-
   _wait: ->
     waitDuration = @options.waitDuration
     Logger.debug('Task runner ready to wait...')
     wait = =>
       Logger.debug('Task runner waiting for ' + waitDuration + 'ms...')
+      @status = 'waiting'
       @waitDf = Q.defer()
       onDone = Meteor.bindEnvironment =>
-        @_deferWait()
         @waitDf.resolve()
+        @status = 'running'
       setTimeout(onDone, waitDuration)
       @waitDf.promise
     @bufferQueue.unshift(wait)
@@ -70,7 +81,6 @@
       @status = 'paused'
       @pauseDf.promise
     @bufferQueue.unshift(pause)
-    clearTimeout(@waitHandle)

   reset: ->
     @status == 'idle'
@@ -79,5 +89,4 @@
       if df && Q.isPending(df.promise)
         df.reject('Task runner reset')
       @[name] = null
-    clearTimeout(@waitHandle)
     Logger.debug('Task runner reset')


More specs for Brush

More specs for Brush

--- spec/javascripts/player/brush_spec.js.coffee
+++ spec/javascripts/player/brush_spec.js.coffee
@@ -45,6 +45,10 @@
       brush = new AsciiIo.Brush fg: undefined
       expect(brush.fgColor()).toEqual 7

+    it 'returns fg if bright is off', ->
+      brush = new AsciiIo.Brush fg: 3, bright: false
+      expect(brush.fgColor()).toEqual 3
+
     it 'returns fg+8 if bright is on', ->
       brush = new AsciiIo.Brush fg: 3, bright: true
       expect(brush.fgColor()).toEqual 11
@@ -54,6 +58,10 @@
     it 'returns 0 if bg is undefined', ->
       brush = new AsciiIo.Brush bg: undefined
       expect(brush.bgColor()).toEqual 0
+
+    it 'returns bg if blink is off', ->
+      brush = new AsciiIo.Brush bg: 4, blink: false
+      expect(brush.bgColor()).toEqual 4

     it 'returns bg+8 if blink is on', ->
       brush = new AsciiIo.Brush bg: 4, blink: true


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -17,7 +17,7 @@
 player.meter 'health'
 # => Sets up a health meter that will be drawn during the player overlay event

-player.include(Bounded)
+

 # now player has all the methods provided by this module
 player.position()


only stop publishing commits when the module is stopped

--- source/server.coffee
+++ source/server.coffee
@@ -38,5 +38,7 @@
     @commitPublisher.startPublishing()

   reset: ->
+    @injector.get('Space.eventSourcing.Commits').remove {}
+
+  stop: ->
     @commitPublisher.stopPublishing()
-    @injector.get('Space.eventSourcing.Commits').remove {}


implemented axe_1 - axe_4 with handaxes

implemented axe_1 - axe_4 with handaxes

--- data/items/handaxe.coffee
+++ data/items/handaxe.coffee
@@ -8,10 +8,12 @@
   weapon_class: 'slash'
   break_odds: .02
   accuracy: (attacker, target, tile) ->
-    if attacker.skills.indexOf 'axe_4' then .35
-    else if attacker.skills.indexOf 'axe_2' then .25
-    else .15
+    chance = .15
+    chance += .10 if attacker.skills.indexOf 'axe_2' isnt -1
+    chance += .10 if attacker.skills.indexOf 'axe_4' isnt -1
+    chance
   damage: (attacker, target, tile) ->
-    if attacker.skills.indexOf 'axe_3' then 3
-    else if attacker.skills.indexOf 'axe_1' then 2
-    else 1
+    dmg = 2
+    dmg += 1 if attacker.skills.indexOf 'axe_1' isnt -1
+    dmg += 1 if attacker.skills.indexOf 'axe_3' isnt -1
+    dmg


feat(Gruntfile): Better output for regex capture for custom build

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -605,8 +605,9 @@
   grunt.loadTasks './lib/grunt'

   grunt.registerTask 'build_details', ->
-    grunt.log.writeln "\n##########\nCustom Build Generated: "
-    grunt.log.write customBuildDest
+    grunt.log.writeln "\n##########"
+    grunt.log.writeln "Custom Build Generated: "
+    grunt.log.write "### " + customBuildDest + " ###"
     grunt.log.writeln "\n##########\n"

   # Tasks


chore: add bump task config

chore: add bump task config

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -61,6 +61,15 @@
         ]
         tasks: ['dev']

+    bump:
+      options:
+        files: [
+          'package.json'
+          # 'bower.json'
+        ]
+        updateConfigs: ['pkg']
+        commitFiles: ['-a']
+        pushTo: 'origin'

   # Constructs the code, runs tests and if everyting is OK, creates a minified
   # version ready for production. This task is intended to be run manually.


fix(grunt): assets in subdirectories are not correctly watched

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -67,13 +67,13 @@
         tasks: ["coffeelint:gruntfile"]

       js:
-        files: ["<%= config.app %>/**/_js/*.js"]
+        files: ["<%= config.app %>/**/_js/**/*.js"]
         tasks: ["copy:serve"]
         options:
           interrupt: true

       less:
-        files: ["<%= config.app %>/**/_less/*.less"]
+        files: ["<%= config.app %>/**/_less/**/*.less"]
         tasks: [
           "less:serve"
           "postcss:serve"


Execute compile task on watch (in place of coffee, which doesn't prepend banner)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -28,7 +28,7 @@

         watch:
             files: 'src/LibroIpsum.coffee'
-            tasks: ['coffee']
+            tasks: ['compile']

     grunt.loadNpmTasks 'grunt-contrib-coffee'
     grunt.loadNpmTasks 'grunt-contrib-concat'


Removed unnecessary options from jade grunt task.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,8 +32,6 @@

     jade:
       compile:
-        options:
-          data: debug: false
         files:
           'public/502.html': 'app/views/errors/502.jade'


add autocomplete picker to form builder

add autocomplete picker to form builder

--- modules/form-builder/main/index.coffee
+++ modules/form-builder/main/index.coffee
@@ -1,4 +1,3 @@
-
 class Form extends hx.EventEmitter
   constructor: (@selector) ->
     super
@@ -102,7 +101,7 @@
         .attr('type', 'button')
         .class(options.buttonClass)

-      pickerOptions = hx.merge({}, options.pickerOptions)
+      pickerOptions = hx.clone options.pickerOptions

       if values.length > 0
         pickerOptions.items = values
@@ -125,6 +124,37 @@
         hidden: options.hidden
         disabled: options.disabled
         disable: (selection, disabled) -> picker.disabled(disabled)
+      }
+
+  addAutocompletePicker: (name, values, options = {}) ->
+    @add name, 'select', 'div', ->
+      elem = @append('button')
+        .attr('type', 'button')
+        .class(options.buttonClass)
+
+      autocompletePickerOptions = hx.merge({buttonClass: options.buttonClass}, options.autocompletePickerOptions)
+
+      if values.length > 0
+        autocompletePickerOptions.items = values
+
+      autocompletePicker = new hx.AutocompletePicker(elem.node(), values, autocompletePickerOptions)
+      input = @append('input').class('hx-hidden-form-input').attr('size', 0)
+      @style('position', 'relative')
+
+      autocompletePicker.value(values[0]) unless typeof options.required is 'boolean'
+
+      if options.required
+        input.node().setCustomValidity('Please select a value from the list')
+        autocompletePicker.on 'change', 'hx.form-builder', ->
+          input.node().setCustomValidity('')
+
+      {
+        required: options.required
+        componentNode: elem.node()
+        key: options.key
+        hidden: options.hidden
+        disabled: options.disabled
+        disable: (selection, disabled) -> autocompletePicker.disabled(disabled)
       }

   addCheckbox: (name, options = {}) ->


[fix] grunt task for images copy

[fix] grunt task for images copy

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -39,7 +39,7 @@
           "<%= yeoman.app %>/*.html"
           "{<%= yeoman.tmp %>,<%= yeoman.app %>}/styles/{,*/}*.css"
           "{<%= yeoman.tmp %>,<%= yeoman.app %>}/scripts/**/*.js"
-          "<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp}"
+          "<%= yeoman.app %>/images/**/*.{png,jpg,jpeg,gif,webp,svg}"
         ]
         tasks: ["livereload"]

@@ -215,7 +215,7 @@
           src: [
             "*.{ico,txt}"
             ".htaccess"
-            "images/{,*/}*.{webp,gif}"
+            "images/{,*/}*.{webp,gif,svg}"
             "fonts/*.*"
           ]
         ]


Add bower config to gruntfile

Add bower config to gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -29,6 +29,16 @@
           sassDir: 'docs/welcome/sass'
           cssDir: 'docs/welcome/css'

+    bower:
+      install:
+        options:
+          targetDir: 'deps'
+          cleanup: true
+          layout: 'byComponent'
+          bowerOptions:
+            forceLatest: true
+            production: true
+
   grunt.loadNpmTasks 'grunt-contrib-watch'
   grunt.loadNpmTasks 'grunt-contrib-uglify'
   grunt.loadNpmTasks 'grunt-contrib-coffee'


Add Swedish to Gruntfile

Add Swedish to Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -112,6 +112,11 @@
                 dest: 'static/locales/fi.json'
                 options:
                     language: 'fi'
+            sv:
+                src: 'locales/*.yaml'
+                dest: 'static/locales/sv.json'
+                options:
+                    language: 'sv'
             en:
                 src: 'locales/*.yaml'
                 dest: 'static/locales/en.json'


added grunt task karma:server to run karma server

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -279,24 +279,17 @@
 				PHANTOMJS_BIN: './node_modules/.bin/phantomjs'

 		karma:
-			server:
-				options:
-					autoWatch: true
-					colors: true
-					configFile: './karma.conf.js'
-					keepalive: true
-					port: 8081
-					reporters: ['progress']
+			options:
+				autoWatch: true
+				colors: true
+				configFile: './karma.conf.js'
+				keepalive: true
+				port: 8081
+				reporters: ['progress']
+			server: {}
 			unit:
 				options:
-					autoWatch: true
 					browsers: ['PhantomJS']
-					colors: true
-					configFile: './karma.conf.js'
-					keepalive: true
-					port: 8081
-					reporters: ['progress']
-					runnerPort: 9100
 					singleRun: true

 		# Sets up file watchers and runs tasks when watched files are changed.
@@ -368,11 +361,8 @@
 		'env:karma'
 		'karma:unit'
 	]
-
-	grunt.registerTask 'test:server', [
-		'default'
-		'env:karma'
-		'karma:server'
+	grunt.registerTask 'karma:run', [
+		'karma:server:run'
 	]

 	# Starts a web server


Run with connect

Run with connect

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,13 +25,20 @@
       files: ['spec/*.coffee', 'runtime/*.js']
       tasks: ['test']

+    # Web server for the browser tests
+    connect:
+      server:
+        options:
+          port: 8000
+
     # BDD tests on browser
     mocha_phantomjs:
-      options:
-        output: 'spec/result.xml'
-        reporter: 'spec'
-        failWithOutput: true
-      all: ['spec/runner.html']
+      all:
+        options:
+          output: 'spec/result.xml'
+          reporter: 'spec'
+          failWithOutput: true
+          urls: ['http://localhost:8000/spec/runner.html']

     # Coding standards
     coffeelint:
@@ -43,10 +50,11 @@

   # Grunt plugins used for testing
   @loadNpmTasks 'grunt-contrib-watch'
+  @loadNpmTasks 'grunt-contrib-connect'
   @loadNpmTasks 'grunt-mocha-phantomjs'
   @loadNpmTasks 'grunt-coffeelint'

   # Our local tasks
   @registerTask 'build', ['noflo_browser']
-  @registerTask 'test', ['coffeelint', 'noflo_browser', 'coffee', 'mocha_phantomjs']
+  @registerTask 'test', ['coffeelint', 'noflo_browser', 'coffee', 'connect', 'mocha_phantomjs']
   @registerTask 'default', ['test']


use grunt-babel instead of grunt-es6-transpiler

https://github.com/babel/grunt-babel

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,9 +6,7 @@
   'use strict'

   require('time-grunt') grunt
-  require('jit-grunt') grunt, {
-    es6transpiler: 'grunt-es6-transpiler'
-  }
+  require('jit-grunt') grunt

   grunt.initConfig
     jsonlint:
@@ -23,20 +21,13 @@
     clean:
       all: ['tasks', 'test/actual/*', 'tmp']

-    es6transpiler:
-      options:
-        environments: ['node', 'mocha']
+    babel:
       task:
         src: ['src/merge_data.js']
         dest: 'tasks/merge_data.js'
       test:
         src: ['test/test.js']
         dest: 'tmp/test-es5.js'
-
-    espower:
-      test:
-        src: ['<%= es6transpiler.test.dest %>']
-        dest: '<%= es6transpiler.test.dest %>'

     merge_data:
       no_options:
@@ -97,10 +88,7 @@
       test:
         options:
           reporter: 'spec'
-        src: ['<%= es6transpiler.test.dest %>']
-
-    release:
-      options: {}
+        src: ['<%= babel.test.dest %>']

   grunt.registerTask 'run_this_plugin', ->
     grunt.loadTasks 'tasks'
@@ -110,13 +98,12 @@
     'clean'
     'jsonlint'
     'jshint'
-    'es6transpiler'
+    'babel'
   ]

   grunt.registerTask 'test', [
     'build'
     'run_this_plugin'
-    'espower'
     'mochaTest'
   ]


slowly, ever so slowly...

slowly, ever so slowly...

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -201,7 +201,11 @@
   ]

   grunt.registerTask 'ci', [
-    'test'
+    'build'
+    'mochaTest:unit'
+    'cucumberjs:unit'
+    'karma:unit'
+    'cucumberjs:integration'
   ]

   grunt.registerTask 'server', [


test chrome beta

test chrome beta

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -275,6 +275,7 @@
           urls: ['http://127.0.0.1:9999/spec/runner.html']
           browsers: [
             browserName: 'googlechrome'
+            platform: 'OS X 10.8'
             version: 'beta'
           ,
             browserName: 'safari'


Fixed paths for webpack output of JS and CSS.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,13 +22,13 @@
 			new ExtractTextPlugin('styles/main.css')
 		]
 		entry: {
-			'./src/scripts/index.coffee'
-			'./src/styles/main.scss'
+			'scripts/main.js': './src/scripts/index.coffee'
+			'styles/main.css': './src/styles/main.scss'
 		}
 		output:
 			path: path.join(__dirname, 'dist')
 			publicPath: 'dist/'
-			filename: '[name].js'
+			filename: '[name]'

 	grunt.initConfig
 		clean: ['dist']


Gruntfile: Remove test task.

Gruntfile: Remove test task.

It's included in build and watcher.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -140,18 +140,6 @@
   grunt.loadNpmTasks 'grunt-closure-coffee-stack'
   grunt.loadNpmTasks 'grunt-npm'

-  grunt.registerTask 'test', 'Build stack with tests', ->
-    grunt.task.run [
-      'clean'
-      'mkdir'
-      'coffee'
-      'coffee2closure'
-      'stylus'
-      'deps'
-      'karma'
-      'builder'
-    ]
-
   grunt.registerTask 'build', 'Build stack.', (app = 'app') ->
     grunt.task.run [
       'clean'


Cleaned up grunt tasks

Cleaned up grunt tasks

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -50,7 +50,7 @@
     watch:
       src:
         files: '<%= coffee.compile.src %>'
-        tasks: ['build', 'qunit']
+        tasks: ['compile', 'qunit']
       test:
         files: '<%= coffee.test.src %>'
         tasks: ['coffee:test', 'qunit']
@@ -72,5 +72,7 @@
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-text-replace')

-  grunt.registerTask('build', ['coffee', 'uglify', 'concat', 'replace'])
-  grunt.registerTask('default', ['clean', 'build', 'qunit'])
+  grunt.registerTask('compile', ['coffee', 'uglify', 'concat'])
+  grunt.registerTask('build', ['compile', 'replace'])
+  grunt.registerTask('test', ['qunit'])
+  grunt.registerTask('default', ['clean', 'build', 'test'])


Gruntfile

Gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -97,24 +97,26 @@
         files: ['emt.coffee','libs/**/*.coffee']
         tasks: [
           'coffeelint'
-          'mochaTest'
           'coffee'
           'codo'
+          'mochaTest:spec'
           # 'uglify'
           # 'concat'
         ]

-  grunt.registerTask('rep', [
+  grunt.registerTask('default', [
+    'coffeelint'
+    'coffee'
+    # 'uglify'
+    # 'concat'
+    'codo'
     'mochaTest:md'
     ])
-  grunt.registerTask('default', [
+  grunt.registerTask('serve', [
     'coffeelint'
     'mochaTest:spec'
     'coffee'
-    'codo'
-    # 'uglify'
-    # 'concat'
     'connect'
     'watch'
     ])


As assetic:watch isn't executed correctly we will dump the assets ourself.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -320,6 +320,8 @@
   grunt.registerTask 'generateJs', [
     'coffee'
     'copy:jQuerySourceMap'
+    'clean:beforeAsseticDump'
+    'shell:asseticDump'
   ]

   # Generate images


Place sourcemap files to root in the zip, since the JS gets moved there too in vulcanization

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -131,9 +131,13 @@
             'bower_components/**/*.woff'
             'bower_components/**/*.ttf'
             'bower_components/**/*.svg'
-            'bower_components/**/*.map'
           ]
           expand: true
+          dest: '/'
+        ,
+          src: ['bower_components/**/*.map']
+          expand: true
+          flatten: true
           dest: '/'
         ,
           src: ['app.js']


removed updatedConfigs from grunt/bump

removed updatedConfigs from grunt/bump

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -15,7 +15,7 @@
     bump:
       options:
         files: ['package.json','bower.json']
-        updateConfigs: ['package.json','bower.json']
+        updateConfigs: []
         commit: true
         commitMessage: 'Release of v%VERSION%'
         commitFiles: ['package.json','bower.json'] # '-a' for all files


on release, keep checking for uncommitted

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -82,7 +82,7 @@
         files:
           src: ['index.js', 'lib/*.js' ]

-  grunt.registerTask 'release:prep', [ 'git:isClean', 'semver:isSynced', 'build', 'test' ]
+  grunt.registerTask 'release:prep', [ 'git:isClean', 'semver:isSynced', 'build', 'test', 'git:isClean' ]

   grunt.registerTask 'release:bump', (type) ->
     type = type || 'patch'


Escape \ in editor.nonWordCharachters

Escape \ in editor.nonWordCharachters

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -19,7 +19,7 @@
     autosave: false
     autoIndent: true
     autoIndentOnPaste: false
-    nonWordCharacters: "./\()\"’-_:,.;<>~!@#$%^&*|+=[]{}`~?"
+    nonWordCharacters: "./\\()\"’-_:,.;<>~!@#$%^&*|+=[]{}`~?"

   @content: (params) ->
     @div class: @classes(params), tabindex: -1, =>


Adapt to contest API change

Adapt to contest API change

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -124,7 +124,7 @@
         done()
         true

-  describe 'with all & branches', ->
+  describe.skip 'with all & branches', ->
     it 'should resolve with result per branch', (done) ->
       brancher = (orig, data) ->
         subtree = orig.tree 'calc'
@@ -259,7 +259,7 @@
         res = results.map (r) -> r.value
         res[idx]
       .then (c, res) ->
-        chai.expect(res).to.equal 15
+        chai.expect(res).to.eql [15]
         done()

   describe.skip 'with contested static node branching', ->


Update gutter

Update gutter

--- src/app/gutter.coffee
+++ src/app/gutter.coffee
@@ -50,13 +50,13 @@

   ### Public ###

-  # Public: Retrieves the containing {Editor}.
+  # Retrieves the containing {Editor}.
   #
   # Returns an {Editor}.
   getEditor: ->
     @parentView

-  # Public: Defines whether to show the gutter or not.
+  # Defines whether to show the gutter or not.
   #
   # showLineNumbers - A {Boolean} which, if `false`, hides the gutter
   setShowLineNumbers: (showLineNumbers) ->
@@ -76,7 +76,7 @@
           break

     @renderLineNumbers(renderFrom, renderTo) if performUpdate
-
+
   renderLineNumbers: (startScreenRow, endScreenRow) ->
     editor = @getEditor()
     maxDigits = editor.getLineCount().toString().length


Return early when href is a hash

Return early when href is a hash

--- src/app/window.coffee
+++ src/app/window.coffee
@@ -30,7 +30,8 @@

   $(document).on 'click', 'a', (e) ->
     location = $(e.target).attr('href')
-    return unless location or location[0] is '#'
+    return unless location
+    return if location[0] is '#'

     if location.indexOf('https://') is 0 or location.indexOf('http://') is 0
       require('child_process').spawn('open', [location]) if location


killing more trapped methods

killing more trapped methods

--- src/natives.coffee
+++ src/natives.coffee
@@ -18,10 +18,6 @@
 trapped_methods =
   java:
     lang:
-      ref:
-        SoftReference: [
-          o 'get()Ljava/lang/Object;', (rs) -> null
-        ]
       System: [
         o 'loadLibrary(L!/!/String;)V', (rs) -> # NOP, because we don't support loading external libraries
         o 'adjustPropertiesForBackwardCompatibility(L!/util/Properties;)V', (rs) -> # NOP (apple-java specific)
@@ -56,12 +52,6 @@
               }
             stack.reverse()
             _this.ref
-        o 'getStackTraceDepth()I', (rs, _this) ->
-            rs.get_obj(_this.fields.stackTrace).array.length
-      ]
-      StringCoding: [
-        o 'deref(L!/!/ThreadLocal;)L!/!/Object;', (rs) -> null
-        o 'set(L!/!/ThreadLocal;L!/!/Object;)V', (rs) -> # NOP
       ]
     util:
       concurrent:
@@ -76,26 +66,8 @@
         o 'getInstance(Ljava/lang/String;)Ljava/util/Currency;', (rs) -> null # because it uses lots of reflection and we don't need it
       ]
     nio:
-      charset:
-        Charset$3: [
-          o 'run()L!/lang/Object;', (rs) -> null
-        ]
       Bits: [
         o 'byteOrder()L!/!/ByteOrder;', (rs) -> rs.static_get {class:'java/nio/ByteOrder',name:'LITTLE_ENDIAN'}
-      ]
-  sun:
-    misc:
-      JavaLangAccess: [
-        o 'registerShutdownHook(ILjava/lang/Runnable;)V', (rs) ->
-            # XXX should probably not be a NOP -- maybe we should call
-            # the runnable ourselves before quit
-      ]
-    util:
-      LocaleServiceProviderPool: [
-        o 'getPool(Ljava/lang/Class;)L!/!/!;', (rs) ->
-            # make a mock
-            rs.init_object 'sun/util/LocaleServiceProviderPool'
-        o 'hasProviders()Z', (rs) -> false  # we can't provide anything
       ]

 doPrivileged = (rs) ->


actually close file handles, per #86

actually close file handles, per #86

--- src/natives.coffee
+++ src/natives.coffee
@@ -603,7 +603,10 @@
                 exceptions.java_throw rs, 'java/io/FileNotFoundException', "Could not open file #{filepath}"
               else
                 throw e
-        o 'close0()V', (rs, _this) -> _this.$file = null
+        o 'close0()V', (rs, _this) ->
+            if _this.$file?
+              fs.closeSync _this.$file
+            _this.$file = null
         o 'skip(J)J', (rs, _this, n_bytes) ->
             if (file = _this.$file)?
               bytes_left = fs.fstatSync(file).size - _this.$pos
@@ -648,7 +651,9 @@
             byte_arr.array[offset+i] = buf.readUInt8(i) for i in [0...bytes_read] by 1
             _this.$pos = gLong.fromNumber(pos+bytes_read)
             return if bytes_read == 0 and len isnt 0 then -1 else bytes_read
-        o 'close0()V', (rs, _this) -> _this.$file = null
+        o 'close0()V', (rs, _this) ->
+            fs.closeSync _this.$file
+            _this.$file = null
       ]
       UnixFileSystem: [
         o 'checkAccess(Ljava/io/File;I)Z', (rs, _this, file, access) ->


Pending scenarios.

Pending scenarios.

--- test/casperjs/helpers/scenario.coffee
+++ test/casperjs/helpers/scenario.coffee
@@ -6,7 +6,7 @@

   scenario: (desc, fn) =>
     desc = "Scenario: #{desc}"
-    @echo "\n#{desc}", "RED_BAR"
+    @echo "\n#{desc}", "INFO_BAR"

     @start @baseUrl

@@ -17,7 +17,11 @@
   feature: (desc, fn) =>
     this.then ->
       @echo "\n#{desc}", "GREEN_BAR"
-      fn.call(this)
+
+      if fn?
+        fn.call this
+      else
+        @echo "pending", "RED_BAR"

 exports.create = (baseUrl) ->
   casper = new Scenario(baseUrl)


fixed ro/rw postfix bug when no bind volume is present (compose bug).

--- src/coffee/compose/actions.coffee
+++ src/coffee/compose/actions.coffee
@@ -42,9 +42,10 @@

     volumePaths = []
     for service in _.values composition.services when service.volumes
-      volumePaths = _.uniq (_.union volumePaths, service.volumes).map (mapping) ->
+      volumePaths = _.uniq (_.union volumePaths, service.volumes.map (mapping) ->
         splits = mapping.split(':')
-        splits[0] if splits.length is 2
+        splits[0] if splits.length >= 2
+      )

     for path in volumePaths when path
       try


now can encode big number, decoding is in progress

--- src/numbase.coffee
+++ src/numbase.coffee
@@ -3,8 +3,13 @@
 ###

 class NumBase
+
   isNum = (n)->
     /^-?[\d]+$/.test "#{n}"
+
+  # a big number to string will get a string 1e+29
+  isBigNum = (n)->
+    /e\+/.test String n

   constructor: (charList) ->
     defaultChartList = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
@@ -13,20 +18,52 @@
       if k isnt charList.indexOf v
         throw new TypeError "duplicated character <#{v}> found"
     @BASE = charList
+    if isBigNum charList.length
+      throw new TypeError 'the base is super big, consider a small one'
     @MAX_BASE = @BASE.length

+  # divide a any number(including big number) with b
+  divide = (n, b)->
+    times = ''
+    mod = ''
+    while n.length
+      while n.length and +mod < b
+        mod += n.substr 0, 1
+        n = n.substr 1
+      mod = +mod
+      times += String Math.floor mod / b
+      mod = String mod % b

+    return {
+      times: times
+      mod: mod
+    }
+
+  ###*
+   * encode a number
+   * @param  {Number|String} n           number to encode, if a big number, pass a string
+   * @param  {Number} b=@MAX_BASE        base size
+   * @return {String}                    encoded string
+  ###
   encode: (n, b=@MAX_BASE) ->
+    if typeof n is 'number' and isBigNum n
+      throw new TypeError 'number you wanna encode is super big, conside pass it as a string instead'
     return n unless isNum(n) and isNum(b) and b <= @MAX_BASE and b > 1
-    # for negtive number
-    prefix = if n < 0 then '-' else ''
-    n = Math.abs n
-    res = []
-    loop
-      res.push @BASE[n % b]
-      n = Math.floor n / b
-      break unless n
-    prefix + res.reverse().join('')
+
+    n = String n
+    sign = ''
+    result = ''
+    if n.charAt(0) is '-'
+      sign = '-'
+      n = n.substr 1
+
+    ret = times: n
+    while ret.times isnt '0'
+      ret = divide ret.times, b
+      result = @BASE[ +ret.mod ] + result
+
+    sign + result
+

   decode: (n, b=@MAX_BASE)->
     return n unless isNum(b) and b <= @MAX_BASE and b > 1


Use `true` instead of CoffeeScript's `yes` in namespace.coffee

--- src/namespace.coffee
+++ src/namespace.coffee
@@ -9,8 +9,8 @@
   pathPrefix: '/'
   pathToHTML: 'html'
   fetchRemoteViews: true
-  usePushState: yes
-  minificationErrors: yes
+  usePushState: true
+  minificationErrors: true

 (Batman.container = do -> this).Batman = Batman  # I am so, so sorry.


Fix indexes

Fix indexes

--- services/track-changes/app/coffee/MongoManager.coffee
+++ services/track-changes/app/coffee/MongoManager.coffee
@@ -124,4 +124,4 @@
 		# For finding project meta-data
 		db.projectHistoryMetaData.ensureIndex { project_id: 1 }
 		# TTL index for auto deleting week old temporary ops
-		db.docHistory.ensureIndex { tempCreatedAt: 1 }, { expireAfterSeconds: 0 }
+		db.docHistory.ensureIndex { expiresAt: 1 }, { expireAfterSeconds: 0 }


Close any old REPL's lying around when plugin starts

--- lib/atom-slime-view.coffee
+++ lib/atom-slime-view.coffee
@@ -7,6 +7,9 @@
   constructor: (serializedState, @swank) ->
     # Start a status view
     @statusView = new StatusView()
+    # Close any currently-opened REPL's that are leftover in the editor from last time
+    process.nextTick =>
+      @closeAllREPLs()

   # Returns an object that can be retrieved when package is activated
   serialize: ->
@@ -31,3 +34,9 @@

   setStatusBar: (@statusBar) ->
     @statusView.attach(@statusBar)
+
+  closeAllREPLs: ->
+    editors = atom.workspace.getTextEditors()
+    for editor in editors
+      if editor.getPath() == '/tmp/repl.lisp-repl'
+        editor.destroy()


lod_and_effect: better highlight the clicked marker

--- lod_and_effect/lod_and_effect.coffee
+++ lod_and_effect/lod_and_effect.coffee
@@ -159,6 +159,7 @@
         .attr("r", 6)
         .attr("fill", purple)
         .attr("stroke", "none")
+        .attr("stroke-width", "2")
         .attr("opacity", 0)
         .on("mouseover", ->
                d3.select(this).attr("opacity", 1))
@@ -167,10 +168,10 @@
         .on("click", (td) ->
                console.log(td)
                markerClick[lastMarker] = 0
-               d3.select("#circle#{lastMarker}").attr("opacity", 0)
+               d3.select("#circle#{lastMarker}").attr("opacity", 0).attr("fill",purple).attr("stroke","none")
                lastMarker = td
                markerClick[td] = 1
-               d3.select(this).attr("opacity", 1))
+               d3.select(this).attr("opacity", 1).attr("fill",pink).attr("stroke",purple))

   # select of chromosome for lower LOD detailed curve
   lastChr = randomChr
@@ -197,6 +198,7 @@
                 .attr("r", 6)
                 .attr("fill", purple)
                 .attr("stroke", "none")
+                .attr("stroke-width", "2")
                 .attr("opacity", 0)
                 .on("mouseover", (td) ->
                        d3.select(this).attr("opacity", 1))
@@ -205,10 +207,11 @@
                 .on("click", (td) ->
                        console.log(td)
                        markerClick[lastMarker] = 0
-                       d3.select("#circle#{lastMarker}").attr("opacity", 0)
+                       d3.select("#circle#{lastMarker}").attr("opacity", 0).attr("fill",purple).attr("stroke","none")
                        lastMarker = td
                        markerClick[td] = 1
-                       d3.select(this).attr("opacity", 1)))
+                       d3.select(this).attr("opacity", 1).attr("fill", pink).attr("stroke",purple)))
+

   # chr labels
   topsvg.append("g").selectAll("empty")


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/flickerable.coffee
+++ test/flickerable.coffee
@@ -8,7 +8,7 @@
   player.include Flickerable

 test 'it should change the alpha value when the flicker duration has elapsed', ->
-  equals, player.I.alpha, 0.9
+  equals player.I.alpha, 0.9

 module()


remove step from game object default events

--- test/game_object.coffee
+++ test/game_object.coffee
@@ -44,11 +44,12 @@
   .create()

 test "[event] update", 2, ->
-  gameObject = GameObject
+  gameObject = GameObject()
+
+
     update: "equals(I.age, 0, 'Age should be 0 on first step')"

   gameObject.trigger "update", 1
-

 test "elapsedTime", 1, ->
   gameObject = GameObject()


[tool,test] job-creator supporting multiple queues and workers

--- test/job-creator.coffee
+++ test/job-creator.coffee
@@ -4,29 +4,37 @@

 class JobCreator
-  constructor: ->
+  constructor: (@queuename = "tacos") ->
     @proteins = "carnitas brisket camarones".split " "
     @salsas   = "habanero chipotle tomatillo".split " "
     @client   = redis.createClient()
-    @queue    = Queue "tacos"
+    @queue    = Queue @queuename, createClient: => @client
+    @queue1   = Queue @queuename, createClient: => @client
+    @queue2   = Queue @queuename, createClient: => @client
+    @queue3   = Queue @queuename, createClient: => @client
+    @queue4   = Queue @queuename, createClient: => @client

-    @queue.process @cookTaco
+    @queue.process  @cookTaco
+    @queue1.process @cookTaco
+    @queue2.process @cookTaco
+    @queue3.process @cookTaco
+    @queue4.process @cookTaco

   cookTaco: (job, done) ->
-    {data} = job
+    {data, queue} = job
     {protein, salsa, cooktime, orderNumber} = data
-    log "##{orderNumber}: #{protein}, #{salsa} cooking for #{(cooktime/1000).toFixed 2}s"
+    log "#{queue.name} ##{orderNumber}: #{protein}, #{salsa} cooking for #{(cooktime/1000).toFixed 2}s"
     wake = ->
-      log "##{orderNumber}: #{protein}, #{salsa} served"
+      log "#{queue.name} ##{orderNumber}: #{protein}, #{salsa} served"
       done()
     setTimeout wake, cooktime

   makeJob: (orderNumber) ->
     protein  = @proteins[Math.floor Math.random() * 3]
     salsa    = @salsas[Math.floor Math.random() * 3]
-    cooktime = Math.random() * 10 * 1000
+    cooktime = Math.random() * 30 * 1000

-    log "ordering taco ##{orderNumber} #{protein}, #{salsa} with cooktime #{(cooktime/1000).toFixed 2}s"
+    log "#{@queuename}: ordering ##{orderNumber} #{protein}, #{salsa} with cooktime #{(cooktime/1000).toFixed 2}s"
     @queue.add {protein, salsa, cooktime, orderNumber}

   keepMaking10PacksOfTacos: (num = 1) ->


Filled out additional missing types for testing expression equality

--- math/expression_equality_spec.coffee
+++ math/expression_equality_spec.coffee
@@ -22,6 +22,7 @@
           @comps.number.build(value: 10),
           @comps.number.build(value: 10)
           )).toEqual true
+

   describe "expression comparison", ->
     it "accepts for empty expressions", ->
@@ -105,6 +106,35 @@
       expected = @exp_builder('^': [10, 22])
       expect(new_exp).not.toBeAnEqualExpressionTo(expected)

+  describe "multiplication", ->
+    it "accepts two multiplication symbols", ->
+      expect(
+        @isEqual(
+          @comps.multiplication.build(),
+          @comps.multiplication.build()
+        )).toEqual true

+  describe "division", ->
+    it "accepts two division symbols", ->
+      expect(
+        @isEqual(
+          @comps.division.build(),
+          @comps.division.build()
+        )).toEqual true

+  describe "pi", ->
+    it "accepts two pi symbols", ->
+      expect(
+        @isEqual(
+          @comps.pi.build(),
+          @comps.pi.build()
+        )).toEqual true

+  describe "subtraction", ->
+    it "accepts two subtraction symbols", ->
+      expect(
+        @isEqual(
+          @comps.subtraction.build(),
+          @comps.subtraction.build()
+        )).toEqual true
+


Added couple code fixes to make it comaptible with API v2

--- src/pagerduty.coffee
+++ src/pagerduty.coffee
@@ -34,7 +34,7 @@
       query = {}

     if pagerDutyServices? && url.match /\/incidents/
-      query['service'] = pagerDutyServices
+      query['service_id'] = pagerDutyServices

     @http(url)
       .query(query)
@@ -110,13 +110,15 @@
             value = false
         cb null, value

-  getIncident: (incident, cb) ->
-    @get "/incidents/#{encodeURIComponent incident}", {}, (err, json) ->
+  getIncident: (incident_key, cb) ->
+    query =
+      incident_key: incident_key
+
+    @get "/incidents", query, (err, json) ->
       if err?
         cb(err)
         return
-
-      cb(null, json)
+      cb(null, json.incidents)

   getIncidents: (status, cb) ->
     query =


Client: Show wins in the summary.

Client: Show wins in the summary.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -842,19 +842,22 @@

   announceWinner: (player, done) =>
     {owner} = @model.getTeam(player)
-    @chatView.print("
#{owner} won!
")
-    @model.set('finished', true)
-    done()
+    message = "#{owner} won!"
+    @announceWin(message, done)

   announceForfeit: (player, done) =>
     {owner} = @model.getTeam(player)
-    @chatView.print("
#{owner} has forfeited!
")
-    @model.set('finished', true)
-    done()
+    message = "#{owner} has forfeited!"
+    @announceWin(message, done)

   announceTimer: (player, done) =>
     {owner} = @model.getTeam(player)
-    @chatView.print("
#{owner} was given the timer win!
")
+    message = "#{owner} was given the timer win!"
+    @announceWin(message, done)
+
+  announceWin: (message, done) =>
+    @chatView.print("
#{message}
")
+    @addSummary(message, newline: true)
     @model.set('finished', true)
     done()


disable SSH access to table-xtract tools

--- client/code/view/tool/menu-item.coffee
+++ client/code/view/tool/menu-item.coffee
@@ -21,6 +21,13 @@
         right: right - 35
         left: 'auto'
       ).show()
+
+      toolName = @model.get('tool').get('name')
+      if toolName == 'table-xtract'
+        $('#tool-options-menu .git-ssh').hide()
+      else
+        $('#tool-options-menu .git-ssh').show()
+
       $('#dropdown-menu-closer').show()
       $('body').on 'click.showOptionsDropdown', ->
         $('#tool-options-menu, #dropdown-menu-closer').hide()


Always cap device scale at 1 if zoom to fit

https://github.com/motif/company/issues/1009#issuecomment-181877492

--- framer/Components/DeviceComponent.coffee
+++ framer/Components/DeviceComponent.coffee
@@ -354,6 +354,9 @@
 			(window.innerWidth  - ((@padding + paddingOffset) * 2)) / width,
 			(window.innerHeight - ((@padding + paddingOffset) * 2)) / height
 		])
+
+		# Never scale the phone beyond 100%
+		phoneScale = 1 if phoneScale > 1

 		return phoneScale


Explicitly remove dist branch

Explicitly remove dist branch

--- webpack/dist/dist.coffee
+++ webpack/dist/dist.coffee
@@ -32,6 +32,7 @@
 git "init"
 git "remote", 'add', 'origin', '../..'
 git "checkout", '-b', 'dist'
+git "push", 'origin', ':dist'	# Remove remote branch
 git "add", ".", '--force'
 git 'commit', '-m', commit
-git 'push', 'origin', 'dist', '--force'
+git 'push', 'origin', 'dist'


modify regex to accept 663 code

modify regex to accept 663 code


Fix TypeError: object is not a function

Fix TypeError: object is not a function

Newer version of mathjs don't return a constructor function anymore, but a singleton.

--- src/calculator.coffee
+++ src/calculator.coffee
@@ -14,7 +14,7 @@
 # Author:
 #   canadianveggie

-mathjs = require("mathjs")()
+mathjs = require("mathjs")

 module.exports = (robot) ->
   robot.respond /(calc|calculate|calculator|convert|math|maths)( me)? (.*)/i, (msg) ->


Update body to inherit from editable view

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -1,12 +1,14 @@
 define (require) ->
   $ = require('jquery')
   Mathjax = require('mathjax')
-  BaseView = require('cs!helpers/backbone/views/base')
+  EditableView = require('cs!helpers/backbone/views/editable')
   template = require('hbs!./body-template')
   require('less!./body')

-  return class MediaBodyView extends BaseView
+  return class MediaBodyView extends EditableView
     template: template
+
+    editable: '.media-body'

     events:
       'click .solution > .ui-toggle-wrapper > .ui-toggle': 'toggleSolution'
@@ -14,14 +16,6 @@
     initialize: () ->
       super()
       @listenTo(@model, 'changePage', @render)
-      @listenTo(@model, 'change:edit', @toggleEdit)
-
-      @observer = new MutationObserver (mutations) =>
-        mutations.forEach (mutation) =>
-          page = @model.get('currentPage')
-
-          @model.set('changed', true)
-          page.set('changed', true)

     onRender: () ->
       MathJax.Hub.Queue(['Typeset', MathJax.Hub], @$el.get(0))
@@ -29,19 +23,3 @@
     toggleSolution: (e) ->
       $solution = $(e.currentTarget).closest('.solution')
       $solution.toggleClass('ui-solution-visible')
-
-    toggleEdit: () ->
-      edit = @model.get('edit')
-      $mediaBody = @$el.children('.media-body')
-
-      $mediaBody.attr('contenteditable', edit)
-
-      if edit
-        config =
-          subtree: true
-          childList: true
-          characterData: true
-
-        @observer.observe($mediaBody.get(0), config)
-      else
-        @observer.disconnect()


#200 extract as composeOperation for being explicit

--- lib/operation-stack.coffee
+++ lib/operation-stack.coffee
@@ -22,6 +22,20 @@
   subscribe: (args...) ->
     @subscriptions.add args...

+  composeOperation: (operation) ->
+    {mode} = @vimState
+    switch
+      when operation.isOperator()
+        if (mode is 'visual')
+          operation = operation.setTarget(@currentSelection)
+      when operation.isTextObject()
+        if (mode in ['visual', 'normal'])
+          operation = @select.setTarget(operation)
+      when operation.isMotion()
+        if (mode is 'visual')
+          operation = @select.setTarget(operation)
+    operation
+
   run: (klass, properties) ->
     klass = Base.getClass(klass) if _.isString(klass)
     try
@@ -29,20 +43,7 @@
       if (@peekTop()?.constructor is klass)
         klass = Base.getClass('MoveToRelativeLine')

-      operation = new klass(@vimState, properties)
-      if @vimState.isMode('visual')
-        switch
-          when operation.isOperator()
-            operation.setTarget(@currentSelection)
-          when operation.isMotion(), operation.isTextObject()
-            operation = @select.setTarget(operation)
-        @stack.push(operation)
-      else if @isEmpty() and operation.isTextObject()
-        # when TextObject invoked directly
-        @stack.push(@select.setTarget(operation))
-      else
-        @stack.push(operation)
-
+      @stack.push @composeOperation(new klass(@vimState, properties))
       @processing = true
       @process()
     catch error
@@ -89,7 +90,7 @@
       @finish()

   cancel: ->
-    unless @vimState.isMode('visual') or @vimState.isMode('insert')
+    if @vimState.mode not in ['visual', 'insert']
       @vimState.activate('reset')
     @finish()


remove size calculating

remove size calculating

--- BackofficeBundle/Resources/public/coffee/areaView.coffee
+++ BackofficeBundle/Resources/public/coffee/areaView.coffee
@@ -102,4 +102,4 @@
     event.parents("li").first().remove()
     refreshUl event.parents("ul").first()
     @sendBlockData()
-)+)


Runner: does datetime comparisons (tested - they work anyway in JavaScript)

--- src/test/runtime/ReactiveRunnerSpec.coffee
+++ src/test/runtime/ReactiveRunnerSpec.coffee
@@ -87,6 +87,17 @@
       parseUserFunctions 'f = 101 <> 100'
       changes.should.eql [{a: false}, {b: true}, {c: true}, {d: false}, {e: false}, {f:true}]

+    it 'all comparison operations on dates', ->
+      parseUserFunctions 'd1=dateValue("2014-02-03 12:00:20")'
+      parseUserFunctions 'd2=dateValue("2014-02-03 12:00:30")'
+      parseUserFunctions 'a = d1 > d1'
+      parseUserFunctions 'b = d1 < d2'
+      parseUserFunctions 'c = d2 >= d1'
+      parseUserFunctions 'd = d2 <= d1'
+      parseUserFunctions 'e = d2 == d1'
+      parseUserFunctions 'f = d2 <> d1'
+      changes[2..].should.eql [{a: false}, {b: true}, {c: true}, {d: false}, {e: false}, {f:true}]
+
     it 'concatenates strings', ->
       parseUserFunctions 'a = "The A"'
       parseUserFunctions 'aa = a + 10 + " times"'


Adjust simulation velocity for scale

Adjust simulation velocity for scale

--- framer/LayerDraggable.coffee
+++ framer/LayerDraggable.coffee
@@ -157,8 +157,6 @@

 		@emit(Events.DragStart, event)

-
-
 	_touchMove: (event) =>

 		return unless @enabled
@@ -332,8 +330,8 @@
 		yFinished = @_simulation.y.finished()

 		velocity = {x:0, y:0}
-		velocity.x = (@_simulation.x.simulator.state.v / @velocityScale) if not xFinished
-		velocity.y = (@_simulation.y.simulator.state.v / @velocityScale) if not yFinished
+		velocity.x = (@_simulation.x.simulator.state.v / @momentumVelocityMultiplier) if not xFinished
+		velocity.y = (@_simulation.y.simulator.state.v / @momentumVelocityMultiplier) if not yFinished

 		return velocity

@@ -472,12 +470,12 @@

 		@_simulation.x.simulator.setState
 			x: @layer.x
-			v: velocity.x * @momentumVelocityMultiplier * @speedX
+			v: velocity.x * @momentumVelocityMultiplier * @speedX * (1 / @_screenScale.x) * @layer.scaleX * @layer.scale
 		@_simulation.x.start() if startSimulationX

 		@_simulation.y.simulator.setState
 			x: @layer.y
-			v: velocity.y * @momentumVelocityMultiplier * @speedY
+			v: velocity.y * @momentumVelocityMultiplier * @speedY * (1 / @_screenScale.y) * @layer.scaleY * @layer.scale
 		@_simulation.y.start() if startSimulationY

 		@emit(Events.AnimationDidStart)


clear comment input after submit & not submit empty comment

--- lms/static/coffee/src/discussion/views/thread_response_view.coffee
+++ lms/static/coffee/src/discussion/views/thread_response_view.coffee
@@ -57,12 +57,15 @@
         if textStatus == 'success'
           @model.set(response)

-  submitComment: ->
+  submitComment: (event) ->
     url = @model.urlFor('reply')
     body = @$(".comment-form-input").val()
+    if not body.trim().length
+      return false
     comment = new Comment(body: body, created_at: (new Date()).toISOString(), username: window.user.get("username"))
     @renderComment(comment)
     @trigger "comment:add"
+    @$(".comment-form-input").val("")

     DiscussionUtil.safeAjax
       $elem: $(event.target)
@@ -71,4 +74,5 @@
       dataType: 'json'
       data:
         body: body
+
     false


Correct path for the new setup

Correct path for the new setup

--- src/lib/loader/ComponentIo.coffee
+++ src/lib/loader/ComponentIo.coffee
@@ -98,8 +98,8 @@
   # We eval the contents to get a runnable component
   try
     # Modify require path for NoFlo since we're inside the NoFlo context
-    source = source.replace "require('noflo')", "require('./NoFlo')"
-    source = source.replace 'require("noflo")', 'require("./NoFlo")'
+    source = source.replace "require('noflo')", "require('../NoFlo')"
+    source = source.replace 'require("noflo")', 'require("../NoFlo")'

     # Eval so we can get a function
     implementation = eval "(function () { var exports = {}; #{source}; return exports; })()"


bugfix only choosing tbody tds show/hide last row if nextmonth is already in previous row

--- modules/gui/lib/widgets/Month.coffee
+++ modules/gui/lib/widgets/Month.coffee
@@ -152,29 +152,36 @@
                 days = 6

             date.setDate date.getDate() - days
-
-            for cell in @find 'td'
-
+
+            for cell in @find 'tbody td'
+
                 cell = $(cell).data 'date', time = date.getTime()

+                day = date.getDate()
+
                 if redraw
-	                cell.removeAttr('class').html('')
-
-	                if time is now
-	                    cell.addClass 'today'
-
-	                if current && time is current
-	                    cell.addClass 'current'
-
-	                if date.getMonth() is prev
-	                    cell.addClass 'prev'
-
-	                if date.getMonth() is next
-	                    cell.addClass 'next'
+                    cell.removeAttr('class').html('')

-                    cell.text day = date.getDate()
-
+                    if time is now
+                        cell.addClass 'today'
+
+                    if current && time is current
+                       cell.addClass 'current'
+
+                    if date.getMonth() is prev
+                        cell.addClass 'prev'
+
+                    if date.getMonth() is next
+                        cell.addClass 'next'
+
+                    cell.text day
+
                 date.setDate day + 1
-
+
+            if (row = @find('tbody tr').last()).find('td').first().hasClass 'next'
+                row.hide()
+            else
+                row.show()
+
             @_wireEvents()


Change render_last to use opts entirely

Change render_last to use opts entirely

--- src/smc-webapp/course/common.cjsx
+++ src/smc-webapp/course/common.cjsx
@@ -304,40 +304,55 @@
             error = "Try to #{name.toLowerCase()} again:\n" + error
         <ErrorDisplay key='error' error={error} style={maxHeight: '140px', overflow:'auto'}/>

-    render_last: (name, obj, type, enable_copy, copy_tip, open_tip, opts) ->
+    render_last: (opts) ->
         opts = defaults opts,
+            name        : required
+            type        : required
+            data        : {}
+            enable_copy : false
+            copy_tip    : ''
+            open_tip    : ''
             omit_errors : false

-        open = => @open(type, @props.info.assignment_id, @props.info.student_id)
-        copy = => @copy(type, @props.info.assignment_id, @props.info.student_id)
-        stop = => @stop(type, @props.info.assignment_id, @props.info.student_id)
-        obj ?= {}
+        open = => @open(opts.type, @props.info.assignment_id, @props.info.student_id)
+        copy = => @copy(opts.type, @props.info.assignment_id, @props.info.student_id)
+        stop = => @stop(opts.type, @props.info.assignment_id, @props.info.student_id)
         v = []
-        if enable_copy
-            if obj.start
-                v.push(@render_open_copying(name, open, stop))
-            else if obj.time
-                v.push(@render_open_recopy(name, open, copy, copy_tip, open_tip))
+        if opts.enable_copy
+            if opts.data.start
+                v.push(@render_open_copying(opts.name, open, stop))
+            else if opts.data.time
+                v.push(@render_open_recopy(opts.name, open, copy, opts.copy_tip, opts.open_tip))
             else
-                v.push(@render_copy(name, copy, copy_tip))
-        if obj.time
-            v.push(@render_last_time(name, obj.time))
-        if obj.error and not opts.omit_errors
-            v.push(@render_error(name, obj.error))
+                v.push(@render_copy(opts.name, copy, opts.copy_tip))
+        if opts.data.time
+            v.push(@render_last_time(opts.name, opts.data.time))
+        if opts.data.error and not opts.omit_errors
+            v.push(@render_error(opts.name, opts.data.error))
         return v

     render_peer_assign: ->
         <Col md={2} key='peer_assign'>
-            {@render_last('Peer Assign', @props.info.last_peer_assignment, 'peer-assigned', @props.info.last_collect?,
-               "Copy collected assignments from your project to this student's project so they can grade them.",
-               "Open the student's copies of this assignment directly in their project, so you can see what they are peer grading.")}
+            {@render_last
+                name        : 'Peer Assign'
+                data        : @props.info.last_peer_assignment
+                type        : 'peer-assigned'
+                enable_copy : @props.info.last_collect?
+                copy_tip    : "Copy collected assignments from your project to this student's project so they can grade them."
+                open_tip    : "Open the student's copies of this assignment directly in their project, so you can see what they are peer grading."
+            }
         </Col>

     render_peer_collect: ->
         <Col md={2} key='peer_collect'>
-            {@render_last('Peer Collect', @props.info.last_peer_collect, 'peer-collected', @props.info.last_peer_assignment?,
-               "Copy the peer-graded assignments from various student projects back to your project so you can assign their official grade.",
-               "Open your copy of your student's peer grading work in your own project, so that you can grade their work.")}
+            {@render_last
+                name        : 'Peer Collect'
+                data        : @props.info.last_peer_collect
+                type        : 'peer-collected'
+                enable_copy : @props.info.last_peer_assignment?
+                copy_tip    : "Copy the peer-graded assignments from various student projects back to your project so you can assign their official grade."
+                open_tip    : "Open your copy of your student's peer grading work in your own project, so that you can grade their work."
+            }
         </Col>

     render: ->
@@ -360,14 +375,27 @@
             <Col md={10} key="rest">
                 <Row>
                     <Col md={width} key='last_assignment'>
-                        {@render_last('Assign', @props.info.last_assignment, 'assigned', true,
-                           "Copy the assignment from your project to this student's project so they can do their homework.",
-                           "Open the student's copy of this assignment directly in their project.  You will be able to see them type, chat with them, leave them hints, etc.", {omit_errors : skip_assignment})}
+                        {@render_last
+                            name        : 'Assign'
+                            data        : @props.info.last_assignment
+                            type        : 'assigned'
+                            enable_copy : true
+                            copy_tip    : "Copy the assignment from your project to this student's project so they can do their homework."
+                            open_tip    : "Open the student's copy of this assignment directly in their project. " +
+                                          "You will be able to see them type, chat with them, leave them hints, etc."
+                            omit_errors : skip_assignment
+                        }
                     </Col>
                     <Col md={width} key='last_collect'>
-                        {@render_last('Collect', @props.info.last_collect, 'collected', @props.info.last_assignment? or skip_assignment,
-                           "Copy the assignment from your student's project back to your project so you can grade their work.",
-                           "Open the copy of your student's work in your own project, so that you can grade their work.", {omit_errors : skip_collect}) if skip_assignment or not @props.info.last_assignment?.error}
+                        {if skip_assignment or not @props.info.last_assignment?.error then @render_last
+                                name        : 'Collect'
+                                data        : @props.info.last_collect
+                                type        : 'collected'
+                                enable_copy : @props.info.last_assignment? or skip_assignment
+                                copy_tip    : "Copy the assignment from your student's project back to your project so you can grade their work."
+                                open_tip    : "Open the copy of your student's work in your own project, so that you can grade their work."
+                                omit_errors : skip_collect
+                        }
                     </Col>
                     {@render_peer_assign()  if peer_grade and @props.info.peer_assignment and not @props.info.last_collect?.error}
                     {@render_peer_collect() if peer_grade and @props.info.peer_collect and not @props.info.peer_assignment?.error}
@@ -375,9 +403,14 @@
                         {@render_grade_col() if show_grade_col}
                     </Col>
                     <Col md={width} key='return_graded'>
-                        {@render_last('Return', @props.info.last_return_graded, 'graded', @props.info.last_collect? or skip_collect,
-                           "Copy the graded assignment back to your student's project.",
-                           "Open the copy of your student's work that you returned to them. This opens the returned assignment directly in their project.") if show_return_graded}
+                        {if show_return_graded then @render_last
+                            name        : 'Return'
+                            data        : @props.info.last_return_graded
+                            type        : 'graded'
+                            enable_copy : @props.info.last_collect? or skip_collect
+                            copy_tip    : "Copy the graded assignment back to your student's project."
+                            open_tip    : "Open the copy of your student's work that you returned to them. " +
+                                          "This opens the returned assignment directly in their project." }
                     </Col>
                 </Row>
             </Col>


Fix click here anti-pattern

Fix click here anti-pattern

--- src/smc-webapp/project_files.cjsx
+++ src/smc-webapp/project_files.cjsx
@@ -432,8 +432,8 @@
             </Col>
             <Col sm={8}>
                 <Alert style={marginTop: '5px', fontWeight : 'bold'} bsStyle='info'>
-                Warning: You are in terminal mode. Click here for more information.
-            </Alert>
+                    You are in terminal mode.
+                </Alert>
             </Col>
             <Col sm={2}>
             </Col>


FIX: Visualise achievement HD target bar use correct data

--- src/app/visualisations/achievement-custom-bar-chart.coffee
+++ src/app/visualisations/achievement-custom-bar-chart.coffee
@@ -656,7 +656,7 @@
       iloTargets[0] = { offset: 0, height: targets[ilo.id][0], color: gradeService.gradeColors.P }
       iloTargets[1] = { offset: iloTargets[0].offset + iloTargets[0].height, height: targets[ilo.id][1], color: gradeService.gradeColors.C  }
       iloTargets[2] = { offset: iloTargets[1].offset + iloTargets[1].height, height: targets[ilo.id][2], color: gradeService.gradeColors.D  }
-      iloTargets[3] = { offset: iloTargets[2].offset + iloTargets[2].height, height: targets[ilo.id][2], color: gradeService.gradeColors.HD }
+      iloTargets[3] = { offset: iloTargets[2].offset + iloTargets[2].height, height: targets[ilo.id][3], color: gradeService.gradeColors.HD }

       if iloTargets[3].offset + iloTargets[3].height  > max
         max = iloTargets[3].offset + iloTargets[3].height


Remove unneeded line of code from rebase

--- src/desktop/apps/artwork/components/commercial/view.coffee
+++ src/desktop/apps/artwork/components/commercial/view.coffee
@@ -58,9 +58,6 @@

     # Show the new buy now flow if you have the lab feature or feature flag enabled
     if sd.ENABLE_NEW_BUY_NOW_FLOW || loggedInUser?.hasLabFeature('New Buy Now Flow')
-    # Show the new buy now flow if you have the lab feature enabled
-    if loggedInUser?.hasLabFeature('New Buy Now Flow')
-
       analytics.track('Click', {
         subject: 'buy now',
         type: 'button',


Fixed spinner

Fixed spinner

--- public/dreamcatcher/controllers/comment_controller.coffee
+++ public/dreamcatcher/controllers/comment_controller.coffee
@@ -86,7 +86,8 @@
         entryUserId: entry.data("userid")
         numberToShow: numberToShow
       }
-    ).removeClass("spinner")
+    )
+    #$(".comments",entry).removeClass("spinner")

   created: (data) ->
     comment = data.comment


Add missing namespace

Add missing namespace

--- resources/assets/coffee/react/beatmapset-page/main.coffee
+++ resources/assets/coffee/react/beatmapset-page/main.coffee
@@ -178,7 +178,7 @@
     $.subscribe 'beatmapset:scoreboard:set.beatmapsetPage', @setCurrentScoreboard
     $.subscribe 'beatmapset:preview:toggle.beatmapsetPage', @togglePreviewPlayingState
     $.subscribe 'beatmapset:hoveredbeatmap:set.beatmapsetPage', @setHoveredBeatmapId
-    $.subscribe 'beatmapset:favorite:toggle', @toggleFavorite
+    $.subscribe 'beatmapset:favorite:toggle.beatmapsetPage', @toggleFavorite

     @setHash()
     @setCurrentScoreboard null, scoreboardType: 'global', resetMods: true


Disabled UI for prefer not to answer checkboxes, addresses #387

--- app/assets/javascripts/cleantheme/surveyAnimation.js.coffee
+++ app/assets/javascripts/cleantheme/surveyAnimation.js.coffee
@@ -100,8 +100,9 @@

   # Handle 'prefer not to answer checkbox'
   $('.preferred-not-to-answer').click (e) ->
-    $(this).find('input:checkbox').prop "checked", !$(this).find('input:checkbox').prop("checked")
-    handleChangedValue($(this))
+    unless $(this).find("input:checkbox").prop 'disabled'
+      $(this).find('input:checkbox').prop "checked", !$(this).find('input:checkbox').prop("checked")
+      handleChangedValue($(this))
     return

   # Respond to click events on conditional events - note that this only works on checkbox inputs


fix indentation issue

fix indentation issue

--- app/assets/javascripts/components/forum-subject-widget.cjsx
+++ app/assets/javascripts/components/forum-subject-widget.cjsx
@@ -50,41 +50,41 @@
     url = @state.connector.search_url(term)
     window.open url, '_blank'

-render: ->
-  return null if ! @state.connector?
-  create_url = @state.connector.create_url @props.subject
-  search_enabled = @state.connector.search_url()?
+  render: ->
+    return null if ! @state.connector?
+    create_url = @state.connector.create_url @props.subject
+    search_enabled = @state.connector.search_url()?

-  subject_posts = @state.posts.subject ? ( @state.posts.subject_set ? [] )
+    subject_posts = @state.posts.subject ? ( @state.posts.subject_set ? [] )

-

-    { if search_enabled
-      <form onSubmit={@handleSearchFormSubmit} method='get' action='javascript:void(0);'><input type="text" ref="search_term" placeholder="Search forum"/></form>
-    }
+

+      { if search_enabled
+        <form onSubmit={@handleSearchFormSubmit} method='get' action='javascript:void(0);'><input type="text" ref="search_term" placeholder="Search forum"/></form>
+      }

-    { if @state.loading and search_enabled
-        Searching for discussions about this {@props.project.term('subject')}...
-      else if subject_posts.length > 0
-
-          Discussion about this {@props.project.term('subject')}:
-

-          { for post,i in subject_posts
-

-              "{post.excerpt.truncate 70}"
-
– {post.author}, {moment(post.updated_at).fromNow()}
-

-          }
-

-
-    }
+      { if @state.loading and search_enabled
+          Searching for discussions about this {@props.project.term('subject')}...
+        else if subject_posts.length > 0
+
+            Discussion about this {@props.project.term('subject')}:
+

+            { for post,i in subject_posts
+

+                "{post.excerpt.truncate 70}"
+
– {post.author}, {moment(post.updated_at).fromNow()}
+

+            }
+

+
+      }

-    { if create_url?
-        Start a { if subject_posts.length > 0 then 'new' else '' } discussion about this {@props.project.term('subject set')}
-      else
-        Oops! Disscussions have not been set up for this {@props.project.term('subject set')}.
-    }
+      { if create_url?
+          Start a { if subject_posts.length > 0 then 'new' else '' } discussion about this {@props.project.term('subject set')}
+        else
+          Oops! Disscussions have not been set up for this {@props.project.term('subject set')}.
+      }

-

+

 window.React = React


adds ability to override templates

adds ability to override templates

--- app/assets/javascripts/spree_checkout/application.js.coffee
+++ app/assets/javascripts/spree_checkout/application.js.coffee
@@ -16,5 +16,19 @@

   Resolver: Ember.DefaultResolver.extend
     resolveTemplate: (parsedName) ->
-      parsedName.fullNameWithoutType = "spree_checkout/#{parsedName.fullNameWithoutType}"
+      parsedName.fullNameWithoutType = @fullName(parsedName.fullNameWithoutType)
       @_super(parsedName)
+
+    fullName: (fullNameWithoutType) ->
+      if Ember.TEMPLATES.hasOwnProperty(@overridePath(fullNameWithoutType))
+        @overridePath(fullNameWithoutType)
+      else
+        @defaultPath(fullNameWithoutType)
+
+    overridePath: (fullNameWithoutType) ->
+      "spree_checkout/overrides/#{fullNameWithoutType}"
+
+    defaultPath: (fullNameWithoutType) ->
+      "spree_checkout/#{fullNameWithoutType}"
+
+


Adjust time limit

Adjust time limit

--- app/assets/javascripts/tutor_report/report/report.js.coffee
+++ app/assets/javascripts/tutor_report/report/report.js.coffee
@@ -72,7 +72,7 @@
     data = ChartUtils.formatSingleBarChartData(res.data)
     legends = []
     data.used_time.className = 'fill-used-time-chart'
-    data.used_time.maxValue = moment.duration(90, 'days').asMilliseconds()
+    data.used_time.maxValue = adjustMaxTime(data.used_time)
     ChartUtils.addLegendValue(legends, data.used_time)
     data.total_neurons_learnt.className = 'fill-total-neurons-learnt-chart'
     ChartUtils.addLegendValue(legends, data.total_neurons_learnt)
@@ -158,4 +158,8 @@
 getParam = (name) ->
   (location.search.split(name + '=')[1] or '').split('&')[0]

+adjustMaxTime = (item) ->
+  maxTimeLimit = moment.duration(90, 'days').asMilliseconds()
+  return  if item.value > maxTimeLimit then item.maxValue else maxTimeLimit
+
 $(document).on "ready page:load", loadCharts


MainView: initial take on sidebar

MainView: initial take on sidebar

--- client/Main/maincontroller/mainviewcontroller.coffee
+++ client/Main/maincontroller/mainviewcontroller.coffee
@@ -13,36 +13,52 @@
     mainView.on 'MainTabPaneShown', (pane) =>
       @mainTabPaneChanged mainView, pane

-    appManager.on 'AppIsBeingShown', (controller)=>
+    appManager.on 'AppIsBeingShown', (controller) =>
       @setBodyClass KD.utils.slugify controller.getOption 'name'

     display.on 'ContentDisplayWantsToBeShown', do =>
       type = null
-      (view)=>
-        if type = view.getOption 'type'
-          @setBodyClass type
+      (view) => @setBodyClass type  if type = view.getOption 'type'

-    windowController.on 'ScrollHappened', do ->
-      threshold     = 50
-      lastScroll    = 0
-      currentHeight = 0
+    windowController.on 'ScrollHappened', @bound 'handleScroll'

-      _.throttle (event)->
-        el = document.body
-        {scrollHeight, scrollTop} = el

-        return  if scrollHeight <= window.innerHeight or scrollTop <= 0
+  loadView:(mainView)->

-        current = scrollTop + window.innerHeight
-        if current > scrollHeight - threshold
-          return if lastScroll > 0
-          appManager.getFrontApp()?.emit "LazyLoadThresholdReached"
-          lastScroll    = current
-          currentHeight = scrollHeight
-        else if current < lastScroll then lastScroll = 0
+    mainView.ready =>

-        if scrollHeight isnt currentHeight then lastScroll = 0
-      , 200
+      {body} = document
+      if KD.checkFlag 'super-admin'
+      then KDView.setElementClass body, 'add', 'super'
+      else KDView.setElementClass body, 'remove', 'super'
+
+
+  handleScroll: do ->
+
+    threshold     = 50
+    lastScroll    = 0
+    currentHeight = 0
+
+    _.throttle (event) ->
+
+      el = document.body
+      {scrollHeight, scrollTop} = el
+
+      return  if scrollHeight <= window.innerHeight or scrollTop <= 0
+
+      current = scrollTop + window.innerHeight
+      if current > scrollHeight - threshold
+        return if lastScroll > 0
+        appManager    = KD.singleton 'appManager'
+        appManager.getFrontApp()?.emit "LazyLoadThresholdReached"
+        lastScroll    = current
+        currentHeight = scrollHeight
+      else if current < lastScroll then lastScroll = 0
+
+      if scrollHeight isnt currentHeight then lastScroll = 0
+
+    , 200
+

   setBodyClass: do ->

@@ -55,14 +71,6 @@
       KDView.setElementClass body, 'add', name
       previousClass = name

-  loadView:(mainView)->
-
-    mainView.ready =>
-
-      {body} = document
-      if KD.checkFlag 'super-admin'
-      then KDView.setElementClass body, 'add', 'super'
-      else KDView.setElementClass body, 'remove', 'super'

   mainTabPaneChanged:(mainView, pane)->

@@ -87,6 +95,7 @@

   setViewState: (options = {}) ->

+    {dock}           = KD.singletons
     {behavior, name} = options

     html     = document.documentElement
@@ -103,5 +112,5 @@
     else KDView.setElementClass html, 'remove', 'app'

     if isApp or name in appsWithDock
-    then @getView().showDock()
-    else @getView().hideDock()
+    then dock.getView().show()
+    else dock.getView().hide()


Sidebar: minor refactor on more offscreenitems indicators

--- client/Main/maincontroller/mainviews/mainview.coffee
+++ client/Main/maincontroller/mainviews/mainview.coffee
@@ -74,19 +74,13 @@
       domId    : 'main-sidebar'
       offscreenIndicatorClassName: 'unread'

-    moreItemsAbove = new KDView
-      cssClass  : 'more-items-above'
-      partial   : "More unread items above"
-
-    moreItemsBelow = new KDView
-      cssClass  : 'more-items-below'
-      partial   : "More unread items below"
-
-    moreItemsAbove.hide()
-    moreItemsBelow.hide()
-
-    @sidebar.addSubView moreItemsAbove
-    @sidebar.addSubView moreItemsBelow
+    @sidebar.addSubView moreItemsAbove = new KDView
+      cssClass  : 'more-items above hidden'
+      partial   : 'Unread items'
+
+    @sidebar.addSubView moreItemsBelow = new KDView
+      cssClass  : 'more-items below hidden'
+      partial   : 'Unread items'

     @sidebar.wrapper.addSubView @activitySidebar = new ActivitySidebar


client: fix typo

client: fix typo

--- client/app/lib/providers/computecontroller.ui.coffee
+++ client/app/lib/providers/computecontroller.ui.coffee
@@ -383,7 +383,7 @@
           button  : 'Yes, remove'
         dontWarnMe:
           title   : 'Are you sure?'
-          message : '
By clicking Yes, You will not recieve anymore update for this stack.
'
+          message : '
By clicking Yes, You will not receive anymore update for this stack.
'
           button  : 'Yes'
         enableTeamOAuth :
           title   : 'Do you want to setup as organization token?'


MachineSettingsPopup::show terminate always for managed vms

--- client/app/lib/providers/machinesettingspopup.coffee
+++ client/app/lib/providers/machinesettingspopup.coffee
@@ -370,7 +370,7 @@

       @isPaidAccount = plan isnt 'free'

-      if plan in ['free', 'hobbyist']
+      if plan in ['free', 'hobbyist'] and not @machine.isManaged()
         @terminateButton.hide()

         if plan is 'hobbyist' and @machine.jMachine.meta?.storage_size isnt 10


Deprioritize environment prediction pattern

Signed-off-by: Sonmez Kartal <d2d15c3d37396a5f2a09f8b42cf5f27d43a3fbde@koding.com>

--- client/landing/site.landing/coffee/core/utils.coffee
+++ client/landing/site.landing/coffee/core/utils.coffee
@@ -64,12 +64,12 @@
     basePattern  = (kd.config.domains.main.split ':')[0].replace /\./g, '\\.'

     predefinedPatterns = [
-      # <team-domain>.<(dev|sandbox|latest|prod)>.koding.com
-      { predict: "(dev|sandbox|latest|prod)\.#{basePattern}" }
-
       # <team-domain>.koding.com
       # <team-domain>.<username>.koding.team
       { predict: "#{basePattern}" }
+
+      # <team-domain>.<(dev|sandbox|latest|prod)>.koding.com
+      { predict: "(dev|sandbox|latest|prod)\.#{basePattern}" }

       # <team-domain>.<IPv4>.xip.io
       # IPV4: A.B.C.D


Fix wording of code comments in mock() function.

Originally stated that mock() was a function closure, which it is, but the code does not make any use of that fact; mock() does not return a function, nor does it pass a function to a handler somewhere.

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -75,8 +75,8 @@
 #     sut.do_something_interesting()                    # run whatever it is we want to test
 #                                                       # end scope: checks expectations, removes expects(), restores original writeFileSync()
 #
-# mock() is a function closure.  Variables are defined first, followed
-# by support functions, and finally the body of the function itself.
+# mock() consists of a number of variable declarations, function
+# definitions, and finally the mock() function body itself.
 #
 # mock() is exported at the end of this file.
 #


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/animated.coffee
+++ src/animated.coffee
@@ -175,7 +175,4 @@
           triggers.each (event) ->
             self.trigger(event)

-        if transition = I.activeAnimation.transition?[I.currentFrameIndex]
-          I.transform = eval(transition)
-
         advanceFrame()


disabling pointer events optimization

disabling pointer events optimization

Former-commit-id: 280d1dde078b0ec0696025a3e55f6df70478e5b4 [formerly 5b5d604e67d7bb5e37bf7aeeec09a1da8a5a01e2] [formerly df41a6786fc5ebaa76d0fe0980505b2b94a3f699 [formerly df41a6786fc5ebaa76d0fe0980505b2b94a3f699 [formerly 5f7c9901fb24ec4773b1892bda7f17d1ea854e79]]]
Former-commit-id: 9643cfe37d7e9a6bf81e57fd96b391dc21b05646 [formerly 011765959942fbdddbff16cfa6a0b337e080638c]
Former-commit-id: 49f4125715f2a994cec626af3bc7861492e28cf7
Former-commit-id: 001f510346ee52bec84914f04cccff4aa75f2c15

--- app/assets/javascripts/backbone/config/javascript.js.coffee
+++ app/assets/javascripts/backbone/config/javascript.js.coffee
@@ -80,15 +80,15 @@

   #adapted from http://www.thecssninja.com/javascript/pointer-events-60fps
-  $body = $('body')
-  timer = null
-  $(window).scroll ->
-    clearTimeout timer
-    if document.body.style.pointerEvents != 'none'
-      document.body.style.pointerEvents = 'none'
+  # $body = $('body')
+  # timer = null
+  # $(window).scroll ->
+  #   clearTimeout timer
+  #   if document.body.style.pointerEvents != 'none'
+  #     document.body.style.pointerEvents = 'none'

-    timer = setTimeout ->
-      document.body.style.pointerEvents = ''
-    , 200
+  #   timer = setTimeout ->
+  #     document.body.style.pointerEvents = ''
+  #   , 200


#133 Undo debug output

#133 Undo debug output

--- app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
+++ app/assets/javascripts/oxalis/model/binary/pullqueue.coffee
@@ -190,7 +190,7 @@
       senders : [
         # new ArrayBufferSocket.WebWorker("ws://#{document.location.host}/binary/ws?dataSetName=#{@dataSetName}&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
         # new ArrayBufferSocket.WebSocket("ws://#{document.location.host}/binary/ws?dataSetName=#{@dataSetName}&cubeSize=#{1 << @cube.BUCKET_SIZE_P}")
-        new ArrayBufferSocket.XmlHttpRequest("/datasets/#{@dataSetName}/layers/#{@dataLayerName}/data?cubeSize=#{1 << @cube.BUCKET_SIZE_P}&annotationTyp=#{@tracingType}&annotationId=#{@tracingId}&from=pull")
+        new ArrayBufferSocket.XmlHttpRequest("/datasets/#{@dataSetName}/layers/#{@dataLayerName}/data?cubeSize=#{1 << @cube.BUCKET_SIZE_P}&annotationTyp=#{@tracingType}&annotationId=#{@tracingId}")
       ]
       requestBufferType : Float32Array
       responseBufferType : Uint8Array


GameObject: Add get_world_X/Y/Z_vector, get_dimensions, get_size, set_size.

--- engine/gameobject.coffee
+++ engine/gameobject.coffee
@@ -449,4 +449,31 @@
         @clear_parent()
         bone.parent_object = this

+    get_world_X_vector: ->
+        wm = @get_world_matrix()
+        return vec3.new wm.m00, wm.m01, wm.m02
+
+    get_world_Y_vector: ->
+        wm = @get_world_matrix()
+        return vec3.new wm.m04, wm.m05, wm.m06
+
+    get_world_Z_vector: ->
+        wm = @get_world_matrix()
+        return vec3.new wm.m08, wm.m09, wm.m10
+
+    get_dimensions: ->
+        dimensions = vec3.clone @bound_box[1]
+        vec3.sub dimensions, dimensions, @bound_box[0]
+        vec3.mul dimensions, dimensions, @scale
+        return dimensions
+
+    get_size: ->
+        d = @get_dimensions()
+        return Math.max(d.x,d.y,d.z)
+
+    set_size: (size)->
+        vec3.scale @scale, @scale, size/@get_size()
+
+
+
 module.exports = {GameObject}


fix test

fix test

--- apps/artwork/components/artists/test/templates/index.coffee
+++ apps/artwork/components/artists/test/templates/index.coffee
@@ -54,5 +54,5 @@

   it 'should display biography', ->
     @$('.side-tabs__nav').find('a[data-id=biography]').attr('data-id').should.equal 'biography'
-    @$('.artwork-artist__content__biography').html().should.equal "He is from Compton"
+    @$('.artwork-artist__content__biography').html().should.containEql "He is from Compton"
     @$('.artwork-artist__content__short-bio').html().should.containEql "We do not know"


fix possible null bug

fix possible null bug

--- src/selection.coffee
+++ src/selection.coffee
@@ -79,11 +79,11 @@
     return if nativeRange? then _nativeRangeToRange.call(this, nativeRange) else null

   preserve: (index, lengthAdded, fn) ->
+    fn = index if _.isFunction(index)
     nativeRange = this.getNativeRange()
     if nativeRange?
       if _.isFunction(index)
-        fn = index
-        _preserveWithLine.call(this, nativeRange, fn)
+        _preserveWithLine.call(this, nativeRange, index)
       else
         _preserveWithIndex.call(this, nativeRange, index, lengthAdded, fn)
     else