escape comments html in timeline

escape comments html in timeline

--- app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee
+++ app/modules/user-timeline/user-timeline-item/user-timeline-item-type.service.coffee
@@ -61,7 +61,7 @@
             key: 'TIMELINE.NEW_COMMENT_US',
             translate_params: ['username', 'obj_name'],
             description: (timeline) ->
-                return timeline.data.comment
+                return $(timeline.data.comment_html).text()
         },
         { # NewIssueComment
             check: (timeline, event) ->
@@ -69,15 +69,15 @@
             key: 'TIMELINE.NEW_COMMENT_ISSUE',
             translate_params: ['username', 'obj_name'],
             description: (timeline) ->
-                return timeline.data.comment
+                return $(timeline.data.comment_html).text()
         },
-        { # NewTask
+        { # NewTaskComment
             check: (timeline, event) ->
                 return timeline.data.comment && event.obj == 'task'
             key: 'TIMELINE.NEW_COMMENT_TASK'
             translate_params: ['username', 'obj_name'],
             description: (timeline) ->
-                return timeline.data.comment
+                return $(timeline.data.comment_html).text()
         },
         { # UsToMilestone
             check: (timeline, event, field_name) ->


Fix indentation

Fix indentation

--- lib/_config/adminConfig.coffee
+++ lib/_config/adminConfig.coffee
@@ -6,18 +6,18 @@
 			icon: 'pencil'
 			extraFields: ['owner']
 			tableColumns: [
-        { label: 'Title', name: 'title' }
-        { label: 'User', name: 'author()', template: 'adminUserCell' }
-      ]
-		Comments: {
+				{ label: 'Title', name: 'title' }
+				{ label: 'User', name: 'author()', template: 'adminUserCell' }
+			]
+		Comments:
 			color: 'green'
 			icon: 'comments'
 			extraFields: ['doc', 'owner']
 			tableColumns: [
-        { label: 'Content', name: 'content' }
-        { label: 'Post', name: 'docTitle()', template: 'adminPostCell' }
-        { label: 'User', name: 'author()', template: 'adminUserCell' }
-      ]
+				{ label: 'Content', name: 'content' }
+				{ label: 'Post', name: 'docTitle()', template: 'adminPostCell' }
+				{ label: 'User', name: 'author()', template: 'adminUserCell' }
+			]
 			children: [
 				{
 					find: (comment) ->
@@ -28,8 +28,7 @@
 						Meteor.users.find comment.owner, limit: 1
 				}
 			]
-		}
 	dashboard:
 		homeUrl: '/dashboard'
 	autoForm:
-	        omitFields: ['createdAt', 'updatedAt']
+		omitFields: ['createdAt', 'updatedAt']


Update event-based-data-object.coffee

Update event-based-data-object.coffee

--- lib/event-based-data-object.coffee
+++ lib/event-based-data-object.coffee
@@ -12,3 +12,8 @@

     onUpdate: (callback) ->
         @emitter.on 'update', callback
+
+        ###
+  https://www.npmjs.com/package/event-kit
+
+###


remove unused reqs

remove unused reqs

--- lib/package-readme-view.coffee
+++ lib/package-readme-view.coffee
@@ -1,5 +1,3 @@
-path = require 'path'
-_ = require 'underscore-plus'
 {$, $$$, View} = require 'atom-space-pen-views'
 roaster = require 'roaster'
 fs = require 'fs'


Use RootView::open instead of RootView::openSync

--- lib/project/result-view.coffee
+++ lib/project/result-view.coffee
@@ -31,4 +31,4 @@
     null

   confirm: ->
-    rootView.openSync(@filePath)
+    rootView.open(@filePath)


Can dismiss the analysis tool window.

Can dismiss the analysis tool window.

--- lib/views/analysis_view.coffee
+++ lib/views/analysis_view.coffee
@@ -4,21 +4,34 @@
 class AnalysisView extends View
   @content: ->
     @div class: 'tool-panel panel-bottom padded', =>
-      'No Errors Detected'
+      @div class: 'pull-right', =>
+        @a href: '#', class: 'icon icon-x', rel: 'dismiss'
+      @div class: 'lenny', =>
+        @text "( ͡° ͜ʖ ͡°) doesn't see any problems. Relax, man."

   initialize: =>
     @subscribe atom.workspaceView, 'dart-tools:problems:show', =>
       @attach()

+    @subscribe this.find("a[rel='dismiss']"), 'click', =>
+      @detach()
+      false
+    this
+
   attach: ->
     atom.workspaceView.appendToBottom(this)
+    @show()
+
+  detach: ->
+    @hide()

   addProblem: (text) ->
+    this.find('.lenny').hide()
+
     row = new ErrorRow()
     row.text(text)
     this.append(row)

-
 class ErrorRow extends View
   @content: ->
     @div class: 'text-warning'


Port duplex to use autocall

Port duplex to use autocall

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -81,7 +81,6 @@
         pure = /[a-zA-Z0-9/-_]+/.test @location()
         assert pure, "location is not pure enough"
         context.scripts.push "/socket.io/socket.io.js"
-        context.sources.push "context.setupDuplexChannel()"
         context.sources.push "var __slice = [].slice"
         context.duplex = tools.urlWithHost yes, @location()
         context.providers = new Array
@@ -96,7 +95,7 @@
     # client end of the Socket.IO channel and creates wrapper around
     # all the providers residing in the current service implementation.
     # Refer to other `Duplex` methods for understanding what goes on.
-    setupDuplexChannel: external ->
+    setupDuplexChannel: @autocall external ->
         try @socket = io.connect @duplex catch error
             message = "blew up Socket.IO: #{error.message}"
             error.message = message.toString(); throw error


Add finalize filter and comment filter support.

--- content_manifest.coffee
+++ content_manifest.coffee
@@ -3,20 +3,41 @@
 class ContentManifest

 	filters: [ ]
+	finalizeFilters: []

 	constructor: (file,@options) ->
 		@rawContent = fs.readFileSync(file,"utf8")
 		@list = @rawContent.split /\n/

+
+	###
+	A filter is registered with a pattern and a callback,
+	The callback takes a file path and should returns filtered content.
+	###
 	addFilter: (pattern,filter) -> @filters.push { pattern: pattern, filter: filter }
+
+	###
+	A finalize filter takes content in string and returns filtered content.
+	###
+	addFinalizeFilter: (filter) -> @finalizeFilters.push filter

 	compile: ->
 		content = ""
 		for item in @list
+			# strip comments
+			continue unless item
+			continue if item.match /^\s*#/
+			item.replace /#.*/,""
+			unless fs.existsSync(f)
+				console.warn "File #{ f } does not exist."
+				continue
+
 			for f in @filters
 				if item.match f.pattern
 					content += f.filter(item)
 					break
+		for f in @finalizeFilters
+			content = f(content)
 		return content

 module.exports = ContentManifest


Add receipt note

Add receipt note

--- client/components/PaymentSuccess.coffee
+++ client/components/PaymentSuccess.coffee
@@ -16,7 +16,7 @@
             Обычно обработка занимает несколько секунд, в особых случаях может продолжаться несколко часов.
             Если через два часа срок оплаченных занятий не будет продлен, свяжитесь со мной.

-
Чек об оплате (в соответствии с законом о самозанятых):

+
Чек об оплате (в соответствии с законом о самозанятых; чек может появиться не сразу, а через 20-30 секунд):

         </Grid>


fixed #147

fixed #147

--- games/tutorial.coffee
+++ games/tutorial.coffee
@@ -146,7 +146,7 @@
       ]
     }
     {
-      name: 'Click "Edit" to Modify. Add Sprites By Clicking Pond.'
+      name: 'Click "Edit" to Modify. Click Sprite to Modify.'
       comment: '
       Now students know enough to start designing their own levels.
       - Click "Edit" to enable design mode.


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_object_effect.coffee
+++ src/game_object_effect.coffee
@@ -1,4 +1,4 @@
-GameObject.Effect = (I={}) ->
+GameObject.Effect = (I={}, self) ->
   # Set some default properties
   Object.reverseMerge I,
     color: "blue"


TAP5-2339: Ajax exception frame complains about "Function expected" in IE8

Resolution involves not re-using the exception container; this seems like it can be a race condition in IE8

--- tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-frame.coffee
+++ tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/exception-frame.coffee
@@ -1,5 +1,3 @@
-# Copyright 2012-2013 The Apache Software Foundation
-#
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
@@ -16,54 +14,51 @@
 #
 # Manages a special element used to present an HTML exception report from an Ajax request (where a non-markup response
 # was expected, including a partial page render response).
-define ["./dom", "underscore"],
-  (dom, _) ->
-    container = null
-    iframe = null
-    iframeDocument = null
+define ["./dom"], (dom) ->

-    write = (content) ->
-      # Clear current content:
-      iframeDocument.open()
-      # Write new content:
-      iframeDocument.write content
-      iframeDocument.close()
+  write = (container, content) ->
+    iframe = (container.findFirst "iframe").element

-    clear = ->
-      write ""
-      container.hide()
-      return false
+    # See http://xkr.us/articles/dom/iframe-document/

-    create = ->
-      return if container
+    iframeDocument = iframe.contentWindow or iframe.contentDocument
+    if iframeDocument.document
+      iframeDocument = iframeDocument.document

-      container = dom.create
-        class: "exception-container"
-        """
-
-

-            <button class="pull-right btn btn-primary">
-
-              Close
-            </button>
-

-        """
+    # Clear current content:
+    iframeDocument.open()
+    # Write new content:
+    iframeDocument.write content
+    iframeDocument.close()

-      dom.body.append container.hide()
+  clear = ->
+    container = @closest '.exception-container'
+    container.remove()
+    return false

-      iframe = (container.findFirst "iframe").element
+  create = ->

-      # See http://xkr.us/articles/dom/iframe-document/
+    container = dom.create
+      class: "exception-container"
+      """
+
+

+          <button class="pull-right btn btn-primary">
+
+            Close
+          </button>
+

+      """

-      iframeDocument = iframe.contentWindow or iframe.contentDocument
-      if iframeDocument.document
-        iframeDocument = iframeDocument.document
+    dom.body.append container.hide()

-      container.on "click", "button", clear
+    container.on "click", "button", clear
+    container

-    # Export single function:
+  # Export single function:

-    (exceptionContent) ->
-      create()
-      write exceptionContent
-      container.show()+  (exceptionContent) ->
+    container = create()
+    write container, exceptionContent
+    container.show()
+    return


there is no message to update yet

there is no message to update yet

--- packages/rocketchat-livechat/app/client/lib/chatMessages.coffee
+++ packages/rocketchat-livechat/app/client/lib/chatMessages.coffee
@@ -95,12 +95,10 @@
 			if not Meteor.userId()
 				Meteor.call 'registerGuest', visitor.getToken(), (error, result) ->
 					if error?
-						ChatMessage.update msgObject._id, { $set: { error: true } }
 						return showError error.reason

 					Meteor.loginWithPassword result.user, result.pass, (error) ->
 						if error
-							ChatMessage.update msgObject._id, { $set: { error: true } }
 							return showError error.reason

 						sendMessage()


chat ui 70%

chat ui 70%

--- components/inputs/tokenizedinput.coffee
+++ components/inputs/tokenizedinput.coffee
@@ -42,7 +42,7 @@
     @decorateLayer()
     # @layer.unsetClass "hide-tokens"
     {input} = @
-    log _oldMatches
+    # log _oldMatches
     if matchRules
       for rule, ruleSet of matchRules
         val = val.slice(0, input.getCaretPosition())


Force an unset so that `set` properly changes the MP property

--- components/split_test/split_test.coffee
+++ components/split_test/split_test.coffee
@@ -13,6 +13,7 @@

   set: (outcome) ->
     Cookies.set @_key(), outcome
+    unsetProperty @_key() # Force unset
     setProperty _.tap({}, (hsh) => hsh[@_key()] = outcome)
     outcome


Updates atom config.

Updates atom config.

--- env/atom.config.cson
+++ env/atom.config.cson
@@ -41,6 +41,8 @@
 		scrollPastEnd: true
 		showIndentGuide: true
 		showInvisibles: true
+		softWrap: true
+		softWrapAtPreferredLineLength: true
 		tabLength: 4
 	"event-watch":
 		schedules:


Implement URL routing for filters

Implement URL routing for filters

--- infokala/static_src/infokala/main_view_model.coffee
+++ infokala/static_src/infokala/main_view_model.coffee
@@ -46,15 +46,18 @@
     ].concat config.messageTypes

     # Special objects used as special filters
-    @filterAll = {name: 'Kaikki', slug: null, fn: (m) => true}
-    @filterActive = {name: 'Aktiiviset', slug: null, fn: (m) => m.state().active}
+    @filterAll = {name: 'Kaikki', slug: '_all', fn: (m) => true}
+    @filterActive = {name: 'Aktiiviset', slug: '_active', fn: (m) => m.state().active}
     @messageStateSpecialFilters = ko.observable [@filterAll, @filterActive]
     @messageStateFilters = ko.observableArray []

-    # activeFilter stores the type slug and a state object, because the state object might be one of the special
-    # cases above and requires identity (not equality) matching
+    # activeFilter stores the type slug and state object we are matching against, possibly including some special cases
     @activeFilter = ko.observable type: null, state: @filterAll
     @activeFilter.subscribe @filterChanged
+
+    window.addEventListener('hashchange', () => @updateFilterFromHash window.location.hash)
+    if window.location.hash
+      @updateFilterFromHash window.location.hash

     @shouldShowNewMessageWarning = ko.pureComputed () =>
         filter = @activeFilter()
@@ -158,10 +161,15 @@
     @activeFilter _.extend @activeFilter(), state: messageState
     @updateHashFromFilter()
     @activeFilter _.extend @activeFilter(), type: messageType.slug
+    filter = @activeFilter()
+    filter.type = messageType.slug
+    @activeFilter {type: messageType.slug, state: @filterAll}
     @updateFilterStates messageType
+    @updateHashFromFilter()

   setFilterState: (messageState) =>
     @activeFilter _.extend @activeFilter(), state: messageState
+    @updateHashFromFilter()

   shouldShowFilters: () =>
     # Show filters if we're showing all items or messages of a type with more than one state


Fix pass key handling.

Fix pass key handling.

Assume `x` is a passkey.

`zH` and `zL` are mapped as usual.

Then in the sequence `zx`, `x` should still be a pass key.

This fixes that behaviour.

Fixes #3271.

--- content_scripts/mode_key_handler.coffee
+++ content_scripts/mode_key_handler.coffee
@@ -73,7 +73,11 @@
   # Keystrokes are *never* considered pass keys if the user has begun entering a command.  So, for example, if
   # 't' is a passKey, then the "t"-s of 'gt' and '99t' are neverthless handled as regular keys.
   isPassKey: (keyChar) ->
-    @isInResetState() and keyChar in (@passKeys ? "")
+    # Find all *continuation* mappings for keyChar in the current key state (i.e. not the full key mapping).
+    mappings = (mapping for mapping in @keyState when keyChar of mapping and mapping != @keyMapping)
+    # If there are no continuation mappings, and there's no count prefix, and keyChar is a pass key, then
+    # it's a pass key.
+    mappings.length == 0 and @countPrefix == 0 and keyChar in (@passKeys ? "")

   isInResetState: ->
     @countPrefix == 0 and @keyState.length == 1


Use Type.string, instead of Type.of().name

--- lib_src/file_system/FileSystem.coffee
+++ lib_src/file_system/FileSystem.coffee
@@ -108,7 +108,7 @@
     content

   entryType: (path) ->
-    switch Type.of(@readFile(path)).name
+    switch Type.string(@readFile(path))
       when 'Object'
         'folder'
       when 'Buffer'


Cleanup the description support

Cleanup the description support

--- src/jira.coffee
+++ src/jira.coffee
@@ -42,13 +42,14 @@
               user = JSON.parse body
               reporter = user[0] if user and user.length is 1
             finally
-              desc = msg.match(/"(.*?)"/)[1] if /"(.*?)"/.test(msg)
-              msg = msg.replace(/"(.*?)"/,"") if desc != undefined
+              message = msg.match[1]
+              desc = message.match(/"(.*?)"/)[1] if /"(.*?)"/.test(message)
+              message = message.replace(/"(.*?)"/,"") if desc != undefined
               issue =
                 fields:
                   project:
                     key: project
-                  summary: msg.match[1]
+                  summary: message
                   labels: ["triage"]
                   description: (if desc != undefined then (desc + "\n") else "") +
                                """


Twitter links for Zoo people

Twitter links for Zoo people

--- content/team.coffee
+++ content/team.coffee
@@ -62,6 +62,7 @@
       name: 'Chris Lintott'
       location: 'Oxford, UK'
       description: 'Co-investigator'
+      url: 'http://twitter.com/chrislintott'
     },{
       image: 'team/geoff.jpg'
       name: 'Geoffrey Belknap'
@@ -83,9 +84,11 @@
       name: 'Victoria Van Hyning'
       location: 'Oxford, UK'
       description: 'Humanities project lead'
+      url: 'http://twitter.com/VanHyningV'
     },{
       image: 'team/adam.jpg'
       name: 'Adam McMaster'
       location: 'Oxford, UK'
       description: 'Web infrastructure'
+      url: 'http://twitter.com/astopy'
     }]


Error on using --action argument.

Error on using --action argument.

--- src/knit.coffee
+++ src/knit.coffee
@@ -42,6 +42,9 @@
   action = 'help'
 if params?.version # Version takes precedence
   action = 'version'
+if params?.action # Action parameter not allowed
+  console.log "ERROR: --action is a reserved parameter. Please use an alternative."
+  process.exit 0

 # Display errors or perform action
 if errors.length > 0


whoops

whoops

--- src/main.coffee
+++ src/main.coffee
@@ -108,7 +108,7 @@
         "min-height": 420
         icon: path.join __dirname, 'icons', 'icon.png'
         show: true
-        titleBarStyle: 'hidden-inset' if platform is 'darwin'
+        titleBarStyle: 'hidden-inset' if process.platform is 'darwin'
     }


don't show assembled code as decimal

don't show assembled code as decimal

--- src/main.coffee
+++ src/main.coffee
@@ -573,7 +573,7 @@
         @asm.setValue($.map(@parser.getFormattedIns("").replace(/\s+$/, "").split("\n"), (x, i) =>
                 switch @unitMode
                     when x == undefined or x == null or x.length == 0 then return ""
-                    when "decimal" then return parseInt(x, 2)
+                    when "decimal" then return x
                     when "hexadecimal" then return toHex2(parseInt(x, 2))
                     else return x
         ).join("\n").replace(/\s+$/, ""))


fix undefined collector bug

fix undefined collector bug


Add price context menu entry

Add price context menu entry

Context menu entry appears on all pages. Opens TCGPlayer page for
selected text.

--- src/menu.coffee
+++ src/menu.coffee
@@ -1,14 +1,27 @@
-onContextMenu = (info, tab) -> chrome.tabs.sendMessage tab.id, { name : info.selectionText }
+pricesUrl = (name) -> "http://store.tcgplayer.com/magic/product/show?ProductName=#{name}"
+
+onContextMenu = (info, tab) ->
+    switch info.menuItemId
+        when "scry-info" then chrome.tabs.sendMessage tab.id, { name : info.selectionText }
+        when "scry-prices" then chrome.tabs.create {
+            index : tab.index + 1
+            url : pricesUrl(info.selectionText)
+        }

 registerContextMenu = ->
     manifest = chrome.runtime.getManifest()
     matches = (script.matches for script in manifest.content_scripts)
     patterns = [].concat.apply([], matches)
     chrome.contextMenus.create {
-        id : "scry"
+        id : "scry-info"
         title : "Scry '%s'"
         contexts : [ "selection" ]
         documentUrlPatterns : patterns
+    }
+    chrome.contextMenus.create {
+        id : "scry-prices"
+        title : "Price '%s'"
+        contexts : [ "selection" ]
     }
     chrome.contextMenus.onClicked.addListener onContextMenu


Hide private Mock methods.

Hide private Mock methods.

Converted the private Mock methods (those that start with _) to
internal functions not tied to the Mock object.  This keeps Mock
uncluttered but does open the functions up for use by any other
code within the mock.coffee source file, which is perhaps not so
bad if we stick to one object per file or have a particularly
simple application/library, as this one is.

--- src/mock.coffee
+++ src/mock.coffee
@@ -84,10 +84,10 @@
   # my_mock.my_method()
   #
   expects: (method_name) ->
-    @_check_expects_usage(method_name)
+    _check_expects_usage(method_name)
     @signatures.unshift( new MethodSignature(method_name) )     # .unshift pushes to front of array
-    @[ method_name ] ?= @_build_mocked_method(method_name)
-    @_set_state("expects")
+    @[ method_name ] ?= _build_mocked_method(@, method_name)
+    _set_state(@, "expects")
     @

   #
@@ -95,10 +95,10 @@
   # my_mock.my_method(1,2,3)
   #
   args: (args...) ->
-    @_check_args_usage(args...)
-    @_check_if_duplicate_signature(@_current_method_name(), args...)
-    @_current_signature().args = args
-    @_set_state("args")
+    _check_args_usage(@, args...)
+    _check_if_duplicate_signature(@, _current_method_name(@), args...)
+    _current_signature(@).args = args
+    _set_state(@, "args")
     @

   #
@@ -106,9 +106,9 @@
   # console.log my_mock.my_method()   # prints 123
   #
   returns: (value) ->
-    @_check_returns_usage(value)
-    @_current_signature().returns = value
-    @_set_state("returns")
+    _check_returns_usage(@, value)
+    _current_signature(@).returns = value
+    _set_state(@, "returns")
     @

   #
@@ -119,9 +119,9 @@
   #   console.log error   # prints "an error"
   #
   throws: (error) ->
-    @_check_throws_usage(error)
-    @_current_signature().throws = error
-    @_set_state("throws")
+    _check_throws_usage(@, error)
+    _current_signature(@).throws = error
+    _set_state(@, "throws")
     @

   #
@@ -130,100 +130,102 @@
   # my_mock.check()       # throws an error because your_method() was not called
   #
   check: ->
-    @_set_state("check")
-    @_check_for_errors()
-    @
-
-  # private
-
-  _current_signature: ->
-    @signatures[0]
-
-  _current_method_name: ->
-    @_current_signature()?.method_name
-
-  _find_signature: (method_name, args...) ->
-    for signature in @signatures when signature.matches(method_name, args...)
-      return signature
-    undefined
-
-  _build_mocked_method: (method_name) ->
-    (args...) ->
-      signature = @_find_signature(method_name, args...)
-      @_throw_unknown_expectation("#{method_name}(#{args})") unless signature?
-      signature.called = true
-      throw signature.throws if signature.throws?
-      signature.returns
-
-  _build_errors: ->
-    errors = ""
-    for signature in @signatures when signature.called == false
-      errors += "'#{signature.method_name}(#{signature.args})' was never called\n"
-    errors
-
-  _set_state: (state) ->
-    @state = state
-
-  _is_state_in: (states...) ->
-    @state in states
-
-  _is_reserved_word: (word) ->
-    word in [ "expects", "args", "returns", "check" ]
-
-  _check_expects_usage: (method_name) ->
-    @_throw_expects_usage() unless method_name?
-    @_throw_reserved_word(method_name) if @_is_reserved_word(method_name)
-
-  _check_args_usage: (args...) ->
-    @_throw_args_usage() if args.length == 0
-    @_throw_args_must_be_after_expects() unless @_is_state_in("expects")
-
-  _check_returns_usage: (value) ->
-    @_throw_returns_usage() unless value?
-    @_throw_returns_must_be_after_expects_or_args() unless @_is_state_in("expects", "args")
+    _set_state(@, "check")
+    _check_for_errors(@)
+    @
+
+
+
+# Private Mock 'methods'; making them functions keeps the Mock object uncluttered.
+
+_set_state = (mock, state) ->
+  mock.state = state
+
+_is_state_in = (mock, states...) ->
+  mock.state in states
+
+_is_reserved_word = (word) ->
+  word in [ "expects", "args", "returns", "check" ]
+
+_check_expects_usage = (method_name) ->
+  _throw_expects_usage() unless method_name?
+  _throw_reserved_word(method_name) if _is_reserved_word(method_name)
+
+_check_args_usage = (mock, args...) ->
+  _throw_args_usage() if args.length == 0
+  _throw_args_must_be_after_expects() unless _is_state_in(mock, "expects")
+
+_check_returns_usage = (mock, value) ->
+  _throw_returns_usage() unless value?
+  _throw_returns_must_be_after_expects_or_args() unless _is_state_in(mock, "expects", "args")
+
+_check_throws_usage = (mock, error) ->
+  _throw_throws_usage(error) unless error?
+  _throw_throws_must_be_after_expects_or_args() unless _is_state_in(mock, "expects", "args")
+
+_check_if_duplicate_signature = (mock, method_name, args...) ->
+  _throw_duplicate_expectation("#{method_name}(#{args})") if _find_signature(mock, method_name, args...)

-  _check_throws_usage: (error) ->
-    @_throw_throws_usage(error) unless error?
-    @_throw_throws_must_be_after_expects_or_args() unless @_is_state_in("expects", "args")
-
-  _check_if_duplicate_signature: (method_name, args...) ->
-    @_throw_duplicate_expectation("#{method_name}(#{args})") if @_find_signature(method_name, args...)
-
-  _check_for_errors: ->
-    errors = @_build_errors()
-    throw errors unless errors == ""
-
-  _throw_expects_usage: ->
-    throw "you need to supply a method name to .expects(), e.g. my_mock.expects('my_method')"
-
-  _throw_reserved_word: (reserved) ->
-    throw "you cannot do my_mock.expects('#{reserved}'); '#{reserved}' is a reserved method name"
-
-  _throw_args_usage: ->
-    throw "you need to supply at least one argument to .args(), e.g. my_mock.expects('my_method').args(42)"
-
-  _throw_args_must_be_after_expects: ->
-    throw ".args() must be called immediately after .expects(), e.g. my_mock.expects('my_method').args(42)"
-
-  _throw_duplicate_expectation: (signature) ->
-    throw "#{signature} is a duplicate expectation"
-
-  _throw_returns_usage: ->
-    throw "you need to supply an argument to .returns(), e.g. my_mock.expects('my_method').returns(123)"
-
-  _throw_returns_must_be_after_expects_or_args: ->
-    throw ".returns() must be called immediately after .expects() or .args()"
-
-  _throw_throws_usage: ->
-    throw "you need to supply an argument to .throws(), e.g. my_mock.expects('my_method').throws('an error')"
-
-  _throw_throws_must_be_after_expects_or_args: ->
-    throw ".throws() must be called immediately after .expects() or .args()"
-
-  _throw_unknown_expectation: (signature) ->
-    throw "#{signature} does not match any expectations"
-
-
+_check_for_errors = (mock) ->
+  errors = _build_errors(mock)
+  throw errors unless errors == ""
+
+_current_signature = (mock) ->
+  mock.signatures[0]
+
+_current_method_name = (mock) ->
+  _current_signature(mock)?.method_name
+
+_find_signature = (mock, method_name, args...) ->
+  for signature in mock.signatures when signature.matches(method_name, args...)
+    return signature
+  undefined
+
+_build_mocked_method = (mock, method_name) ->
+  (args...) ->
+    signature = _find_signature(mock, method_name, args...)
+    _throw_unknown_expectation("#{method_name}(#{args})") unless signature?
+    signature.called = true
+    throw signature.throws if signature.throws?
+    signature.returns
+
+_build_errors = (mock) ->
+  errors = ""
+  for signature in mock.signatures when signature.called == false
+    errors += "'#{signature.method_name}(#{signature.args})' was never called\n"
+  errors
+
+_throw_expects_usage = ->
+  throw "you need to supply a method name to .expects(), e.g. my_mock.expects('my_method')"
+
+_throw_reserved_word = (reserved) ->
+  throw "you cannot do my_mock.expects('#{reserved}'); '#{reserved}' is a reserved method name"
+
+_throw_args_usage = ->
+  throw "you need to supply at least one argument to .args(), e.g. my_mock.expects('my_method').args(42)"
+
+_throw_args_must_be_after_expects = ->
+  throw ".args() must be called immediately after .expects(), e.g. my_mock.expects('my_method').args(42)"
+
+_throw_duplicate_expectation = (signature) ->
+  throw "#{signature} is a duplicate expectation"
+
+_throw_returns_usage = ->
+  throw "you need to supply an argument to .returns(), e.g. my_mock.expects('my_method').returns(123)"
+
+_throw_returns_must_be_after_expects_or_args = ->
+  throw ".returns() must be called immediately after .expects() or .args()"
+
+_throw_throws_usage = ->
+  throw "you need to supply an argument to .throws(), e.g. my_mock.expects('my_method').throws('an error')"
+
+_throw_throws_must_be_after_expects_or_args = ->
+  throw ".throws() must be called immediately after .expects() or .args()"
+
+_throw_unknown_expectation = (signature) ->
+  throw "#{signature} does not match any expectations"
+
+

 #
 # mock() is a convenience function to ensure that _build_errors() is
@@ -239,7 +241,7 @@
 mock = (fn) ->
   mocks = ( new Mock() for i in [1..5] )
   fn.apply(undefined, mocks)
-  errors = ( mock._build_errors() for mock in mocks ).join("")
+  errors = ( _build_errors(mock) for mock in mocks ).join("")
   throw errors unless errors == ""


config/generateKonfig: add clearbit credentials into config file

--- config/generateKonfig.coffee
+++ config/generateKonfig.coffee
@@ -184,6 +184,7 @@
     segment                : credentials.segment
     dummyAdmins            : credentials.dummyAdmins
     druid                  : credentials.druid
+    clearbit               : credentials.clearbit

     algolia                : algoliaSecret
     gatekeeper             : gatekeeper
@@ -251,6 +252,7 @@
     segment                       : credentials.segment
     dummyAdmins                   : credentials.dummyAdmins
     druid                         : credentials.druid
+    clearbit                      : credentials.clearbit

     paymentwebhook                : paymentwebhook
     regions                       : regions


[Activity] removed animation on bulk item additions on activity feed

... it affects the performance badly and doesn't really put some beauty in it.

--- client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/landing/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -102,7 +102,10 @@
         if activity?.teaser
           activity.teaser.createdAtTimestamps = overviewItem.createdAt
           view = @addHiddenItem activity.teaser, index, animation
-          view.slideIn => @removeFromHiddenItems view
+          view.setClass 'no-anim'
+          view.unsetClass 'hidden-item'
+          KD.utils.defer -> view.unsetClass 'no-anim'
+          @removeFromHiddenItems view
           activityIds.push activity.teaser._id

     @checkIfLikedBefore activityIds


:lipstick:

:lipstick:

--- src/pane.coffee
+++ src/pane.coffee
@@ -153,8 +153,7 @@
   # Public: Get the active pane item in this pane.
   #
   # Returns a pane item.
-  getActiveItem: ->
-    @activeItem
+  getActiveItem: -> @activeItem

   setActiveItem: (activeItem) ->
     unless activeItem is @activeItem


style changes

style changes

--- app/index.coffee
+++ app/index.coffee
@@ -4,46 +4,48 @@
 vis = new Vis()

 do ->
-    font_loaded = new Promise (resolve) ->
-        require('./load')(
-            font: "fnt/palatino.fnt",
-            image: "fnt/palatino.png"
-        , (font, texture) -> resolve({ font: font, texture: texture }))
+  font_loaded = new Promise (resolve) ->
+    require('./load')(
+      font: "fnt/palatino.fnt",
+      image: "fnt/palatino.png"
+    , (font, texture) -> resolve({ font: font, texture: texture }))

-    font_loaded.then (obj) ->
-        vis.setTexture(obj.texture)
-        vis.setFont(obj.font)
-    .then ->
-        switch window.location.hash
-            when "", "#chain" then do chainMode
-            when "#colocation" then do colocationMode
-            when "#lines" then do linesMode
-
-        # test = vis.newTest()
-        # interval = -> test.foo()
-        # setInterval interval, 1000
+  font_loaded.then (obj) ->
+    vis.setTexture(obj.texture)
+    vis.setFont(obj.font)
+  .then ->
+    switch window.location.hash
+      when "" then do all
+      when "#chain" then do chainMode
+      when "#colocation" then do colocationMode
+      when "#lines" then do linesMode

 apiUrl = (call) ->
-    "#{window.location.origin}/api/#{call}"
+  "#{window.location.origin}/api/#{call}"

 getJson = (apiCall, message) ->
-    new Promise (resolve) ->
-        console.info message
-        url = apiUrl apiCall
-        d3.json url, resolve
+  new Promise (resolve) ->
+    console.info message
+    url = apiUrl apiCall
+    d3.json url, resolve
+
+all = ->
+  console.info "Starting all."
+  order = [ "chain", "lines", "colocation" ]
+  mode = order[0]

 linesMode = ->
-    console.info "Starting Lines Mode."
-    getJson "get-lines.json", "Requesting Lines..."
-        .then vis.addLines
+  console.info "Starting Lines Mode."
+  getJson "get-lines.json", "Requesting Lines..."
+    .then vis.addLines

 chainMode = ->
-    console.info "Starting Chain Mode."
-    getJson "get-chain.json", "Requesting poetry chain..."
-        .then (d) ->
-            vis.addChain d[0]
+  console.info "Starting Chain Mode."
+  getJson "get-chain.json", "Requesting poetry chain..."
+    .then (d) ->
+      vis.addChain d[0]

 colocationMode = ->
-    console.info "Starting Colocation Mode."
-    getJson "get-colocation.json", "Requesting colocation network..."
-        .then vis.addNetwork
+  console.info "Starting Colocation Mode."
+  getJson "get-colocation.json", "Requesting colocation network..."
+    .then vis.addNetwork


fix: previous bug not the fault of piping

--- app/index.coffee
+++ app/index.coffee
@@ -19,13 +19,5 @@
   req.pipe bitbucket
   bitbucket.pipe res

-  # End the connection
-  req.on 'end', ->
-    console.log "*** end: #{url}"
-    req.unpipe()
-  req.on 'close', ->
-    console.log "*** close: #{url}"
-    req.unpipe()
-
 app.listen PORT
 console.log "Listening on port #{PORT}"


Standardize on underscore notation for locale keys

--- app/index.coffee
+++ app/index.coffee
@@ -51,7 +51,7 @@
 languageManager = new LanguageManager
   translations:
     en: label: 'English', strings: enUs
-    "zh-tw": label: "繁體中文 Chinese (Traditional)", strings: './translations/zh-tw.json'
+    zh_tw: label: "繁體中文 Chinese (Traditional)", strings: './translations/zh-tw.json'

 languageManager.on 'change-language', (e, code, strings) ->
   translate.load strings


basic init script, captures tab urls and displays them in the console after feching them throguth the API

--- ext/app/background/init.coffee
+++ ext/app/background/init.coffee
@@ -3,9 +3,22 @@
 #
 #

+api = new PhisherApi()
+
+# called when an url is successfully verified agains the api
+verify_success = (response)->
+  console.log response
+
+verify_error = (response)->
+  if response.status == 404
+    console.log response.responseJSON
+
+# listen for tab changes
 tabListener = (tabId, changeInfo, tab)->
   if changeInfo.status == "complete"
     console.log tab.url
+    url = new URL(tab.url)
+    api.verify url: url.hostname, success: verify_success, error: verify_error

 # this file has changed
 chrome.tabs.onUpdated.addListener(tabListener)


null check fsPath on unlink file

null check fsPath on unlink file

--- services/filestore/app/coffee/LocalFileWriter.coffee
+++ services/filestore/app/coffee/LocalFileWriter.coffee
@@ -27,6 +27,8 @@
 		stream.pipe writeStream

 	deleteFile: (fsPath, callback)->
+		if !fsPath? or fsPath == ""
+			return callback()
 		logger.log fsPath:fsPath, "removing local temp file"
 		fs.unlink fsPath, callback


support separate processes for web and api

via an environment variable WEB_TYPE

--- services/web/app/coffee/infrastructure/Server.coffee
+++ services/web/app/coffee/infrastructure/Server.coffee
@@ -161,15 +161,27 @@
 logger.info ("creating HTTP server").yellow
 server = require('http').createServer(app)

-# process api routes first, if nothing matched fall though and use
-# web middlewear + routes
-app.use(apiRouter)
-app.use(ErrorController.handleApiError)
-app.use(webRouter)
-app.use(ErrorController.handleError)
+# can set WEB_TYPE=api-only or WEB_TYPE=web-only for separate web and
+# api processes
+switch process.env.WEB_TYPE
+	when "api-only"
+		logger.info("providing api router");
+		app.use(apiRouter)
+		app.use(ErrorController.handleApiError)
+	when "web-only"
+		logger.info("providing web router");
+		app.use(webRouter)
+		app.use(ErrorController.handleError)
+	else
+		logger.info("providing web and api router");
+		# process api routes first, if nothing matched fall though and use
+		# web middleware + routes
+		app.use(apiRouter)
+		app.use(ErrorController.handleApiError)
+		app.use(webRouter)
+		app.use(ErrorController.handleError)

 router = new Router(webRouter, apiRouter)
-

 module.exports =
 	app: app


Small bug fixed in CoffeeScript gene table code.

--- webapp/public/pbsfinder/javascripts/jobs/table_header.coffee
+++ webapp/public/pbsfinder/javascripts/jobs/table_header.coffee
@@ -31,7 +31,7 @@
   # Check pagination need.
   unless isFootableNeeded()
     p = false
-    $('div#pager-div page-select').css('display', 'none')
+    $('div#pager-div #page-select').css('display', 'none')

   # Footable invocation.
   initFootable()


remove for now

remove for now

--- lib/analytics.coffee
+++ lib/analytics.coffee
@@ -126,7 +126,4 @@
     hash = {}
     hash[key] = enabledDisabled
     module.exports.setProperty hash
-    enabledDisabled == 'enabled'
-
-module.exports.load = (cb) ->
-  if mixpanel.__loaded then cb() else mixpanel.set_config loaded: cb+    enabledDisabled == 'enabled'


Ensure touch fires before the hh_client checks for errors

--- lib/atom-hack.coffee
+++ lib/atom-hack.coffee
@@ -48,8 +48,8 @@
           return Resolve([]) unless FilePath or AtomHack.Hack.config.status # Files that have not be saved
           if AtomHack.Hack.config.type is 'local' or not AtomHack.Hack.config.autoPush
             FileName = FilePath.split('/').pop();
-            AtomHack.Hack.exec('touch ' + FileName, Path.dirname(FilePath));
-            LePromise = AtomHack.Hack.exec('hh_client --json', Path.dirname(FilePath))
+            LePromise = AtomHack.Hack.exec('touch ' + FileName, Path.dirname(FilePath)).then ->
+                AtomHack.Hack.exec('hh_client --json', Path.dirname(FilePath))
           else
             LePromise = AtomHack.Hack.transfer(FilePath).then ->
               AtomHack.Hack.exec('hh_client --json', Path.dirname(FilePath))


change for page extend

change for page extend

--- lib/bin_utils.coffee
+++ lib/bin_utils.coffee
@@ -223,7 +223,7 @@

       # pages/index.coffee
       fs.writeFileSync path.join(pkg_path, 'presenters', 'index.coffee'), """
-        class exports.Page extends lt3.Page
+        class exports.Page
           events:
             'click .greeting': 'onClickGreeting'


BundleUp now support asset function as well as patch string

--- lib/bundle_up.coffee
+++ lib/bundle_up.coffee
@@ -5,7 +5,7 @@
 compilers = require './default_compilers'

 class BundleUp
-  constructor: (app, assetPath, options = {bundle:false}) ->
+  constructor: (app, asset, options = {bundle:false}) ->
     unless options.compilers?
       options.compilers = compilers
     else
@@ -21,7 +21,12 @@
     @js = new Js(options)
     @css = new Css(options)

-    require(assetPath)(new AssetsManager(@css, @js))
+    if typeof asset == "string"
+      require(asset)(new AssetsManager(@css, @js))
+    else if typeof asset == 'function'
+      asset(new AssetsManager(@css, @js))
+    else
+      throw new Error("Unsupported asset type")

     if options.bundle
       @js.toBundles()


Wrap command in bash --login

Wrap command in bash --login

Also log errors to console for better debugging support.
This should be improved to show the full error message to users.

References #1

--- lib/go-format.coffee
+++ lib/go-format.coffee
@@ -35,6 +35,11 @@
     atom.subscribe editor.getBuffer(), 'destroyed', =>
       atom.unsubscribe(editor.getBuffer())

+  command: (editor) ->
+    executable = atom.config.get('go-format.executable')
+    cmd = "#{executable} #{editor.getPath()}"
+    "bash --login -c '#{cmd.replace(/'/g, "\\'")}'"
+
   format: (editor) ->
     if editor and editor.getPath()
       scope = editor.getCursorScopes()[0]
@@ -44,11 +49,11 @@
           editorView.gutter.removeClassFromAllLines('go-format-error')
           editorView.gutter.find('.go-format-error-msg').remove()

-        cmd = atom.config.get('go-format.executable')
-        exec cmd + ' ' + editor.getPath(), (err, stdout, stderr) =>
+        exec @command(editor), (err, stdout, stderr) =>
           if not err or err.code is 0
             @view.html('').hide()
           else
+            console.log("[go-format save error]: " + stderr)
             message = 'Format error.'
             if stderr.match(/No such file or directory/)
               message = 'Cannot find gofmt executable.'


Remove old comments

Remove old comments

--- lib/list-edit.coffee
+++ lib/list-edit.coffee
@@ -44,24 +44,6 @@

   cutCmd: ->
     console.log 'Executing command list-edit-cut'
-    # select
-    # copy
-    # delete
-    # TODO: clean up all these comments and put clear comments at actual code
-    #  1 elt:  remove, including whitespace
-    # >1 elts: if not last, remove, put pre whitespace on pre whitespace of next element
-    #         if last, put post whitespace on previous element
-    # [pre1ELT1post1,pre2ELT2post2,pre3ELT3post3]
-    # [pre1ELT1post1,pre2ELT3post3]
-
-    # spec:
-    # if newElts.length > 0
-    #   if cut == 0  # then newElts[0] will exist, so not the last one and no need to fix post
-    #     newElts[0].pre = cutElts[0].pre
-    #   else
-    #    if cut == newElts.length # newElts.length > 1, so newElts[n-1] exists, not first and no need to fix pre
-    #      newElts[n-1].post = cutElts[m].post
-
     @withSelectedList (editor, textBuffer, bufferText, selectionIxRange, {openBracket: openBracket, separator: separator, elts: listElements}, [cutStart, cutEnd]) ->
       if listElements.length == 0
         atom.notifications.addWarning 'List cut in empty list.'


Forgotten comment, #579

Forgotten comment, #579

--- lib/migration.coffee
+++ lib/migration.coffee
@@ -358,6 +358,8 @@
 		['items_proto', 'addCol', 'armor_class uonline_armor_class']
 	]
 	[
+		# Converts old ways format 'Somewhere=123|Elsewhere=234'
+		# to JSON array '[{"target":123, "text":"Somewhere"}, {"target":234, "text":"Elsewhere"}]'
 		['locations', 'changeCol', 'ways',
 		"""json USING (
 			'[' ||


dispatch params changed

dispatch params changed

--- src/qqplugin.coffee
+++ src/qqplugin.coffee
@@ -6,8 +6,10 @@
         for i, plugin_name of @plugins
             @listeners.push require "../plugins/#{plugin_name}"

-    dispatch: (robot,message)->
+    dispatch: (content ,send, robot, message)->
         for i, plugin of @listeners
-            plugin( robot , message)
+            plugin(content ,send, robot, message)

-module.exports = QQPlugin             +
+
+module.exports = QQPlugin


Remove breakpoint line highlight when debugger is disabled or the user continues

--- lib/php-debug.coffee
+++ lib/php-debug.coffee
@@ -122,6 +122,10 @@
     @GlobalContext.onBreak (breakpoint) =>
       @doBreak(breakpoint)

+    @GlobalContext.onRunning () =>
+      if @currentBreakDecoration
+        @currentBreakDecoration.destroy()
+
     @GlobalContext.onWatchpointsChange () =>
       if @GlobalContext.getCurrentDebugContext()
         @GlobalContext.getCurrentDebugContext().syncCurrentContext()
@@ -182,6 +186,8 @@
     marker = editor.markBufferRange(range)

   toggleDebugging: ->
+    if @currentBreakDecoration
+      @currentBreakDecoration.destroy()
     pane = atom.workspace.paneForItem(@unifiedWindow)
     if !pane
       @showWindows()


Log args that cause exceptions.

Log args that cause exceptions.

--- lib/profiling.coffee
+++ lib/profiling.coffee
@@ -7,3 +7,15 @@
       count += 1
       console.log("Function called #{count} times")
       base.apply(this, arguments)
+
+this.logexceptionargs = (base) ->
+    exceptionArgs = {}
+    ->
+        try
+            base.apply(this, arguments)
+        catch error
+            key = util.getHash(JSON.stringify(arguments))
+            if not exceptionArgs.hasOwnProperty(key)
+                exceptionArgs[key] = arguments
+            console.log("Exception was thrown")
+            throw error


Add grammar when loaded

Add grammar when loaded

--- src/registry.coffee
+++ src/registry.coffee
@@ -36,11 +36,15 @@
     @grammarsByScopeName[scopeName]

   loadGrammarSync: (grammarPath) ->
-    new Grammar(this, fs.readObjectSync(grammarPath))
+    grammar = new Grammar(this, fs.readObjectSync(grammarPath))
+    @addGrammar(grammar)
+    grammar

   loadGrammar: (grammarPath, done) ->
     fs.readObject grammarPath, (error, object) =>
       if error?
         done?(error)
       else
-        done?(null, new Grammar(this, object))
+        grammar = new Grammar(this, object)
+        @addGrammar(grammar)
+        done?(null, grammar)


Fixed namespace pollution as events bubble up the heirarchy.

--- src/view.coffee
+++ src/view.coffee
@@ -84,6 +84,9 @@

         return false

+    ###
+    Removing children
+    ###
     removeChild: (child) ->
         @children = without(@children, child)
         child.parent = null
@@ -123,6 +126,13 @@
         if @namespace then args[0] = @namespace + '.' + args[0]

         # Trigger the root namespaced event
-        if @parent then @parent.trigger.apply(@parent, args)
+        if @parent then @parent._bubbleTrigger.apply(@parent, args)
+
+    ###
+    Used when bubbling to prevent namespace pollution as it goes up the chain.
+    ###
+    _bubbleTrigger: (args...) ->
+        Backbone.View::trigger.apply(@, args)
+        if @parent then @parent._bubbleTrigger.apply(@parent, args)

 module.exports = View


Add more keybindings.

Add more keybindings.

--- src/settings.coffee
+++ src/settings.coffee
@@ -8,18 +8,32 @@
 else
   module.exports =
     key2joy:
+      32: retro.DEVICE_ID_JOYPAD_B
+      91: retro.DEVICE_ID_JOYPAD_Y
+      18: retro.DEVICE_ID_JOYPAD_A
+      90: retro.DEVICE_ID_JOYPAD_X
       66: retro.DEVICE_ID_JOYPAD_B
       89: retro.DEVICE_ID_JOYPAD_Y
       65: retro.DEVICE_ID_JOYPAD_A
       88: retro.DEVICE_ID_JOYPAD_X
       76: retro.DEVICE_ID_JOYPAD_L
       82: retro.DEVICE_ID_JOYPAD_R
+      222: retro.DEVICE_ID_JOYPAD_SELECT
+      13: retro.DEVICE_ID_JOYPAD_START
+      16: retro.DEVICE_ID_JOYPAD_SELECT
+      9: retro.DEVICE_ID_JOYPAD_SELECT
+      73: retro.DEVICE_ID_JOYPAD_X
+      74: retro.DEVICE_ID_JOYPAD_Y
+      75: retro.DEVICE_ID_JOYPAD_B
+      76: retro.DEVICE_ID_JOYPAD_A
       38: retro.DEVICE_ID_JOYPAD_UP
       40: retro.DEVICE_ID_JOYPAD_DOWN
       37: retro.DEVICE_ID_JOYPAD_LEFT
       39: retro.DEVICE_ID_JOYPAD_RIGHT
-      222: retro.DEVICE_ID_JOYPAD_SELECT
-      13: retro.DEVICE_ID_JOYPAD_START
+      87: retro.DEVICE_ID_JOYPAD_UP
+      83: retro.DEVICE_ID_JOYPAD_DOWN
+      65: retro.DEVICE_ID_JOYPAD_LEFT
+      68: retro.DEVICE_ID_JOYPAD_RIGHT
     gp2joy:
       0: retro.DEVICE_ID_JOYPAD_B
       1: retro.DEVICE_ID_JOYPAD_A


Fix viewBox computation with overflow. Fix #22

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -361,7 +361,7 @@
       if symbol.viewBox?
         node.setAttribute 'viewBox', symbol.viewBox
     ## Lay out the symbols in the drawing via SVG <use>.
-    viewBox = [0, 0, 0, 0]
+    viewBox = [0, 0, 0, 0]  ## initially x-min, y-min, x-max, y-max
     levels = {}
     y = 0
     for row, i in symbols
@@ -377,20 +377,21 @@
         use.setAttributeNS SVGNS, 'width', symbol.viewBox?[2] ? symbol.width
         use.setAttributeNS SVGNS, 'height', symbol.viewBox?[3] ? symbol.height
         if symbol.overflowBox?
-          viewBox[0] = Math.min viewBox[0],
-            x + symbol.overflowBox[0] - symbol.viewBox[0]
-          viewBox[1] = Math.min viewBox[1],
-            y + symbol.overflowBox[1] - symbol.viewBox[1]
-          viewBox[2] = Math.max viewBox[2],
-            x + symbol.overflowBox[2]
-          viewBox[3] = Math.max viewBox[3],
-            y + symbol.overflowBox[3]
+          dx = symbol.overflowBox[0] - symbol.viewBox[0]
+          dy = symbol.overflowBox[1] - symbol.viewBox[1]
+          viewBox[0] = Math.min viewBox[0], x + dx
+          viewBox[1] = Math.min viewBox[1], y + dy
+          viewBox[2] = Math.max viewBox[2], x + dx + symbol.overflowBox[2]
+          viewBox[3] = Math.max viewBox[3], y + dy + symbol.overflowBox[3]
         x += symbol.width
         viewBox[2] = Math.max viewBox[2], x
         if symbol.height > rowHeight
           rowHeight = symbol.height
       y += rowHeight
       viewBox[3] = Math.max viewBox[3], y
+    ## Change from x-min, y-min, x-max, y-max to x-min, y-min, width, height
+    viewBox[2] = viewBox[2] - viewBox[0]
+    viewBox[3] = viewBox[3] - viewBox[1]
     ## Sort by level
     levelOrder = (level for level of levels).sort (x, y) -> x-y
     for level in levelOrder


Testing more webhook

Testing more webhook

--- src/worldcup.coffee
+++ src/worldcup.coffee
@@ -231,8 +231,9 @@
       msg.send "'Live' score is off"

   robot.router.post '/last_goal/:room', (req, res) ->
-    console.log req
-    console.log res
+    room = req.params.room
+    robot.messageRoom room, "#{req}, #{res}"
+    res.end()

   robot.router.post '/worldcup/goal/:room', (req, res) ->
      room = req.params.room


createteam/test: remove unneeded timeout

--- servers/lib/server/handlers/createteam.test.coffee
+++ servers/lib/server/handlers/createteam.test.coffee
@@ -609,14 +609,12 @@
             next()

       (next) ->
-        setTimeout ->
-          JGroup.one { slug: options.body.slug }, (err, group) ->
-            console.log({err, group})
-            expect(err).to.not.exist
-            expect(group).to.exist
-            expect(group.config.plan).to.be.equal 'foo'
-            next()
-        , 1000
+        JGroup.one { slug: options.body.slug }, (err, group) ->
+          console.log({err, group})
+          expect(err).to.not.exist
+          expect(group).to.exist
+          expect(group.config.plan).to.be.equal 'foo'
+          next()
     ]

     async.series queue, (err) ->


add test suite for teamAccessCode

add test suite for teamAccessCode

--- servers/lib/server/handlers/createteam.test.coffee
+++ servers/lib/server/handlers/createteam.test.coffee
@@ -590,18 +590,20 @@
       done()

-  it 'should send HTTP 400 when teamAccessCode non-existent', (done) ->
-
-    expectedBody            = 'Team Invitation is not found'
-    createTeamRequestParams = generateCreateTeamRequestParams
-      body             :
-        teamAccessCode : 'someNonExistentTeamAccessCode'
-
-    request.post createTeamRequestParams, (err, res, body) ->
-      expect(err)             .to.not.exist
-      expect(res.statusCode)  .to.be.equal 400
-      expect(body)            .to.be.equal expectedBody
-      done()
+  describe 'when teamAccessCode is provided', ->
+
+    it 'should send HTTP 400 if teamAccessCode non-existent', (done) ->
+
+      expectedBody            = 'Team Invitation is not found'
+      createTeamRequestParams = generateCreateTeamRequestParams
+        body             :
+          teamAccessCode : 'someNonExistentTeamAccessCode'
+
+      request.post createTeamRequestParams, (err, res, body) ->
+        expect(err)             .to.not.exist
+        expect(res.statusCode)  .to.be.equal 400
+        expect(body)            .to.be.equal expectedBody
+        done()

 runTests()


test(usb): update Buffer creation to use .from

--- test/usb.coffee
+++ test/usb.coffee
@@ -63,7 +63,7 @@
 			done()

 	describe 'control transfer', ->
-		b = Buffer([0x30...0x40])
+		b = Buffer.from([0x30...0x40])
 		it 'should OUT transfer when the IN bit is not set', (done) ->
 			device.controlTransfer 0x40, 0x81, 0, 0, b, (e) ->
 				assert.ok(e == undefined, e)


Clean code

Clean code

--- app/assets/javascripts/cocoon/iceberg.js.coffee
+++ app/assets/javascripts/cocoon/iceberg.js.coffee
@@ -3,7 +3,6 @@

   class Iceberg
     constructor: (@line, mode) ->
-      # console.log 'this', this
       @display = @line.find('.item-display')

       @_bindButtons(@newForm())
@@ -26,7 +25,6 @@
         @setFormSubmitable()

     _bindButtons: (form) ->
-      # console.log '_bindButtons:this', this
       that = this
       $(form).find('button[data-validate="item-form"]').each ->
         $(this).click (event) ->
@@ -40,7 +38,6 @@

     _bindEditEvent: ->
       @line.find('*[data-edit="item-form"]').click =>
-        # console.log @line, this
         @display.addClass('hidden')

         clone = @oldForm().clone()
@@ -61,7 +58,6 @@
           that.interpolate(form)

     validate: ->
-      # console.log 'validate:this', this
       @interpolate()

       @display.removeClass('hidden')
@@ -108,7 +104,6 @@
       @interpolateStoring()

     cancel: ->
-      # console.log 'cancel:this', this
       if @line.find('.nested-item-form').length is 1
         @line.remove()
       else
@@ -147,11 +142,9 @@
             @line.find(element.data('input-to-show')).addClass('hidden')

     oldForm: ->
-      # console.log 'oldForm:this', this
       @line.find('.nested-item-form:hidden')

     newForm: ->
-      # console.log 'newForm:this', this
       @line.find('.nested-item-form:visible')

     interpolateStoring: ->


setting mobile-layout-inner padding-top with js to prevent the header from obscuring products

--- app/assets/javascripts/mobile/_mobile.js.coffee
+++ app/assets/javascripts/mobile/_mobile.js.coffee
@@ -5,6 +5,7 @@
     toggle_menu = -> ($ '#menu').toggle()

     if has_touch_events
+        ($ '.mobile-layout-inner').css('padding-top', ($ '.mobile-header').height())
         ($ '#menu_button').tap((e) ->
             e.preventDefault()
             toggle_menu())


Toggle instead of slide on graphic show

Toggle instead of slide on graphic show

--- app/assets/javascripts/module/graphic.js.coffee
+++ app/assets/javascripts/module/graphic.js.coffee
@@ -1,6 +1,6 @@
 class Module.Graphic
   constructor: (@container, @metric_name, @module_id) ->
-    $('tr#'+@container).slideDown('slow')
+    $('tr#'+@container).slideToggle('slow')
     this.load()

   load: ->


taxon concept route not nested under taxon concepts

--- app/assets/javascripts/species/router.js.coffee
+++ app/assets/javascripts/species/router.js.coffee
@@ -3,11 +3,11 @@
     path: "/taxon_concepts"
     queryParams: ['taxonomy', 'taxon_concept_query', 'geo_entities_ids', 'geo_entity_scope', 'page']
     }, () ->
-    @resource 'taxonConcept', { path: "/:taxon_concept_id" }, () ->
-      @route 'legal'
-      @route 'names'
-      @route 'distribution'
-      @route 'references'
+  @resource 'taxonConcept', { path: "/taxon_concepts/:taxon_concept_id" }, () ->
+    @route 'legal'
+    @route 'names'
+    @route 'distribution'
+    @route 'references'
   @route 'about'

 Species.Router.reopen


Update $routeChangeError message (#198)

Update $routeChangeError message (#198)

The current UX to alert the user with a browser alert when Angular emits a `routeChangeError` event is a bit aggressive and confusing to the user. This commit changes this behaviour to log the error to the console, and redirecting the user to a 404 page.

🎩  @nancynaluz

--- app/assets/javascripts/sprangular/module.coffee
+++ app/assets/javascripts/sprangular/module.coffee
@@ -11,7 +11,7 @@
     paymentMethods = Env.config.payment_methods

     if paymentMethods.length == 0
-      alert 'Gateway is not configured in Spree...'
+      $log.info 'Gateway is not configured in Spree...'

 Sprangular.startupData = {}

@@ -113,5 +113,5 @@

   $rootScope.$on '$routeChangeError', (event, current, previous, rejection) ->
     Status.routeChanging = false
-    alert "Error changing route. See console for details."
     $log.info "Error changing route", rejection
+    $location.path "/404"


Show error message when no gateway configured

--- app/assets/javascripts/sprangular/module.coffee
+++ app/assets/javascripts/sprangular/module.coffee
@@ -1,10 +1,10 @@
 # Main Module
 window.Sprangular = angular.module "Sprangular", ['ui.bootstrap', 'ngRoute', 'ngResource', 'ngAnimate', 'underscore', 'ngSanitize', 'rawFilter', 'mgcrea.ngStrap', 'infinite-scroll']
-  .run ->
-    if PAYMENT_METHODS.stripe
-      Stripe.setPublishableKey PAYMENT_METHODS.stripe.publishable_key
-    else
-      alert 'Stripe Payment Method is not configured in Spree...'
+  .run (Env) ->
+    paymentMethods = Env.config.payment_methods
+
+    if !paymentMethods.gateway
+      alert 'Gateway is not configured in Spree...'

 Sprangular.extend = (instance, type) ->
   return unless instance


Removed checkout/details route

Removed checkout/details route

--- app/assets/javascripts/sprangular/routes.coffee
+++ app/assets/javascripts/sprangular/routes.coffee
@@ -62,10 +62,6 @@

     .when '/checkout',
       requires: {user: true, cart: true}
-      redirectTo: '/checkout/details'
-
-    .when '/checkout',
-      requires: {user: true, cart: true}
       controller: 'CheckoutCtrl'
       templateUrl: 'checkout/index.html'
       resolve:


Load Facebook SDK v2

Load Facebook SDK v2

--- app/assets/javascripts/sugar/facebook.js.coffee
+++ app/assets/javascripts/sugar/facebook.js.coffee
@@ -5,6 +5,8 @@

 Sugar.Facebook =
   appId: false
+  apiReady: false
+
   init: ->
     @appId = Sugar.Configuration.facebookAppId
     if $(".fb_button").length > 0
@@ -13,21 +15,34 @@
     $(Sugar).bind "postsloaded", (event, posts) ->
       Sugar.Facebook.parsePosts(posts)

+  withAPI: (callback) ->
+    if @apiReady
+      callback()
+    else
+      interval = setInterval =>
+        if @apiReady
+          clearInterval interval
+          callback()
+      , 50
+
   loadAsync: ->
-    window.fbAsyncInit = ->
+    window.fbAsyncInit = =>
       FB.init
-        appId: Sugar.Facebook.appId
-        status: true # Check login status
-        cookie: true # Enable cookies to allow the server to access the session
-        xfbml:  true # Parse XFBML
+        version: 'v2.0'
+        appId:   Sugar.Facebook.appId
+        status:  true # Check login status
+        cookie:  true # Enable cookies to allow the server to access the session
+        xfbml:   true # Parse XFBML
+      @apiReady = true

     $("body").append "<div id=\"fb-root\" />"
-    e = document.createElement("script")
-    e.src = document.location.protocol + "//connect.facebook.net/en_US/all.js"
-    e.async = true
-    document.getElementById("fb-root").appendChild e
+
+    fjs = document.getElementsByTagName("script")[0]
+    js = document.createElement("script")
+    js.id = "facebook-jssdk"
+    js.src = "//connect.facebook.net/en_US/sdk.js"
+    fjs.parentNode.insertBefore js, fjs

   parsePosts: (posts) ->
-    if @appId && FB
-      posts.each ->
-        FB.XFBML.parse(this)
+    @withAPI -> posts.each ->
+      FB.XFBML.parse(this)


Button Component: Auto bind any element attribute that starts with 'data-'

--- app/scripts/components/BsButtonComponent.coffee
+++ app/scripts/components/BsButtonComponent.coffee
@@ -13,7 +13,7 @@
             for key,val of @get('content')
                 @set key, val
         else
-            @attributeBindings.pushObject attr for attr of @ when attr.match(/^data-\w*-text$/)?
+            @attributeBindings.pushObject attr for attr of @ when attr.match(/^data-\w*$/)?

     blockClass: ( ->
         if @block then "#{@classTypePrefix}-block" else null


Split email invite list on commas and newlines

--- app/views/courses/InviteToClassroomModal.coffee
+++ app/views/courses/InviteToClassroomModal.coffee
@@ -16,7 +16,7 @@

   onClickSendInvitesButton: ->
     emails = @$('#invite-emails-textarea').val()
-    emails = emails.split('\n')
+    emails = emails.split(/[,\n]/)
     emails = _.filter((_.string.trim(email) for email in emails))
     if not emails.length
       return


Get rid of delays on button/highlight hover

--- js-library/app/js/jail_iframe/classes/highlight.coffee
+++ js-library/app/js/jail_iframe/classes/highlight.coffee
@@ -1,21 +1,12 @@
 highlight_time_on_load_and_creation = 2000
 highlight_time_on_in_view = 1500

-delay_before_mouseover_detected = 75
+class Highlighter
+  constructor: (@$elements, @className) ->
+    throw 'Higlighter requires class' unless @className

-if FactlinkJailRoot.can_haz.client_buttons
-  delay_between_highlight_and_show_button_open = 1000
-else
-  delay_between_highlight_and_show_button_open = 0
-
-delay_before_mouseout_detected = 300
-
-class Highlighter
-  constructor: (@$elements, @class) ->
-    throw 'Higlighter requires class' unless @class
-
-  highlight:   -> @$elements.addClass(@class)
-  dehighlight: -> @$elements.removeClass(@class)
+  highlight:   -> @$elements.addClass(@className)
+  dehighlight: -> @$elements.removeClass(@className)

 class HighlightInteraction
   constructor: (elements, @id, @options) ->
@@ -38,18 +29,6 @@
     $elements.on 'mouseenter', @_onHover
     $elements.on 'mouseleave', @_onUnhover

-    @button_attention = new FactlinkJailRoot.AttentionSpan
-      onAttentionLost:   => @show_button.stopHovering()
-      onAttentionGained: => @show_button.startHovering()
-      wait_for_attention:  delay_before_mouseover_detected + delay_between_highlight_and_show_button_open
-      wait_for_neglection: delay_before_mouseout_detected
-
-    @highlight_attention = new FactlinkJailRoot.AttentionSpan
-      onAttentionLost:   => @highlighter.dehighlight()
-      onAttentionGained: => @highlighter.highlight()
-      wait_for_attention:  delay_before_mouseover_detected
-      wait_for_neglection: delay_before_mouseout_detected
-
   _onClick: =>
     @show_button.startLoading() # must be called after show
     FactlinkJailRoot.on 'modalOpened', @_onModalOpened, @
@@ -60,12 +39,12 @@
     FactlinkJailRoot.off 'modalOpened', @_onModalOpened, @

   _onHover: =>
-    @button_attention.gainAttention()
-    @highlight_attention.gainAttention()
+    @show_button.startHovering()
+    @highlighter.highlight()

   _onUnhover: =>
-    @button_attention.loseAttention()
-    @highlight_attention.loseAttention()
+    @show_button.stopHovering()
+    @highlighter.dehighlight()

   destroy: ->
     @show_button.destroy()


head assets should not attempt to update during partial replace

--- lib/assets/javascripts/turbograft/turbolinks.coffee
+++ lib/assets/javascripts/turbograft/turbolinks.coffee
@@ -130,28 +130,32 @@
   @loadPage: (url, xhr, options = {}) ->
     triggerEvent 'page:receive'
     options.updatePushState ?= true
-    if upstreamDocument = processResponse(xhr, options.partialReplace)
+    if upstreamDocument = processResponse(xhr)
       reflectNewUrl url if options.updatePushState
-
-      new TurboHead(document, upstreamDocument).update(
-        onHeadUpdateSuccess = ->
-          nodes = changePage(
-            upstreamDocument.querySelector('title')?.textContent,
-            removeNoscriptTags(upstreamDocument.querySelector('body')),
-            CSRFToken.get(upstreamDocument).token,
-            'runScripts',
-            options
-          )
-          reflectRedirectedUrl(xhr) if options.updatePushState
-          options.onLoadFunction?()
-          triggerEvent 'page:load', nodes
-        ,
-        onHeadUpdateError = ->
-          Turbolinks.fullPageNavigate(url.absolute)
-      )
+      unless options.partialReplace
+        return new TurboHead(document, upstreamDocument).update(
+          onHeadUpdateSuccess = ->
+            updateBody(upstreamDocument, xhr, options)
+          ,
+          onHeadUpdateError = ->
+            Turbolinks.fullPageNavigate(url.absolute)
+        )
+      return updateBody(upstreamDocument, xhr, options)
     else
       triggerEvent 'page:error', xhr
       Turbolinks.fullPageNavigate(url.absolute) if url?
+
+  updateBody = (upstreamDocument, xhr, options) ->
+    nodes = changePage(
+      upstreamDocument.querySelector('title')?.textContent,
+      removeNoscriptTags(upstreamDocument.querySelector('body')),
+      CSRFToken.get(upstreamDocument).token,
+      'runScripts',
+      options
+    )
+    reflectRedirectedUrl(xhr) if options.updatePushState
+    options.onLoadFunction?()
+    triggerEvent 'page:load', nodes

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


scrollview: change attribute name

scrollview: change attribute name

--- lib/components/scrollview/customscrollviewinner.coffee
+++ lib/components/scrollview/customscrollviewinner.coffee
@@ -74,7 +74,7 @@

     {deltaX, deltaY, deltaFactor} = event

-    return yes  if @getElement().dataset.scrollable
+    return yes  if @getElement().dataset.innerItemWillScroll

     speed = deltaFactor or @getOptions().mouseWheelSpeed or 1
     x     = -deltaX


make sample data into array

make sample data into array

--- packages/rocketchat-integrations/server/api/api.coffee
+++ packages/rocketchat-integrations/server/api/api.coffee
@@ -182,19 +182,38 @@

 Api.addRoute 'sample/:integrationId/:userId/:token', authRequired: true,
 	get: ->
-		console.log 'Sample integration'
-
-		return {} =
-			statusCode: 200
-			body:
+		console.log 'Sample Integration'
+
+		return {} =
+			statusCode: 200
+			body: [
 				token: Random.id(24)
 				channel_id: Random.id()
 				channel_name: 'general'
 				timestamp: new Date
 				user_id: Random.id()
 				user_name: 'rocket.cat'
-				text: 'Sample text'
+				text: 'Sample text 1'
 				trigger_word: 'Sample'
+			],[
+				token: Random.id(24)
+				channel_id: Random.id()
+				channel_name: 'general'
+				timestamp: new Date
+				user_id: Random.id()
+				user_name: 'rocket.cat'
+				text: 'Sample text 2'
+				trigger_word: 'Sample'
+			],[
+				token: Random.id(24)
+				channel_id: Random.id()
+				channel_name: 'general'
+				timestamp: new Date
+				user_id: Random.id()
+				user_name: 'rocket.cat'
+				text: 'Sample text 3'
+				trigger_word: 'Sample'
+			]

 Api.addRoute 'info/:integrationId/:userId/:token', authRequired: true,


Add logs to OrchestraView

Add logs to OrchestraView

--- BackofficeBundle/Resources/public/coffee/OrchestraView.coffee
+++ BackofficeBundle/Resources/public/coffee/OrchestraView.coffee
@@ -7,27 +7,40 @@
     $.each templates, (index, templateName) ->
       currentView.compiledTemplates[templateName] = false
       return
+
+#    console.log('>>>> Starting templates loading >>>>')
     $.each templates, (index, templateName) ->
       currentView.loadTemplate(templateName)
       return
     return

   loadTemplate: (templateName) ->
+#    console.log('Loading template ' + templateName)
+
     templateLoader.loadRemoteTemplate templateName, getCurrentLocale(), @
     return

   onTemplateLoaded: (templateName, templateData) ->
     @compiledTemplates[templateName] = _.template(templateData)
+#    console.log(templateName + ' compiled')

     ready = true
     $.each @compiledTemplates, (templateName, templateData) ->
       ready = false if templateData is false
       return

-    @render() if ready
+#    @render() if ready
+    if ready
+#      console.log('<<<< Templates loading ended <<<<')
+      @render()
     return

   renderTemplate: (templateName, parameters) ->
+    console.log('=== Rendering ' + templateName + ' in ===')
+    console.log(@el)
+    console.log('With params')
+    console.log(parameters)
+
     @compiledTemplates[templateName](parameters)

 )


:fire: console.log

:fire: console.log

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -824,7 +824,6 @@
     try
       fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath)
       fs.moveSync(initialPath, newPath)
-      console.log "MOVED!", initialPath, newPath
       @emitter.emit 'entry-moved', {initialPath, newPath}

       if repo = repoForPath(newPath)


Bug Fix: Showing last battle detail after import packet

--- views/option-area.cjsx
+++ views/option-area.cjsx
@@ -60,9 +60,10 @@
     try
       packet = clipboard.readText(packet)
       packet = JSON.parse packet
-      @props.updateBattleDetail packet
+      @props.toggleAutoShow false
       @setState
         selectedPacket: packet
+      @props.updateBattleDetail packet
     finally
       window.showModal
         title: __ "Paste Data"


Curl error proxy aware

Curl error proxy aware

--- actions/curl.coffee
+++ actions/curl.coffee
@@ -86,15 +86,17 @@
   , (err, installed) ->
     next err, if installed then ctx.OK else ctx.PASS

-module.exports.push name: 'Curl # Proxy Check', callback: (ctx, next) ->
-  {proxy, check, check_match} = ctx.config.curl
-  return next() unless check
+module.exports.push name: 'Curl # Connection Check', callback: (ctx, next) ->
+  {check, check_match, config} = ctx.config.curl
+  return next null, ctx.INAPPLICABLE unless check
   ctx.execute
     cmd: "curl -s #{check}"
     stdout: null
   , (err, executed, stdout, stderr) ->
     return next err if err
-    return next new Error "Proxy not active" unless  check_match.test stdout
+    unless check_match.test stdout
+      msg = "#{if config.proxy then 'Proxy' else 'Connection'} not active"
+      return next new Error msg
     next null, ctx.PASS

 ###


Remove openjdk

Remove openjdk

--- actions/java.coffee
+++ actions/java.coffee
@@ -12,6 +12,32 @@
   throw new Error "Configuration property 'java.location' is required." unless ctx.config.java.location
   throw new Error "Configuration property 'java.version' is required." unless ctx.config.java.version
   # ctx.config.java.version ?= (/\w+-([\w\d]+)-/.exec path.basename ctx.config.java.location)[0]
+
+module.exports.push (ctx, next) ->
+  @name 'Java # Remove OpenJDK'
+  ctx.execute
+    cmd: 'yum list installed | grep openjdk'
+    code_skipped: 1
+  , (err, installed, stdout) ->
+    return next err, ctx.PASS if err or not installed
+    packages = for l in stdout.trim().split('\n') then /(.*?) *$/.exec(l)[1]
+    ctx.execute
+      cmd: "yum remove -y #{packages.join ' '}"
+    , (err) ->
+      next err, ctx.OK
+
+# module.exports.push (ctx, next) ->
+#   @name 'Java # Remove GCJ'
+#   ctx.execute
+#     cmd: 'yum list installed | grep gcj'
+#     code_skipped: 1
+#   , (err, installed, stdout) ->
+#     return next err, ctx.PASS if err or not installed
+#     packages = for l in stdout.trim().split('\n') then /(.*?) /.exec(l)[1]
+#     ctx.execute
+#       cmd: "yum remove -y #{packages.join ' '}"
+#     , (err) ->
+#       next err, ctx.OK

 ###
 Followed instruction from: http://www.if-not-true-then-false.com/2010/install-sun-oracle-java-jdk-jre-6-on-fedora-centos-red-hat-rhel/
@@ -76,7 +102,19 @@
     #   next err, if downloaded then ctx.OK else ctx.PASS
     # , 10000

+module.exports.push (ctx, next) ->
+  @name 'Java # Env'
+  ctx.write
+    destination: '/etc/profile.d/java.sh'
+    mode: 644
+    content: """
+    export JAVA_HOME=/usr/java/default
+    export PATH=$PATH:/usr/java/default/bin
+    """
+  , (err, written) ->
+    next err, if written then ctx.OK else ctx.PASS

+


test layout levels

test layout levels

--- src/layout/test_layout.coffee
+++ src/layout/test_layout.coffee
@@ -23,5 +23,9 @@

   test "layout sprites in world", (t) ->
     t.ok world = god(rx, config), 'world'
+    t.ok shapes = world.get('shapes'), 'shapes'
+    t.ok levels = world.get('levels'), 'levels'
+    t.ok world.is_array(levels), 'levels  rx_array'
+    t.ok level = levels.at(0), 'level'
     t.end()


feat: add types, Enum, Decimal.

feat: add types, Enum, Decimal.

refer following PR.
https://github.com/boiawang/sequelize-db-export-import/pull/9

--- lib/InitialMigrationExport.coffee
+++ lib/InitialMigrationExport.coffee
@@ -129,6 +129,12 @@

               if field.Type.match('unsigned')
                 typeOutStr += '.UNSIGNED'
+            else if type.value is 'DECIMAL'
+              length = field.Type.match(/\(\d+,\d+\)/)
+              typeOutStr += if length then length else ''
+            else if type.value is 'ENUM'
+              length = field.Type.match(/\(.*\)/)
+              typeOutStr += if length then length else ''

         if type is 'coffee'
           if (exportDefaultValue is true) && (field.Default != null)


Only show active macros, groups and users.

--- app/assets/javascripts/app/controllers/ticket_overview.coffee
+++ app/assets/javascripts/app/controllers/ticket_overview.coffee
@@ -470,8 +470,8 @@
       @delay(update, 2800, 'overview:fetch')

   renderOptions: =>
-    macros = App.Macro.all()
-    groups = App.Group.all()
+    macros = App.Macro.findAllByAttribute('active', true)
+    groups = App.Group.findAllByAttribute('active', true)
     users = []
     items = @el.find('[name="bulk"]:checked')

@@ -495,8 +495,10 @@
               delete possibleUsers[user_id.toString()]
         possibleUserGroups[ticket.group_id.toString()] = true
     for user_id, _exists of possibleUsers
-      user = App.User.find(user_id)
-      users.push user
+      if App.User.exists(user_id)
+        user = App.User.find(user_id)
+        if user.active is true
+          users.push user
     ###
     users = [
       App.User.find(2),


Clean up code a little, add annotations to a lot of variables and code.

--- web/site/search.coffee
+++ web/site/search.coffee
@@ -12,8 +12,21 @@
     $scope.selectedType = ""
     $scope.selectedVolume = ""
     $scope.selectedFilter = ""
+
+    # Array that holds the names of the types the user
+    # can filter by (volume, party)
     $scope.typeDisplay = []
+
+    # Array that holds the current available filters
+    # for the selected type.
     $scope.filterDisplay = []
+
+    # These hold the current number of returned values
+    # for each type. These are all set in parseResults.
+    $scope.volumeCount = 0
+    $scope.partyCount = 0
+    $scope.totalCount = 0
+
     $scope.selectSessionStr = "Volumes w/ Sessions"
     $scope.selectHighlightStr = "Volumes w/ Highlights"
     $scope.selectPeopleFilters = ["All", "Authorized Investigators", "Institutions"]
@@ -22,24 +35,38 @@
     $scope.partyDisplayStr = "People"
     $scope.partyLinkPrefix = "party/"
     $scope.volumeLinkPrefix = "volume/"
+
+    # Holds the current text of the search box
     $scope.searchBoxQuery = ""
+
+    # List of affiliations (currently unused)
     $scope.affiliations = []
-    # $scope.selectHighlightStr = "Volumes w/ Highlights"
+
+    # Number of results of each facet to return.
+    # Currently hardcoded to 10.
     $scope.limit = 10
     display.title = 'Search'

+    # Name of the currently selected filter.
     currentFilter = ""

   ###########################
   # Functions for transforming a document into strings
   ###########################
-    $scope.formPartyLink = (doc) ->
+
+    # Def formPartyLink:
+    # Given a party document, returns a hyperlink.
+    formPartyLink = (doc) ->
       return partyLinkPrefix + doc.party_id_i

-    $scope.formVolumeLink = (doc) ->
+    # Def formVolumeLink:
+    # Function that, given a volume document, returns a hyperlink.
+    formVolumeLink = (doc) ->
       return volumeLinkPrefix + doc.volume_id_i

+    # Def formVolumeResult:
+    # This is a helper function that concats parts of a volume into a string.
     $scope.formVolumeResult = (doc) ->
       # Form everything except the title, which we'll do in HTML so we can make it the link
       res = []
@@ -58,6 +85,9 @@
   ###########################
   # Search handlers
   ###########################
+
+    # Def searchBox: read the current text in the search box
+    # and initiate a search. If blank, return all results.
     $scope.searchBox = ->
       console.log("The search box was :", $scope.searchBoxQuery)
       console.log("The original query was :", $scope.originalQuery)
@@ -68,10 +98,11 @@
       $scope.query = $scope.originalQuery
       $scope.offset = 0
       console.log("NEW SEARCH:", $scope.query)
-      $scope.search()
-
-    # Put an ng-key enter here so it does what we offset
-    $scope.search = (query = "") ->
+      search()
+
+    # Def search: the function that is actually calling the search from Haskell.
+    # Get the results from Solr and pass them into parseResults.
+    search = (query = "") ->
       # This is for if we want to pass an argument into this...
       if query.length > 0
         $scope.query = query
@@ -86,44 +117,55 @@
       console.log(promise)
       promise.then (res) ->
         console.log("GOT RES:", res)
-        $scope.parseResults(res.data)
-
+        parseResults(res.data)
+
+    # This function will return the larger number of results of the
+    # two types. So if there are more volumes than parties, it returns
+    # the number of volumes.
     $scope.maxResults = ->
       return Math.max($scope.volumeCount, $scope.partyCount)

     #################################
-    # Parse results, the main workhorse function
+    # Parse results, the main workhorse function.
+    # This function is called after each search and is used to set
+    # all of the params for use in the html.
     # ##############################
-    $scope.parseResults = (res) ->
+    parseResults = (res) ->
       if res == "null"
         $scope.query = "*"
-        $scope.search()
+        search()
         return
       # if !$scope.query? or !$scope.query or $scope.query == "" or $scope.query == "null" or $scope.query == "false" or $scope.query == "undefined"
         # $scope.query = "*"
       console.log("query before anything:", $scope.query)
       console.log("RES:", res)
-      $scope.partyResults = $scope.getResults("party", res)
-      $scope.volumeResults = $scope.getResults("volume", res)
-
-      $scope.partyCount = $scope.getTypeCounts("party", res)
-      $scope.volumeCount = $scope.getTypeCounts("volume", res)
-
-      $scope.suggestedQuery = $scope.getCollation(res)
-
+      $scope.partyResults = getResults("party", res)
+      $scope.volumeResults = getResults("volume", res)
+
+      # Set the number of parties and volumes returned.
+      $scope.partyCount = getTypeCounts("party", res)
+      $scope.volumeCount = getTypeCounts("volume", res)
+
+      # If there is a suggested query, extract it.
+      $scope.suggestedQuery = getSuggestedQuery(res)
+
+      # Include the number of returned results in the type display.
       $scope.typeDisplay = [$scope.partyDisplayStr + " (" + ($scope.partyCount || 0) + ")",
         $scope.volumeDisplayStr + " (" + ($scope.volumeCount || 0) + ")"]

       console.log("PARTY RESULTS", $scope.partyResults)
       console.log("SUGGESTED QUERY", $scope.suggestedQuery)

-      if $scope.partyCount
-        $scope.affiliations = (c.party_affiliation_s for c in $scope.partyResults.docs)
-      else
-        $scope.affiliations = ["No people found"]
+      # Code for extracting user affiliations for filtering, unused.
+      # if $scope.partyCount
+        # $scope.affiliations = (c.party_affiliation_s for c in $scope.partyResults.docs)
+      # else
+        # $scope.affiliations = ["No people found"]

       $scope.totalCount = $scope.partyCount + $scope.volumeCount

+
+      # Set up pagination
       $scope.minPage = 1
       $scope.maxPage = 1 + ($scope.maxResults() / ($scope.limit + 1))
       pageRange = []
@@ -134,29 +176,24 @@

       $scope.goToPage = (page) ->
         $scope.offset = $scope.limit * (page-1)
-        $scope.search()
-
-      ##########################
-      # Set up pagination
-      # ########################
+        search()

       if parseInt($scope.maxResults()) > ($scope.offset + $scope.limit)
         $scope.next = ->
           $scope.offset = $scope.offset + $scope.limit
-          $scope.search()
+          search()
       else
         $scope.next = undefined
       if $scope.offset > 0
         $scope.prev = ->
           $scope.offset = Math.max(0, $scope.offset - $scope.limit)
-          $scope.search()
+          search()
       else
         $scope.prev = undefined

-      $scope.updateFilterBoxOptions()
+      updateFilterBoxOptions()

       $scope.number = 1 + ($scope.offset / $scope.limit)
-      console.log("NUMBER: ", $scope.number, $scope.offset, $scope.limit)

 ################################
 # End parse results
@@ -166,21 +203,21 @@
 # Helper functions
 ################################

-    $scope.findFacet = (typeName, res) ->
+    findFacet = (typeName, res) ->
       if res != null and res.facets.content_type
          facets = (facet.val for facet in res.facets.content_type.buckets)
          return facets.indexOf(typeName)
       else
          return -1

-    $scope.getTypeCounts = (type, res) ->
-      idx = $scope.findFacet(type, res)
+    getTypeCounts = (type, res) ->
+      idx = findFacet(type, res)
       console.log(idx)
       if idx < 0
         return null
       return res?.facets?.content_type?.buckets[idx].count ? 0

-    $scope.findResult = (typeName, res) ->
+    findResult = (typeName, res) ->
       console.log("RESULT IS", res)
       if res != null
         groups = (group.groupValue for group in res.grouped.content_type.groups)
@@ -188,35 +225,35 @@
       else
         return -1

-    $scope.getSpelling = (res) ->
+    getCorrectedSpelling = (res) ->
       return res.spellcheck.suggestions

-    $scope.getCollation = (res) ->
+    getSuggestedQuery = (res) ->
       if res.spellcheck?.collations.length > 0
         return res.spellcheck.collations[1].collationQuery
       return null

-    $scope.getResults = (type, res) ->
-      idx = $scope.findResult(type, res)
+    getResults = (type, res) ->
+      idx = findResult(type, res)
       if idx < 0
         return null
       return res.grouped.content_type.groups[idx].doclist

-    $scope.getVolumeFeatureBoxOpts = ->
+    getVolumeFilterBoxOpts = ->
       opts = [$scope.selectSessionStr, $scope.selectHighlightStr]
       return opts

-    $scope.getPartyFeatureBoxOpts = ->
+    getPartyFilterBoxOpts = ->
       opts = [$scope.selectSessionStr, $scope.selectHighlightStr]
       return opts

-    $scope.updateFilterBoxOptions = ->
+    updateFilterBoxOptions = ->
       console.log("SELTYPE", $scope.selectedType)
       if $scope.selectedType and currentFilter != $scope.selectedType
         # We have to reset offset
         $scope.offset = 0
         if $scope.selectedType.join(" ").includes($scope.volumeDisplayStr)
-          $scope.filterDisplay = (s for s in $scope.getVolumeFeatureBoxOpts())
+          $scope.filterDisplay = (s for s in getVolumeFilterBoxOpts())
         if $scope.selectedType.join(" ").includes($scope.partyDisplayStr)
           # console.log("AFFILIATIONS:", $scope.affiliations)
           $scope.filterDisplay = (s for s in $scope.selectPeopleFilters)
@@ -224,7 +261,7 @@

     # Takes a group of documents, an argument name, and a value, then counts
     # how many documents in the set have that value
-    $scope.countWithArg = (group, argument, value) ->
+    countWithArg = (group, argument, value) ->
       console.log(group)
       result = (d for d in group.docs when d[argument] == value).length || 0
       return result
@@ -235,10 +272,10 @@
       console.log($scope.selectedType, $scope.selectedType.join(" "))
       if $scope.selectedType.join(" ").includes($scope.volumeDisplayStr)
         $scope.query = $scope.originalQuery
-        $scope.search()
+        search()
       if $scope.selectedType.join(" ").includes($scope.partyDisplayStr)
         $scope.query = $scope.originalQuery
-        $scope.search()
+        search()
       console.log($scope.selectedType, $scope.filterDisplay)

     # Action to do something when a filter option is clicked
@@ -246,48 +283,48 @@
       console.log("FILTER BOX CLICKED", $scope.selectedFilter)
       if $scope.selectedType.join(" ").includes($scope.volumeDisplayStr)
         if $scope.selectSessionStr in $scope.selectedFilter
-          $scope.filterBySession()
+          filterBySession()
         if $scope.selectHighlightStr in $scope.selectedFilter
-          $scope.filterByHighlight()
+          filterByHighlight()
       if $scope.selectedType.join(" ").includes($scope.partyDisplayStr)
         # $scope.partyFilterBoxClick()
-        $scope.filterParties($scope.selectedFilter)
+        filterParties($scope.selectedFilter)
         console.log("FILTERING BY PARTY")
       $scope.offset = 0 # Reset the offset
-      $scope.search()
-
-    $scope.addArgToQuery = (query, arg, val) ->
+      search()
+
+    addArgToQuery = (query, arg, val) ->
       return query + "|arg=" + arg + ":\"" + val + "\""

     $scope.partyFilterBoxClick = ->
-      $scope.filterByAffiliation()
-
-    $scope.filterByAffiliation = ->
+      filterByAffiliation()
+
+    filterByAffiliation = ->
       $scope.query = $scope.originalQuery + "|arg=party_affiliation_s:\"" + $scope.selectedFilter + "\""

-      # funcs = {"session" : filterBySession, "highlight" : $scope.filterByHighlight}
+      # funcs = {"session" : filterBySession, "highlight" : filterByHighlight}

     # Now we want to rerun the search but only return vols w/ highlights
-    $scope.filterByHighlight = ->
+    filterByHighlight = ->
       $scope.requireHighlight = true
       $scope.query = $scope.originalQuery + "|arg=volume_has_excerpt_b:true"

-    $scope.filterBySession = ->
+    filterBySession = ->
       $scope.requireSession = true
       $scope.query = $scope.originalQuery + "|arg=volume_has_sessions_b:true"

-    $scope.filterParties = (filterName) ->
+    filterParties = (filterName) ->
       console.log("Filtering parties by", filterName)
       if filterName.join(" ").includes("All")
         # Select all parties
         $scope.query = $scope.originalQuery # TODO should this do something else?
       else if filterName.join(" ").includes("Institution")
         # Select institutions
-        $scope.query = $scope.addArgToQuery($scope.originalQuery, "party_is_institution_b", "true")
+        $scope.query = addArgToQuery($scope.originalQuery, "party_is_institution_b", "true")
       else if filterName.join(" ").includes("Authorized")
         # Select authorized users only
-        $scope.query = $scope.addArgToQuery($scope.originalQuery, "party_is_authorized_b", "true")
+        $scope.query = addArgToQuery($scope.originalQuery, "party_is_authorized_b", "true")
       console.log("Query after filter", $scope.query)

@@ -301,7 +338,7 @@
       $scope.originalQuery = params.query
     console.log(results)
     $scope.offset = parseInt($location.search().offset, 10) || 0
-    $scope.parseResults(results)
+    parseResults(results)

     console.log(results)


Add reject function for arrays, remove empty strings from interests in show.js.coffee.

--- app/assets/javascripts/application/views/users/show.js.coffee
+++ app/assets/javascripts/application/views/users/show.js.coffee
@@ -1,10 +1,20 @@
 class App.Views.Users.Show extends App.View
   template: JST['application/templates/users/show']
+
+  Array::reject = (deleteValue) ->
+    i = 0
+    while i < @length
+      if @[i] == deleteValue
+        @splice i, 1
+        i--
+      i++
+    this

   render: ->
     if @model.attributes.user_settings.interests
       intList = @model.attributes.user_settings.interests.split(",").map (interest) ->
         interest.trim()
+      intList = intList.reject('')
       @model.attributes.user_settings.interests = intList
     else
       @model.attributes.user_settings.interests = ""


Added ability to ignore loading indicator

--- app/assets/javascripts/sprangular/interceptors/loading.coffee
+++ app/assets/javascripts/sprangular/interceptors/loading.coffee
@@ -4,7 +4,7 @@

   $provide.factory 'loadingInterceptor', ($q, Status) ->
     request: (config) ->
-      Status.httpLoading = true
+      Status.httpLoading = true unless config.ignoreLoadingIndicator

       config


Don't re-escape section names in LM gradebook

In OutcomeGradebookView, refrain from calling htmlEscape the array of
sections, since the section names have already been escaped by the time
we see them.

fixes EVAL-1225
flag=none

Test plan:
- Create a course with some sections whose names contain characters
  subject to being escaped in HTML (&, <, >, ", ')
  - Add some students in those sections
- Enable the Learning Mastery Gradebook feature for the course
- Open Gradebook and select Learning Mastery Gradebook via the
  "Gradebook" menu
- In the section dropdown at the top right, the section names should not
  appear to contain HTML-escaped chars (e.g., you should see & not
  &)

Change-Id: Ic0997a8403060858256b2bf8ecc15f72cb010bdb
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/249159
Tested-by: Service Cloud Jenkins <9144042a601061f88f1e1d7a1753ea3e2972119d@instructure.com>
QA-Review: Adrian Packel <9c6a39807293376f29cba17542b4cb1b663ecc4a@instructure.com>
Reviewed-by: Syed Hussain <1379c89cd1d253d3c5a299cc4d8f6002ada9d6b5@instructure.com>
Reviewed-by: Spencer Olson <7cc0890d6f1f70810a023e7173eca5724d6c8e49@instructure.com>
Product-Review: Syed Hussain <1379c89cd1d253d3c5a299cc4d8f6002ada9d6b5@instructure.com>

--- app/coffeescripts/views/gradebook/OutcomeGradebookView.coffee
+++ app/coffeescripts/views/gradebook/OutcomeGradebookView.coffee
@@ -23,7 +23,6 @@
 import ReactDOM from 'react-dom'
 import {View} from 'Backbone'

-import htmlEscape from 'str/htmlEscape'
 import Slick from 'vendor/slickgrid'
 import Grid from '../../gradebook/OutcomeGradebookGrid'
 import userSettings from '../../userSettings'
@@ -227,8 +226,7 @@
       Grid.Util.saveOutcomes(response.linked.outcomes)
       Grid.Util.saveStudents(response.linked.users)
       Grid.Util.saveOutcomePaths(response.linked.outcome_paths)
-      sections = @learningMastery.getSections().map(htmlEscape)
-      Grid.Util.saveSections(sections)
+      Grid.Util.saveSections(@learningMastery.getSections())
       [columns, rows] = Grid.Util.toGrid(response, column: { formatter: Grid.View.cell })
       @columns = columns
       if @$('#no_results_outcomes:checkbox:checked').length == 1


io.sails.url is required for native mobile app

--- www/js/index.coffee
+++ www/js/index.coffee
@@ -1,8 +1,9 @@
 env = require './env.coffee'

+io.sails.url = env.server.app.urlRoot
 io.sails.path = "#{env.path}/socket.io"
 io.sails.useCORSRouteToGetCookie = false
-
+
 window.Promise = require 'promise'
 window._ = require 'lodash'
 window.$ = require 'jquery'


change to use new context.cio to get the chain, use serverClient instead of connection

--- lib/emit-new-server-client.coffee
+++ lib/emit-new-server-client.coffee
@@ -1,17 +1,17 @@
 # accept options to the module's builder function to generate a chain function
-module.exports = (options) -> (_, context) ->
+module.exports = (_, context) ->

   # depends on if socket is secured or not
   which = if @isSecure then 'secureConnection' else 'connection'

   # listen for a connection, run the other chain when we get one
   @server.on which, (connection) ->
-    options.serverClientChain.run
+    context.cio.getServerClientChain().run
       # make it build a new object using the server's options (context)
       # and the new connection only in the new object
       base: context
       props:
-        connection:
+        serverClient:
           value: connection
           writable   : true
           enumerable : true


Add a getActiveMinimap method

Add a getActiveMinimap method

--- lib/mixins/view-management.coffee
+++ lib/mixins/view-management.coffee
@@ -29,6 +29,8 @@
   # Returns the {MinimapView} object associated to the passed-in {Editor}.
   minimapForEditor: (editor) ->
     @minimapViews[editor.id] if editor?
+
+  getActiveMinimap: -> @minimapForEditor(atom.workspace.getActiveEditor())

   # Public: Calls `iterator` for each present and future minimap views.
   #


mousedown instead of onclick for the first zooming event

--- js/msa/zoombar.coffee
+++ js/msa/zoombar.coffee
@@ -22,7 +22,7 @@
       #zoomSlider.addEventListener "change", (evt) =>
       #  @_reDraw()

-      zoomSlider.addEventListener "click", (evt) =>
+      zoomSlider.addEventListener "mousedown", (evt) =>
         @firstClick = true
         @_reDraw()


Fixed point NaN in results

Fixed point NaN in results

--- app/client/game/play.coffee
+++ app/client/game/play.coffee
@@ -100,7 +100,8 @@
     $('audio')[current_game().current_question].pause()

     # calculate points
-    points = parseInt($('.audio-bar').text(), 10)
+    points = parseInt($('#audio-bar').text(), 10)
+    if isNaN points then points = CONFIG.POINTS_PER_QUESTION
     answer = event.target.text[0]

     # update game


removing the youtube thingy to be placed in the model

--- app/coffee/showentry.coffee
+++ app/coffee/showentry.coffee
@@ -66,26 +66,26 @@

   # Setup youtube links to show thumbnails
-  $('.entryLinks .link').each( (i, val) ->
-    url = $(this).find('a').attr('href')
-    domain = url.substring(0, url.indexOf('/', 14))
-
-    if domain is "http://www.youtube.com" or domain is "www.youtube.com"
-      splitTextArray = url.split('v=');
-      filePath = 'http://gdata.youtube.com/feeds/api/videos?q=' + splitTextArray[splitTextArray.length - 1] + '&alt=json&max-results=30&format=5';
-
-      # Get the data from YOUTUBE
-      $.ajax({
-        url: filePath
-        dataType: 'jsonp'
-        success: (data) ->
-          console.log data
-          newElement = '

'
-          #newElement = '

<input class="linkTitleValue" value="' + data.feed.entry[0].title.$t + '" name="entry[links_attributes][][title]" />

<input value="' + newText + '" class="linkTitleValue" name="entry[links_attributes][][url]" style="width: 320px;">

X

';
-          $('.content .gallery').append(newElement);
-      });
-
-  )
+  # $('.entryLinks .link').each( (i, val) ->
+  #     url = $(this).find('a').attr('href')
+  #     domain = url.substring(0, url.indexOf('/', 14))
+  #
+  #     if domain is "http://www.youtube.com" or domain is "www.youtube.com"
+  #       splitTextArray = url.split('v=');
+  #       filePath = 'http://gdata.youtube.com/feeds/api/videos?q=' + splitTextArray[splitTextArray.length - 1] + '&alt=json&max-results=30&format=5';
+  #
+  #       # Get the data from YOUTUBE
+  #       $.ajax({
+  #         url: filePath
+  #         dataType: 'jsonp'
+  #         success: (data) ->
+  #           console.log data
+  #           newElement = '

'
+  #           #newElement = '

<input class="linkTitleValue" value="' + data.feed.entry[0].title.$t + '" name="entry[links_attributes][][title]" />

<input value="' + newText + '" class="linkTitleValue" name="entry[links_attributes][][url]" style="width: 320px;">

X

';
+  #           $('.content .gallery').append(newElement);
+  #       });
+  #
+  #   )
 # function getBaseURL() {
 #     var url = location.href;  // entire url including querystring - also: window.location.href;
 #     var baseURL = url.substring(0, url.indexOf('/', 14));


Removed very awesome comment

Removed very awesome comment

--- app/js/util/scrollto.coffee
+++ app/js/util/scrollto.coffee
@@ -8,5 +8,4 @@

 $(window).bind 'factlink.factsLoaded', ->
   if FactlinkConfig?.scrollto
-    # Get al the facts on the current page
     Factlink?.scrollTo FactlinkConfig.scrollto


create array of highlight objects to persist between plots

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -19,6 +19,8 @@
     @smallestY = Math.min @data.y...
     @largestX  = Math.max @data.x...
     @largestY  = Math.max @data.y...
+
+    @highlights = []

     @dataLength = Math.min @data.x.length, @data.y.length

@@ -193,16 +195,7 @@
       @highlightCurve(entry.xL,entry.xR)

   highlightCurve: (xLeft,xRight) ->
-    # console.log 'highlightCurve()'
-    for i in [0...@dataLength]
-      if @data.x[i] >= xLeft and @data.x[i] <= xRight
-        x = ((+@data.x[i]+@toDays(@leftPadding)-@xMin)/(@xMax-@xMin)) * (@canvas.width-@leftPadding)
-        y = ((+@data.y[i]-@yMin)/(@yMax-@yMin)) * @canvas.height
-        y = -y + @canvas.height # flip y-values
-        @ctx.beginPath()
-        @ctx.fillStyle = "rgba(252, 69, 65, 1.0)" #"#fc4541"
-        @ctx.fillRect(x,y,2,2)
-    return
+    @highlights.push {'xLeft': xLeft, 'xRight': xRight}

   plotPoints: (xMin = @smallestX, xMax = @largestX, yMin = @smallestY, yMax = @largestY) ->
     # console.log 'plotPoints(): [',xMin,',',xMax,']'
@@ -244,6 +237,19 @@
     @scale = (parseFloat(@largestX) - parseFloat(@smallestX)) / (parseFloat(@xMax) - parseFloat(@xMin))
     @rescaleMarks(xMin, xMax)

+    # draw highlights
+    for highlight in [ @highlights... ]
+      console.log 'HIGHLIGHT: ', highlight
+      console.log "HIGHLIGHT: (#{highlight.xLeft},#{highlight.xRight})"
+      for i in [0...@dataLength]
+        if @data.x[i] >= highlight.xLeft and @data.x[i] <= highlight.xRight
+          x = ((+@data.x[i]+@toDays(@leftPadding)-@xMin)/(@xMax-@xMin)) * (@canvas.width-@leftPadding)
+          y = ((+@data.y[i]-@yMin)/(@yMax-@yMin)) * @canvas.height
+          y = -y + @canvas.height # flip y-values
+          @ctx.beginPath()
+          @ctx.fillStyle = "rgba(252, 69, 65, 1.0)" #"#fc4541"
+          @ctx.fillRect(x,y,2,2)
+
     return

   rescaleMarks: (xMin, xMax) ->


normalize y-tickmarks (max brightness = 1)

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -17,14 +17,27 @@

     # remove outliers and normalize
     @removeOutliers(3)
-    # @data.y = @normalize(@data.y)
+    @data.y = @normalize(@data.y)
+
+
+
+
+
+    # # normalize
+    # yNorm = []
+    # yMin = Math.min @data.y...
+    # yMax = Math.max @data.y...
+    # for yValue, i in [ @data.y... ]
+    #   yNorm[i] = (+yValue-yMin)/(yMax-yMin)
+    # @data.y = yNorm
+

     # normalize y values to unity mean
     yMean = @mean(@data.y)
     console.log 'yMean: ', yMean

-    for yValue, i in [ @data.y... ]
-      @data.y[i] =  (@data.y[i]-@smallestY) / (@largestY-@smallestY)
+    # for yValue, i in [ @data.y... ]
+    #   @data.y[i] =  (@data.y[i]-@smallestY) / (@largestY-@smallestY)
       # console.log 'y: ', @data.y[i]

     # for yValue, i in [ @data.y... ]
@@ -37,6 +50,16 @@
     @smallestY = Math.min @data.y...
     @largestX = Math.max  @data.x...
     @largestY = Math.max  @data.y...
+
+  normalize: (values) ->
+    vMax = Math.max values...
+    vMin = Math.min values...
+    vMean = @mean(values)
+    v_norm = []
+    for v, i in [ values... ]
+      v_norm[i] = (v-vMin)/(vMax-vMin)
+      # v_norm[i] = v_norm[i] / vMean
+    return v_norm

   disableMarking: ->
     @markingDisabled = true
@@ -107,10 +130,6 @@
     @data.x = x_new
     @data.y = y_new

-    # # normalize
-    # for yValue in [ @data.y... ]
-    #   yValue = (+yValue-@smallestY)/(@largestY-@smallestY)
-
   std: (data) ->
     mean = @mean(data)
     sum = 0


allow marks on left edge (resolves #95)

allow marks on left edge (resolves #95)

--- app/lib/canvas-graph.coffee
+++ app/lib/canvas-graph.coffee
@@ -45,7 +45,7 @@
     # debugger
     # console.log 'onMouseDown()'
     xClick = e.pageX - e.target.getBoundingClientRect().left - window.scrollX
-    return if xClick < 80 # display line instead
+    return if xClick < @leftPadding # display line instead
     @addMarkToGraph(e)

   onMouseMove: (e) =>