:art: also use path.join in the specs file

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -1467,7 +1467,7 @@
               atom.commands.dispatch(treeView.element, "tree-view:paste")

               extension = getFullExtension(filePath)
-              file = path.dirname(filePath) + path.sep + path.basename(filePath, extension)
+              file = path.join(path.dirname(filePath), path.basename(filePath, extension))

               numberedFileName0 = "#{file}0#{extension}"
               numberedFileName1 = "#{file}1#{extension}"
@@ -1501,7 +1501,7 @@
                 atom.commands.dispatch(treeView.element, "tree-view:paste")

                 extension = getFullExtension(dotFilePath)
-                file = path.dirname(dotFilePath) + path.sep + path.basename(dotFilePath, extension)
+                file = path.join(path.dirname(dotFilePath), path.basename(dotFilePath, extension))

                 numberedFileName0 = "#{file}0#{extension}"
                 numberedFileName1 = "#{file}1#{extension}"
@@ -1528,7 +1528,7 @@
               atom.commands.dispatch(treeView.element, "tree-view:paste")

               extension = getFullExtension(filePath)
-              file = path.dirname(filePath) + path.sep + path.basename(filePath, extension)
+              file = path.join(path.dirname(filePath), path.basename(filePath, extension))

               numberedFileName0 = "#{file}0#{extension}"
               numberedFileName1 = "#{file}1#{extension}"
@@ -1567,7 +1567,7 @@
               atom.commands.dispatch(treeView.element, "tree-view:paste")

               extension = getFullExtension(dotFilePath)
-              file = path.dirname(dotFilePath) + path.sep + path.basename(dotFilePath, extension)
+              file = path.join(path.dirname(dotFilePath), path.basename(dotFilePath, extension))

               numberedFileName0 = "#{file}0#{extension}"
               numberedFileName1 = "#{file}1#{extension}"


Finish removing jQuery from tree-view-spec.coffee

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -225,7 +225,7 @@
     it "restores the focus state of the tree view", ->
       jasmine.attachToDOM(workspaceElement)
       treeView.focus()
-      expect(treeView.list).toMatchSelector ':focus'
+      expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
       atom.packages.deactivatePackage("tree-view")

       waitsForPromise ->
@@ -233,7 +233,7 @@

       runs ->
         treeView = atom.workspace.getLeftPanels()[0].getItem()
-        expect(treeView.list).toMatchSelector ':focus'
+        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

     it "restores the scroll top when toggled", ->
       workspaceElement.style.height = '5px'
@@ -294,7 +294,7 @@
         treeView.detach()
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle')
         expect(treeView.hasParent()).toBeTruthy()
-        expect(treeView.list).toMatchSelector(':focus')
+        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

     describe "when tree-view:toggle-side is triggered on the root view", ->
       describe "when the tree view is on the left", ->
@@ -330,7 +330,7 @@
         treeView.detach()
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
         expect(treeView.hasParent()).toBeTruthy()
-        expect(treeView.list).toMatchSelector(':focus')
+        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

     describe "when the tree view is shown", ->
       it "focuses the tree view", ->
@@ -342,7 +342,7 @@
           expect(treeView).toBeVisible()
           atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
           expect(treeView).toBeVisible()
-          expect(treeView.list).toMatchSelector(':focus')
+          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

       describe "when the tree view is focused", ->
         it "unfocuses the tree view", ->
@@ -354,7 +354,7 @@
             expect(treeView).toBeVisible()
             atom.commands.dispatch(workspaceElement, 'tree-view:toggle-focus')
             expect(treeView).toBeVisible()
-            expect(treeView.list).not.toMatchSelector(':focus')
+            expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus()

   describe "when tree-view:reveal-active-file is triggered on the root view", ->
     beforeEach ->
@@ -464,10 +464,10 @@
       runs ->
         jasmine.attachToDOM(workspaceElement)
         treeView.focus()
-        expect(treeView.list).toMatchSelector(':focus')
+        expect(treeView.element.querySelector('.tree-view')).toHaveFocus()
         atom.commands.dispatch(treeView.element, 'tool-panel:unfocus')
         expect(treeView).toBeVisible()
-        expect(treeView.list).not.toMatchSelector(':focus')
+        expect(treeView.element.querySelector('.tree-view')).not.toHaveFocus()
         expect(atom.workspace.getActivePane().isActive()).toBe(true)

   describe "copy path commands", ->
@@ -2132,7 +2132,7 @@
           it "removes the dialog and focuses the tree view", ->
             atom.commands.dispatch moveDialog.element, 'core:cancel'
             expect(atom.workspace.getModalPanels().length).toBe 0
-            expect(treeView.find(".tree-view")).toMatchSelector(':focus')
+            expect(treeView.find(".tree-view")).toHaveFocus()

         describe "when the move dialog's editor loses focus", ->
           it "removes the dialog and focuses root view", ->
@@ -2250,7 +2250,7 @@
             jasmine.attachToDOM(treeView.element)
             atom.commands.dispatch copyDialog.element, 'core:cancel'
             expect(atom.workspace.getModalPanels().length).toBe 0
-            expect(treeView.find(".tree-view")).toMatchSelector(':focus')
+            expect(treeView.find(".tree-view")).toHaveFocus()

         describe "when the duplicate dialog's editor loses focus", ->
           it "removes the dialog and focuses root view", ->
@@ -2649,14 +2649,6 @@
           muEntries = Array.from(muDir.children[1].children).map (element) -> element.innerText
           expect(muEntries).toEqual(["nu#{path.sep}xi", "xi"])

-    findDirectoryContainingText = (element, text) ->
-      directories = Array.from(element.querySelectorAll('.entries .directory'))
-      directories.find((directory) -> directory.header.textContent is text)
-
-    findFileContainingText = (element, text) ->
-      files = Array.from(element.querySelectorAll('.entries .file'))
-      files.find((file) -> file.fileName.textContent is text)
-
   describe "Git status decorations", ->
     [projectPath, modifiedFile, originalFileContent] = []

@@ -2686,60 +2678,59 @@

       treeView.useSyncFS = true
       treeView.updateRoots()
-      $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand()
+      treeView.roots[0].entries.querySelectorAll('.directory')[1].expand()

     describe "when the project is the repository root", ->
       it "adds a custom style", ->
-        expect(treeView.find('.icon-repo').length).toBe 1
+        expect(treeView.element.querySelectorAll('.icon-repo').length).toBe 1

     describe "when a file is modified", ->
       it "adds a custom style", ->
-        $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand()
-        expect(treeView.find('.file:contains(b.txt)')).toHaveClass 'status-modified'
+        expect(treeView.element.querySelector('.file.status-modified')).toHaveText('b.txt')

     describe "when a directory if modified", ->
       it "adds a custom style", ->
-        expect(treeView.find('.directory:contains(dir)')).toHaveClass 'status-modified'
+        expect(treeView.element.querySelector('.directory.status-modified').header).toHaveText('dir')

     describe "when a file is new", ->
       it "adds a custom style", ->
-        $(treeView.roots[0].entries).find('.directory:contains(dir2)')[0].expand()
-        expect(treeView.find('.file:contains(new2)')).toHaveClass 'status-added'
+        treeView.roots[0].entries.querySelectorAll('.directory')[2].expand()
+        expect(treeView.element.querySelector('.file.status-added')).toHaveText('new2')

     describe "when a directory is new", ->
       it "adds a custom style", ->
-        expect(treeView.find('.directory:contains(dir2)')).toHaveClass 'status-added'
+        expect(treeView.element.querySelector('.directory.status-added').header).toHaveText('dir2')

     describe "when a file is ignored", ->
       it "adds a custom style", ->
-        expect(treeView.find('.file:contains(ignored.txt)')).toHaveClass 'status-ignored'
+        expect(treeView.element.querySelector('.file.status-ignored')).toHaveText('ignored.txt')

     describe "when a file is selected in a directory", ->
       beforeEach ->
         jasmine.attachToDOM(workspaceElement)
         treeView.focus()
-        element.expand() for element in treeView.find('.directory')
-        fileView = treeView.find('.file:contains(new2)')
+        element.expand() for element in treeView.element.querySelectorAll('.directory')
+        fileView = treeView.element.querySelector('.file.status-added')
         expect(fileView).not.toBeNull()
         fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

       describe "when the file is deleted", ->
         it "updates the style of the directory", ->
           expect(treeView.selectedEntry().getPath()).toContain(path.join('dir2', 'new2'))
-          dirView = $(treeView.roots[0].entries).find('.directory:contains(dir2)')
+          dirView = findDirectoryContainingText(treeView.roots[0], 'dir2')
           expect(dirView).not.toBeNull()
-          spyOn(dirView[0].directory, 'updateStatus')
+          spyOn(dirView.directory, 'updateStatus')
           spyOn(atom, 'confirm').andCallFake (dialog) ->
             dialog.buttons["Move to Trash"]()
           atom.commands.dispatch(treeView.element, 'tree-view:remove')
-          expect(dirView[0].directory.updateStatus).toHaveBeenCalled()
+          expect(dirView.directory.updateStatus).toHaveBeenCalled()

     describe "when the project is a symbolic link to the repository root", ->
       beforeEach ->
         symlinkPath = temp.path('tree-view-project')
         fs.symlinkSync(projectPath, symlinkPath, 'junction')
         atom.project.setPaths([symlinkPath])
-        $(treeView.roots[0].entries).find('.directory:contains(dir)')[0].expand()
+        treeView.roots[0].entries.querySelectorAll('.directory')[1].expand()

         waitsFor (done) ->
           disposable = atom.project.getRepositories()[0].onDidChangeStatuses ->
@@ -2748,29 +2739,31 @@

       describe "when a file is modified", ->
         it "updates its and its parent directories' styles", ->
-          expect(treeView.find('.file:contains(b.txt)')).toHaveClass 'status-modified'
-          expect(treeView.find('.directory:contains(dir)')).toHaveClass 'status-modified'
+          expect(treeView.element.querySelector('.file.status-modified')).toHaveText('b.txt')
+          expect(treeView.element.querySelector('.directory.status-modified').header).toHaveText('dir')

       describe "when a file loses its modified status", ->
         it "updates its and its parent directories' styles", ->
           fs.writeFileSync(modifiedFile, originalFileContent)
           atom.project.getRepositories()[0].getPathStatus(modifiedFile)

-          expect(treeView.find('.file:contains(b.txt)')).not.toHaveClass 'status-modified'
-          expect(treeView.find('.directory:contains(dir)')).not.toHaveClass 'status-modified'
+          expect(treeView.element.querySelector('.file.status-modified')).not.toExist()
+          expect(treeView.element.querySelector('.directory.status-modified')).not.toExist()

   describe "when the resize handle is double clicked", ->
     beforeEach ->
-      treeView.width(10).find('.list-tree').width 100
+      treeView.element.style.width = '10px'
+      treeView.element.querySelector('.list-tree').style.width = '100px'
+      jasmine.attachToDOM(workspaceElement)

     it "sets the width of the tree to be the width of the list", ->
-      expect(treeView.width()).toBe 10
-      treeView.find('.tree-view-resize-handle').trigger 'dblclick'
-      expect(treeView.width()).toBeGreaterThan 10
-
-      treeView.width(1000)
-      treeView.find('.tree-view-resize-handle').trigger 'dblclick'
-      expect(treeView.width()).toBeLessThan 1000
+      expect(parseInt(treeView.element.style.width)).toBe 10
+      treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true}))
+      expect(parseInt(treeView.element.style.width)).toBeGreaterThan 10
+
+      treeView.element.style.width = '1000px'
+      treeView.element.querySelector('.tree-view-resize-handle').dispatchEvent(new MouseEvent('dblclick', {bubbles: true}))
+      expect(parseInt(treeView.element.style.width)).toBeLessThan 1000

   describe "when other panels are added", ->
     beforeEach ->
@@ -2780,20 +2773,20 @@
       expect(treeView).toBeVisible()
       expect(atom.workspace.getLeftPanels().length).toBe(1)

-      treeView.width(100)
-
-      expect(treeView.width()).toBe(100)
+      treeView.element.style.width = '100px'
+
+      expect(parseInt(treeView.element.style.width)).toBe(100)

       panel = document.createElement('div')
       panel.style.width = '100px'
       atom.workspace.addLeftPanel({item: panel, priority: 10})

       expect(atom.workspace.getLeftPanels().length).toBe(2)
-      expect(treeView.width()).toBe(100)
+      expect(parseInt(treeView.element.style.width)).toBe(100)

       treeView.resizeTreeView({pageX: 250, which: 1})

-      expect(treeView.width()).toBe(150)
+      expect(parseInt(treeView.element.style.width)).toBe(150)

     it "should resize normally on the right side", ->
       atom.commands.dispatch(workspaceElement, 'tree-view:toggle-side')
@@ -2802,20 +2795,20 @@
       expect(treeView).toBeVisible()
       expect(atom.workspace.getRightPanels().length).toBe(1)

-      treeView.width(100)
-
-      expect(treeView.width()).toBe(100)
+      treeView.element.style.width = '100px'
+
+      expect(parseInt(treeView.element.style.width)).toBe(100)

       panel = document.createElement('div')
       panel.style.width = '100px'
       atom.workspace.addRightPanel({item: panel, priority: 10})

       expect(atom.workspace.getRightPanels().length).toBe(2)
-      expect(treeView.width()).toBe(100)
-
-      treeView.resizeTreeView({pageX: $(document.body).width() - 250, which: 1})
-
-      expect(treeView.width()).toBe(150)
+      expect(parseInt(treeView.element.style.width)).toBe(100)
+
+      treeView.resizeTreeView({pageX: document.body.offsetWidth - 250, which: 1})
+
+      expect(parseInt(treeView.element.style.width)).toBe(150)

   describe "selecting items", ->
     [dirView, fileView1, fileView2, fileView3, treeView, rootDirPath, dirPath, filePath1, filePath2, filePath3] = []
@@ -2835,30 +2828,28 @@

       atom.project.setPaths([rootDirPath])

-      dirView = $(treeView.roots[0].entries).find('.directory:contains(test-dir)')
-      dirView[0].expand()
-      fileView1 = treeView.find('.file:contains(test-file1.txt)')
-      fileView2 = treeView.find('.file:contains(test-file2.txt)')
-      fileView3 = treeView.find('.file:contains(test-file3.txt)')
+      dirView = treeView.entryForPath(dirPath)
+      dirView.expand()
+      [fileView1, fileView2, fileView3] = dirView.querySelectorAll('.file')

     describe 'selecting multiple items', ->
       it 'switches the contextual menu to muli-select mode', ->
         fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-        fileView2.trigger($.Event('mousedown', {shiftKey: true}))
-        expect(treeView.find('.tree-view')).toHaveClass('multi-select')
-        fileView3.trigger($.Event('mousedown'))
-        expect(treeView.find('.tree-view')).toHaveClass('full-menu')
+        fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true}))
+        expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')
+        fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true}))
+        expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')

     describe 'selecting multiple items', ->
       it 'switches the contextual menu to muli-select mode', ->
         fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-        fileView2.trigger($.Event('mousedown', {shiftKey: true}))
+        fileView2.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true}))
         expect(treeView.find('.tree-view')).toHaveClass('multi-select')

       describe 'using the shift key', ->
         it 'selects the items between the already selected item and the shift clicked item', ->
           fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-          fileView3.trigger($.Event('mousedown', {shiftKey: true}))
+          fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, shiftKey: true}))
           expect(fileView1).toHaveClass('selected')
           expect(fileView2).toHaveClass('selected')
           expect(fileView3).toHaveClass('selected')
@@ -2866,7 +2857,7 @@
       describe 'using the metakey(cmd) key', ->
         it 'selects the cmd clicked item in addition to the original selected item', ->
           fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-          fileView3.trigger($.Event('mousedown', {metaKey: true}))
+          fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true}))
           expect(fileView1).toHaveClass('selected')
           expect(fileView3).toHaveClass('selected')
           expect(fileView2).not.toHaveClass('selected')
@@ -2885,7 +2876,7 @@
         describe 'using the ctrl key', ->
           it 'selects the ctrl clicked item in addition to the original selected item', ->
             fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-            fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
+            fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
             expect(fileView1).toHaveClass('selected')
             expect(fileView3).toHaveClass('selected')
             expect(fileView2).not.toHaveClass('selected')
@@ -2905,87 +2896,87 @@
           describe "previous item is selected but the ctrl clicked item is not", ->
             it 'selects the clicked item, but deselects the previous item', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
               expect(fileView1).not.toHaveClass('selected')
               expect(fileView3).toHaveClass('selected')
               expect(fileView2).not.toHaveClass('selected')

             it 'displays the full contextual menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
-              expect(treeView.list).toHaveClass('full-menu')
-              expect(treeView.list).not.toHaveClass('multi-select')
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')

           describe 'previous item is selected including the ctrl clicked', ->
             it 'displays the multi-select menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {metaKey: true}))
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
-              expect(treeView.list).not.toHaveClass('full-menu')
-              expect(treeView.list).toHaveClass('multi-select')
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')

             it 'does not deselect any of the items', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {metaKey: true}))
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
               expect(fileView1).toHaveClass('selected')
               expect(fileView3).toHaveClass('selected')

           describe 'when clicked item is the only item selected', ->
             it 'displays the full contextual menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
-              expect(treeView.list).toHaveClass('full-menu')
-              expect(treeView.list).not.toHaveClass('multi-select')
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')

           describe 'when no item is selected', ->
             it 'selects the ctrl clicked item', ->
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
               expect(fileView3).toHaveClass('selected')

             it 'displays the full context menu', ->
-              fileView3.trigger($.Event('mousedown', {ctrlKey: true}))
-              expect(treeView.list).toHaveClass('full-menu')
-              expect(treeView.list).not.toHaveClass('multi-select')
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, ctrlKey: true}))
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')

         describe "right-clicking", ->
           describe 'when multiple items are selected', ->
             it 'displays the multi-select context menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {metaKey: true}))
-              fileView3.trigger($.Event('mousedown', {button: 2}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, metaKey: true}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView1).toHaveClass('selected')
               expect(fileView3).toHaveClass('selected')
-              expect(treeView.list).not.toHaveClass('full-menu')
-              expect(treeView.list).toHaveClass('multi-select')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('multi-select')

           describe 'when a single item is selected', ->
             it 'displays the full context menu', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {button: 2}))
-              expect(treeView.list).toHaveClass('full-menu')
-              expect(treeView.list).not.toHaveClass('multi-select')
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')

             it 'selects right clicked item', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {button: 2}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView3).toHaveClass('selected')

             it 'de-selects the previously selected item', ->
               fileView1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
-              fileView3.trigger($.Event('mousedown', {button: 2}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView1).not.toHaveClass('selected')

           describe 'when no item is selected', ->
             it 'selects the right clicked item', ->
-              fileView3.trigger($.Event('mousedown', {button: 2}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView3).toHaveClass('selected')

             it 'shows the full context menu', ->
-              fileView3.trigger($.Event('mousedown', {button: 2}))
+              fileView3.dispatchEvent(new MouseEvent('mousedown', {bubbles: true, button: 2}))
               expect(fileView3).toHaveClass('selected')
-              expect(treeView.list).toHaveClass('full-menu')
-              expect(treeView.list).not.toHaveClass('multi-select')
+              expect(treeView.element.querySelector('.tree-view')).toHaveClass('full-menu')
+              expect(treeView.element.querySelector('.tree-view')).not.toHaveClass('multi-select')

   describe "the sortFoldersBeforeFiles config option", ->
     [dirView, fileView, dirView2, fileView2, fileView3, rootDirPath, dirPath, filePath, dirPath2, filePath2, filePath3] = []
@@ -3031,16 +3022,16 @@

       expect(topLevelEntries).toEqual(["alpha", "gamma", "alpha.txt", "zeta.txt"])

-      alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-      alphaDir[0].expand()
-      alphaEntries = [].slice.call(alphaDir[0].children[1].children).map (element) ->
+      alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+      alphaDir.expand()
+      alphaEntries = [].slice.call(alphaDir.children[1].children).map (element) ->
         element.innerText

       expect(alphaEntries).toEqual(["eta", "beta.txt"])

-      gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
-      gammaDir[0].expand()
-      gammaEntries = [].slice.call(gammaDir[0].children[1].children).map (element) ->
+      gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
+      gammaDir.expand()
+      gammaEntries = [].slice.call(gammaDir.children[1].children).map (element) ->
         element.innerText

       expect(gammaEntries).toEqual(["theta", "delta.txt", "epsilon.txt"])
@@ -3053,16 +3044,16 @@

       expect(topLevelEntries).toEqual(["alpha", "alpha.txt", "gamma", "zeta.txt"])

-      alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-      alphaDir[0].expand()
-      alphaEntries = [].slice.call(alphaDir[0].children[1].children).map (element) ->
+      alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+      alphaDir.expand()
+      alphaEntries = [].slice.call(alphaDir.children[1].children).map (element) ->
         element.innerText

       expect(alphaEntries).toEqual(["beta.txt", "eta"])

-      gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
-      gammaDir[0].expand()
-      gammaEntries = [].slice.call(gammaDir[0].children[1].children).map (element) ->
+      gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
+      gammaDir.expand()
+      gammaEntries = [].slice.call(gammaDir.children[1].children).map (element) ->
         element.innerText

       expect(gammaEntries).toEqual(["delta.txt", "epsilon.txt", "theta"])
@@ -3123,7 +3114,7 @@
       waitsForPromise ->
         atom.workspace.open()
       runs ->
-        $(workspaceElement).focus()
+        workspaceElement.focus()
         expect(treeView.showCurrentFileInFileManager()).toBeUndefined()

     it "shows file in file manager when some file is opened", ->
@@ -3205,14 +3196,15 @@
     describe "when dragging a FileView onto a DirectoryView's header", ->
       it "should add the selected class to the DirectoryView", ->
         # Dragging theta onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-
-        gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
-        gammaDir[0].expand()
-        deltaFile = gammaDir[0].entries.children[2]
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
+        gammaDir.expand()
+        deltaFile = gammaDir.entries.children[2]

         [dragStartEvent, dragEnterEvent, dropEvent] =
-            eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0])
+            eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header'))
         treeView.onDragStart(dragStartEvent)
         treeView.onDragEnter(dragEnterEvent)
         expect(alphaDir).toHaveClass('selected')
@@ -3228,104 +3220,104 @@
     describe "when dropping a FileView onto a DirectoryView's header", ->
       it "should move the file to the hovered directory", ->
         # Dragging delta.txt onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-        alphaDir[0].expand()
-
-        gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
-        gammaDir[0].expand()
-        deltaFile = gammaDir[0].entries.children[2]
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
+        gammaDir.expand()
+        deltaFile = gammaDir.entries.children[2]

         [dragStartEvent, dragEnterEvent, dropEvent] =
-            eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.find('.header')[0], alphaDir[0])
+            eventHelpers.buildInternalDragEvents(deltaFile, alphaDir.querySelector('.header'), alphaDir)

         runs ->
           treeView.onDragStart(dragStartEvent)
           treeView.onDrop(dropEvent)
-          expect(alphaDir[0].children.length).toBe 2
+          expect(alphaDir.children.length).toBe 2

         waitsFor "directory view contents to refresh", ->
-          $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2
+          findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2

         runs ->
-          expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3
+          expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3

     describe "when dropping a DirectoryView onto a DirectoryView's header", ->
       it "should move the directory to the hovered directory", ->
         # Dragging thetaDir onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-        alphaDir[0].expand()
-
-        gammaDir = $(treeView.roots[0].entries).find('.directory:contains(gamma):first')
-        gammaDir[0].expand()
-        thetaDir = gammaDir[0].entries.children[0]
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma')
+        gammaDir.expand()
+        thetaDir = gammaDir.entries.children[0]

         [dragStartEvent, dragEnterEvent, dropEvent] =
-            eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.find('.header')[0], alphaDir[0])
+            eventHelpers.buildInternalDragEvents(thetaDir, alphaDir.querySelector('.header'), alphaDir)

         runs ->
           treeView.onDragStart(dragStartEvent)
           treeView.onDrop(dropEvent)
-          expect(alphaDir[0].children.length).toBe 2
+          expect(alphaDir.children.length).toBe 2

         waitsFor "directory view contents to refresh", ->
-          $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2
+          findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2

         runs ->
-          expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3
+          expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3

     describe "when dragging a file from the OS onto a DirectoryView's header", ->
       it "should move the file to the hovered directory", ->
         # Dragging delta.txt from OS file explorer onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-        alphaDir[0].expand()
-
-        dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir[0])
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir)

         runs ->
           treeView.onDrop(dropEvent)
-          expect(alphaDir[0].children.length).toBe 2
+          expect(alphaDir.children.length).toBe 2

         waitsFor "directory view contents to refresh", ->
-          $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2
+          findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2

         runs ->
-          expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3
+          expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3

     describe "when dragging a directory from the OS onto a DirectoryView's header", ->
       it "should move the directory to the hovered directory", ->
         # Dragging gammaDir from OS file explorer onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-        alphaDir[0].expand()
-
-        dropEvent = eventHelpers.buildExternalDropEvent([gammaDirPath], alphaDir[0])
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        dropEvent = eventHelpers.buildExternalDropEvent([gammaDirPath], alphaDir)

         runs ->
           treeView.onDrop(dropEvent)
-          expect(alphaDir[0].children.length).toBe 2
+          expect(alphaDir.children.length).toBe 2

         waitsFor "directory view contents to refresh", ->
-          $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 2
+          findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2

         runs ->
-          expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 3
+          expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3

     describe "when dragging a file and directory from the OS onto a DirectoryView's header", ->
       it "should move the file and directory to the hovered directory", ->
         # Dragging delta.txt and gammaDir from OS file explorer onto alphaDir
-        alphaDir = $(treeView.roots[0].entries).find('.directory:contains(alpha):first')
-        alphaDir[0].expand()
-
-        dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir[0])
+        alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha')
+        alphaDir.expand()
+
+        dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir)

         runs ->
           treeView.onDrop(dropEvent)
-          expect(alphaDir[0].children.length).toBe 2
+          expect(alphaDir.children.length).toBe 2

         waitsFor "directory view contents to refresh", ->
-          $(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length > 3
+          findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 3

         runs ->
-          expect($(treeView.roots[0].entries).find('.directory:contains(alpha):first .entry').length).toBe 4
+          expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4

   describe "the alwaysOpenExisting config option", ->
     it "defaults to unset", ->
@@ -3340,21 +3332,21 @@
         treeView.focus()

         waitsForFileToOpen ->
-          sampleJs.trigger clickEvent(originalEvent: {detail: 1})
+          sampleJs.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

         runs ->
           expect(sampleJs).toHaveClass 'selected'
           expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.js')
-          expect(treeView.list).toHaveFocus()
+          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

         waitsForFileToOpen ->
-          sampleTxt.trigger clickEvent(originalEvent: {detail: 1})
+          sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

         runs ->
           expect(sampleTxt).toHaveClass 'selected'
-          expect(treeView.find('.selected').length).toBe 1
+          expect(treeView.element.querySelectorAll('.selected').length).toBe 1
           expect(atom.workspace.getActivePaneItem().getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')
-          expect(treeView.list).toHaveFocus()
+          expect(treeView.element.querySelector('.tree-view')).toHaveFocus()

     describe "opening existing opened files in existing split panes", ->
       beforeEach ->
@@ -3420,7 +3412,7 @@
             treeView.focus()

             waitsForFileToOpen ->
-              sampleTxt.trigger clickEvent(originalEvent: {detail: 1})
+              sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

             runs ->
               activePaneItem = atom.workspace.getActivePaneItem()
@@ -3445,8 +3437,8 @@
           treeView.focus()

           waitsForFileToOpen ->
-            sampleTxt.trigger clickEvent(originalEvent: {detail: 1})
-            sampleTxt.trigger clickEvent(originalEvent: {detail: 2})
+            sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
+            sampleTxt.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 2}))

           waits 100

@@ -3499,14 +3491,14 @@
       describe "when dragging on the top part of the root", ->
         it "should add the placeholder above the directory", ->
           # Dragging gammaDir onto alphaDir
-          alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-          gammaDir = $(treeView).find('.project-root:contains(gamma):first')
+          alphaDir = treeView.roots[0]
+          gammaDir = treeView.roots[1]
           [dragStartEvent, dragOverEvents, dragEndEvent] =
-            eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view')
+            eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view')

           treeView.rootDragAndDrop.onDragStart(dragStartEvent)
           treeView.rootDragAndDrop.onDragOver(dragOverEvents.top)
-          expect(alphaDir[0].previousSibling).toHaveClass('placeholder')
+          expect(alphaDir.previousSibling).toHaveClass('placeholder')

           # Is removed when drag ends
           treeView.rootDragAndDrop.onDragEnd(dragEndEvent)
@@ -3515,14 +3507,14 @@
       describe "when dragging on the bottom part of the root", ->
         it "should add the placeholder below the directory", ->
           # Dragging gammaDir onto alphaDir
-          alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-          gammaDir = $(treeView).find('.project-root:contains(gamma):first')
+          alphaDir = treeView.roots[0]
+          gammaDir = treeView.roots[1]
           [dragStartEvent, dragOverEvents, dragEndEvent] =
-            eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view')
+            eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view')

           treeView.rootDragAndDrop.onDragStart(dragStartEvent)
           treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom)
-          expect(alphaDir[0].nextSibling).toHaveClass('placeholder')
+          expect(alphaDir.nextSibling).toHaveClass('placeholder')

           # Is removed when drag ends
           treeView.rootDragAndDrop.onDragEnd(dragEndEvent)
@@ -3531,16 +3523,16 @@
       describe "when below all entries", ->
         it "should add the placeholder below the last directory", ->
           # Dragging gammaDir onto alphaDir
-          alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-          lastDir = $(treeView).find('.project-root:last')
+          alphaDir = treeView.roots[0]
+          lastDir = treeView.roots[treeView.roots.length - 1]
           [dragStartEvent, dragOverEvents, dragEndEvent] =
-            eventHelpers.buildPositionalDragEvents(alphaDir.find('.project-root-header')[0], treeView.find('.tree-view')[0])
-
-          expect(alphaDir[0]).not.toEqual(lastDir[0])
+            eventHelpers.buildPositionalDragEvents(alphaDir.querySelector('.project-root-header'), treeView.element.querySelector('.tree-view'))
+
+          expect(alphaDir).not.toEqual(lastDir)

           treeView.rootDragAndDrop.onDragStart(dragStartEvent)
           treeView.rootDragAndDrop.onDragOver(dragOverEvents.bottom)
-          expect(lastDir[0].nextSibling).toHaveClass('placeholder')
+          expect(lastDir.nextSibling).toHaveClass('placeholder')

           # Is removed when drag ends
           treeView.rootDragAndDrop.onDragEnd(dragEndEvent)
@@ -3551,10 +3543,10 @@
       describe "when dropping on the top part of the header", ->
         it "should add the placeholder above the directory", ->
           # dropping gammaDir above alphaDir
-          alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-          gammaDir = $(treeView).find('.project-root:contains(gamma):first')
+          alphaDir = treeView.roots[0]
+          gammaDir = treeView.roots[1]
           [dragStartEvent, dragDropEvents] =
-            eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view')
+            eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'), alphaDir, '.tree-view')

           treeView.rootDragAndDrop.onDragStart(dragStartEvent)
           treeView.rootDragAndDrop.onDrop(dragDropEvents.top)
@@ -3568,10 +3560,10 @@
       describe "when dropping on the bottom part of the header", ->
         it "should add the placeholder below the directory", ->
           # dropping thetaDir below alphaDir
-          alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-          thetaDir = $(treeView).find('.project-root:contains(theta):first')
+          alphaDir = treeView.roots[0]
+          thetaDir = treeView.roots[2]
           [dragStartEvent, dragDropEvents] =
-            eventHelpers.buildPositionalDragEvents(thetaDir.find('.project-root-header')[0], alphaDir[0], '.tree-view')
+            eventHelpers.buildPositionalDragEvents(thetaDir.querySelector('.project-root-header'), alphaDir, '.tree-view')

           treeView.rootDragAndDrop.onDragStart(dragStartEvent)
           treeView.rootDragAndDrop.onDrop(dragDropEvents.bottom)
@@ -3585,8 +3577,8 @@

     describe "when a root folder is dragged out of application", ->
       it "should carry the folder's information", ->
-        gammaDir = $(treeView).find('.project-root:contains(gamma):first')
-        [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0])
+        gammaDir = treeView.roots[1]
+        [dragStartEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'))
         treeView.rootDragAndDrop.onDragStart(dragStartEvent)

         expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual gammaDirPath
@@ -3595,8 +3587,8 @@

     describe "when a root folder is dropped from another Atom window", ->
       it "adds the root folder to the window", ->
-        alphaDir = $(treeView).find('.project-root:contains(alpha):first')
-        [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.find('.project-root-header')[0], '.tree-view')
+        alphaDir = treeView.roots[0]
+        [_, dragDropEvents] = eventHelpers.buildPositionalDragEvents(null, alphaDir.querySelector('.project-root-header'), '.tree-view')

         dropEvent = dragDropEvents.bottom
         dropEvent.originalEvent.dataTransfer.setData('atom-tree-view-event', true)
@@ -3620,14 +3612,21 @@

     describe "when a root folder is dropped to another Atom window", ->
       it "removes the root folder from the first window", ->
-        gammaDir = $(treeView).find('.project-root:contains(gamma):first')
-        [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.find('.project-root-header')[0])
+        gammaDir = treeView.roots[1]
+        [dragStartEvent, dropEvent] = eventHelpers.buildPositionalDragEvents(gammaDir.querySelector('.project-root-header'))
         treeView.rootDragAndDrop.onDragStart(dragStartEvent)
-        treeView.rootDragAndDrop.onDropOnOtherWindow({}, gammaDir.index())
+        treeView.rootDragAndDrop.onDropOnOtherWindow({}, Array.from(gammaDir.parentElement.children).indexOf(gammaDir))

         expect(atom.project.getPaths()).toEqual [alphaDirPath, thetaDirPath]
         expect('.placeholder').not.toExist()

+  findDirectoryContainingText = (element, text) ->
+    directories = Array.from(element.querySelectorAll('.entries .directory'))
+    directories.find((directory) -> directory.header.textContent is text)
+
+  findFileContainingText = (element, text) ->
+    files = Array.from(element.querySelectorAll('.entries .file'))
+    files.find((file) -> file.fileName.textContent is text)

 describe 'Icon class handling', ->
   it 'allows multiple classes to be passed', ->


Add svgmin to 'develop' task

Add svgmin to 'develop' task


ApiRequest: remove unused stuff.

ApiRequest: remove unused stuff.

--- src/OVirtApiRequest.coffee
+++ src/OVirtApiRequest.coffee
@@ -24,9 +24,7 @@
   request: (options, callback) ->
     request options, (error, response, body) ->
       if not error and response.statusCode is 200
-        xml2js.parseString body, (error, result) ->
-          callback error if error
-          callback null, result
+        callback error, body
       else
         if error
           callback error
@@ -35,12 +33,6 @@

   getRequestUri: (options) ->
     url = @uri
-
-  getRequestOptions: (params) ->
-    options =
-      collection:
-        name: ''
-        id: ''

   call: (params, callback) ->
     if typeof params is 'function'


:lipstick: Test styles

:lipstick: Test styles

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -75,15 +75,16 @@
         #{stylesheet}

         atom-text-editor atom-text-editor-minimap, atom-text-editor::shadow atom-text-editor-minimap {
-          background: rgba(255,0,0,0.3) !important;
+          background: rgba(255,0,0,0.3);
         }

         atom-text-editor atom-text-editor-minimap::shadow .minimap-scroll-indicator, atom-text-editor::shadow atom-text-editor-minimap::shadow .minimap-scroll-indicator {
-          background: rgba(0,0,255,0.3) !important;
+          background: rgba(0,0,255,0.3);
         }

         atom-text-editor atom-text-editor-minimap::shadow .minimap-visible-area, atom-text-editor::shadow atom-text-editor-minimap::shadow .minimap-visible-area {
-          background: rgba(0,255,0,0.3) !important;
+          background: rgba(0,255,0,0.3);
+          opacity: 1;
         }
       """


Add specs for key-path methods

Add specs for key-path methods

--- spec/underscore-plus-spec.coffee
+++ spec/underscore-plus-spec.coffee
@@ -234,3 +234,17 @@
       expect(_.uncamelcase('test')).toBe 'Test'
       expect(_.uncamelcase(' test ')).toBe 'Test'
       expect(_.uncamelcase('__ParksAndRec')).toBe 'Parks And Rec'
+
+  describe "::valueForKeyPath(object, keyPath)", ->
+    it "retrieves the value at the given key path or undefined if none exists", ->
+      object = {a: b: c: 2}
+      expect(_.valueForKeyPath(object, 'a.b.c')).toBe 2
+      expect(_.valueForKeyPath(object, 'a.b')).toEqual {c: 2}
+      expect(_.valueForKeyPath(object, 'a.x')).toBeUndefined()
+
+  describe "::setValueForKeyPath(object, keyPath, value)", ->
+    it "assigns a value at the given key path, creating intermediate objects if needed", ->
+      object = {}
+      _.setValueForKeyPath(object, 'a.b.c', 1)
+      _.setValueForKeyPath(object, 'd', 2)
+      expect(object).toEqual {a: {b: c: 1}, d: 2}


fixed bug that relationships were not automatically removed, rel.id should have been rel._id

--- node_modules_koding/jraphical/lib/module.coffee
+++ node_modules_koding/jraphical/lib/module.coffee
@@ -444,12 +444,11 @@
     {constructor} = @
     id = @getId()
     Relationship.all $or: [{sourceId: id}, {targetId: id}], (err, rels)=>
-      if err
-        callback err
-      else
-        relIds = (rel.id for rel in rels)
-        Relationship.remove (_id: $in: relIds), =>
-          constructor.remove _id: id, callback
+      return callback err if err
+      relIds = (rel._id for rel in rels)
+      Relationship.remove (_id: $in: relIds), (err)=>
+        return callback err if err
+        constructor.remove _id: id, callback

   beginGraphlet:-> new Graphlet nodes: [@]


Refactor up.util.isBlank

Refactor up.util.isBlank

--- lib/assets/javascripts/unpoly/util.coffee
+++ lib/assets/javascripts/unpoly/util.coffee
@@ -389,8 +389,9 @@
   isBlank = (object) ->
     return true if isMissing(object)
     return false if isFunction(object)
-    return (isObject(object) && Object.keys(object).length == 0) || # object
-      (object.length == 0) # string, Array, jQuery
+    return true if isObject(object) && Object.keys(object).length == 0 # object
+    return true if object.length == 0 # string, array, jQuery
+    return false

   ###**
   Returns the given argument if the argument is [present](/up.util.isPresent),


fixing tests

fixing tests

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -181,8 +181,7 @@
     slug = _s.slugify(author.user.name) + '-' + titleSlug
   else
     slug = titleSlug
-  article.slugs.push slug
-  article.slugs = [slug].concat _.uniq(article.slugs)
+  article.slugs = _.unique(article.slugs).concat [slug]
   article

 denormalizeAuthor = (article, author, callback) ->


Exclude preceding newlines from INDENT tokens.

Produces more meanignful locationData.

--- lexer.coffee
+++ lexer.coffee
@@ -328,7 +328,7 @@
         @suppressNewlines()
         return indent.length
       diff = size - @indent + @outdebt
-      @token 'INDENT', diff, 0, indent.length
+      @token 'INDENT', diff, indent.length - size, size
       @indents.push diff
       @ends.push 'OUTDENT'
       @outdebt = @indebt = 0


Remove leftover line

Remove leftover line

--- src/access/database.coffee
+++ src/access/database.coffee
@@ -215,8 +215,6 @@
             arg = arguments[0]
             return o.setMany(arg) if _.isObject(arg)

-            keyValues = _.unwrapArgs
-
             gw = @getTableGateway(t.many)
             return gw.findOne.apply(gw, arguments)


client: add 'Show All' link next to right blocks

--- client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
+++ client/Social/Activity/ContentDisplays/ActivityRightBlock.coffee
@@ -20,6 +20,7 @@

#{@getOption 'title'}

       {{> @tickerListView}}
+      {{> @tickerListView}}

     """

@@ -37,18 +38,48 @@
   constructor:(options={}, data)->
     @itemClass = ActiveUserItemView

+    @showAllLink = new KDCustomHTMLView
+      tagName : "a"
+      partial : "Show All"
+      click   : (event) -> KD.singletons.router.handleRoute "/Members"
+    , data
+
     options.title    = "Active Users"
     options.cssClass = "active-users"
     super options, data

     KD.remote.api.ActiveItems.fetchUsers {}, @bound 'renderItems'

+  pistachio:->
+    """
+

+
#{@getOption 'title'}

+      {{> @tickerListView}}
+      {{> @showAllLink}}
+

+    """
+
 class ActiveTopics extends ActivityRightBase
   constructor:(options={}, data)->
     @itemClass = ActiveTopicItemView
+
+    @showAllLink = new KDCustomHTMLView
+      tagName : "a"
+      partial : "Show All"
+      click   : (event) -> KD.singletons.router.handleRoute "/Topics"
+    , data

     options.title    = "Active Topics"
     options.cssClass = "active-topics"
     super options, data

     KD.remote.api.ActiveItems.fetchTopics {}, @bound 'renderItems'
+
+  pistachio:->
+    """
+

+
#{@getOption 'title'}

+      {{> @tickerListView}}
+      {{> @showAllLink}}
+

+    """


app-settings-menu: Added an error notification when user try to compile an application from a file which is not related with an app.

--- client/landing/app/Applications/Ace.kdapplication/AppView.coffee
+++ client/landing/app/Applications/Ace.kdapplication/AppView.coffee
@@ -75,6 +75,7 @@

   compileAndRun: ->
     manifest = KodingAppsController.getManifestFromPath @getData().path
+    return @ace.notify "Not found an app to compile", null, yes unless manifest?.name
     @ace.notify "Compiling...", null, yes
     @getSingleton('kodingAppsController').compileApp manifest.name, (err)=>
       if not err


cassandra --> rethink: account creation actions

--- salvus/cassandra.coffee
+++ salvus/cassandra.coffee
@@ -3187,6 +3187,19 @@
             cb      : cb

     r_account_creation_actions: (cb) =>
+        db = require('rethink').rethinkdb()
+        @dump_table
+            table : 'account_creation_actions'
+            columns : ['email_address', 'actions']
+            #limit   : 20
+            each    : (row, cb) ->
+                f = (a, cb) ->
+                    db.account_creation_actions
+                        email_address : row.email_address
+                        action : misc.from_json(a)
+                        cb     : cb
+                async.map(row.actions, f, cb)
+            cb      : cb

     r_blobs: (cb) =>
         table = require('rethink').rethinkdb().table('blobs')


cassandra: reduce load on the server temporarily by hub

--- salvus/cassandra.coffee
+++ salvus/cassandra.coffee
@@ -716,7 +716,8 @@
         misc.retry_until_success
             f         : f
             max_tries : @query_max_retry
-            max_delay : 3000
+            max_delay : 15000
+            factor    : 1.6
             cb        : (err) =>
                 if err
                     err = "query failed even after #{@query_max_retry} attempts -- giving up -- #{err}"


Nodejs: metrics: refactored private _apply function to be able to send duration correctly

--- node_modules_koding/bongo/lib/bongo.coffee
+++ node_modules_koding/bongo/lib/bongo.coffee
@@ -397,14 +397,20 @@
         bongo.handleUserError "unknown method! #{method.method}"
         return

-      timer = new bongo.metrics.timer  if bongo.metrics

       _apply = (fn, ctx, args) ->
+        if bongo.metrics
+          # wrapping callback function if there is one in the args
+          if typeof (callback = args[args.length - 1]) is 'function'
+            start = new Date()
+            args[args.length - 1] = (rest...) ->
+              duration = new Date() - start
+              bongo.metrics.sendMethodMetrics { method, duration }
+              callback rest...
+          # else sending metrics with duration equal to zero
+          else
+            bongo.metrics.sendMethodMetrics { method, 0 }
         fn.apply ctx, args
-
-        return  unless bongo.metrics
-        duration = timer.getElapsedTimeInMilliSecs()
-        bongo.metrics.sendMethodMetrics { method, duration }

       if fn.isSecurityEnabled
         if client


StaticPage: Markdown rendering is moved to renderBody

--- server/lib/crawler/staticpages/activity.coffee
+++ server/lib/crawler/staticpages/activity.coffee
@@ -87,13 +87,6 @@
         """
       tags += content

-  marked = require 'marked'
-
-  body = marked body,
-    gfm       : true
-    pedantic  : false
-    sanitize  : true
-
   content  =
     """
     <div class="kdview kdlistitemview kdlistitemview-activity activity-item status">


sidebar: redirect to message item route directly

anchor elements in `KDModalView` were not subject to window click event
handler of `KDWindowController`. this patch is only meant as a workaround.

--- client/Social/Activity/sidebar/messageitem.coffee
+++ client/Social/Activity/sidebar/messageitem.coffee
@@ -30,6 +30,14 @@
     @getDelegate().removeItem this

+  click: (event) ->
+
+    KD.utils.stopDOMEvent event
+    KD.singleton('router').handleRoute "/#{@getOption 'route'}"
+
+    super event
+
+
   pistachio: ->

     """


modify keymaps on jump heading

modify keymaps on jump heading


Mekko: make collections instance variables

Clean-up.

--- app/assets/javascripts/d3/mekko.coffee
+++ app/assets/javascripts/d3/mekko.coffee
@@ -2,8 +2,9 @@
   # This represents a carrier within a sector
   Node: class extends Backbone.Model
     initialize: =>
-      @sector = D3.mekko.sector_list.find((s) => s.get('key') == @get('sector'))
-      @carrier = D3.mekko.carrier_list.find((s) => s.get('key') == @get('carrier'))
+      @view = @get 'view'
+      @sector  = @view.sector_list.find((s) => s.get('key') == @get('sector'))
+      @carrier = @view.carrier_list.find((s) => s.get('key') == @get('carrier'))
       @sector.nodes.push this
       @carrier.nodes.push this

@@ -15,11 +16,7 @@

   NodeGroup: class extends Backbone.Model
       initialize: -> @nodes = []
-
-      total_value: =>
-        total = 0
-        total += n.val() for n in @nodes
-        total
+      total_value: => d3.sum @nodes, (n) -> n.val()

   View: class extends D3ChartView
     el: "body"
@@ -33,9 +30,9 @@
     outer_height: -> 410
     # lots of ugly transformations to adapt the current conventions
     prepare_data: =>
-      @sector_list = D3.mekko.sector_list = new D3.mekko.GroupCollection()
-      @carrier_list = D3.mekko.carrier_list = new D3.mekko.GroupCollection()
-      @node_list = D3.mekko.node_list = new D3.mekko.NodeList()
+      @sector_list  = new D3.mekko.GroupCollection()
+      @carrier_list = new D3.mekko.GroupCollection()
+      @node_list    = new D3.mekko.NodeList()

       sectors  = []
       carriers = []
@@ -44,17 +41,20 @@
         carriers.push
           label: s.get('label')
           color: s.get('color')
+          view: this
       sectors  = _.uniq sectors
       carriers = _.uniq(carriers, false, (c) -> c.label)

       for sector in sectors
         @sector_list.add
           key: sector
+          view: this
       for carrier in carriers
         @carrier_list.add
           key: carrier.label
           label: carrier.label # used by the legend
           color: carrier.color
+          view: this

       for s in @series
         sector  = s.get('group_translated')
@@ -64,6 +64,7 @@
           carrier: carrier
           gquery: s.get('gquery')
           color: s.get('color')
+          view: this

     draw: =>
       @prepare_data()
@@ -82,6 +83,9 @@
         .attr("width", @width + margins.left + margins.right)
         .append("svg:g")
         .attr("transform", "translate(#{margins.left}, #{margins.top})")
+
+      @x = d3.scale.linear().range([0, @width])
+      @y = d3.scale.linear().range([0, @series_height])

       y_scale = d3.scale.linear().domain([100,0]).range([0, @series_height])

@@ -132,11 +136,12 @@
         position:
           my: 'bottom right'
           at: 'top center'
+          follow: 'mouse'

     refresh: =>
       total_value = @node_list.grand_total()
-      @x = d3.scale.linear().domain([0, total_value]).range([0, @width])
-      @y = d3.scale.linear().domain([0, total_value]).range([0, @series_height])
+      @x.domain([0, total_value])
+      @y.domain([0, total_value])

       @sector_offset = 0

@@ -161,29 +166,26 @@
       for sector in @sector_list.pluck('key')
         offsets[sector] = 0

-      @svg.selectAll(".carrier").
-        data(@node_list.models, (d) -> d.key()).
-        transition().duration(500).
-        attr("width", (d) => @x d.sector.total_value() ).
-        attr("height", (d) =>
+      @svg.selectAll(".carrier")
+        .data(@node_list.models, (d) -> d.key())
+        .transition().duration(500)
+        .attr("width", (d) => @x d.sector.total_value() )
+        .attr("height", (d) =>
           x = d.val() / d.sector.total_value() * @series_height
           if _.isNaN(x) then 0 else x
-        ).
-        attr("y", (d) =>
+        )
+        .attr("y", (d) =>
           old = offsets[d.get 'sector']
           x = d.val() / d.sector.total_value() * @series_height
           if _.isNaN(x) then x = 0
           offsets[d.get 'sector'] += x
           old
-        ).
-        attr("data-tooltip", (d) -> d.tooltip_text())
+        )
+        .attr("data-tooltip", (d) -> d.tooltip_text())

 class D3.mekko.GroupCollection extends Backbone.Collection
   model: D3.mekko.NodeGroup

 class D3.mekko.NodeList extends Backbone.Collection
   model: D3.mekko.Node
-  grand_total: =>
-    t = 0
-    t += n.val() for n in @models
-    t
+  grand_total: => d3.sum @models, (n) -> n.val()


Dropdown menus in the react style

Dropdown menus in the react style

--- SingularityUI/app/components/common/input/DropDown.cjsx
+++ SingularityUI/app/components/common/input/DropDown.cjsx
@@ -4,12 +4,12 @@

     render: ->
         dropDownOpts = []
-        dropDownOpts.push(<option key=0 value='noValueChosen'>{@props.title}</option>)
+        dropDownOpts.push(<option key=0 value='noValueChosen'></option>)
         i = 1
         for element in @props.choices
             dropDownOpts.push(<option key={i} value={element.value}>{element.user}</option>)
             i++
-        return <select type={@props.inputType} onChange={@props.updateFn} value={@props.value} defaultValue={@props.defaultValue}>
+        return <select className='form-control' type={@props.inputType} onChange={@props.updateFn} value={@props.value} defaultValue={@props.defaultValue}>
                     {dropDownOpts}
                 </select>


restricted note edit shortcut to only up key

Updated CHANGELOG

Removed CHANGELOG entry

Moved conditional to helper method

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -102,11 +102,14 @@

   keydownNoteText: (e) ->
     $this = $(this)
-    if $this.val() is '' and e.which is 38 #aka the up key
+    if $this.val() is '' and e.which is 38 and not isMetaKey e
       myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
       if myLastNote.length
         myLastNoteEditBtn = myLastNote.find('.js-note-edit')
         myLastNoteEditBtn.trigger('click', [true, myLastNote])
+
+  isMetaKey = (e) ->
+    (e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)

   initRefresh: ->
     clearInterval(Notes.interval)


Pin flash message to top if award note is invalid

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -113,7 +113,9 @@
   renderNote: (note) ->
     unless note.valid
       if note.award
-        new Flash('You have already used this award emoji !', 'alert')
+        flash = new Flash('You have already used this award emoji !', 'alert')
+        flash.pinToTop()
+        flash.raise()
       return

     # render note if it not present in loaded list


Coffeescript syntax correction

Coffeescript syntax correction

--- app/assets/javascripts/plans.js.coffee
+++ app/assets/javascripts/plans.js.coffee
@@ -3,10 +3,11 @@
 # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

 jQuery ($)->
-	$( "#sections-accordion" ).accordion({
-		heightStyle: "content",
+	$("#sections-accordion").accordion
+		heightStyle: "content"
 		collapsible: true
-	})
+
+
 	#$( "form.answer textarea" ). blur(->
 		#$(this).parents('form:first').submit()
 	#)


enhance: update message and reload

enhance: update message and reload

--- app/components/auth-mfa.coffee
+++ app/components/auth-mfa.coffee
@@ -53,9 +53,13 @@
         otp: enableMFAOTP
       @get("ajax").post ENV.endpoints.enableMFA, data: data
       .then (data)->
-        that.get("notify").success "success"
+        that.get("notify").success "Multi Factor Authentication is now enabled"
         that.set "enableMFAOTP", ""
         that.set "showMFAEnableModal", false
+        setTimeout ->
+          window.location.reload() # FIXME: Hackish Way
+        ,
+          3 * 1000
       .catch (error) ->
         that.get("notify").error error.payload.message
         for error in error.errors
@@ -70,9 +74,13 @@
         otp: disableMFAOTP
       @get("ajax").post ENV.endpoints.disableMFA, data: data
       .then (data)->
-        that.get("notify").success "success"
+        that.get("notify").success "Multi Factor Authentication is now disabled"
         that.set "disableMFAOTP", ""
         that.set "showMFADisableModal", false
+        setTimeout ->
+          window.location.reload() # FIXME: Hackish Way
+        ,
+          3 * 1000
       .catch (error) ->
         that.get("notify").error error.payload.message
         for error in error.errors


support for new error structure

support for new error structure


Todos

Todos

--- platform.coffee
+++ platform.coffee
@@ -20,6 +20,8 @@

 -> Instead of require()ing JSON, it should be read and JSON.parse()d
 -> Models and routes should be allowed to return their stuff asynchronously
+-> Find out why 'view engine' isn't getting set ('no default engine and no extension specified' for res.render)
+-> Add ability to 'watch' file changes, e.g. routes, models, app, config, etc
 ###

 module.exports = (args...) ->


check version before it is modified by applyOp

--- app/coffee/ShareJsUpdateManager.coffee
+++ app/coffee/ShareJsUpdateManager.coffee
@@ -23,7 +23,8 @@
 	applyUpdate: (project_id, doc_id, update, lines, version, callback = (error, updatedDocLines) ->) ->
 		logger.log project_id: project_id, doc_id: doc_id, update: update, "applying sharejs updates"
 		jobs = []
-
+		# record the update version before it is modified
+		incomingUpdateVersion = update.version
 		# We could use a global model for all docs, but we're hitting issues with the
 		# internal state of ShareJS not being accessible for clearing caches, and
 		# getting stuck due to queued callbacks (line 260 of sharejs/server/model.coffee)
@@ -44,7 +45,7 @@
 			model.getSnapshot doc_key, (error, data) =>
 				return callback(error) if error?
 				# only check hash when present and no other updates have been applied
-				if update.hash? and update.v == version
+				if update.hash? and incomingUpdateVersion == version
 					ourHash = ShareJsUpdateManager._computeHash(data.snapshot)
 					return callback(new Error("Invalid hash")) if ourHash != update.hash
 				docLines = data.snapshot.split(/\r\n|\n|\r/)


prevent closed sprints dissapear when the user drag and US to the current sprint

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -170,7 +170,7 @@

             @scope.sprints = sprints
             @scope.openSprints = _.filter(sprints, (sprint) => not sprint.closed).reverse()
-            @scope.closedSprints =  []
+            @scope.closedSprints =  [] if !@scope.closedSprints

             @scope.sprintsCounter = sprints.length
             @scope.sprintsById = groupBy(sprints, (x) -> x.id)


restore backlogs filters

restore backlogs filters

--- app/coffee/modules/backlog/main.coffee
+++ app/coffee/modules/backlog/main.coffee
@@ -200,22 +200,46 @@
             @rootscope.$broadcast("sprints:loaded", sprints)
             return sprints

+    restoreFilters: ->
+        selectedTags = @scope.oldSelectedTags
+        selectedStatuses = @scope.oldSelectedStatuses
+
+        return if !selectedStatuses and !selectedStatuses
+
+        @scope.filtersQ = @scope.filtersQOld
+
+        @.replaceFilter("q", @scope.filtersQ)
+
+        _.each [selectedTags, selectedStatuses], (filterGrp) =>
+            _.each filterGrp, (item) =>
+                filters = @scope.filters[item.type]
+                filter = _.find(filters, {id: item.id})
+                filter.selected = true
+
+                @.selectFilter(item.type, item.id)
+
+        @.loadUserstories()
+
     resetFilters: ->
         selectedTags = _.filter(@scope.filters.tags, "selected")
         selectedStatuses = _.filter(@scope.filters.status, "selected")

+        @scope.oldSelectedTags = selectedTags
+        @scope.oldSelectedStatuses = selectedStatuses
+
+        @scope.filtersQOld = @scope.filtersQ
         @scope.filtersQ = ""
+        @.replaceFilter("q", null)

         _.each [selectedTags, selectedStatuses], (filterGrp) =>
             _.each filterGrp, (item) =>
                 filters = @scope.filters[item.type]
-                filter = _.find(filters, {id: taiga.toString(item.id)})
+                filter = _.find(filters, {id: item.id})
                 filter.selected = false

                 @.unselectFilter(item.type, item.id)

         @.loadUserstories()
-        @rootscope.$broadcast("filters:update")

     loadUserstories: ->
         @scope.httpParams = @.getUrlFilters()
@@ -732,6 +756,8 @@

         if !sidebar.hasClass("active")
             $ctrl.resetFilters()
+        else
+            $ctrl.restoreFilters()

         $ctrl.toggleActiveFilters()


toggle-logs -> toggle-log

toggle-logs -> toggle-log


sabRE listens on IPv6 now

sabRE listens on IPv6 now

--- cs_app/app.coffee
+++ cs_app/app.coffee
@@ -159,7 +159,7 @@

 if settings.loaded
   # start the server
-  app.listen settings.port, () =>
+  app.listen settings.port, '::', () =>
     logger.info 'server listening on port ' + settings.port
   .on 'error', (e) ->
     logger.error _.sprintf('could not listen on port %d, is it already in use?', settings.port)


grammar issue

grammar issue

--- app/assets/javascripts/md_simple_editor.js.coffee
+++ app/assets/javascripts/md_simple_editor.js.coffee
@@ -35,7 +35,7 @@
           else if option == 'md_underline'
              "Your undelined text here "
           else if option == 'md_table'
-             "\n|Header|Header|Header|\n|:------|:-------:|------:|\n|Left aligment|Centered|right aligment|\n\n
"
+             "\n|Header|Header|Header|\n|:------|:-------:|------:|\n|Left alignment|Centered|Right alignment|\n\n
"
           else if option == 'md_minus'
              "\n
\n"
           else if option == 'md_square'


Has-many association between EventSearch and EventType.

--- app/assets/javascripts/models/event_search.coffee
+++ app/assets/javascripts/models/event_search.coffee
@@ -2,7 +2,7 @@
 HM = DS.hasMany

 Pancakes.EventSearch = DS.Model.extend
-  eventTypes: A 'string'
+  eventTypes: HM 'Pancakes.EventType'
   scheduledStartDate: A 'string'
   scheduledEndDate: A 'string'
   dataCollectors: A 'string'


mytimeline: getUserTopics: Check if timeline is available

--- src/js/mytimeline.coffee
+++ src/js/mytimeline.coffee
@@ -20,6 +20,8 @@

 class MyTimeline
   @getUserTopics: (callback) =>
+    if not Pebble.timelineSubscriptions
+      return
     return @userTopics if @userTopics

     misc.retry timelineSubscriptions, (err, subscribedTopics) =>


change default to 1000 and constraint from 1

--- app/assets/javascripts/uploadcare/settings.coffee
+++ app/assets/javascripts/uploadcare/settings.coffee
@@ -20,7 +20,7 @@
     imagesOnly: false
     clearable: false
     multiple: false
-    multipleMax: 0
+    multipleMax: 1000
     multipleMin: 1
     multipleMaxStrict: false
     imageShrink: false
@@ -52,7 +52,7 @@

   constraints =
     multipleMax:
-      min: 0
+      min: 1
       max: 1000

   presets =


make more straightforward logic

make more straightforward logic

--- app/assets/javascripts/uploadcare/settings.coffee
+++ app/assets/javascripts/uploadcare/settings.coffee
@@ -207,14 +207,14 @@
     if settings.previewProxy and not settings.previewUrlCallback
       settings.previewUrlCallback = (url, info) ->
         encodedUrl = encodeURIComponent(url)
+
         justAppend = /\=$/.test(@previewProxy)
-        useAmpersand = /[^\&\?]$/.test(@previewProxy)
+        useAmpersand = /[^\&\?\=]$/.test(@previewProxy)
         addQuestionSign = not /\?/.test(@previewProxy)

-        queryPart = if justAppend then encodedUrl else
-          if useAmpersand then "&url=#{encodedUrl}" else "url=#{encodedUrl}"
-
-        queryPart = '?' + queryPart if addQuestionSign
+        queryPart = if justAppend then encodedUrl else "url=#{encodedUrl}"
+        queryPart = '&'.concat(queryPart) if useAmpersand
+        queryPart = '?'.concat(queryPart) if addQuestionSign

         utils.normalizeUrl(settings.previewProxy) + queryPart


Fixed room.coffee

Fixed room.coffee

--- app/domain/room.coffee
+++ app/domain/room.coffee
@@ -4,7 +4,7 @@
     constructor: (@info) ->
         if not @info?
             throw new Error "roomInfo is undefined"
-        @queue = new SongQueue
+        @queue = new SongQueue()
         @timer = undefined
         @currentSongTime = 0
     getInfo: -> @info
@@ -19,7 +19,7 @@
             @resetTimer
         else
             @currentSongTime += 1
-            if @queue.getFirst().getDuration() is @currentSongTime
+            if @queue.getFirst().getDuration() == @currentSongTime
                 @queue.removeFirst()
                 @currentSongTime = 0
     activate: ->
@@ -28,7 +28,7 @@
             @timer = setInterval ->
               @tick()
             , 1000
-            null
+            undefined
         else undefined
     isActive: ->
         @timer?


set course calendar selected for group scheduler items fixes 9933

test plan:
* in calendar 2 create a scheduler appointment
* setup time slots for the appointment
* select a course calendar for the apointment
* select the checkbox for users to signup in groups
* save and publish
* edit the scheduler appointment
* you should be able to add another time slot

Change-Id: Ic32e76787bbb183f10d256d83a1680704f05ff40
Reviewed-on: https://gerrit.instructure.com/15461
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Amber Taniuchi <ef4bfd1e6d278f6bc7d4a478f02be2cb9581a0a5@instructure.com>
Reviewed-by: Cameron Matheson <1d572acbfa68c7c6e541c7b840d6b622e5c0dc91@instructure.com>

--- app/coffeescripts/calendar/ContextSelector.coffee
+++ app/coffeescripts/calendar/ContextSelector.coffee
@@ -96,6 +96,7 @@
       if @apptGroup.sub_context_codes.length > 0
         if @apptGroup.sub_context_codes[0].match /^group_category_/
           for c, item of @contextSelectorItems
+            item.setState('on')
             item.lock()
         else
           contextsBySubContext = {}


Match ternary operator

Match ternary operator

--- grammars/csound.cson
+++ grammars/csound.cson
@@ -185,7 +185,7 @@
       }
       {
         name: 'keyword.operator.csound'
-        match: '[+\\-*/%^!=&|<>#~¬]'
+        match: '[+\\-*/%^!=&|<>#~¬?:]'
       }
       {
         include: '#quotedStrings'


make aware of the 2 modes of code and teletype parsing (block / inline)


Moved destroy to IconButton

Moved destroy to IconButton

--- js-library/app/js/jail_iframe/classes/buttons.coffee
+++ js-library/app/js/jail_iframe/classes/buttons.coffee
@@ -48,12 +48,6 @@
     @frame.fadeOut()
     @_visible = false

-  destroy: =>
-    @frame.destroy()
-    @_robustHover.destroy()
-    $(window).off 'resize', @_updatePosition
-    clearInterval @_interval
-    @$boundingBox?.remove()

 class FactlinkJailRoot.CreateButton extends Button
   content: """
@@ -111,6 +105,13 @@

 class IconButton extends Button

+  destroy: =>
+    @frame.destroy()
+    @_robustHover.destroy()
+    $(window).off 'resize', @_updatePosition
+    clearInterval @_interval
+    @$boundingBox?.remove()
+

 class FactlinkJailRoot.ShowButton extends IconButton
   content: '

'


Do not cache waterpoints with requests in MainCtrl

Doing that caused new reports or report status changes not to be
reflected.

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -47,7 +47,7 @@
       if $scope.where.status_group
         where.status_group = $scope.where.status_group
       if $scope.where.reports_only
-        $http.get('/api/waterpoints/requests', cache: true).success (requests) ->
+        $http.get('/api/waterpoints/requests').success (requests) ->
           where.wpt_code = "$in": requests
           query where, $scope.where.max_results
       else


port mappings inside docker info in json

--- SingularityUI/app/views/newDeploy.coffee
+++ SingularityUI/app/views/newDeploy.coffee
@@ -103,8 +103,8 @@
             if $dockerPorts.length
                 for $dockerPort in $dockerPorts
                     $dockerPort = $ $dockerPort
-                    deployObject.containerInfo.portMappings = [] unless deployObject.containerInfo.portMappings
-                    deployObject.containerInfo.portMappings.push
+                    deployObject.containerInfo.docker.portMappings = [] unless deployObject.containerInfo.portMappings
+                    deployObject.containerInfo.docker.portMappings.push
                         containerPortType: @valOrNothing '.cont-port-type', $dockerPort
                         containerPort:     @valOrNothing '.cont-port', $dockerPort
                         hostPortType:      @valOrNothing '.host-port-type', $dockerPort


Don't decrease load here, deactivate already does it

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -620,7 +620,6 @@
           @result.__bracketClosingAfter.push ctx

     debug "#{@nodeInstance.nodeId} finished processing #{@nodeInstance.load}"
-    @nodeInstance.load--

     if @nodeInstance.isOrdered()
       @result.__resolved = true


Process queue only once per done

Process queue only once per done

--- src/lib/Component.coffee
+++ src/lib/Component.coffee
@@ -379,7 +379,7 @@
     return if context.deactivated # prevent double deactivation
     context.deactivated = true
     context.activated = false
-    if @ordered or @autoOrdering
+    if @isOrdered()
       @processOutputQueue()
     @load--
     @emit 'deactivate', @load
@@ -745,7 +745,4 @@

     debug "#{@nodeInstance.nodeId} finished processing #{@nodeInstance.load}"

-    if @nodeInstance.isOrdered()
-      @result.__resolved = true
-      @nodeInstance.processOutputQueue()
     @nodeInstance.deactivate @context


[multiline] Wrap <fieldset /> tags in a <form />

This is to avoid a bug in Chrome for iOS, which throws an error for
`<fieldset />` elements that aren't wrapped in a `<form />`. More
details on the bug
[here](https://code.google.com/p/chromium/issues/detail?id=570095#c9).

--- app/assets/javascripts/components/overview/details.cjsx
+++ app/assets/javascripts/components/overview/details.cjsx
@@ -81,66 +81,68 @@
         {staff}

{CourseUtils.i18n('school', @props.course.string_prefix)}: {@props.course.school}

{CourseUtils.i18n('term', @props.course.string_prefix)}: {@props.course.term}

-        {passcode}
-        <fieldset>
-          <TextInput
-            onChange={@updateDetails}
-            value={@props.course.expected_students}
-            value_key='expected_students'
-            editable={@props.editable}
-            type='number'
-            label={CourseUtils.i18n('expected_students', @props.course.string_prefix)}
-          />
-        </fieldset>
-        <fieldset>
-          <TextInput
-            onChange={@updateDetails}
-            value={@props.course.start}
-            value_key='start'
-            editable={@props.editable}
-            type='date'
-            label='Start'
-            required=true
-          />
-        </fieldset>
-        <fieldset>
-          <TextInput
-            onChange={@updateDetails}
-            value={@props.course.end}
-            value_key='end'
-            editable={@props.editable}
-            type='date'
-            label='End'
-            date_props={minDate: moment(@props.course.start).add(1, 'week')}
-            enabled={@props.course.start?}
-            required=true
-          />
-        </fieldset>
-        <fieldset>
-          <TextInput
-            onChange={@updateDetails}
-            value={@props.course.timeline_start}
-            value_key='timeline_start'
-            editable={@props.editable}
-            type='date'
-            label={CourseUtils.i18n('assignment_start', @props.course.string_prefix)}
-            date_props={timeline_start_props}
-            required=true
-          />
-        </fieldset>
-        <fieldset>
-          <TextInput
-            onChange={@updateDetails}
-            value={@props.course.timeline_end}
-            value_key='timeline_end'
-            editable={@props.editable}
-            type='date'
-            label={CourseUtils.i18n('assignment_end', @props.course.string_prefix)}
-            date_props={timeline_end_props}
-            required=true
-          />
-        </fieldset>
-

+        <form>
+          {passcode}
+          <fieldset>
+            <TextInput
+              onChange={@updateDetails}
+              value={@props.course.expected_students}
+              value_key='expected_students'
+              editable={@props.editable}
+              type='number'
+              label={CourseUtils.i18n('expected_students', @props.course.string_prefix)}
+            />
+          </fieldset>
+          <fieldset>
+            <TextInput
+              onChange={@updateDetails}
+              value={@props.course.start}
+              value_key='start'
+              editable={@props.editable}
+              type='date'
+              label='Start'
+              required=true
+            />
+          </fieldset>
+          <fieldset>
+            <TextInput
+              onChange={@updateDetails}
+              value={@props.course.end}
+              value_key='end'
+              editable={@props.editable}
+              type='date'
+              label='End'
+              date_props={minDate: moment(@props.course.start).add(1, 'week')}
+              enabled={@props.course.start?}
+              required=true
+            />
+          </fieldset>
+          <fieldset>
+            <TextInput
+              onChange={@updateDetails}
+              value={@props.course.timeline_start}
+              value_key='timeline_start'
+              editable={@props.editable}
+              type='date'
+              label={CourseUtils.i18n('assignment_start', @props.course.string_prefix)}
+              date_props={timeline_start_props}
+              required=true
+            />
+          </fieldset>
+          <fieldset>
+            <TextInput
+              onChange={@updateDetails}
+              value={@props.course.timeline_end}
+              value_key='timeline_end'
+              editable={@props.editable}
+              type='date'
+              label={CourseUtils.i18n('assignment_end', @props.course.string_prefix)}
+              date_props={timeline_end_props}
+              required=true
+            />
+          </fieldset>
+        </form>
+

           Cohorts: {cohorts}
           <CohortButton {...@props} show={@props.editable && @props.current_user.admin && (@props.course.submitted || @props.course.legacy) } />


close #33

close #33

--- app/assets/javascripts/components/project.js.jsx.coffee
+++ app/assets/javascripts/components/project.js.jsx.coffee
@@ -11,9 +11,8 @@
           {this.props.title}

-

-            {this.props.description}
-

+

+

            Читать далее


whoops, teams stay at 310

whoops, teams stay at 310

--- models/team.coffee
+++ models/team.coffee
@@ -41,7 +41,7 @@
 TeamSchema.static 'canRegister', (next) ->
   Team.count {}, (err, count) ->
     return next err if err
-    max = 300 + 1 # +1 because team fortnight labs doesn't count
+    max = 310 + 1 # +1 because team fortnight labs doesn't count
     next null, count < max, max - count

 TeamSchema.static 'uniqueName', (name, next) ->


Fixing wording

Fixing wording

--- scripts/triggers.coffee
+++ scripts/triggers.coffee
@@ -73,7 +73,7 @@
     name = msg.match[1]

     robot.brain.remove "trigger:#{name}", phrase
-    msg.reply "Poof! And now I know nothing about #{name} unless it's hardcoded"
+    msg.reply "Now I know nothing about '#{name}' unless it's hardcoded"

   robot.hear /^(([^:\s!]+)[:\s]+)?(!\w+)(.*)/i, (msg) ->


Permalink – instead of showing permalink, reflect back current url

--- app/assets/javascripts/resolve/load_permalink.js.coffee
+++ app/assets/javascripts/resolve/load_permalink.js.coffee
@@ -15,8 +15,8 @@
 	originalLink   = $(this)
 	valueContainer = $("#umlaut-permalink-container")

-	loadedLink = $(".umlaut-permalink-container a")
-	url = loadedLink.attr("href")
+  #loadedLink = $(".umlaut-permalink-container a")
+	url = window.location.href #loadedLink.attr("href")
 	input = $("<input />")
 	input.attr("value", url)
 	input.attr("readonly", "true")


Fix spiderfied cluster opacity problem.

Fix spiderfied cluster opacity problem.

--- src/map-base-view.coffee
+++ src/map-base-view.coffee
@@ -117,6 +117,16 @@
             markerClusterGroup.on 'spiderfied', (e) =>
                 icon = $(e.target._spiderfied?._icon)
                 icon?.fadeTo('fast', 0)
+
+            @_lastOpenedClusterIcon = null
+            markerClusterGroup.on 'spiderfied', (e) =>
+                # Work around css hover forced opacity showing the
+                # clicked cluster which should be hidden.
+                if @_lastOpenedClusterIcon
+                    L.DomUtil.removeClass @_lastOpenedClusterIcon, 'hidden'
+                icon = e.target._spiderfied._icon
+                L.DomUtil.addClass icon, 'hidden'
+                @_lastOpenedClusterIcon = icon

         getZoomlevelToShowAllMarkers: ->
             layer = p13n.get('map_background_layer')


Fix TokenSet's default include function

Fix TokenSet's default include function

--- app/TokenSet.coffee
+++ app/TokenSet.coffee
@@ -29,27 +29,24 @@
   #
   # The return value may contain duplicates.
   findTokensFromUnigrams: (unigrams) ->
-    unigramsBuffer = new Buffer(unigrams, 'utf-8') # Encode as binary
-
     startPositions = new Array(0) # a (fake) circular buffer, max size maxNgramLength

     ret = []

     testNgrams = (endPosition) =>
       for startPosition in startPositions
-        if @test(unigramsBuffer, startPosition, endPosition)
-          s = unigramsBuffer.toString('utf-8', startPosition, endPosition)
-          ret.push(s)
+        s = unigrams.slice(startPosition, endPosition)
+        ret.push(s) if @test(s)
       null

-    for byte, pos in unigramsBuffer
-      if byte == 0x20 # Space
+    for char, pos in unigrams
+      if char == ' ' # Space
         testNgrams(pos)
         startPositions.shift() if startPositions.length == @maxNgramSize
         startPositions.push(pos + 1)

     while startPositions.length > 1
-      testNgrams(unigramsBuffer.length)
+      testNgrams(unigrams.length)
       startPositions.shift()

     ret


fix bugs on sort event

fix bugs on sort event

--- app/static/scripts/room.coffee
+++ app/static/scripts/room.coffee
@@ -251,10 +251,13 @@
                     visible = (pos.t > 0 && pos.l > 0 && pos.t + pos.h < pos.docH && pos.l + pos.w < pos.docW)
                     if !visible
                         $(ui.sender).sortable 'cancel'
+            sort: (event, ui) ->
+                $('.module').css 'clear', ''
             stop: (event, ui) ->
                 draggedItemId = '#' + ui.item.attr 'id'
-                prevItem = do ui.item.prev
-                if ui.position.top >= (do prevItem.height)
+                prevHeight = do (do ui.item.prev).height
+                nextHeight = do (do ui.item.next).height
+                if ui.position.top >= prevHeight && ui.position.top >= nextHeight
                     $(draggedItemId).css 'clear', 'left'
                 else
                     $(draggedItemId).css 'clear', ''


Fix tests according to new implementation : we displays 4 events now.

--- test/front/coffee/suggest.spec.coffee
+++ test/front/coffee/suggest.spec.coffee
@@ -194,7 +194,7 @@

     SUGGEST.displayPreviewResult callbackResponse

-    expect( $('#resultSize') ).toHaveText( "2 autres évènements trouvés" )
+    expect( $('#resultSize') ).toHaveText( "1 autres évènements trouvés" )

     expect( $('#callbackNoResult').css('display') ).toEqual('none')


Get CSRF token from a header

Get CSRF token from a header

--- app/api/auth.coffee
+++ app/api/auth.coffee
@@ -7,13 +7,6 @@
   'Content-Type': 'application/json'
   'Accept': 'application/json'

-# This will match the CSRF token in a string of HTML.
-# TODO: Get JSON instead.
-CSRF_TOKEN_PATTERN = do ->
-  NAME_ATTR = '''name=['"]csrf-token['"]'''
-  CONTENT_ATTR = '''content=['"](.+)['"]'''
-  ///#{NAME_ATTR}\s*#{CONTENT_ATTR}|#{CONTENT_ATTR}\s*#{NAME_ATTR}///
-
 # We don't want to wait until the token is already expired before refreshing it.
 TOKEN_EXPIRATION_ALLOWANCE = 10 * 1000

@@ -24,10 +17,9 @@

   _getAuthToken: ->
     console?.log 'Getting auth token'
-    makeHTTPRequest 'GET', config.host + "/users/sign_in/?now=#{Date.now()}", null, 'Accept': 'text/html'
+    makeHTTPRequest 'GET', config.host + "/users/sign_in/?now=#{Date.now()}", null, JSON_HEADERS
       .then (request) ->
-        [_, authTokenMatch1, authTokenMatch2] = request.responseText.match CSRF_TOKEN_PATTERN
-        authToken = authTokenMatch1 ? authTokenMatch2
+        authToken = request.getResponseHeader 'X-CSRF-Token'
         console?.info "Got auth token #{authToken[...6]}..."
         authToken


Issue #535 OHM-194: Added additional fields to the client model

--- src/model/clients.coffee
+++ src/model/clients.coffee
@@ -11,7 +11,12 @@
   "passwordAlgorithm":  String
   "passwordHash":       String
   "passwordSalt":       String
-  "certFingerprint":	String
-
+  "certFingerprint":    String
+  "organization":       String
+  "softwareName":       String
+  "description":        String
+  "contantPerson":      String
+  "contantPersonEmail": String
+
 #compile the Client Schema into a Model
 exports.Client = connectionDefault.model 'Client', ClientSchema


Focus dashboard name input. Closes #67

Focus dashboard name input. Closes #67

--- app/app_view.coffee
+++ app/app_view.coffee
@@ -69,6 +69,7 @@
   createDashboardFromDialog: =>
     dashboardDialog = new DashboardDialog { parent: @ }
     $('body').append dashboardDialog.render().el
+    dashboardDialog.$el.find('input').first().focus()

   createTools: (tools, dataSource) ->
     toolsFormat = []


add minify css task and compress task for build

--- app/templates/_Gulpfile.coffee
+++ app/templates/_Gulpfile.coffee
@@ -97,6 +97,9 @@
 		.pipe plumber()
 		.pipe coffee({bare: true}).on('error', gutil.log)
 		.pipe uglify()
+		.pipe rename((path) ->
+			path.extname = ".min" + path.extname
+		)
 		.pipe gulp.dest(conf.path.js)
 		.pipe reload({stream: true})

@@ -112,10 +115,21 @@
 #*------------------------------------*\
 #   $CONTRIB-UGLIFY
 #*------------------------------------*/
-# 	uglify:
-# 		target:
-# 			files:
-# 				'<%= conf.path.js %>/main.min.js': '<%= conf.path.js %>/main.js'
+gulp.task "compress", () ->
+	gulp.src [conf.path.js + "/**/*.js"]
+	.pipe uglify()
+	.pipe rename({suffix: '.min'})
+	.pipe gulp.dest(conf.path.js)
+
+
+#*------------------------------------*\
+#   $CONTRIB-SASS
+#*------------------------------------*/
+gulp.task "minify", () ->
+  gulp.src([conf.path.css + "/style.scss"])
+    .pipe minifyCSS()
+    .pipe rename({suffix: '.min'})
+    .pipe gulp.dest(conf.path.css)

 #*------------------------------------*\
@@ -296,7 +310,7 @@
 # #*------------------------------------*/
 # grunt.registerTask 'default', ['watch']
 # grunt.registerTask 'optim', ['imageoptim', 'svgmin']
-# grunt.registerTask 'build', ['uglify', 'sass:minify', 'optim']
+gulp.task "build", ["compress", "minify"]
 # grunt.registerTask 'depcheck', ['devUpdate:check']
 # grunt.registerTask 'depask', ['devUpdate:ask']
 # grunt.registerTask 'depup', ['devUpdate:up']


remove caching from sass

remove caching from sass

--- app/templates/_gulpfile.coffee
+++ app/templates/_gulpfile.coffee
@@ -41,8 +41,7 @@
 gulp.task "sass", () ->
   gulp.src([conf.path.pvt.scss + "/style.scss"])
 		.pipe plumber(conf.plumber)
-		.pipe cache(sass({errLogToConsole: true}))
-		.pipe remember()
+		.pipe sass({errLogToConsole: true})
 		.pipe gulp.dest(conf.path.pvt.css)
 		.pipe reload({stream: true})


Flush subject queue when the subject set changes

--- app/classifier.cjsx
+++ app/classifier.cjsx
@@ -27,7 +27,7 @@
     @subjects.update {api, project, subject_set_id: subject_set.id}
     @classifications.update {api, project, workflow}

-    if newProps.user != @props.user
+    if newProps.user != @props.user || newProps.subject_set.id != @props.subject_set.id
       @setState currentSubjects: [], =>
         @subjects.flush()
         @subjects.fetch()


Centralize 'copied to clipboard' Noty messages

--- app/views/core/CocoView.coffee
+++ app/views/core/CocoView.coffee
@@ -567,9 +567,11 @@
   tryCopy: ->
     try
       document.execCommand('copy')
+      message = 'Copied to clipboard'
+      noty text: message, layout: 'topCenter', type: 'info', killer: false, timeout: 2000
     catch err
       message = 'Oops, unable to copy'
-      noty text: message, layout: 'topCenter', type: 'error', killer: false
+      noty text: message, layout: 'topCenter', type: 'error', killer: false, timeout: 3000

   wait: (event) -> new Promise((resolve) => @once(event, resolve))


fix graphite autocomplete

fix graphite autocomplete

--- app/graphite.coffee
+++ app/graphite.coffee
@@ -241,6 +241,7 @@
       msg = pre.innerText.trim()
     msg ? 'Unknown error'

+  # TODO this is only use for complete
   parse_find_response: (query, response) ->
     parts = query.split '.'
     pattern_parts = parts.map graphite.is_pattern
@@ -248,7 +249,7 @@
       if node.is_leaf
         node.path
       else
-        node.path + '.*'
+        node.path + '.'
     patterned_list = for path in list
       result = for matched, i in path.split '.'
         if pattern_parts[i]


sinon.js

sinon.js

--- browsertest/jasmine-harness.coffee
+++ browsertest/jasmine-harness.coffee
@@ -5,7 +5,7 @@

 html = require '../lib/jasmine-html.js'
 tap = require '../lib/jasmine.tap_reporter.js'
-sinon = require 'sinon'
+sinon = require '../lib/sinon.js'
 require '../lib/jquery.js'

 # expose helpers


Ensure HTTP_DATA event can be re-added

Ensure HTTP_DATA event can be re-added

--- test/unit/event_listeners.spec.coffee
+++ test/unit/event_listeners.spec.coffee
@@ -149,6 +149,14 @@

       expect(response.httpResponse.body.toString()).toEqual('')

+    it 'allows HTTP_DATA event to be re-added', ->
+      request = makeRequest()
+      request.on('httpData', ->)
+      request.on('httpData', AWS.EventListeners.Http.HTTP_DATA)
+      response = request.send()
+
+      expect(response.httpResponse.body.toString()).toEqual('FOOBARBAZQUX')
+
   describe 'retry', ->
     it 'retries a request with a set maximum retries', ->
       sendHandler = createSpy('send')


Do not set ROOT_URL_PATH_PREFIX based on Site_URL

--- packages/rocketchat-lib/lib/startup/settingsOnLoadSiteUrl.coffee
+++ packages/rocketchat-lib/lib/startup/settingsOnLoadSiteUrl.coffee
@@ -8,7 +8,7 @@
 			prefix = match[2].replace(/\/$/, '')

 		__meteor_runtime_config__.ROOT_URL = host
-		__meteor_runtime_config__.ROOT_URL_PATH_PREFIX = prefix
+		# __meteor_runtime_config__.ROOT_URL_PATH_PREFIX = prefix

 		if Meteor.absoluteUrl.defaultOptions?.rootUrl?
 			Meteor.absoluteUrl.defaultOptions.rootUrl = host


fixed bug with time formatting

fixed bug with time formatting

--- src/number/format.coffee
+++ src/number/format.coffee
@@ -4,12 +4,12 @@
 module.exports = (number, opts) ->

   if "locale" of opts
-    locale = opts.locale.format
+    locale = opts.locale
   else
-    locale = defaultLocale.format
+    locale = defaultLocale

   time   = locale.time.slice()
-  format = d3.locale locale
+  format = d3.locale locale.format

   opts   = {} unless opts
   vars   = opts.vars or {}
@@ -40,9 +40,9 @@
     # Format number to precision level using proper scale
     number = d3.formatPrefix(number).scale number
     number = format.numberFormat(".3g") number
-    number = number.replace locale.decimal, "."
+    number = number.replace locale.format.decimal, "."
     number = parseFloat(number) + ""
-    number = number.replace ".", locale.decimal
+    number = number.replace ".", locale.format.decimal
     ret = number + symbol
   else if length is 3
     ret = format.numberFormat(",f") number


enable mouse scroll

enable mouse scroll

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -11,6 +11,3 @@
   highlightStyle    : 'monokai'
   highlightLanguage : 'remark'
   ratio             : '16:9'
-  navigation: {
-  	scroll: false
-  }


reduced code repetition in app.coffee

reduced code repetition in app.coffee

Minor code improvements in the `app.coffee` file, which make the
code slightly DRYer than before.

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -225,8 +225,9 @@

   onClickDownload: =>
     $a = $("<a>")
-      .attr "href", window.URL.createObjectURL(new Blob([@editor.getValue()], {type: "text/txt"}))
-      .attr "download", "design.html"
+      .attr
+        download: 'design.html'
+        href: window.URL.createObjectURL(new Blob([@editor.getValue()], {type: "text/txt"}))
       .appendTo "body"

     $a[0].click()
@@ -235,20 +236,19 @@

   onChange: (e) =>
     @debouncedSaveContent()
-    if e.data.action is "insertText"
+    insertTextAction = e.data.action is "insertText"
+    if insertTextAction
       @increaseStreak()
       @debouncedEndStreak()

     @throttledShake()

-    pos = if e.data.action is "insertText"
-      e.data.range.end
-    else
-      e.data.range.start
+    range = e.data.range
+    pos = if insertTextAction then range.end else range.start

     token = @editor.session.getTokenAt pos.row, pos.column

     _.defer =>
       @throttledSpawnParticles(token.type) if token

-$ -> new App+$ -> new App