client: home: teamsettings part made functional

--- client/home/lib/myteam/hometeamsettings.coffee
+++ client/home/lib/myteam/hometeamsettings.coffee
@@ -33,7 +33,6 @@

     @teamName = new kd.InputView
       name         : 'title'
-      cssClass     : 'name'
       defaultValue : Encoder.htmlDecode team.title ? ''

     @teamDomain = new kd.InputView
@@ -47,8 +46,8 @@
     #   defaultValue : Encoder.htmlDecode team.allowedDomains?.join(', ') ? ''

     @save  = new CustomLinkView
-      cssClass : "HomeAppView--button primary#{unless @canEditGroup then ' hidden' else ''}"
-      title    : 'SAVE'
+      cssClass : "HomeAppView--button primary fr#{unless @_canEdit then ' hidden' else ''}"
+      title    : 'SAVE CHANGES'
       click    : @bound 'update'

     @delete  = new CustomLinkView
@@ -137,10 +136,10 @@

     team         = @getData()
     dataToUpdate = {}
-    # newDomains   = separateCommas domains
+    title        = @teamName.getValue()
+    # newDomains = separateCommas domains

-    unless @teamName.getValue() is team.title
-      dataToUpdate.title = formData.title
+    dataToUpdate.title = title  unless title is team.title

     # unless _.isEqual newDomains, team.allowedDomains
     #   for domain in newDomains when not validator.isURL domain
@@ -171,15 +170,17 @@

     <form>
-      <fieldset class='half'>
-        Team Name
-        {{> @teamName}}
-      </fieldset>
-      <fieldset class='half'>
-        Koding URL
-        {{> @teamDomain }}
-      </fieldset>
-      <fieldset>
+

+        <fieldset class='half'>
+          Team Name
+          {{> @teamName}}
+        </fieldset>
+        <fieldset class='half TeamUrl'>
+          Koding URL
+          {{> @teamDomain }}
+        </fieldset>
+

+      <fieldset class='HomeAppView--ActionBar'>
         {{> @delete}}
         {{> @save}}
       </fieldset>


Don't encourage skip (esc) when in full-screen mode where esc exits full-screen mode

--- app/views/play/level/LevelDialogueView.coffee
+++ app/views/play/level/LevelDialogueView.coffee
@@ -68,12 +68,15 @@
     else
       s = $.i18n.t('play_level.hud_continue_short', defaultValue: 'Continue')
       sk = $.i18n.t('play_level.skip_tutorial', defaultValue: 'skip: esc')
-      if not @escapePressed
+      if not @escapePressed and not @isFullScreen()
         group.append('' + sk + '')
       group.append($('<button class="btn btn-small banner with-dot">' + s + '

</button>'))
       @lastResponses = null
     @animator = new DialogueAnimator(message, @bubble)
     @messageInterval = setInterval(@addMoreMessage, 1000 / 30)  # 30 FPS
+
+  isFullScreen: ->
+    document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen

   addMoreMessage: =>
     if @animator.done()


Completed basic search

Completed basic search

--- app/webroot/coffee/views/search/search.coffee
+++ app/webroot/coffee/views/search/search.coffee
@@ -23,8 +23,9 @@
     #  collection: @search.results

     # Setup our Router
-    #@router = new arcs.routers.Search
-    #  search: @search
+    #router = new arcs.routers.Search
+      #search: @search
+    #console.log(router)

     # Start Backbone.history
     Backbone.history.start
@@ -233,51 +234,53 @@
   addFacet: (e) ->
     e.preventDefault()
     @search.vs.searchBox.addFacet(e.target.text,'',10)
+
+  search = () ->
+    val = $(".searchBoxInput").val()
+    console.log(val)
+    resources = new Promise((resolve, reject) ->
+      resourcequery = encodeURIComponent("(Type,like,#{val}),or,(Resource Identifier,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
+      req = $.getJSON arcs.baseURL + "resources/search?q=#{resourcequery}&sid=736", (response) ->
+        resolve(response)
+    )
+    projects = new Promise((resolve, reject) ->
+      projectquery = encodeURIComponent("(Country,like,#{val})")
+      req = $.getJSON arcs.baseURL + "resources/search?q=#{projectquery}&sid=734", (response) ->
+        resolve(response)
+    )
+    seasons = new Promise((resolve, reject) ->
+      seasonquery = encodeURIComponent("(Title,like,#{val}),or,(Description,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
+      req = $.getJSON arcs.baseURL + "resources/search?q=#{seasonquery}&sid=735", (response) ->
+        resolve(response)
+    )
+    excavations = new Promise((resolve, reject) ->
+      excavationquery = encodeURIComponent("(Name,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
+      req = $.getJSON arcs.baseURL + "resources/search?q=#{excavationquery}&sid=740", (response) ->
+        resolve(response)
+    )
+    observations = new Promise((resolve, reject) ->
+      observationquery = encodeURIComponent("(Monument Classification,like,#{val}),or,(Monument.Type,like,#{val}),or,(Monument.Material,like,#{val}),or,(Monument.Technique,like,#{val}),or,(Monument.Period,like,#{val}),or,(Monument.Terminus Ante Quem,like,#{val}),or,(Monument.Terminus Post Quem,like,#{val})")
+      req = $.getJSON arcs.baseURL + "resources/search?q=#{observationquery}&sid=739", (response) ->
+        resolve(response)
+    )
+
+    totalResults = {}
+    Promise.all([resources,projects,seasons,excavations,observations]).then((values) ->
+      #console.log(values)
+      for item in values
+        Array::push.apply totalResults, item.results
+      #console.log(totalResults)
+      Search.prototype._render results: totalResults
+      $('#search-pagination').html arcs.tmpl('search/paginate', results: totalResults)
+    )

   #Activates on enter press: search
   $ ->
     $(".searchBoxInput").keyup (e) ->
       if e.keyCode == 13
         e.preventDefault()
-        #resources search
-        val = $(".searchBoxInput").val()
-        console.log(val)
-        resources = new Promise((resolve, reject) ->
-          resourcequery = encodeURIComponent("(Type,like,#{val}),or,(Resource Identifier,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
-          req = $.getJSON arcs.baseURL + "resources/search?q=#{resourcequery}&sid=736", (response) ->
-            resolve(response)
-        )
-        projects = new Promise((resolve, reject) ->
-          projectquery = encodeURIComponent("(Country,like,#{val})")
-          req = $.getJSON arcs.baseURL + "resources/search?q=#{projectquery}&sid=734", (response) ->
-            resolve(response)
-        )
-        seasons = new Promise((resolve, reject) ->
-          seasonquery = encodeURIComponent("(Title,like,#{val}),or,(Description,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
-          req = $.getJSON arcs.baseURL + "resources/search?q=#{seasonquery}&sid=735", (response) ->
-            resolve(response)
-        )
-        excavations = new Promise((resolve, reject) ->
-          excavationquery = encodeURIComponent("(Name,like,#{val}),or,(Earliest Date,like,#{val}),or,(Latest Date,like,#{val})")
-          req = $.getJSON arcs.baseURL + "resources/search?q=#{excavationquery}&sid=740", (response) ->
-            resolve(response)
-        )
-        observations = new Promise((resolve, reject) ->
-          observationquery = encodeURIComponent("(Monument Classification,like,#{val}),or,(Monument.Type,like,#{val}),or,(Monument.Material,like,#{val}),or,(Monument.Technique,like,#{val}),or,(Monument.Period,like,#{val}),or,(Monument.Terminus Ante Quem,like,#{val}),or,(Monument.Terminus Post Quem,like,#{val})")
-          req = $.getJSON arcs.baseURL + "resources/search?q=#{observationquery}&sid=739", (response) ->
-            resolve(response)
-        )
-
-        totalResults = {}
-        Promise.all([resources,projects,seasons,excavations,observations]).then((values) ->
-          #console.log(values)
-
-          for item in values
-            Array::push.apply totalResults, item.results
-          #console.log(totalResults)
-          Search.prototype._render results: totalResults
-          $('#search-pagination').html arcs.tmpl('search/paginate', results: totalResults)
-        )
+        search()
+

       #projects search
       #resourcequery = encodeURIComponent("(Type,like,Pho),or,(Type,like,Note)")
@@ -309,7 +312,7 @@
   # Actually render the results. Can append or replace.
   # If there are no results, adds a 'No Results' message.
   _render: (results, append=false) ->
-    $results = $('#search-results')
+    $results = $('.flex-container')
     template = if @options.grid then 'search/grid' else 'search/list'
     results = results.results
     console.log(results)


landing: capitalized URL path

landing: capitalized URL path

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -267,7 +267,7 @@
       # '/Home'  : handleRoot
       '/About' : createSectionHandler 'Activity'

-      '/landing/:page': noop
+      '/Landing/:page': noop
       '/R/:username'  : noop

       # verbs


focus first input after edit begins

focus first input after edit begins

--- client/lanes/components/grid/EditingMixin.cjsx
+++ client/lanes/components/grid/EditingMixin.cjsx
@@ -13,6 +13,7 @@
         syncImmediatly: React.PropTypes.oneOfType([
             React.PropTypes.bool, React.PropTypes.func
         ])
+
     editorTypes:
         text: (props) ->
             <input type="text" {...props}
@@ -31,6 +32,8 @@

     listenNetworkEvents: true
     getDefaultProps: -> editors: {}
+    componentDidMount: ->
+        _.defer => _.dom(@).qs('input').focusAndSelect()

     renderControls: ->
         if @props.allowDelete


Changed 'underscore' to 'underscorize'

Changed 'underscore' to 'underscorize'

When I saw a pull request related to the 'underscore' method it took me
a moment to realize that it had nothing to do with the underscore.js
library. This name change should prevent similar confusion in the
future.

Related to #15.

--- lib/generator/index.coffee
+++ lib/generator/index.coffee
@@ -3,7 +3,7 @@
 pd = require('pretty-data').pd
 wrench = require 'wrench'

-underscore = (str) ->
+underscorize = (str) ->
   str.toLowerCase().replace /[-\s]/g, '_'

 dasherize = (str) ->
@@ -18,7 +18,7 @@
     .join '.'

 generateAppId = (options) ->
-  urlToPackage(options.puburl) + '.' + underscore options.name
+  urlToPackage(options.puburl) + '.' + underscorize options.name

 buildPackage = (options) ->
   name: options.id


Inherit [data-trix-mutable] hidden selection styles

--- src/trix/controllers/attachment_editor_controller.coffee
+++ src/trix/controllers/attachment_editor_controller.coffee
@@ -97,6 +97,7 @@
       tagName: "textarea"
       className: css.attachmentCaptionEditor
       attributes: placeholder: lang.captionPlaceholder
+      data: trixMutable: true
     textarea.value = @attachmentPiece.getCaption()

     textareaClone = textarea.cloneNode()


Focus root view if previously focused element is no longer attached

This can be the case when a select list is triggered while a different
select list is already open and so the second list to open has no
element to restore focus to when it closes.

--- lib/go-to-line-view.coffee
+++ lib/go-to-line-view.coffee
@@ -36,6 +36,11 @@
     @detaching = true
     @miniEditor.setText('')

+    if @previouslyFocusedElement?.isOnDom()
+      @previouslyFocusedElement.focus()
+    else
+      rootView.focus()
+
     super

     @detaching = false


do not render Install app button if the user is not authenticated

--- imports/ui/appstore.cjsx
+++ imports/ui/appstore.cjsx
@@ -89,17 +89,20 @@

-                <button type="button" className="btn btn-success btn-lg dropdown-toggle" data-toggle="dropdown">file_download Install</button>
-                <form role="form" className="dropdown-menu dropdown-menu-right" style={padding:10}>
-

-

-                        Installing may overwrite an existing app called {@state.selectedApp.name} with version {@state.selectedApp.version}.
-

-                        Do you wish to continue?
-

-

-                    <button onClick={@installApp} type="button" className="btn-install-app btn btn-lg btn-success pull-right">Yes!</button>
-                </form>
+                {if Helpers.isAuthenticated()
+                  [
+                    <button type="button" className="btn btn-success btn-lg dropdown-toggle" data-toggle="dropdown">file_download Install</button>
+                    <form role="form" className="dropdown-menu dropdown-menu-right" style={padding:10}>
+

+

+                            Installing may overwrite an existing app called {@state.selectedApp.name} with version {@state.selectedApp.version}.
+

+                            Do you wish to continue?
+

+

+                        <button onClick={@installApp} type="button" className="btn-install-app btn btn-lg btn-success pull-right">Yes!</button>
+                    </form>
+                  ]}


Bug Fix

Bug Fix

On iPhone, without this code the change menu Width function could occur causing the menu to close when moving the screen up and down with your finger

--- app/assets/javascripts/static_pages.js.coffee
+++ app/assets/javascripts/static_pages.js.coffee
@@ -40,7 +40,8 @@
 		$(".menu").css("left", "-20%")
 		$('.siteContainer').css('left', "0%")

-	$(window).bind("resize", changeMenuWidth)
+	if($(window).width() > 767)
+		$(window).bind("resize", changeMenuWidth)

 	# Menu Open/Close Button
 	$('.btn-menu').click ->
@@ -56,7 +57,7 @@
 			if ($(window).width() < 767)
 				$('body').css('position', "relative")
 			$(".menu").data("state", "closed")
-
+
 		else if $(".menu").data("state") == "closed"
 			$('.menu').animate(left:edgeLocation, openSpeed)
 			$('.siteContainer').animate(left:siteWidth, openSpeed)


update questions header when toggling show archived

--- app/assets/javascripts/views/home_page.coffee
+++ app/assets/javascripts/views/home_page.coffee
@@ -42,6 +42,7 @@
     else
       @questionsList.setRelation(Question.where('archivedAt <=': null))
     @showArchived = !@showArchived
+    @updateQuestionsHeader()
     return true

   addQuestion: ->


Tweak

Tweak

--- src/core/typed-value.coffee
+++ src/core/typed-value.coffee
@@ -34,7 +34,7 @@
       @_updateWithAccessor(value, 'collection', new_value)

     else if _.isUndefined(@value_type) or @value_type isnt new_type
-      @_updateValueObservable(new_value) # create new
+      @_updateValueObservable(new_value)

     else if @value_type is kb.TYPE_MODEL
       @_updateWithAccessor(value, 'model', new_value)


Revert "Fixed typo"

Revert "Fixed typo"

This reverts commit 8f115d7ad18df632be7b59c30c4494aefeea095f.

--- collections/MandrillSettings.collection.coffee
+++ collections/MandrillSettings.collection.coffee
@@ -38,5 +38,5 @@
 		@update settings._id, upd
 	else
 		ins = {}
-		ins[key] = val
+		inst[key] = val
 		@insert ins


fixes gradebook cell mis-alignment

fixes gradebook cell mis-alignment

the total_grade column's width was not being
parsed as an integer, therefore when adding all
the column's widths together to get the
width of the gradebook, the width was
being returned as a string. this caused
the gradebook to have mis-aligned columns
and prevented editing of grades until the
total_grade column was resized.

closes CNVS-21117

test plan
  - resize the total_grade column in gradebook
  - reload the page
  - the gradebook should load properly:
    - the cells should be editable
    - the cells should have proper styling

Change-Id: Id202817ab649f88255218e4d63b9ded9ce79556e
Reviewed-on: https://gerrit.instructure.com/56157
Tested-by: Jenkins
Reviewed-by: Josh Simpson <bfb4987b134a00f73d881fff5113b1d5fa1876bc@instructure.com>
QA-Review: Adrian Foong <84c1094366ed084f9dbd0e92a3de1886a73fdc57@instructure.com>
Product-Review: Josh Simpson <bfb4987b134a00f73d881fff5113b1d5fa1876bc@instructure.com>

--- app/coffeescripts/gradebook2/Gradebook.coffee
+++ app/coffeescripts/gradebook2/Gradebook.coffee
@@ -1244,7 +1244,7 @@

       totalWidth = testWidth("Total", columnWidths.total.min, columnWidths.total.max)
       if @gradebookColumnSizeSettings && @gradebookColumnSizeSettings['total_grade']
-        totalWidth = @gradebookColumnSizeSettings['total_grade']
+        totalWidth = parseInt(@gradebookColumnSizeSettings['total_grade'])

       total_column =
         id: "total_grade"


Fix tooltip plugin calculated offset bug

--- vendor/assets/javascripts/mice/tooltip.coffee
+++ vendor/assets/javascripts/mice/tooltip.coffee
@@ -224,9 +224,11 @@
       }, if isBody then { top: 0, left: 0 } else $element.offset())

     getCalculatedOffset: (placement, pos, actualWidth, actualHeight) ->
-      return if placement == 'bottom' then { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } else
-             if placement == 'top'    then { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } else
-             if placement == 'left'   then { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } else { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
+      switch placement
+        when 'bottom' then { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  }
+        when 'top'    then { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  }
+        when 'left'   then { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth }
+        when 'right'  then { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }

     getViewportAdjustedDelta: (placement, pos, actualWidth, actualHeight) ->
       delta = { top: 0, left: 0 }


Refactored streaming

Refactored streaming

--- src/io.coffee
+++ src/io.coffee
@@ -107,17 +107,26 @@

   _stream: (source, outstream) ->
     res = @_write source, '-'
-    res.stdout.pipe outstream
-    res.promise.otherwise (err) ->
+    if (piped = res.stdout?)
+      res.stdout.pipe outstream, end: false
+    res.promise.then ->
+      unless piped
+        throw new Error 'Failed to pipe stdout'
+      outstream.end()
+    .otherwise (err) ->
       outstream.emit 'error', err
-    res.stdout

   stream: ->
     res = new PassThrough()
     if @_stdin
       @_stream @source, res
     else
+      res.once 'end', =>
+        @_cleanup '_tmpFilename'
+      res.once 'error', =>
+        @_cleanup '_tmpFilename'
       When(@_fileSource()).then (filename) =>
-        (@_stream filename, res).once 'close', =>
-          @_cleanup '_tmpFilename'
+        @_stream filename, res
+      .otherwise (err) ->
+        res.emit 'error', err
     res


fixed coffeelint errors on workers/social/kodingadmins.coffee

--- workers/social/lib/social/kodingadmins.coffee
+++ workers/social/lib/social/kodingadmins.coffee
@@ -55,4 +55,6 @@
     firstName : 'Geraint'
     lastName  : 'Jones'
   }
-]+]
+
+


Document resin/data

Document resin/data

--- lib/resin/data/data.coffee
+++ lib/resin/data/data.coffee
@@ -4,12 +4,17 @@
 fsUtils = require('./fs-utils/fs-utils')
 exports.prefix = require('./data-prefix')

+# TODO: codo doesn't recognises functions in this file
+# because they have haltIfNoPrefix before the definition.
+
+# @nodoc
 haltIfNoPrefix = (callback) ->
 	return ->
 		if not exports.prefix.get()?
 			throw new Error('Did you forget to set a prefix?')
 		return callback.apply(null, arguments)

+# @nodoc
 constructPath = (key) ->
 	prefix = exports.prefix.get()
 	result = path.join(prefix, key)
@@ -19,6 +24,29 @@

 	return result

+# Get data by key
+#
+# We call "data" to the information saved by the application in order to work properly.
+# Examples of data are the token, cached downloads and much more.
+#
+# @param {String} key path relative to dataPrefix
+# @param {Object} options node fs options for when reading the resource
+# @param {Function} callback callback(error, value)
+#
+# @throw {Error} Will throw if data prefix was not previously set
+#
+# @example Get token
+#		resin.data.get 'token', encoding: 'utf8', (error, token) ->
+#			throw error if error?
+#			console.log(token)
+#
+#	@example Get nested token
+#		# Note: You should use the appropriate path.sep for your os
+#		# http://nodejs.org/api/path.html#path_path_sep
+#		resin.data.get 'my/nested/token', encoding: 'utf8', (error, token) ->
+#			throw error if error?
+#			console.log(token)
+#
 exports.get = haltIfNoPrefix (key, options, callback) ->
 	exports.has key, (hasKey) ->
 		if not hasKey
@@ -30,14 +58,55 @@
 		keyPath = constructPath(key)
 		fs.readFile(keyPath, options, callback)

+# Set/Update a data resource
+#
+# @param {String} key path relative to dataPrefix
+# @param {String, Buffer} value key value
+# @param {Object} options node fs options for when reading the resource
+# @param {Function} callback callback(error)
+#
+# @throw {Error} Will throw if data prefix was not previously set
+#
+# @note You can save a buffer, but we strongly recommend saving plain text when possible
+#
+# @example Set value
+#		resin.data.set 'customValue', 'Hello World', encoding: 'utf8', (error) ->
+#			throw error if error?
+#			console.log("Value saved to #{resin.data.prefix.get()}/customValue")
+#
 exports.set = haltIfNoPrefix (key, value, options, callback) ->
 	keyPath = constructPath(key)
 	fs.writeFile(keyPath, value, options, callback)

+# Check if value exists
+#
+# @param {String} key path relative to dataPrefix
+# @param {Function} callback callback(hasKey)
+#
+# @throw {Error} Will throw if data prefix was not previously set
+#
+# @example Has value
+#		resin.data.has 'foo/bar', (hasFooBar) ->
+#			if hasFooBar
+#				console.log('It\'s there!')
+#			else
+#				console.log('It\'s not there!')
+#
 exports.has = haltIfNoPrefix (key, callback) ->
 	keyPath = constructPath(key)
 	fs.exists(keyPath, callback)

+# Remove a key
+#
+# @param {String} key path relative to dataPrefix
+# @param {Function} callback callback(error)
+#
+# @throw {Error} Will throw if data prefix was not previously set
+#
+# @example Remove token
+#		resin.data.remove 'token', (error) ->
+#			throw error if error?
+#
 exports.remove = haltIfNoPrefix (key, callback) ->
 	keyPath = constructPath(key)


Fix images referenced from deleted messages

If message is deleted or otherwise default-folded,
don't check for (and fold) images it references

--- client/message.coffee
+++ client/message.coffee
@@ -230,14 +230,17 @@
     images[id].attachment = tid
     if images[id].count > 0 and not messageFolded.get(images[id].attachment)?
       messageFolded.set images[id].attachment, true
-  $(@firstNode).children('.panel-body').children('.message-body').find('img[src^="/gridfs/fs/"]')
-  .each ->
-    id = url2file @getAttribute('src')
-    initImage id
-    subimages.push id
-    images[id].count += 1
-    if images[id].attachment? and not messageFolded.get(images[id].attachment)?
-      messageFolded.set images[id].attachment, true
+  ## If message is deleted or otherwise default-folded,
+  ## don't check for images it references.
+  unless messageFolded.get @data._id
+    $(@firstNode).children('.panel-body').children('.message-body').find('img[src^="/gridfs/fs/"]')
+    .each ->
+      id = url2file @getAttribute('src')
+      initImage id
+      subimages.push id
+      images[id].count += 1
+      if images[id].attachment? and not messageFolded.get(images[id].attachment)?
+        messageFolded.set images[id].attachment, true

 scrollDelay = 750


removing conlang.js file- check it out from here if you want a copy

--- app/assets/javascripts/conlang.js.coffee
+++ app/assets/javascripts/conlang.js.coffee
@@ -1,202 +0,0 @@
-
-startWord = '^'
-endWord = '$'
-
-##
-# Models the list of characters in the chain. The characters each have
-# a corresponding int value, which is the proportion of those
-# characters which follow the key pattern.
-#
-class CharList
-
-  constructor: ->
-    @chars = {}
-    @total = 0
-
-  ##
-  # add a single character
-  #
-  add: (char) =>
-    @chars[char] ||= 0
-    @chars[char] += 1
-    @total += 1
-
-  chars:
-    @chars
-
-  ##
-  # sum of all the character values
-  #
-  total: =>
-    @total
-
-  ##
-  # select a character based on a random value compared with the
-  # character probability factorposition
-  #
-  # for example, assuming a structure of
-  # a: 22
-  # d: 5
-  # e: 45
-  # ..etc
-  #
-  # c = new Charlist
-  # ... create structure as above ...
-  # c.choice(25)
-  # > 'd'
-  #
-  choice: (selection) =>
-    selection = Math.floor(Math.random() * @total) unless selection
-    chars = {}
-    sum = 0
-    chars[char] = sum += count for char, count of @chars
-    for char, position of chars
-      return char if position >= selection
-    return false
-##
-# The histogram of letter probabilities. It is keyed by tuples, the
-# length of which is defined by the lookback parameter.
-#
-class Chain
-
-  constructor: (lookback = 2) ->
-    @lookback = lookback
-    @store = {}
-    @key = []
-
-  ##
-  # Add a string to the store
-  #
-  add: (string) =>
-    @key = []
-    string = string + endWord
-    @key.push startWord for i in [@lookback..1]
-    @addChar char for char in string
-
-
-  ##
-  # Add a char to the store- if the char is a space, mark the
-  # beginning and end of a word.
-  #
-  addChar: (char) =>
-    @store[@key] ||= new CharList()
-    @store[@key].add char
-    @key.push(char)
-    @key.shift()
-
-##
-# Markov chain representation using a Markov Chain implementation to
-# make fantasy words from list of corpora.
-#
-class MarkovChain
-
-  constructor: (corpora, callback, dictionary = '/dict.txt', lookback = 2) ->
-    @callback = callback
-    @progressbar = $('#progressbar')
-    @steps = Math.ceil(100 / (corpora.length + 2))
-    @output = $('#output')
-    @words = {}
-    @lookback = lookback
-    @name = $('#corpora-name')
-    @chain = new Chain(@lookback)
-    $.get dictionary, {}, (data) =>
-      @dictionary = data.split('\n') if dictionary
-      this.progress()
-      @loadCorpora corpora
-
-  ##
-  # Update the progress bar
-  #
-  progress: =>
-    @progressbar.progressbar("value",  @progressbar.progressbar("value") + @steps)
-
-  ##
-  # Load the corpora set.
-  #
-  loadCorpora: (corpora) =>
-    $.get '/language/corpus/load',
-      {corpus: corpora[0]},
-      (data) =>
-        @progress()
-        console.log corpora[0]
-        @chain.add word for word in data.toLowerCase().replace(/[0-9\.,-\/#!$%\^&\*;:{}=\-_~()\[\]»«"?\n\t„]/g," ").replace(/[0-9]/g,' ').split(' ')
-        if corpora.length > 1
-          @loadCorpora(corpora[1..])
-        else
-          @saveCorpora
-
-  ##
-  # Sends the corpora set to the back end
-  #
-  saveCorpora: () =>
-    $.post '/histogram/create',
-      chain: @chain
-      name: @name.value
-
-  ##
-  # Make the words out of the digested corpora.
-  #
-  makeWords: =>
-    @words =  (@makeWord(word) for word in @dictionary)
-    this.progress()
-    @callback(this)
-    @words
-
-  ##
-  # Make a single word.
-  #
-  makeWord: (original) =>
-    word = ""
-    char = startWord
-    key = []
-    key.push startWord for i in [@lookback..1]
-    while not char and char is not ' '
-      char = @chain.store[key].choice()
-    while typeof char is not 'undefined'
-      break if char is endWord
-      word += char unless char is startWord
-      key.push char
-      key.shift()
-      char = if @chain.store[key] then @chain.store[key].choice() else undefined
-    [original, word]
-
-$ ->
-  window.CharList = CharList
-  window.Chain = Chain
-  window.MarkovChain = MarkovChain
-
-  $('#progressbar').progressbar()
-
-  $('#language-lookback').change (e) ->
-    val = $(this).val()
-    val = if isNaN(parseInt(val)) then 2 else parseInt(val)
-    val = 3 if val > 3
-    val = 1 if val < 1
-    $(this).val(val)
-
-
-  $('#language-process').click (e) ->
-    $output = $('#language-output')
-    $name = $('#language-name')
-    $output.prepend('
' + $name.val() + '
')
-    callback = (markovchain) ->
-      $output.find('tbody').append("
#{newword[0]}	#{newword[1]}
") for newword in markovchain.words
-      markovchain.progress()
-      $.post '/language/create'
-        name: $name.val()
-        glossary: markovchain.words.to_json
-        description: $('#language-description').val()
-        (event) =>
-          # nop
-    try
-      markovChain = new MarkovChain(
-        $(corpus).val() for corpus in $('.language-corpus') when $(corpus).val() != '',
-        callback,
-        '/dict.txt',
-        $('#language-lookback').val()
-      )
-    catch e
-      console.log e
-    finally
-      false
-    false


removing old code

removing old code

--- app/assets/javascripts/gallery.js.coffee
+++ app/assets/javascripts/gallery.js.coffee
@@ -14,9 +14,4 @@
         </p>
         </div>
         """
-        $('#gallery').append html
-    # $('#gallery')
-    #   .on "ajax:success", (event, data, status, xhr) ->
-    #     $(@).append xhr.responseText
-    #   .on "ajax:error", (event, xhr, status, error) ->
-    #     $(@).append "Oops! Something went wrong!"+        $('#gallery').append html


Fix destruction of annotations

Fix destruction of annotations

--- app/api/classification.coffee
+++ app/api/classification.coffee
@@ -23,4 +23,5 @@
     annotation

   handleAnnotationDestroy: (index) ->
-    @update annotations: @annotations.splice index, 1
+    @annotations.splice index, 1
+    @update 'annotations'


Fix the AWS-SDK Persistor

Fix the AWS-SDK Persistor

--- app/coffee/AWSSDKPersistorManager.coffee
+++ app/coffee/AWSSDKPersistorManager.coffee
@@ -3,20 +3,20 @@
 _ = require "underscore"
 fs = require "fs"

-s3 = aws.S3()
+s3 = new aws.S3()

 module.exports =
   sendFile: (bucketName, key, fsPath, callback)->
     logger.log bucketName:bucketName, key:key, "send file data to s3"
     stream = fs.createReadStream fsPath
-    s3.putObject Bucket: bucketName, Key: key, Body: stream, (err, data) ->
+    s3.upload Bucket: bucketName, Key: key, Body: stream, (err, data) ->
       if err?
         logger.err err: err, Bucket: bucketName, Key: key, "error sending file data to s3"
       callback err

   sendStream: (bucketName, key, stream, callback)->
     logger.log bucketName:bucketName, key:key, "send file stream to s3"
-    s3.putObject Bucket: bucketName, Key: key, Body: stream, (err, data) ->
+    s3.upload Bucket: bucketName, Key: key, Body: stream, (err, data) ->
       if err?
         logger.err err: err, Bucket: bucketName, Key: key, "error sending file stream to s3"
       callback err


Order by time desc, nicer exit code

Order by time desc, nicer exit code

--- code/exportrss.coffee
+++ code/exportrss.coffee
@@ -45,15 +45,33 @@
       feed_url: boxurl + 'http/' + filename
     exports.feed = feed
     # db.each "select * from loggit_event where type='start'", eachRow, done
-    db.each 'select start.runid as runid, start.time as start_time, exit.time as exit_time, start.pid as pid, start.command as command, exit.exit_signal as exit_signal, exit.exit_status as exit_status from (select * from loggit_event where type = "start") as start left join (select * from loggit_event where type = "exit") as exit on start.runid==exit.runid;', eachRow, done
+    db.each """
+      select
+      start.runid as runid, start.time as start_time, exit.time as exit_time, start.pid as pid,
+      start.command as command, exit.exit_signal as exit_signal, exit.exit_status as exit_status
+      from
+        (select * from loggit_event where type = "start") as start
+        left join
+        (select * from loggit_event where type = "exit") as exit
+        on start.runid==exit.runid
+      order by start_time desc;""", eachRow, done
+
 Gather = exports.Gather
+
+exitMsg = (row) ->
+  signal = row.exit_signal || ""
+  if row.exit_status?
+    status = row.exit_status
+  else
+    status = ""
+  return signal+status

 eachRow = (err, row) ->
   feed.item
     title: row.command,
     url: boxurl + 'sqlite?q=select*from+loggit_event+where+runid="'+row.runid+'"',
     date: row.start_time,
-    description: "Exit: "+row.exit_signal+row.exit_status
+    description: "Exit: "+exitMsg(row)
     guid: row.runid

 allDone = ->


app: added start method

app: added start method

--- coffee/src/app.coffee
+++ coffee/src/app.coffee
@@ -5,20 +5,29 @@
 # which contain a Router, a View, and any app-wide state, and logic.
 class athena.lib.App

+
   # mixin Backbone.Events (not a class)
   _.extend @prototype, Backbone.Events
+

   constructor: (@options) ->
     @eventhub = @
     @initialize()

+
   # overridable classes for constructing app elements
   View: athena.lib.ContainerView
   Router: athena.lib.Router
+

   initialize: =>
     @view = new @View eventhub: @eventhub
     @router = new @Router eventhub: @eventhub

+
   showPage: (page) =>
     @view.content page
+
+
+  start: =>
+    Backbone.history.start {pushState: true}


Modes; disable debugging output by default.

--- content_scripts/mode.coffee
+++ content_scripts/mode.coffee
@@ -34,7 +34,7 @@

 class Mode
   # If Mode.debug is true, then we generate a trace of modes being activated and deactivated on the console.
-  debug: true
+  debug: false
   @modes: []

   # Constants; short, readable names for the return values expected by handlerStack.bubbleEvent.


絆システムの対応を再修正

絆システムの対応を再修正

--- app/assets/javascripts/models/party.js.coffee
+++ app/assets/javascripts/models/party.js.coffee
@@ -84,12 +84,12 @@
       m = mems[k]
       return unless m

-      if Arcana.sameArcana(m.arcana, ta) || Arcana.sameArcana(m.arcana, tc)
+      if Arcana.sameCharacter(m.arcana, ta) || Arcana.sameArcana(m.arcana, tc)
         mems[k] = null
         return

       return unless m.chainArcana
-      if Arcana.sameArcana(m.chainArcana, ta) || Arcana.sameArcana(m.chainArcana, tc)
+      if Arcana.sameArcana(m.chainArcana, ta) || Arcana.sameCharacter(m.chainArcana, tc)
         mems[k] = new Member(m.arcana)
     mems


Fix bug with active tab remembering (saving cookie with different path)

--- app/assets/javascripts/project_show.js.coffee
+++ app/assets/javascripts/project_show.js.coffee
@@ -6,7 +6,7 @@
       new Flash('Star toggle failed. Try again later.', 'alert')

     $("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
-        $.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
+        $.cookie "default_view", $(e.target).attr("href"), { expires: 30, path: '/' }

       defaultView = $.cookie("default_view")
       if defaultView


Fixed a bug that caused rubric pages to have duplicate ids

--- app/assets/javascripts/rubricEditor.js.coffee
+++ app/assets/javascripts/rubricEditor.js.coffee
@@ -365,11 +365,11 @@
   # nextId('counter') returns the next available id number for 'counter'
   # nextId('counter', newId) increases the counter to newId and returns newId. If next available id is higher than newId, the counter is not increased.
   nextId: (counterName, idNumber) ->
-    if idNumber
+    if idNumber?
       @idCounters[counterName] = idNumber if idNumber > @idCounters[counterName]
       return idNumber
     else
-      return @idCounters[counterName]++
+      return ++@idCounters[counterName]

   initializeDefault: ->


Removing loading message that jquery mobile creates

--- app/assets/javascripts/static_pages.js.coffee
+++ app/assets/javascripts/static_pages.js.coffee
@@ -10,6 +10,7 @@
 mobileMenuWidth = "80%"
 desktopMenuWidth = "20%"
 mobileSiteWidth = "85%"
+$.mobile.loadingMessage = false;

 changeMenuWidth = (e) ->
 	if ($(window).width() < ResponsiveWindowWidth)


About: fixed logo url

About: fixed logo url

--- client/About/AppView.coffee
+++ client/About/AppView.coffee
@@ -17,7 +17,7 @@
     @logoPackButton = new KDCustomHTMLView
       tagName       : 'a'
       attributes    :
-        href        : '/a/koding-logo.pdf'
+        href        : '/a/images/koding-logo.pdf'
         target      : '_blank'
       partial       : "Download Logo Pack"
       cssClass      : "solid green kdbutton"


log: minor fix

log: minor fix

--- client/Main/errorlog.coffee
+++ client/Main/errorlog.coffee
@@ -15,8 +15,8 @@
       terminalVersion
       codeVersion
       userAgent
-      useNewKites_new : KD.useNewKites
-      osKiteVersion   : osVersion
+      useNewKites   : KD.useNewKites
+      osKiteVersion : osVersion
     }, params

     KD.remote.api.JErrorLog.create error, ->


Refactor machine event listener binding procedure

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -404,23 +404,7 @@
                   kd.getSingleton('mainView').activitySidebar.initiateFakeCounter()

           @prepareCollaboration()
-
-          actionRequiredStates = [Pending, Stopping, Stopped, Terminating, Terminated]
-          computeController.on "public-#{machineId}", (event) =>
-
-            if event.status in actionRequiredStates
-
-              KodingKontrol.dcNotification?.destroy()
-              KodingKontrol.dcNotification = null
-
-              machineItem.getBaseKite( no ).disconnect()
-
-              unless @machineStateModal
-                @createMachineStateModal { state, container, machineItem }
-
-              else
-                if event.status in actionRequiredStates
-                  @machineStateModal.updateStatus event
+          @bindMachineEvents machineItem

         else
           @createMachineStateModal { state: 'NotFound', container }
@@ -432,6 +416,33 @@
         isOnboardingModalShown = @appStorage.getValue 'isOnboardingModalShown'

         callback()
+
+
+  bindMachineEvents: (machineItem) ->
+
+    actionRequiredStates = [Pending, Stopping, Stopped, Terminating, Terminated]
+
+    kd.getSingleton 'computeController'
+
+      .on "public-#{machineItem._id}", (event) =>
+
+        if event.status in actionRequiredStates
+          @showStateMachineModal machineItem, event
+
+
+  showStateMachineModal: (machineItem, event) ->
+
+    KodingKontrol.dcNotification?.destroy()
+    KodingKontrol.dcNotification = null
+
+    machineItem.getBaseKite( no ).disconnect()
+
+    if @machineStateModal
+      @machineStateModal.updateStatus event
+    else
+      {state}   = machineItem.status
+      container = @getView()
+      @createMachineStateModal { state, container, machineItem }

   createMachineStateModal: (options = {}) ->


StatusBar: Show full path as minified as file name.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -956,10 +956,10 @@
     {status} = @statusBar

     text = if component is 'editor'
-      {cursor, file} = data
+      { cursor, file } = data
       """

#{++cursor.row}:#{++cursor.column}

-
#{file.name}

+
#{FSHelper.minimizePath file.path}

       """

     else if component is 'terminal' then "Terminal on #{data.machineName}"


alt+letter highlights first item in menu

--- client/jquery-dyalog.coffee
+++ client/jquery-dyalog.coffee
@@ -63,6 +63,9 @@
       $(document)
         .on 'keydown', '*', 'f10', -> $m.children().eq(0).addClass('m-open').find('a').eq(1).focus(); false
         .on 'keyup keypress', (e) -> !isAccessKeyEvent e
-        .keydown (e) -> if isAccessKeyEvent e then $m.find("[accessKey=#{String.fromCharCode(e.which).toLowerCase()}]:visible").mousedown(); false
         .mousedown (e) -> (if !$(e.target).closest('.menu').length then mFocus null); return
+        .keydown (e) ->
+          if isAccessKeyEvent e
+            $x = $m.find "[accessKey=#{String.fromCharCode(e.which).toLowerCase()}]:visible"
+            if $x.length then $x.mousedown(); $x.parent().find('a').eq(1).focus(); false
       return


Fix typo in Gradebook.coffee

Fix typo in Gradebook.coffee

Error was introduced while re-adding a SFU MOD during merge

--- app/coffeescripts/gradebook2/Gradebook.coffee
+++ app/coffeescripts/gradebook2/Gradebook.coffee
@@ -782,6 +782,7 @@
         resizable: false
         sortable: true
         formatter: @htmlContentFormatter
+      },
       # SFU MOD CANVAS-188 Add SIS ID column
       {
         id: 'sis_id'


add build task to gulpfile and change task dependencies

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -22,14 +22,11 @@
 SRC = './src/*.coffee'
 LIBDIR = './lib'

-gulp.task 'default', [ 'standalone' ], ->
+gulp.task 'default', ->
   gulp.src SRC
     .pipe coffee()
       .on 'error', gutil.log
     .pipe gulp.dest LIBDIR
-
-gulp.task 'watch', [ 'default' ], ->
-  gulp.watch [ './src/*' ] , [ 'default' ]

 gulp.task 'standalone', ->
   browserify ENTRY, {
@@ -53,6 +50,11 @@
     }
     .pipe gulp.dest DISTDIR

+gulp.task 'build', [ 'default', 'standalone' ]
+
+gulp.task 'watch', [ 'build' ], ->
+  gulp.watch [ './src/*' ] , [ 'build' ]
+
 gulp.task 'test', ->
   gulp.src './test/*_test.coffee', { read: false }
     .pipe mocha {
@@ -73,10 +75,10 @@
     .pipe rename 'lcov.info'
     .pipe streamify coveralls()

-gulp.task 'testwatch', ['test', 'default'], ->
+gulp.task 'testwatch', ['test' ], ->
   gulp.watch ['./src/*', './test/*'], ['test', 'default']

-gulp.task 'testvisual', [ 'default' ], ->
+gulp.task 'testvisual', [ 'watch' ], ->
   server = new stat.Server '.'
   require('http').createServer( (request, response) ->
     request.addListener( 'end', ->


Default task in Gulpfile.

Default task in Gulpfile.

--- Gulpfile.coffee
+++ Gulpfile.coffee
@@ -30,11 +30,7 @@
 console.timeEnd 'Loading deps'

-gulp.task 'default', ->
-	console.log ''
-	console.log chalk.green " Specify a task, like #{chalk.blue 'build'} or #{chalk.blue 'watch'}."
-	console.log chalk.green " Run #{chalk.blue 'gulp --tasks'} for some hints."
-	console.log ''
+gulp.task 'default', seq 'check', 'build', 'test'

 gulp.task 'build', ->
@@ -80,8 +76,6 @@
 gulp.task 'watch', ['build'], ->
 	return gulp.watch ['./browser.coffee', './lib/validation.coffee'], ['build-and-notify']

-
-# Experimental stuff

 gulp.task 'check', ->
 	mustcontain = require './gulp-tasks/mustcontain.coffee'


Moves sharify below artsyXappMiddleware so that GRAVITY_XAPP_TOKEN gets set

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -21,13 +21,6 @@
   Backbone.sync = require "backbone-super-sync"
   Backbone.sync.editRequest = (req) -> req.set('X-XAPP-TOKEN': sharify.data.GRAVITY_XAPP_TOKEN)

-  # Setup some initial data for shared modules
-  app.use sharify
-    GRAVITY_URL: GRAVITY_URL
-    JS_EXT: (if "production" is NODE_ENV then ".min.js.gz" else ".js")
-    CSS_EXT: (if "production" is NODE_ENV then ".min.css.gz" else ".css")
-    ASSET_PATH: ASSET_PATH
-
   # General settings
   app.use express.favicon()
   app.use express.logger('dev')
@@ -40,6 +33,13 @@
     clientSecret: CLIENT_SECRET
     sharifyData: sharify.data
   ) unless app.get('env') is 'test'
+
+  # Setup some initial data for shared modules
+  app.use sharify
+    GRAVITY_URL: GRAVITY_URL
+    JS_EXT: (if "production" is NODE_ENV then ".min.js.gz" else ".js")
+    CSS_EXT: (if "production" is NODE_ENV then ".min.css.gz" else ".css")
+    ASSET_PATH: ASSET_PATH

   # Development only
   if "development" is NODE_ENV


Added comments to browserify failed tests

--- test/spec/plugins/localization_and_defaults.tests.coffee
+++ test/spec/plugins/localization_and_defaults.tests.coffee
@@ -237,6 +237,7 @@
     assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
     done()

+  # TODO: fix these tests in browserify
   it.skip '2. internals test (Coffeescript inheritance)', (done) ->
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager
@@ -308,6 +309,7 @@
     assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
     done()

+  # TODO: fix these tests in browserify
   it.skip '3. internals test (Javascript inheritance)', (done) ->
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager
@@ -872,6 +874,7 @@
       assert.equal(kb.statistics.registeredStatsString('all released'), 'all released', "Cleanup: stats"); kb.statistics = null
       done()

+  # TODO: fix these tests in browserify
   it.skip '3. internals test (Coffeescript inheritance)', (done) ->
     kb.statistics = new kb.Statistics() # turn on stats
     kb.locale_manager = locale_manager


Fix long highscore names

Fix long highscore names

--- app/client/game/result.coffee
+++ app/client/game/result.coffee
@@ -45,6 +45,10 @@
       FlashMessages.sendError "Dit navn kan ikke være tomt"
       $('button#submit-highscore').attr 'disabled', false
       return
+    else if name.length > 32
+      FlashMessages.sendError "Dit navn er for langt"
+      $('button#submit-highscore').attr 'disabled', false
+      return

     Meteor.call 'submitHighscore', name, currentGameId(), (error, result) ->
       if error?


record whether a pack is temporary in the pack itself

using the expiresAt field no longer determines if the pack is
temporary because archived packs have an expiresAt field added when
they are retrieved from s3

--- app/coffee/PackManager.coffee
+++ app/coffee/PackManager.coffee
@@ -92,6 +92,7 @@
 				end_ts: last.meta.end_ts
 			v: first.v
 			v_end: last.v
+			temporary: temporary
 		if temporary
 			newPack.expiresAt = new Date(Date.now() + 7 * DAYS)
 		logger.log {project_id, doc_id, newUpdates}, "inserting updates into new pack"


Added one more function to the search page design

--- app/webroot/coffee/views/search/search.coffee
+++ app/webroot/coffee/views/search/search.coffee
@@ -76,15 +76,23 @@
       # Make jQuery UI call our selection methods.
       selecting: (e, ui) =>
         $(ui.selecting).parents('.result').addClass('selected')
+        $(ui.selecting).parents('.result').children('.select-button').html 'DE-SELECT'
+        $(ui.selecting).parents('.result').children('.select-button').addClass 'de-select'
         @afterSelection()
       selected: (e, ui) =>
         $(ui.selected).parents('.result').addClass('selected')
+        $(ui.selected).parents('.result').children('.select-button').html 'DE-SELECT'
+        $(ui.selected).parents('.result').children('.select-button').addClass 'de-select'
         @afterSelection()
       unselecting: (e, ui) =>
         $(ui.unselecting).parents('.result').removeClass('selected')
+        $(ui.unselecting).parents('.result').children('.select-button').html 'SELECT'
+        $(ui.unselecting).parents('.result').children('.select-button').removeClass 'de-select'
         @afterSelection()
       unselected: (e, ui) =>
         $(ui.unselected).parents('.result').removeClass('selected')
+        $(ui.unselected).parents('.result').children('.select-button').html 'SELECT'
+        $(ui.unselected).parents('.result').children('.select-button').removeClass 'de-select'
         @afterSelection()

   # Make an instance of our Search utility and setup endless scrolling.


Only using the follow button for a single object

--- vendor/assets/javascripts/amoeba-components/combo_button.js.coffee
+++ vendor/assets/javascripts/amoeba-components/combo_button.js.coffee
@@ -1,12 +1,8 @@
 class Amoeba.Components.ComboButton
   constructor: (@$container, @options) ->
-    if @$container.length == 1
-      @on = if @$container.hasClass(@options.offState) then false else true
-      @$button = @$container.find(@options.button)
-      @bind()
-    else
-      @$container.each (i, container) =>
-        new @constructor($(container), @options)
+    @on = if @$container.hasClass(@options.offState) then false else true
+    @$button = @$container.find(@options.button)
+    @bind()

   bind: ->
     @$button.on('click', @onClick)


Layer: Fixed positioning, if CSS sets maxWidth or maxHeight on Layer (like in Tooltip).

--- src/base/Layer/Layer.coffee
+++ src/base/Layer/Layer.coffee
@@ -872,9 +872,13 @@
 			top: vp.layer_pos.top + body_scroll_top
 			left: vp.layer_pos.left + body_scroll_left
 			minWidth: minWidth
-			maxWidth: vp.width + vp.overlap_width - (vp.layer_pos.left - vp.left)
-			maxHeight: vp.height + vp.overlap_height - (vp.layer_pos.top - vp.top)
 			margin: 0
+
+		if not dim_layer.computedStyle.maxWidth
+			set_css.maxWidth = vp.width + vp.overlap_width - (vp.layer_pos.left - vp.left)
+
+		if not dim_layer.computedStyle.maxHeight
+			set_css.maxHeight =  vp.height + vp.overlap_height - (vp.layer_pos.top - vp.top)

 		if placement == "c"
 			is_fixed = true


Fix translations for manually sorted lists

--- app/controllers/tasks.coffee
+++ app/controllers/tasks.coffee
@@ -181,9 +181,18 @@

     else
       for task in tasks
-        # Translations
-        task.notesplaceholder = $.i18n._("Notes")
-        task.dateplaceholder = $.i18n._("Due Date")
+        # DRY, MUCH. SORRY WORLD :(
+        task.notesplaceholder = $.i18n._ "Notes"
+        task.dateplaceholder = $.i18n._ "Due Date"
+        task.checkboxalttext = $.i18n._ "Mark as completed"
+        task.lowalttext = $.i18n._ "Set priority to low"
+        task.mediumalttext = $.i18n._ "Set priority to medium"
+        task.highalttext = $.i18n._ "Set priority to high"
+
+        task.dateValue = Task.prettyDate(new Date(task.date)).words
+        task.dateClass = Task.prettyDate(new Date(task.date)).className
+
+        task.listName = List.find(task.list).name if list.id is "all"

         html = @template(task) + html


Add mixin-related core extension plugin tests.

Factor out assertGeneralPlugin.

--- tests/js/core.plugin.coffee
+++ tests/js/core.plugin.coffee
@@ -26,6 +26,26 @@
           classNames:
             someElement: 'foo'

+      @mixins = {}
+
+      $.createMixin @mixins, 'base',
+        init: ->
+          @trigger 'did-init'
+
+      $.createMixin @mixins, 'someMixin',
+        decorate: ->
+          @on 'did-init', => @initSomeMixin()
+        initSomeMixin: ->
+          @someMixinProperty = 'foo'
+        someMixinMethod: -> 'foo'
+
+      $.createMixin @mixins, 'someOtherMixin',
+        decorate: ->
+          @on 'did-init', => @initSomeOtherMixin()
+        initSomeOtherMixin: ->
+          @someOtherMixinProperty = 'bar'
+        someOtherMixinMethod: -> 'bar'
+
       @baseCreateOptions =
         name: 'somePlugin'
         namespace: @namespace
@@ -36,19 +56,42 @@

       @$someElement = $ '<div>'

-  QUnit.test 'createPlugin with apiClass and baseMixins', (assert) ->
-    hlf.createPlugin @baseCreateOptions
+  assertGeneralPlugin = (assert) ->
     assert.ok @$someElement.somePlugin,
       'Plugin method should have been created and attached to jQuery elements.'
     @$someElement.somePlugin()
     result = @$someElement.somePlugin()
-    assert.ok result instanceof SomePlugin,
-      'Plugin method should return instance upon re-invocation without any parameters.'
     assert.hasFunctions result, ['evt', 'attr', 'cls', 'debugLog', 'select'],
       'Plugin instance should have generated API additions.'
     assert.hasOwnProperties result, _.keys(@namespace.defaults),
       'Plugin instance should have options merged in as properties.'
     assert.ok result.$someElement,
       'Plugin instance should have auto-selected sub elements based on selectors option.'
+    return result
+
+  QUnit.test 'createPlugin with apiClass and baseMixins', (assert) ->
+    hlf.createPlugin @baseCreateOptions
+    result = assertGeneralPlugin.call @, assert
+    assert.ok result instanceof SomePlugin,
+      'Plugin method should return instance upon re-invocation without any parameters.'
+
+  QUnit.test 'createPlugin with apiMixins and baseMixins', (assert) ->
+    createOptions = @baseCreateOptions
+    createOptions.apiClass = null
+    createOptions.apiMixins = @mixins
+    createOptions.baseMixins.push 'data', 'event'
+    hlf.createPlugin createOptions
+    result = assertGeneralPlugin.call @, assert
+    assert.hasFunctions result, ['data', 'on', 'off', 'trigger'],
+      'Base mixin methods should have been added to instance.'
+    assert.strictEqual result.someMixinMethod(), 'foo',
+      'Mixin method attached to instance should work.'
+    assert.strictEqual result.someOtherMixinMethod(), 'bar',
+      'Mixin method attached to instance should work.'
+    assert.strictEqual result.someMixinProperty, 'foo',
+      'Mixin property should have been set on deferred initialization.'
+    assert.strictEqual result.someOtherMixinProperty, 'bar',
+      'Mixin property should have been set on deferred initialization.'
+

   yes


Update list of PHP versions

Update list of PHP versions


update docs with new ruby versions

update docs with new ruby versions


Get rid of comment.

Get rid of comment.

--- tests/scripts/fuzzer.coffee
+++ tests/scripts/fuzzer.coffee
@@ -21,7 +21,6 @@
       window.Fuzzer[deltaRef] =
         new window.Tandem.Delta(d.startLength, d.endLength, d.ops)

-    # Create a delta from a Ruby hash
     createDelta: (template) ->
       startLength = template['startLength']
       endLength = template['endLength']


remove old lighting layer spec

remove old lighting layer spec

--- spec/javascripts/layers/lighting_spec.js.coffee
+++ spec/javascripts/layers/lighting_spec.js.coffee
@@ -1,7 +0,0 @@
-describe "Jax.Material.Lighting", ->
-  matr = null
-  beforeEach ->
-    matr = new Jax.Material layers: [ { type: 'Lighting' } ]
-
-  it "with no light sources", ->
-    new Jax.Mesh.Sphere(material: matr)


sftp in menu

sftp in menu

--- menus/sftp-deployment.cson
+++ menus/sftp-deployment.cson
@@ -31,19 +31,19 @@
         { 'type': 'separator' }
     ]

-    'menu': [
-        {
-            'label': 'Packages'
+'menu': [
+    {
+        'label': 'Packages'
+        'submenu': [
+            'label': 'FTP/SFTP'
             'submenu': [
-                'label': 'FTP/SFTP'
-                'submenu': [
-                    { 'label': 'Map to remote...', 'command': 'sftp-deployment:mapToRemote' }
-                    { 'type': 'separator' }
-                    { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
-                    { 'label': 'Upload open files', 'command': 'sftp-deployment:uploadOpenFiles' }
-                    { 'type': 'separator' }
-                    { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
-                ]
+                { 'label': 'Map to remote...', 'command': 'sftp-deployment:mapToRemote' }
+                { 'type': 'separator' }
+                { 'label': 'Upload current file', 'command': 'sftp-deployment:uploadCurrentFile' }
+                { 'label': 'Upload open files', 'command': 'sftp-deployment:uploadOpenFiles' }
+                { 'type': 'separator' }
+                { 'label': 'Download current file', 'command': 'sftp-deployment:downloadCurrentFile' }
             ]
-        }
-    ]
+        ]
+    }
+]


remove debugging logging

remove debugging logging

--- app/assets/javascripts/droom/forms.js.coffee
+++ app/assets/javascripts/droom/forms.js.coffee
@@ -270,7 +270,6 @@
   $.fn.remote_link = (callback) ->
     @
       .on 'ajax:beforeSend', (event, xhr, settings) ->
-        console.log "remote_link.beforeSend"
         $(@).addClass('waiting')
         xhr.setRequestHeader('X-PJAX', 'true')
       .on 'ajax:error', (event, xhr, status) ->
@@ -588,7 +587,6 @@

     send: () =>
       formData = new FormData @_form.get(0)
-      console.log "formdata!", formData
       @xhr = new XMLHttpRequest()
       @xhr.onreadystatechange = @update
       @xhr.upload.onprogress = @progress
@@ -712,12 +710,10 @@
       @_notice.removeClass('erratic successful').addClass(cssclass).text(message)

     submittable: () =>
-      console.log "√ submittable"
       @submit.removeClass("unavailable")
       @blocked = false

     unsubmittable: () =>
-      console.log "x unsubmittable"
       @submit.addClass("unavailable")
       @blocked = true

@@ -732,7 +728,6 @@
       @confirmation.val() is @field.val()

     stumbit: (e) =>
-      console.log "Stumbit!", @blocked
       if @blocked
         e.preventDefault()


fixed popups

fixed popups

--- app/assets/javascripts/maps-search.js.coffee
+++ app/assets/javascripts/maps-search.js.coffee
@@ -39,7 +39,8 @@
     image: itinerary.user.profile_picture
   google.maps.event.addListener customMarker, 'click', ->
     icare.infoWindow.setContent customMarker.options.infoWindowContent
-    icare.infoWindow.open icare.map, customMarker
+    icare.infoWindow.setPosition customMarker.position
+    icare.infoWindow.open icare.map
   marker = new google.maps.Marker
     icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=A|FF0000|000000'
   directionsPath = new google.maps.Polyline


Fixing the 'Select none' button

Fixing the 'Select none' button

--- app/assets/javascripts/submissions.js.coffee
+++ app/assets/javascripts/submissions.js.coffee
@@ -2,6 +2,6 @@

 jQuery ->
   # Attach event listeners
-  $('#reviews-select-all').click(-> $('#submissions_table input.review_check').each((i, element) -> element.checked = true ))
-  $('#reviews-select-finished').click(-> $('#submissions_table input.review_check_finished').each((i, element) -> element.checked = true; ))
-  $('#reviews-select-none').click(-> $('#submissions_table input.review_check').each((i, element) -> element.checked = false))
+  $('#reviews-select-finished').click(-> $('#submissions_table input.review_check_finished').prop('checked', true))
+  $('#reviews-select-all').click(-> $('#submissions_table input.review_check').prop('checked', true))
+  $('#reviews-select-none').click(-> $('#submissions_table input.review_check').prop('checked', false))


removed spurious callbacks

removed spurious callbacks

--- evolution.coffee
+++ evolution.coffee
@@ -680,7 +680,6 @@
       influence = @affinity other
       if influence
         data = @otherData other
-        @reactToOther other, data
         influence /= Math.pow( data.distance, 2 )
         influence *= @g()
         [ xa, ya ] = data.vector influence
@@ -701,7 +700,6 @@
         vx *= f
         vy *= f
       @velocity = [ vx, vy ]
-      @postReact()
       @universe.moveThing @
   # influencing entities nearby
   auditoryRange: -> @genes.auditoryRange[0]
@@ -710,9 +708,6 @@
     seen = {}
     nearby = @universe.near @, 1, @auditoryRange(), seen, candidates
     nearby.concat @universe.near @, @angle, @visualRange(), seen, candidates
-  # reaction hook for prepare for call to reactToOther
-  postReact: ->
-  # reaction hook for changing state based on other reacted to
   reactToOther: ( other, data ) ->
   prey: (other) ->
     false


Minor fixes and improvements

Minor fixes and improvements

--- app/coffee/modules/common/attachments.coffee
+++ app/coffee/modules/common/attachments.coffee
@@ -61,10 +61,10 @@
                     _.sorBy($scope.attachments, 'order')

         ## Total attachments counter
-        $scope.$watch "attachmentsCount", (attachmentsCount) ->
-            $el.find("span.attachments-num").html(attachmentsCount)
-
-        ## See deprecated attachments
+        $scope.$watch "attachmentsCount", (count) ->
+            $el.find("span.attachments-num").html(count)
+
+        ## Show/Hide deprecated attachments
         $scope.showDeprecatedAttachments = false

         $scope.$watch "deprecatedAttachmentsCount", (deprecatedAttachmentsCount) ->
@@ -83,9 +83,11 @@

             if $scope.showDeprecatedAttachments
                 target.find("span.text").html("- hide deprecated attachments") # TODO: i18n
+                                        .prop("title", "hide deprecated attachments") # TODO: i18n
                 $el.find("div.single-attachment.deprecated").removeClass("hidden")
             else
                 target.find("span.text").html("+ show deprecated attachments") # TODO: i18n
+                                        .prop("title", "show deprecated attachments") # TODO: i18n
                 $el.find("div.single-attachment.deprecated").addClass("hidden")

         ## On destroy
@@ -104,7 +106,7 @@
 ## Attachment Directive
 #############################################################################

-AttachmentDirective = ($log, $repo, $rs, $confirm) ->
+AttachmentDirective = ($log, $repo, $confirm) ->
     singleAttachment = _.template("""

@@ -139,7 +141,7 @@

     """) # TODO: i18n

-    link = ($scope, $el, $attrs, $model) ->
+    link = ($scope, $el, $attrs) ->
         $ctrl = $el.controller()

         render = (attachment, isEditable=false) ->
@@ -205,9 +207,9 @@
             newIsDeprecated = $el.find("input[name='is-deprecated']").prop("checked")

             if newDescription != attachment.description
-                attachment.setAttr("description", newDescription)
+                attachment.description = newDescription
             if newIsDeprecated != attachment.is_deprecated
-                attachment.setAttr("is_deprecated", newIsDeprecated)
+                attachment.is_deprecated = newIsDeprecated

             onSuccess = ->
                 $ctrl.loadAttachments(attachment.object_id)
@@ -216,17 +218,13 @@
             onError = ->
                 $confirm.notify("error")

-            if attachment.isModified()
-                $repo.save(attachment).then(onSuccess, onError)
+            $repo.save(attachment).then(onSuccess, onError)

         ## On destroy
         $scope.$on "$destroy", ->
             $el.off()

-    return {
-        link: link,
-        require: "ngModel"
-    }
-
-module.directive("tgAttachment", ["$log", "$tgRepo", "$tgResources", "$tgConfirm",
+    return {link: link}
+
+module.directive("tgAttachment", ["$log", "$tgRepo", "$tgConfirm",
                                   AttachmentDirective])


Fix an error that appeared in NewRelic

Fix an error that appeared in NewRelic

In newrelic, there was a few errors that looked like:
Error: this.initHeaderDropMenus is not a function

at HTMLDivElement.<anonymous> in
https://du11hjcvx0uqb.cloudfront.net/br/dist/webpack-production/gradebook.bundle-bf95a5d074.js
Line 13143 : Column 6

This fixes that

Test plan:
* I don’t exactly know how to reproduce it other than that I saw it in
  NewRelic and I can see in the code why it would happen and how we
  could fix it. But the old gradebook page should work

Change-Id: I861426cea54d683819675b56801b3f36a3e0f90f
Reviewed-on: https://gerrit.instructure.com/195529
Reviewed-by: Jeremy Neander <20252aad5efdc666f083d82ab63ed0f9f72a043e@instructure.com>
Tested-by: Jenkins
QA-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/gradebook/Gradebook.coffee
+++ app/coffeescripts/gradebook/Gradebook.coffee
@@ -842,6 +842,7 @@
       )()
       @initHeaderDropMenus()
       originalStopFn = $headers.sortable 'option', 'stop'
+      gb = this
       (fixupStopCallback = ->
         $headers.sortable 'option', 'stop', (event, ui) ->
           # we need to set the items selector back to the default because slickgrid's 'stop'
@@ -850,7 +851,7 @@
           $headers.sortable 'option', 'items', originalItemsSelector
           returnVal = originalStopFn.apply(this, arguments)
           makeOnlyAssignmentsSortable() # set it back
-          @initHeaderDropMenus()
+          gb.initHeaderDropMenus()
           fixupStopCallback() # originalStopFn re-creates sortable widget so we need to re-fix
           returnVal
       )()


use the correct inverse :stuck_tongue_out:

--- app/models/invitation.coffee
+++ app/models/invitation.coffee
@@ -4,8 +4,8 @@

   role : DS.attr 'number'
   email: DS.attr 'string'
-  user : DS.belongsTo 'user', inverse: 'collaborations'
-  project : DS.belongsTo 'project', inverse: 'collaborations'
+  user : DS.belongsTo 'user', inverse: 'invitations'
+  project : DS.belongsTo 'project', inverse: 'invitations'

 `export default Invitation`


extracting point

extracting point

--- src/point.coffee
+++ src/point.coffee
@@ -201,5 +201,7 @@
       p2.y - p1.y,
       p2.x - p1.x
     )
+
+  window["Point"] = Point
 )()


edit workflows request if query

edit workflows request if query

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -9,6 +9,7 @@
 {sugarClient} = require 'panoptes-client/lib/sugar'
 classNames = require 'classnames'
 getWorkflowsInOrder = require '../../lib/get-workflows-in-order'
+isAdmin = require '../../lib/is-admin'

 counterpart.registerTranslations 'en',
   project:
@@ -87,8 +88,12 @@
       @context.geordi?.remember projectToken: nextProps.project?.slug

     # Only call to get workflow if we know if there is a user or not and the project is finished loading
-    if nextContext.initialLoadComplete and not nextProps.loading and nextProps.preferences and @state.activeWorkflows.length is 0
-      @getAllWorkflows(nextProps.project)
+    if nextContext.initialLoadComplete and not nextProps.loading and nextProps.preferences and @state.activeWorkflows.length is 0 and not @state.loadingSelectedWorkflow
+
+      if nextProps.location.query?.workflow? and ('allow workflow query' in nextProps.project.experimental_tools or @checkUserRoles(nextProps.project, nextProps.user))
+        @getAllWorkflows(nextProps.project, { id: nextProps.location.query.workflow })
+      else
+        @getAllWorkflows(nextProps.project)

     if nextProps.preferences?.preferences? and @state.selectedWorkflow?
       if nextProps.preferences?.preferences.selected_workflow isnt @state.selectedWorkflow.id
@@ -118,10 +123,9 @@
       .then (pages) =>
         @setState {pages}

-  getAllWorkflows: (project) ->
+  getAllWorkflows: (project, query = { active: true }) ->
     @setState { loadingSelectedWorkflow: true }
-
-    getWorkflowsInOrder(project, { active: true })
+    getWorkflowsInOrder(project, query)
       .then (activeWorkflows) =>
         @setState { activeWorkflows }
       .then =>
@@ -130,9 +134,9 @@
         @getSelectedWorkflow(project, @props.preferences)

   getSelectedWorkflow: (project, preferences) ->
-    # preference user selected workflow, then project owner set workflow, then default workflow
+    # preference workflow query, then user selected workflow, then project owner set workflow, then default workflow
     # if none of those are set, select random workflow
-    if @props.location.query?.workflow? and 'allow workflow query' in @props.project.experimental_tools
+    if @props.location.query?.workflow? and ('allow workflow query' in @props.project.experimental_tools or @checkUserRoles(project, @props.user))
       preferredWorkflowID = @props.location.query.workflow
       unless preferences?.preferences.selected_workflow is preferredWorkflowID
         @props.onChangePreferences 'preferences.selected_workflow', preferredWorkflowID
@@ -183,6 +187,16 @@

   redirectClassifyLink: (redirect) ->
     "#{redirect.replace(/\/?#?\/+$/, "")}/#/classify"
+
+  checkUserRoles: (project, user) ->
+    getUserRoles = project.get('project_roles', user_id: user.id)
+      .then ([userRoles]) =>
+        userRoles.roles
+      .catch =>
+        []
+
+    getUserRoles.then (userRoles) =>
+      isAdmin() or 'owner' in userRoles or 'collaborator' in userRoles

   render: ->
     projectPath = "/projects/#{@props.project.slug}"


#95 consistency

#95 consistency

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -49,7 +49,7 @@
         editor.setCursorBufferPosition(cursorPosition)
       for row in foldStartRows.reverse() when not editor.isFoldedAtBufferRow(row)
         editor.foldBufferRow(row)
-      editor.element.setScrollTop(scrollTop)
+      editorElement.setScrollTop(scrollTop)

     # [BUG?] atom-narrow#95
     # When editor is not active on narrow:close, immediate scrollTop setting get editor content blank.


allow arbitary length foldPrefix

allow arbitary length foldPrefix

--- lib/utils.coffee
+++ lib/utils.coffee
@@ -283,7 +283,7 @@
           # Let's strip the “^” character from our original line, for later use.
           line = line.replace blockStrip, '$1'
           # Also strip it from our `blockline`.
-          blockline = blockline[1...]
+          blockline = blockline[foldPrefix.length...]

         # Check if this block-comment stays embedded in the code.
         else if ignorePrefix? and blockline.indexOf(ignorePrefix) is 0
@@ -296,7 +296,7 @@
           # Let's strip the “}” character from our original line, for later use.
           line = line.replace blockStrip, '$1'
           # Also strip it from our `blockline`.
-          blockline = blockline[1...]
+          blockline = blockline[foldPrefix.length...]

       # This flag is triggered above.
       if inBlock


Better debugging support (handle exceptions in release mode only - we want good stack traces when in development)

--- src/common/mecha.log.coffee
+++ src/common/mecha.log.coffee
@@ -1,8 +1,24 @@
 # Log messages to the console if it is available
 # See http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
+
+mechaDebug = true

 mecha.log = (if console? and console.log? then () -> console.log.apply console, arguments else () -> return)
 mecha.logInternalError = (if console? and console.error? then () -> console.error.apply console, arguments else () -> return)
 mecha.logApiError = (if console? and console.error? then () -> console.error.apply console, arguments else () -> return)
 mecha.logApiWarning = (if console? and console.warn? then () -> console.warn.apply console, arguments else () -> return)
+mecha.logException = (functionName, error) ->
+  logArgs = ["Uncaught exception in `#{functionName}`:\n", "#{error.message}\n"]
+  logArgs.push error.stack if error.stack?
+  mecha.logInternalError logArgs...
+  throw error

+safeExport = (name, f) ->
+  if mechaDebug
+    f
+  else
+    return ->
+      try
+        f arguments...
+      catch error
+        mecha.logException name, error


Memoize per_page only for current path

Memoize per_page only for current path

--- app/assets/javascripts/ab_admin/core/init.js.coffee
+++ app/assets/javascripts/ab_admin/core/init.js.coffee
@@ -15,7 +15,7 @@
     $(document).trigger('admin:list_init')

     $('.per_page').click ->
-      $.cookie('pp', $(this).data('val'))
+      $.cookie('pp', $(this).data('val'), {path: window.location.pathname})

     $('#list').on 'click', 'tr.list_edit .form_cancel', ->
       e.preventDefault()


Don't delete last field of user, clear it

--- app/assets/javascripts/incident_reports/edit.coffee
+++ app/assets/javascripts/incident_reports/edit.coffee
@@ -39,8 +39,9 @@

 deleteFields = (fieldsSelector) ->
   fields = $(fieldsSelector).last()
-  fields.find('input').remove()
-  fields.remove()
+  fields.find('input').val('').prop 'checked', false
+  if $(fieldsSelector).length > 1
+    fields.remove()

 deleteWitnessFields = (event) ->
   event.preventDefault()


That didn't work

That didn't work

--- app/assets/javascripts/incident_reports/edit.coffee
+++ app/assets/javascripts/incident_reports/edit.coffee
@@ -40,7 +40,7 @@
 deleteFields = (fieldsSelector, fieldsCheckbox) ->
   fields = $(fieldsSelector).last()
   fields.find('input').val('').prop 'checked', false
-  if $(fieldsSelector).length === 1
+  if $(fieldsSelector).length == 1
     $(fieldsCheckbox).prop 'checked', false
   if $(fieldsSelector).length > 1
     fields.remove()


allow user to see all papers with a role

assumes properly permissions papers are pushed

--- app/assets/javascripts/routes/index_route.js.coffee
+++ app/assets/javascripts/routes/index_route.js.coffee
@@ -7,8 +7,7 @@
     @store.find('commentLook') # don't wait to fulfill
     controller.set('model', model)
     papers = @store.filter 'litePaper', (p) ->
-      checkRole = (role) -> p.get('roles').contains(role)
-      checkRole('My Paper') || checkRole('Collaborator') || checkRole('Reviewer')
+      !Ember.isEmpty(p.get('roles'))

     controller.set('papers', papers)
     controller.set('unreadComments', @store.all('commentLook'))


Add sensible default to config

Add sensible default to config

--- src/recipes/config.cookbook.template.cson
+++ src/recipes/config.cookbook.template.cson
@@ -1 +1 @@
-recipes_path: ""+recipes_path: "_workshop/recipes"


Cleaner iteration over script body

Cleaner iteration over script body

--- src/robot.coffee
+++ src/robot.coffee
@@ -257,7 +257,7 @@
       throw err if err?

       currentSection = null
-      for i, line of body.split "\n"
+      for line in body.split "\n"
         break unless line[0] is '#' or line.substr(0, 2) is '//'

         # remove leading '# '
@@ -283,7 +283,7 @@
       if currentSection is null
         @logger.info "#{path} is using deprecated documentation syntax"
         scriptDocumentation.commands = []
-        for i, line of body.split("\n")
+        for line in body.split("\n")
           break    if not (line[0] == '#' or line.substr(0, 2) == '//')
           continue if not line.match('-')
           cleanedLine = line[2..line.length].replace(/^hubot/i, @name).trim()


Add Polish translations

Add Polish translations

--- src/javascripts/i18n.coffee
+++ src/javascripts/i18n.coffee
@@ -1,9 +1,9 @@
 class I18n
   # List of locales that follow BCP-47
-  SUPPORTED_LOCALES = ['en-US', 'de-DE', 'es-ES', 'nl-NL']
+  SUPPORTED_LOCALES = ['en-US', 'de-DE', 'es-ES', 'nl-NL', 'pl-PL']
   # E.g a user with en-GB will get the language 'en'
   # The languages are in ISO 639-1
-  SUPPORTED_LANGUAGES = ['en', 'de', 'es', 'nl']
+  SUPPORTED_LANGUAGES = ['en', 'de', 'es', 'nl', 'pl']

   # locale<string> BCP-47, e.g. "en-US", can be nil
   # defaultLocale<string> BCP-47, e.g. "de-DE", should not be nil
@@ -36,6 +36,7 @@
       when "de" then "de-DE"
       when "es" then "es-ES"
       when "nl" then "nl-NL"
+      when "pl" then "pl-PL"

   t: (key) ->
     keys = key.split('.')
@@ -222,4 +223,47 @@
         podcastOnAlexa: 'Toon Podcast op Alexa'
         subscribe: 'Abonneer'

+    'pl-PL':
+      chaptermarks:
+        show: 'Pokaż znaczniki rozdziałów'
+        title: 'Rozdziały'
+      chromecast:
+        play: 'Odtwórz w chromecast'
+      download:
+        episode: 'Pobierz odcinek'
+      episode_info:
+        more_info: 'Pokaż więcej informacji'
+        title: 'Informacje o odcinku'
+      playlist:
+        show: 'Pokaż listę odtwarzania'
+        title: 'Lista odtwarzania'
+        load_more: 'Załaduj więcej odcinków'
+      progress_bar:
+        switch_time_mode: 'Przełącz tryb wyświetlania czasu'
+      share:
+        copy_episode_link: 'Kopiuj link do odcinka'
+        email: 'E-mail'
+        embed_player: 'Osadź odtwarzacz'
+        episode: 'Odcinek'
+        episode_url: 'Udostępnij link do odcinka'
+        start_at: 'Rozpocznij od'
+      theme:
+        playPause: 'Odtwórz'
+        play: 'Odtwórz'
+        pause: 'Pauza'
+        backward: '10s wstecz'
+        forward: '30s do przodu'
+        changePlaybackSpeed: 'Zmień prędkość odtwarzania'
+      transcript:
+        search: 'Szukaj w transkrypcji'
+        show: 'Pokaż transkrypcję'
+        title: 'Transkrypcja'
+      subscribeBar:
+        allEpisodes: 'Wszystkie odcinki'
+        podcastOnItunes: 'Zobacz podcast na Apple Podcasts'
+        podcastOnSpotify: 'Zobacz podcast na Spotify'
+        podcastOnDeezer: 'Zobacz podcast na Deezer'
+        podcastOnAlexa: 'Zobacz podcast na Alexa'
+        subscribe: 'Subskrybuj'
+
 module.exports = I18n


Scale extent acknowledged in click zoom.

--- app/scripts/alchemy/core/interactions.coffee
+++ app/scripts/alchemy/core/interactions.coffee
@@ -97,26 +97,28 @@
                                                                 scale(#{ d3.event.scale })" )

     clickZoom:  (direction) ->
-                    startTransform = alchemy.vis
-                                            .attr("transform")
-                                            .match(/(-*\d+\.*\d*)/g)
-                                            .map( (a) -> return parseFloat(a) )
-                    endTransform = startTransform
+                    [x, y, scale] = alchemy.vis
+                                           .attr("transform")
+                                           .match(/(-*\d+\.*\d*)/g)
+                                           .map( (a) -> return parseFloat(a) )
+
                     alchemy.vis
                         .attr("transform", ->
                             if direction == "in"
-                                return "translate(#{ endTransform[0..1]}) scale(#{ endTransform[2] = endTransform[2]+0.2 })"
-                            else if direction == "out"
-                                return "translate(#{ endTransform[0..1]}) scale(#{ endTransform[2] = endTransform[2]-0.2 })"
+                                scale += 0.2 if scale < alchemy.conf.scaleExtent[1]
+                                return "translate(#{x},#{y}) scale(#{ scale })"
+                            else if direction == "out"
+                                scale -= 0.2 if scale > alchemy.conf.scaleExtent[0]
+                                return "translate(#{x},#{y}) scale(#{ scale })"
                             else if direction == "reset"
                                 return "translate(0,0) scale(1)"
-                            else
+                            else
                                 console.log 'error'
                             )
                     if not @._zoomBehavior?
                         @._zoomBehavior = d3.behavior.zoom()
-                    @._zoomBehavior.scale(endTransform[2])
-                                   .translate(endTransform[0..1])
+                    @._zoomBehavior.scale(scale)
+                                   .translate([x,y])

     toggleControlDash: () ->
         #toggle off-canvas class on click


add CIDs (inspired by Backbone)

add CIDs (inspired by Backbone)

--- src/spine.coffee
+++ src/spine.coffee
@@ -85,6 +85,7 @@
   @extend Events

   @records: {}
+  @crecords: {}
   @attributes: []

   @configure: (name, attributes...) ->
@@ -99,10 +100,18 @@
   @toString: -> "#{@className}(#{@attributes.join(", ")})"

   @find: (id) ->
+    if ("#{id}").match(/c-\d+/)
+      return @findCID(id)
+
     record = @records[id]
     throw('Unknown record') unless record
     record.clone()
-
+
+  @findCID: (cid) ->
+    record = @crecords[cid]
+    throw('Unknown record') unless record
+    record.clone()
+
   @exists: (id) ->
     try
       return @find(id)
@@ -116,9 +125,10 @@
     records = [records] unless isArray(records)

     for record in records
-      record.newRecord    = false
-      record.id           or= guid()
-      @records[record.id] = record
+      record.newRecord      = false
+      record.id           or= record.cid
+      @records[record.id]   = record
+      @crecords[record.cid] = record

     @trigger('refresh', not options.clear and records)
     @
@@ -201,9 +211,6 @@
   @fromForm: ->
     (new this).fromForm(arguments...)

-  @guid: ->
-    guid()
-
   # Private

   @recordsValues: ->
@@ -214,6 +221,11 @@

   @cloneArray: (array) ->
     (value.clone() for value in array)
+
+  @idCounter: 0
+
+  @uid: ->
+    @idCounter++

   # Instance

@@ -221,7 +233,7 @@

   constructor: (atts) ->
     super
-    @ids = []
+    @cid = 'c' + @constructor.uid()
     @load atts if atts

   isNew: () ->
@@ -251,8 +263,8 @@
     result

   eql: (rec) ->
-    rec and rec.constructor is @constructor and
-      (rec.id is @id or @id in rec.ids or rec.id in @ids)
+    rec and rec.constructor is @constructor and
+      (rec.id is @id or rec.cid is @cid)

   save: (options = {}) ->
     unless options.validate is false
@@ -275,7 +287,6 @@
     @save(options)

   changeID: (id) ->
-    @ids.push(@id)
     records = @constructor.records
     records[id] = records[@id]
     delete records[@id]
@@ -285,6 +296,7 @@
   destroy: (options = {}) ->
     @trigger('beforeDestroy', options)
     delete @constructor.records[@id]
+    delete @constructor.crecords[@cid]
     @destroyed = true
     @trigger('destroy', options)
     @trigger('change', 'destroy', options)
@@ -295,6 +307,7 @@
     result = new @constructor(@attributes())
     if newRecord is false
       result.newRecord = @newRecord
+      result.cid       = @cid
     else
       delete result.id
     result
@@ -336,11 +349,14 @@

   create: (options) ->
     @trigger('beforeCreate', options)
-    @id          = @constructor.guid() unless @id
+    @id          = @constructor.uid() unless @id
     @newRecord   = false
-    records      = @constructor.records
-    records[@id] = @dup(false)
-    clone        = records[@id].clone()
+
+    record       = @dup(false)
+    @constructor.records[@id]  = record
+    @constructor.crecords[@id] = record
+
+    clone        = record.clone()
     clone.trigger('create', options)
     clone.trigger('change', 'create', options)
     clone
@@ -474,13 +490,6 @@
 makeArray = (args) ->
   Array.prototype.slice.call(args, 0)

-guid = ->
-  'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy]/g, (c) ->
-    r = Math.random() * 16 | 0
-    v = if c is 'x' then r else r & 3 | 8
-    v.toString 16
-  .toUpperCase()
-
 # Globals

 Spine = @Spine   = {}


move tests to hengist

move tests to hengist


GitProvider: code improvements of GitHubProvider

--- workers/social/lib/social/models/gitprovider/githubprovider.coffee
+++ workers/social/lib/social/models/gitprovider/githubprovider.coffee
@@ -33,14 +33,20 @@

     { repos } = gh
     { TEMPLATE_PATH, README_PATH } = Constants
-    repos.getContent { user, repo, path: TEMPLATE_PATH }, (err, templateData) ->
-      return callback err  if err
+    queue = [
+      (next) ->
+        repos.getContent { user, repo, path: TEMPLATE_PATH }, (err, data) ->
+          return next err  if err
+          next null, helpers.decodeContent data
+      (next) ->
+        repos.getContent { user, repo, path: README_PATH }, (err, data) ->
+          return next()  if err
+          next null, helpers.decodeContent data
+    ]

-      template = helpers.decodeContent templateData
-
-      repos.getContent { user, repo, path: README_PATH }, (err, readmeData) ->
-        readme = helpers.decodeContent readmeData  unless err
-        callback null, { template, readme }
+    return async.series queue, (err, results) ->
+      [ template, readme ] = results
+      callback err, { template, readme }

   importStackTemplateWithRawUrl: (user, repo, path, callback) ->
@@ -49,18 +55,18 @@

     queue = [
       (next) ->
-        templateOptions =
+        options =
           host   : RAW_GITHUB_HOST
           path   : "/#{user}/#{repo}/master/#{TEMPLATE_PATH}"
           method : 'GET'
-        helpers.loadRawContent templateOptions, (template) ->
+        helpers.loadRawContent options, (template) ->
             next null, template
         (next) ->
-          readmeOptions =
+          options =
             host   : RAW_GITHUB_HOST
             path   : "/#{user}/#{repo}/master/#{README_PATH}"
             method : 'GET'
-          helpers.loadRawContent readmeOptions, (readme) ->
+          helpers.loadRawContent options, (readme) ->
             next null, readme
       ]


fix #38 missing  in slider code

fix #38 missing  in slider code

--- src/scripts/models/sliderViewModel.coffee
+++ src/scripts/models/sliderViewModel.coffee
@@ -8,7 +8,7 @@

     # Properties
     @hasjQueryUi = libraries.jqueryUi
-    @value = ko.asObservable if options.value? then options.value else minDefault
+    @value = ko.asObservable if options.value? then options.value else @minDefault
     @min = ko.asObservable if options.min? then options.min else @getMinFromValidationRule()
     @max = ko.asObservable if options.max? then options.max else @getMaxFromValidationRule()
     @disabled = ko.asObservable if options.disabled? then options.disabled else false


fixed coffeelint errors on workers/social/lib/social/models/unsubscribedmail.coffee

--- workers/social/lib/social/models/unsubscribedmail.coffee
+++ workers/social/lib/social/models/unsubscribedmail.coffee
@@ -1,4 +1,4 @@
-{Model} = require 'bongo'
+{ Model } = require 'bongo'

 module.exports = class JUnsubscribedMail extends Model

@@ -16,12 +16,14 @@
         type         : Date
         default      : -> new Date

-  @isUnsubscribed = (email, callback)->
-    @one {email}, (err, unsubscribed)->
+  @isUnsubscribed = (email, callback) ->
+    @one { email }, (err, unsubscribed) ->
       callback err, unsubscribed?

-  @removeFromList = (email, callback=->)->
-    @one {email}, (err, unsubscribed)->
+  @removeFromList = (email, callback = -> ) ->
+    @one { email }, (err, unsubscribed) ->
       return callback err                  if err
       return unsubscribed.remove callback  if unsubscribed
       callback null
+
+


feature: finally, animation works correctly! Only making steps and saving has left

--- coffee/application.coffee
+++ coffee/application.coffee
@@ -437,6 +437,7 @@
     E('animate-info').innerHTML = "Start: #{showNode @startChain}, #{JSON.stringify @startOffset}<br/>End: #{showNode @endChain}, #{JSON.stringify @endOffset}"

   animate: (observer, stepsPerGen, generations, callback)->
+    return unless @startChain? and @endChain?
     #global (surreally big) view matrix is:
     #
     # Moffset * M(chain)
@@ -452,23 +453,33 @@
     # T = MoffsetEnd * M(chainEnd + invChain(chainStart) * MoffsetStart^-1

     #Not very sure but lets try
-    Mdelta = appendInverseChain(@endChain, @startChain,appendRewrite).repr(tessellation.group)
-
+    #Mdelta = appendInverseChain(@endChain, @startChain,appendRewrite).repr(tessellation.group)
+    inv = (c) -> inverseChain(c, appendRewrite)
+    app = (c1, c2) -> appendChain(c1,c2, appendRewrite)
+
+    # e, S bad
+    # S, e bad
+    #
+    # E, s good? Seems to be good, but power calculation is wrong.
+    Mdelta = app(inv(@endChain), @startChain ).repr(tessellation.group)
+
+
     T = M.mul(M.mul(@endOffset, Mdelta), M.hyperbolicInv(@startOffset))

-    dT = M.powerPade T, 1.0/(stepsPerGen * generations)
-    console.log "Offset matrix determined: #{JSON.stringify T}"
-
-    observer.navigateTo @startChain, @startOffset
-    drawEverything()
+    #dT = M.powerPade T, 1.0/(stepsPerGen * generations)
+    #console.log "Offset matrix determined: #{JSON.stringify T}"
+
     steps = generations * stepsPerGen
-    timerId = setInterval (->
+    timerId = setInterval (=>
+      observer.navigateTo @startChain, @startOffset
+      p = 1.0-steps/(stepsPerGen * generations)
+      dT = M.powerPade T, p
       observer.modifyView dT
+      drawEverything()
       steps -=1
-      console.log "offset! remaining steps: #{steps}"
-      console.log "cur tfm: #{JSON.stringify observer.getViewOffsetMatrix()}"
-      drawEverything()
-      if steps <= 0
+      #console.log "offset! remaining steps: #{steps}"
+      #console.log "cur tfm: #{JSON.stringify observer.getViewOffsetMatrix()}"
+      if steps < 0
         clearInterval timerId
         console.log "End animation"
       ), 500


Show at most 1000 markers on map

Show at most 1000 markers on map

--- src/views.coffee
+++ src/views.coffee
@@ -35,7 +35,7 @@
             querys = params.split('&')
             paramsArray = querys[0].split '=', 2
             unitCollection = new models.UnitList()
-            dataFilter = {}
+            dataFilter = page_size: 1000
             dataFilter[paramsArray[0]] = paramsArray[1]
             unitCollection.fetch(
                 data: dataFilter


Removing unused console.log.

Removing unused console.log.

--- routers/get-specific-tweet.coffee
+++ routers/get-specific-tweet.coffee
@@ -4,7 +4,6 @@
 module.exports = (app) ->
   app.get "/:username/status/:id", (req, res) ->
     url = "https://twitter.com/" + req.params.username + "/status/" + req.params.id
-    console.log url
     request url, (err, rrr, body) ->
       $ = cheerio.load(body)
       tweet = $(".permalink-tweet .tweet-text").html()
@@ -20,4 +19,4 @@
         userName: userName
         timeStamp: timeStamp
         tweet: tweet
-        avatarURL: avatarURL+        avatarURL: avatarURL


ENHANCE: Modify recorder service to add capability of chunk sending

--- src/app/common/services/recorder-service.coffee
+++ src/app/common/services/recorder-service.coffee
@@ -196,7 +196,6 @@

     # Called each time a chunk of recording becomes available
     _onDataAvailable: (evt) ->
-      console.log("data available")
       @chunks.push(evt.data)
       @chunkType = evt.data.type

@@ -210,8 +209,6 @@
         blob: blob
       }

-      console.log(recording)
-
       @em.dispatchEvent(new CustomEvent('recording', { detail: { recording: recording } }))

       @chunks = []
@@ -219,7 +216,10 @@
       if (@state != 'inactive')
         return

-      # cleanup
+      _cleanup()
+      return
+
+    _cleanup: () ->
       @chunkType = null
       if (@destinationNode)
         @destinationNode.disconnect()


Remove suppressUpdates

Remove suppressUpdates

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -22,7 +22,6 @@
   selectOnMouseMove: false
   batchingUpdates: false
   updateRequested: false
-  updatesSuppressed: false
   cursorsMoved: false
   selectionChanged: false
   selectionAdded: false
@@ -182,16 +181,10 @@
     @props.parentView.trigger 'editor:display-updated'

   requestUpdate: ->
-    unless @updatesSuppressed
-      if @batchingUpdates
-        @updateRequested = true
-      else
-        @forceUpdate()
-
-  suppressUpdates: (fn) ->
-    @updatesSuppressed = true
-    fn()
-    @updatesSuppressed = false
+    if @batchingUpdates
+      @updateRequested = true
+    else
+      @forceUpdate()

   getRenderedRowRange: ->
     {editor, lineOverdrawMargin} = @props


Remove `getGutterDecorations()`

Remove `getGutterDecorations()`

--- src/editor-component.coffee
+++ src/editor-component.coffee
@@ -277,17 +277,6 @@
         filteredDecorations[screenRow].push new Decoration(null, class: 'foldable')

     filteredDecorations
-
-  getGutterDecorations:  (renderedRowRange) ->
-    [renderedStartRow, renderedEndRow] = renderedRowRange
-
-    bufferRows = editor.bufferRowsForScreenRows(renderedStartRow, renderedEndRow - 1)
-
-    decorations = {}
-    for bufferRow in bufferRows
-      decorations[bufferRow] = editor.decorationsForBufferRow(bufferRow, 'gutter')
-      decorations[bufferRow].push {class: 'foldable'} if editor.isFoldableAtBufferRow(bufferRow)
-    decorations

   observeEditor: ->
     {editor} = @props


also including ranking error message

also including ranking error message

--- jsapp/xlform/src/model.row.coffee
+++ jsapp/xlform/src/model.row.coffee
@@ -10,6 +10,7 @@
 $rowDetail = require './model.rowDetail'
 $choices = require './model.choices'
 $skipLogicHelpers = require './mv.skipLogicHelpers'
+_t = require('utils').t

 module.exports = do ->
   row = {}
@@ -190,7 +191,7 @@
       @_extendAll(rr)
       rankConstraintMessageKey = 'kobo--rank-constraint-message'
       if !rr.get(rankConstraintMessageKey)
-        rr.set(rankConstraintMessageKey, 'Items cannot be selected more than once')
+        rr.set(rankConstraintMessageKey, _t('Items cannot be selected more than once'))

     _beginEndKey: ->
       'rank'


Added json method.

Added json method.

Code from [https://github.com/mauricemach/zappa/pull/123](mauricemach/zappa#123)

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -259,6 +259,7 @@
           response: res
           next: next
           send: -> res.send.apply res, arguments
+          json: -> res.json.apply res, arguments
           redirect: -> res.redirect.apply res, arguments
           render: ->
             if typeof arguments[0] isnt 'object'


Using coffeescript!

Using coffeescript!

--- src/zappa.coffee
+++ src/zappa.coffee
@@ -364,9 +364,7 @@
               for k, v of arguments[0]
                 render.apply @, [k, v]

-        render = (name,opts,fn) ->
-
-          opts ?= {}
+        render = (name,opts = {},fn) ->

           report = fn ? next


no message

no message

--- src/game/states/game.coffee
+++ src/game/states/game.coffee
@@ -46,11 +46,6 @@
 class Game

   @player = null
-<<<<<<< HEAD
-  @successTxt = null
-  @timeTimerStart = null
-=======
->>>>>>> 328f0b7d78706196e0e6c3dda98514c86f483ddb

   create: ->
     #setup game window
@@ -86,28 +81,16 @@
      	 @xdistance = Math.abs(@player.x - star.x)
      	 @ydistance = Math.abs(@player.y - star.y)

-<<<<<<< HEAD
-      #if Player on star
-     if @xdistance < 10 && @ydistance < 10
-        if not @printed
-     	   @star.alpha = 1
-     	   @printed = true
-      #else if Player not on star
-     else
-        if @printed
-         @game.add.tween(@star).to({alpha:0.1},3000,Phaser.Easing.Cubic.Out,true)
-     	   #@star.alpha = 0.1
-     	   @printed = false
-=======
      	 if @xdistance < 20 && @ydistance < 20
+        #if Player on star
      	    if not @printed[i]
-     	       star.alpha = 1
+     	       @game.add.tween(@star).to({alpha:0.1},1000,Phaser.Easing.Cubic.Out,true)
      	       @printed[i] = true
      	 else
+       #if Player not on star
      	    if @printed[i]
-     	       star.alpha = 0.3
+     	       @game.add.tween(@star).to({alpha:0.1},3000,Phaser.Easing.Cubic.Out,true)
      	       @printed[i] = false
->>>>>>> 328f0b7d78706196e0e6c3dda98514c86f483ddb

      if @cursors.left.isDown
         @player.body.moveLeft(200)


no lag

no lag

--- src/game/states/game.coffee
+++ src/game/states/game.coffee
@@ -102,6 +102,7 @@
      	       @game.add.tween(star).to({alpha:1},200,Phaser.Easing.Quintic.Out,true)
      	 else
        #if Player not on star
+               @game.Tween.removeAllTweens();
      	       @game.add.tween(star).to({alpha:0.1},15000,Phaser.Easing.Quintic.Out,true)

      	 if won
@@ -109,7 +110,7 @@
      	       won = false

      if won
-     	@add.text(10, 10, "Congratulations! Level Complete!", { font: "15px Arial", fill: "#ff0044", align: "center" })
+     	@add.text(230, 4, "Congratulations! Level Complete!", { font: "15px Arial", fill: "#ff0044", align: "center" })
      	@levelcomplete = true

      if @cursors.left.isDown


Fix for #112: Handle missing js grammar

Fix for #112: Handle missing js grammar

--- lib/Components/ConsolePane.coffee
+++ lib/Components/ConsolePane.coffee
@@ -10,6 +10,7 @@
   jsGrammar = atom.grammars.grammarForScopeName('source.js')

   tokenizeLine = (text) ->
+    return h('div.line', {}, text) unless jsGrammar
     {tokens} = jsGrammar.tokenizeLine(text)
     h('div.line', {}, [
       h('span.test.shell-session', {}, tokens.map((token) ->


Remove redundant settings button

Remove redundant settings button

--- lib/available-package-view.coffee
+++ lib/available-package-view.coffee
@@ -48,12 +48,6 @@
             @button type: 'button', class: 'btn', outlet: 'uninstallButton', => # TODO hide in installedpackagesview
               @span class: 'icon icon-trashcan'
               @text "Uninstall"
-            @button type: 'button', class: 'btn', outlet: 'learnMoreButton', =>
-              @span class: 'icon icon-book'
-              @text "Learn more"
-            @button type: 'button', class: 'btn', outlet: 'settingsButton', =>
-              @span class: 'icon icon-gear'
-              @text "Settings"
             @button outlet: 'enablementButton', class: 'btn btn-default', =>
               @span class: 'icon icon-playback-pause'
               @span class: 'disable-text', 'Disable'
@@ -79,9 +73,6 @@
       @uninstall()

     @packageName.on 'click', =>
-      @parents('.settings-view').view()?.showPanel(@pack.name, {back: 'Installed Packages'})
-
-    @settingsButton.on 'click', =>
       @parents('.settings-view').view()?.showPanel(@pack.name, {back: 'Installed Packages'})

     @learnMoreButton.on 'click', =>
@@ -123,7 +114,6 @@
     @subscribeToPackageEvent 'package-installed package-install-failed theme-installed theme-install-failed', (pack, error) =>
       @installButton.prop('disabled', false)
       unless error?
-        @settingsButton.show()
         @installButton.hide()
         @uninstallButton.show()

@@ -140,13 +130,10 @@
       unless error?
         @installButton.show()
         @uninstallButton.hide()
-        @settingsButton.hide()

     if @isInstalled() or @isDisabled()
       @installButton.hide()
       @uninstallButton.show()
-    else
-      @settingsButton.hide()

   isInstalled: -> atom.packages.isPackageLoaded(@pack.name) and not atom.packages.isPackageDisabled(@pack.name)


fixed bug of notAcked

fixed bug of notAcked

--- lib/clientTransformManager.coffee
+++ lib/clientTransformManager.coffee
@@ -83,7 +83,7 @@

     acknowledge: (ack) =>
       for i in [0...@notAcked.length]
-        if @notAcked[i].state.get @id is ack[@id]
+        if @notAcked[i].state[@id] is ack[@id]
           index = i
           break


example event

example event

--- assets/ng/controllers.coffee
+++ assets/ng/controllers.coffee
@@ -22,6 +22,7 @@

   $scope.setSong = (name) ->
     $scope.t = new Track(null, songs[name])
+    ga('send', 'event', 'setSong', name)

   $scope.chList = ->
     list = []


progress on different load paths

progress on different load paths

--- src/user.iced
+++ src/user.iced
@@ -151,11 +151,31 @@

   #--------------

+  @lookup_username_from_key_id_64 : (ki64, cb) ->
+    esc = make_esc cb, "User::lookup_username_from_key_id_64"
+    log.debug "+ #{ki64}: map to username"
+    args =
+      endpoint : "key/basics"
+      args : { pgp_key_id : ki64 }
+    await req.get args, esc defer body
+    username = body.username
+    log.debug "- #{ki64}: map -> #{username}"
+    cb null, username
+
+  #--------------
+
   @load : ({username,ki64}, cb) ->
     esc = make_esc cb, "User::load"
+    k = if username? then username else "Key: #{ki64}"
     log.debug "+ #{username}: load user"
-    await User.load_from_server {username,ki64}, esc defer remote
+
     await User.load_from_storage {username,ki64}, esc defer local
+
+    # If we need to, get the new username
+    if not username? then username = local?.basics?.username
+    if not username? then await User.lookup_username_from_key_id_64 ki64, esc defer username
+
+    await User.load_from_server {username}, esc defer remote
     changed = true
     force_store = false
     if local?
@@ -173,7 +193,7 @@

   #--------------

-  @load_from_server : ({username,uid}, cb) ->
+  @load_from_server : ({username}, cb) ->
     log.debug "+ #{username}: load user from server"
     args =
       endpoint : "user/lookup"
@@ -188,19 +208,16 @@
   #--------------

   @load_from_storage : ({username, ki64}, cb) ->
-    k = username or ki64
-    log.debug "+ #{k}: load user from local storage"
-    ret = null
-    if uid?
-      await db.get { key : uid }, defer err, row
-    else
-      await db.lookup { type : constants.lookups.username, name: username }, defer err, row
+    name = username or ki64
+    log.debug "+ #{name}: load user from local storage"
+    type = if username? then constants.lookup.username else constants.lookup.key_id_64_to_user
+    await db.lookup { type, name }, defer err, row
     if not err? and row?
       ret = new User row.value
       await ret.load_sig_chain_from_storage defer err
       if err?
         ret = null
-    log.debug "- #{k}: loaded user from local storage"
+    log.debug "- #{name}: loaded user from local storage"
     cb err, ret

   #--------------


operations: rename channel node to "posts"

--- src/local/operations.coffee
+++ src/local/operations.coffee
@@ -60,7 +60,7 @@
         user = @req.actor
         listener = @req.sender
         nodeTypes = [
-                'channel', 'status',
+                'posts', 'status',
                 'geoloc/previous', 'geoloc/current',
                 'geoloc/next', 'subscriptions']
         async.series nodeTypes.map((nodeType) ->