allow color mixes to overshoot

allow color mixes to overshoot

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -107,8 +107,8 @@
 			b: @b
 			a: 0

-	mix: (colorB, fraction) ->
-		return Color.mix(@, colorB, fraction)
+	mix: (colorB, fraction, limit = false) ->
+		return Color.mix(@, colorB, fraction, limit)

 	toInspect: =>
 		"<#{@constructor.name} r:#{@r} g:#{@g} b:#{@b} a:#{@a}>"
@@ -116,12 +116,9 @@
 	##############################################################
 	## Class methods

-	@mix: (colorA, colorB, fraction) ->
+	@mix: (colorA, colorB, fraction = .5, limit = false) ->

 		result = null
-
-		if isNaN(fraction)
-			fraction = .5

 		if typeof colorA is "string" and @isColorString(colorA)
 			colorA = new Color colorA
@@ -140,10 +137,10 @@
 		if colorB instanceof Color

 			result = new Color
-				r: Utils.modulate(fraction, [0, 1], [colorA._r, colorB._r], true)
-				g: Utils.modulate(fraction, [0, 1], [colorA._g, colorB._g], true)
-				b: Utils.modulate(fraction, [0, 1], [colorA._b, colorB._b], true)
-				a: Utils.modulate(fraction, [0, 1], [colorA._a, colorB._a], true)
+				r: Utils.modulate(fraction, [0, 1], [colorA._r, colorB._r], limit)
+				g: Utils.modulate(fraction, [0, 1], [colorA._g, colorB._g], limit)
+				b: Utils.modulate(fraction, [0, 1], [colorA._b, colorB._b], limit)
+				a: Utils.modulate(fraction, [0, 1], [colorA._a, colorB._a], limit)

 		return result


Update atom config - telemetry and editorconfig

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -11,6 +11,7 @@
     disabledPackages: [
       "markdown-preview"
     ]
+    telemetryConsent: "limited"
     themes: [
       "atom-material-ui"
       "solarized-dark-syntax"
@@ -53,3 +54,7 @@
     position: "Left"
   welcome:
     showOnStartup: false
+".editorconfig.source":
+  editor:
+    softWrap: true
+    softWrapAtPreferredLineLength: true


default atom fontsize

default atom fontsize

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -12,7 +12,6 @@
     ]
     telemetryConsent: "limited"
   editor:
-    fontSize: 15
     invisibles: {}
     showIndentGuide: true
     softWrapAtPreferredLineLength: true


Fix delete button on sub comments

Fix delete button on sub comments

--- core/app/backbone/views/sub_comments/react_sub_comment.coffee
+++ core/app/backbone/views/sub_comments/react_sub_comment.coffee
@@ -43,4 +43,4 @@
           _div ["sub-comment-post-delete"],
             ReactSlidingDeleteButton
               model: @model()
-              onDelete: -> @model().destroy wait: true
+              onDelete: => @model().destroy wait: true


update save-session config path

update save-session config path

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -14,7 +14,7 @@
     softWrapAtPreferredLineLength: true
     tabLength: 4
   "save-session":
-    dataSaveFolder: "/Users/chiragraman/.dotfiles/.atom/packages/save-session/projects"
+    dataSaveFolder: "/Users/chiragraman/.dotfiles/atom.symlink/packages/save-session/projects"
   minimap:
     plugins:
       "highlight-selected": true


Modify atom config

Modify atom config

--- atom.symlink/config.cson
+++ atom.symlink/config.cson
@@ -56,7 +56,8 @@
     panelDisplayMode: "closed"
   "linter-eslint":
     useGlobalEslint: true
-  "linter-ui-default": {}
+  "linter-ui-default":
+    showPanel: true
   "prettier-atom":
     formatOnSaveOptions: {}
     prettierOptions:


updated the rewriter to use @prop instead of this.prop -- compiled JS doesn't change

--- src/rewriter.coffee
+++ src/rewriter.coffee
@@ -41,17 +41,17 @@
 # a time. This could certainly be changed into a single pass through the
 # stream, with a big ol' efficient switch, but it's much nicer like this.
 re::rewrite: (tokens) ->
-  this.tokens: tokens
-  this.adjust_comments()
-  this.remove_leading_newlines()
-  this.remove_mid_expression_newlines()
-  this.move_commas_outside_outdents()
-  this.close_open_calls_and_indexes()
-  this.add_implicit_indentation()
-  this.add_implicit_parentheses()
-  this.ensure_balance(BALANCED_PAIRS)
-  this.rewrite_closing_parens()
-  this.tokens
+  @tokens: tokens
+  @adjust_comments()
+  @remove_leading_newlines()
+  @remove_mid_expression_newlines()
+  @move_commas_outside_outdents()
+  @close_open_calls_and_indexes()
+  @add_implicit_indentation()
+  @add_implicit_parentheses()
+  @ensure_balance(BALANCED_PAIRS)
+  @rewrite_closing_parens()
+  @tokens

 # Rewrite the token stream, looking one token ahead and behind.
 # Allow the return value of the block to tell us how many tokens to move
@@ -60,31 +60,31 @@
 re::scan_tokens: (block) ->
   i: 0
   while true
-    break unless this.tokens[i]
-    move: block(this.tokens[i - 1], this.tokens[i], this.tokens[i + 1], i)
+    break unless @tokens[i]
+    move: block(@tokens[i - 1], @tokens[i], @tokens[i + 1], i)
     i += move
   true

 # Massage newlines and indentations so that comments don't have to be
 # correctly indented, or appear on their own line.
 re::adjust_comments: ->
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     return 1 unless token[0] is 'COMMENT'
-    before: this.tokens[i - 2]
-    after:  this.tokens[i + 2]
+    before: @tokens[i - 2]
+    after:  @tokens[i + 2]
     if before and after and
         ((before[0] is 'INDENT' and after[0] is 'OUTDENT') or
         (before[0] is 'OUTDENT' and after[0] is 'INDENT')) and
         before[1] is after[1]
-      this.tokens.splice(i + 2, 1)
-      this.tokens.splice(i - 2, 1)
+      @tokens.splice(i + 2, 1)
+      @tokens.splice(i - 2, 1)
       return 0
     else if prev and prev[0] is 'TERMINATOR' and after[0] is 'INDENT'
-      this.tokens.splice(i + 2, 1)
-      this.tokens[i - 1]: after
+      @tokens.splice(i + 2, 1)
+      @tokens[i - 1]: after
       return 1
     else if prev and prev[0] isnt 'TERMINATOR' and prev[0] isnt 'INDENT' and prev[0] isnt 'OUTDENT'
-      this.tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]])
+      @tokens.splice(i, 0, ['TERMINATOR', "\n", prev[2]])
       return 2
     else
       return 1
@@ -92,21 +92,21 @@
 # Leading newlines would introduce an ambiguity in the grammar, so we
 # dispatch them here.
 re::remove_leading_newlines: ->
-  this.tokens.shift() if this.tokens[0][0] is 'TERMINATOR'
+  @tokens.shift() if @tokens[0][0] is 'TERMINATOR'

 # Some blocks occur in the middle of expressions -- when we're expecting
 # this, remove their trailing newlines.
 re::remove_mid_expression_newlines: ->
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     return 1 unless post and EXPRESSION_CLOSE.indexOf(post[0]) >= 0 and token[0] is 'TERMINATOR'
-    this.tokens.splice(i, 1)
+    @tokens.splice(i, 1)
     return 0

 # Make sure that we don't accidentally break trailing commas, which need
 # to go on the outside of expression closers.
 re::move_commas_outside_outdents: ->
-  this.scan_tokens (prev, token, post, i) =>
-    this.tokens.splice(i, 1, token) if token[0] is 'OUTDENT' and prev[0] is ','
+  @scan_tokens (prev, token, post, i) =>
+    @tokens.splice(i, 1, token) if token[0] is 'OUTDENT' and prev[0] is ','
     return 1

 # We've tagged the opening parenthesis of a method call, and the opening
@@ -114,7 +114,7 @@
 re::close_open_calls_and_indexes: ->
   parens:   [0]
   brackets: [0]
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     switch token[0]
       when 'CALL_START'  then parens.push(0)
       when 'INDEX_START' then brackets.push(0)
@@ -139,7 +139,7 @@
 # deal with them.
 re::add_implicit_parentheses: ->
   stack: [0]
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     stack.push(0) if token[0] is 'INDENT'
     if token[0] is 'OUTDENT'
       last: stack.pop()
@@ -147,12 +147,12 @@
     if stack[stack.length - 1] > 0 and (IMPLICIT_END.indexOf(token[0]) >= 0 or !post?)
       idx: if token[0] is 'OUTDENT' then i + 1 else i
       for tmp in [0...stack[stack.length - 1]]
-        this.tokens.splice(idx, 0, ['CALL_END', ')', token[2]])
+        @tokens.splice(idx, 0, ['CALL_END', ')', token[2]])
       size: stack[stack.length - 1] + 1
       stack[stack.length - 1]: 0
       return size
     return 1 unless prev and IMPLICIT_FUNC.indexOf(prev[0]) >= 0 and IMPLICIT_CALL.indexOf(token[0]) >= 0
-    this.tokens.splice(i, 0, ['CALL_START', '(', token[2]])
+    @tokens.splice(i, 0, ['CALL_START', '(', token[2]])
     stack[stack.length - 1] += 1
     return 2

@@ -161,33 +161,33 @@
 # blocks, so it doesn't need to.
 # ')' can close a single-line block, but we need to make sure it's balanced.
 re::add_implicit_indentation: ->
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     return 1 unless SINGLE_LINERS.indexOf(token[0]) >= 0 and post[0] isnt 'INDENT' and
       not (token[0] is 'ELSE' and post[0] is 'IF')
     starter: token[0]
-    this.tokens.splice(i + 1, 0, ['INDENT', 2, token[2]])
+    @tokens.splice(i + 1, 0, ['INDENT', 2, token[2]])
     idx: i + 1
     parens: 0
     while true
       idx += 1
-      tok: this.tokens[idx]
+      tok: @tokens[idx]
       if (not tok or SINGLE_CLOSERS.indexOf(tok[0]) >= 0 or
           (tok[0] is ')' && parens is 0)) and
           not (starter is 'ELSE' and tok[0] is 'ELSE')
-        insertion: if this.tokens[idx - 1][0] is "," then idx - 1 else idx
-        this.tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]])
+        insertion: if @tokens[idx - 1][0] is "," then idx - 1 else idx
+        @tokens.splice(insertion, 0, ['OUTDENT', 2, token[2]])
         break
       parens += 1 if tok[0] is '('
       parens -= 1 if tok[0] is ')'
     return 1 unless token[0] is 'THEN'
-    this.tokens.splice(i, 1)
+    @tokens.splice(i, 1)
     return 0

 # Ensure that all listed pairs of tokens are correctly balanced throughout
 # the course of the token stream.
 re::ensure_balance: (pairs) ->
   levels: {}
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     for pair in pairs
       [open, close]: pair
       levels[open] ||= 0
@@ -215,7 +215,7 @@
   stack: []
   debt:  {}
   (debt[key]: 0) for key, val of INVERSES
-  this.scan_tokens (prev, token, post, i) =>
+  @scan_tokens (prev, token, post, i) =>
     tag: token[0]
     inv: INVERSES[token[0]]
     # Push openers onto the stack.
@@ -227,7 +227,7 @@
       # If the tag is already in our debt, swallow it.
       if debt[inv] > 0
         debt[inv] -= 1
-        this.tokens.splice(i, 1)
+        @tokens.splice(i, 1)
         return 0
       else
         # Pop the stack of open delimiters.
@@ -240,7 +240,7 @@
           # Unexpected close, insert correct close, adding to the debt.
           debt[mtag] += 1
           val: if mtag is 'INDENT' then match[1] else INVERSES[mtag]
-          this.tokens.splice(i, 0, [INVERSES[mtag], val])
+          @tokens.splice(i, 0, [INVERSES[mtag], val])
           return 1
     else
       return 1


Use more delimeters for jsMath

Use more delimeters for jsMath

--- server/ssr/renderOnServer.coffee
+++ server/ssr/renderOnServer.coffee
@@ -45,8 +45,8 @@
                     extensions: ["tex2jax.js"],
                     jax: ["input/TeX", "output/HTML-CSS"],
                     tex2jax: {
-                        inlineMath: [ ["$","$"] ],
-                        displayMath: [ ["$$","$$"] ],
+                        inlineMath: [ ["$","$"], ["\\\\(", "\\\\)"] ],
+                        displayMath: [ ["$$","$$"], ["\\\\[", "\\\\]"] ],
                         processEscapes: true
                     },
                     "HTML-CSS": { availableFonts: ["TeX"] }


command: JSLint => JavaScript Lint

command: JSLint => JavaScript Lint

--- command.coffee
+++ command.coffee
@@ -1,6 +1,6 @@
 # The `coffee` utility. Handles command-line compilation of CoffeeScript
 # into various forms: saved into `.js` files or printed to stdout, piped to
-# [JSLint](http://javascriptlint.com/) or recompiled every time the source is
+# [JavaScript Lint](http://javascriptlint.com/) or recompiled every time the source is
 # saved, printed as a token stream or as the syntax tree, or launch an
 # interactive REPL.

@@ -32,7 +32,7 @@
   ['-j', '--join [FILE]',     'concatenate the scripts before compiling']
   ['-w', '--watch',           'watch scripts for changes, and recompile']
   ['-p', '--print',           'print the compiled JavaScript to stdout']
-  ['-l', '--lint',            'pipe the compiled JavaScript through JSLint']
+  ['-l', '--lint',            'pipe the compiled JavaScript through JavaScript Lint']
   ['-s', '--stdio',           'listen for and compile scripts over stdio']
   ['-e', '--eval',            'compile a string from the command line']
   ['-r', '--require [FILE*]', 'require a library before executing your script']


Factor out code for attributeOrStyle

Factor out code for attributeOrStyle

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -120,19 +120,26 @@
   xml.documentElement.hasAttribute(prop) and
   /^\s*auto\s*$/i.test xml.documentElement.getAttribute prop

+attributeOrStyle = (node, attr, styleKey = attr) ->
+  if value = node.getAttribute attr
+    value.trim()
+  else
+    style = node.getAttribute 'style'
+    if style
+      match = /(?:^|;)\s*#{styleKey}\s*:\s*([^;\s][^;]*)/i.exec style
+      match?[1]
+
 zIndex = (node) ->
   ## Check whether DOM node has a specified z-index, defaulting to zero.
   ## Note that z-index must be an integer.
   ## 1. https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/z-index suggests
   ## a z-index="..." attribute.  Check for this first.
-  if z = node.getAttribute 'z-index'
-    return parseInt z
   ## 2. Look for style="z-index:..." as in HTML.
-  style = node.getAttribute 'style'
-  return 0 unless style
-  match = /(?:^|\W)z-index\s*:\s*([-\d]+)/i.exec style
-  return 0 unless match?
-  parseInt match[1]
+  z = parseInt attributeOrStyle node, 'z-index'
+  if isNaN z
+    0
+  else
+    z

 class Symbol
   @svgEncoding: 'utf8'
@@ -258,9 +265,8 @@
     @xml.documentElement.removeAttribute 'xmlns'
     @viewBox = svgBBox @xml
     @overflowBox = overflowBox @xml
-    @overflowVisible =
-      @xml.documentElement.hasAttribute('style') and
-      /overflow\s*:\s*visible/.test @xml.documentElement.getAttribute 'style'
+    overflow = attributeOrStyle @xml.documentElement, 'overflow'
+    @overflowVisible = (overflow? and /^visible\b/.test overflow)
     @width = @height = null
     if @viewBox?
       @width = @viewBox[2]


Fixed compile pane not returning proper URI

--- lib/coffee-compile-editor.coffee
+++ lib/coffee-compile-editor.coffee
@@ -37,4 +37,4 @@
     @setText text

   getTitle: -> "Compiled #{@sourceEditor?.getTitle() or ''}".trim()
-  getURI:   -> "coffeecompile://editor/#{@sourceEditorId}"
+  getURI:   -> "coffeecompile://editor/#{@sourceEditor.id}"


winctrl: currentCombos is obsolete

winctrl: currentCombos is obsolete

--- lib/core/windowcontroller.coffee
+++ lib/core/windowcontroller.coffee
@@ -23,7 +23,6 @@

     @windowResizeListeners = {}
     @keyEventsToBeListened = ['keydown', 'keyup', 'keypress']
-    @currentCombos         = {}
     @keyView               = null
     @dragView              = null
     @layers                = []
@@ -254,10 +253,6 @@
   getKeyView:-> @keyView

   key:(event)->
-    # log event.type, @keyView.constructor.name, @keyView.getOptions().name
-    # if Object.keys(@currentCombos).length > 0
-    #   return yes
-    # else
     @emit event.type, event
     @keyView?.handleEvent event


Missed a few.

Missed a few.

--- spec/conflict-marker-spec.coffee
+++ spec/conflict-marker-spec.coffee
@@ -51,16 +51,9 @@
       editor.insertText "Make conflict 1 dirty"
       detectDirty()

-      m.remark()
       lines = m.linesForMarker m.conflicts[1].ours.marker
       expect(lines.hasClass 'conflict-dirty').toBe(true)
       expect(lines.hasClass 'conflict-ours').toBe(false)
-
-    it 'applies the "resolved" class to resolved conflicts', ->
-      m.conflicts[1].ours.resolve()
-      m.remark()
-      lines = m.linesForMarker m.conflicts[1].ours.marker
-      expect(lines.hasClass 'conflict-resolved').toBe(true)

     it 'broadcasts the "merge-conflicts:resolved" event', ->
       event = null


:white_check_mark: add test

:white_check_mark: add test

--- spec/lib/models-generator.coffee
+++ spec/lib/models-generator.coffee
@@ -43,8 +43,6 @@
             assert mergedConfig.hasOwnProperty 'a'
             assert not mergedConfig.hasOwnProperty 'b'

-
-
     describe 'getEmptyJSContent', ->

         it 'returns valid JS code', ->
@@ -56,7 +54,23 @@

             vm.runInContext(mGenerator.getEmptyJSContent(), context)

-
+    describe 'getJSContent', ->
+
+        it 'return valid js code with validation methods', ->
+
+            mGenerator = new ModelsGenerator()
+            definition = [
+                username:
+                    required: true
+                    min: 6,
+                    max: 10,
+                    pattern: '^[a-z]'
+            ]
+            result = mGenerator.getJSContent(definition)
+            assert /validatesPresenceOf\('username'\)/.test(result)
+            assert /validatesFormatOf\('username', \{ with: \/\^\[a-z\]\/ \}\)/.test(result)
+            assert /validatesLengthOf\('username', \{ max: 10 \}\)/.test(result)
+            assert /validatesLengthOf\('username', \{ min: 6 \}\)/.test(result)

     describe 'generateJSONandJS', ->

@@ -67,7 +81,7 @@
             fs.mkdirsSync @generator.destinationDir

             @modelName = 'test-model'
-            @contents = JSON.stringify test: true
+            @contents = test: true

             @generator.generateJSONandJS(@modelName, @contents)

@@ -83,6 +97,41 @@
             content = fs.readFileSync(@generator.destinationDir + '/test-model.js', 'utf8')
             assert content is @generator.getEmptyJSContent()

+    describe 'generateJSONandJS, when give the validation define', ->
+
+        before ->
+            @generator = new ModelsGenerator()
+            @generator.destinationDir = __dirname + '/a/b/c'
+
+            fs.mkdirsSync @generator.destinationDir
+
+            @modelName = 'test-model'
+            @definition =
+                patient:
+                    name: 'patient',
+                    plural: 'patient',
+                    properties:
+                        email:
+                            required: false
+                        username:
+                            type: 'string'
+                            required: true
+                    validations: [
+                        username:
+                            max: 10
+                            min: 6
+                    ]
+
+            modelDefinition = @generator.createModelDefinitions @definition
+            @generator.generateJSONandJS(@modelName, modelDefinition.patient)
+
+        after ->
+            fs.removeSync __dirname + '/a'
+
+        it 'generate non empty JS file', ->
+            content = fs.readFileSync(@generator.destinationDir + '/test-model.js', 'utf8')
+            assert /validatesLengthOf/.test(content)
+            assert /'username', { min: 6 }/.test(content)

     describe 'generateBuiltinModels', ->

@@ -364,4 +413,4 @@

                 assert.deepEqual acls.splice(8, 4), acl

-                done()+                done()


:lipstick: Whitespace

:lipstick: Whitespace

--- spec/minimap-element-spec.coffee
+++ spec/minimap-element-spec.coffee
@@ -109,6 +109,5 @@
         expect(visibleArea.offsetHeight).toBeCloseTo(minimap.getTextEditorHeight(), 0)

       it 'sets the visible visible area offset', ->
-
         expect(visibleArea.offsetTop).toBeCloseTo(minimap.getTextEditorScrollTop() - minimap.getMinimapScrollTop(), 0)
         expect(visibleArea.offsetLeft).toBeCloseTo(minimap.getTextEditorScrollLeft(), 0)


Didn't expect specs to be directly calling loadOutdated, but ok

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -247,10 +247,10 @@
         callback(0, '["boop"]', '')
         onWillThrowError: ->

-      packageManager.loadOutdated ->
+      packageManager.loadOutdated false, ->
       expect(packageManager.apmCache.loadOutdated.value).toMatch(['boop'])

-      packageManager.loadOutdated ->
+      packageManager.loadOutdated false, ->
       expect(packageManager.runCommand.calls.length).toBe(1)

@@ -259,10 +259,10 @@
         callback(0, '["boop"]', '')
         onWillThrowError: ->

-      packageManager.loadOutdated ->
+      packageManager.loadOutdated false, ->
       now = Date.now()
       spyOn(Date, 'now').andReturn((-> now + packageManager.CACHE_EXPIRY + 1)())
-      packageManager.loadOutdated ->
+      packageManager.loadOutdated false, ->

       expect(packageManager.runCommand.calls.length).toBe(2)

@@ -281,16 +281,16 @@
     spyOn(atom.packages, 'unloadPackage').andReturn(true)
     spyOn(atom.packages, 'loadPackage').andReturn(true)

-    packageManager.loadOutdated ->
+    packageManager.loadOutdated false, ->
     expect(packageManager.runCommand.calls.length).toBe(0)

     packageManager.update {}, {}, -> # +1 runCommand call to update the package
-    packageManager.loadOutdated -> # +1 runCommand call to load outdated because the cache should be wiped
+    packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
     expect(packageManager.runCommand.calls.length).toBe(2)

     packageManager.install {}, -> # +1 runCommand call to install the package
-    packageManager.loadOutdated -> # +1 runCommand call to load outdated because the cache should be wiped
+    packageManager.loadOutdated false, -> # +1 runCommand call to load outdated because the cache should be wiped
     expect(packageManager.runCommand.calls.length).toBe(4)

-    packageManager.loadOutdated -> # +0 runCommand call, should be cached
+    packageManager.loadOutdated false, -> # +0 runCommand call, should be cached
     expect(packageManager.runCommand.calls.length).toBe(4)


Don’t use jQuery in package-manager-spec

--- spec/package-manager-spec.coffee
+++ spec/package-manager-spec.coffee
@@ -1,10 +1,14 @@
 path = require 'path'
-{$, $$} = require '../src/space-pen-extensions'
 Package = require '../src/package'
 {Disposable} = require 'atom'

 describe "PackageManager", ->
   workspaceElement = null
+
+  createTestElement = (className) ->
+    element = document.createElement('div')
+    element.className = className
+    element

   beforeEach ->
     workspaceElement = atom.views.getView(atom.workspace)
@@ -393,36 +397,36 @@
     describe "keymap loading", ->
       describe "when the metadata does not contain a 'keymaps' manifest", ->
         it "loads all the .cson/.json files in the keymaps directory", ->
-          element1 = $$ -> @div class: 'test-1'
-          element2 = $$ -> @div class: 'test-2'
-          element3 = $$ -> @div class: 'test-3'
-
-          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
-          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])).toHaveLength 0
-          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
+          element1 = createTestElement('test-1')
+          element2 = createTestElement('test-2')
+          element3 = createTestElement('test-3')
+
+          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0
+          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)).toHaveLength 0
+          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0

           waitsForPromise ->
             atom.packages.activatePackage("package-with-keymaps")

           runs ->
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe "test-1"
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2[0])[0].command).toBe "test-2"
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3[0])).toHaveLength 0
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe "test-1"
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element2)[0].command).toBe "test-2"
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element3)).toHaveLength 0

       describe "when the metadata contains a 'keymaps' manifest", ->
         it "loads only the keymaps specified by the manifest, in the specified order", ->
-          element1 = $$ -> @div class: 'test-1'
-          element3 = $$ -> @div class: 'test-3'
-
-          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
+          element1 = createTestElement('test-1')
+          element3 = createTestElement('test-3')
+
+          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0

           waitsForPromise ->
             atom.packages.activatePackage("package-with-keymaps-manifest")

           runs ->
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1[0])[0].command).toBe 'keymap-2'
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3[0])).toHaveLength 0
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-n', target: element1)[0].command).toBe 'keymap-2'
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-y', target: element3)).toHaveLength 0

       describe "when the keymap file is empty", ->
         it "does not throw an error on activation", ->
@@ -434,9 +438,9 @@

       describe "when the package's keymaps have been disabled", ->
         it "does not add the keymaps", ->
-          element1 = $$ -> @div class: 'test-1'
-
-          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
+          element1 = createTestElement('test-1')
+
+          expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0

           atom.config.set("core.packagesWithKeymapsDisabled", ["package-with-keymaps-manifest"])

@@ -444,11 +448,11 @@
             atom.packages.activatePackage("package-with-keymaps-manifest")

           runs ->
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0

       describe "when the package's keymaps are disabled and re-enabled after it is activated", ->
         it "removes and re-adds the keymaps", ->
-          element1 = $$ -> @div class: 'test-1'
+          element1 = createTestElement('test-1')
           atom.packages.observePackagesWithKeymapsDisabled()

           waitsForPromise ->
@@ -456,10 +460,10 @@

           runs ->
             atom.config.set("core.packagesWithKeymapsDisabled", ['package-with-keymaps-manifest'])
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])).toHaveLength 0
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)).toHaveLength 0

             atom.config.set("core.packagesWithKeymapsDisabled", [])
-            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1[0])[0].command).toBe 'keymap-1'
+            expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: element1)[0].command).toBe 'keymap-1'

     describe "menu loading", ->
       beforeEach ->
@@ -468,7 +472,7 @@

       describe "when the metadata does not contain a 'menus' manifest", ->
         it "loads all the .cson/.json files in the menus directory", ->
-          element = ($$ -> @div class: 'test-1')[0]
+          element = createTestElement('test-1')

           expect(atom.contextMenu.templateForElement(element)).toEqual []

@@ -485,7 +489,7 @@

       describe "when the metadata contains a 'menus' manifest", ->
         it "loads only the menus specified by the manifest, in the specified order", ->
-          element = ($$ -> @div class: 'test-1')[0]
+          element = createTestElement('test-1')

           expect(atom.contextMenu.templateForElement(element)).toEqual []

@@ -529,7 +533,8 @@
             expect(atom.themes.stylesheetElementForId(one)).not.toBeNull()
             expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
             expect(atom.themes.stylesheetElementForId(three)).toBeNull()
-            expect($('#jasmine-content').css('font-size')).toBe '1px'
+
+            expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '1px'

       describe "when the metadata does not contain a 'styleSheets' manifest", ->
         it "loads all style sheets from the styles directory", ->
@@ -556,7 +561,7 @@
             expect(atom.themes.stylesheetElementForId(two)).not.toBeNull()
             expect(atom.themes.stylesheetElementForId(three)).not.toBeNull()
             expect(atom.themes.stylesheetElementForId(four)).not.toBeNull()
-            expect($('#jasmine-content').css('font-size')).toBe '3px'
+            expect(getComputedStyle(document.querySelector('#jasmine-content')).fontSize).toBe '3px'

       it "assigns the stylesheet's context based on the filename", ->
         waitsForPromise ->
@@ -741,8 +746,8 @@

       runs ->
         atom.packages.deactivatePackage('package-with-keymaps')
-        expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-1')[0])).toHaveLength 0
-        expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: ($$ -> @div class: 'test-2')[0])).toHaveLength 0
+        expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-1'))).toHaveLength 0
+        expect(atom.keymaps.findKeyBindings(keystrokes: 'ctrl-z', target: createTestElement('test-2'))).toHaveLength 0

     it "removes the package's stylesheets", ->
       waitsForPromise ->


Test that polling continues when the server responds with a 404 or 304 status

--- spec/unpoly/radio_spec.js.coffee
+++ spec/unpoly/radio_spec.js.coffee
@@ -622,7 +622,25 @@
         next.after 250, ->
           expect(jasmine.Ajax.requests.count()).toBe(2)

-      it 'keeps polling if a request failed', asyncSpec (next) ->
+      it 'keeps polling if the server responds with a 304 Not Modified status', asyncSpec (next) ->
+        up.radio.config.pollInterval = 250
+
+        up.hello(fixture('.element[up-poll][up-source="/source"]'))
+
+        next.after 50, ->
+          expect(jasmine.Ajax.requests.count()).toBe(0)
+
+        next.after 250, ->
+          expect(jasmine.Ajax.requests.count()).toBe(1)
+          jasmine.respondWith(status: 304, responseText: '')
+
+        next.after 50, ->
+          expect(jasmine.Ajax.requests.count()).toBe(1)
+
+        next.after 250, ->
+          expect(jasmine.Ajax.requests.count()).toBe(2)
+
+      it 'keeps polling if a request failed with a network issue', asyncSpec (next) ->
         up.radio.config.pollInterval = 75
         reloadSpy = spyOn(up, 'reload').and.callFake -> return Promise.reject(up.error.failed('network error'))

@@ -633,6 +651,24 @@

         next.after 75, ->
           expect(reloadSpy.calls.count()).toBe(2)
+
+      it 'keeps polling if the server responds with a 404 Not Found error', asyncSpec (next) ->
+        up.radio.config.pollInterval = 250
+
+        up.hello(fixture('.element[up-poll][up-source="/source"]'))
+
+        next.after 50, ->
+          expect(jasmine.Ajax.requests.count()).toBe(0)
+
+        next.after 250, ->
+          expect(jasmine.Ajax.requests.count()).toBe(1)
+          jasmine.respondWith(status: 404, responseText: 'Not found')
+
+        next.after 50, ->
+          expect(jasmine.Ajax.requests.count()).toBe(1)
+
+        next.after 250, ->
+          expect(jasmine.Ajax.requests.count()).toBe(2)

       it 'does not reload if the tab is hidden', asyncSpec (next) ->
         up.radio.config.pollInterval = 50


:fire: coffee script version of the canvas drawer mixin

--- lib/mixins/_canvas-drawer.coffee
+++ lib/mixins/_canvas-drawer.coffee
@@ -1,486 +0,0 @@
-_ = require 'underscore-plus'
-Mixin = require 'mixto'
-
-# Public: The {CanvasDrawer} mixin is responsible for the rendering of a
-# {Minimap} in a `canvas` element.
-#
-# This mixin is injected in the {MinimapElement} prototype, so all these methods
-# are available on any {MinimapElement} instance.
-module.exports =
-class CanvasDrawer extends Mixin
-  ### Public ###
-
-  # Initializes the canvas elements needed to perform the {Minimap} rendering.
-  initializeCanvas: ->
-    @canvas = document.createElement('canvas')
-    @context = @canvas.getContext('2d')
-    @canvas.webkitImageSmoothingEnabled = false
-    @pendingChanges ?= []
-
-    @offscreenCanvas = document.createElement('canvas')
-    @offscreenContext = @offscreenCanvas.getContext('2d')
-
-  # Performs an update of the rendered {Minimap} based on the changes registered
-  # in the instance.
-  updateCanvas: ->
-    firstRow = @minimap.getFirstVisibleScreenRow()
-    lastRow = @minimap.getLastVisibleScreenRow()
-
-    intactRanges = @computeIntactRanges(firstRow, lastRow)
-
-    @context.clearRect(0,0,@canvas.width, @canvas.height)
-
-    if intactRanges.length is 0
-      @drawLines(@context, firstRow, lastRow, 0)
-    else
-      for intact in intactRanges
-        @copyBitmapPart(@context, @offscreenCanvas, intact.domStart, intact.start-firstRow, intact.end-intact.start)
-      @fillGapsBetweenIntactRanges(@context, intactRanges, firstRow, lastRow)
-
-    # copy displayed canvas to offscreen canvas
-    @offscreenCanvas.width = @canvas.width
-    @offscreenCanvas.height = @canvas.height
-    @offscreenContext.drawImage(@canvas, 0, 0)
-    @offscreenFirstRow = firstRow
-    @offscreenLastRow = lastRow
-
-  #     ######   #######  ##        #######  ########   ######
-  #    ##    ## ##     ## ##       ##     ## ##     ## ##    ##
-  #    ##       ##     ## ##       ##     ## ##     ## ##
-  #    ##       ##     ## ##       ##     ## ########   ######
-  #    ##       ##     ## ##       ##     ## ##   ##         ##
-  #    ##    ## ##     ## ##       ##     ## ##    ##  ##    ##
-  #     ######   #######  ########  #######  ##     ##  ######
-
-  # Returns the opacity value to use when rendering the {Minimap} text.
-  #
-  # Returns a {Number}.
-  getTextOpacity: -> @textOpacity
-
-  # Returns the default text color for an editor content.
-  #
-  # The color value is directly read from the `TextEditorView` computed
-  # styles.
-  #
-  # Returns a {String}.
-  getDefaultColor: ->
-    color = @retrieveStyleFromDom(['.editor'], 'color', false, true)
-    @transparentize(color, @getTextOpacity())
-
-  # Returns the text color for the passed-in `token` object.
-  #
-  # The color value is read from the DOM by creating a node structure
-  # that match the token `scope` property.
-  #
-  # token - A token {Object}.
-  #
-  # Returns a {String}.
-  getTokenColor: (token) -> @retrieveTokenColorFromDom(token)
-
-  # Returns the background color for the passed-in `decoration` object.
-  #
-  # The color value is read from the DOM by creating a node structure
-  # that match the decoration `scope` property unless the decoration
-  # provides its own `color` property.
-  #
-  # decoration - A `Decoration` object.
-  #
-  # Returns a {String}.
-  getDecorationColor: (decoration) ->
-    properties = decoration.getProperties()
-    return properties.color if properties.color?
-    @retrieveDecorationColorFromDom(decoration)
-
-  # Internal: Returns the text color for the passed-in token.
-  #
-  # token - A token {Object}.
-  #
-  # Returns a {String}.
-  retrieveTokenColorFromDom: (token) ->
-    # This is quite an expensive operation so results are cached in getTokenColor.
-    scopes = (token.scopeDescriptor or token.scopes)
-    color = @retrieveStyleFromDom(scopes, 'color')
-    @transparentize(color, @getTextOpacity())
-
-  # Internal: Returns the background color for the passed-in decoration.
-  #
-  # decoration - A `Decoration` object.
-  #
-  # Returns a {String}.
-  retrieveDecorationColorFromDom: (decoration) ->
-    @retrieveStyleFromDom(decoration.getProperties().scope.split(/\s+/), 'background-color', false)
-
-  # Internal: Converts a `rgb(...)` color into a `rgba(...)` color
-  # with the specified opacity.
-  #
-  # color - The {String} of the color to modify.
-  # opacity - The opacity {Number} to apply to the color.
-  #
-  # Returns a {String}.
-  transparentize: (color, opacity=1) ->
-    color.replace('rgb(', 'rgba(').replace(')', ", #{opacity})")
-
-  #    ########  ########     ###    ##      ##
-  #    ##     ## ##     ##   ## ##   ##  ##  ##
-  #    ##     ## ##     ##  ##   ##  ##  ##  ##
-  #    ##     ## ########  ##     ## ##  ##  ##
-  #    ##     ## ##   ##   ######### ##  ##  ##
-  #    ##     ## ##    ##  ##     ## ##  ##  ##
-  #    ########  ##     ## ##     ##  ###  ###
-
-  # Internal: Draws lines on the passed-in `context`.
-  #
-  # The lines range to draw is specified by the `firstRow` and `lastRow`
-  # parameters.
-  #
-  # context - The canvas context {Object} into which drawing the lines.
-  # firstRow - The starting row {Number} of the lines range to draw.
-  # endRow - The ending row {Number} of the lines range to draw.
-  # offsetRow - The offset {Number} to apply to rows index.
-  drawLines: (context, firstRow, lastRow, offsetRow) ->
-    return if firstRow > lastRow
-
-    lines = @getTextEditor().tokenizedLinesForScreenRows(firstRow, lastRow)
-    lineHeight = @minimap.getLineHeight() * devicePixelRatio
-    charHeight = @minimap.getCharHeight() * devicePixelRatio
-    charWidth = @minimap.getCharWidth() * devicePixelRatio
-    canvasWidth = @canvas.width
-    displayCodeHighlights = @displayCodeHighlights
-    decorations = @minimap.decorationsByTypeThenRows(firstRow, lastRow)
-
-    line = lines[0]
-
-    # Whitespaces can be substituted by other characters so we need
-    # to replace them when that's the case.
-    invisibleRegExp = @getInvisibleRegExp(line)
-
-    for line, row in lines
-      x = 0
-      y = offsetRow + row
-      screenRow = firstRow + row
-      y0 = y*lineHeight
-
-      # Line decorations are first drawn on the canvas.
-      lineDecorations = decorations['line']?[screenRow]
-
-      @drawLineDecorations(context, lineDecorations, y0, canvasWidth, lineHeight) if lineDecorations?.length
-
-      # Then comes the highlight decoration with `highlight-under` type.
-      highlightDecorations = decorations['highlight-under']?[firstRow + row]
-      if highlightDecorations?.length
-        for decoration in highlightDecorations
-          @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)
-
-      # Then the line tokens are drawn
-      if line?.tokens?
-        for token in line.tokens
-          w = token.screenDelta
-          unless token.isOnlyWhitespace()
-            color = if displayCodeHighlights
-              @getTokenColor(token)
-            else
-              @getDefaultColor()
-
-            value = token.value
-            value = value.replace(invisibleRegExp, ' ') if invisibleRegExp?
-
-            x = @drawToken(context, value, color, x, y0, charWidth, charHeight)
-          else
-            x += w * charWidth
-
-          break if x > canvasWidth
-
-      # Finally the highlight over decorations are drawn.
-      highlightDecorations = decorations['highlight-over']?[firstRow + row]
-      if highlightDecorations?.length
-        for decoration in highlightDecorations
-          @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)
-
-      # And the highlight box decorations are drawn.
-      highlightDecorations = decorations['highlight-outline']?[firstRow + row]
-      if highlightDecorations?.length
-        for decoration in highlightDecorations
-          @drawHighlightOutlineDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)
-
-    context.fill()
-
-  # Internal: Returns the regexp to replace invisibles substitution characters
-  # in editor lines.
-  #
-  # line - The screen line for which replacing the invisibles characters.
-  getInvisibleRegExp: (line) ->
-    if line? and line.invisibles?
-      invisibles = []
-      invisibles.push line.invisibles.cr if line.invisibles.cr?
-      invisibles.push line.invisibles.eol if line.invisibles.eol?
-      invisibles.push line.invisibles.space if line.invisibles.space?
-      invisibles.push line.invisibles.tab if line.invisibles.tab?
-
-      ///#{invisibles.filter((s) -> typeof s is 'string').map(_.escapeRegExp).join('|')}///g
-
-  # Internal: Draws a single token on the given context.
-  #
-  # context - The canvas context object onto which draw the token.
-  # text - The {String} text of the token.
-  # color - The {String} color of the token.
-  # x - The {Number} position on the x axis at which render the token.
-  # y - The {Number} position on the y axis at which render the token.
-  # charWidth - The char width {Number}.
-  # charHeight - The char height {Number}.
-  #
-  # Returns a {Number} that correspond to the new x position after the render.
-  drawToken: (context, text, color, x, y, charWidth, charHeight) ->
-    context.fillStyle = color
-    chars = 0
-    for char in text
-      if /\s/.test char
-        if chars > 0
-          context.fillRect(x-(chars * charWidth), y, chars*charWidth, charHeight)
-        chars = 0
-      else
-        chars++
-
-      x += charWidth
-
-    context.fillRect(x-(chars * charWidth), y, chars*charWidth, charHeight) if chars > 0
-
-    x
-
-  # Internal: Draws a line decoration on the passed-in context.
-  #
-  # context - The canvas context object.
-  # decoration - The `Decoration` object to render.
-  # y - The {Number} position on the y axis at which render the decoration.
-  # canvasWidth - The {Number} of the canvas width.
-  # lineHeight - The {Number} for the line height.
-  drawLineDecorations: (context, decorations, y, canvasWidth, lineHeight) ->
-    for decoration in decorations
-      context.fillStyle = @getDecorationColor(decoration)
-      context.fillRect(0,y,canvasWidth,lineHeight)
-
-    return
-
-  # Internal: Draws a highlight decoration on the passed-in context.
-  #
-  # It renders only the part of the highlight corresponding to the specified
-  # row.
-  #
-  # context - The canvas context object.
-  # decoration - The `Decoration` object to render.
-  # y - The {Number} position on the y axis at which render the decoration.
-  # screenRow - The row {Number} corresponding to the rendered row.
-  # lineHeight - The {Number} for the line height.
-  # charWidth - The {Number} for the character width.
-  # canvasWidth - The {Number} of the canvas width.
-  drawHighlightDecoration: (context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth) ->
-    context.fillStyle = @getDecorationColor(decoration)
-    range = decoration.getMarker().getScreenRange()
-    rowSpan = range.end.row - range.start.row
-
-    if rowSpan is 0
-      colSpan = range.end.column - range.start.column
-      context.fillRect(range.start.column*charWidth,y*lineHeight,colSpan*charWidth,lineHeight)
-    else
-      if screenRow is range.start.row
-        x = range.start.column * charWidth
-        context.fillRect(x,y*lineHeight,canvasWidth-x,lineHeight)
-      else if screenRow is range.end.row
-        context.fillRect(0,y*lineHeight,range.end.column * charWidth,lineHeight)
-      else
-        context.fillRect(0,y*lineHeight,canvasWidth,lineHeight)
-
-  # Internal: Draws a highlight outline decoration on the passed-in context.
-  #
-  # It renders only the part of the highlight corresponding to the specified
-  # row.
-  #
-  # context - The canvas context object.
-  # decoration - The `Decoration` object to render.
-  # y - The {Number} position on the y axis at which render the decoration.
-  # screenRow - The row {Number} corresponding to the rendered row.
-  # lineHeight - The {Number} for the line height.
-  # charWidth - The {Number} for the character width.
-  # canvasWidth - The {Number} of the canvas width.
-  drawHighlightOutlineDecoration: (context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth) ->
-    context.fillStyle = @getDecorationColor(decoration)
-    range = decoration.getMarker().getScreenRange()
-    rowSpan = range.end.row - range.start.row
-
-    if rowSpan is 0
-      colSpan = range.end.column - range.start.column
-      width = colSpan * charWidth
-      xStart = range.start.column * charWidth
-      xEnd = xStart + width
-      yStart = y * lineHeight
-      yEnd = yStart + lineHeight
-
-      context.fillRect(xStart, yStart, width, 1)
-      context.fillRect(xStart, yEnd, width, 1)
-      context.fillRect(xStart, yStart, 1, lineHeight)
-      context.fillRect(xEnd, yStart, 1, lineHeight)
-
-    else if rowSpan is 1
-      xStart = range.start.column * charWidth
-      xEnd = range.end.column * charWidth
-      if screenRow is range.start.row
-        width = canvasWidth - xStart
-        yStart = y * lineHeight
-        yEnd = yStart + lineHeight
-        xBottomStart = Math.max(xStart, xEnd)
-        bottomWidth = canvasWidth - xBottomStart
-
-        context.fillRect(xStart, yStart, width, 1)
-        context.fillRect(xBottomStart, yEnd, bottomWidth, 1)
-        context.fillRect(xStart, yStart, 1, lineHeight)
-        context.fillRect(canvasWidth - 1, yStart, 1, lineHeight)
-      else
-        width = canvasWidth - xStart
-        yStart = y * lineHeight
-        yEnd = yStart + lineHeight
-        bottomWidth = canvasWidth - xEnd
-
-        context.fillRect(0, yStart, xStart, 1)
-        context.fillRect(0, yEnd, xEnd, 1)
-        context.fillRect(0, yStart, 1, lineHeight)
-        context.fillRect(xEnd, yStart, 1, lineHeight)
-    else
-      xStart = range.start.column * charWidth
-      xEnd = range.end.column * charWidth
-
-      if screenRow is range.start.row
-        width = canvasWidth - xStart
-        yStart = y * lineHeight
-        yEnd = yStart + lineHeight
-
-        context.fillRect(xStart, yStart, width, 1)
-        context.fillRect(xStart, yStart, 1, lineHeight)
-        context.fillRect(canvasWidth - 1, yStart, 1, lineHeight)
-
-      else if screenRow is range.end.row
-        width = canvasWidth - xStart
-        yStart = y * lineHeight
-        yEnd = yStart + lineHeight
-
-        context.fillRect(0, yEnd, xEnd, 1)
-        context.fillRect(0, yStart, 1, lineHeight)
-        context.fillRect(xEnd, yStart, 1, lineHeight)
-      else
-        yStart = y * lineHeight
-        yEnd = yStart + lineHeight
-
-        context.fillRect(0, yStart, 1, lineHeight)
-        context.fillRect(canvasWidth - 1, yStart, 1, lineHeight)
-
-        if screenRow is range.start.row + 1
-          context.fillRect(0, yStart, xStart, 1)
-
-        if screenRow is range.end.row - 1
-          context.fillRect(xEnd, yEnd, canvasWidth - xEnd, 1)
-
-  # Internal: Copy a part of the offscreen bitmap into the onscreen one to
-  # reduce the amount of rendered lines during scroll.
-  #
-  # context - The canvas context object.
-  # bitmapCanvas - The source bitmap.
-  # srcRow - The row {Number} on the source bitmap.
-  # destRow - The row {Number} on the destination bitmap.
-  # rowCount - The {Number} of rows to copy.
-  copyBitmapPart: (context, bitmapCanvas, srcRow, destRow, rowCount) ->
-    lineHeight = @minimap.getLineHeight() * devicePixelRatio
-    context.drawImage(bitmapCanvas, 0, srcRow * lineHeight, bitmapCanvas.width, rowCount * lineHeight, 0, destRow * lineHeight, bitmapCanvas.width, rowCount * lineHeight)
-
-  #    ########     ###    ##    ##  ######   ########  ######
-  #    ##     ##   ## ##   ###   ## ##    ##  ##       ##    ##
-  #    ##     ##  ##   ##  ####  ## ##        ##       ##
-  #    ########  ##     ## ## ## ## ##   #### ######    ######
-  #    ##   ##   ######### ##  #### ##    ##  ##             ##
-  #    ##    ##  ##     ## ##   ### ##    ##  ##       ##    ##
-  #    ##     ## ##     ## ##    ##  ######   ########  ######
-
-  ### Internal ###
-
-  # Renders the lines between the intact ranges when an update has pending
-  # changes.
-  #
-  # context - The canvas context object.
-  # intactRanges - The {Array} of intact ranges.
-  # firstRow - The first visible row index {Number}.
-  # lastRow - The last visible row index {Number}.
-  fillGapsBetweenIntactRanges: (context, intactRanges, firstRow, lastRow) ->
-    currentRow = firstRow
-    # intactRanges is sorted, we can safely fill between ranges
-    for intact in intactRanges
-      @drawLines(context, currentRow, intact.start-1, currentRow-firstRow)
-      currentRow = intact.end
-    if currentRow <= lastRow
-      @drawLines(context, currentRow, lastRow, currentRow-firstRow)
-
-  # Computes the ranges that are not affected by the current pending changes.
-  #
-  # firstRow - The first visible row index {Number}.
-  # lastRow - The last visible row index {Number}.
-  #
-  # Returns an {Array} of ranges.
-  computeIntactRanges: (firstRow, lastRow) ->
-    return [] if !@offscreenFirstRow? and !@offscreenLastRow?
-
-    intactRanges = [{start: @offscreenFirstRow, end: @offscreenLastRow, domStart: 0}]
-
-    for change in @pendingChanges
-      newIntactRanges = []
-      for range in intactRanges
-        if change.end < range.start and change.screenDelta != 0
-          newIntactRanges.push(
-            start: range.start + change.screenDelta
-            end: range.end + change.screenDelta
-            domStart: range.domStart
-          )
-        else if change.end < range.start or change.start > range.end
-          newIntactRanges.push(range)
-        else
-          if change.start > range.start
-            newIntactRanges.push(
-              start: range.start
-              end: change.start - 1
-              domStart: range.domStart)
-          if change.end < range.end
-            # If the bufferDelta is 0 then it's a change in the screen lines
-            # due to soft wrapping, we don't need to touch to the intact ranges
-            unless change.bufferDelta is 0
-              newIntactRanges.push(
-                start: change.end + change.screenDelta + 1
-                end: range.end + change.screenDelta
-                domStart: range.domStart + change.end + 1 - range.start
-              )
-
-        intactRange = newIntactRanges[newIntactRanges.length - 1]
-
-      intactRanges = newIntactRanges
-
-    @truncateIntactRanges(intactRanges, firstRow, lastRow)
-
-    @pendingChanges = []
-
-    intactRanges
-
-  # Truncates the intact ranges so that they doesn't expand past the visible
-  # area of the minimap.
-  #
-  # intactRanges - The {Array} of ranges to truncate.
-  # firstRow - The first visible row index {Number}.
-  # lastRow - The last visible row index {Number}.
-  #
-  # Returns an {Array} of ranges.
-  truncateIntactRanges: (intactRanges, firstRow, lastRow) ->
-    i = 0
-    while i < intactRanges.length
-      range = intactRanges[i]
-      if range.start < firstRow
-        range.domStart += firstRow - range.start
-        range.start = firstRow
-      if range.end > lastRow
-        range.end = lastRow
-      if range.start >= range.end
-        intactRanges.splice(i--, 1)
-      i++
-    intactRanges.sort (a, b) -> a.domStart - b.domStart


api/hub: nailing api key behavior

api/hub: nailing api key behavior

--- src/smc-hub/test/api/apikey.coffee
+++ src/smc-hub/test/api/apikey.coffee
@@ -33,7 +33,7 @@
                 winston.info(err, resp)
                 done(err)

-    it 'wrong api key', (done) ->
+    it 'allows a wrong api key to ping', (done) ->
         fake_key = 'sk_173r2rj32'

         http_message_api_v1
@@ -51,11 +51,28 @@
                 #else
                 #    expect(err).toInclude('No account found.')
                 #    done()
-                expect(err).toBe(undefined)
-                expect(resp).toBe({})
+                expect(resp.event).toBe('pong')
+                expect(resp.now).toExist()
                 done(err)

-    it 'blocks banned users', (done) ->
+    it 'blocks a wrong api key from creating a project', (done) ->
+        fake_key = 'sk_173nsmeje32'
+
+        http_message_api_v1
+            event         : 'create_project'
+            database      : api.db
+            compute_server: api.compute_server
+            api_key       : fake_key
+            ip_address    : '3.4.5.6'
+            logger        : api.logger
+            body          : {start: true}
+            cb            : (err, resp) ->
+                winston.info(err, resp)
+                expect(resp.event).toBe('error')
+                expect(resp.error).toInclude('You must be signed in')
+                done(err)
+
+    it 'blocks banned users, even for pings', (done) ->
         async.series([
             (cb) ->
                 api.db.ban_user
@@ -69,7 +86,7 @@
                     database      : api.db
                     compute_server: api.compute_server
                     api_key       : api.api_key
-                    ip_address    : '2.3.4.5'
+                    ip_address    : '7.8.9.10'
                     logger        : api.logger
                     body          : {}
                     cb            : (err, resp) ->


Fixes the context-menu selectors

Fixes the context-menu selectors

They recently changed it in tree-view package

--- menus/menu.cson
+++ menus/menu.cson
@@ -1,5 +1,5 @@
 # See https://atom.io/docs/latest/creating-a-package#menus for more details
 'context-menu':
-  '.tree-view.full-menu': [
-    {label: 'New Component', command: 'ueno-atom-menu:new-component'}
+  '.tree-view .full-menu [is="tree-view-directory"]': [
+    { label: 'New Component', command: 'ueno-atom-menu:new-component' }
   ]


:art: one check

:art: one check

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -187,7 +187,7 @@
     selections = @editor.getSelections()
     return unless selections?
     @editor.transact =>
-      if match.prefix? and match.prefix.length > 0
+      if match.prefix?.length > 0
         @editor.selectLeft(match.prefix.length)
         @editor.delete()


Add TODO in file summary

Add TODO in file summary

--- lib/changes/file-summary.coffee
+++ lib/changes/file-summary.coffee
@@ -49,6 +49,7 @@
     filename  = pathParts.pop()
     dir       = pathParts.join(path.sep)
     dir      += path.sep if dir
+    # TODO: This should probably return an object with descriptive keys.
     [dir, filename]

   path: ->


minor corrections

minor corrections

--- library/exposure/pinpoint.coffee
+++ library/exposure/pinpoint.coffee
@@ -76,7 +76,6 @@
         assert _.isFunction(c = callback or null), noCallback
         assert _.isString(s = selector or null), noSelector
         assert instruct = try queries: [element: selector]
-        _.head(instruct.queries).elementAttributes = false
         pp = "watch mutation of %s selector for #{@service}"
         try logger.info pp.toString(), selector.toString()
         make = -> observer = new MutationSummary instruct


Test POST to områder collection

Test POST to områder collection

--- test/integration/collection-int.coffee
+++ test/integration/collection-int.coffee
@@ -365,6 +365,14 @@
         ]
       .end done

+  it 'should succeed posting to områder collection', (done) ->
+    req.post '/omr%C3%A5der?api_key=dnt'
+      .send navn: 'Test'
+      .expect 201
+      .expect (res) ->
+        assert.equal res.body.document.navn, 'Test'
+      .end done
+
 for method in ['put', 'patch', 'delete']
   describe method.toUpperCase(), ->
     it 'should return 405 Method Not Allowed status', (done) ->


travis timing

travis timing

--- test/integration/eventsAPITests.coffee
+++ test/integration/eventsAPITests.coffee
@@ -87,7 +87,7 @@
             # Create mock endpoint to forward requests to
             mockServer = testUtils.createMockMediatorServer 200, mockResponse, 1232, ->
               mockServer2 = testUtils.createMockMediatorServer 200, mockResponse, 1233, ->
-                slowMockServer = testUtils.createSlowMockMediatorServer 500*global.testTimeoutFactor, 200, mockResponse, 1234, -> done()
+                slowMockServer = testUtils.createSlowMockMediatorServer 150*global.testTimeoutFactor, 200, mockResponse, 1234, -> done()

   after (done) ->
     Channel.remove { name: 'TEST DATA - Mock endpoint' }, ->
@@ -274,7 +274,7 @@

               done()

-        setTimeout validate, 2000 * global.testTimeoutFactor
+        setTimeout validate, 1100 * global.testTimeoutFactor

   it 'should add mediator info for slow secondary routes', (done) ->
     startTime = new Date()
@@ -309,4 +309,4 @@

               done()

-        setTimeout validate, 2000 * global.testTimeoutFactor
+        setTimeout validate, 1100 * global.testTimeoutFactor


change == to equiv function

change == to equiv function

--- lib/assets/javascripts/directives/filteredselect.js.coffee
+++ lib/assets/javascripts/directives/filteredselect.js.coffee
@@ -45,6 +45,12 @@
         span.append tempHolder
         element.append span
         tempHolder.append template
+      equiv = (left,right) ->
+        return true if left == right
+        return true if (!!left && !!right) == false
+        if !isNaN(left) && !isNaN(right)
+          return true if parseFloat(left) == parseFloat(right)
+        false

       ulHeight = ->
         full = window.innerHeight
@@ -76,15 +82,19 @@
             li = angular.element '' + viewValueFn(item) + ''
           ip = angular.element '<input type="hidden">'
           ip.val(modelValueFn(item))
+          console.dir typeof modelValueFn(item)
           li.append(ip)
           li.bind 'mousedown', ($event)->
+            console.dir typeof $event.target.children[0].value
             updateValue($event.target.children[0].value)
           template.append(li)
       setInitialValue = ->
         unless isMobile
           obj = {}
           obj[modelValue] = ngModel.$modelValue
-          list = $filter('filter')(collection(scope), obj,true)
+          console.dir obj
+          list = $filter('filter')(collection(scope), obj,equiv)
+          console.dir list
           viewScope = list[0] if list
           search.val(if viewScope then viewValueFn(viewScope) else '')
         else
@@ -106,8 +116,7 @@
       updateValue = (model) ->
         ngModel.$setViewValue(model)
         tempHolder.removeClass('active')
-        $timeout ->
-          setInitialValue() unless isMobile
+        setInitialValue()
       done = false
       fieldset = element
       until done


Added generic copyToClipboard().

Added generic copyToClipboard().

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -17,6 +17,12 @@
   title: (name) ->
 #    $('html head title').html( @Config.get(product_name) + ' - ' + App.i18n.translateInline(name) )
     document.title = @Config.get('product_name') + ' - ' + App.i18n.translatePlain(name)
+
+  copyToClipboard: (text) ->
+    if window.clipboardData # IE
+      window.clipboardData.setData( 'Text', text )
+    else
+      window.prompt( "Copy to clipboard: Ctrl+C, Enter", text )

   # add @notify methode to create notification
   notify: (data) ->


Fix select callback issue on program selection

--- app/assets/javascripts/app/controllers/people/affairs/products.js.coffee
+++ app/assets/javascripts/app/controllers/people/affairs/products.js.coffee
@@ -63,10 +63,18 @@
     @product_description.html @view('people/affairs/products/_description')(object: ui.item)

   update_description_on_select: =>
+
+    select_callback_for = (div, ui) =>
+      div.html @view('people/affairs/products/_description')(object: ui.item)
+      text_field = div.parent().find("input[type='search']")
+      hidden_field = div.parent().find("input[type='hidden']")
+      hidden_field.attr('value', ui.item.id)
+      text_field.attr('value', ui.item.title) if ui.item.title
+
     @parent_field.autocomplete
-      select: (e, ui) => @parent_description.html @view('people/affairs/products/_description')(object: ui.item)
+      select: (e, ui) => select_callback_for(@parent_description, ui)
     @program_field.autocomplete
-      select: (e, ui) => @program_description.html @view('people/affairs/products/_description')(object: ui.item)
+      select: (e, ui) => select_callback_for(@program_description, ui)

 class New extends PersonAffairProductExtention
   events:


user new api url

user new api url

--- app/assets/javascripts/angular_files/components/calendar.api.coffee
+++ app/assets/javascripts/angular_files/components/calendar.api.coffee
@@ -1,8 +1,7 @@
-#/api/v1/needs.json
 angular.module("iamin")
   .service("ApiCalendar", ['$http', ($http) ->
     class ApiCalendar
-      needs: -> $http.get("/api/v1/org/needs.json").then (res) =>
+      needs: -> $http.get("/api/calendar/org/needs.json").then (res) =>
         @_parse_needs res.data.data

       addNeed: (need) ->
@@ -15,7 +14,7 @@
         # need['start-time'] = need.start
         # need['end-time'] = need.end#.format()
         $http({
-          url: "/api/v1/needs.json",
+          url: "/api/calendar/needs.json",
           dataType: "json",
           method: "POST",
           headers: {
@@ -35,7 +34,7 @@
         # need['start-time'] = need.start
         # need['end-time'] = need.end#.format()
         $http({
-          url: "/api/v1/needs/" + niceNeed.id + '.json'
+          url: "/api/calendar/needs/" + niceNeed.id + '.json'
           dataType: "json",
           method: "PUT",
           headers: {
@@ -55,7 +54,7 @@
         # need['start-time'] = need.start
         # need['end-time'] = need.end#.format()
         $http({
-          url: "/api/v1/needs/" + need.id + '.json'
+          url: "/api/calendar/needs/" + need.id + '.json'
           dataType: "json",
           method: "DELETE",
           headers: {


Adjust gallery center padding values

Adjust gallery center padding values

--- app/assets/javascripts/martian_components/components/gallery.coffee
+++ app/assets/javascripts/martian_components/components/gallery.coffee
@@ -29,7 +29,7 @@
     if @options.padding
       customOptions =
         centerMode:     true
-        centerPadding:  '32%'
+        centerPadding:  '30%'
         slidesToShow:   1
         slidesToScroll: 1
         focusOnSelect:  true
@@ -37,6 +37,10 @@
           {
             breakpoint: 1600
             settings: {centerPadding:  '28%'}
+          }
+          {
+            breakpoint: 1400
+            settings: {centerPadding:  '25%'}
           }
           {
             breakpoint: 1100


use ComboKeys instead of mousetrap change isGlobal to global change targetNode to targetNodeSelector add stopCallback that should stop firing events when you type inside input like elements and the content is printable remove ref prop

--- src/component/shortcuts.coffee
+++ src/component/shortcuts.coffee
@@ -1,7 +1,7 @@
 React = require 'react'
 ReactDOM = require 'react-dom'
 invariant = require 'invariant'
-createMousetrap = require 'mousetrap'
+Combokeys = require 'combokeys'

 shortcuts = React.createFactory 'shortcuts'

@@ -9,8 +9,8 @@
 module.exports = React.createClass
   displayName: 'Shortcuts'

-  # NOTE: mousetrap must be instance per component
-  _mousetrap: null
+  # NOTE: combokeys must be instance per component
+  _combokeys: null

   contextTypes:
     shortcuts: React.PropTypes.object.isRequired
@@ -23,78 +23,88 @@
     eventType: React.PropTypes.string
     stopPropagation: React.PropTypes.bool
     preventDefault: React.PropTypes.bool
-    targetNode: React.PropTypes.object
-    ref: React.PropTypes.string
-    isGlobal: React.PropTypes.bool
+    targetNodeSelector: React.PropTypes.string
+    global: React.PropTypes.bool

   getDefaultProps: ->
     tabIndex: null
     className: null
-    ref: null
     eventType: null
     stopPropagation: true
     preventDefault: false
-    targetNode: null
-    isGlobal: false
+    targetNodeSelector: null
+    global: false

   _bindShortcuts: (shortcutsArr) ->
     element = @_getElementToBind()
     element.setAttribute('tabindex', @props.tabIndex or -1)
-    @_mousetrap = createMousetrap(element)
-    @_monkeyPatchMousetrap()
-    @_mousetrap.bind(shortcutsArr, @_handleShortcuts, @props.eventType)
+    @_combokeys = new Combokeys(element)
+    @_decorateCombokeys()
+    @_combokeys.bind(shortcutsArr, @_handleShortcuts, @props.eventType)

-    if @props.isGlobal
+    if @props.global
       element.addEventListener 'shortcuts:global', @_customGlobalHandler

   _customGlobalHandler: (e) ->
-    {character, modifiers, event} = e.detail
+    { character, modifiers, event } = e.detail
+
+    targetNode = null
+    if @props.targetNodeSelector
+      targetNode = document.querySelector(@props.targetNodeSelector)

     if e.target isnt ReactDOM.findDOMNode(this) and
-        e.target isnt @props.targetNode
-      # NOTE: this is kind of hack
-      @_mousetrap._handleKey(character, modifiers, event, true)
+        e.target isnt targetNode
+      @_combokeys.handleKey(character, modifiers, event, true)

-  _lastEvent: null
+  _decorateCombokeys: ->
+    element = @_getElementToBind()
+    originalHandleKey = @_combokeys.handleKey.bind(@_combokeys)

-  _monkeyPatchMousetrap: ->
-    element = @_getElementToBind()
-    originalHandleKey = @_mousetrap._handleKey
+    # NOTE: stopCallback is a method that is called to see
+    # if the keyboard event should fire
+    @_combokeys.stopCallback = (event, element, combo) ->
+      isInputLikeElement = element.tagName == 'INPUT' or
+        element.tagName == 'SELECT' or element.tagName == 'TEXTAREA' or
+          (element.contentEditable and element.contentEditable == 'true')
+      isReturnString = event.key?.length == 1

-    @_mousetrap._handleKey = (character, modifiers, event, customEvent) =>
+      if isInputLikeElement and isReturnString
+        return true
+
+      return false
+
+    @_combokeys.handleKey = (character, modifiers, event, customEvent) =>
       if not customEvent
         element.dispatchEvent new CustomEvent 'shortcuts:global',
           detail: {character, modifiers, event}
           bubbles: true
           cancelable: true

-        return null if @_lastEvent is event
-
-      @_lastEvent = event
       event.preventDefault() if @props.preventDefault
       event.stopPropagation() if @props.stopPropagation and not customEvent
+
       originalHandleKey(character, modifiers, event)

   _getElementToBind: ->
-    if @props.targetNode
-      element = @props.targetNode
+    if @props.targetNodeSelector
+      element = document.querySelector(@props.targetNodeSelector)
+      invariant(element, "Node selector '#{@props.targetNodeSelector}'  was not found.")
     else
       element = ReactDOM.findDOMNode(this)

-    invariant(element, 'TargetNode was not found.')
     return element

-  _unbindShortcuts: (shortcutsArr) ->
+  _unbindShortcuts: ->
     element = @_getElementToBind()
     element.removeAttribute('tabindex')

-    if @_mousetrap
-      @_mousetrap.unbind(shortcutsArr, @props.eventType)
-      @_mousetrap.reset()
+    if @_combokeys
+      @_combokeys.detach()
+      @_combokeys.reset()

   _onUpdate: ->
     shortcutsArr = @context.shortcuts.getShortcuts(@props.name)
-    @_unbindShortcuts(shortcutsArr)
+    @_unbindShortcuts()
     @_bindShortcuts(shortcutsArr)

   componentDidMount: ->
@@ -105,9 +115,8 @@
     shortcutsArr = @context.shortcuts.getShortcuts(@props.name)
     @_unbindShortcuts(shortcutsArr)
     @context.shortcuts.removeUpdateListener(@_onUpdate)
-    @_lastEvent = null

-    if @props.isGlobal
+    if @props.global
       element = @_getElementToBind()
       element.removeEventListener 'shortcuts:global', @_customGlobalHandler

@@ -116,11 +125,8 @@
     @props.handler(shortcutName, event)

   render: ->
-    element = shortcuts
-
-    element
+    shortcuts
       tabIndex: @props.tabIndex or -1
-      ref: @props.ref
       className: @props.className,

       @props.children


Create templates when constructing `NotificationElement`

--- lib/notification-element.coffee
+++ lib/notification-element.coffee
@@ -45,12 +45,13 @@
 class NotificationElement
   animationDuration: 360
   visibilityDuration: 5000
-  fatalTemplate: TemplateHelper.create(FatalMetaNotificationTemplate)
-  metaTemplate: TemplateHelper.create(MetaNotificationTemplate)
-  buttonListTemplate: TemplateHelper.create(ButtonListTemplate)
-  buttonTemplate: TemplateHelper.create(ButtonTemplate)

   constructor: (@model) ->
+    @fatalTemplate = TemplateHelper.create(FatalMetaNotificationTemplate)
+    @metaTemplate = TemplateHelper.create(MetaNotificationTemplate)
+    @buttonListTemplate = TemplateHelper.create(ButtonListTemplate)
+    @buttonTemplate = TemplateHelper.create(ButtonTemplate)
+
     @element = document.createElement('atom-notification')
     @issue = new NotificationIssue(@model) if @model.getType() is 'fatal'
     @renderPromise = @render().catch (e) ->


Fix guard clause

Fix guard clause

--- lib/rubocop-auto-correct.coffee
+++ lib/rubocop-auto-correct.coffee
@@ -50,7 +50,7 @@
     process

   run: (editor) ->
-    if editor.getGrammar().scopeName != "source.ruby"
+    unless editor.getGrammar().scopeName.match("ruby")
       return atom.notifications.addError("Only use source.ruby")
     if editor.isModified()
       editor.save()


Removed newline at the end of the dummy grammar

--- grammars/dummy.cson
+++ grammars/dummy.cson
@@ -1 +1 @@
-'scopeName': 'levels.source.dummy'
+'scopeName': 'levels.source.dummy'


Edit line comment + replace $base with $self

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -6,7 +6,7 @@
 # Atom uses the following regex engine -> https://github.com/kkos/oniguruma/blob/master/doc/RE

 # Rules to be implemented. Please add to repository and link to a 'meta' to include.
-# The following rules correspond to the ten special latex characters. They are applied at the outmost level, but can be called inside patterns by using `$base`
+# The following rules correspond to the ten special latex characters. They are applied at the outmost level, but can be called inside patterns by using `$self`
 patterns: [
   { include: '#metaControl' } # must go first to catch escaped sequences
   { include: '#metaPercent' }
@@ -72,10 +72,10 @@
     patterns: [
       comment: 'Optimisation good to implement here. Rework metaComment to be a more general check for escaped characters and comments'
       {
-        include: '#escapedCharacter' # for an escaped %. Possible deprecated by $base match
+        include: '#escapedCharacter' # for an escaped %. Possible deprecated by $self match
       }
       { include: '#lineComment' }
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   metaCloseBrace: {
@@ -169,10 +169,12 @@
     match: '''\\\\([\\`\\'\\^\\"\\~\\=\\.]\\s*(\\w|\\{\\w?\\})|[Hcklbdruv]\\{\\w?\\})'''
   }
   lineComment: {
-    comment: 'Line comment. This will find an available % sign and match it to just before another % (where it looks for a magic comment) or the end.'
     name: 'comment.line.percentage.latex'
     begin: '%'
-    end: '(?=%)|$'
+    end: '$'
+    patterns: [
+      { include: '#magicComment' }
+    ]
   }
   escapedCurlyBracket: {
     comment: 'Escaped }. Possible deprecated by escapedCharacter.'
@@ -360,7 +362,7 @@
         name: 'invalid.illegal.latex'
         match: '(\\\\(\\[|\\(|\\)))'
       }
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   displayMathMode: {
@@ -390,7 +392,7 @@
         name: 'invalid.illegal.latex'
         match: '(?<=[^\\\\])(\\$)(?=[^\\$])|(\\\\(\\[|\\(|\\)))'
       }
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   envGeneric: {
@@ -417,7 +419,7 @@
       3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   envDocument: {
@@ -443,7 +445,7 @@
       3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   envFigure: {
@@ -476,7 +478,7 @@
       3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   metaEnvMath: {
@@ -509,7 +511,7 @@
       3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   listEnvironment: {
@@ -536,7 +538,7 @@
       3:
         name: 'punctuation.definition.argument.end.latex'
     patterns: [
-      { include: '$base' }
+      { include: '$self' }
     ]
   }
   makeAtSomething: {
@@ -552,7 +554,7 @@
         name: 'support.function.makeat.latex'
     patterns: [
       { include: '#controlWordWithAt' }
-      { include: '$base'}
+      { include: '$self'}
     ]
   }
   inputFile: {
@@ -687,3 +689,5 @@
     name: 'constant.character.latex'
     match: '\\^\\^(\\d+|.|$)'
   }
+
+# Unfortunate necessity (mainly making a unique grammar for each coding language)


Future additions (taken from language-latex)

--- grammars/latex.cson
+++ grammars/latex.cson
@@ -123,6 +123,16 @@
       { include: '#makeAtSomething' }
       { include: '#verbatim' }
       { include: '#item' }
+      # { include: '#url' }
+      # { include: '#text' } # To break out of mathmode; need to investigate how to enforce plain styling
+      # { include: '#commandDefinitions' }
+      # { include: '#styling' } # eg. \bf \it (deprecated)
+      # { include: '#latexStyling' } # eg. \emph
+      # { include: '#marginpar' }
+      # { include: '#footnote' }
+      # { include: '#metaReferences' } # maybe include \ref \label as well?
+      # { include: '#verbScantokens' } # not sure what this is for
+      # { include: '#constants' } # words that represent constants
     ]
   }
   metaEnvironment: {
@@ -131,6 +141,14 @@
       { include: '#envDocument' }
       { include: '#envFigure' }
       { include: '#metaEnvMath' }
+      # { include: '#metaEnvTable' }
+      # { include: '#metaEnvList' }
+      # { include: '#envGnuplot' }
+      # { include: '#metaEnvMinted' }
+      # { include: '#metaEnvListing' }
+      # { include: '#envLuaCode' }
+      # { include: '#envVerbatim' }
+      # { include: '#tikzPicture' }
       { include: '#envGeneric' }
     ]
   }
@@ -206,12 +224,12 @@
     ]
   }
   sectioning: {
-    comment: 'Covers all sectioning commands such as section, subsection, subparagraph, etc.'
+    comment: 'Covers all sectioning commands such as section, subsection, subparagraph, etc. The last four are KOMAscript specific.'
     name: 'support.function.section.latex'
-    begin: '\\\\((sub){0,2}section|(sub){0,1}paragraph|part|chapter)(\\*)?(?=[^a-zA-Z@])'
-    beginCaptures:
-      0:
-        name: 'entity.name.section.latex'
+    begin: '\\\\((sub){0,2}section|(sub){0,1}paragraph|part|chapter|addpart|addchap|addsec|minisec)(\\*)?(?=[^a-zA-Z@])'
+    beginCaptures:
+      0:
+        name: 'entity.name.section.$1.latex'
     end: '(?<=\\})|(^\\s*$)'
     endCaptures:
       1:
@@ -497,13 +515,17 @@
   metaEnvMath: {
     comment: 'Matches math environments'
     patterns: [
-      { include: '#alignMathEnvironment' }
-    ]
-  }
-  alignMathEnvironment: {
-    comment: 'Matches align environment'
-    contentName: 'meta.environment.math.align.latex'
-    begin: '\\\\begin(\\{)(align(\\*)?)(\\})'
+      { include: '#envMath' }
+      # some sort of `alignat` env?
+      # aligned|gathered
+      # eqnarray (deprecated)
+    ]
+  }
+  envMath: {
+    comment: 'Matches math environments'
+    name: 'meta.environment.math.$2.latex'
+    contentName: 'string.other.math.block.$2.latex'
+    begin: '\\\\begin(\\{)(align|equation|multline|split|gather)(\\*)?(\\})'
     beginCaptures:
       0:
         name: 'support.function.environment.begin.latex'


actually fix gen illegal bug

actually fix gen illegal bug

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -1248,7 +1248,7 @@
   'illegal_names':
     'patterns': [
       {
-        'match': '(_all|_b|byte|_coef|_cons|double|float|if|in|int|long|_n|_N|_pi|_pred|_rc|_skip|str[0-9]+|strL|using|with|\\b[0-9][A-Za-z0-9_]+\\b|[A-Za-z_][A-Za-z0-9_]{32,})'
+        'match': '\\b(_all|_b|byte|_coef|_cons|double|float|if|in|int|long|_n|_N|_pi|_pred|_rc|_skip|str[0-9]+|strL|using|with|[0-9][A-Za-z0-9_]+|[A-Za-z_][A-Za-z0-9_]{32,})\\b'
         'name': 'invalid.illegal.name.stata'
         'comment': 'highlights as illegal reserved names and those with more than 32 characters'
       }


adjust code

adjust code

--- client/app/lib/util/transformEmails.coffee
+++ client/app/lib/util/transformEmails.coffee
@@ -3,6 +3,6 @@
 module.exports = (str = '') ->

   output = for word in str.split(' ')
-      if validator.isEmail word then "[#{word}](mailto:#{word})" else word
-
-    return output.join(' ')+    if validator.isEmail word then "[#{word}](mailto:#{word})" else word
+
+  return output.join(' ')


mixpanel api key changed, terminal open event handled.

--- client/landing/app/MainApp/mixpanel.coffee
+++ client/landing/app/MainApp/mixpanel.coffee
@@ -8,10 +8,11 @@
     if eventName is "Login" or (eventName is "Groups" and eventData is "ChangeGroup")
       # identify user on mixpanel
       @registerUser()
-
+    else if eventName is "Connected to backend"
+      @track eventName, KD.nick()
+      @registerUser()
     else if eventName is "New User Signed Up"
       @track eventName, KD.whoami().profile
-
     else if eventName is "User Opened Ace"
       {title, privacy, visibility} = eventData

@@ -19,12 +20,11 @@
       @setOnce 'First Time Ace Opened', Date.now()
       @track eventName, options

-    else if eventName is "User Opened Terminal"
+    else if eventName is "userOpenedTerminal"
       {title, privacy, visibility} = eventData
-
       options = {title, privacy, visibility, $user}
       @setOnce 'First Time Terminal Opened', Date.now()
-      @track eventName, options
+      @track "User Opened Terminal", options

     else if eventName is "Apps" and eventData is "Install"


fixed comment

fixed comment

--- tests/api/basic/test.traversing.coffee
+++ tests/api/basic/test.traversing.coffee
@@ -72,6 +72,9 @@

       $('#lorem span').prev().get(0).name.should.equal "strong"

+    'get (top-level)' : ($) ->
+      should.not.exist $('html').prev()
+
   'siblings' :
     topic : loadCheerio


Add missing query name

Add missing query name

--- src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
+++ src/scripts/modules/search/results/breadcrumbs/breadcrumbs.coffee
@@ -13,8 +13,12 @@
     "type": "Type"
     "pubYear": "Publication Date"
     "subject": "Subject"
+    "title": "Title"
     "text": "Text"
   }
+
+  capitalize: (str) ->
+    return (str.split(' ').map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' '

   return class SearchResultsBreadcrumbsView extends BaseView
     template: template
@@ -27,7 +31,7 @@
           queries.push
             limit: key
             value: limit[key]
-            name: QUERY_NAMES[key]
+            name: QUERY_NAMES[key] or capitalize(key)

       return {queries: queries}


:lipstick:

:lipstick:

--- src/atom/binding-set.coffee
+++ src/atom/binding-set.coffee
@@ -16,10 +16,13 @@

   constructor: (@selector, mapOrFunction) ->
     @specificity = Specificity(@selector)
+    @commandForEvent = @buildEventHandler(mapOrFunction)
+
+  buildEventHandler: (mapOrFunction) ->
     if _.isFunction(mapOrFunction)
-      @commandForEvent = mapOrFunction
+      mapOrFunction
     else
-      @commandForEvent = (event) =>
+      (event) =>
         for pattern, command of mapOrFunction
           return command if @eventMatchesPattern(event, pattern)
         null


make mediator.subscribeOnce method readonly

--- src/chaplin/mediator.coffee
+++ src/chaplin/mediator.coffee
@@ -76,7 +76,7 @@

 # Make properties readonly.
 utils.readonly mediator,
-  'subscribe', 'unsubscribe', 'publish', 'setHandler', 'execute', 'removeHandlers'
+  'subscribe', 'subscribeOnce', 'unsubscribe', 'publish', 'setHandler', 'execute', 'removeHandlers'

 # Sealing the mediator
 # --------------------


remove half-baked breaking tests

remove half-baked breaking tests

--- karma/test-modals.coffee
+++ karma/test-modals.coffee
@@ -178,12 +178,8 @@

     expect($scope.fields.text).toEqual({val: "", maxLength: 10})

-    $scope.fetchFaqs()
-    $scope.setReponse($cope.replies[0].answer)
     $scope.form = {$valid: true}
     $scope.ok()
-
-    expect(modalInstance.close).toHaveBeenCalledWith("example answer 1")

     $scope.cancel()


"Username" instead of "Email"

"Username" instead of "Email"

--- www/login.coffee
+++ www/login.coffee
@@ -5,7 +5,7 @@
 		'@##@'+document.getElementById('password').value)),window.ajax('')"

 	login_form.innerHTML="
-		E-Mail:
<input id=email type=email></input>

+		Username:
<input id=email type=username></input>

 		Password:
<input id=password type=password></input>

 		<input type=submit value=Login></input>"


Add paths config option for stylus.

Add paths config option for stylus.

Closes #146.

--- src/compilers/stylus.coffee
+++ src/compilers/stylus.coffee
@@ -24,6 +24,10 @@
         .set("firebug", @options.stylus?.firebug)
         .include(@getAppPath "src")

+      if typeof @options.stylus?.paths is "object"
+        paths = (@getAppPath path for path in @options.stylus.paths)
+        compiler.set("paths", paths)
+
       compiler.use nib if nib
       compiler.render (error, css) =>
         return @logError error if error?


S56617 Disabled tests failing due to Firefox web driver issues. Opened DE18922 to address

--- test/spec/roadmapplanningboard/TimeframePlanningColumnSpec.coffee
+++ test/spec/roadmapplanningboard/TimeframePlanningColumnSpec.coffee
@@ -163,13 +163,13 @@
       @createColumn()
       expect(@column.getColumnHeader().query('roadmapthemeheader').length).toBe 1

-    it 'should have an editable theme header', ->
+    xit 'should have an editable theme header', ->
       @createColumn()
       theme = @column.getColumnHeader().query('roadmapthemeheader')[0]
       @click(theme.getEl()).then =>
         expect(!!theme.getEl().down('textarea')).toBe true

-    it 'should have an uneditable theme header', ->
+    xit 'should have an uneditable theme header', ->
       @createColumn
         editPermissions:
           theme: false
@@ -211,13 +211,13 @@
     afterEach ->
       @column.destroy()

-    it 'should have an editable timeframe date', ->
+    xit 'should have an editable timeframe date', ->
       @createColumn()
       dateRange = @column.dateRange.getEl()
       @click(dateRange).then =>
         expect(!!@column.timeframePopover).toBe true

-    it 'should have an uneditable timeframe date', ->
+    xit 'should have an uneditable timeframe date', ->
       @createColumn
         editPermissions:
           timeframeDates: false


Added KDTimeAgoView to CakefileIncludes.

--- core/JView.coffee
+++ core/JView.coffee
@@ -1,5 +1,5 @@
 class JView extends KDView
-
+
   viewAppended:->

     @setTemplate @pistachio()


Utils: decode html before applying markdown

--- core/utils.coffee
+++ core/utils.coffee
@@ -180,7 +180,7 @@
     # - links are broken due to textexpansions (images too i guess)
     return null unless text

-    marked text,
+    marked Encoder.htmlDecode(text),
       gfm       : true
       pedantic  : false
       sanitize  : true
@@ -422,7 +422,7 @@
     arr = (str?.split(delim).map (part) -> do part.trim) ? []
     arr = arr.filter Boolean  if filterEmpty
     return arr
-
+
   objectToArray: (options)->
     for key, option of options
       option.title ?= key


utils: add a method #partition

utils: add a method #partition

This, for creating a 2-d array of results which satisfy and fail the given predicate function

--- core/utils.coffee
+++ core/utils.coffee
@@ -380,6 +380,13 @@
       option.key    = key
       option

+  # The partition function takes a list and predicate fn and returns the pair of lists
+  # of elements which do and do not satisfy the predicate, respectively.
+  # (stolen from CoffeeScriptRedux)
+  partition: (list, fn) ->
+    result = [[], []]
+    result[+!fn item].push item for item in list
+    result
 ###
 //     Underscore.js 1.3.1
 //     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.


updated atom config

updated atom config

--- .atom/config.cson
+++ .atom/config.cson
@@ -31,7 +31,6 @@
   "exception-reporting":
     userId: "55e744ef-d3cd-d5d1-2156-7300b28614ce"
   "find-and-replace":
-    closeFindPanelAfterSearch: true
     openProjectFindResultsInRightPane: true
   "fuzzy-finder":
     preserveLastSearch: true


stuff

stuff

--- .atom/config.cson
+++ .atom/config.cson
@@ -12,11 +12,10 @@
     ]
     telemetryConsent: "limited"
     themes: [
-      "solarized-dark-ui"
-      "solarized-dark-syntax"
+      "solarized-light-ui"
+      "solarized-light-syntax"
     ]
   editor:
-    fontSize: 17
     preferredLineLength: 120
   "exception-reporting":
     userId: "9363c2a1-ce13-c340-7351-14b4c86035b6"


Highlight Jenkinsfiles

Highlight Jenkinsfiles

--- .atom/config.cson
+++ .atom/config.cson
@@ -24,7 +24,9 @@
   "file-types":
     "*.wxs$": "text.xml"
     "Dockerfile.*": "source.dockerfile"
+    "Jenkinsfile.*": "source.groovy"
     "Makefile*": "source.makefile"
+    inventory: "source.editorconfig"
   "go-plus":
     goPath: "/Users/stefan/go"
     showPanel: false


Hide vcs-ignored files in Atom

Hide vcs-ignored files in Atom

--- .atom/config.cson
+++ .atom/config.cson
@@ -42,6 +42,8 @@
   "package-sync":
     createOnChange: true
     forceOverwrite: true
+  "tree-view":
+    hideVcsIgnoredFiles: true
   welcome:
     showOnStartup: false
   whitespace: {}


Update Atom config

Update Atom config

--- .atom/config.cson
+++ .atom/config.cson
@@ -13,6 +13,7 @@
     showInvisibles: true
   "exception-reporting":
     userId: "76d6bd0a-16fd-42b8-0163-bce5c8c7e379"
+  linter: {}
   welcome:
     showOnStartup: false
   whitespace:


 Merge branch 'master.guestCleanerWorker' into 'master'

guestCleanerWorker: Added JName removal.

--- workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
+++ workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
@@ -7,7 +7,7 @@
   {Relationship} = jraphical
   constructor: (@bongo, @options = {}) ->

-  whitlistedModels = ["JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JLimit"]
+  whitlistedModels = ["JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JLimit", "JName"]

   collectDataAndRelationships:(relationships, callback)->
     toBeDeletedData = {}
@@ -41,7 +41,7 @@
       deleteEntry {ids: ids, modelName: modelName}

   clean:=>
-    {JAccount, JSession} = @bongo.models
+    {JAccount, JSession, JName} = @bongo.models

     usageLimitInMinutes = @options.usageLimitInMinutes or 60
     filterDate = new Date(Date.now()-(1000*60*usageLimitInMinutes))
@@ -103,6 +103,11 @@
               JSession.remove {guestId:guestId},(err)->
                 if err then console.error err
                 queue.next()
+            =>
+              #If we don't delete JNames, we eventually have millions of them.
+              JName.remove {name : account.profile.nickname},(err)->
+                if err then console.error err
+                queue.next()
             ->
               #Delete JAccount itself
               account.remove (err)->


enable cache

enable cache

--- vendor/assets/javascripts/auto_select2/ajax_select2.js.coffee
+++ vendor/assets/javascripts/auto_select2/ajax_select2.js.coffee
@@ -52,6 +52,7 @@
       else
         formatResultFunc = formatResult
       s2DefaultOptions = {
+        cache: true
         allowClear: true
         multiple: false
         formatSelection: formatSelectionFunc


group: membership policy: add signature annotations

--- workers/social/lib/social/models/group/membershippolicy.coffee
+++ workers/social/lib/social/models/group/membershippolicy.coffee
@@ -1,5 +1,5 @@
 {Module} = require 'jraphical'
-{Model} = require 'bongo'
+{Model, signature} = require 'bongo'

 module.exports = class JMembershipPolicy extends Module

@@ -10,8 +10,8 @@
   @set
     softDelete            : yes
     sharedMethods         :
-      static              : ['byGroupSlug']
-      instance            : ['explain']
+      static              :
+        byGroupSlug       : (signature String, Function)
     sharedEvents          :
       static              : []
       instance            : ['updateInstance']


support for multiple specs in exports

support for multiple specs in exports

--- src/crystal/generate.coffee
+++ src/crystal/generate.coffee
@@ -122,7 +122,17 @@
 					module_config.exports[export_name].schema = schema

 				# handle spec
-				if typeof(exported.spec) == 'string' && exported.spec.match(/\./)
+				if exported.spec instanceof Array
+					spec = {}
+					for exported_spec in exported.spec
+						export_path = "#{module_path}/.crystal/spec/#{exported_spec}"
+						if fs.existsSync export_path
+							exported_spec = yaml.safeLoad fs.readFileSync(export_path)
+						else
+							exported_spec = exported_spec
+						spec = extend true, true, spec, exported_spec
+					module_config.exports[export_name].spec = spec
+				else if typeof(exported.spec) == 'string' && exported.spec.match(/\./)
 					export_path = "#{module_path}/.crystal/spec/#{exported.spec}"
 					if fs.existsSync export_path
 						spec = yaml.safeLoad fs.readFileSync(export_path)
@@ -376,7 +386,7 @@
 				console.log validate.errors
 				console.log("ERROR: Specification failed validation.")
 				for error in validate.errors
-					console.log "- #{error.message} for specification (#{error.context.substr(2)})"
+					console.log "- #{error.message} for specification (#{error.context.substr(2)}) in generator (#{output.generator})"
 				throw new Error "ERROR: Invalid specification."

 		# get engine


fixed filenames for generators without an engine

--- src/code/crystal/generate.coffee
+++ src/code/crystal/generate.coffee
@@ -184,6 +184,8 @@
 			# pass filename thru engine
 			if engine
 				filename = engine { name: file.name }, generator_filename, helpers
+			else
+				filename = generator_filename

 			# append path to filename
 			if output.path


Remove author comments from bootstrap-tour.coffee

--- src/coffee/bootstrap-tour.coffee
+++ src/coffee/bootstrap-tour.coffee
@@ -1,10 +1,3 @@
-# @Author: azizmashour
-# @Date:   2017-06-15T11:59:09+01:00
-# @Last modified by:   azizmashour
-# @Last modified time: 2017-06-15T14:42:09+01:00
-
-
-
 ((window, factory) ->
   if typeof define is 'function' and define.amd
     define ['jquery'], (jQuery) -> (window.Tour = factory(jQuery))


Make `grunt clean` remove build/ directory

--- bokehjs/Gruntfile.coffee
+++ bokehjs/Gruntfile.coffee
@@ -48,7 +48,8 @@
           src: ['**/*']
           dest : 'build/js/vendor'
         ]
-    clean: ['release/js/vendor', 'release/js/views', 'release/build.txt']
+
+    clean: ['build']

     less:
       development:


Removed start() call from Histogram constructor

--- src/controllers/Histogram.coffee
+++ src/controllers/Histogram.coffee
@@ -8,8 +8,6 @@
     @variable = 'extinction_u'
     @width = 640
     @height = 480
-
-    @start()

   createGraph: =>
     if !@data.length


Scenario 18. added

Scenario 18. added

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -196,3 +196,12 @@
     # console.log expectedOutput
     expect(output).to.equal(expectedOutput)

+  it 'Scenario 18. Child with attribute in the child', ->
+
+    input = fs.readFileSync './test/scenarios/scenario-18-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-18-expected.html', 'utf8'
+    # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)


Scenario 28. added

Scenario 28. added

Scenario 28. Two tags with a attribute

--- test/scenario-based-testing.coffee
+++ test/scenario-based-testing.coffee
@@ -294,4 +294,14 @@
     # console.log output
     expectedOutput = fs.readFileSync './test/scenarios/scenario-27-expected.html', 'utf8'
     # console.log expectedOutput
+    expect(output).to.equal(expectedOutput)
+
+  it 'Scenario 28. Two tags with a attribute', ->
+
+    input = fs.readFileSync './test/scenarios/scenario-28-input.cohtml', 'utf8'
+    # console.log input
+    output = toHtml5 input
+    # console.log output
+    expectedOutput = fs.readFileSync './test/scenarios/scenario-28-expected.html', 'utf8'
+    # console.log expectedOutput
     expect(output).to.equal(expectedOutput)


fix dataset add tab view

fix dataset add tab view

--- app/assets/javascripts/admin/views/dataset/dataset_add_view.coffee
+++ app/assets/javascripts/admin/views/dataset/dataset_add_view.coffee
@@ -13,14 +13,14 @@

-            Upload Dataset
+            Upload Dataset

-            Add NDStore Dataset
+            Add NDStore Dataset

-

+


Moved to new log backend.

Moved to new log backend.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -235,7 +235,6 @@

   form_update: (e) ->
     ticket_article_type_id = $(e.target).find('option:selected').val()
-    @log 'eeee', e, ticket_article_type_id
     article_type = App.TicketArticleType.find( ticket_article_type_id )
     @form_update_execute(article_type)

@@ -285,8 +284,6 @@
     article_type = App.TicketArticleType.find( article.ticket_article_type_id )
     customer = App.User.find( article.created_by_id )

-    @log 'reply', e, article_type
-
     # update form
     @form_update_execute(article_type)

@@ -307,7 +304,6 @@

       # set to in body
       to = customer.accounts['twitter'].username || customer.accounts['twitter'].uid
-      @log 'c', customer
       @el.find('[name="body"]').val('@' + to)

     else if article_type.name is 'twitter direct-message'
@@ -348,7 +344,7 @@
   update: (e) =>
     e.preventDefault()
     params = @formParam(e.target)
-    @log 'update', params, @ticket
+    @log 'TicketZoom', 'notice', 'update', params, @ticket

     # update ticket
     ticket_update = {}
@@ -366,14 +362,14 @@
       return

     @ticket.load( ticket_update )
-    @log 'update ticket', ticket_update, @ticket
+    @log 'TicketZoom', 'notice', 'update ticket', ticket_update, @ticket

     # disable form
     @formDisable(e)

     errors = @ticket.validate()
-    @log 'errors', errors
     if errors
+      @log 'TicketZoom', 'error', 'update', errors
       @formEnable(e)

     @ticket.save(
@@ -395,16 +391,16 @@
           else
             sender = App.Collection.findByAttribute( 'TicketArticleSender', 'name', 'Agent' )
           params.ticket_article_sender_id = sender.id
-          @log 'updateAttributes', params, sender, sender.id
+          @log 'TicketZoom', 'notice', 'update article', params, sender
           article.load(params)
           errors = article.validate()
           if errors
-            @log 'error new article', errors
+            @log 'TicketZoom', 'error', 'update article', errors
           article.save(
             success: (r) =>
               @fetch(@ticket.id)
             error: (r) =>
-              @log 'error', r
+              @log 'TicketZoom', 'error', 'update article', r
           )
         else
           @fetch(@ticket.id)


Fixed show more feature if translation mode is enabled.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -203,7 +203,10 @@
   show_toogle: (e) ->
     e.preventDefault()
     $(e.target).hide()
-    $(e.target).next('div').show()
+    if $(e.target).next('div')[0]
+      $(e.target).next('div').show()
+    else
+      $(e.target).parent().next('div').show()

   history_view: (e) ->
     e.preventDefault()


initialize from default answer values after cl show

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -201,26 +201,51 @@
     # clear out text fields, selects and uncheck radio and checkboxes
     #TODO set these to default values once we implement default values - kdh
     # container.find("input[type!=hidden]").val("")
-    textFields = container.find(":text").val("")
-    textAreas = container.find("textarea").val("")
+    textFields = container.find(":text")
+    textFields.each ->
+      if $(this).attr("data-default-answer") && $(this).data("default-answer") != "null"
+        $(this).val($(this).data("default-answer"))
+      else
+        $(this).val("")
+
+    textAreas = container.find("textarea")
+    textAreas.each ->
+      if $(this).attr("data-default-answer") && $(this).data("default-answer") != "null"
+        $(this).val($(this).data("default-answer"))
+      else
+        $(this).val("")
+
     # un-select dropdown
     selects = container.find("select")
     selects.each ->
-      $(this).val("")
+      if $(this).attr("data-default-answer") && $(this).data("default-answer") != "null"
+        $(this).val($(this).data("default-answer"))
+      else
+        $(this).val("")
+
       $(this).trigger("chosen:updated") if $(this).hasClass('chosen')
+
+    # uncheck all checkboxes
+    checkboxes = container.find(":checkbox")
+    checkboxes.each ->
+      if $(this).attr("data-default-answer") && $(this).data("default-answer") == "true"
+        $(this).prop('checked', true)
+      else
+        $(this).prop('checked', false)
+
+    # un-select radio buttons
+    radiobuttons = container.find(":radio")
+    radiobuttons.each ->
+      if $(this).attr("data-default-answer") && $(this).data("default-answer") != "null" && $(this).data("label-value") == $(this).data("default-answer")
+        $(this).prop('checked', true)
+      else
+        $(this).prop('checked', false)
+
     multiselects = container.find("select[multiple]")
     multiselects.each ->
       id = $(this).attr('id')
       $('#' + id + ' option:selected').removeAttr("selected")
       $(this).trigger("chosen:updated") if $(this).hasClass('chosen-multiselect')
-    # uncheck all checkboxes
-    checkboxes = container.find(":checkbox")
-    checkboxes.each ->
-      $(this).prop('checked', false)
-    # un-select radio buttons
-    radiobuttons = container.find(":radio")
-    radiobuttons.each ->
-      $(this).prop('checked', false)

     # kdh commenting out triggering change event because it is having an exponential effect and causing performance problems on conditional logic #157849774
     # trigger onchange event which is needed for embedded conditional logic