Tooltips are 500ms for now (closes #111)

--- coffee/src/views/controls_view.coffee
+++ coffee/src/views/controls_view.coffee
@@ -240,7 +240,7 @@
 class FullscreenControlView extends IconControlView
   controlName: => 'FullscreenControl'
   className: @classNameExtend 'fullscreen'
-  tooltip: => title: 'Fullscreen', delay: show: 1500
+  tooltip: => title: 'Fullscreen', delay: show: 500
   defaults: => _.extend super,
     icon: 'fullscreen'

@@ -249,7 +249,7 @@
 class EditControlView extends IconControlView
   controlName: => 'EditControl'
   className: @classNameExtend 'edit'
-  tooltip: => title: 'Edit', delay: show: 1500
+  tooltip: => title: 'Edit', delay: show: 500
   defaults: => _.extend super,
     icon: 'edit'

@@ -258,7 +258,7 @@
 class SourcesControlView extends IconControlView
   controlName: => 'SourcesControl'
   className: @classNameExtend 'sources'
-  tooltip: => title: 'Sources', delay: show: 1500
+  tooltip: => title: 'Sources', delay: show: 500
   defaults: => _.extend super,
     icon: 'globe'

@@ -267,7 +267,7 @@
 class PreviousControlView extends IconControlView
   controlName: => 'PreviousControl'
   className: @classNameExtend 'previous'
-  tooltip: => title: 'Previous', delay: show: 1500
+  tooltip: => title: 'Previous', delay: show: 500
   defaults: => _.extend super,
     icon: 'arrow-left'

@@ -276,7 +276,7 @@
 class NextControlView extends IconControlView
   controlName: => 'NextControl'
   className: @classNameExtend 'next'
-  tooltip: => title: 'Next', delay: show: 1500
+  tooltip: => title: 'Next', delay: show: 500
   defaults: => _.extend super,
     icon: 'arrow-right'

@@ -285,7 +285,7 @@
 class GridControlView extends IconControlView
   controlName: => 'GridControl'
   className: @classNameExtend 'grid'
-  tooltip: => title: 'Grid', delay: show: 1500
+  tooltip: => title: 'Grid', delay: show: 500
   defaults: => _.extend super,
     icon: 'th'

@@ -294,7 +294,7 @@
 class PlayControlView extends IconControlView
   controlName: => 'PlayControl'
   className: @classNameExtend 'play'
-  tooltip: => title: 'Play', delay: show: 1500
+  tooltip: => title: 'Play', delay: show: 500
   defaults: => _.extend super,
     icon: 'play'

@@ -303,7 +303,7 @@
 class PauseControlView extends IconControlView
   controlName: => 'PauseControl'
   className: @classNameExtend 'pause'
-  tooltip: => title: 'Pause', delay: show: 1500
+  tooltip: => title: 'Pause', delay: show: 500
   defaults: => _.extend super,
     icon: 'pause'

@@ -312,7 +312,7 @@
 class RandomControlView extends IconControlView
   controlName: => 'RandomControl'
   className: @classNameExtend 'random'
-  tooltip: => title: 'Random', delay: show: 1500
+  tooltip: => title: 'Random', delay: show: 500
   defaults: => _.extend super,
     icon: 'magic'

@@ -349,7 +349,7 @@
 class AcornControlView extends ImageControlView
   controlName: => 'AcornControl'
   className: @classNameExtend 'acorn'
-  tooltip: => title: 'Website', delay: show: 1500
+  tooltip: => title: 'Website', delay: show: 500
   defaults: => _.extend super,
     image: acorn.config.img.acorn

@@ -359,7 +359,7 @@

   controlName: => 'ElapsedTimeControl'

-  tooltip: => title: 'Elapsed Time', delay: show: 1500
+  tooltip: => title: 'Elapsed Time', delay: show: 500
   className: @classNameExtend 'elapsed-time-control-view'


small fixes to javascript validator

small fixes to javascript validator

--- coffeescript/rails.validations.coffee
+++ coffeescript/rails.validations.coffee
@@ -50,7 +50,10 @@
     # we don't want the loop to break out by mistake
     true

-  if valid then form.trigger('form:validate:pass.ClientSideValidations') else form.trigger('form:validate:fail.ClientSideValidations')
+  if valid
+    form.trigger('form:validate:pass.ClientSideValidations')
+  else
+    form.trigger('form:validate:fail.ClientSideValidations')

   form.trigger('form:validate:after.ClientSideValidations')
   valid
@@ -409,7 +412,6 @@
   else
     "//#{window.location.host}/validators/#{validator}"

-
 window.ClientSideValidations.disableValidators = () ->
   return if window.ClientSideValidations.disabled_validators == undefined
   for validator, func of window.ClientSideValidations.validators.remote


bug in example API

bug in example API

--- src/example_api.coffee
+++ src/example_api.coffee
@@ -181,7 +181,8 @@
         status: 200

       update:
-        method: "GET"
+        method: "PUT"
+        request_schema: "user"
         response_schema: "user"
         status: 200


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -132,9 +132,7 @@
 @param {Object} entityData
 ###
 GameObject.construct = (entityData) ->
-  if typeof entityData == "string"
-    entityData.constantize()(entityData)
-  else if entityData.class
+  if entityData.class
     entityData.class.constantize()(entityData)
   else
     GameObject(entityData)


remove non-default units from expected options array

--- spec/javascripts/unit/admin/services/variant_unit_manager_spec.js.coffee
+++ spec/javascripts/unit/admin/services/variant_unit_manager_spec.js.coffee
@@ -43,8 +43,6 @@
     it "returns an array of options", ->
       expect(VariantUnitManager.variantUnitOptions()).toEqual [
         ["Weight (g)", "weight_1"],
-        ["Weight (oz)", "weight_28.35"],
-        ["Weight (lb)", "weight_453.6"],
         ["Weight (kg)", "weight_1000"],
         ["Weight (T)", "weight_1000000"],
         ["Volume (mL)", "volume_0.001"],


geometry_utils: Added v_dist_point_to_rect to r3.coffee

--- engine/geometry_utils/r3.coffee
+++ engine/geometry_utils/r3.coffee
@@ -1,10 +1,13 @@

 {mat3, vec3, vec4, quat} = require 'gl-matrix'

+#Constants
+Z_VECTOR = vec3.fromValues 0,0,1
+
+#Temporal
 m3 = mat3.create()
-Z_VECTOR = vec3.fromValues 0,0,1
-na = vec3.create()
-nb = vec3.create()
+v1 = vec3.create()
+v2 = vec3.create()
 q = quat.create()

 planes_intersection = (out, m)->
@@ -27,17 +30,27 @@
     # d is the director vector of the rect
     # the result will be 2 planes which define the rect, in a
     # 4x2 row-major matrix (or first half of 4x4)
-    vec3.set na, 1,0,0
-    vec3.set nb, 0,1,0
+    vec3.set v1, 1,0,0
+    vec3.set v2, 0,1,0
     quat.rotationTo q, Z_VECTOR, d
-    vec3.transformQuat na, na, q
-    vec3.transformQuat nb, nb, q
-    plane_from_norm_point out, nb, p
+    vec3.transformQuat v1, v1, q
+    vec3.transformQuat v2, v2, q
+    plane_from_norm_point out, v2, p
     out[4] = out[0]
     out[5] = out[1]
     out[6] = out[2]
     out[7] = out[3]
-    plane_from_norm_point out, na, p
+    plane_from_norm_point out, v1, p
     return out

-module.exports = {planes_intersection, plane_from_norm_point, rect_from_dir_point}
+v_dist_point_to_rect = (out, p, rp, dir)->
+    # p is a point
+    # rp is a point of the rect
+    # dir is the director vector of the rect
+
+    vec3.normalize dir, dir
+    vec3.cross out, vec3.sub(v1,p,rp), dir
+    return out
+
+
+module.exports = {planes_intersection, plane_from_norm_point, rect_from_dir_point, v_dist_point_to_rect}


Fixed an uninitialized variable problem

Fixed an uninitialized variable problem

--- src/typhoon/article.coffee
+++ src/typhoon/article.coffee
@@ -24,7 +24,8 @@
   constructor: (data) ->
     data = data.replace /\r\n/g, "\n"
     meta = data.split(/\n\n/, 1).toString()
-
+
+    @_meta = {}
     @body data.substring meta.length + 2

     while match = meta.match /([a-z0-9]+)\:\s*(.*)\s*\n?/i
@@ -52,14 +53,12 @@
     else
       Article._extension

-  _body: ''
   body: (body = null) ->
     if body
       @_body = body
     else
       @_body

-  _meta: {}
   meta: (key = null, value = null) ->
     if value
       @_meta[key] = value


Moved to utils

Moved to utils

--- src/typhoon/patches.coffee
+++ src/typhoon/patches.coffee
@@ -1,88 +0,0 @@
-fs = require 'fs'
-haml = require 'hamljs'
-
-###
-Render a file containing haml
-###
-
-haml.renderFile = (filename, encoding, options, callback) ->
-  options or= {}
-  options.filename or= filename
-  options.cache = true unless options.hasOwnProperty 'cache'
-
-  if haml.cache[filename] && options.cache
-    process.nextTick ->
-      callback null, haml.render(null, options)
-  else
-    fs.readFile filename, encoding, (err, str) ->
-      if err
-        callback err
-      else
-        callback null, haml.render(str, options)
-
-###
-Prefix numbers less than 10 with a 0
-###
-
-Number.prototype.pad = ->
-  if this < 10
-    "0" + @toString()
-  else
-    return @toString()
-
-###
-Pretty dates i.e. January 2, 2011
-###
-
-Date.prototype.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
-Date.prototype.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
-
-Date.prototype.pretty = ->
-  @months[@getUTCMonth()] + ' ' + @getUTCDate() + ', ' + @getUTCFullYear()
-
-Date.prototype.isoformat = ->
-  @getUTCFullYear() + '-' + (@getUTCMonth() + 1).pad() + '-' + @getUTCDate().pad()
-
-###
-Format a date to RFC822 (used in feeds)
-###
-
-Date.prototype.rfc822 = ->
-  @days[@getUTCDay()][0..2] + ', ' +
-  @getUTCDate().pad() + ' ' +
-  @months[@getUTCMonth()][0..2] + ' ' +
-  @getUTCFullYear() + ' ' +
-  @getUTCHours().pad() + ':' +
-  @getUTCMinutes().pad() + ':' +
-  @getUTCSeconds().pad() + ' ' + 'GMT'
-
-###
-Slugify a string
-###
-
-String.prototype.slug = ->
-  str = this
-
-  # Trim spaces
-  str = str.replace /^\s+|\s+$/g, ''
-
-  # Lowercase
-  str = str.toLowerCase()
-
-  # Remove accents by swapping
-  from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'
-  to = 'aaaaeeeeiiiioooouuuunc------'
-
-  for char, i in from
-    str = str.replace new RegExp(char, 'g'), to.charAt i
-
-  # Remove uncatched invalid chars
-  str = str.replace /[^a-z0-9\s\-]/g, ''
-
-  # Collapse spaces and replace with dashes
-  str = str.replace /\s+/g, '-'
-
-  # Collapse dashes
-  str = str.replace /-+/g, '-'
-
-  str


Coffeefy.

Coffeefy.

--- src/coffee/xmlimport.coffee
+++ src/coffee/xmlimport.coffee
@@ -2,7 +2,7 @@
 {parseString} = require 'xml2js'
 Config = require('../config').config
 Rest = require('sphere-node-connect').Rest
-Sync = require("sphere-product-sync").Sync
+Sync = require('sphere-product-sync').Sync
 Q = require('q')

 # Define XmlImport object
@@ -19,7 +19,7 @@
   if data.attachments
     for k,v of data.attachments
       @transform @getAndFix(v), (data)=>
-        @createOrUpdate(data, callback)
+        @createOrUpdate data, callback
   else
     @returnError 'No products given', callback

@@ -37,7 +37,7 @@
     @sync.start p, (diffResult)=>
       console.log "SYNC %j", diffResult
       if diffResult.status
-        callback(diffResult)
+        callback diffResult
       else
         @rest.POST "/products", JSON.stringify(p), (error, response, body)->
           if response.statusCode is 201
@@ -74,12 +74,12 @@
     for k,row of xmljs.row
       v =
         id: 0
-      @mapCategories(row, v)
-      @mapAttributes(row, v)
-      @mapPrices(row, v, customerGroupId)
-      img = @mapImages(row)
-
-      if @isVariant(row)
+      @mapCategories row, v
+      @mapAttributes row, v
+      @mapPrices row, v, customerGroupId
+      img = @mapImages row
+
+      if @isVariant row
         parentId = row.basisUidartnr
         variants[parentId] = [] if not variants[parentId]
         v.id = variants[parentId].length + 2
@@ -96,8 +96,8 @@
           taxCategory:
             typeId: 'tax-category'
             id: taxCategoryId
-          name: @lang(n)
-          slug: @lang(@slugify(n))
+          name: @lang n
+          slug: @lang @slugify(n)
           description: @lang(@val(row, 'description', ''))
           masterVariant: v
           variants: []
@@ -105,7 +105,7 @@
         if id2id[row.uid]
           p.id = id2id[row.uid]

-        @mapCategories(row, p)
+        @mapCategories row, p
         products.push p
         images[row.uid] = []
         images[row.uid].push img
@@ -177,7 +177,7 @@

   numbers = [ 'hight', 'length', 'wide' ]
   for n in numbers
-    v = @val(row, n, '')
+    v = @val row, n, ''
     continue unless v.length > 0
     d =
       name: n
@@ -193,7 +193,7 @@
   for orig,trans of enums
     trans = orig unless trans.length > 0
     v = 'NO'
-    v = 'YES' if @val(row, orig, '') == '1'
+    v = 'YES' if @val(row, orig, '') is '1'
     d =
       name: trans
       value: v
@@ -206,13 +206,13 @@

   p =
     value:
-      centAmount: @getPrice(row, 'priceb2c')
+      centAmount: @getPrice row, 'priceb2c'
       currencyCode: currency
   j.prices.push p

   p =
     value:
-      centAmount: @getPrice(row, 'priceb2b')
+      centAmount: @getPrice row, 'priceb2b'
       currencyCode: currency
     customerGroup:
       typeId: 'customer-group'
@@ -222,7 +222,7 @@
   return if @val(row, 'specialpriceflag', '') is '0'
   p =
     value:
-      centAmount: @getPrice(row, 'specialprice')
+      centAmount: @getPrice row, 'specialprice'
       currencyCode: currency,
     country: country
   j.prices.push p
@@ -233,7 +233,7 @@
 exports.XmlImport.prototype.isVariant = (row)->
   if row.basisUidartnr is undefined
     return false
-  if row.basisUidartnr == row.uid
+  if row.basisUidartnr is row.uid
     return false
   true

@@ -245,13 +245,13 @@
   deferred.promise

 exports.XmlImport.prototype.productType = ->
-  @getFirst('/product-types')
+  @getFirst '/product-types'

 exports.XmlImport.prototype.taxCategory = ->
-  @getFirst('/tax-categories')
+  @getFirst '/tax-categories'

 exports.XmlImport.prototype.customerGroup = ->
-  @getFirst('/customer-groups')
+  @getFirst '/customer-groups'

 exports.XmlImport.prototype.getFirst = (endpoint) ->
   deferred = Q.defer()


migrate load note list to use NoteManager


Remove default mp3 file

Remove default mp3 file

--- app/assets/javascripts/mix.js.coffee
+++ app/assets/javascripts/mix.js.coffee
@@ -126,7 +126,6 @@
     cursorColor: 'navy'
     minimap: true
     minPxPerSec: 100
-  wavesurfer.load '1.mp3'
   wavesurfer.initMinimap
     height: 30
     waveColor: '#ddd'


Use hash-history mode.

Use hash-history mode.

The access method used by some Pancakes users obfuscates application
URLs, which likely means that history-based routing is out.  My hope is
that said mechanism doesn't interact badly with the hash mode.

(If it does, that access method will have to go.)

--- app/assets/javascripts/router.coffee
+++ app/assets/javascripts/router.coffee
@@ -3,7 +3,4 @@
     @resource 'event_searches.show', path: '/:event_search_id'
     @route 'new'

-Pancakes.Router.reopen
-  location: 'history'
-
 # vim:ts=2:sw=2:et:tw=78


remove debugging code

remove debugging code

--- app/components/eui-dropbutton.coffee
+++ app/components/eui-dropbutton.coffee
@@ -19,7 +19,6 @@
           isOpenBinding: 'parent.popupIsOpen'
           actionList: @get('actionList')
           style: 'bubble'
-          test: 'save'

     primaryAction: ->
       @sendAction 'primaryAction.action', @


Fix coupons, Once more with feeling.

Fix coupons, Once more with feeling.

--- app/views/core/SubscribeModal.coffee
+++ app/views/core/SubscribeModal.coffee
@@ -120,6 +120,12 @@
       return unless jqxhr # in case of cancellations
       @onSubscriptionError(jqxhr, 'Failed to finish subscription purchase')

+  makePurchaseOps: ->
+    out = {data: {}}
+    if utils.getQueryVariable('coupon')?
+      out.data.coupon = utils.getQueryVariable('coupon')
+    out
+
   onClickSaleButton: ->
     @playSound 'menu-button-click'
     return @openModalView new CreateAccountModal() if me.get('anonymous')
@@ -136,7 +142,7 @@
       @state = 'purchasing'
       @render()
       # Purchasing a year
-      return Promise.resolve(@yearProduct.purchase(token))
+      return Promise.resolve(@yearProduct.purchase(token, @makePurchaseOps()))
     .then (response) =>
       application.tracker?.trackEvent 'Finished 1 year subscription purchase', value: @purchasedAmount
       me.set 'stripe', response?.stripe if response?.stripe?
@@ -159,7 +165,7 @@
       @state = 'purchasing'
       @render()
       # Purchasing a year
-      return Promise.resolve(@lifetimeProduct.purchase(token))
+      return Promise.resolve(@lifetimeProduct.purchase(token, @makePurchaseOps()))
     .then (response) =>
       application.tracker?.trackEvent 'Finish Lifetime Purchase', value: @purchasedAmount
       me.set 'stripe', response?.stripe if response?.stripe?


Fix /students courses being in wrong order

--- app/views/courses/CoursesView.coffee
+++ app/views/courses/CoursesView.coffee
@@ -12,6 +12,8 @@
 Course = require 'models/Course'
 Classroom = require 'models/Classroom'
 Classrooms = require 'collections/Classrooms'
+Courses = require 'collections/Courses'
+CourseInstances = require 'collections/CourseInstances'
 LevelSession = require 'models/LevelSession'
 Levels = require 'collections/Levels'
 NameLoader = require 'core/NameLoader'
@@ -41,7 +43,7 @@
   initialize: ->
     @classCodeQueryVar = utils.getQueryVariable('_cc', false)
     @courseInstances = new CocoCollection([], { url: "/db/user/#{me.id}/course_instances", model: CourseInstance})
-    @courseInstances.comparator = (ci) -> return ci.get('classroomID') + ci.get('courseID')
+    @courseInstances.comparator = (ci) -> return ci.get('classroomID') + utils.orderedCourseIDs.indexOf ci.get('courseID')
     @listenToOnce @courseInstances, 'sync', @onCourseInstancesLoaded
     @supermodel.loadCollection(@courseInstances, { cache: false })
     @classrooms = new CocoCollection([], { url: "/db/classroom", model: Classroom})
@@ -50,8 +52,9 @@
     @ownedClassrooms = new Classrooms()
     @ownedClassrooms.fetchMine({data: {project: '_id'}})
     @supermodel.trackCollection(@ownedClassrooms)
-    @courses = new CocoCollection([], { url: "/db/course", model: Course})
-    @supermodel.loadCollection(@courses)
+    @courses = new Courses()
+    @courses.fetch()
+    @supermodel.trackCollection(@courses)
     @originalLevelMap = {}
     @urls = require('core/urls')


conditionalize transcribe button on group browse page

--- app/assets/javascripts/components/group-page.cjsx
+++ app/assets/javascripts/components/group-page.cjsx
@@ -221,8 +221,8 @@
       @updateFirstPageArray

-  getStatusMessage: (group) ->
-    if group.status == 'complete'
+  getStatusMessage: (group_status) ->
+    if group_status == 'complete'
       return (

           <svg xmlns='http://www.w3.org/2000/svg' width='19.003' height='19.073' viewBox='0 0 19.003 19.073'>
@@ -234,7 +234,7 @@

       )

-    else if group.status == 'in_progress'
+    else if group_status == 'in_progress'
       return (

           <svg xmlns='http://www.w3.org/2000/svg' width='22' height='19.552' viewBox='0 0 22 19.552'>
@@ -245,7 +245,7 @@

       )

-    else if group.status == 'coming_soon'
+    else if group_status == 'coming_soon'
       return (

           <svg xmlns='http://www.w3.org/2000/svg' width='19' height='18.998' viewBox='0 0 19 18.998'>
@@ -258,6 +258,30 @@
       )
     else
       return ()
+
+  getStatusRegion: (group_status) ->
+    if group_status == 'in_progress'
+      return (
+

+

+
+
Transcribe

+
+

+

+            {@getStatusMessage(group_status)}
+

+

+      )
+    else
+      return (
+

+

+            {@getStatusMessage(group_status)}
+

+

+      )
+

   render: ->
     if ! @state.group?
@@ -308,14 +332,7 @@

-

-
-
Transcribe

-
-

-

-              {@getStatusMessage(@state.group_json)}
-

+            { @getStatusRegion(@state.group_json.status) }


Remove extra "/" in Scenario url_path

Remove extra "/" in Scenario url_path

--- app/assets/javascripts/lib/models/scenario.coffee
+++ app/assets/javascripts/lib/models/scenario.coffee
@@ -13,6 +13,6 @@
       source: 'ETM'

   # Returns the base scenario URL, taking into account CORS support
-  url_path: => App.api.path "/scenarios/#{@api_session_id()}"
+  url_path: => App.api.path "scenarios/#{@api_session_id()}"

   reset: => App.reset_scenario()


Fixed a coffeescript typo

Fixed a coffeescript typo

--- app/assets/javascripts/room_reservation.js.coffee
+++ app/assets/javascripts/room_reservation.js.coffee
@@ -66,7 +66,7 @@
   #initialize_modal_form = ->
   $(document).on 'click', ".launch_modal", ->
     $("#ajax-modal").removeClass("fullscreen")
-    $("#ajax-modal").find(".modal-footer").html$('<button type="button" data-dismiss="modal" class="btn btn-large">Cancel</button>')
+    $("#ajax-modal").find(".modal-footer").html($('<button type="button" data-dismiss="modal" class="btn btn-large">Cancel</button>'))g
     $("#ajax-modal").find(".modal-title").html("Loading...")
     $("#ajax-modal").find(".modal-header").find(".legend, p").remove()
     $("#ajax-modal").find(".modal-body-content").html('')


Add hotkey for "reply with selected text" to Issueable

--- app/assets/javascripts/shortcuts_issueable.coffee
+++ app/assets/javascripts/shortcuts_issueable.coffee
@@ -11,9 +11,35 @@
       $('.js-milestone').select2('open')
       return false
     )
-
+    Mousetrap.bind('r', =>
+      @replyWithSelectedText()
+      return false
+    )
+
     if isMergeRequest
       @enabledHelp.push('.hidden-shortcut.merge_requests')
     else
       @enabledHelp.push('.hidden-shortcut.issues')

+  replyWithSelectedText: ->
+    if window.getSelection
+      selected = window.getSelection().toString()
+      replyField = $('.js-main-target-form #note_note')
+
+      return if selected.trim() == ""
+
+      # Put a '>' character before each non-empty line in the selection
+      quote = _.map selected.split("\n"), (val) ->
+        "> #{val}\n" if val.trim() != ''
+
+      # If replyField already has some content, add a newline before our quote
+      separator = replyField.val().trim() != "" and "\n" or ''
+
+      replyField.val (_, current) ->
+        current + separator + quote.join('') + "\n"
+
+      # Trigger autosave for the added text
+      replyField.trigger('input')
+
+      # Focus the input field
+      replyField.focus()


remove code comment

remove code comment

--- app/assets/javascripts/uploadcare/files.js.coffee
+++ app/assets/javascripts/uploadcare/files.js.coffee
@@ -14,7 +14,6 @@

 namespace 'uploadcare', (ns) ->

-  # backwards compatibility
   ns.fileFrom = (type, data, settings) ->
     converters[type](settings, data)[0].promise()


terminal: call `WebTerm.Terminal#setKeyFocus` if focused

--- client/Terminal/src/Terminal.coffee
+++ client/Terminal/src/Terminal.coffee
@@ -139,7 +139,7 @@

   setFocused: (value) ->
     @cursor.setFocused value
-    KD.utils.defer => @setKeyFocus()
+    if value then KD.utils.defer => @setKeyFocus()

   setSize: (x, y) ->
     return if x is @sizeX and y is @sizeY


utils: slight improvement to .showError()

--- client/app/MainApp/KD.extend.coffee
+++ client/app/MainApp/KD.extend.coffee
@@ -92,7 +92,7 @@
     no

   showError:(err, messages)->
-    return  unless err
+    return no  unless err

     if 'string' is typeof err
       message = err
@@ -123,6 +123,7 @@
     new KDNotificationView {title, content, duration}

     warn "KodingError:", err.message  unless err.name is 'AccessDenied'
+    err?

   getPathInfo: (fullPath)->
     return no unless fullPath


fixed animalEndpointGroup update issues

fixed animalEndpointGroup update issues

--- client/coffee/animalEvidence.coffee
+++ client/coffee/animalEvidence.coffee
@@ -142,7 +142,7 @@

         isValid = NestedCollection.simpleSchema().namedContext().validate(modifier, {modifier: true})
         if isValid
-            NestedCollection.update(@_id, modifier)
+            NestedCollection.update(@_id, modifier, {removeEmptyStrings: false})
             Session.set("nestedEvidenceEditingId", null)
             share.removeNestedFormModal(tmpl)
         else


client: home: put headers to my team

client: home: put headers to my team

--- client/home/lib/myteam/index.coffee
+++ client/home/lib/myteam/index.coffee
@@ -8,6 +8,12 @@
   'Invite Using Slack' : HomeTeamConnectSlack
   'Send Invites'       : HomeTeamSendInvites
   Teammates            : HomeTeamTeammates
+
+header = (title) ->
+  new kd.CustomHTMLView
+    tagName  : 'header'
+    cssClass : 'HomeAppView--sectionHeader'
+    partial  : title

 section = (name, options, data) ->
   new (SECTIONS[name] or kd.View) options or {
@@ -27,7 +33,13 @@
     { groupsController } = kd.singletons

     groupsController.ready =>
+
+      @wrapper.addSubView header  'Invite Using Slack'
+      @wrapper.addSubView section 'Invite Using Slack'
+
+      @wrapper.addSubView header  'Send Invites'
+      @wrapper.addSubView section 'Send Invites'
+
       team = groupsController.getCurrentGroup()
-      @wrapper.addSubView section 'Invite Using Slack'
-      @wrapper.addSubView section 'Send Invites'
+      @wrapper.addSubView header  'Teammates'
       @wrapper.addSubView section 'Teammates', null, team


rm stray console

rm stray console

--- apps/partnerships/client/edit.coffee
+++ apps/partnerships/client/edit.coffee
@@ -21,7 +21,6 @@

 renderPreview = (data) ->
   renderData = JSON.parse JSON.stringify(data)
-  console.log 'renderData', renderData
   $('#partnerships-edit-preview').html(
     template _.extend(renderData, crop: crop, path: location.pathname, subject: sd.SUBJECT)
   )


FIX: Locate tooltip position for alignment bullet chart to mouse

--- src/app/visualisations/partials/directives/alignment-bullet-chart.coffee
+++ src/app/visualisations/partials/directives/alignment-bullet-chart.coffee
@@ -477,6 +477,7 @@
           tooltip.hidden(true)

         bullet.dispatch.on 'elementMousemove.tooltip', (evt) ->
+          tooltip.position({ top: d3.event.pageY, left: d3.event.pageX })()
           tooltip()

         #============================================================


Disable for now, lets keep conversations with users own name.

--- client/landing/app/Applications/Chat.kdapplication/Views/conversationlistitemtitle.coffee
+++ client/landing/app/Applications/Chat.kdapplication/Views/conversationlistitemtitle.coffee
@@ -2,7 +2,7 @@

   constructor:(options = {}, data)->
     options.cssClass = 'chat-item'
-    data = [nick for nick in data when nick isnt KD.nick()].first
+    # data = [nick for nick in data when nick isnt KD.nick()].first
     super

   viewAppended:->


commented

commented

--- lib/wd-sync.coffee
+++ lib/wd-sync.coffee
@@ -13,6 +13,7 @@
   }

 wdSync =
+  # similar to wd
   remote: (args...) ->
     # lookink for sync mode
     mode = 'sync'


KD.extend: #showError log the error itself

In the case of server-side validation errors, it's helpful to inspect the error object itself, because the message "Errors were encountered during validation" is generic, but if you look at the .errors property, you see the actual errors that occurred.

--- client/app/MainApp/KD.extend.coffee
+++ client/app/MainApp/KD.extend.coffee
@@ -122,7 +122,9 @@

     new KDNotificationView {title, content, duration}

-    warn "KodingError:", err.message  unless err.name is 'AccessDenied'
+    unless err.name is 'AccessDenied'
+      warn "KodingError:", err.message
+      error err
     err?

   getPathInfo: (fullPath)->


Use slug.slug instead name.name

Use slug.slug instead name.name

--- client/app/MainApp/mq.config.coffee
+++ client/app/MainApp/mq.config.coffee
@@ -34,7 +34,7 @@
         err = null
         queue = name.slugs.map (slug) => =>
           selector = {}
-          selector[slug.usedAsPath] = name.name
+          selector[slug.usedAsPath] = slug.slug
           @api[slug.constructorName].one? selector, (err, model)->
             if err then callback err
             else


add fat arrow scoping

add fat arrow scoping

--- components/article/client/view.coffee
+++ components/article/client/view.coffee
@@ -75,9 +75,9 @@

   setupPartnerBreadcrumb: =>
     new Partner(id: @article.get('partner_ids')?[0]).fetch
-      success: (partner) ->
+      success: (partner) =>
         new Profile(id: partner.get('default_profile_id')).fetch
-          success: (profile) ->
+          success: (profile) =>
             @$('#article-body-container').addClass('partner').prepend partnerBreadcrumbTemplate
               name: partner.get('name')
               href: profile.href()


KDFramework: curryCssClass issue fixed on KDHeaderView

--- components/header/KDHeaderView.coffee
+++ components/header/KDHeaderView.coffee
@@ -16,13 +16,11 @@
     @$().find('span').html title

   setDomElement:(cssClass = "")->
-    type    = @getOption "type"
-    tagName = switch type
+    {type} = @getOptions()
+    @setOption "tagName", switch type
       when "big"    then "h1"
       when "medium" then "h2"
       when "small"  then "h3"
       else "h4"
-    @setOption "tagName", tagName
-    @setOption "cssClass", @utils.curryCssClass "kdheaderview", @getOption cssClass

-    super
+    super @utils.curryCssClass("kdheaderview", cssClass)


client: welcome: when no stack is available members see pendingStack step rather than buildStack

--- client/home/lib/flux/getters.coffee
+++ client/home/lib/flux/getters.coffee
@@ -1,14 +1,16 @@
 kd = require 'kd'
 welcomeStepsAll = [ 'WelcomeStepsStore' ]
 EnvironmentFlux = require 'app/flux/environment'
+
+
+isAdmin = -> kd.singletons.groupsController.canEditGroup()

 welcomeStepsByRole = [
   EnvironmentFlux.getters.stacks
   welcomeStepsAll
   (stacks, steps) ->
-    { groupsController } = kd.singletons
-    steps = if groupsController.canEditGroup()
+    steps = if isAdmin()
       steps.get('admin').merge steps.get 'common'
     else
       steps.get('member').merge steps.get 'common'
@@ -21,10 +23,11 @@
   (stacks, steps) ->

     if stacks.size and status = stacks.first()?.get 'status'
-      unless status is 'NotInitialized'
+      if status isnt 'NotInitialized'
         steps = steps.delete 'pendingStack'
+        steps = steps.delete 'stackCreation'  unless isAdmin()
     else
-      steps = steps.delete 'pendingStack'
+        steps = steps.delete 'buildStack'

     return steps.sortBy (a) -> a.get('order')
 ]


Server: implemented simple express server

--- client/landing/server/server.coffee
+++ client/landing/server/server.coffee
@@ -1,3 +1,24 @@
+
 do ->

-  console.log 'run a server of your choice here!!!'+  express = require 'express'
+  gutil   = require 'gulp-util'
+
+  app     = express()
+  log     = (color, message) -> gutil.log gutil.colors[color] message
+
+  app.use '/', express.static "#{__dirname}/../static"
+
+  app.get '*', (req, res) ->
+    {url}      = req
+    console.log url, "/#!#{url}"
+    redirectTo = "/#!#{url}"
+
+    res.header 'Location', redirectTo
+    res.send 301
+
+  app.listen 3000
+
+  log 'green', "HTTP server for #{__dirname}/static/ is ready at localhost:3000"
+
+  return


Klient: keep track of opened/terminated sessions in localStorage

--- client/Main/kite/kites/klient.coffee
+++ client/Main/kite/kites/klient.coffee
@@ -39,10 +39,7 @@
   init: ->

     @connect()  unless @_connectAttempted
-
-    if @terminalSessions.length is 0 and not @_fetchingSessions
-    then @fetchTerminalSessions()
-    else Promise.resolve()
+    Promise.resolve()

   # setTransport is used to override the setTransport method in KodingKite
@@ -69,8 +66,10 @@

       .then (remote) =>

+        @addToActiveSessions remote.session
+
         if @terminalSessions.length is 0
-          @fetchTerminalSessions()
+          @fetchTerminalSessions remote.session
         else
           unless remote.session in @terminalSessions
             @terminalSessions.push remote.session
@@ -86,13 +85,17 @@

       .then (state) =>

+        @removeFromActiveSessions session
+
         @terminalSessions = @terminalSessions.filter (currentSession)->
           session isnt currentSession

         return state

-  fetchTerminalSessions: ->
+  fetchTerminalSessions: (session)->
+
+    return Promise.resolve()  if @_fetchingSessions

     @_fetchingSessions = yes

@@ -101,6 +104,12 @@
     .then (sessions)=>

       @terminalSessions = sessions
+
+      if session and session not in @terminalSessions
+        @terminalSessions.push session
+
+      @syncSessionsWithLocalStorage()
+
       @_fetchingSessions = no

     .timeout 10000
@@ -108,5 +117,47 @@
     .catch (err)=>

       # Reset current sessions if fails
-      @terminalSessions = []
+      if err.message is 'no sessions available'
+        @terminalSessions = if session then [session] else []
+      else
+        @terminalSessions = []
+
       @_fetchingSessions = no
+
+      @syncSessionsWithLocalStorage()
+
+
+
+  getLocalStorage = ->
+
+    return KD.singletons.localStorageController.storage 'Klient', '1.0'
+
+  setActiveSessions = (sessions)->
+
+    getLocalStorage().setValue 'activeSessions', sessions
+
+
+  getActiveSessions: ->
+
+    return (getLocalStorage().getValue 'activeSessions') ? []
+
+
+  syncSessionsWithLocalStorage: ->
+
+    setActiveSessions @getActiveSessions().filter (session)=>
+      session in @terminalSessions
+
+
+  addToActiveSessions: (session)->
+
+    activeSessions = @getActiveSessions()
+    activeSessions.push session  unless session in activeSessions
+
+    setActiveSessions activeSessions
+
+
+  removeFromActiveSessions: (session)->
+
+    activeSessions = @getActiveSessions().filter (old)-> session isnt old
+
+    setActiveSessions activeSessions


Add test files to account/lib/test/index.coffee

--- client/account/lib/test/index.coffee
+++ client/account/lib/test/index.coffee
@@ -1,3 +1,5 @@
 require './accountsessionlistwrapper.test'
 require './accountsshkeylistcontroller.test'
-require './accountsshkeylist.test'+require './accountsshkeylist.test'
+require './accountlinkedaccountslistcontroller.test'
+require './accountlinkedaccountslist.test'


Use expire client ID utility

Use expire client ID utility

--- client/app/lib/maincontroller.coffee
+++ client/app/lib/maincontroller.coffee
@@ -13,6 +13,7 @@
 whoami                   = require './util/whoami'
 checkFlag                = require './util/checkFlag'
 setVersionCookie         = require './util/setVersionCookie'
+expireClientId           = require './util/expireClientId'
 ActivityController       = require './activitycontroller'
 AppStorageController     = require './appstoragecontroller'
 ApplicationManager       = require './applicationmanager'
@@ -200,9 +201,7 @@

     # this is last guard that we can take for guestuser issue ~ GG
     if account.profile?.nickname is "guestuser"
-      kookies.expire 'clientId'
-      global.location.href = '/'
-      return
+      return expireClientId()

     globals.userAccount = account
     connectedState.connected = yes


Hotfix: wrong link to My Way

Hotfix: wrong link to My Way

--- client/src/app/game/home/home.coffee
+++ client/src/app/game/home/home.coffee
@@ -83,10 +83,10 @@
             description: "Feed the animals coming down the train"
           }
           {
-            title: "My Way"
+            title: "Red My Way"
             author: "Dioptre"
             screenshot: "myWay.png"
-            id: "b14ed49c932dab87"
+            id: "bb23ce4b9da32bab"
             description: "Program your robot to complete the puzzles"
           }
         ]


remove redundant line

remove redundant line

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -159,7 +159,6 @@
         @utils.defer ->
           lastRoute = KD.getSingleton("router").visitedRoutes.last

-          lastRoute = KD.getSingleton("router").visitedRoutes.last
           if lastRoute and /^\/(?:Reset|Register|Verify|Confirm)\//.test lastRoute
             lastRoute = "/Activity"


Resurrection: Check for splitview existence.

--- client/ide/lib/workspace/idelayoutmanager.coffee
+++ client/ide/lib/workspace/idelayoutmanager.coffee
@@ -379,9 +379,10 @@
     for splitView in splitViews
       { first } = splitView.panels

-      data.sizes.push
-        width   : first.getWidth()
-        height  : first.getHeight()
+      if first
+        data.sizes.push
+          width   : first.getWidth()
+          height  : first.getHeight()

     return data


tellkite getCommandFailover fixed

tellkite getCommandFailover fixed

--- client/landing/app/MainApp/account-mixins.coffee
+++ client/landing/app/MainApp/account-mixins.coffee
@@ -1,10 +1,10 @@
 AccountMixin = do ->
-
+
   init:(api)->
     {JAccount, JGuest} = api
-
+
     JGuest::fetchNonce = ->
-
+
     nonces = []

     fetchNonces = (callback)->
@@ -15,14 +15,14 @@
         else
           nonces = nonces.concat moreNonces
         callback nonces
-
+
     fetchNonce = (callback)->
       nonce = nonces.shift()
       if nonce? then callback nonce
       else fetchNonces -> fetchNonce callback
-
+
     JAccount::fetchNonce = fetchNonce
-
+
     JAccount::fetchKiteChannelName = (kiteId, callback)->
       @_kiteChannels or= {}
       kiteChannelId = @_kiteChannels[kiteId]
@@ -33,29 +33,31 @@
         else
           @_kiteChannels[kiteId] = kiteChannelId
           callback null, kiteChannelId
-
+
     JAccount::tellKite = do->
       {Scrubber, Store} = bongo.dnodeProtocol

       localStore = new Store
       remoteStore = new Store
-
+
       transports = {}
-
+
       changeTransport = (channelId, {transport})->
         log 'CHANGING TRANSPORT: ', transport
         transports[channelId] = transport
-
+
       resetTransport = (channelId)->
         delete transports[channelId]
-
+
       getCommandFailover =(secretChannelId, options, callback)->
-        count = 0
+
+        options.callbackCount or= 0
+
         kallback = __utils.getCancellableCallback callback
         kanceller = __utils.getCancellableCallback =>
           kallback.cancel()
           resetTransport(secretChannelId)
-          if count++ < 10
+          if options.callbackCount++ < 10
             @tellKite options, callback
           else
             callback new Error "Couldn't connect to backend"
@@ -63,7 +65,7 @@
         ->
           kanceller.cancel()
           kallback arguments...
-
+
       sendScrubbedCommand =(channelId, url, options)->
         transport = transports[channelId]
         data =
@@ -84,16 +86,16 @@
                 xhrFields :
                   withCredentials: yes
               }
-
+
       getKiteUri =(kiteName)-> KD.apiUri+"/1.0/kite/#{kiteName}"
-
+
       sendCommand =(channelId, kiteName, args, callbackId)->
         scrubber = new Scrubber localStore
         scrubber.scrub args, =>
           scrubbed = scrubber.toDnodeProtocol()
           scrubbed.method or= callbackId
           sendScrubbedCommand channelId, getKiteUri(kiteName), scrubbed
-
+
       request = (secretChannelId, callbackId, args)->
         kiteName = secretChannelId.split('-')[1]
         sendCommand secretChannelId, kiteName, [{
@@ -101,7 +103,7 @@
           withArgs  : args
           secretChannelId
         }], callbackId
-
+
       fetchChannel =do ->
         messageHandler =(secretChannelId, args) ->
           callback = localStore.get(args.method)
@@ -129,7 +131,7 @@
                 channel.bind 'error', myMessageHandler
                 channel.bind 'changeTransport', changeTransport.bind null, secretChannelId
                 callback channel
-
+
       (options, callback)->
         account = @
         @fetchKiteChannelName options.kiteName, (err, secretChannelId)->


Add some edge indexes to the tests, refs noflo/noflo#128

--- spec/network.coffee
+++ spec/network.coffee
@@ -72,6 +72,7 @@
             tgt:
               node: 'Bar'
               port: 'in'
+              index: 2
             metadata:
               route: 5
             graph: 'foo'
@@ -126,6 +127,7 @@
             tgt:
               node: 'Bar'
               port: 'in'
+              index: 2
             metadata:
               route: 5
             graph: 'foo'


Update 960-grid-system.coffee

Update 960-grid-system.coffee

--- data/points/960-grid-system.coffee
+++ data/points/960-grid-system.coffee
@@ -2,7 +2,7 @@
 title: "960 Grid System"
 language: [ "css" ]
 function: [ "layout-framework" ]
-theory: [ "grid-design", "responsive-layout" ]
+theory: [ "grid-design" ]
 relevance: [ "bootstrap" ]
 homepage: "http://960.gs/"
 github: "http://github.com/nathansmith/960-grid-system/"


Add some extra error logging to client.coffee

--- bokehjs/src/coffee/common/client.coffee
+++ bokehjs/src/coffee/common/client.coffee
@@ -12,7 +12,14 @@
     @buffers = []

   @assemble : (header_json, metadata_json, content_json) ->
-    new Message(JSON.parse(header_json), JSON.parse(metadata_json), JSON.parse(content_json))
+    try
+      header = JSON.parse(header_json)
+      metadata = JSON.parse(metadata_json)
+      content = JSON.parse(content_json)
+      new Message(header, metadata, content)
+    catch e
+      logger.error("Failure parsing json #{e} #{header_json} #{metadata_json} #{content_json}", e)
+      throw e

   @create_header : (msgtype, options) ->
     header = {
@@ -28,13 +35,16 @@
     new Message(header, {}, content)

   send : (socket) ->
-    header_json = JSON.stringify(@header)
-    metadata_json = JSON.stringify(@metadata)
-    content_json = JSON.stringify(@content)
-    #console.log("h #{header_json} m #{metadata_json} c #{content_json}")
-    socket.send(header_json)
-    socket.send(metadata_json)
-    socket.send(content_json)
+    try
+      header_json = JSON.stringify(@header)
+      metadata_json = JSON.stringify(@metadata)
+      content_json = JSON.stringify(@content)
+      socket.send(header_json)
+      socket.send(metadata_json)
+      socket.send(content_json)
+    catch e
+      logger.error("Error sending ", @, e)
+      throw e

   complete : ->
     if @header? and @metadata? and @content?
@@ -160,12 +170,14 @@
     setTimeout retry, milliseconds

   send : (message) ->
+    if @socket == null
+      throw new Error("not connected so cannot send #{message}")
     message.send(@socket)

   send_with_reply : (message) ->
     promise = new Promise (resolve, reject) =>
       @_pending_replies[message.msgid()] = [resolve, reject]
-      message.send(@socket)
+      @send(message)

     promise.then(
       (message) ->


fix permission problems

fix permission problems

--- both/methods/shift/shiftSchedule.coffee
+++ both/methods/shift/shiftSchedule.coffee
@@ -127,7 +127,6 @@
 		if Meteor.isServer
 			check userId, isExistingUser
 			check { shiftId: shiftId, teamId: teamId }, isExistingShiftAndTeam
-			check { projectId: shift.projectId, userId: Meteor.userId() }, isShiftScheduler
 			check { tagId: shift.tagId, userId: userId }, isTagParticipant

 			for team in shift.teams when team._id == teamId
@@ -182,8 +181,6 @@
 		if Meteor.isServer
 			check userId, isExistingUser
 			check { shiftId: shiftId, teamId: teamId }, isExistingShiftAndTeam
-			check { projectId: shift.projectId, userId: Meteor.userId() }, isShiftScheduler
-			check { tagId: shift.tagId, userId: userId }, isTeamleader

 			for team in shift.teams when team._id == teamId
 				for participant in team.participants


Do a check for null docs

Do a check for null docs

--- app/coffee/HttpController.coffee
+++ app/coffee/HttpController.coffee
@@ -18,7 +18,11 @@
 		logger.log project_id: project_id, "getting all docs"
 		DocManager.getAllDocs project_id, (error, docs = []) ->
 			return next(error) if error?
-			res.json docs.map(HttpController._buildDocView)
+			docViews = []
+			for doc in docs
+				if doc? # There can end up being null docs for some reason :( (probably a race condition)
+					docViews.push HttpController._buildDocView(doc)
+			res.json docViews

 	updateDoc: (req, res, next = (error) ->) ->
 		project_id = req.params.project_id


changed logging to easier to see when the http request starts

--- app/coffee/HttpController.coffee
+++ app/coffee/HttpController.coffee
@@ -50,7 +50,7 @@
 			res.send 400 # Bad Request
 			return

-		logger.log project_id: project_id, doc_id: doc_id, "updating doc"
+		logger.log project_id: project_id, doc_id: doc_id, "got http request to update doc"
 		DocManager.updateDoc project_id, doc_id, lines, (error, modified, rev) ->
 			return next(error) if error?
 			res.json {


Fix project avatar requests Fallback to project.get('avatar')

--- app/components/project-icon.cjsx
+++ app/components/project-icon.cjsx
@@ -26,10 +26,14 @@
   getDetails: (project) ->
     apiClient.type 'avatars'
       .get project.links.avatar.id
-      .catch =>
-        null
       .then (avatar) =>
         @setState {avatar}
+      .catch =>
+        project.get 'avatar'
+          .catch =>
+            null
+          .then (avatar) =>
+            @setState {avatar}

   render: ->
     content = [


enhance: check for valid urls

enhance: check for valid urls

--- app/components/vnc-viewer.coffee
+++ app/components/vnc-viewer.coffee
@@ -156,13 +156,19 @@
     closeModal: ->
       @set "showAPIScanModal", false

-    addUrlFilterAndStartScan: ->
+    addUrlFilterAndStartScan: (callback)->
       form = @$('.input')
       urls = ""
       that = @
       params = jQuery.makeArray(form)
       params.forEach (param) ->
         url = param.value
+        if !hasApiFilter url
+          callback(that.get("notify").error "Please enter any url filter")
+        if isRegexFailed url
+          callback(that.get("notify").error "Please enter a valid url filter")
+        if !isAllowedCharacters url
+          callback(that.get("notify").error "Special Characters not allowed")
         urls = that.get "urls"
         if Ember.isEmpty urls
           urls = url
@@ -170,6 +176,8 @@
           urls = [urls, url].join ','
         that.set "urls", urls
       @set "isApiScanEnabled", true
+      if !hasApiFilter urls
+        return @get("notify").error "Please enter any url filter"
       isApiScanEnabled = @get "isApiScanEnabled"
       project_id = @get "file.project.id"
       apiScanOptions = [ENV.host,ENV.namespace, ENV.endpoints.apiScanOptions, project_id].join '/'


fix typo in guest observer content (resolves #233)

--- app/lib/guest_obs_content.coffee
+++ app/lib/guest_obs_content.coffee
@@ -11,7 +11,7 @@
   }
   {
     title: "#glitch"
-    description: "Occational malfunction of data reduction pipeline."
+    description: "Occasional malfunction of data reduction pipeline."
     example:"images/guest/glitch.png"
   }
   {


[LFTUI-107] correcting issue where streams were appearing with incorrect permission level in user stream listing

--- src/coffee/directives/user_streams.coffee
+++ src/coffee/directives/user_streams.coffee
@@ -12,7 +12,10 @@

     $scope.permissionFor = (stream) ->
       found = null
-      found = p if p.stream == stream.id for p in $scope.permissions
+
+      for p in $scope.permissions
+        found = p if p.stream == stream.id
+
       found

     makeStream = (stream) ->


instructions book: fix VM context menu and open VM terminal

--- client/app/MainApp/book/bookview.coffee
+++ client/app/MainApp/book/bookview.coffee
@@ -356,41 +356,68 @@
       # move cursor to file tree menu
       @pointer.$().offset vmOffset

-  showVMMenu:->
-    @mainView.sidebar.animateLeftNavOut()
-
-    toggle = KD.getSingleton("appManager").get("StartTab").getView().serverContainerToggle
-    @pointer.once 'transitionend', =>
-      if toggle.getState().title is "Show environments"
-        @clickAnimation()
-        toggle.$().click()
-        @utils.wait 2000, =>
-          @destroyPointer()
-      else
-        @destroyPointer()
-
-    @setClass 'moveUp'
-    @mainView.once 'transitionend', =>
-      @utils.wait 1000, =>
-        @pointer.$().offset toggle.$().offset()
+  showVMMenu: (callback) ->
+    startTabView = KD.getSingleton("appManager").get("StartTab").getView()
+
+    presentation = =>
+      defaultVMName = KD.getSingleton("vmController").defaultVmName
+      {machinesContainer} = startTabView.serverContainer
+      for own id, dia of machinesContainer.dias
+        if dia.getData().title is defaultVMName
+          chevron = dia.$ ".chevron"
+
+          @pointer.once "transitionend", =>
+            @clickAnimation()
+            chevron.click()
+
+            contextMenu = $ '.jcontextmenu'
+            contextMenu.addClass "hidden"
+
+            @utils.wait 500, =>
+              contextMenu.offset chevron.offset()
+              contextMenu.removeClass "hidden"
+              chevron.addClass "hidden"
+
+              if callback then callback()
+              else @destroyPointer()
+
+          chevron.removeClass "hidden"
+          chevron.show()
+          @pointer.$().offset chevron.offset()
+          break
+
+    toggle = startTabView.serverContainerToggle
+
+    if toggle.getState().title is "Hide environments"
+      presentation()
+    else
+      @mainView.sidebar.animateLeftNavOut()
+
+      @pointer.once 'transitionend', =>
+        if toggle.getState().title is "Show environments"
+          @clickAnimation()
+          toggle.$().click()
+          @utils.wait 2000, =>
+            presentation()
+        else
+          presentation()
+
+      @mainView.once 'transitionend', =>
+        @utils.wait 1000, =>
+          @pointer.$().offset toggle.$().offset()

   showVMTerminal:->
-    @mainView.sidebar.animateLeftNavOut()
-
-    toggle = KD.getSingleton("appManager").get("StartTab").getView().serverContainerToggle
-    @pointer.once 'transitionend', =>
-      if toggle.getState().title is "Show environments"
-        @clickAnimation()
-        toggle.$().click()
-        @utils.wait 2000, =>
-          @destroyPointer()
-      else
-        @destroyPointer()
-
-    @setClass 'moveUp'
-    @mainView.once 'transitionend', =>
-      @utils.wait 200, =>
-        @pointer.$().offset toggle.$().offset()
+    @showVMMenu =>
+      openTerminal = $($(".jcontextmenu li")[3])
+
+      @pointer.once "transitionend", =>
+        @utils.wait 1000, =>
+          @clickAnimation()
+          @utils.wait 500, =>
+            openTerminal.click()
+            @destroyPointer()
+
+      @pointer.$().offset openTerminal.offset()

   showRecentFilesMenu:->
     @pointer.once 'transitionend', =>


IDEView: getPlusMenuItems: add existing sessions of current machine into plus menu list

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -221,9 +221,28 @@
           @tabView.removePane pane

   getPlusMenuItems: ->
+
+    machine = @getCurrentMachine()
+
+    terminalSessions =
+      "New Session"  :
+        callback     : => @createTerminal machine
+        separator    : yes
+      "Existing Sessions":
+        disabled     : yes
+
+    sessions = machine?.getBaseKite().terminalSessions or []
+
+    unless sessions.length
+      delete terminalSessions["Existing Sessions"]
+
+    sessions.forEach (session, i) =>
+      terminalSessions["Session #{i+1}"] =
+        callback : => @createTerminal machine, null, session
+
     items =
       'New File'          : callback : => @createEditor()
-      'New Terminal'      : callback : => @createTerminal()
+      'New Terminal'      : children : terminalSessions
       # 'New Browser'       : callback : => @createPreview()
       'New Drawing Board' :
         callback          : => @createDrawingBoard()


Nix list anim

Nix list anim

--- client/source/views/entries/list.coffee
+++ client/source/views/entries/list.coffee
@@ -12,18 +12,3 @@
   getTemplateData: ->
     _.extend super,
       bucket: @bucket.toJSON()
-
-  render: ->
-    super
-
-    @$('.center, .entry').each (i, el) ->
-
-      TweenLite.from el, .2,
-        opacity: 0
-        # scale: .99
-        delay: i * .05
-        y: 15
-        ease: Cubic.easeInOut
-        # delay: .15
-        # ease: Elastic.easeOut
-        # easeParams: [.5]


Add comment

Add comment

--- app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_multiword_term.jsx.coffee
+++ app/assets/javascripts/rglossa/react/components/centre/search_inputs/cwb_multiword_term.jsx.coffee
@@ -29,6 +29,14 @@

   componentWillReceiveProps: (nextProps) ->
+    # When we receive new props, we need to add any newly selected POS or
+    # features. We don't need to worry about removing any, because they
+    # can only be removed either by a) clicking on a tag in the list, in
+    # which case bootstrap-tags will remove it itself, or b) editing the
+    # search expression in the regex view, in which case the multiword view
+    # will be mounted anew when we switch to it and the tags will be set up
+    # by componentDidMount.
+
     tagsInput = $(@refs.taglist.getDOMNode()).tags()

     if nextProps.term.pos isnt @props.term.pos


used own version of underscore library

used own version of underscore library

--- generators/app/index.coffee
+++ generators/app/index.coffee
@@ -2,10 +2,16 @@
 chalk       = require 'chalk'
 yosay       = require 'yosay'
 generators  = require 'yeoman-generator'
+_s          = require 'underscore.string'

 module.exports = generators.Base.extend

   initializing : ->
+
+
+  install : ->
+
+    @installDependencies()

   prompting : ->
@@ -20,25 +26,31 @@
       {
         type: 'input'
         name: 'appName'
-        message: 'Your app name?'
+        message: 'Enter your app name'
         default : this.appname
       }
       {
         type: 'input'
         name: 'appWidth'
-        message: 'Your app width?'
+        message: 'Enter desired canvas width'
         default : 640
       }
       {
         type: 'input'
         name: 'appHeight'
-        message: 'Your app height?'
+        message: 'Enter desired canvas height'
         default : 480
       }
+      {
+        type: 'input'
+        name: 'phaserVersion'
+        message: 'Which Phaser version do you want to use?'
+        default : '^2.4.2'
+      }
     ], (props) =>
+      props.appName = _s.slugify props.appName
       @props = props
       done()
-

   configuring : ->
@@ -52,15 +64,15 @@
     app : ->
       @fs.copyTpl(
         @templatePath('_index.html'), @destinationPath('app/index.html'),
-        { appName: @props.appName }
+        { appName : @props.appName }
       )

       @fs.copy @templatePath('assets/**'),                     @destinationPath('app/assets')
       @fs.copy @templatePath('styles/**'),                     @destinationPath('app/styles')
-      @fs.copy @templatePath('scripts/states/boot.coffee'),    @destinationPath('app/scripts/boot.coffee')
-      @fs.copy @templatePath('scripts/states/menu.coffee'),    @destinationPath('app/scripts/menu.coffee')
-      @fs.copy @templatePath('scripts/states/main.coffee'),    @destinationPath('app/scripts/main.coffee')
-      @fs.copy @templatePath('scripts/states/preload.coffee'), @destinationPath('app/scripts/preload.coffee')
+      @fs.copy @templatePath('scripts/states/boot.coffee'),    @destinationPath('app/scripts/states/boot.coffee')
+      @fs.copy @templatePath('scripts/states/menu.coffee'),    @destinationPath('app/scripts/states/menu.coffee')
+      @fs.copy @templatePath('scripts/states/main.coffee'),    @destinationPath('app/scripts/states/main.coffee')
+      @fs.copy @templatePath('scripts/states/preload.coffee'), @destinationPath('app/scripts/states/preload.coffee')

       @fs.copyTpl(
         @templatePath('scripts/game.coffee'), @destinationPath('app/scripts/game.coffee'),
@@ -69,15 +81,18 @@

     bower : ->

-      @fs.copy @templatePath('_bower.json'), @destinationPath('bower.json')
       @fs.copy @templatePath('bowerrc'),     @destinationPath('.bowerrc')
+      @fs.copyTpl(
+        @templatePath('_bower.json'), @destinationPath('bower.json'),
+        { phaserVersion : @props.phaserVersion }
+      )

     npm : ->

       @fs.copyTpl(
         @templatePath('_package.json'), @destinationPath('package.json'),
-        { appName: @props.appName }
+        { appName : @props.appName }
       )

@@ -86,12 +101,8 @@
       @fs.copy @templatePath('_gulpfile.coffee'), @destinationPath('gulpfile.coffee')

-  install : ->
+  end : ->

-    @installDependencies()
+    @log 'Yeoman completed his mission successfully!'

-  end : ->
-
-
-


custom colors: fix hover text color in playlist entries

--- src/javascripts/custom_styles.coffee
+++ src/javascripts/custom_styles.coffee
@@ -78,6 +78,8 @@
       color: #{@textHover};
     }

+    .podcast-player .panels .chaptermarks ul li.active,
+    .podcast-player .panels .playlist ul li.active,
     .podcast-player .panels .transcript .transcript-text li.active {
       color: #{@primary};
     }


FORMS-1049 Add `telusplanet.net` to autocorrect

* Added `telusplanet.net` to the list of mailcheck email providers.

--- app/assets/javascripts/ama_layout/autocorrect_email.js.coffee
+++ app/assets/javascripts/ama_layout/autocorrect_email.js.coffee
@@ -3,6 +3,7 @@
     'msn.com'
     'bellsouth.net'
     'telus.net'
+    'telusplanet.net'
     'comcast.net'
     'optusnet.com.au'
     'earthlink.net'


Add params default

Add params default

--- app/assets/javascripts/media_lib/jquery.nmdUploader.js.coffee
+++ app/assets/javascripts/media_lib/jquery.nmdUploader.js.coffee
@@ -27,6 +27,7 @@
       settings = $.extend true,
         iframeSelector: '.js-uploader-iframe'
         openSelector: '.js-uploader-open'
+        params: {}
       , options

       throw new Error("host isn't defined") unless settings.host
@@ -36,7 +37,7 @@
         $uploader = $ @

         # сохраняем кастомные параметры
-        $uploader.data 'params', settings.params if settings.params?
+        $uploader.data 'params', settings.params

         $uploader.attr('id', $.md5(Math.random())[0..3]) unless $uploader.attr('id')
         uploaderId = $uploader.attr('id')


manually triggering #clienteChanged. Old implementation introduced bugs

--- app/assets/javascripts/mixins/cliente_on_form_mixin.js.coffee
+++ app/assets/javascripts/mixins/cliente_on_form_mixin.js.coffee
@@ -5,13 +5,12 @@
   queryForClienteOnIdentificacionChange: false

 # view events
-  clienteChanged: (->
+  clienteChanged: ->
     Ember.debug "cliente changed"
     cliente = @get("model.cliente") || Ember.Object.create()
     attributes = ["tipoId", "nombres", "direccion", "telefono", "email"]
     for attribute in attributes
       @set attribute, cliente.get(attribute)
-  ).observes("model.cliente")

 # query callbacks
   gotClientes: (clientes) ->
@@ -22,7 +21,10 @@
   didNotGetCliente: ->
     @setProperties
       isFetchingCliente: false
-      cliente: undefined
+      cliente: null
+
+  triggerClienteChange: ->
+    @clienteChanged()

 # query methods
   queryForCliente: (identificacion) ->
@@ -31,7 +33,7 @@
         isFetchingCliente: true
         currentClienteId: identificacion
       store = @get("controller").get("store")
-      store.find("cliente", identificacion: identificacion).then $.proxy(@gotClientes, @), $.proxy(@didNotGetCliente, @)
+      store.find("cliente", identificacion: identificacion).then($.proxy(@gotClientes, @), $.proxy(@didNotGetCliente, @)).finally($.proxy(@triggerClienteChange, @))

 # input callbacks
   identificacionChanged: (event) ->


When opening a folder, automatically close any other open folder.

--- app/assets/javascripts/ng-services/ng-OpenFolderSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-OpenFolderSvc.js.coffee
@@ -10,6 +10,9 @@
   #---------------------------------------------
   set: (folder)->
     animationsSvc.open_folder folder
+    # When opening a folder, close any other open folder
+    if $rootScope.current_open_folder?
+      animationsSvc.close_folder $rootScope.current_open_folder
     $rootScope.current_open_folder = folder

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


dashboard : use KD.showError and scope variables to display error

--- client/Dashboard/views/generalsettingsview.coffee
+++ client/Dashboard/views/generalsettingsview.coffee
@@ -68,11 +68,9 @@
     saveButton = @settingsForm.buttons.Save
     group      = @getData()

-    # fix me:  make this a single call
-    group.modify formData, (err)=>
-      if err
-        saveButton.hideLoader()
-        return new KDNotificationView { title: err.message, duration: 1000 }
+    group.modify formData, (err)->
+      saveButton.hideLoader()
+      return KD.showError err if err

       new KDNotificationView
         title    : "Group settings saved"


Bugs: referenceerror fix WIP

Bugs: referenceerror fix WIP

--- client/Social/Bugs/Views/bugstatusitemlist.coffee
+++ client/Social/Bugs/Views/bugstatusitemlist.coffee
@@ -1,4 +1,4 @@
-class BugStatusItemList extends StatusActivityItemView
+class BugStatusItemList extends ActivityListItemView

   constructor:( options={}, data)->
     super options, data


groups: LoginLink renamed, group view hiding moved to method

--- client/app/MainApp/maincontroller/mainview.coffee
+++ client/app/MainApp/maincontroller/mainview.coffee
@@ -66,7 +66,9 @@
       attributes:
         href    : "#"
       click     : (event)=>
-        return if @groupsEnabled()
+        if @groupsEnabled()
+          @closeGroupView()
+          return

         event.stopPropagation()
         event.preventDefault()
@@ -198,38 +200,44 @@
     $('body').addClass "login"
     console.log "LOGGED IN WITH GROUPS"

-    LoginLink = new KDCustomHTMLView
+    groupLink = new KDCustomHTMLView
       partial: "Login"
       cssClass: "bigLink"

-
-
     if state
       KD.getSingleton('router').handleRoute "/#{@getSingleton("router")?.getCurrentPath()+'/Activity'}", state:{}
-      LoginLink.click = =>
-        @mainTabView.showHandleContainer()
-        @groupLandingView._windowDidResize = =>
-        # @utils.wait 200, =>
-        $('.group-landing').css 'height', 0
-
-      LoginLink.updatePartial 'Go to Group'
+      groupLink.click = =>
+        @closeGroupView()
+
+      groupLink.updatePartial 'Go to Group'

     else
-      LoginLink.click = =>
+      groupLink.click = =>
         @groupLandingView._windowDidResize = =>

         @getSingleton('mainController').loginScreen.show()
         @getSingleton('mainController').loginScreen.animateToForm 'login'
         $('.group-landing').css 'height', 0

-    LoginLink.appendToSelector '.group-login-buttons'
+    groupLink.appendToSelector '.group-login-buttons'

     $('.group-landing').css 'height', window.innerHeight - 50
+
+  closeGroupView:->
+    @mainTabView.showHandleContainer()
+    @groupLandingView._windowDidResize = noop
+    $('.group-landing').css 'height', 0

   decorateLoginState:(isLoggedIn = no)->

     @groupLandingView = new KDView
       lazyDomId : 'group-landing'
+
+    groupLandingContentView = new KDView
+      lazyDomId : 'group-landing-content'
+
+    groupLandingGroupContentView = new KDView
+      lazyDomId : 'group-content-wrapper'

     @groupLandingView.listenWindowResize()

@@ -241,12 +249,6 @@
         height : groupLandingContentView.getHeight() - (256)

-    groupLandingContentView = new KDView
-      lazyDomId : 'group-landing-content'
-
-    groupLandingGroupContentView = new KDView
-      lazyDomId : 'group-content-wrapper'
-

     if isLoggedIn
       if @groupsEnabled()


ComputeController::create: wait for reset to be done

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -326,8 +326,8 @@

   create: (options, callback)->
     remote.api.ComputeProvider.create options, (err, machine)=>
-      @reset yes  unless err?
-      callback err, machine
+      return callback err  if err?
+      @reset yes, -> callback null, machine

   createDefaultStack: ->


ComputeController: buildStack: if stack is building, follow apply events and don't allow to call build again

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -610,6 +610,15 @@

     return  unless @verifyStackRequirements stack

+    state = stack.status?.state ? 'Unknown'
+
+    unless state is 'NotInitialized'
+      if state is 'Building'
+        @eventListener.addListener 'apply', stack._id
+      else
+        kd.warn 'Stack already initialized, skipping.', stack
+      return
+
     stack.machines.forEach (machineId) =>
       return  unless machine = @findMachineFromMachineId machineId


minor fix

minor fix

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -111,7 +111,7 @@
       KDEventTypes       : "viewAppended"
       listenedToInstance : @launchrock
       callback           : =>
-        @launchrock.setPartial """

"""
+        @launchrock.setPartial """

"""

     @slideShow = new KDView
       cssClass : "slide-show"


(minor) Boyscouting: " -> '

(minor) Boyscouting: " -> '

--- core/app/backbone/factlink/text_input_view.coffee
+++ core/app/backbone/factlink/text_input_view.coffee
@@ -2,12 +2,12 @@
 class Backbone.Factlink.TextInputView extends Backbone.Marionette.ItemView
   events:
     'click': 'focusInput'
-    "keydown .typeahead": "parseKeyDown"
-    "keyup .typeahead": "updateModel"
+    'keydown .typeahead': 'parseKeyDown'
+    'keyup .typeahead': 'updateModel'

   triggers:
-    "focus .typeahead": "focus"
-    "blur .typeahead": "blur"
+    'focus .typeahead': 'focus'
+    'blur .typeahead': 'blur'

   templateHelpers: =>
     placeholder: @options.placeholder
@@ -38,7 +38,7 @@
     if @model.get('text') != @$inputField().val()
       @$inputField().val(@model.get('text'))

-  enable: -> @$inputField().prop "disabled", false
-  disable: ->@$inputField().prop "disabled", true
+  enable: -> @$inputField().prop 'disabled', false
+  disable: ->@$inputField().prop 'disabled', true

-  $inputField: -> @$(".typeahead")
+  $inputField: -> @$('.typeahead')


Make them intuitive

Make them intuitive

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domaincreationform.coffee
@@ -3,18 +3,16 @@
   subDomainPattern = \
     /^([a-z0-9]([_\-](?![_\-])|[a-z0-9]){0,60}[a-z0-9]|[a-z0-9])$/

-  # -- Form -- #
   domainOptions = [
     { title : "Create A Subdomain",     value : "subdomain" }
     { title : "Register New Domain",    value : "new"}
     { title : "Use An Existing Domain", value : "existing", disabled : yes }
   ]

-  constructor:(options = {})->
+  constructor:(options = {}, data)->
     options.cssClass = "environments-add-domain-form"
-    super options
+    super options, data

-    # -- Header -- #
     @addSubView @header = new KDHeaderView
       title             : "Add a domain"

@@ -43,14 +41,14 @@
     @newDomainPane = new KDTabPaneView { name : "NewDomain" }
     @newDomainPane.addSubView @newDomainEntryForm = new DomainBuyForm
     @tabs.addPane @newDomainPane
-    @newDomainEntryForm.on 'registerDomain', @bound 'registerNewDomain'
+    @newDomainEntryForm.on 'registerDomain', @bound 'checkDomainAvailability'

     @subDomainPane = new KDTabPaneView { name : "SubDomain" }
     @subDomainPane.addSubView @subDomainEntryForm = new SubDomainCreateForm
     @subDomainEntryForm.on 'registerDomain', @bound 'createSubDomain'
     @tabs.addPane @subDomainPane

-  registerNewDomain: ->
+  checkDomainAvailability: ->
     {createButton}        = @newDomainEntryForm.buttons
     {domains, domainName} = @newDomainEntryForm.inputs
     domainName            = "#{domainName.getValue()}.#{domains.getValue()}"
@@ -65,7 +63,7 @@
     {createButton}        = @subDomainEntryForm.buttons
     domainName            = domainName.getValue()

-    # Test given subdomain
+    # Check given subdomain
     unless subDomainPattern.test domainName
       createButton.hideLoader()
       return notifyUser "#{domainName} is an invalid subdomain."
@@ -168,7 +166,7 @@
       buttons               :
         createButton        :
           name              : "createButton"
-          title             : options.buttonTitle or "Check availability..."
+          title             : options.buttonTitle or "Check availability"
           style             : "cupid-green"
           cssClass          : "add-domain"
           type              : "submit"


Deleted unused span

Deleted unused span

--- core/app/backbone/views/react_install_extension_button.coffee
+++ core/app/backbone/views/react_install_extension_button.coffee
@@ -6,15 +6,12 @@
       _div ['visible-when-chrome'],
         _a ['button-success', @props.extraButtonClasses, href: 'javascript:chrome.webstore.install()'],
           _span ['extension-install-button install-chrome']
-          _span [''],
             'Install Factlink for Chrome'
       _div ['visible-when-firefox'],
         _a ['button-success', @props.extraButtonClasses, href: 'https://static.factlink.com/extension/firefox/factlink-latest.xpi'],
           _span ['extension-install-button install-firefox']
-          _span [''],
             'Install Factlink for Firefox'
       _div ['visible-when-safari'],
         _a ['button-success', @props.extraButtonClasses, href: 'https://static.factlink.com/extension/firefox/factlink.safariextz'],
           _span ['extension-install-button install-safari']
-          _span [''],
             'Install Factlink for Safari'


domain: WIP, pass along the productData

domain: WIP, pass along the productData

--- client/landing/app/Applications/Environments.kdapplication/views/domains/domainbuyform.coffee
+++ client/landing/app/Applications/Environments.kdapplication/views/domains/domainbuyform.coffee
@@ -21,13 +21,13 @@
         productForm : new DomainProductForm
         confirmForm : new DomainBuyConfirmForm { domain, year, price }

-      modal.on 'PaymentConfirmed', ({ productInfo, paymentMethodId }) =>
-        @buyDomain { domain, year, price, paymentMethodId, productInfo }
+      modal.on 'PaymentConfirmed', ({ productData, paymentMethodId }) =>
+        @buyDomain { domain, year, price, paymentMethodId, productData }

   buyDomain: (options) ->
     { JPaymentCharge } = KD.remote.api
-
-    JPaymentCharge.charge options, -> debugger
+    debugger
+    # JPaymentCharge.charge options, -> debugger

   viewAppended:->
     tldList = []


Fixing askBruce crash because of bad indentation

--- src/schneier.coffee
+++ src/schneier.coffee
@@ -24,16 +24,16 @@
   askBruce = (msg, url) ->
     msg.http(url)
       .get() (err, res, body) ->
-      if err
-        msg.send "Bruce Schneier says: #{err}"
-      else
-        parser = new xml2js.Parser()
-        parser.parseString body, (err, result) ->
-          if err
-            msg.send "Got: #{err}"
-            output = ""
-          facts = []
-          for fact in result.channel.item
-            output =  "#{fact.description}"
-            facts.push(output)
-          msg.send msg.random facts+        if err
+          msg.send "Bruce Schneier says: #{err}"
+        else
+          parser = new xml2js.Parser()
+          parser.parseString body, (err, result) ->
+            if err
+              msg.send "Got: #{err}"
+              output = ""
+            facts = []
+            for fact in result.channel.item
+              output =  "#{fact.description}"
+              facts.push(output)
+            msg.send msg.random facts


cache operator regexps

cache operator regexps

--- src/selector.coffee
+++ src/selector.coffee
@@ -2,9 +2,6 @@
 parse = require 'slick/lib/parser'

 # TODO selector using asyncxml.Builder::register
-
-test = (regex, value) ->
-    RegExp(regex).test(value)

 class Selector extends EventEmitter
@@ -17,12 +14,23 @@
         @builder = @template.xml ? @template
         @builder._selectors = ({i, level:0} for s,i in @selectors)
         @builder.on('add', @onadd.bind(this))
+        this

     onlistener: (event, listener) ->
         for sel in parse event
             sel.event =  event
             @builder?._selectors.push {i:@selectors.length, level:0}
             @selectors.push sel
+            # cache operator regexps
+            for s of sel when s.attributes?
+                for a in s.attributes
+                    esc = a.escapedValue
+                    a.operatorRegExp = switch a.operator
+                        when '^=' then RegExp(      "^#{esc}"       )
+                        when '$=' then RegExp(       "#{esc}$"      )
+                        when '~=' then RegExp("(^|\\s)#{esc}(\\s|$)")
+                        when '|=' then RegExp(      "^#{esc}(-|$)"  )
+                        else null
         this

     onadd: (parent, el) ->
@@ -62,12 +70,10 @@
             return no if not (val = tag.attr(a.name))
             esc = a.escapedValue
             switch a.operator
-                when '^=' then return no unless test(      "^#{esc}"       ,val)
-                when '$=' then return no unless test(       "#{esc}$"      ,val)
-                when '~=' then return no unless test("(^|\\s)#{esc}(\\s|$)",val)
-                when '|=' then return no unless test(      "^#{esc}(-|$)"  ,val)
                 when '*=' then return no if val.indexOf(a.value) is -1
                 when  '=' then return no if val isnt a.value
+                when '^=','$=','~=','|='
+                    return no unless a.operatorRegExp.test(val)
                 else return no
         # pseudos
         # TODO


removed line

removed line

--- src/snockets.coffee
+++ src/snockets.coffee
@@ -625,6 +625,5 @@

   return JSON.parse generated.toString()

-
 timeEq = (date1, date2) ->
   date1? and date2? and date1.getTime() is date2.getTime()


version no longer needs parsed

version no longer needs parsed

--- src/lib/content.coffee
+++ src/lib/content.coffee
@@ -30,8 +30,7 @@
 # Wrap the function functionality in a request for Template's extension ID and
 # current version so that it can be used to detect previous injections.
 chrome.extension.sendRequest type: 'info', (data) ->
-  isMac   = navigator.userAgent.toLowerCase().indexOf('mac') isnt -1
-  version = data.version.replace /\./g, ''
+  isMac = navigator.userAgent.toLowerCase().indexOf('mac') isnt -1
   # Only add the listeners if a previous injection isn't detected for version
   # of Template that is currently running.
   return if document.body.getAttribute(data.id) is data.version


Fix setId when called after makeXML

Fix setId when called after makeXML

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -431,7 +431,7 @@
     @svg = removeSVGComments @svg

   setId: (@id) ->
-    @xml.setAttribute 'id', @id if @xml?
+    @xml.documentElement.setAttribute 'id', @id if @xml?
   makeXML: ->
     return @xml if @xml?
     @makeSVG()


Warn instead of crash when mapping returns string

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -688,8 +688,10 @@
   load: (data) ->
     if typeof data == 'function'
       @function = data
+    else if typeof data == 'object'
+      @merge data
     else
-      @merge data
+      console.warn "Mapping file #{@filename} returned invalid mapping data of type #{typeof data}"
   merge: (data) ->
     settings = @settings
     settings = {...settings, dirname: path.dirname @filename} if @filename?


Tweak documentation

Tweak documentation

--- src/lib/filters.coffee
+++ src/lib/filters.coffee
@@ -311,10 +311,7 @@
 # ### Arguments.
 # <pre>
 #   chan - [r, g, b, rgb]
-#   start - [x, y] (start of curve; 0 - 255)
-#   ctrl1 - [x, y] (control point 1; 0 - 255)
-#   ctrl2 - [x, y] (control point 2; 0 - 255)
-#   end   - [x, y] (end of curve; 0 - 255)
+#   cps - [x, y]* (curve control points, min. 2; 0 - 255)
 # </pre>
 #
 # The first argument represents the channels you wish to modify with the filter. It can be an


Use correct variable name for last_name.

--- src/telegram.coffee
+++ src/telegram.coffee
@@ -76,7 +76,7 @@

         # Check for any changes, if the first or lastname updated...we will
         # user the new user object instead of the one from the brain
-        if result.first_name != user.first_name or result.last_namme != user.last_name
+        if result.first_name != user.first_name or result.last_name != user.last_name
             @robot.brain.data.users[user.id] = user
             @robot.logger.info "User " + user.id + " regenerated. Persisting new user object."
             return user


use schema when doctype is just on

use schema when doctype is just on

--- src/template.coffee
+++ src/template.coffee
@@ -38,6 +38,7 @@
         opts.end ?= on
         # schema
         schema_input = opts.schema
+        opts._schema = opts.schema
         # resolve schema name input
         s = aliases[schema_input] or schema_input or 'xml'
         # load self closing schema
@@ -92,7 +93,7 @@
         opts = @opts
         # load doctype if enabled
         if opts.doctype is on
-            opts.doctype = 'html'
+            opts.doctype = opts._schema or 'html'
         # resolve doctype name input
         d = aliases[opts.doctype] or opts.doctype
         # write doctype


expose template in builder

expose template in builder

--- src/template.coffee
+++ src/template.coffee
@@ -52,6 +52,7 @@
         ff ExtendedBuilder::, opts.schema
         # instantiate
         @xml = new ExtendedBuilder opts
+        @xml.template = this
         # override query
         old_query = @xml.query
         @xml.query = (type, tag, key) ->


removed the requirement of a folder

removed the requirement of a folder

--- app/assets/javascripts/app.js.coffee
+++ app/assets/javascripts/app.js.coffee
@@ -3,8 +3,7 @@
 #= require_tree ./models
 #= require_tree ./controllers
 #= require_tree ./helpers
-# require_tree ./views
-#= require_tree ./components
+# require_tree ./components
 #= require_tree ./templates
 #= require_tree ./routes
 #= require ./router


don't error if a http resource can not be download

try and continue, log the error but you might still be able to
compile. prevents issue with badly uploaded images in filstore

--- services/clsi/app/coffee/ResourceWriter.coffee
+++ services/clsi/app/coffee/ResourceWriter.coffee
@@ -5,6 +5,7 @@
 mkdirp = require "mkdirp"
 OutputFileFinder = require "./OutputFileFinder"
 Metrics = require "./Metrics"
+logger = require "logger-sharelatex"

 module.exports = ResourceWriter =
 	syncResourcesToDisk: (project_id, resources, basePath, callback = (error) ->) ->
@@ -55,13 +56,10 @@
 			return callback(error) if error?
 			# TODO: Don't overwrite file if it hasn't been modified
 			if resource.url?
-				UrlCache.downloadUrlToFile(
-					project_id,
-					resource.url,
-					path,
-					resource.modified,
-					callback
-				)
+				UrlCache.downloadUrlToFile project_id, resource.url, path, resource.modified, (err)->
+					if err?
+						logger.err err:err, "error downloading file for resources"
+					callback() #try and continue compiling even if http resource can not be downloaded at this time
 			else
 				fs.writeFile path, resource.content, callback


account: fix minor code style issue

account: fix minor code style issue

--- client/Account/views/username.coffee
+++ client/Account/views/username.coffee
@@ -122,7 +122,7 @@
     reader.readAsDataURL file

-  uploadAvatar: (avatar, callback)->
+  uploadAvatar: (avatar, callback) ->

     {mimeType, content} = avatar

@@ -138,6 +138,7 @@
       return KD.showError err  if err

       @account.modify "profile.avatar": "#{url}"
+

   update: (formData) ->


app: util: showError fixed Coffeelint

app: util: showError fixed Coffeelint

--- client/app/lib/util/showError.coffee
+++ client/app/lib/util/showError.coffee
@@ -11,7 +11,7 @@

   if 'string' is typeof err
     message = err
-    err     = {message}
+    err     = { message }

   defaultMessages =
     AccessDenied : 'Permission denied'
@@ -30,15 +30,15 @@
     if 'string' is typeof errMessage
       title = errMessage
     else if errMessage.title? and errMessage.content?
-      {title, content} = errMessage
+      { title, content } = errMessage

   duration = errMessage.duration or 2500
   title  or= err.message

-  new KDNotificationView {title, content, duration}
+  new KDNotificationView { title, content, duration }

   unless err.name is 'AccessDenied'
-    kd.warn "KodingError:", err.message
+    kd.warn 'KodingError:', err.message
     kd.error err
   err?


select endpoint and endpointTest from object if existing

--- client/coffee/genotoxEvidence.coffee
+++ client/coffee/genotoxEvidence.coffee
@@ -97,7 +97,11 @@
         "<option value='#{val}'>#{val}</option>"

     selector = $(tmpl.find('select[name="endpoint"]'))
-    existing = "option[value='#{ selector.val() }']"
+    if tmpl.data.isNew
+        existing = "option[value='#{ selector.val() }']"
+    else
+        existing = "option[value='#{ tmpl.data.endpoint }']"
+
     selector.html(options)
     found = selector.find(existing)
     if (found.length > 0)
@@ -128,7 +132,11 @@
         "<option value='#{val}'>#{val}</option>"

     selector = $(tmpl.find('select[name="endpointTest"]'))
-    existing = "option[value='#{ selector.val() }']"
+    if tmpl.data.isNew
+        existing = "option[value='#{ selector.val() }']"
+    else
+        existing = "option[value='#{ tmpl.data.endpointTest }']"
+
     selector.html(options)
     found = selector.find(existing)
     if (found.length > 0)


Formatting.

Formatting.

--- src/map-printer.coffee
+++ src/map-printer.coffee
@@ -49,9 +49,9 @@
             map = @mapView.map
             markers = @mapView.allMarkers._featureGroup._layers

-            listOfUnits = document.createElement('div')
-            listOfUnits.id = PRINT_LEGEND_ELEMENT_ID;
-            document.body.appendChild(listOfUnits);
+            listOfUnits = document.createElement 'div'
+            listOfUnits.id = PRINT_LEGEND_ELEMENT_ID
+            document.body.appendChild listOfUnits

             mapBounds = map._originalGetBounds()

@@ -71,53 +71,49 @@
                 # Adjust the icon anchor to correct place
                 marker.options.icon.options.iconAnchor = new L.Point(3*iconSize/4, iconSize/4)

-                # map.getBounds and map._originalGetBounds both give the bounds
-                # of the active area. -> Need to get whole #map bounds manually.
-                bounds = map.getPixelBounds()
-                sw = map.unproject bounds.getBottomLeft()
-                ne = map.unproject bounds.getTopRight()
+                unless mapBounds.contains marker.getLatLng()
+                    return

-                if mapBounds.contains marker.getLatLng()
-                    marker.vid = ++vid
-                    # Don't throw the actual icon away
-                    marker._iconStore = marker._icon
+                marker.vid = ++vid
+                # Don't throw the actual icon away
+                marker._iconStore = marker._icon

-                    canvasIcon = document.createElement('canvas')
-                    canvasIcon.height = iconSize
-                    canvasIcon.width = iconSize
-                    ctx = canvasIcon.getContext('2d')
-                    drawer = new draw.NumberCircleMaker(iconSize/2);
-                    drawer.drawNumberedCircle(ctx, marker.vid)
-                    marker._icon = canvasIcon
-                    marker._icon.src = canvasIcon.toDataURL();
+                canvasIcon = document.createElement('canvas')
+                canvasIcon.height = iconSize
+                canvasIcon.width = iconSize
+                ctx = canvasIcon.getContext('2d')
+                drawer = new draw.NumberCircleMaker(iconSize/2);
+                drawer.drawNumberedCircle(ctx, marker.vid)
+                marker._icon = canvasIcon
+                marker._icon.src = canvasIcon.toDataURL();

-                    markerLegend = document.createElement 'div'
-                    markerLegend.innerHTML = "
" + marker.vid + ": " + "
";
+                markerLegend = document.createElement 'div'
+                markerLegend.innerHTML = "
" + marker.vid + ": " + "
";

-                    getClusteredUnits = (markerCluster) ->
-                        unitNames = []
-                        for own mid, mm of markerCluster._markers
-                            unitNames.push mm.unit.attributes.name[p13n.getLanguage()]
-                        for own mcid, mc of markerCluster._childClusters
-                            unitNames = unitNames.concat getClusteredUnits(mc)
-                        unitNames
+                getClusteredUnits = (markerCluster) ->
+                    unitNames = []
+                    for own mid, mm of markerCluster._markers
+                        unitNames.push mm.unit.attributes.name[p13n.getLanguage()]
+                    for own mcid, mc of markerCluster._childClusters
+                        unitNames = unitNames.concat getClusteredUnits(mc)
+                    unitNames

-                    if marker instanceof L.MarkerCluster
-                        # Adjust the icon anchor position for clusters with these magic numbers
-                        marker.options.icon.options.iconAnchor = new L.Point(5*iconSize/6, iconSize / 6)
-                        unitNames = getClusteredUnits(marker)
-                        for name in unitNames
-                            div = document.createElement 'div'
-                            div.className = 'printed-unit-name'
-                            div.textContent = name
-                            markerLegend.appendChild div
-
-                    else
+                if marker instanceof L.MarkerCluster
+                    # Adjust the icon anchor position for clusters with these magic numbers
+                    marker.options.icon.options.iconAnchor = new L.Point(5*iconSize/6, iconSize / 6)
+                    unitNames = getClusteredUnits(marker)
+                    for name in unitNames
                         div = document.createElement 'div'
                         div.className = 'printed-unit-name'
-                        div.textContent = marker.unit.attributes.name[p13n.getLanguage()]
+                        div.textContent = name
                         markerLegend.appendChild div
-                    listOfUnits.appendChild(markerLegend)
+
+                else
+                    div = document.createElement 'div'
+                    div.className = 'printed-unit-name'
+                    div.textContent = marker.unit.attributes.name[p13n.getLanguage()]
+                    markerLegend.appendChild div
+                listOfUnits.appendChild markerLegend

             leafletImage map, (err, canvas) =>
                 if err


completed bus schemas

completed bus schemas

--- app/schemas/subscriptions/bus.coffee
+++ app/schemas/subscriptions/bus.coffee
@@ -27,13 +27,45 @@
         $ref: "bus"

   "bus:new-message":
-    {} # TODO schema
+    title: "Message sent"
+    $schema: "http://json-schema.org/draft-04/schema#"
+    description: "A new message was sent"
+    type: "object"
+    properties:
+      message:
+        type: "string"
+      bus:
+        $ref: "bus"

   "bus:player-joined":
-    {} # TODO schema
+    title: "Player joined"
+    $schema: "http://json-schema.org/draft-04/schema#"
+    description: "A new player has joined"
+    type: "object"
+    properties:
+      player:
+        type: "object"
+      bus:
+        $ref: "bus"

   "bus:player-left":
-    {} # TODO schema
+    title: "Player left"
+    $schema: "http://json-schema.org/draft-04/schema#"
+    description: "A player has left"
+    type: "object"
+    properties:
+      player:
+        type: "object"
+      bus:
+        $ref: "bus"

   "bus:player-states-changed":
-    {} # TODO schema
+    title: "Player state changes"
+    $schema: "http://json-schema.org/draft-04/schema#"
+    description: "State of the players has changed"
+    type: "object"
+    properties:
+      player:
+        type: "array"
+      bus:
+        $ref: "bus"


clean up

clean up

--- app/server/methods/generators.coffee
+++ app/server/methods/generators.coffee
@@ -150,7 +150,7 @@

         if user.services?.google?.email?
           console.log("Sending Email Link to", user.services.google.email)
-          link = Meteor.absoluteUrl("cases/bid/pdf/#{fileId}?case=#{id}")
+          link = Meteor.absoluteUrl("cases/bid/download/#{fileId}?case=#{id}")
           gradesLink = Meteor.absoluteUrl("")
           Email.send
             from: "grades@sharedrecord.org"
@@ -219,7 +219,7 @@

         if user.services?.google?.email?
           console.log("Sending Email Link to", user.services.google.email)
-          link = Meteor.absoluteUrl("cases/mou/pdf/#{fileId}?case=#{id}")
+          link = Meteor.absoluteUrl("cases/mou/download/#{fileId}?case=#{id}")
           gradesLink = Meteor.absoluteUrl("")
           Email.send
             from: "grades@sharedrecord.org"


Update index.coffee

Update index.coffee

--- src/mobile/apps/profile/index.coffee
+++ src/mobile/apps/profile/index.coffee
@@ -49,5 +49,4 @@
 app.get(route, routes.setProfile) for route in vanityRoutes
 app.get '/profile/:id', routes.index
 app.get '/editorial', routes.redirectEditorial
-app.get '/collect', routes.redirectCollect
 app.get '/:id', routes.index


Fix vomnibar start-up messages.

Fix vomnibar start-up messages.

--- content_scripts/ui_component.coffee
+++ content_scripts/ui_component.coffee
@@ -33,7 +33,8 @@
       @iframeElement.contentWindow.postMessage secret, chrome.runtime.getURL(""), [messageChannel.port2]

   postMessage: (message) ->
-    @iframePort.postMessage message
+    # We use "?" here because the iframe port is initialized asynchronously, and may not yet be ready.
+    @iframePort?.postMessage message

   activate: (@options) ->
     @postMessage @options if @options?


Account: minor unnecessary

Account: minor unnecessary

--- client/Account/AppController.coffee
+++ client/Account/AppController.coffee
@@ -11,8 +11,6 @@
     routes                       :
       "/:name?/Account"          : -> KD.singletons.router.handleRoute '/Account/Profile'
       "/:name?/Account/:section" : ({params:{section}})-> handler (app)-> app.openSection section
-    behavior                     : "hideTabs"
-    hiddenHandle                 : yes

   items =
     personal :


payment: add operation types

payment: add operation types

--- client/Main/payment/workflow.coffee
+++ client/Main/payment/workflow.coffee
@@ -27,6 +27,12 @@
     PAYPAL : 'paypal'
     KODING : 'koding'

+  @operation =
+    UPGRADE         : 1
+    INTERVAL_CHANGE : 0
+    DOWNGRADE       : -1
+
+
   FAILED_ATTEMPT_LIMIT = 3

   @isUpgrade = (current, selected) ->


fix app colors

fix app colors

* remove colors that matches the orange action color
* remove colors that matches the blue actionable color


fix modal saving

fix modal saving

--- client/app/views/event_modal.coffee
+++ client/app/views/event_modal.coffee
@@ -1,4 +1,5 @@
 ViewCollection = require 'lib/view_collection'
+Event          = require 'models/event'
 random         = require 'lib/random'
 app            = require 'application'

@@ -109,19 +110,20 @@

     save: =>
         return if @$('confirm-btn').hasClass 'disabled'
-        @model.set
+        data =
             description: @$('#basic-summary').val()
             place: @$('#basic-place').val()
-            start: Date.create(@$('#basic-start').val()).format Event.dateFormat
-            end: Date.create(@$('#basic-end').val()).format Event.dateFormat
+            start: Date.create(@$('#basic-start').val()).format Event.dateFormat, 'en'
+            end: Date.create(@$('#basic-end').val()).format Event.dateFormat, 'en'

         if @$('#rrule-help').is ':visible'
-            @model.set rrule: @getRRule().toString()
+            data.rrule = @getRRule().toString()
         else
-            @model.set 'rrule', ''
-
-
-        @model.save {},
+            data.rrule = ''
+
+
+        @model.save data,
+            wait: true
             success: =>
                 @close()
             error: =>


home/stacks: send options to Stack create page to destroy modal

--- client/home/lib/stacks/index.coffee
+++ client/home/lib/stacks/index.coffee
@@ -49,7 +49,7 @@
     #   router.handleRoute "/Home/Stacks/#{kd.utils.slugify pane.name}"

     kd.singletons.mainController.ready =>
-      @createStacksViews()
+      @createStacksViews options
       @createVMsViews()
       @createCredentialsViews()

@@ -63,12 +63,12 @@

-  createStacksViews: ->
+  createStacksViews: (options) ->

     EnvironmentFlux.actions.loadTeamStackTemplates()
     EnvironmentFlux.actions.loadPrivateStackTemplates()

-    @stacks.addSubView new HomeStacksCreate
+    @stacks.addSubView new HomeStacksCreate options

     @stacks.addSubView headerize 'Team Stacks'
     @stacks.addSubView sectionize 'Team Stacks', HomeStacksTeamStacks, { delegate : this }


Login: reload to root

Login: reload to root

--- client/landing/Login/AppView.coffee
+++ client/landing/Login/AppView.coffee
@@ -381,7 +381,7 @@
           @registerForm.reset()
           @registerForm.button.hideLoader()
           @hide()
-          location.replace('/Activity')
+          location.replace('/')

   doFinishRegistration: (formData) ->
     (KD.getSingleton 'mainController').handleFinishRegistration formData, @bound 'afterLoginCallback'


Support setting currency with a number

Support setting currency with a number

--- client/skr/components/Currency.cjsx
+++ client/skr/components/Currency.cjsx
@@ -5,7 +5,10 @@
         symbol: '$'

     propTypes:
-        amount: React.PropTypes.instanceOf(_.bigDecimal)
+        amount: React.PropTypes.oneOfType([
+            React.PropTypes.number,
+            React.PropTypes.instanceOf(_.bigDecimal)
+        ])
         symbol: React.PropTypes.string

     render: ->


started bugfixing removing dice

started bugfixing removing dice

--- client/src/Screen/GoalScreen.coffee
+++ client/src/Screen/GoalScreen.coffee
@@ -60,7 +60,7 @@
     thisReference = this
     if(@bracketClicks == 0)
       @leftBracketIndex = $(element).index('li.dot')
-      if(@leftBracketIndex < @numberDots)
+      if(@leftBracketIndex < @numberDots-1)
         $(element).attr('data-bracket','left')
         @bracketClicks = (@bracketClicks+1)%2
         if(@leftBracketIndex == 0 && @numberInGoal > 1)
@@ -132,8 +132,11 @@
     nextDot     = $('li.dice[data-index='+index+']').next()
     if($(previousDot).data('bracket') is 'left' and $(nextDot).data('bracket') is 'right')
       $(previousDot).attr('data-bracket', 'none')
+      $(previousDot).remove()
+      @numberDots--
     else
       $(previousDot).attr('data-bracket', nextDot.data('bracket'))
+
     $(nextDot).remove()
     # Remove the dice from the goal
     $('#added-goal li.dice[data-index='+index+']').remove()


chrome is fast

chrome is fast

--- coffee/differential_evolution.coffee
+++ coffee/differential_evolution.coffee
@@ -1,6 +1,6 @@
 # differential evolution algorithm

-ITERATION_SLEEP = 1/40 * 1000 # milliseconds
+ITERATION_SLEEP = 1 #chrome is very good in updating the scene. firefox: 1/40 * 1000 # milliseconds
 DISPLAY_ITERATION_INFO = true
 SHOW_DEATH_PARTICLE = false


Move command to correct spot.

Move command to correct spot.

This command (LinkHints.activateModeToCopyLinkUrl) has been in the wrong
spot for quite some time.  This just moves it to be with the other
link-hints commands.

--- background_scripts/commands.coffee
+++ background_scripts/commands.coffee
@@ -131,7 +131,6 @@
       "reload",
       "toggleViewSource",
       "copyCurrentUrl",
-      "LinkHints.activateModeToCopyLinkUrl",
       "openCopiedUrlInCurrentTab",
       "openCopiedUrlInNewTab",
       "goUp",
@@ -147,6 +146,7 @@
       "LinkHints.activateModeWithQueue",
       "LinkHints.activateModeToDownloadLink",
       "LinkHints.activateModeToOpenIncognito",
+      "LinkHints.activateModeToCopyLinkUrl",
       "goPrevious",
       "goNext",
       "nextFrame",
@@ -238,9 +238,10 @@

   "gi": "focusInput"

-  "f":     "LinkHints.activateMode"
-  "F":     "LinkHints.activateModeToOpenInNewTab"
+  "f": "LinkHints.activateMode"
+  "F": "LinkHints.activateModeToOpenInNewTab"
   "<a-f>": "LinkHints.activateModeWithQueue"
+  "yf": "LinkHints.activateModeToCopyLinkUrl"

   "/": "enterFindMode"
   "n": "performFind"
@@ -250,7 +251,6 @@
   "]]": "goNext"

   "yy": "copyCurrentUrl"
-  "yf": "LinkHints.activateModeToCopyLinkUrl"

   "p": "openCopiedUrlInCurrentTab"
   "P": "openCopiedUrlInNewTab"
@@ -315,7 +315,6 @@
   toggleViewSource: ["View page source", { noRepeat: true }]

   copyCurrentUrl: ["Copy the current URL to the clipboard", { noRepeat: true }]
-  "LinkHints.activateModeToCopyLinkUrl": ["Copy a link URL to the clipboard"]
   openCopiedUrlInCurrentTab: ["Open the clipboard's URL in the current tab", { background: true, noRepeat: true }]
   openCopiedUrlInNewTab: ["Open the clipboard's URL in a new tab", { background: true, repeatLimit: 20 }]

@@ -332,6 +331,7 @@
   "LinkHints.activateModeWithQueue": ["Open multiple links in a new tab", { noRepeat: true }]
   "LinkHints.activateModeToOpenIncognito": ["Open a link in incognito window"]
   "LinkHints.activateModeToDownloadLink": ["Download link url"]
+  "LinkHints.activateModeToCopyLinkUrl": ["Copy a link URL to the clipboard"]

   enterFindMode: ["Enter find mode", { noRepeat: true }]
   performFind: ["Cycle forward to the next find match"]


fix to disconnect by without ping

fix to disconnect by without ping

--- src/typetalk.coffee
+++ src/typetalk.coffee
@@ -85,9 +85,10 @@

       ws.on 'open', () =>
         connected = true
-        setInterval =>
+        clearInterval timerId
+        timerId = setInterval =>
           ws.ping 'ping'
-        , 1000 * 60 * 10
+        , 1000 * 10 * 15
         @robot.logger.info "Typetalk WebSocket connected"

       ws.on 'error', (event) =>


checked for destroyed in collision

checked for destroyed in collision

--- src/physics/modules/collision.coffee
+++ src/physics/modules/collision.coffee
@@ -30,6 +30,7 @@
         @quadtree.visit( (node, x1, y1, x2, y2) ->
           p = node.point
           if p isnt null
+            return false if p.destroyed # this node got cleaned up
             return false unless d isnt p.d and p.d.collision # skip this point and continue searching lower levels of the hierarchy
             if (p.x >= x0) and (p.x < x3) and (p.y >= y0) and (p.y < y3)
               Collision.check(d, p.d) # check for collision and run reactions if collision occurred


Add some debugging

Add some debugging

--- src/worldcup.coffee
+++ src/worldcup.coffee
@@ -210,8 +210,11 @@
             msg.send "There are no suspensions from cards :("

   robot.respond /(worldcup|wc)( live)( .*)/i, (msg) ->
-    # permitted_rooms = process.env.WC_LIVE_PERMITTED_ROOMS
-    # return if permitted_rooms and msg.message.user.room not in permitted_rooms
+    current_room = '' + msg.message.user.room
+    permitted_rooms = process.env.WC_LIVE_PERMITTED_ROOMS
+    msg.send "ROOM: #{current_room}"
+    msg.send "PERMITTED: #{permitted_rooms}"
+    return if permitted_rooms and current_room not in permitted_rooms

     status = msg.match[3].trim()


Update a comment to be a tad more descriptive

--- src/wrangler.coffee
+++ src/wrangler.coffee
@@ -58,7 +58,7 @@
 			@model[index] = property
 		@update()

-	# Update the DOM
+	# Do an inital update of the DOM
 	wrangler.update()

 	# We're done!


registration smoothness WIP

registration smoothness WIP

--- core/KDView.coffee
+++ core/KDView.coffee
@@ -680,8 +680,8 @@
     if color
       @$overlay.css "background-color" : color

-    if parent is "body"
-      @$overlay.appendTo "body"
+    if "string" is typeof parent
+      @$overlay.appendTo $(parent)
     else if parent instanceof KDView
       @__zIndex = parseInt(@$().css("z-index"), 10) or 0
       @$overlay.css "z-index", @__zIndex + 1