Remove ! from url.

Remove ! from url.

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -48,7 +48,7 @@
             {templateUrl: 'partials/wiki.html', controller: WikiController})

     $routeProvider.otherwise({redirectTo: '/login'})
-    $locationProvider.hashPrefix('!')
+    #$locationProvider.hashPrefix('!')

     defaultHeaders =
         "Content-Type": "application/json",
@@ -121,31 +121,31 @@

     $rootScope.urls =
         projectsUrl: ->
-            return '/#!/'
+            return '/#/'

         backlogUrl: (projectId) ->
-            return _.str.sprintf("/#!/project/%s/backlog", projectId)
+            return _.str.sprintf("/#/project/%s/backlog", projectId)

         dashboardUrl: (projectId, sprintId) ->
-            return _.str.sprintf("/#!/project/%s/dashboard/%s", projectId, sprintId)
+            return _.str.sprintf("/#/project/%s/dashboard/%s", projectId, sprintId)

         issuesUrl: (projectId, issueId) ->
             if issueId != undefined
-                return _.str.sprintf("/#!/project/%s/issues/%s", projectId, issueId)
+                return _.str.sprintf("/#/project/%s/issues/%s", projectId, issueId)
             else
-                return _.str.sprintf("/#!/project/%s/issues", projectId)
+                return _.str.sprintf("/#/project/%s/issues", projectId)

         tasksUrl: (projectId, taskId) ->
-            return _.str.sprintf("/#!/project/%s/tasks/%s", projectId, taskId)
+            return _.str.sprintf("/#/project/%s/tasks/%s", projectId, taskId)

         questionsUrl: (projectId, issueId) ->
             if issueId is undefined
-                return _.str.sprintf("/#!/project/%s/questions/%s", projectId, issueId)
+                return _.str.sprintf("/#/project/%s/questions/%s", projectId, issueId)
             else
-                return _.str.sprintf("/#!/project/%s/questions", projectId)
+                return _.str.sprintf("/#/project/%s/questions", projectId)

         wikiUrl: (projectId, pageName) ->
-            return _.str.sprintf("/#!/project/%s/wiki/%s", projectId, _.str.slugify(pageName))
+            return _.str.sprintf("/#/project/%s/wiki/%s", projectId, _.str.slugify(pageName))

     $rootScope.logout = () ->
         storage.clear()


Remove trailing whitespace for jshint

Remove trailing whitespace for jshint

--- app/coffee/app.coffee
+++ app/coffee/app.coffee
@@ -1,6 +1,6 @@
 angular
    .module("angularTodoList", ["ui.router"])
-   .config(['$stateProvider', '$urlMatcherFactoryProvider',
+   .config(['$stateProvider', '$urlMatcherFactoryProvider',
       ($stateProvider, $urlMatcherFactoryProvider) ->

          $urlMatcherFactoryProvider.strictMode(false)


remove debug code from spec

remove debug code from spec

--- spec/operators-spec.coffee
+++ spec/operators-spec.coffee
@@ -202,7 +202,6 @@
         keydown('y')

       it "is in linewise motion", ->
-        console.log("CK", vimState.getRegister('"').type)
         expect(vimState.getRegister('"').type).toEqual "linewise"

       it "saves the lines to the default register", ->


Use Atom::replicate to obtain second project

--- spec/root-view-spec.coffee
+++ spec/root-view-spec.coffee
@@ -20,10 +20,11 @@

     refreshRootViewAndProject = ->
       rootViewState = rootView.serialize()
-      projectState = project.serialize()
+      project.getState().serializeForPersistence()
+      project2 = atom.replicate().get('project')
       rootView.remove()
       project.destroy()
-      window.project = deserialize(projectState)
+      window.project = project2
       window.rootView = deserialize(rootViewState)
       rootView.attachToDom()


Fix tests for double-clicking files

Fix tests for double-clicking files

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -588,12 +588,14 @@

         it "terminates pending state on second click", ->
           sampleJs.trigger clickEvent(originalEvent: {detail: 2})
-          expect(activePaneItem.isPending()).toBe false
+          waitsFor ->
+            activePaneItem.isPending() is false

         it "does not create pending state on subsequent single click", ->
           sampleJs.trigger clickEvent(originalEvent: {detail: 2})
           sampleJs.trigger clickEvent(originalEvent: {detail: 1})
-          expect(activePaneItem.isPending()).toBe false
+          waitsFor ->
+            activePaneItem.isPending() is false

       describe "when a file is single-clicked, then double-clicked", ->
         activePaneItem = null
@@ -611,15 +613,12 @@
           expect(activePaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')
           expect(activePaneItem.isPending()).toBe true

-        it "terminates pending state on the double-click and have focus", ->
+        it "terminates pending state on the double-click and focuses file", ->
           sampleJs.trigger clickEvent(originalEvent: {detail: 1})
           sampleJs.trigger clickEvent(originalEvent: {detail: 2})
           expect(atom.views.getView(activePaneItem)).toHaveFocus()
-          expect(activePaneItem.isPending()).toBe false
-
-          sampleJs.trigger 'dblclick'
-          expect(atom.views.getView(activePaneItem)).toHaveFocus()
-          expect(activePaneItem.isPending()).toBe false
+          waitsFor ->
+            activePaneItem.isPending() is false

   describe "when a directory is single-clicked", ->
     it "is selected", ->


Rename isDirectory() to isDirectorySync()

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -680,7 +680,7 @@
               addDialog.miniEditor.insertText("new/dir/")
               addDialog.trigger 'core:confirm'
               expect(fsUtils.exists(newPath)).toBeTruthy()
-              expect(fsUtils.isDirectory(newPath)).toBeTruthy()
+              expect(fsUtils.isDirectorySync(newPath)).toBeTruthy()
               expect(addDialog.parent()).not.toExist()
               expect(rootView.getActiveView().getPath()).not.toBe newPath
               expect(treeView.find(".tree-view")).toMatchSelector(':focus')
@@ -693,7 +693,7 @@
               addDialog.miniEditor.insertText("new2/")
               addDialog.trigger 'core:confirm'
               expect(fsUtils.exists(newPath)).toBeTruthy()
-              expect(fsUtils.isDirectory(newPath)).toBeTruthy()
+              expect(fsUtils.isDirectorySync(newPath)).toBeTruthy()
               expect(addDialog.parent()).not.toExist()
               expect(rootView.getActiveView().getPath()).not.toBe newPath
               expect(treeView.find(".tree-view")).toMatchSelector(':focus')


Unfocus spec

Unfocus spec

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -3,7 +3,7 @@
 Workspace = require '../src/workspace'
 {View} = require '../src/space-pen-extensions'

-fdescribe "Workspace", ->
+describe "Workspace", ->
   workspace = null

   beforeEach ->


Remove calls to openSync from the workspace-spec

--- spec/workspace-spec.coffee
+++ spec/workspace-spec.coffee
@@ -1,6 +1,6 @@
 Workspace = require '../src/workspace'

-describe "Workspace", ->
+fdescribe "Workspace", ->
   workspace = null

   beforeEach ->
@@ -37,12 +37,17 @@
       describe "when called with a uri", ->
         describe "when the active pane already has an editor for the given uri", ->
           it "activates the existing editor on the active pane", ->
-            editor1 = workspace.openSync('a')
-            editor2 = workspace.openSync('b')
-
             editor = null
+            editor1 = null
+            editor2 = null
+
             waitsForPromise ->
-              workspace.open('a').then (o) -> editor = o
+              workspace.open('a').then (o) ->
+                editor1 = o
+                workspace.open('b').then (o) ->
+                  editor2 = o
+                  workspace.open('a').then (o) ->
+                    editor = o

             runs ->
               expect(editor).toBe editor1
@@ -63,12 +68,22 @@

     describe "when the 'searchAllPanes' option is true", ->
       describe "when an editor for the given uri is already open on an inactive pane", ->
-        it "activates the existing editor on the inactive pane, then activates that pane", ->
-          editor1 = workspace.openSync('a')
-          pane1 = workspace.activePane
-          pane2 = workspace.activePane.splitRight()
-          editor2 = workspace.openSync('b')
-          expect(workspace.activePaneItem).toBe editor2
+        fit "activates the existing editor on the inactive pane, then activates that pane", ->
+          editor1 = null
+          editor2 = null
+          pane1 = workspace.getActivePane()
+          pane2 = workspace.getActivePane().splitRight()
+
+          waitsForPromise ->
+            pane1.activate()
+            workspace.open('a').then (o) -> editor1 = o
+
+          waitsForPromise ->
+            pane2.activate()
+            workspace.open('b').then (o) -> editor2 = o
+
+          runs ->
+            expect(workspace.activePaneItem).toBe editor2

           waitsForPromise ->
             workspace.open('a', searchAllPanes: true)
@@ -164,80 +179,43 @@
       runs ->
         expect(newEditorHandler).toHaveBeenCalledWith editor

-  describe "::openSync(uri, options)", ->
-    [activePane, initialItemCount] = []
-
-    beforeEach ->
-      activePane = workspace.activePane
-      spyOn(activePane, 'activate')
-      initialItemCount = activePane.items.length
-
-    describe "when called without a uri", ->
-      it "adds and activates an empty editor on the active pane", ->
-        editor = workspace.openSync()
-        expect(activePane.items.length).toBe initialItemCount + 1
-        expect(activePane.activeItem).toBe editor
-        expect(editor.getPath()).toBeUndefined()
-        expect(activePane.activate).toHaveBeenCalled()
-
-    describe "when called with a uri", ->
-      describe "when the active pane already has an editor for the given uri", ->
-        it "activates the existing editor on the active pane", ->
-          editor1 = workspace.openSync('a')
-          editor2 = workspace.openSync('b')
-          expect(activePane.activeItem).toBe editor2
-          expect(activePane.items.length).toBe 2
-
-          editor = workspace.openSync(editor1.getPath())
-          expect(editor).toBe editor1
-          expect(activePane.activeItem).toBe editor
-          expect(activePane.activate).toHaveBeenCalled()
-          expect(activePane.items.length).toBe 2
-
-      describe "when the active pane does not have an editor for the given uri", ->
-        it "adds and activates a new editor for the given path on the active pane", ->
-          editor = workspace.openSync('a')
-          expect(activePane.items.length).toBe 1
-          expect(activePane.activeItem).toBe editor
-          expect(activePane.activate).toHaveBeenCalled()
-
-    describe "when the 'activatePane' option is false", ->
-      it "does not activate the active pane", ->
-        workspace.openSync('b', activatePane: false)
-        expect(activePane.activate).not.toHaveBeenCalled()
-
   describe "::reopenItemSync()", ->
     it "opens the uri associated with the last closed pane that isn't currently open", ->
       pane = workspace.activePane
-      workspace.openSync('a')
-      workspace.openSync('b')
-      workspace.openSync('file1')
-      workspace.openSync()
-
-      # does not reopen items with no uri
-      expect(workspace.activePaneItem.getUri()).toBeUndefined()
-      pane.destroyActiveItem()
-      workspace.reopenItemSync()
-      expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
-
-      # destroy all items
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
-      pane.destroyActiveItem()
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
-      pane.destroyActiveItem()
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
-      pane.destroyActiveItem()
-
-      # reopens items with uris
-      expect(workspace.activePaneItem).toBeUndefined()
-      workspace.reopenItemSync()
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
+      waitsForPromise ->
+        workspace.open('a').then ->
+          workspace.open('b').then ->
+            workspace.open('file1').then ->
+              workspace.open()
+
+      runs ->
+        # does not reopen items with no uri
+        expect(workspace.activePaneItem.getUri()).toBeUndefined()
+        pane.destroyActiveItem()
+        workspace.reopenItemSync()
+        expect(workspace.activePaneItem.getUri()).not.toBeUndefined()
+
+        # destroy all items
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
+        pane.destroyActiveItem()
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
+        pane.destroyActiveItem()
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')
+        pane.destroyActiveItem()
+
+        # reopens items with uris
+        expect(workspace.activePaneItem).toBeUndefined()
+        workspace.reopenItemSync()
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a')

       # does not reopen items that are already open
-      workspace.openSync('b')
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
-      workspace.reopenItemSync()
-      expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')
+      waitsForPromise ->
+        workspace.open('b')
+
+      runs ->
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b')
+        workspace.reopenItemSync()
+        expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1')

   describe "::increase/decreaseFontSize()", ->
     it "increases/decreases the font size without going below 1", ->
@@ -272,11 +250,14 @@

   describe "when an editor is copied", ->
     it "emits an 'editor-created' event", ->
+      editor = null
       handler = jasmine.createSpy('editorCreatedHandler')
       workspace.on 'editor-created', handler

-      editor1 = workspace.openSync("a")
-      expect(handler.callCount).toBe 1
-
-      editor2 = editor1.copy()
-      expect(handler.callCount).toBe 2
+      waitsForPromise ->
+        workspace.open("a").then (o) -> editor = o
+
+      runs ->
+        expect(handler.callCount).toBe 1
+        editorCopy = editor.copy()
+        expect(handler.callCount).toBe 2


fix null url exceptions by preventing full page navigation on null, introduce page:error for error cases

--- lib/assets/javascripts/turbograft/turbolinks.coffee
+++ lib/assets/javascripts/turbograft/turbolinks.coffee
@@ -150,7 +150,8 @@
           Turbolinks.fullPageNavigate(url.absolute)
       )
     else
-      Turbolinks.fullPageNavigate(url.absolute)
+      triggerEvent 'page:error', xhr
+      Turbolinks.fullPageNavigate(url.absolute) if url?

   changePage = (title, body, csrfToken, runScripts, options = {}) ->
     document.title = title if title


whitespace

whitespace

--- js/jack-in-the-box.coffee
+++ js/jack-in-the-box.coffee
@@ -77,4 +77,4 @@
     this.each ->
       if $( this ).data( 'jackInTheBox' ) is undefined
         plugin = new $.jackInTheBox( this, options )
-        $( this).data( 'jackInTheBox', plugin )
+        $( this ).data( 'jackInTheBox', plugin )


make default wifi-connect ssid boombeastic-ish

--- app/src/config.coffee
+++ app/src/config.coffee
@@ -1,5 +1,5 @@
 module.exports =
-	ssid: process.env.PORTAL_SSID or 'ResinAP'
+	ssid: process.env.PORTAL_SSID or 'BoomBeastic-' + process.env.HOSTNAME
 	passphrase: process.env.PORTAL_PASSPHRASE
 	iface: process.env.PORTAL_INTERFACE or 'wlan0'
 	gateway: process.env.PORTAL_GATEWAY or '192.168.42.1'


Add '...' to sliced keys in namespace/shard

--- admin/static/coffee/namespaces/shards.coffee
+++ admin/static/coffee/namespaces/shards.coffee
@@ -241,7 +241,8 @@
                             if typeof keys[i] is 'number'
                                 keys[i] = keys[i].toString()
                             if typeof keys[i] is 'string'
-                                keys[i] =keys[i].slice 0, 7
+                                if keys[i].length > 7
+                                    keys[i] =keys[i].slice(0, 7)+'...'

                         result = 'Shard: '
                         result += '[ '+keys[0]+', '+keys[1]+']'


Replace .attr("value", val) with .val(val)

Looks like this has changed behviour in the later version of jquery
we now use https://github.com/jquery/jquery-migrate/blob/master/warnings.md#jqmigrate-jqueryfnattrvalue-val-no-longer-sets-properties

--- app/assets/javascripts/admin/admin.js.coffee
+++ app/assets/javascripts/admin/admin.js.coffee
@@ -12,13 +12,13 @@
         $('#request_hidden_user_subject, #request_hidden_user_explanation, #request_hide_button').show()
         info_request_id = $('#hide_request_form').attr('data-info-request-id')
         reason = $(this).val()
-        $('#request_hidden_user_explanation_field').attr("value", "[loading default text...]")
+        $('#request_hidden_user_explanation_field').val("[loading default text...]")
         $.ajax "/hidden_user_explanation?reason=" + reason + "&info_request_id=" + info_request_id,
           type: "GET"
           dataType: "text"
           error: (data, textStatus, jqXHR) ->
-            $('#request_hidden_user_explanation_field').attr("value", "Error: #{textStatus}")
+            $('#request_hidden_user_explanation_field').val("Error: #{textStatus}")
           success: (data, textStatus, jqXHR) ->
-            $('#request_hidden_user_explanation_field').attr("value", data)
+            $('#request_hidden_user_explanation_field').val(data)
       )


Remove modernizr from app asset precompilation

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -7,7 +7,6 @@
 #= require jquery
 #= require jquery_ujs
 #= require foundation
-#= require modernizr-2.6.2.min
 #= require_tree .

 $ ->


Remove references to ember dependencies

Remove references to ember dependencies

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -12,14 +12,5 @@

 #= require jquery
 #= require jquery_ujs
-#= require ember
-#= require ember-data
-#= require csrf
-#= require zeroclipboard
-#= require moment
 #= require_self
-#= require review
-
-# for more details see: http://emberjs.com/guides/application/
-window.Review = Ember.Application.create(LOG_TRANSITIONS: true)
 #= require_tree .


wizard fixes

wizard fixes

--- app/assets/javascripts/pages/websites.coffee
+++ app/assets/javascripts/pages/websites.coffee
@@ -34,25 +34,28 @@
   if n != 0
     $('.stepwizard-row a').removeClass('btn-primary')
     $('.stepwizard-row a').addClass('btn-default')
+    $('.tab'+n).attr('disabled', false)
     $('.stepwizard a[href="#step-' + n + '"]').tab 'show'
     $('.stepwizard-row a[href="#step-' + n + '"]').removeClass 'btn-default'
     $('.stepwizard-row a[href="#step-' + n + '"]').addClass 'btn-primary'
   return

 $('.tab').hide()
-# $('.tab2, .tab3').addClass('disabled')
+$('.tab2, .tab3').addClass('disabled')

-$('#back').on 'click', () ->
+$('#back, .tab2').on 'click', () ->
   $('.tab').hide()
+  $('.tab3').addClass('disabled')
   goToStep(2)

 $('#finish').on 'click', () ->
   location.href = '/errors'

-$('#platform a').on 'click', (e) ->
+$('#platform a, .tab3').on 'click', (e) ->
   $('.tab').hide()
   $('#'+this.name).show()
   goToStep(3)
+  $('.tab3').removeClass('disabled')

 $('#modalWebsite').submit (e) ->


update place service

update place service

--- app/src/modules/places/places.service.coffee
+++ app/src/modules/places/places.service.coffee
@@ -43,3 +43,27 @@
                 console.log err
                 deferred.reject(err)
         deferred.promise
+
+    getOpeningHours: (id) ->
+        deferred = $q.defer()
+        if _.isNaN(id) || _.isUndefined(id)
+            deferred.resolve({})
+        else
+            Restangular.one('places', id).getList('opening_hours').then (result) ->
+                deferred.resolve(result)
+            , (error) ->
+                AlertsServ.logError(error)
+                deferred.reject(error)
+        deferred.promise
+
+    getAddress: (id) ->
+        deferred = $q.defer()
+        if _.isNaN(id) || _.isUndefined(id)
+            deferred.resolve({})
+        else
+            Restangular.one('places', id).customGET('address').then (result) ->
+                deferred.resolve(result)
+            , (error) ->
+                AlertsServ.logError(error)
+                deferred.reject(error)
+        deferred.promise


Timing improvement for subgraphs

Timing improvement for subgraphs

--- src/components/Graph.coffee
+++ src/components/Graph.coffee
@@ -55,8 +55,9 @@
         return false

     setToReady: ->
-        @ready = true
-        @emit "ready"
+        process.nextTick =>
+            @ready = true
+            @emit "ready"

     findEdgePorts: (name, process) ->
         for portName, port of process.component.inPorts


- added multiline injectors

- added multiline injectors

--- src/crystal/generate.coffee
+++ src/crystal/generate.coffee
@@ -254,7 +254,10 @@
 					injector = injector.replace /[\s]+/g, ''
 					injector = injector.substr 3, injector.length-6
 					if injectors and injectors[injector]
-						injected = fs.readFileSync ".crystal/#{injectors[injector]}", 'utf8'
+						if injectors[injector] instanceof Array
+							injected = injectors[injector].join "\n"
+						else
+							injected = fs.readFileSync ".crystal/#{injectors[injector]}", 'utf8'
 						inject = ''
 						for inj in injected.split "\n"
 							inject += "#{injector_tabs}#{inj}\n"


update route template

update route template

--- lib/generators/templates/route.js.coffee
+++ lib/generators/templates/route.js.coffee
@@ -1,2 +1,3 @@
-<%= application_name.camelize %>.<%= class_name.camelize %>Route = Ember.Route.extend
-  # model: (params)->
+<%= application_name.camelize %>.<%= class_name.camelize %>Route = Ember.Route.extend({
+
+})


Switch products to abstract

Switch products to abstract

--- src/features/product.coffee
+++ src/features/product.coffee
@@ -1,33 +1,15 @@
-	class Product
+	class Product extends Abstract

-		constructor: (@m) ->
-
-		Get: (id, callback, error) ->
-
-			return @m.Request 'products/'+id, 'GET', null, callback, error
-
-		Find: (terms, callback, error) ->
-
-			return @m.Request 'products', 'GET', terms, callback, error
-
-		List: (terms, callback, error) ->
-
-			return @m.Request 'products', 'GET', terms, callback, error
+		endpoint: 'products'

 		Search: (terms, callback, error) ->

-			return @m.Request 'products/search', 'GET', terms, callback, error
-
-		Fields: (id = 0, callback, error) ->
-
-			uri  = 'products/'+ if id != 0 then id+'/fields' else 'fields'
-
-			return @m.Request uri, 'GET', null, callback, error
+			return @m.Request @endpoint+'/search', 'GET', terms, callback, error

 		Modifiers: (id, callback, error) ->

-			return @m.Request 'products/'+id+'/modifiers', 'GET', null, callback, error
+			return @m.Request @endpoint+'/'+id+'/modifiers', 'GET', null, callback, error

 		Variations: (id, callback, error) ->

-			return @m.Request 'products/'+id+'/variations', 'GET', null, callback, error
+			return @m.Request @endpoint+'/'+id+'/variations', 'GET', null, callback, error


修复BUG

修复BUG

--- lib/rapid-dev/rapid-dev-mode-view.coffee
+++ lib/rapid-dev/rapid-dev-mode-view.coffee
@@ -34,12 +34,12 @@
     @projectInfos = {}

     @addProjectListItem path for path in @findProject()
-    @addProjectListItem
     @on 'click', '.settingsItem', (e) =>
       @menuClick(e.currentTarget)

     @codePackList.on 'click', '.editModule', (e) =>
       @openBuilder(e)
+

   attached: ->
     console.log 'start'
@@ -87,7 +87,6 @@
       liStr = "
#{config.name}
"
       @projectInfos[config.identifier] = config
       @other.before liStr
-      @toggleAddBtn()

   addModuleItem: (projectPath,modules) ->
     htmlStr = ''
@@ -108,6 +107,7 @@
     projectID = target.dataset.id
     config = @projectInfos[projectID]
     console.log projectPath
+    @toggleAddBtn()
     @codePackList.empty()
     @addModuleItem projectPath,config.modules


Remove ace-adaptor file. It is no longer used

--- src/app/ace-adaptor.coffee
+++ src/app/ace-adaptor.coffee
@@ -1,28 +0,0 @@
-Range = require 'range'
-
-module.exports =
-class AceAdaptor
-  foldWidgets: {}
-
-  constructor: (@editSession) ->
-    @buffer = @editSession.buffer
-
-  getLine: (bufferRow) ->
-    @buffer.lineForRow(bufferRow)
-
-  getLength: ->
-    @buffer.getLineCount()
-
-  $findClosingBracket: (bracketType, bufferPosition) ->
-    @editSession.tokenizedBuffer.findClosingBracket([bufferPosition.row, bufferPosition.column - 1])
-
-  indentRows: (startRow, endRow, indentString) ->
-    for row in [startRow..endRow]
-      @buffer.insert([row, 0], indentString)
-
-  replace: (range, text) ->
-    range = Range.fromObject(range)
-    @buffer.change(range, text)
-
-  findMatchingBracket: ({row, column}) ->
-    @editSession.tokenizedBuffer.findOpeningBracket([row, column])


[modify] app configuration to use html sanitize

--- src/app/index.route.coffee
+++ src/app/index.route.coffee
@@ -1,6 +1,8 @@
 angular.module 'clientManager'
-  .config ($stateProvider, $urlRouterProvider) ->
+  .config ($stateProvider, $urlRouterProvider, $translateProvider) ->
     'ngInject'
+    $translateProvider.useSanitizeValueStrategy 'sanitize'
+
     $stateProvider
       .state 'wr',
         url: ''
@@ -17,4 +19,4 @@
             controllerAs: 'main'

     $urlRouterProvider.when '', '/home'
-    $urlRouterProvider.otherwise '/home'
+    $urlRouterProvider.otherwise '/home'


Equation Builder tweaks, adds equation checking support

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -499,6 +499,48 @@
       new_exp = @manip.build_square_root().perform(exp)
       expect(new_exp.expression().last().value()).toEqual '2'

+
+  describe "substituting variables", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_substitute_variables(variables: [{name: "funky", value: "10"}])
+      expression_for_performance: ->
+        @exp_pos_builder({variable: "funky"})
+
+    it "substitutes variables", ->
+      exp_pos = @exp_pos_builder({variable: "funky"})
+      manip = @manip.build_substitute_variables(variables: [{name: "funky", value: "10"}])
+      new_exp = manip.perform(exp_pos)
+      expect(new_exp.expression()).toBeAnEqualExpressionTo(@exp_pos_builder(10).expression())
+
+  describe "getting different sides of an equation", ->
+    describe "getting left side", ->
+      it_plays_manipulation_role
+        subject: ->
+          @manip.build_get_left_side()
+        expression_for_performance: ->
+          @exp_pos_builder({variable: "funky"}, '=', {variable: 'chunky'})
+
+      it "returns left sides of equation", ->
+        expr = @exp_pos_builder({variable: "funky"}, '=', {variable: 'chunky'})
+        new_exp = @manip.build_get_left_side().perform(expr)
+        expected = @exp_builder({variable: 'funky'})
+        expect(new_exp.expression()).toBeAnEqualExpressionTo(expected)
+
+    describe "getting right side", ->
+      it_plays_manipulation_role
+        subject: ->
+          @manip.build_get_right_side()
+        expression_for_performance: ->
+          @exp_pos_builder({variable: "funky"}, '=', {variable: 'chunky'})
+
+      it "returns right side of equation", ->
+        expr = @exp_pos_builder({variable: "funky"}, '=', {variable: 'chunky'})
+        new_exp = @manip.build_get_right_side().perform(expr)
+        expected = @exp_builder({variable: 'chunky'})
+        expect(new_exp.expression()).toBeAnEqualExpressionTo(expected)
+
+
   describe "utilities", ->
     describe "implicit multiplication", ->
       it "should have tests that go here", ->


rename "numerator denominator" to "fraction"

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -482,16 +482,16 @@
       expect(new_exp.last(1)).toBeInstanceOf @components.classes.multiplication

-  describe "appending numerator and denominator fraction", ->
-    it_plays_manipulation_role
-      subject: ->
-        @manip.build_append_numerator_denominator()
+  describe "appending a fraction", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_fraction()
       expression_for_performance: ->
         @exp_pos_builder()

     it "adds a numerator/denominator where we expect", ->
       exp = @exp_pos_builder(1)
-      new_exp = @manip.build_append_numerator_denominator().perform(exp).expression()
+      new_exp = @manip.build_append_fraction().perform(exp).expression()

       expect(new_exp.last()).toBeInstanceOf @components.classes.fraction
       expect(new_exp.last().numerator()).toBeInstanceOf @components.classes.expression


Minor fixes

Minor fixes

--- files.coffee
+++ files.coffee
@@ -782,9 +782,8 @@
                   response.end()

                 else
-
+                  opts           = JSON.parse body
                   console.info "[FilesCollection] [File Start HTTP] #{opts.file.name} - #{opts.fileId}" if self.debug
-                  opts           = JSON.parse body
                   opts.file.meta = fixJSONParse opts.file.meta if opts?.file?.meta
                   {result}       = self._prepareUpload _.clone(opts), @userId, 'Start Method'
                   opts._id       = opts.fileId
@@ -1559,7 +1558,7 @@
         unless @config.isBase64
           @fileData =
             size: @config.file.size
-            type: @config.file.type
+            type: @config.type or @config.file.type
             name: @config.fileName or @config.file.name
             meta: @config.meta


plot -> newPlot

plot -> newPlot

--- src/plotly_renderers.coffee
+++ src/plotly_renderers.coffee
@@ -104,7 +104,7 @@

         renderArea = $("<div>", style: "display:none;").appendTo $("body")
         result = $("<div>").appendTo renderArea
-        Plotly.plot(result[0], [data], $.extend(layout, opts.plotly))
+        Plotly.newPlot(result[0], [data], $.extend(layout, opts.plotly))
         result.detach()
         renderArea.remove()
         return result


add nonfunctional edit button to each user row

--- src/component/users.coffee
+++ src/component/users.coffee
@@ -105,11 +105,20 @@
                   k.td -> k.a {href: url}, user.email
                   k.td -> k.a {href: url}, user.created_at
                   k.td ->
-                    k.a {
-                      className: 'btn btn-default'
-                      onClick: (event) ->
-                        event.preventDefault()
-                        event.stopPropagation()
-                        console.log "delete user #{user.id}"
-                    }, ->
-                      k.span {className: 'glyphicon glyphicon-trash'}
+                    k.div {className: 'btn-group'}, ->
+                      k.a {
+                        className: 'btn btn-default'
+                        onClick: (event) ->
+                          event.preventDefault()
+                          event.stopPropagation()
+                          console.log "update user #{user.id}"
+                      }, ->
+                        k.span {className: 'glyphicon glyphicon-pencil'}
+                      k.a {
+                        className: 'btn btn-default'
+                        onClick: (event) ->
+                          event.preventDefault()
+                          event.stopPropagation()
+                          console.log "delete user #{user.id}"
+                      }, ->
+                        k.span {className: 'glyphicon glyphicon-trash'}


Add ability to close tabs

Add ability to close tabs

--- src/components/header.cjsx
+++ src/components/header.cjsx
@@ -6,6 +6,8 @@
     menuItems: React.PropTypes.array

   constructor: (@props) ->
+    @state =
+      menuItems: @props.menuItems

   render: =>

@@ -38,12 +40,21 @@
     else return

   _renderMenuItems: =>
-    @props.menuItems.map (item, index) ->
+    @state.menuItems.map (item, index) =>
       classnames = if index is 0 then 'active' else ''

         {item.name}
-
+
+

+  _handleCloseItem: (e) =>
+    index = parseInt(e.target.value, 10)
+
+    @setState (state) ->
+      state.menuItems.splice(index, 1)
+
+      menuItems: state.menuItems
+
 module.exports = Header


Remove Unnecessary "console.log"s

Remove Unnecessary "console.log"s

--- src/ship/RoomManager.coffee
+++ src/ship/RoomManager.coffee
@@ -76,10 +76,7 @@
         forcePoint = @ship.gridToWorld(hole)
         @ship.body.applyForce(force, forcePoint)

-        console.log "flow #{flow}, applying force #{force} in direction #{forceDirection} at #{hole}"
-
-
-    @doors.forEach (door) =>
+rasdf @doors.forEach (door) =>
       if door.isOpen
         adjacentRooms = door.getAdjacentRooms()
         adjacentRooms.forEach (otherRoom) =>
@@ -112,8 +109,6 @@
             forcePoint = @ship.gridToWorld(door.position)
             @ship.body.applyForce(force, forcePoint)

-            console.log "flow #{flow}, applying force #{force} in direction #{forceDirection} at #{door.position}"
-
   # Apply a suction force to everyone in the room
   applySuction: (position, direction, flow) =>
     position = @ship.gridToWorld(position)


priority = 20 for al-value

priority = 20 for al-value

--- src/directive/value.coffee
+++ src/directive/value.coffee
@@ -1,31 +1,30 @@

-alight.d.al.value = (scope, element, variable) ->
-    self =
-        onDom: ->
-            f$.on element, 'input', self.updateModel
-            f$.on element, 'change', self.updateModel
-            scope.$watch '$destroy', self.offDom
-            return
-        offDom: ->
-            f$.off element, 'input', self.updateModel
-            f$.off element, 'change', self.updateModel
-            return
-        updateModel: ->
-            alight.nextTick ->
-                value = element.value
-                scope.$setValue variable, value
+alight.d.al.value =
+    priority: 20
+    init: (scope, element, variable) ->
+        self =
+            onDom: ->
+                f$.on element, 'input', self.updateModel
+                f$.on element, 'change', self.updateModel
+                scope.$watch '$destroy', self.offDom
+                return
+            offDom: ->
+                f$.off element, 'input', self.updateModel
+                f$.off element, 'change', self.updateModel
+                return
+            updateModel: ->
+                scope.$setValue variable, element.value
                 scope.$scan
                     skipWatch: self.watch
                 return
-            return
-        watchModel: ->
-            self.watch = scope.$watch variable, self.updateDom
-            return
-        updateDom: (value) ->
-            value ?= ''
-            element.value = value
-            '$scanNoChanges'
-        start: ->
-            self.onDom()
-            self.watchModel()
-            return
+            watchModel: ->
+                self.watch = scope.$watch variable, self.updateDom
+                return
+            updateDom: (value) ->
+                value ?= ''
+                element.value = value
+                '$scanNoChanges'
+            start: ->
+                self.onDom()
+                self.watchModel()
+                return


no explicit return

no explicit return

Signed-off-by: Jessica González <52329a27ea5670aeec0efeeb0773bf1b4f1affa1@missallsunday.com>

--- src/summernoteDrafts.coffee
+++ src/summernoteDrafts.coffee
@@ -116,12 +116,12 @@
       @$dialog.appendTo $container
       return

-    destroy = () ->
+    destroy = ->
       ui.hideDialog @$dialog
         .remove
       return

-    show = () ->
+    show = ->
       ui.showDialog @$dialog
       $selectedDraft = @$dialog.find '.note-draft'
         .click (e) ->
@@ -138,10 +138,10 @@
           # if no data show some error or something
           false

-    getDrafts = () ->
+    getDrafts = ->
       drafts = []
       store.each (value, key) ->
         if key.indexOf(options.sDrafts.storePrefix) >= 0
           drafts[key] = value

-      return drafts
+      drafts


all urls

all urls

--- src/github-calendar.coffee
+++ src/github-calendar.coffee
@@ -5,6 +5,8 @@
 page = 1
 model = []
 eventMap = {}
+urls = [1..10].map (i)->
+	GITHUB_USER_URL + '/' + user + '/events?page=' + i

 @Date.prototype.format = ->
 	@.getFullYear() + '-' + ( @.getMonth() + 1 ) + '-' + @.getDate()


Don't create extra nodes for data-partial.

--- src/view/dom/readers.coffee
+++ src/view/dom/readers.coffee
@@ -68,10 +68,11 @@
   partial: (definition) ->
     {node, keyPath, view} = definition

-    partialView = new Batman.View(source: keyPath, parentNode: node)
+    partialView = new Batman.View(source: keyPath, parentNode: node, node: node)

     skipChildren: true
     ready: ->
+      partialView.loadView(node)
       view.subviews.add(partialView)

   defineview: (definition) ->


mathjax isn't defined on karma test runner

--- src/helpers/mathjax.coffee
+++ src/helpers/mathjax.coffee
@@ -25,7 +25,7 @@

   # Return immediatly if no [data-math] or <math> elements are present
   # TODO: If the MathJax Queue is not available then MathJax has not loaded yet. Add a load callback to enqueue.
-  return unless window.MathJax.Hub?.Queue? and (_.any(nodes) or hasMath)
+  return unless window.MathJax?.Hub?.Queue? and (_.any(nodes) or hasMath)

   for node in nodes
     formula = node.getAttribute('data-math')


fix judging rotation

fix judging rotation

--- views/application.js.coffee
+++ views/application.js.coffee
@@ -128,7 +128,7 @@
   , ->
     $(this).find('.localtime').remove()

-  $('.body.judging-index img').each ->
+  $('.body.judging img').each ->
     r = 'rotate(' + new String(Math.random()*6-3) + 'deg)'
     $(this)
       .css('-webkit-transform', r)


init cache on server restart

init cache on server restart

--- workers/cacher/index.coffee
+++ workers/cacher/index.coffee
@@ -66,3 +66,5 @@
         JActivityCache.modifyByTeaser {teaserId, createdAt}

     console.log "Activity Cache Worker is ready."
+
+    JActivityCache.init()


Er, actually removing types from JavaObject.

--- src/java_object.coffee
+++ src/java_object.coffee
@@ -26,14 +26,13 @@

   toString: ->
     if @array.length <= 10
-      "<#{@type} [#{@array}] (*#{@ref})>"
+      "<#{@cls.this_class} [#{@array}] (*#{@ref})>"
     else
-      "<#{@type} of length #{@array.length} (*#{@ref})>"
+      "<#{@cls.this_class} of length #{@array.length} (*#{@ref})>"

 class root.JavaObject
   constructor: (rs, @cls, obj={}) ->
-    @type = @cls.this_class # XXX: Remove ASAP.
     @ref = rs.high_oref++
     # Use default fields as a prototype.
     @fields = Object.create(@cls.get_default_fields(rs))
@@ -80,10 +79,10 @@
       @set_field rs, f.cls + '/' + f.field.name, value

   toString: ->
-    if @type.toClassString() is 'java/lang/String'
-      "<#{@type} '#{@jvm2js_str()}' (*#{@ref})>"
+    if @cls.toClassString() is 'java/lang/String'
+      "<#{@cls.this_class} '#{@jvm2js_str()}' (*#{@ref})>"
     else
-      "<#{@type} (*#{@ref})>"
+      "<#{@cls.this_class} (*#{@ref})>"

   # Convert a Java String object into an equivalent JS one.
   jvm2js_str: ->
@@ -94,7 +93,7 @@
   constructor: (rs, @$cls) ->
     super rs, rs.class_lookup(c2t 'java/lang/Class')

-  toString: -> "<Class #{@$type} (*#{@ref})>"
+  toString: -> "<Class #{@$cls.this_class} (*#{@ref})>"

 root.thread_name = (rs, thread) ->
   util.chars2js_str thread.get_field rs, 'java/lang/Thread/name'


fix(container): fixed init container value to being an empty object


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -3,7 +3,7 @@

 @name LevelState
 @constructor
-@param {Number} amplitude How much to scale the oscillator function value
+@param {Number} duration:
 @param {Number} period How fast the osciallator function repeats
 @param {Number} offset How much to offset the created osciallator function. Useful for translating between sin and cosine functions.
 ###


Update app.cjsx

Update app.cjsx

--- views/app.cjsx
+++ views/app.cjsx
@@ -472,12 +472,11 @@
   content =

诶嘿！欢迎使用 poi v{POI_VERSION}！使用之前看看下面！

-
poi 不会修改任何游戏内的发包与收包，但是请使用可信的 poi 版本和可信的插件！

+
poi 不会修改任何游戏内的发包与收包，但是请使用可信的 poi 版本和可信的插件！

poi 默认不使用代理。更改代理的设置在设置面板中可以找到。

使用岛风go的选择HTTP代理，地址是127.0.0.1，端口8099。（默认情况下）

使用自己本地的Shadowsocks或者Socks5代理的选择Socks5代理。

-
使用Shadowsocks也可以用内置的Shadowsocks模块，不过性能不是很好。

使用VPN的选择不使用代理就好了。

poi 如果有显示错误，可以手动调整一下内容大小，布局会自动适配。


Subscribe to sockets earlier so debugging works even when components throw errors

--- src/lib/Network.coffee
+++ src/lib/Network.coffee
@@ -381,11 +381,11 @@

       return

+    # Subscribe to events from the socket
+    @subscribeSocket socket
+
     @connectPort socket, to, edge.to.port, edge.to.index, true
     @connectPort socket, from, edge.from.port, edge.from.index, false
-
-    # Subscribe to events from the socket
-    @subscribeSocket socket

     @connections.push socket
     callback() if callback


Default config settings.

Default config settings.

--- lib/Watchers/Asset.coffee
+++ lib/Watchers/Asset.coffee
@@ -43,6 +43,7 @@
         @promise = @_defer.promise
         @content = ""
         @filelist = {}
+        @config = @config or {verbose: no}

         unless @pattern? and @pattern instanceof Array
             throw new Error "No sane pattern, have #{@pattern}"


chore: extended default delay time to 500ms

--- src/lib/delayedsave.coffee
+++ src/lib/delayedsave.coffee
@@ -1,5 +1,5 @@
 DelayedSave = (options, self, datahandler = null) ->
-  (value, prop, delay = 350) =>
+  (value, prop, delay = 500) =>
     if self.id?
       if self[prop] && self[prop].editing?
         self[prop].editing 'pending'


fix(encryptable): fixed variable name collision

--- src/lib/encryptable.coffee
+++ src/lib/encryptable.coffee
@@ -1,40 +1,40 @@
-Encryptable = (self, container) ->
-  # Decrypt given container and populate fields with decrypted values
+Encryptable = (self, encrypted_container, encrypted_editable) ->
+  # Decrypt given encrypted_container and populate fields with decrypted values
   #
-  self.decrypt = (container) =>
-    if typeof container == 'string' && container != null && container != ''
-      dencdata = decryptData sessionStorage.getItem('appKey'), container
+  self.decrypt = (encrypted_container) =>
+    if typeof encrypted_container == 'string' && encrypted_container != null && encrypted_container != ''
+      dencdata = decryptData sessionStorage.getItem('appKey'), encrypted_container

-      container = JSON.parse(dencdata)
+      encrypted_container = JSON.parse(dencdata)

-      for index, value of container
+      for index, value of encrypted_container
         self[index] = value

-  if typeof container == 'string'
-    self.decrypt self[container]
-  else if typeof container == 'object'
-    for container in container
+  if typeof encrypted_container == 'string'
+    self.decrypt self[encrypted_container]
+  else if typeof encrypted_container == 'object'
+    for container in encrypted_container
       self.decrypt self[container]

   self.enc_update = (data, callback) =>
     for index, value of data
-      if index in options.encrypted_editable
+      if index in encrypted_editable
         # encrypt
-        if typeof self[options.encrypted_container] != 'object' || self[options.encrypted_container] == null
-          self[options.encrypted_container] = {}
+        if typeof self[encrypted_container] != 'object' || self[encrypted_container] == null
+          self[encrypted_container] = {}

-        self[options.encrypted_container][index] = value
+        self[encrypted_container][index] = value

-    self.save_encrypted_container()
+    self.save_encrypted_encrypted_container()

     if callback?
       callback()

-  self.save_encrypted_container = =>
-    encdata = encryptData(sessionStorage.getItem('appKey'), JSON.stringify(self[options.encrypted_container]))
+  self.save_encrypted_encrypted_container = =>
+    encdata = encryptData(sessionStorage.getItem('appKey'), JSON.stringify(self[encrypted_container]))

     edit_value = {}
-    edit_value[options.encrypted_container] = encdata
+    edit_value[encrypted_container] = encdata
     self.update edit_value

   return self


1. add config 2. reorder commands 3. remove unused code 4. add publish app method

--- lib/atom-butterfly.coffee
+++ lib/atom-butterfly.coffee
@@ -7,14 +7,27 @@

 module.exports =

+  configDefaults:
+    chameleonServerAddress: 'http://localhost'
+    tanant: ""
+    username: ""
+
   activate: (state) ->
-    atom.workspaceView.command "atom-butterfly:install", => @cmdInstall()
     atom.workspaceView.command "atom-butterfly:debug", => @cmdDebug()

+    #New
     atom.workspaceView.command "atom-butterfly:create-project", => @cmdCreateProject()
+    atom.workspaceView.command "atom-butterfly:create-package", => @cmdCreatePackage()
+    atom.workspaceView.command "atom-butterfly:create-file", => @cmdCreateFile()
+
+    #Product
+    atom.workspaceView.command "atom-butterfly:install", => @cmdInstall()
     atom.workspaceView.command "atom-butterfly:run-on-server", => @cmdRunOnServer()
+    atom.workspaceView.command "atom-butterfly:emulator", => @cmdLaunchEmulator()

-    atom.workspaceView.command "atom-butterfly:emulator", =>@cmdLaunchEmulator()
+    #publish
+    atom.workspaceView.command "atom-chameleon:publish-application", => @cmdPublishApplication()
+    atom.workspaceView.command "atom-chameleon:publish-package", => @cmdPublishPackage()

   deactivate: ->
     @runOnServerView?.destroy()
@@ -23,8 +36,10 @@
     @debugServer?.stop()

   cmdDebug: ->
-    BowerView = require './scaffold/bower-view'
-    new BowerView().attach()
+    # BowerView = require './scaffold/bower-view'
+    # new BowerView().attach()
+    webview = document.querySelector('webview')
+    webview.openDevTools() unless webview.isDevToolsOpened()

   cmdCreateProject: ->

@@ -63,15 +78,6 @@
         .finally ->
           pv.destroy()

-        # Scaffolder.createProject(options)
-        # .on 'message', (message) ->
-        #   pv.setTitle(message)
-        # .on 'progress', (progress) ->
-        #   pv.setProgress(progress)
-        # .on 'finish', ->
-        #   pv.destroy()
-        #   atom.open {pathsToOpen: [destPath]}
-
   cmdInstall: ->

     pv = new ProgressView("Install Butterfly.js...")
@@ -84,14 +90,6 @@
       pv.setProgress(progress.progress) if progress.progress
     .then ->
       pv.destroy()
-
-    # Scaffolder.installFramework()
-    # .on 'message', (message) ->
-    #   pv.setTitle(message)
-    # .on 'progress', (progress) ->
-    #   pv.setProgress(progress)
-    # .on 'finish', ->
-    #   pv.destroy()

   cmdRunOnServer: ->

@@ -129,3 +127,7 @@
       @emulatorView = new EmulatorView()

     @emulatorView.toggle()
+
+  cmdPublishApplication: ->
+
+


:racehorse: Remove AcceptFilter

:racehorse: Remove AcceptFilter

...so that Chrome doesn't need to switch to user space when iterating over line
nodes.

--- src/lines-yardstick.coffee
+++ src/lines-yardstick.coffee
@@ -1,5 +1,4 @@
 TokenIterator = require './token-iterator'
-AcceptFilter = {acceptNode: -> NodeFilter.FILTER_ACCEPT}
 {Point} = require 'text-buffer'

 module.exports =
@@ -27,7 +26,7 @@

     return new Point(row, 0) unless lineNode? and tokenizedLine?

-    iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT, AcceptFilter)
+    iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT)
     charIndex = 0

     @tokenIterator.reset(tokenizedLine)
@@ -87,7 +86,7 @@
     return 0 unless lineNode?

     indexWithinTextNode = null
-    iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT, AcceptFilter)
+    iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT)
     charIndex = 0

     @tokenIterator.reset(tokenizedLine)


api: Change getExpiry to put the timestamp after the end of the period.

--- index.coffee
+++ index.coffee
@@ -369,7 +369,8 @@
     week = day * 7
     # Generate a random offset between one day and a week
     offset = Math.random() * (week - day) + day
-    moment.utc(timestamp).add -1 * offset, 'seconds'
+    timestamp = moment.utc(timestamp).add 1, @options.period
+    moment.utc(timestamp).add offset, 'seconds'

   ###
   # Record events


Updating proxy so it can connect to arbitrary remote instances

--- index.coffee
+++ index.coffee
@@ -1,202 +1,173 @@
-# set the process title for `ps`
-
 process.title = 'reddish-proxy'
-
-
-# import modules

 url = require 'url'
 net = require 'net'
-tls = require 'tls'
 redis = require 'redis'
 optimist = require 'optimist'
+tls = require 'tls'
+
+options = optimist
+  .usage("Usage: #{process.title} --local [local] --remote [remote] --key [key] -p1 [port] -p2 [port]")
+  .demand(['local', 'remote', 'key'])
+  .default('local', 'redis://127.0.0.1:6379')
+  .default('remote', 'https://reddi.sh')
+  .default('remote_port', 8000)
+  .default('remote_monitor_port', 8001)
+  .default('secure', false)
+  .default('verbose', false)
+  .boolean('secure')
+  .boolean('verbose')
+  .describe('help', 'Print this help text.')
+  .describe('key', 'Your Reddish connection key from your reddish instance.')
+  .describe('local', 'The redis:// url to your local redis server.')
+  .describe('remote', 'The url to your remote reddish instance socket.')
+  .describe('remote_port', 'The standard port to your remote reddish instance socket.')
+  .describe('remote_monitor_port', 'The monitor port to your remote reddish instance socket.')
+  .describe('secure', 'Should TLS/SSL be used (your reddish server will need to be configured with certs).')
+  .describe('verbose', 'Turn on extra logging.')
+  .alias('local', 'l')
+  .alias('remote', 'r')
+  .alias('remote_port', 'p1')
+  .alias('remote_monitor_port', 'p2')
+  .alias('key', 'k')
+  .alias('secure', 's')
+  .alias('verbose', 'v')
+  .argv
+
+{ key, local, remote, verbose, secure } = options

-# setup command line arguments
+# cli colors

-argv = optimist
-  .usage("Usage: #{process.title} --url [url] --key [key]")
-  .demand(['url', 'key'])
-  .default('url', 'redis://127.0.0.1:6379')
-  .alias('url', 'u')
-  .describe('url', 'A formatted redis url')
-  .describe('key', 'Your Reddish connection key')
-  .alias('key', 'k')
-  .argv
+R = "\x1b[31m"
+G = "\x1b[32m"
+Y = "\x1b[33m"
+X = "\x1b[39m"

-# deconstruct args
-
-{ key, url: arg_url } = argv
-
-
-# connection key validation regex
+# validate the connection key

 key_regex = /^[a-f0-9]{40}$/i

-
-# valid the connection key
-
 unless key_regex.test(key)
-  console.error 'ERROR: Invalid connection key', key
+  console.error "#{R}ERROR#{X}", 'Invalid connection key', key
   return

 # deconstruct `redis://` url

-{ port: redis_port, hostname: redis_hostname } = url.parse(arg_url)
+{ protocol, port: local_port, hostname: local_hostname } = url.parse(local)

-# setup Reddish connection details
+# validate the connection key

-reddish_port = 8000
-reddish_monitor_port = 8001
-reddish_hostname = if env = process.env.NODE_ENV is 'test' then 'dev.freeflow.io' else 'reddi.sh'
+local_protocol_regex = /^redis/i
+
+unless local_protocol_regex.test(protocol)
+  console.error "#{R}ERROR#{X}", 'Invalid redis protocol', protocol
+  return
+
+options.local_hostname = local_hostname
+options.local_port = local_port

-# setup handshaken and connected state
+# deconstruct Reddish connection details

-handshaken = monitor_handshaken = false
-connected = monitor_connected = false
+{ hostname: remote_hostname } = url.parse(remote)
+
+options.remote_hostname = remote_hostname

-# initialize Redis connection
+class Connection
+  @verbose: verbose
+  @secure: secure

-console.log 'Redis client connecting...', redis_port, redis_hostname
-redis_client = net.createConnection(redis_port, redis_hostname)
-redis_client.setTimeout(0)
-redis_client.setNoDelay()
-redis_client.setKeepAlive(true)
+  handshaken: false
+  connected: false

-console.log 'Redis monitor client connecting...', redis_port, redis_hostname
-redis_monitor_client = net.createConnection(redis_port, redis_hostname)
-redis_monitor_client.setTimeout(0)
-redis_monitor_client.setNoDelay()
-redis_monitor_client.setKeepAlive(true)
+  handleLocalConnect: =>
+    console.log "Local redis client connected", "#{@local_hostname}:#{@local_port}" if Connection.verbose

+  handleLocalData: (data) =>
+    @remote_endpoint.write(data) if @remote_endpoint and @handshaken

-# initialize Reddish connection
+  handleLocalClose: (err) =>
+    console.error "#{R}ERROR#{X}", 'Local client closed', "#{@local_hostname}:#{@local_port}"
+    if @connected
+      console.error "#{R}ERROR#{X}", 'Reconnecting Local client', "#{@local_hostname}:#{@local_port}"
+      @local_client.connect(@local_port, @local_hostname)

-console.log 'Reddish endpoint connecting...', reddish_port, reddish_hostname
-reddish_endpoint = tls.connect reddish_port, reddish_hostname, ->
-  connected = true
+  handleLocalError: (err) =>
+    console.error "#{R}ERROR#{X}", 'Local client error', err.message if err

-  unless handshaken
-    console.log "Handshaking with endpoint at #{reddish_hostname}:#{reddish_port}..."
-    reddish_endpoint.write(data = JSON.stringify(key: key))
+  initializeLocalConnection: (options) ->
+    console.log 'Local redis client connecting...', "#{@local_hostname}:#{@local_port}" if Connection.verbose

-reddish_endpoint.setTimeout(0)
-reddish_endpoint.setNoDelay()
-# reddish_endpoint.setKeepAlive(true)
+    @local_client = net.createConnection(@local_port, @local_hostname)
+    @local_client.setTimeout(0)
+    @local_client.setNoDelay()
+    @local_client.setKeepAlive(true)

-console.log 'Reddish monitor endpoint connecting...', reddish_monitor_port, reddish_hostname
-reddish_monitor_endpoint = tls.connect reddish_monitor_port, reddish_hostname, ->
-  monitor_connected = true
+    @local_client.on 'connect', @handleLocalConnect
+    @local_client.on 'data', @handleLocalData
+    @local_client.on 'close', @handleLocalClose
+    @local_client.on 'error', @handleLocalError

-  unless monitor_handshaken
-    console.log "Handshaking with monitor endpoint at #{reddish_hostname}:#{reddish_monitor_port}..."
-    reddish_monitor_endpoint.write(data = JSON.stringify(key: key))
-reddish_monitor_endpoint.setTimeout(0)
-reddish_monitor_endpoint.setNoDelay()
-# reddish_monitor_endpoint.setKeepAlive(true)
+  handleRemoteConnect: =>
+    @connected = true

+    unless @handshaken
+      console.log "Handshaking with endpoint at #{@remote_hostname}:#{@remote_port}..." if Connection.verbose
+      @remote_endpoint.write(data = JSON.stringify(key: @key))

-# handle Redis connect event
-
-redis_client.on 'connect', ->
-  console.log "Redis client connected to #{redis_hostname}:#{redis_port}"
-
-redis_monitor_client.on 'connect', ->
-  console.log "Redis monitor client connected to #{redis_hostname}:#{redis_port}"
-
-
-# handle Redis data event
-
-redis_client.on 'data', (data) ->
-  reddish_endpoint.write(data) if handshaken
-
-redis_monitor_client.on 'data', (data) ->
-  reddish_monitor_endpoint.write(data) if monitor_handshaken
-
-
-# handle Redis close event
-
-redis_client.on 'close', (err) ->
-  console.error 'ERROR: Redis client closed'
-  if connected
-    console.error 'ERROR: Reconnecting Redis client'
-    redis_client.connect(redis_port, redis_hostname)
-
-redis_monitor_client.on 'close', (err) ->
-  console.error 'ERROR: Redis monitor client closed'
-  if monitor_connected
-    console.error 'ERROR: Reconnecting monitor Redis client'
-    redis_client.connect(redis_port, redis_hostname)
-
-
-# handle Redis error event
-
-redis_client.on 'error', (err) ->
-  console.error 'ERROR: Redis client error', err.message if err
-
-redis_monitor_client.on 'error', (err) ->
-  console.error 'ERROR: Redis monitor client error', err.message if err
-
-
-# handle Reddish data event
-
-reddish_endpoint.on 'data', (data) ->
-  unless handshaken
+  handleRemoteHandshake: (data) =>
     try
       json = JSON.parse(data.toString())

       if err = json?.error
-        console.error 'ERROR: Endpoint handshake failed:', err
+        console.error "#{R}ERROR#{X}", 'Endpoint handshake failed:', err
         return

-      console.log 'Endpoint handshake succeeded'
-      console.log "SUCCESS: Proxying redis client at #{redis_hostname}:#{redis_port} to reddish endpoint at #{reddish_hostname}:#{reddish_port}..."
-      handshaken = true
-    catch err then console.error 'ERROR: Endpoint handshake failed:', err
-    return
+      console.log "#{G}SUCCESS#{X}", "Proxying redis client at #{@local_hostname}:#{@local_port} to reddish endpoint at #{@remote_hostname}:#{@remote_port}..."
+      @handshaken = true
+    catch err
+      console.error "#{R}ERROR#{X}", 'Endpoint handshake failed:', err

-  redis_client.write(data)
+  handleRemoteData: (data) =>
+    unless @handshaken
+      return @handleRemoteHandshake(data)

-reddish_monitor_endpoint.on 'data', (data) ->
-  unless monitor_handshaken
-    try
-      json = JSON.parse(data.toString())
+    @local_client.write(data)

-      if err = json?.error
-        console.error 'ERROR: Monitor endpoint handshake failed:', err
-        return
+  handleRemoteClose: (err) =>
+    @connected = false
+    console.error "#{R}ERROR#{X}", 'Remote endpoint closed, closing Local client', (err.message if err)
+    @local_client.end()

-      console.log 'Monitor endpoint handshake succeeded'
-      console.log "SUCCESS: Proxying redis monitor client at #{redis_hostname}:#{redis_port} to reddish monitor endpoint at #{reddish_hostname}:#{reddish_monitor_port}..."
-      monitor_handshaken = true
-    catch err then console.error 'ERROR: Monitor endpoint handshake failed:', err
-    return
+  handleRemoteError: (err) =>
+    console.error "#{R}ERROR#{X}", 'Remote endpoint error', (err.message if err)

-  redis_monitor_client.write(data)
+  initializeRemoteConnection: ->
+    console.log 'Remote endpoint connecting...', "#{@remote_hostname}:#{@remote_port}" if Connection.verbose

+    if Connection.secure
+      @remote_endpoint = tls.connect @remote_port, @remote_hostname, @handleRemoteConnect
+    else
+      @remote_endpoint = net.connect @remote_port, @remote_hostname, @handleRemoteConnect

-# handle Reddish close event
+    @remote_endpoint.setTimeout(0)
+    @remote_endpoint.setNoDelay()
+    @remote_endpoint.setKeepAlive(true)

-reddish_endpoint.on 'close', (err) ->
-  connected = false
-  console.error 'ERROR: Reddish endpoint closed: closing Redis client'
-  redis_client.end()
+    @remote_endpoint.on 'data', @handleRemoteData
+    @remote_endpoint.on 'close', @handleRemoteClose
+    @remote_endpoint.on 'error', @handleRemoteError

-reddish_monitor_endpoint.on 'close', (err) ->
-  monitor_connected = false
-  console.error 'ERROR: Reddish monitor endpoint closed: closing Redis monitor client'
-  redis_monitor_client.end()
+  constructor: (@remote_port, options) ->
+    { @local_hostname, @local_port, @remote_hostname, @key } = options

+    @initializeLocalConnection()
+    @initializeRemoteConnection()

-# handle Reddish error event
-
-reddish_endpoint.on 'error', (err) ->
-  console.error 'ERROR: Reddish endpoint error', err.message if err
-
-reddish_monitor_endpoint.on 'error', (err) ->
-  console.error 'ERROR: Reddish monitor endpoint error', err.message if err
+remote_connection = new Connection(options.remote_port, options)
+remote_monitor_connection = new Connection(options.remote_monitor_port, options)


Adds functionality for piped action typed

--- src/modules/actions.coffee
+++ src/modules/actions.coffee
@@ -28,7 +28,8 @@
   # Find action in DOM
   #
   findActions: ->
-    @_fireAction(action) for action in document.querySelectorAll("[#{@ACTIVE_ELEMENT}]")
+    for actions in document.querySelectorAll("[#{@ACTIVE_ELEMENT}]")
+      @_fireAction(action) for action in actions.split('|')

   # Subscribe actions to PubSub
   #
@@ -44,4 +45,4 @@
   #
   _getActions: ()-> Object.create(_actions)

-module.exports = a = new Actions()+module.exports = a = new Actions()


proper status code

proper status code

--- index.coffee
+++ index.coffee
@@ -217,7 +217,7 @@
   else if req.xhr and req.user?
     res.send { success: true, user: req.user.toJSON() }
   else if req.xhr and not req.user?
-    res.send { success: false, error: "Missing user." }
+    res.send 500, { success: false, error: "Missing user." }
   else
     next()

@@ -283,7 +283,7 @@
 #
 headerLogin = (req, res, next) ->
   return next() if req.path is opts.logoutPath
-  if token = req.get('X-Access-Token') or req.query.access_token
+  if token = req.get('X-Access-Token')
     req.login new opts.CurrentUser(accessToken: token), next
   else
     next()


Fix timing issue with printing

Fix timing issue with printing

--- src/map-printer.coffee
+++ src/map-printer.coffee
@@ -107,7 +107,8 @@
                 document.getElementById('images').appendChild img
                 @makingPrint = false
                 if notOnBeforePrint
-                    window.print()
+                    prn = => window.print()
+                    _.defer prn

         afterPrint: () =>
             if @makingPrint


Add more examples to test for undefined values

--- test/testJsonPointer.coffee
+++ test/testJsonPointer.coffee
@@ -47,12 +47,13 @@

     it "returns undefined if value is not found", () ->
       target =
-        foo: "bar"
+        foo: "bar",
+        baz: [1, 2, 3]
+      targetAsString = JSON.stringify target
+      pointers = ["/oof", "/baz/4", "/foo/bar", "/foo/bar/baz"]

-      targetAsString = JSON.stringify target
-
-      actual = jsonPointer.get targetAsString, "/baz/0"
-      expect(actual).to.be.undefined
+      evaluate = (pointer) -> jsonPointer.get targetAsString, pointer
+      expect(evaluate(p)).to.be.undefined for p in pointers

     describe "validates pointer and", () ->


Add point/size shortcut tests

Add point/size shortcut tests

--- test/tests/LayerTest.coffee
+++ test/tests/LayerTest.coffee
@@ -817,6 +817,16 @@
 			layerB.screenScaledFrame().should.eql {"x":255,"y":280,"width":240,"height":240}
 			layerC.screenScaledFrame().should.eql {"x":223,"y":228,"width":384,"height":336}

+		it "should accept point shortcut", ->
+			layer = new Layer point:10
+			layer.x.should.equal 10
+			layer.y.should.equal 10
+
+		it "should accept size shortcut", ->
+			layer = new Layer size:10
+			layer.width.should.equal 10
+			layer.height.should.equal 10
+
 	describe "Center", ->

 		it "should center", ->


bring scaling functions back in, remove canvaswidth


feat(core): improve cron format in messages

--- src/model/scrum.coffee
+++ src/model/scrum.coffee
@@ -153,7 +153,7 @@
         this.cronJob.stop()

     toPrintable: ->
-        this._id.toString() + ": " + this._room.toString() + " at " + this._time.toString()
+        this._id.toString() + ": " + this._room.toString() + " at `" + this._time.toString() + "`"

     getId: ->
         this._id


#206 - Bound transcribe box to the edges of the browser/digitized image

--- app/assets/javascripts/lib/draggable.cjsx
+++ app/assets/javascripts/lib/draggable.cjsx
@@ -61,13 +61,21 @@
     return if $(e.target).parents(@props.disableDragIn.join(',')).length > 0
     console.log "handleStart"

-    pos = $(this.getDOMNode()).position()
+    $el = $(this.getDOMNode())
+    pos = $el.position()
+    offset = $el.offset()
+    parent_left = offset.left - pos.left
+    parent_top = offset.top - pos.top

     @setState
       dragging: true,
       rel:
         x: e.pageX - pos.left,
         y: e.pageY - pos.top
+        min_x: -parent_left
+        min_y: -parent_top
+        max_x: $(document).width() - $el.width() - parent_left
+        max_y: $(document).height() - $el.height() - parent_top

     @_rememberCoords e
     # console.log "previous coords", @_previousEventCoords
@@ -89,6 +97,13 @@

     x = e.pageX - this.state.rel.x
     y = e.pageY - this.state.rel.y
+
+    # ensure element is in bounds of document
+    x = this.state.rel.min_x if x < this.state.rel.min_x
+    y = this.state.rel.min_y if y < this.state.rel.min_y
+    x = this.state.rel.max_x if x > this.state.rel.max_x
+    y = this.state.rel.max_y if y > this.state.rel.max_y
+
     @setState
       x: x
       y: y


appcontroller: emit an event when an editor pane is opened

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -275,7 +275,11 @@

   openFile: (file, contents, callback = noop, emitChange) ->

-    @activeTabView.emit 'FileNeedsToBeOpened', file, contents, callback, emitChange
+    kallback = (pane) =>
+      @emit 'EditorPaneDidOpen', pane  if pane?.options.paneType is 'editor'
+      callback pane
+
+    @activeTabView.emit 'FileNeedsToBeOpened', file, contents, kallback, emitChange

   openMachineTerminal: (machineData) ->


Don't blow up on body tag, return self

Don't blow up on body tag, return self

--- client/lanes/lib/dom.coffee
+++ client/lanes/lib/dom.coffee
@@ -14,6 +14,7 @@
 wrap = (fn) ->
     return ->
         fn(this.el, arguments...)
+        return this

 for name, func of Lanes.Vendor.dom
     Lanes.lib.Dom::[name] = wrap(func)
@@ -41,7 +42,9 @@
         unknown
     else if _.isFunction(unknown.getDOMNode)
         unknown.getDOMNode()
-    else
+    else if unknown.nodeType is 9 # body tag
+        unknown
+    else if unknown
         throw new TypeError("Unable to obtain dom reference to #{unknown}")

     if query


Prevent form to create a selector if a selecor already exists

--- app/assets/javascripts/selector.js.coffee
+++ app/assets/javascripts/selector.js.coffee
@@ -13,41 +13,47 @@
     id: null

     _create: ->
-      @element.wrap $("<div>", class: "selector")
-
-      @element.attr "autocomplete", "off"
-      @element.addClass('selector-search')
-
-      # Create drop down button
-      @dropDownButton = $ "<a>",
-          href: "##{@element.attr('id')}"
-          rel: 'dropdown'
-          tabindex: -1
-          class: 'selector-dropdown btn btn-default dropdown-toggle sr-only'
-        .insertAfter @element
       @lastSearch = @element.val()
-
-      # Create drop down menu
-      @dropDownMenu = $ "<div>",
-          class: "items-menu selector-choices-list"
-        .hide()
-        .insertAfter(@element)
-
-      # Create value field if it doesn't exist
-      if @element.data("valueField")?
-        @valueField = $ @element.data("valueField")
-      else
-        @valueField = $ "<input>",
-          type: "hidden"
-          name: @element.attr('name')
-          class: 'selector-value'
-          'data-parameter-name': @element.attr('data-value-parameter-name')
-        @element.after @valueField
-      @element.removeAttr "name"
-      if @element.attr("required") is "true"
-        @valueField.attr "required", "true"
-      unless @valueField.val()? and @valueField.val() != ''
-        @valueField.attr("value", @element.val())
+      if @element.parent().is('.selector')
+        parent = @element.parent()
+        @dropDownButton = parent.find('.selector-dropdown[rel="dropdown"]').first()
+        @dropDownMenu = parent.find('.selector-choices-list').first()
+        @valueField = parent.find('.selector-value').first()
+      else
+        @element.wrap $("<div>", class: "selector")
+
+        @element.attr "autocomplete", "off"
+        @element.addClass('selector-search')
+
+        # Create drop down button
+        @dropDownButton = $ "<a>",
+            href: "##{@element.attr('id')}"
+            rel: 'dropdown'
+            tabindex: -1
+            class: 'selector-dropdown btn btn-default dropdown-toggle sr-only'
+          .insertAfter @element
+
+        # Create drop down menu
+        @dropDownMenu = $ "<div>",
+            class: "items-menu selector-choices-list"
+          .hide()
+          .insertAfter(@element)
+
+        # Create value field if it doesn't exist
+        if @element.data("valueField")?
+          @valueField = $ @element.data("valueField")
+        else
+          @valueField = $ "<input>",
+            type: "hidden"
+            name: @element.attr('name')
+            class: 'selector-value'
+            'data-parameter-name': @element.attr('data-value-parameter-name')
+          @element.after @valueField
+        @element.removeAttr "name"
+        if @element.attr("required") is "true"
+          @valueField.attr "required", "true"
+        unless @valueField.val()? and @valueField.val() != ''
+          @valueField.attr("value", @element.val())
       this._on @element,
         keypress: "_keypress"
         keyup: "_keyup"


added brooklyn to sheet admin home

added brooklyn to sheet admin home

--- app/assets/javascripts/sheet_index.coffee
+++ app/assets/javascripts/sheet_index.coffee
@@ -6,6 +6,10 @@
 			minZoom: 13
 			maxZoom: 21
 		)
+
+		overlay = L.mapbox.tileLayer('https://s3.amazonaws.com/maptiles.nypl.org/860/860spec.json',
+			zIndex: 3
+		).addTo(@map)

 		s = @
 		@map.on


Better error handling

Better error handling

--- src/octonode/client.coffee
+++ src/octonode/client.coffee
@@ -45,8 +45,8 @@
       body = JSON.parse(body || '{}')
     catch err
       return callback(err)
-    return callback(new Error(body.message)) if res.statusCode is 422
-    return callback(new Error(body.message)) if res.statusCode in [400, 401, 404]
+    return callback(new Error(body.message)) if body.message and res.statusCode is 422
+    return callback(new Error(body.message)) if body.message and res.statusCode in [400, 401, 404]
     callback null, res.statusCode, body

   # Github api GET request


Give feedback to Free for Teacher's teacher signups when failing. Closes #9880

The Free for Teacher's teacher signup rejects a login if the login is already in use.
It did not give any feedback about why it was being rejected. Fixed that by
giving feedback. See issue for security discussions and longer-term fixes.

Testing Notes
==========
* Enable the "Open Registration" so the "Need a Canvas Account?" banner
  shows up on the login page. Works on Free for Teachers or locally when
  the default account has the Settings "Open Registration" checked.
* Use an existing user (like a student) that has an email address
  as their login.
* Go to the /register_from_website banner link and click "I'm a Teacher"
* Enter the existing user's name and email address.
* Click "Start Teaching". It should display "Email already in use" on the Email
  field in a validation error display bubble.

Change-Id: If5bce13f3c1e2dfa2e6ec4db5de4e1b4cd77b235
Reviewed-on: https://gerrit.instructure.com/13082
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Jon Jensen <44f878afe53efc66b76772bd845eb65944ed8232@instructure.com>

--- app/coffeescripts/models/Pseudonym.coffee
+++ app/coffeescripts/models/Pseudonym.coffee
@@ -10,6 +10,7 @@
         too_short:    I18n.t("errors.required", "Required")
         too_long:     I18n.t("errors.too_long", "Can't exceed %{max} characters", {max: 100})
         invalid:      I18n.t("errors.invalid", "May only contain letters, numbers, or the following: %{characters}", {characters: ". + - _ @ ="})
+        taken:        I18n.t("errors.taken", "Email already in use")
         bad_credentials: I18n.t("errors.bad_credentials", "Invalid username or password")
       password:
         too_short:    I18n.t("errors.too_short", "Must be at least %{min} characters", {min: 6})


Fixed error with reduce function

Fixed error with reduce function

--- src/output/eeSchema.coffee
+++ src/output/eeSchema.coffee
@@ -1,7 +1,6 @@

 module.exports = (data, lib) ->
   pins = data.symbol.pins.reduce (last, a) ->
-    last ||= {}
     side = a[3]
     last[side] ||= []

@@ -23,6 +22,7 @@
       name: a[1]
       type: type
     last
+  , {}

   hSpacing = data.symbol.hSpacing || data.symbol.spacing || 100
   vSpacing = data.symbol.vSpacing || data.symbol.spacing || 100


Don't use fatal error for package.json errors

Notifications can't associate it with a project path since the
project never loads

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -429,4 +429,4 @@
     detail = error.message + " in #{metadataPath}"
     stack = error.stack + "\n  at #{metadataPath}:1:1"
     message = "Failed to load the #{path.basename(packagePath)} package"
-    atom.notifications.addFatalError(message, {stack, detail, dismissable: true})
+    atom.notifications.addError(message, {stack, detail, dismissable: true})


Copy the path part of an item's url path, or just the path as is if not a url

--- lib/file-info-view.coffee
+++ lib/file-info-view.coffee
@@ -15,7 +15,7 @@
     @subscribeToActiveItem()

     clickHandler = =>
-      text = @getActiveItem()?.getPath?() or @getActiveItem()?.getTitle?() or ''
+      text = @getActiveItemCopyText()
       atom.clipboard.write(text)
       setTimeout =>
         @handleCopiedTooltip()
@@ -26,12 +26,16 @@

   handleCopiedTooltip: ->
     @copiedTooltip?.dispose()
-    text = @getActiveItem()?.getPath?() or @getActiveItem()?.getTitle?() or ''
+    text = @getActiveItemCopyText()
     @copiedTooltip = atom.tooltips.add this,
       title: "Copied: #{text}"
       trigger: 'click'
       delay:
         show: 0
+
+  getActiveItemCopyText: ->
+    # An item path could be a url, but we only want to copy the `path` part of it.
+    require('url').parse(@getActiveItem()?.getPath?() or '').path or @getActiveItem()?.getTitle?() or ''

   subscribeToActiveItem: ->
     @modifiedSubscription?.dispose()


Fix #3.

Fix #3.

Meh.

--- lib/linter-crystal.coffee
+++ lib/linter-crystal.coffee
@@ -21,16 +21,16 @@

   constructor: (@editor)->
     super(@editor)
-    @lintLiveListen = atom.config.observe 'linter-crystal.liveLinting', =>
-      @lintLive = atom.config.get 'linter-crystal.liveLinting'
-    @colorListen = atom.config.observe 'linter-crystal.colorOutput', =>
-      @colorBuild = atom.config.get 'linter-crystal.colorOutput'
-    @buildOutputListen = atom.config.observe 'linter-crystal.buildArtifacts', =>
-      @buildOutput = atom.config.get 'linter-crystal.buildArtifacts'
+    @listen =
+      atom.config.observe 'linter-crystal.liveLinting', (value) =>
+        @lintLive = value
+    atom.config.observe 'linter-crystal.colorOutput', (value) =>
+      @colorBuild = value
+    atom.config.observe 'linter-crystal.buildArtifacts', (value) =>
+      @buildOutput = value

   lintFile: (filePath, callback) ->
     @cmd = atom.config.get 'linter-crystal.command'
-    {command, args} = @getCmdAndArgs(filePath)
     unless @colorBuild
       @cmd = "#{@cmd} --no-color"
     unless @buildOutput
@@ -43,8 +43,6 @@
       console.log "linter-crystal running command: #{@cmd}"

   destroy: ->
-    @lintLiveListen.dispose()
-    @colorListen.dipose()
-    @buildOutputListen.dispose()
+    @listen.dispose()

 module.exports = LinterCrystal


Force new connection on io.connect #42

Force new connection on io.connect #42

--- tests/support/client.coffee
+++ tests/support/client.coffee
@@ -54,7 +54,9 @@
   head: (args...) -> @request 'head', args...
   patch: (args...) -> @request 'patch', args...

-  connect: -> @socket = io.connect("http://#{@host}:#{@port}")
+  connect: ->
+      @socket = io.connect("http://#{@host}:#{@port}", { 'force new connection': true })
+
   on: -> @socket.on.apply @socket, arguments
   emit: -> @socket.emit.apply @socket, arguments


refactored options init

refactored options init

--- lib/mixins/options.coffee
+++ lib/mixins/options.coffee
@@ -20,15 +20,15 @@
     options = @getOptions() or {}
     options.component = @
     unless @isDomSource()
-      options.aaData = @getRows() or []
-      options.aoColumns = @getColumns() or []
+      options.data = @getRows() or []
+      options.columns = @getColumns() or []
       # If the componet was declared with a collection and a query it is setup as a reactive datatable.
       if @getCollection() and @getQuery()
-        options.bServerSide = true
-        options.bProcessing = true
+        options.serverSide = true
+        options.processing = true
         # `options.sAjaxSource` is currently useless, but is passed into `fnServerData` by datatables.
-        options.sAjaxSource = "useful?"
+        options.ajaxSource = "useful?"
         # This binds the datatables `fnServerData` server callback to this component instance.
         # `_.debounce` is used to prevent unneccesary subcription calls while typing a search
-        options.fnServerData = _.debounce( @fnServerData.bind( @ ), 300 )
+        options.serverData = _.debounce( @fnServerData.bind( @ ), 300 )
     @setOptions _.defaults( options, @defaultOptions )


Fixed DuelStatsView power totals including future units.

--- app/views/play/level/DuelStatsView.coffee
+++ app/views/play/level/DuelStatsView.coffee
@@ -89,7 +89,7 @@
       palisade: 10
       peasant: 50
     powers = humans: 0, ogres: 0
-    for thang in @options.thangs when thang.health > 0
+    for thang in @options.thangs when thang.health > 0 and thang.exists
       powers[thang.team] += @costTable[thang.type] or 0 if powers[thang.team]?
     for player in @players
       utils.replaceText @$find(player.team, '.power-value'), powers[player.team]


bug:Fix extra hero modal during level loads

Closes #4479

--- app/views/play/level/PlayLevelView.coffee
+++ app/views/play/level/PlayLevelView.coffee
@@ -420,11 +420,13 @@
       raider = '55527eb0b8abf4ba1fe9a107'
       e.session.set 'heroConfig', {"thangType":raider,"inventory":{}}
     else if e.level.isType('hero', 'hero-ladder', 'hero-coop') and not _.size e.session.get('heroConfig')?.inventory ? {}
-      @setupManager?.destroy()
-      @setupManager = new LevelSetupManager({supermodel: @supermodel, level: e.level, levelID: @levelID, parent: @, session: e.session, courseID: @courseID, courseInstanceID: @courseInstanceID})
-      @setupManager.open()
-
-
+      # Delaying this check briefly so LevelLoader.loadDependenciesForSession has a chance to set the heroConfig on the level session
+      _.defer =>
+        return if _.size(e.session.get('heroConfig')?.inventory ? {})
+        # TODO: which scenario is this executed for?
+        @setupManager?.destroy()
+        @setupManager = new LevelSetupManager({supermodel: @supermodel, level: e.level, levelID: @levelID, parent: @, session: e.session, courseID: @courseID, courseInstanceID: @courseInstanceID})
+        @setupManager.open()

   onLoaded: ->
     _.defer => @onLevelLoaderLoaded()


markdown

markdown

--- resource/translators/unicode_translator.coffee
+++ resource/translators/unicode_translator.coffee
@@ -67,6 +67,7 @@
         @latex += '\\textbf{'

       when 'a'
+        # zotero://open-pdf/0_5P2KA4XM/7 is actually a reference.
         @latex += "\\href{#{tag.attrs.href}}{" if tag.attrs.href?.length > 0

       when 'sup'


Persistence fixes for memberBalance and teamFee.balance

--- src/persistence.coffee
+++ src/persistence.coffee
@@ -339,6 +339,33 @@
         err
       ).callback callback

+  # Update memberBalance and teamFee when saving memberPayment
+  wrapMethod sdk, 'saveMemberPayment', (saveMemberPayment) ->
+    (memberPayment, callback) ->
+      saveMemberPayment.call(this, memberPayment).then((result) ->
+        promises.when(
+          sdk.loadMemberBalances(memberId: memberPayment.memberId)
+          sdk.loadTeamFees(id: memberPayment.teamFeeId)
+        ).then -> result
+      ).callback callback
+
+
+  # Update memberBalances when saving teamFee
+  wrapMethod sdk, 'saveTeamFee', (saveTeamFee) ->
+    (teamFee, callback) ->
+      saveTeamFee.call(this, teamFee).then((result) ->
+        sdk.loadMemberBalances(teamId: teamFee.teamId).then ->
+          result
+      ).callback callback
+
+
+  # Update memberBalances when deleting teamFee
+  wrapMethod sdk, 'deleteTeamFee', (deleteTeamFee) ->
+    (teamFee, callback) ->
+      deleteTeamFee.call(this, teamFee).then((result) ->
+        sdk.loadMemberBalances(teamId: teamFee.teamId).then ->
+          result
+      ).callback callback

   # Remove all records belonging to a team when it is deleted
   wrapMethod sdk, 'deleteTeam', (deleteTeam) ->


Simplify code

Simplify code

--- client/explore.coffee
+++ client/explore.coffee
@@ -29,12 +29,11 @@
 Template.explore.onRendered(->
   logger.debug("Template explore has been rendered")

-  projectsCursor = Projects.find({}, {sort: [["created", "asc"]]})
-  projects = projectsCursor.fetch()
+  projects = Projects.find({}, {sort: [["created", "asc"]]})
   @autorun(->
     logger.debug("Installing change observer")
     ignore = true
-    projectsCursor.observe({
+    projects.observe({
       added: (project) ->
         if ignore
           return


Nicer fade

Nicer fade

--- apps/gallery_insights/client/index.coffee
+++ apps/gallery_insights/client/index.coffee
@@ -24,5 +24,5 @@
         $('.gallery-insights-error').text(xhr.responseText)
       success: (res) =>
         $(e.currentTarget).attr 'data-state', null
-        $('.gallery-insights-form').fadeOut()
-        $('.gallery-insights-thank-you').fadeIn()+        $('.gallery-insights-form').fadeOut ->
+          $('.gallery-insights-thank-you').fadeIn()


When requesting a stream, lowercase both the request and the comparing stream so we can ignore case

--- src/scripts/graylog.coffee
+++ src/scripts/graylog.coffee
@@ -56,13 +56,15 @@
       msg.send what

 graylogStreamMessages = (msg, stream_title, count, cb) ->
+  stream_title = stream_title.toLowerCase()
   if stream_title == 'all'
     graylogMessages msg, 'messages.json', (messages) ->
       sayMessages messages, count, cb
   else
     graylogStreamList msg, (streams) ->
       for stream in streams
-        if stream.title == stream_title
+        lstream = stream.title.toLowerCase()
+        if lstream == stream_title
           url = "streams/#{stream._id}-#{stream.title}/messages.json"
           graylogMessages msg, url, (messages) ->
             sayMessages messages, count, cb


handle ⍞ input without wiping out the last line

--- client/session.coffee
+++ client/session.coffee
@@ -79,8 +79,8 @@
     cm.setOption 'readOnly', false
     cm.setOption 'cursorHeight', 1
     l = cm.lineCount() - 1
-    cm.replaceRange (if why == 4 then '' else '      '), {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'Dyalog'
-    cm.setCursor l, 6
+    if why != 4 then cm.replaceRange '      ', {line: l, ch: 0}, {line: l, ch: cm.getLine(l).length}, 'Dyalog'
+    cm.setCursor l, cm.getLine(l).length
     return

   updateSize: -> cm.setSize $e.width(), $e.height()


:art:

:art:

--- src/scrollbar-style.coffee
+++ src/scrollbar-style.coffee
@@ -3,11 +3,10 @@

 emitter = new Emitter()
 observer = new ScrollbarStyleObserver ->
-  emitter.emit 'did-preferred-scrollbar-style-change', observer.getPreferredScrollbarStyle()
+  emitter.emit 'did-preferred-scrollbar-style-change', exports.getPreferredScrollbarStyle()

-module.exports =
-  getPreferredScrollbarStyle: ->
-    observer.getPreferredScrollbarStyle()
+exports.getPreferredScrollbarStyle = ->
+  observer.getPreferredScrollbarStyle()

-  onDidPreferredScrollbarStyleChange: (callback) ->
-    emitter.on 'did-preferred-scrollbar-style-change', callback
+exports.onDidPreferredScrollbarStyleChange = (callback) ->
+  emitter.on 'did-preferred-scrollbar-style-change', callback


Added tooltips to tool buttons

Added tooltips to tool buttons

--- coffee/toolbar.coffee
+++ coffee/toolbar.coffee
@@ -77,11 +77,11 @@
       @lc.redo()

   initTools: ->
-    @$el.find('.tool-pencil').click (e) =>
+    @$el.find('.tool-pencil').tooltip({title: "Pencil"}).click (e) =>
       @lc.tool = new LC.Pencil()

-    @$el.find('.tool-eraser').click (e) =>
+    @$el.find('.tool-eraser').tooltip({title: "Eraser"}).click (e) =>
       @lc.tool = new LC.Eraser()

-    @$el.find('.tool-pan').click (e) =>
+    @$el.find('.tool-pan').tooltip({title: "Pan"}).click (e) =>
       @lc.tool = new LC.Pan()


Services API (re)implemented correctly(?) after TypeError

--- lib/status-message.coffee
+++ lib/status-message.coffee
@@ -1,12 +1,17 @@
 module.exports =
+    activate: -> # ...
+
+    consumeStatusBar: (statusBar) ->
+      @statusBarTile = statusBar.addLeftTile(item: myElement, priority: 100)
+
     display: (text, timeout) ->
         clearTimeout(@timeout) if @timeout?
         @statusBarTile?.destroy()
         statusBar = document.querySelector("status-bar")
         span = document.createElement('span')
         span.textContent = text
-        if statusBar?
-            @statusBarTile = statusBar.addLeftTile(item: span, priority: 100)
+        #if statusBar?
+        #    @statusBarTile = statusBar.addLeftTile(item: span, priority: 100)

         if timeout?
             clearTimeout(@timeout) if @timeout?


fixed accidential rendering of an empty 'doc-section-header'-tags

--- lib/styles/default.coffee
+++ lib/styles/default.coffee
@@ -137,7 +137,7 @@
         secondPart.push tag.markdown for tag in sections.metadata if sections.metadata?
         metaOutput += " #{humanize.joinSentence secondPart}"

-      output += "#{metaOutput}\n\n" if metaOutput?
+      output += "#{metaOutput}\n\n" if metaOutput isnt ''

       output += "#{tag.markdown}\n\n" for tag in sections.description if sections.description?


storage: fix path (which doesn't include /sbin/ when daemon started from cron)

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -1299,7 +1299,7 @@
         (cb) ->
             dbg("ensure projects zpool is imported")
             misc_node.execute_code
-                command : 'zpool'
+                command : '/sbin/zpool'
                 args    : ['import', 'projects']
                 timeout : 180
                 cb      : (err,output) ->


webapp: increase mathjax limit for processing formulas

--- src/smc-webapp/last.coffee
+++ src/smc-webapp/last.coffee
@@ -73,6 +73,7 @@
         skipTags: ["script","noscript","style","textarea","pre","code"]

     TeX:
+        MAXBUFFER: 100000  # see https://github.com/mathjax/MathJax/issues/910
         extensions: ["autoload-all.js", "noUndefined.js", "noErrors.js"]
         Macros:  # get these from sage/misc/latex.py
             Bold:  ["\\mathbb{#1}",1]


simplified usage stats

simplified usage stats

--- src/smc-webapp/r_help.cjsx
+++ src/smc-webapp/r_help.cjsx
@@ -79,7 +79,7 @@

 Live server stats <Loading />

         else
             n = @number_of_clients()
-            <ProgressBar now={Math.max(n / 6 , 45 / 8) } label={"#{n} active users"} />
+            <ProgressBar style={marginBottom:0} now={Math.max(n / 6 , 45 / 8) } label={"#{n} active users"} />

     render_active_projects_stats: ->
         n = @props.projects_edited?[RECENT_TIMES_KEY.active] ? @props.active_projects
@@ -92,17 +92,14 @@
                  {@props.projects_edited?[RECENT_TIMES_KEY.last_hour]  ? @props.last_hour_projects} projects in the last hour,
                  {@props.projects_edited?[RECENT_TIMES_KEY.last_day]   ? @props.last_day_projects} projects in the last day,
                  {@props.projects_edited?[RECENT_TIMES_KEY.last_week]  ? @props.last_week_projects} projects in the last week and
-                 {@props.projects_edited?[RECENT_TIMES_KEY.last_month] ? @props.last_month_projects} projects in the last month
+                 {@props.projects_edited?[RECENT_TIMES_KEY.last_month] ? @props.last_month_projects} projects in the last month.
+                <Space/>
+                <Icon name='line-chart' fixedWidth /> More data...
+

-    render_historical_usage: ->
-

-
-                <Icon name='line-chart' fixedWidth />Historical usage statistics
-             — number of projects and users over time
-

-
     render_historical_metrics: ->
+        return  # disabled, due to being broken...

                 <Icon name='area-chart' fixedWidth />Historical system metrics
@@ -123,9 +120,8 @@

                 {@render_signed_in_stats()}
-                {@render_active_projects_stats()}
+                {# @render_active_projects_stats()}
                 {@render_recent_usage_stats()}
-                {@render_historical_usage()}
                 {@render_historical_metrics()}


increase active users bar

increase active users bar

--- src/smc-webapp/r_help.cjsx
+++ src/smc-webapp/r_help.cjsx
@@ -74,7 +74,7 @@
             <li style={li_style}> Live server stats <Loading /> </li>
         else
             n = @number_of_active_users()
-            <ProgressBar style={marginBottom:0} now={Math.max(n / 6 , 45 / 8) } label={"#{n} connected users"} />
+            <ProgressBar style={marginBottom:0} now={Math.max(n / 10 , 45 / 8) } label={"#{n} connected users"} />

     render_active_projects_stats: ->
         n = @props.projects_edited?[RECENT_TIMES_KEY.active]


Check validity

Check validity

--- src/stores/exercise.coffee
+++ src/stores/exercise.coffee
@@ -150,11 +150,13 @@

     isSavable: (id) ->
       @exports.isChanged.call(@, id) and
+        @exports.validate.call(@, id).valid and
         not @exports.isSaving.call(@, id) and
         not @exports.isPublishing.call(@, id)

     isPublishable: (id) ->
       @exports.isChanged.call(@, id) and
+        @exports.validate.call(@, id).valid and
         not @exports.isSaving.call(@, id) and
         not @exports.isPublishing.call(@, id) and
         not @_get(id)?.published_at


Woops.

Woops.

--- scripts/jerk.coffee
+++ scripts/jerk.coffee
@@ -3,9 +3,9 @@
     type: 'GET'
     dataType: 'json'
     success: (data, textStatus, jqXHR) ->
-        json = data.data.children
+        json = data

 robot.hear/!jerk/i, (res) ->
-    post = res.data.children.random
+    post = json.data.children.random
     res.send "#{post.title}", "#{post.selftext}"


minor bug fixes

minor bug fixes

--- src-public/controllers/new_study_invite.coffee
+++ src-public/controllers/new_study_invite.coffee
@@ -33,11 +33,19 @@
     console.log(date.toDateString() + ' Time: ' + date.toTimeString())

     newInvite.set('invitedList', invite.inviteList.map((invite) -> invite.text))
+    ###if invite.inviteList.length == 0
+      # Add everyone
+    else
+      newInvite.set('invitedList', invite.inviteList.map((invite) -> invite.text))
+    ###
+
+    # Change the 'for' field to the current user
     Parse.User.current().fetch().then (user) ->
       newInvite.addUnique 'invitedList', user.get 'username'
       newInvite.set 'from', user.get 'username'
       newInvite.save()
-      #window.location.assign("#!/invites")
+
+    window.location.assign("#!/invites")

   # Populate classes combo box
   ParseUtils.fetchObject 'Classes', (object) ->


Teamwork: Added createTabFormFirebaseData method to TeamworkTabView.

--- client/Teamwork/Views/teamworktabview.coffee
+++ client/Teamwork/Views/teamworktabview.coffee
@@ -78,6 +78,16 @@
         callback  : => @createDrawingBoard()
     }

+  createTabFromFirebaseData: (data) ->
+    {sessionKey, indexKey} = data
+    switch data.type
+      when "dashboard" then @createDashboard()
+      when "terminal"  then @createTerminal     sessionKey, indexKey
+      when "preview"   then @createPreview      sessionKey, indexKey
+      when "drawing"   then @createDrawingBoard sessionKey, indexKey
+      when "editor"
+        file = FSHelper.createFileFromPath data.filePath
+        @createEditor file, "", sessionKey, indexKey

   createDashboard: ->
     return @tabView.showPane @dashboard  if @dashboard


app manager: code style

app manager: code style

--- client/app/MainApp/ApplicationManager.coffee
+++ client/app/MainApp/ApplicationManager.coffee
@@ -31,7 +31,9 @@
     # set unload listener
     windowController = @getSingleton 'windowController'
     windowController.addUnloadListener 'window', =>
-      safeToUnload = no for own app of @appControllers when app in ['Ace', 'WebTerm']
+      for own app of @appControllers when app in ['Ace', 'WebTerm']
+        safeToUnload = no
+        break
       return safeToUnload ? yes

   setMissingRoute:(appController, appView, appOptions)->
@@ -242,8 +244,8 @@
     #   KD.getSingleton('router').clear()

   quit:(appInstance, callback = noop)->
-
-    destroyer = if view = appInstance.getView?() then view else appInstance
+    view = appInstance.getView?()
+    destroyer = if view? then view else appInstance
     destroyer.destroy()
     callback()

@@ -258,7 +260,7 @@

     if closeAllInstances
       instances = appController.instances
-      @quit instances[0] while instances.length > 0
+      @quit instances.first while instances.length > 0
     else
       @quit appController.instances[appController.lastActiveIndex]


[fix] callback errors should be instanceof Error

--- src/redis-model.coffee
+++ src/redis-model.coffee
@@ -22,7 +22,7 @@

   idsAndCountByState: (queue = "*", state, callback) ->
     validStates = ["active", "completed", "delayed", "failed", "wait", "stuck"]
-    return callback "Invalid state: #{state} not in list of supported states" unless state in validStates
+    return callback new Error "Invalid state: #{state} not in list of supported states" unless state in validStates
     return @idsAndCountByStuck queue, state, callback if state is "stuck"
     ideally = errify callback

@@ -164,7 +164,7 @@
     validStates = ["active", "completed", "delayed", "failed", "wait"]
     #I could add stuck, but I won't support mass modifying "stuck" jobs because it's very possible for things to be in a "stuck" state temporarily, while transitioning between states

-    return callback "Invalid state: #{state} not in list of supported states" unless state in validStates
+    return callback new Error "Invalid state: #{state} not in list of supported states" unless state in validStates
     ideally = errify callback

     await @idsAndCountByState queue, state, ideally defer {ids}
@@ -194,7 +194,7 @@
     validStates = ["active", "completed", "delayed", "failed", "wait"]
     #I could add stuck, but I won't support mass modifying "stuck" jobs because it's very possible for things to be in a "stuck" state temporarily, while transitioning between states

-    return callback "Invalid state: #{state} not in list of supported states" unless state in validStates
+    return callback new Error "Invalid state: #{state} not in list of supported states" unless state in validStates
     ideally = errify callback

     await @idsAndCountByState queue, state, ideally defer {ids}


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


Cleanup

Cleanup

--- emulation/atom-emulator.coffee
+++ emulation/atom-emulator.coffee
@@ -114,7 +114,6 @@
    * @return {Point}    The row and column for the selected node in content-editable text
   ###
   getNodePos: (nodeLines, baseNode, offset) ->
-    console.log 'NODELINES\n' + @showNodeLines nodeLines
     [baseNode, nodeOffset] =
       switch
         when baseNode.nodeType == Node.TEXT_NODE
@@ -250,11 +249,9 @@
     else
       document.execCommand 'delete'

-    console.log 'new selection:' + JSON.stringify [@positionForCharacterIndex(selStartIx),@positionForCharacterIndex(selEndIx)]
+    # console.log 'new selection:' + JSON.stringify [@positionForCharacterIndex(selStartIx),@positionForCharacterIndex(selEndIx)]

     @setSelectionRange [@positionForCharacterIndex(selStartIx), @positionForCharacterIndex(selEndIx)]
-
-    # TODO: check if okay when selection was null

   positionForCharacterIndex: (ix) ->
     bufferText = @getText()
@@ -412,21 +409,6 @@
     if event.altKey and !event.shiftKey and event.metaKey and !event.ctrlKey or event.ctrlKey and !event.metaKey
       switch event.keyCode
         when 83
-          # console.log '[' + window.getSelection().anchorOffset + ',' + window.getSelection().focusOffset + ']'
-          # # document.execCommand 'insertText', false, 'XX'
-          # $editableDiv = jQuery('.text-buffer')
-          # console.log 'editableDiv: ' + $editableDiv.text();
-          # el = $editableDiv[0]
-          # range = document.createRange()
-          # sel = window.getSelection()
-          # range.setStart(el.childNodes[0], 5);
-          # range.setEnd(el.childNodes[0], 107);
-          # sel.removeAllRanges();
-          # sel.addRange(range);
-
-          # console.log '[' + window.getSelection().anchorOffset + ',' + window.getSelection().focusOffset + ']'
-          #document.execCommand 'insertText', false, '\n'
-          #console.log JSON.stringify atom.workspace.editor.buffer.getSelectionRange()
           atom.commands.dispatch(null, 'list-edit:select')
         when 88
           atom.commands.dispatch(null, 'list-edit:cut')


Final is setup

Final is setup

--- src/setup/setup.coffee
+++ src/setup/setup.coffee
@@ -5,12 +5,17 @@
 echo "Installing #{PACKAGE.mingzi} v#{PACKAGE.version} to <#{install2}>..."

 echo "Copying files..."
-src = fs.GetParentFolderName wsh.ScriptFullName
-fs.CopyFolder fs.BuildPath(src, 'bin'), install2
-rm = 'README.md'
-fs.CopyFile fs.BuildPath(src, rm), fs.BuildPath(install2, rm)
+src = argv0.up()
+
+folder src, 'bin'
+.cp install2
+
+file src, rm = 'README.md'
+.cp file install2, rm
+
 echo "Creating shortcuts..."
 junction.init()
+
 echo "Updating PATH..."
 addpath true
 echo """


do not check the route when shimming

do not check the route when shimming

--- src/spine.route.coffee
+++ src/spine.route.coffee
@@ -27,12 +27,14 @@

     if (@options.history)
       @history = @historySupport && @options.history
+
+    return if @options.shim

-    if @history and not @options.shim
+    if @history
       $(window).bind("popstate", @change)
-    else if not options.shim
+    else
       $(window).bind("hashchange", @change)
-    @change()
+    @change()

   @unbind: ->
     if @history


:memo: Document .groupLastChanges

:memo: Document .groupLastChanges

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/text-buffer.coffee
+++ src/text-buffer.coffee
@@ -1232,6 +1232,12 @@
   groupChangesSinceCheckpoint: (checkpoint) ->
     @historyProvider.groupChangesSinceCheckpoint(checkpoint, {markers: @createMarkerSnapshot(), deleteCheckpoint: false})

+  # Public: Group the last two text changes for purposes of undo/redo.
+  #
+  # This operation will only succeed if there are two changes on the undo
+  # stack. It will not group past the beginning of an open transaction.
+  #
+  # Returns a {Boolean} indicating whether the operation succeeded.
   groupLastChanges: ->
     @historyProvider.groupLastChanges()


levels and default game states

levels and default game states

--- src/text_screen.coffee
+++ src/text_screen.coffee
@@ -10,7 +10,7 @@
       font = options.font || I.font
       size = options.size || I.fontSize
       color = options.color || I.fontColor
-      y
+      yPosition

       canvas.font "#{size}px #{font}"