Prem goodbye

Prem goodbye

--- scripts/offshore.coffee
+++ scripts/offshore.coffee
@@ -16,6 +16,7 @@
 #   hubot who will be triumphant
 #   hubot what is Chris doing today
 #   hubot Chris excuse
+#   hubot goodbye Prem
 #
 # Author:
 #   Mateusz Kopij <sagasu>
@@ -57,6 +58,10 @@
     msg.send "https://raw.githubusercontent.com/sagasu/offshore-team/master/img/chris-donkey-cart-race.jpg"
     msg.send "racing on his ride."

+  robot.respond /goodbye Prem/i, (msg)->
+    msg.send "https://raw.githubusercontent.com/sagasu/offshore-team/master/img/goodbyeold.jpg"
+    msg.send "hey, wait... are you sure that this is the right goodbye?"
+
   robot.respond /who will be victorious/i, (msg)->
     msg.send "The Offshore team! :sachin: :prem: :kostas: :chris: :drmatt:"


Fix empty collections throwing from their card

--- app/pages/collections.cjsx
+++ app/pages/collections.cjsx
@@ -41,10 +41,12 @@

   imagePromise: (collection) ->
     apiClient.type('subjects').get(collection_id: collection.id, page_size: 1)
-      .index(0)
-      .then (subject) ->
-        firstKey = Object.keys(subject.locations[0])[0]
-        subject.locations[0][firstKey]
+      .then ([subject]) ->
+        if subject?
+          firstKey = Object.keys(subject.locations[0])[0]
+          subject.locations[0][firstKey]
+        else
+          '/simple-avatar.jpg'

   cardLink: (collection) ->
     [owner, name] = collection.slug.split('/')


Adjust Atom linter settings

Adjust Atom linter settings

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -11,8 +11,13 @@
     showInvisibles: true
   "exception-reporting":
     userId: "f8b42441-9e5a-b0a5-3076-474dbe6b2797"
+  linter:
+    errorPanelHeight: 160
+    showErrorPanel: false
+  "linter-flake8":
+    ignoreErrorCodes: [
+      "E128"
+      "E501"
+    ]
   welcome:
     showOnStartup: false
-  linter: {}
-  "autocomplete-plus":
-    autoActivationDelay: 150


Atom: add atom-ide-ui

Atom: add atom-ide-ui

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -1,4 +1,6 @@
 "*":
+  "atom-ide-ui":
+    "atom-ide-diagnostics-ui": {}
   "atom-material-ui":
     colors:
       abaseColor: "#607d8b"
@@ -21,6 +23,9 @@
       "terminal-plus"
       "git-plus"
       "atom-terminal"
+      "linter-flow"
+      "linter"
+      "js-hyperclick"
     ]
     packagesWithKeymapsDisabled: []
     telemetryConsent: "limited"
@@ -48,7 +53,7 @@
   "linter-flow":
     executablePath: "./node_modules/.bin/flow"
   "linter-ui-default":
-    panelHeight: 98
+    panelHeight: 385
     showPanel: true
   "one-dark-ui":
     fontSize: 12


atom: update config

atom: update config

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -1,110 +1,81 @@
 "*":
-  "atom-beautify":
-    general:
-      _analyticsUserId: "7c261ce3-6cd2-4c8d-8448-f6c5da663968"
-    js:
-      beautify_on_save: true
-      brace_style: "collapse-preserve-inline"
-      default_beautifier: "Prettier"
-      end_with_newline: true
-      object_curly_spacing: true
-      preserve_newlines: false
-      space_before_conditional: false
-      wrap_line_length: 80
-    json:
-      beautify_on_save: true
-      brace_style: "collapse-preserve-inline"
-      end_with_newline: true
-      object_curly_spacing: true
-      preserve_newlines: false
-      space_before_conditional: false
-  "atom-ide-ui":
-    "atom-ide-code-format": {}
-    "atom-ide-diagnostics-ui": {}
-    use: {}
-  "atom-material-ui":
-    colors:
-      abaseColor: "#607d8b"
-      predefinedColor: "Blue Grey"
-    fonts:
-      fontSize: 14
-    tabs:
-      compactTabs: true
-      tintedTabBar: true
-    treeView:
-      blendTabs: true
-      compactList: false
-    ui: {}
-  "atom-terminal":
-    app: "/usr/bin/urxvt"
   core:
     autoHideMenuBar: true
+    automaticallyUpdate: false
     disabledPackages: [
-      "vim-mode-plus"
-      "terminal-plus"
-      "git-plus"
-      "atom-terminal"
-      "linter-flow"
-      "js-hyperclick"
-      "atom-beautify"
       "linter"
     ]
-    packagesWithKeymapsDisabled: []
+    ignoredNames: [
+      ".git"
+      ".hg"
+      ".svn"
+      ".DS_Store"
+      "._*"
+      "Thumbs.db"
+      "desktop.ini"
+      ".pioenvs"
+      ".piolibdeps"
+      ".clang_complete"
+      ".gcc-flags.json"
+    ]
     telemetryConsent: "limited"
   editor:
     fontFamily: "Iosevka Light"
     fontSize: 12
-    invisibles: {}
     showIndentGuide: true
     showInvisibles: true
   "exception-reporting":
-    userId: "4d56d261-15ad-6e7a-da88-15cc222fa36b"
+    userId: "4ff3bdfb-77c7-41f8-9575-e0a385fccd65"
   "file-icons":
     onChanges: true
-  "git-plus":
-    general:
-      _analyticsUserId: "c06216f1-4f1b-4c00-b6dd-cf0399c819d8"
-  "hit-stats": {}
-  hyperclick:
-    linuxTriggerKeys: "shiftKey,ctrlKey"
   "ide-flowtype":
     tryFlowBin: true
-  "language-babel":
-    autoIndentJSX: true
-  linter: {}
-  "linter-flow":
-    executablePath: "./node_modules/.bin/flow"
-  "linter-ui-default":
-    panelHeight: 280
-    showPanel: true
-  "markdown-preview": {}
-  "one-dark-ui":
-    fontSize: 12
-  "one-light-ui":
-    fontSize: 12
+  minimap:
+    plugins:
+      "git-diff": true
+      "git-diffDecorationsZIndex": 0
+  "platformio-ide-terminal":
+    ansiColors:
+      normal:
+        black: "#3b4252"
+        blue: "#81a1c1"
+        cyan: "#88c0d0"
+        green: "#a3be8c"
+        magenta: "#b48ead"
+        red: "#bf616a"
+        white: "#e5e9f0"
+        yellow: "#ebcb8b"
+      zBright:
+        brightBlack: "#4c566a"
+        brightBlue: "#81a1c1"
+        brightCyan: "#8fbcbb"
+        brightGreen: "#a3be8c"
+        brightMagenta: "#b48ead"
+        brightRed: "#bf616a"
+        brightWhite: "#eceff4"
+        brightYellow: "#ebcb8b"
+    iconColors:
+      blue: "#81a1c1"
+      cyan: "#88c0d0"
+      green: "#a3be8c"
+      magenta: "#b48ead"
+      orange: "#ebcb8b"
+      pink: "#bf616a"
+      purple: "#b48ead"
+      red: "#bf616a"
+      yellow: "#ebcb8b"
+    style:
+      fontFamily: "Iosevka Term Light"
+      fontSize: "12"
   "prettier-atom":
     formatOnSaveOptions:
       enabled: true
+      isDisabledIfNoConfigFile: true
       showInStatusBar: true
-    prettierOptions:
-      singleQuote: true
-      trailingComma: "es5"
-    useEslint: true
-  "split-diff":
-    diffWords: true
-    ignoreWhitespace: true
-    leftEditorColor: "red"
-    rightEditorColor: "green"
-    scrollSyncType: "Vertical + Horizontal"
-    syncHorizontalScroll: true
-  "terminal-plus":
-    core:
-      workingDirectory: "Active File"
-    style:
-      theme: "pro"
+  "tool-bar":
+    visible: false
   "tree-view":
+    hideIgnoredNames: true
     hideVcsIgnoredFiles: true
-  "vim-mode-plus": {}
   welcome:
     showOnStartup: false
-  wordcount: {}


Revert "Prevent the Return key from submitting while composing using an IME"

This reverts commit d13f1cf7b8fd52f858c4594a56d3a4e10b89b191.

--- app/assets/javascripts/answerbox/comment.coffee
+++ app/assets/javascripts/answerbox/comment.coffee
@@ -13,14 +13,8 @@
       commentBox.slideUp()
       btn[0].dataset.state = 'hidden'

-isComposing = false
-compositionHasJustEnded = false

 $(document).on "keyup", "input[name=ab-comment-new]", (evt) ->
-  if isComposing or compositionHasJustEnded
-    compositionHasJustEnded = false
-    return
-
   input = $(this)
   aid = input[0].dataset.aId
   ctr = $("span#ab-comment-charcount-#{aid}")
@@ -55,18 +49,6 @@
       complete: (jqxhr, status) ->
         input.removeAttr 'disabled'

-# IME Return key handling
-$(document).on "compositionstart", "input[name=ab-comment-new]", (evt) ->
-  isComposing = true
-
-$(document).on "compositionend", "input[name=ab-comment-new]", (evt) ->
-  isComposing = false
-  compositionHasJustEnded = true
-
-$(document).on "keydown", "input[name=ab-comment-new]", (evt) ->
-  # 229 is a special keyCode for events processed by an IME
-  # https://developer.mozilla.org/en-US/docs/Web/API/Document/keyup_event
-  compositionHasJustEnded = false unless event.which == 229

 # character count
 $(document).on "input", "input[name=ab-comment-new]", (evt) ->


:fire: manual toggle selection-only command

--- lib/misc-command.coffee
+++ lib/misc-command.coffee
@@ -267,10 +267,3 @@
       disposable.dispose()
       disposable = null
       @vimState.activate('insert')
-
-class ToggleSelectionOnlyMode extends MiscCommand
-  @extend()
-  @commandScope: 'atom-text-editor.vim-mode-plus.visual-mode'
-
-  execute: ->
-    @vimState.toggleClassList('selection-only')


Updated AJAX data types

Updated AJAX data types

--- source/micro.ajax.coffee
+++ source/micro.ajax.coffee
@@ -1,23 +1,34 @@
-'use strict'
+"use strict"

-utils = require './micro.utils.coffee'
-db    = require './micro.localstorage.coffee'
+utils = require "./micro.utils.coffee"
+db    = require "./micro.localstorage.coffee"
+
+DEFAULT =
+  TYPE: "GET"
+  MIME: "json"
+
+MIME_TYPES =
+  json: "application/json"
+  form: "application/x-www-form-urlencoded"
+  html: "text/html"
+  text: "text/plain"

 ajax =
   ajaxSettings:
-    type: 'GET'
-    async: true
-    cache: false
-    cacheDB: 'ajaxRequests'
+    type         : DEFAULT.TYPE
+    async        : true
+    cache        : false
+    cacheDB      : 'xhrs'
     minutesCached: 5
-    success: (res) ->
-    error: (res) ->
-    contentType : 'application/json'
-    headers: {}
-    crossDomain: false
-    timeout: 0
+    success      : (res) ->
+    error        : (res) ->
+    dataType     : DEFAULT.MIME
+    headers      : {}
+    crossDomain  : false
+    timeout      : 0

   ajax: (options) ->
+    @ajaxSettings.headers = {}
     settings = utils.extend {}, @ajaxSettings
     options  = utils.extend settings, options

@@ -46,21 +57,21 @@

   serialize: (options) ->
     data = options.data
-    if options.contentType is 'application/json'
+    if options.dataType is DEFAULT.MIME
       data = JSON.stringify options.data
     data

 # -- Private methods -----------------------------------------------------------
 _xhrHeaders = (xhr, options) ->
-  options.headers['Content-Type'] = options.contentType if options.contentType
-  options.headers['Accept'] = options.contentType if options.contentType
+  options.headers['Content-Type'] = MIME_TYPES[options.dataType] if options.dataType
+  options.headers['Accept'] = MIME_TYPES[options.dataType] if options.dataType
   for header of options.headers
     xhr.setRequestHeader header, options.headers[header]
   return

 _parseResponse = (xhr, options) ->
   response = xhr
-  if options.contentType is 'application/json'
+  if options.dataType is DEFAULT.MIME
     response = JSON.parse xhr.responseText
   response


fix coffee [ci skip]

fix coffee [ci skip]

--- app/assets/javascripts/rails_admin/ui.js.coffee
+++ app/assets/javascripts/rails_admin/ui.js.coffee
@@ -28,5 +28,4 @@
     width = $(this).data("animate-width-to")
     $(this).animate(width: width, length, 'easeOutQuad')

-  // bootstrap init
   $('[rel=tooltip]').tooltip(delay: { show: 500, hide: 100 });


switch lat/long in summary for #38

switch lat/long in summary for #38

--- classify/summary.coffee
+++ classify/summary.coffee
@@ -27,12 +27,13 @@

#{translate 'classifyPage.summary.youMarked'}

#{@roundTo(2, percentCircled)}% of the image

#{translate 'classifyPage.summary.locatedNear'}

-
#{@roundTo(3, lat)} N
#{@roundTo(3, long)} W

+
#{@roundTo(3, long)} N
#{@roundTo(3, lat)} W

           #{if @userSetAGoal() then @goalText() else ''}
            #{translate 'classifyPage.summary.talk'}

"
+    # TODO: switch lat/long positions when back-end data is corrected

   goalText: ->
     "
Goal Countdown:


Collect pages for practice from passed in sections

--- src/components/learning-guide/practice-button.cjsx
+++ src/components/learning-guide/practice-button.cjsx
@@ -3,25 +3,27 @@
 _ = require 'underscore'

 LearningGuide = require '../../flux/learning-guide'
+ChapterSectionType = require './chapter-section-type'

 module.exports = React.createClass
   displayName: 'PracticeButton'

   propTypes:
     courseId: React.PropTypes.string.isRequired
-    title: React.PropTypes.string.isRequired
-
+    title:    React.PropTypes.string.isRequired
+    sections: React.PropTypes.arrayOf(ChapterSectionType)
   contextTypes:
     router: React.PropTypes.func

   onClick: ->
-    {courseId} = @props
-    page_ids = LearningGuide.Student.store.getPracticePages(@props.courseId, @props.practiceType)
+    {courseId, sections} = @props
+    page_ids = LearningGuide.Helpers.pagesForSections(sections)
     unless _.isEmpty(page_ids)
       @context.router.transitionTo('viewPractice', {courseId}, {page_ids})

   render: ->
-    page_ids = LearningGuide.Student.store.getPracticePages(@props.courseId)
+    {sections} = @props
+    page_ids = LearningGuide.Helpers.pagesForSections(sections)
     classNames = ['practice', @props.practiceType]
     isDisabled = _.isEmpty(page_ids)
     classNames.push 'disabled' if isDisabled


EditorModal: Fixed harcoded property value.

--- client/Main/CommonViews/editormodal.coffee
+++ client/Main/CommonViews/editormodal.coffee
@@ -9,7 +9,7 @@
     options.overlay              ?= yes
     options.editor               ?=
       readOnly                    : yes
-    options.removeOnOverlayClick  = no
+    options.removeOnOverlayClick ?= no

     super options, data


Global ajax handlers compatibility with jQ >= 1.9

--- app/assets/javascripts/ultimate/flash.js.coffee
+++ app/assets/javascripts/ultimate/flash.js.coffee
@@ -76,13 +76,14 @@
       jFlash = $(flash)
       jFlash.html @_prepareText(jFlash.html(), jFlash)
       @_setTimeout jFlash
+    jDocument = $(document)
     # binding hook ajaxError handler
-    @$el.ajaxError =>
+    jDocument.ajaxError =>
       if @showAjaxErrors
         a = @_ajaxParseArguments(arguments)
         @ajaxError a.data, a.jqXHR
     # binding hook ajaxSuccess handler
-    @$el.ajaxSuccess =>
+    jDocument.ajaxSuccess =>
       if @showAjaxSuccesses
         a = @_ajaxParseArguments(arguments)
         @ajaxSuccess a.data, a.jqXHR


Remove leaflet attribution control prefix.

--- app/assets/javascripts/views/map.view.js.coffee
+++ app/assets/javascripts/views/map.view.js.coffee
@@ -242,12 +242,6 @@
   BOUNDS_CONTAINS_BUFFER: 0.41

   createLayer: (latlng, options)->
-    if latlng instanceof Wheelmap.PopupController
-      options = latlng.getProperties('wheelchair', 'icon')
-      latlng = latlng.get('location')
-
-      console.log(options, latlng)
-
     new L.Marker latlng,
       riseOnHover: true
       icon: Wheelmap.MarkerLayer.createIcon(options)


Fix file input abort ujs error

Fix file input abort ujs error

--- app/assets/javascripts/markdown_area.js.coffee
+++ app/assets/javascripts/markdown_area.js.coffee
@@ -19,7 +19,7 @@
       preview.html(data)
       preview.css height: 'auto'

-$(document).on 'change', '.markdown-area .file-upload input', (event) ->
+$(document).on 'change', '.markdown-area .file-upload input[type=file]', (event) ->
   textarea = $(this).closest('.markdown-area').find('textarea')

   $.each event.target.files, ->
@@ -51,3 +51,6 @@
           textarea[0].selectionStart = textarea[0].selectionEnd = pos + data.url.length
         else
           textarea[0].selectionStart = textarea[0].selectionEnd = pos
+
+  # Clear input, or ujs submit will be abort.
+  $(this).replaceWith($(this).val('').clone())


Add lineman build task to copy generated files

--- frontend/config/application.coffee
+++ frontend/config/application.coffee
@@ -20,10 +20,21 @@
   # enableSass: true

   # configure lineman to load additional angular related npm tasks
-  loadNpmTasks: [ "grunt-ngmin" ]
+  loadNpmTasks: [ "grunt-ngmin"]
+
   # task override configuration
   prependTasks:
     dist: ["ngmin"]         # ngmin should run in dist only
+
+  watch:
+    scripts:
+      files: ["generated/**"],
+      tasks: ['copy:dev']
+
+  copy:
+    dev:
+      files: [expand: true, cwd: 'generated', src: ['css/**', 'js/**', '!**/spec.js',
+              '!**/*.less*', '!**/*.coffee*', '!**/*.*.map'], dest: '../lib/app/public' ]

   # configuration for grunt-ngmin, this happens _after_ concat once, which is the ngmin ideal :)
   ngmin: {


Humanize error messages from their code name

--- app/src/base/errors.coffee
+++ app/src/base/errors.coffee
@@ -75,8 +75,8 @@
   #   ledger.error.new("an error message")
   #   ledger.error.new(NotFound, "an error message")
   new: (code, msg) ->
-    defaultMessage =  ledger.errors.DefaultMessages[code] or _.findKey(ledger.errors, (c) -> c is code)
-    [code, msg] = [0, code] if defaultMessage == undefined
+    defaultMessage =  ledger.errors.DefaultMessages[code] or _.str.humanize(_.findKey(ledger.errors, (c) -> c is code))
+    [code, msg] = [0, code] if _.str.isBlank defaultMessage
     self = new Error(msg || defaultMessage)
     self.code = code
     self.name = _.invert(ledger.errors)[code]


got clearfilters navigating correctly by doing it on client

--- app/assets/javascripts/views/measure.js.coffee
+++ app/assets/javascripts/views/measure.js.coffee
@@ -55,18 +55,19 @@
     event.preventDefault()

   resetAllFiltersShow: (event) ->
+    self=this;
     # hack to prevent subsequent page from looking for dead query_cache
     event.preventDefault()
-    provider = this.provider_id || PopHealth.rootProvider.id
-    this.provider_id=provider
+    provider = self.provider_id || PopHealth.rootProvider.id
+    self.provider_id=provider
     delete this.submeasure.queries[provider]
     PopHealth.currentUser.get('preferences').c4filters=[];
     $.post(
       'api/queries/'+this.submeasure.attributes.id+'/clearfilters'
       $.param({default_provider_id : provider})
-#      (data) ->
-  #      PopHealth.currentUser.get('preferences').c4filters = null
-#        Backbone.history.navigate('/#providers/'+this.provider_id)
+      (data, xhr) ->
+        PopHealth.currentUser.get('preferences').c4filters = null
+        Backbone.history.navigate('/#providers/'+self.provider_id)
     )

   saveFilter: (filter, url) ->


starting on a real-time data

starting on a real-time data

--- app/assets/javascripts/views/navigation.coffee
+++ app/assets/javascripts/views/navigation.coffee
@@ -9,6 +9,7 @@

   initialize: =>
     Bus.events.on 'plan:complete', @add_segments
+    Bus.events.on 'plan:complete', @get_real_time
     Bus.events.on 'plan:clear', @render

   render: =>
@@ -45,3 +46,21 @@
   to_current_location: =>
     navigator.geolocation.getCurrentPosition (position) ->
       @$('#to_query').val("#{position.coords.latitude},#{position.coords.longitude}")
+
+  get_real_time: (plan) =>
+    segments = plan.itineraries[0].legs
+    first_transit_leg = _.find segments, (segment) ->
+      segment.mode != 'WALK'
+    #console.log first_transit_leg
+    # TODO: get the onebusaway agency id from the agency name, cache this!
+    $.get "/workshop/agencies/otp/#{first_transit_leg.agencyId}.json", (response) =>
+      # OBA gives a response with an invalid content type, force it to json
+      $.ajax(
+        url: "/oba/where/arrivals-and-departures-for-stop/#{response.oba_id}_#{first_transit_leg.from.stopId.id}.json"
+        data: { key: 'TEST' }
+        success: (real_time) ->
+          Bus.events.trigger 'real_time:complete', real_time.data.arrivalsAndDepartures
+        dataType: 'json'
+      )
+
+    #_.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; })


run: add seperate psql check function

run: add seperate psql check function

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -643,6 +643,10 @@

       }

+      function check_psql () {
+        command -v psql          >/dev/null 2>&1 || { echo >&2 "I require psql but it's not installed. (brew install postgresql)  Aborting."; exit 1; }
+      }
+
       function check_service_dependencies () {
         echo "checking required services: nginx, docker, mongo, graphicsmagick..."
         command -v go            >/dev/null 2>&1 || { echo >&2 "I require go but it's not installed.  Aborting."; exit 1; }
@@ -654,7 +658,7 @@
         command -v gulp          >/dev/null 2>&1 || { echo >&2 "I require gulp but it's not installed. (npm i gulp -g)  Aborting."; exit 1; }
         # command -v stylus      >/dev/null 2>&1 || { echo >&2 "I require stylus  but it's not installed. (npm i stylus -g)  Aborting."; exit 1; }
         command -v coffee        >/dev/null 2>&1 || { echo >&2 "I require coffee-script but it's not installed. (npm i coffee-script -g)  Aborting."; exit 1; }
-        command -v psql          >/dev/null 2>&1 || { echo >&2 "I require psql but it's not installed. (brew install postgresql)  Aborting."; exit 1; }
+        check_psql

         if [[ `uname` == 'Darwin' ]]; then
           brew info graphicsmagick >/dev/null 2>&1 || { echo >&2 "I require graphicsmagick but it's not installed.  Aborting."; exit 1; }
@@ -898,7 +902,7 @@
         open supervisor.html

       elif [ "$1" == "migrationfile" ]; then
-        check_service_dependencies
+        check_psql

         if [ "$2" == "" ]; then
           echo "Please choose a migration file name. (ex. add_created_at_column_account)"


Use verbosed client builder

Use verbosed client builder

--- config/main.dev.coffee
+++ config/main.dev.coffee
@@ -193,7 +193,7 @@
     webserver           : command : "./watch-node #{projectRoot}/servers/index.js                    -c #{configName} -p #{KONFIG.webserver.port}   --disable-newrelic"
     socialworker        : command : "./watch-node #{projectRoot}/workers/social/index.js             -c #{configName} -p #{KONFIG.social.port}      -r #{region} --disable-newrelic --kite-port=13020"

-    clientWatcher       : command : "ulimit -n 1024 && coffee #{projectRoot}/build-client.coffee    --watch --sourceMapsUri /sourcemaps"
+    clientWatcher       : command : "ulimit -n 1024 && coffee #{projectRoot}/build-client.coffee    --watch --sourceMapsUri /sourcemaps --verbose true"

     ngrokProxy          : command : "coffee #{projectRoot}/ngrokProxy --user #{publicHostname}"


groupsettings : logo update view fix

groupsettings : logo update view fix

--- client/Dashboard/views/grouplogosettings.coffee
+++ client/Dashboard/views/grouplogosettings.coffee
@@ -2,29 +2,33 @@
   constructor:(options = {}, data)->
     super options, data

-    @groupLogoView = new KDCustomHTMLView
-      tagName     : 'img'
-      size        :
-          width   : 55
-          height  : 55
-      attributes  :
-        src       : ""
-      click       : (event) =>
-        new UploadImageModalView
-          title      : "Change Group Logo"
-          image      :
-            type     : "logo"
-            size     :
-              width  : 55
-              height : 55
-          preview    :
-            size     :
-              width  : 220
-              height : 220
+    {groupsController} = KD.singletons
+    groupsController.ready =>
+      group = groupsController.getCurrentGroup()

-  pistachio:->
-    """
-    {{> @groupLogoView}}
-    """
+      groupLogoView = new KDCustomHTMLView
+        tagName     : 'figure'
+        size        :
+            width   : 55
+            height  : 55
+        attributes  :
+          style     : "background-image: url(#{group.customize?.logo});"
+        click       : (event) ->
+          new UploadImageModalView
+            title      : "Change Group Logo"
+            image      :
+              type     : "logo"
+              size     :
+                width  : 55
+                height : 55
+            preview    :
+              size     :
+                width  : 220
+                height : 220
+
+      @addSubView groupLogoView
+
+      group.on "update", ->
+        groupLogoView.setCss 'background-image', "url(#{group.customize?.logo})"

   viewAppended:JView::viewAppended


adding in sort by chapter section when grouping exercises

--- src/flux/exercise.coffee
+++ src/flux/exercise.coffee
@@ -59,9 +59,11 @@
       @_exercises[pageIds.toString()] or throw new Error('BUG: Invalid page ids')

     getGroupedExercises: (pageIds) ->
-      _.groupBy(@_exercises[pageIds.toString()], (exercise) ->
+      byChapterSection = (exercise) ->
         _.findWhere(exercise.tags, {type: EXERCISE_TAGS.LO}).chapter_section
-      )
+
+      exercises = _.sortBy(@_exercises[pageIds.toString()], byChapterSection)
+      _.groupBy(exercises, byChapterSection)

     getExerciseById: (exercise_id) ->
       @_exerciseCache[exercise_id]


Rename event variable

Rename event variable

--- client/controllers/resolvedIncidentsPlot.coffee
+++ client/controllers/resolvedIncidentsPlot.coffee
@@ -192,22 +192,22 @@
     Template.instance().loading.get()

 Template.resolvedIncidentsPlot.events
-  "click .incident-type-selector .cases": (e, instance)->
+  "click .incident-type-selector .cases": (event, instance)->
     instance.incidentType.set("cases")
     instance.plotType.set("cumulative")

-  "click .incident-type-selector .deaths": (e, instance)->
+  "click .incident-type-selector .deaths": (event, instance)->
     instance.incidentType.set("deaths")
     instance.plotType.set("cumulative")

-  "click .incident-type-selector .case-rate": (e, instance)->
+  "click .incident-type-selector .case-rate": (event, instance)->
     instance.incidentType.set("cases")
     instance.plotType.set("rate")

-  "click .incident-type-selector .death-rate": (e, instance)->
+  "click .incident-type-selector .death-rate": (event, instance)->
     instance.incidentType.set("deaths")
     instance.plotType.set("rate")

-  "click .rickshaw_graph": (e, instance)->
+  "click .rickshaw_graph": (event, instance)->
     if instance.hoveredIntervalClickEvent
-      instance.hoveredIntervalClickEvent(e, instance)
+      instance.hoveredIntervalClickEvent(event, instance)


on unexpected server errors, print exception details, too

--- src/portals/stubs.coffee
+++ src/portals/stubs.coffee
@@ -36,7 +36,7 @@
             @Endpoints.find criteria, callback
          catch e
             response.statusCode =  500
-            @responded 500, request.url, "unexpectedly generated a server error"
+            @responded 500, request.url, "unexpectedly generated a server error: #{e.message}"
             response.end()


Removed messy output

Removed messy output

--- src/projectorHtml.coffee
+++ src/projectorHtml.coffee
@@ -130,8 +130,8 @@
       destroy = (->)

       currentDom = @$projectorHtmlCursor()
-      startNode = currentDom.ownerDocument.createComment('^' + expr)
-      endNode = currentDom.ownerDocument.createComment('$' + expr)
+      startNode = currentDom.ownerDocument.createComment('^')
+      endNode = currentDom.ownerDocument.createComment('$')

       @$projectorHtmlCursor startNode
       @$projectorHtmlCursor endNode


We have root now

We have root now

--- client/landing/app/MainApp/book/bookdata.coffee
+++ client/landing/app/MainApp/book/bookdata.coffee
@@ -66,7 +66,7 @@
   ,
     title     : "Terminal"
     content   : """
Terminal is a very important aspect of development, that's why we have invested a lot of time to provide a fast, smooth and responsive console.

-
It's an Ubuntu VM that you can use to program Java,C++,Perl,Python,Ruby,Node,Erlang,Haskell and what not, out of the box. Other than being root, everything is possible. This VM is not a simulation, it is a real computer, and it's yours.
"""
+
It's an Ubuntu VM that you can use to program Java,C++,Perl,Python,Ruby,Node,Erlang,Haskell and what not, out of the box. Everything is possible. This VM is not a simulation, it is a real computer, and it's yours.
"""
   ,
     title     : "Apps"
     content   : """
What makes Koding so useful are the apps provided by its users. Here you can perform one click installs of incredibly useful applications provided by users and major web development tools.


keywords

keywords

--- grammars/fstar.cson
+++ grammars/fstar.cson
@@ -17,7 +17,7 @@
     'name'  : 'keyword.other.fstar'
     'match' : '\\b(opaque|logic|requires|ensures|modifies|match|with|and|kind|\
                   type|val|open|module|effect|default|new_effect|sub_effect|\
-                  total|private)\\b'
+                  total|private|begin|end)\\b'
   }
   {
     'name'  : 'constant.numeric.integer.decimal.fstar'
@@ -37,7 +37,8 @@
   }
   {
       'name'  : 'constant.language.monad.fstar'
-      'match' : '\\b(Pure|PURE|Tot|STATE|ST|St|ALL|All|EXN|Exn|Ex|DIV|Div|GHOST|Ghost|GTot)\\b'
+      'match' : '\\b(Pure|PURE|Tot|STATE|ST|St|ALL|All|EXN|Exn|Ex|\
+                     DIV|Div|GHOST|Ghost|GTot|Lemma)\\b'
   }
   {
     'name'  : 'keyword.other.arrow.fstar'


rename #numbers to #constants

rename #numbers to #constants

--- grammars/stata.cson
+++ grammars/stata.cson
@@ -24,7 +24,7 @@
     'include': '#comments'
   }
   {
-    'include': '#numbers'
+    'include': '#constants'
   }
   {
     'include': '#operators'
@@ -135,7 +135,7 @@
       '3':
         'patterns': [
           {
-            'include': '#numbers'
+            'include': '#constants'
           }
           {
             'match': 'm|n'
@@ -147,7 +147,7 @@
       '5':
         'patterns': [
           {
-            'include': '#numbers'
+            'include': '#constants'
           }
           {
             'match': 'm|n'
@@ -218,7 +218,7 @@
       '2':
         'patterns': [
           {
-            'include': '#numbers'
+            'include': '#constants'
           }
           {
             'include': '#operators'
@@ -598,7 +598,7 @@
             'name': 'punctuation.definition.parameters.end.stata'
         'patterns': [
           {
-            'include': '#numbers'
+            'include': '#constants'
           }
           {
             'match': '(\\{|\\})'
@@ -900,7 +900,7 @@
         ]
       }
     ]
-  'numbers':
+  'constants':
     'patterns': [
       {
         'match': '\\b(?i:(\\d+\\.\\d*(e[\\-\\+]?\\d+)?))(?=[^a-zA-Z_])'
@@ -1074,16 +1074,9 @@
             'include': '#operators'
           }
           {
-            'include': '#numbers'
-          }
-        ]
-      }
-    ]
-  'generic_names':
-    'patterns': [
-      {
-        'comment': 'this currently doesn\'t do anything'
-        'match': '[A-Za-z_][A-Za-z0-9_]{0,31}'
+            'include': '#constants'
+          }
+        ]
       }
     ]
   'reserved_names':
@@ -1518,7 +1511,7 @@
           '9':
             'patterns': [
               {
-                'include': '#numbers'
+                'include': '#constants'
               }
               {
                 'match': ','
@@ -1580,7 +1573,7 @@
           '8':
             'patterns': [
               {
-                'include': '#numbers'
+                'include': '#constants'
               }
               {
                 'match': ','
@@ -1666,7 +1659,7 @@
                 'include': '#comments-triple-slash'
               }
               {
-                'include': '#numbers'
+                'include': '#constants'
               }
             ]
           '10':
@@ -1746,7 +1739,7 @@
                 'include': '#comments-triple-slash'
               }
               {
-                'include': '#numbers'
+                'include': '#constants'
               }
             ]
           '9':


actually fix Int32Array issue in ie9

actually fix Int32Array issue in ie9

--- lib/analytics.coffee
+++ lib/analytics.coffee
@@ -6,8 +6,11 @@

 _ = require 'underscore'
 sd = require('sharify').data
-createHash = require('crypto').createHash
 qs = require('querystring')
+
+# createHash('md5') requires Int32Array which is not supported by IE9
+if Int32Array
+  createHash = require('crypto').createHash

 _.mixin(require 'underscore.string')

@@ -104,8 +107,8 @@
 maxTrackableMultiIds = 50

 module.exports.encodeMulti = (ids) ->
-  # createHash('md5') requires Int32Array which is not supported by IE9
-  if Int32Array
+  console.log createHash
+  if createHash
     ids = _.compact(ids)
     (_.map ids, (id) -> createHash('md5').update(id).digest('hex').substr(0, 8)).join("-")


Add error handling with c2dm

Add error handling with c2dm

Requires a pull request to be released upstream:

    https://github.com/SpeCT/node-c2dm/pull/14

--- lib/pushservices.coffee
+++ lib/pushservices.coffee
@@ -32,7 +32,11 @@
         if not (subOptions & event.OPTION_IGNORE_MESSAGE) and message = payload.localizedMessage(info.lang)
             note['data.message'] = message
         note["data.#{key}"] = value for key, value of payload.data
-        @driver.send note # TODO handle retries and device error accounting
+        @driver.send note (err, msgid) ->
+            switch err
+                when 'InvalidRegistration', 'NotRegistered'
+                    # Handle C2DM API feedback about no longer or invalid registrations
+                    device.delete()

 class PushServiceMPNS


[EVENTRIC-43] Fix eventric include for the watcher task

--- spec/helper/setup.coffee
+++ spec/helper/setup.coffee
@@ -21,7 +21,7 @@

 beforeEach ->
-  root.eventric = require 'eventric'
+  root.eventric = require 'eventric/src'

 afterEach ->
@@ -34,4 +34,4 @@
   mockery.disable()

-root._spec_setup = true+root._spec_setup = true


tests: Use debug() in heterogenous test

tests: Use debug() in heterogenous test

--- spec/heterogenous.coffee
+++ spec/heterogenous.coffee
@@ -4,6 +4,8 @@
 path = require 'path'
 chai = require 'chai' unless chai
 child_process = require 'child_process'
+
+debug = require('debug')('msgflo:spec:heterogenous')

 python = process.env.PYTHON or 'python'
 foreignParticipants =
@@ -14,22 +16,23 @@
 startProcess = (args, callback) ->
   prog = args[0]
   args = args.slice(1)
-#  console.log 'start', prog, args.join(' ')
+  debug 'starting', prog, args.join(' ')
   child = child_process.spawn prog, args
+  debug 'started PID', child.pid
   returned = false
   child.on 'error', (err) ->
-#    console.log 'error', err
+    debug 'error', err
     return if returned
     returned = true
     return callback err
   # We assume that when somethis is send on stdout, starting is complete
   child.stdout.on 'data', (data) ->
-#    console.log 'stdout', data.toString()
+    debug 'stdout', data.toString()
     return if returned
     returned = true
     return callback null
   child.stderr.on 'data', (data) ->
-#    console.log 'stderr', data.toString()
+    debug 'stderr', data.toString()
     return if returned
     returned = true
     return callback new Error data.toString()


lets only traverse the png buffer once mkay?

--- blend.coffee
+++ blend.coffee
@@ -4,11 +4,6 @@

 parsePng = (buff, cb)->
     pngparse.parse buff, (err, data) ->
-        i = 0
-        len = data.data.length
-        while i < len
-            data.data[i] = 0xff - data.data[i] if i%4 == 3
-            i++
         cb null, data.data

 composit = (a,b,cb)->
@@ -29,6 +24,8 @@
 		len = base.length
 		while i < len
 			j = i+((4 - ((i+1) % 4)) %4)
+			if (j - i ) == 3
+				top[j] = 0xff - top[j]
 			b = base[i]
 			t = top[i]
 			c = (255-top[j]) / 255


Closes #252. Fixed cases where VimFx steals keys from focused XUL menulist

--- extension/packages/utils.coffee
+++ extension/packages/utils.coffee
@@ -10,6 +10,7 @@
 HTMLInputElement    = Ci.nsIDOMHTMLInputElement
 HTMLTextAreaElement = Ci.nsIDOMHTMLTextAreaElement
 HTMLSelectElement   = Ci.nsIDOMHTMLSelectElement
+XULMenuListElement  = Ci.nsIDOMXULMenuListElement
 XULDocument         = Ci.nsIDOMXULDocument
 XULElement          = Ci.nsIDOMXULElement
 XPathResult         = Ci.nsIDOMXPathResult
@@ -79,6 +80,7 @@
          element instanceof HTMLInputElement or \
          element instanceof HTMLTextAreaElement or \
          element instanceof HTMLSelectElement or \
+         element instanceof XULMenuListElement or \
          element.getAttribute('g_editable') == 'true' or \
          element.getAttribute('contenteditable')?.toLowerCase() == 'true' or \
          element.ownerDocument?.designMode?.toLowerCase() == 'on'


add licensing

add licensing

--- app/scripts/alchemy/core/modifyElements.coffee
+++ app/scripts/alchemy/core/modifyElements.coffee
@@ -1,3 +1,19 @@
+# Alchemy.js is a graph drawing application for the web.
+# Copyright (C) 2014  GraphAlchemist, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
 alchemy.modifyElements =
     init: () ->
         if alchemy.conf.showEditor is true


Hide sheet when Canvas is too narrow

Hide sheet when Canvas is too narrow

--- extras/Studio.framer/app.coffee
+++ extras/Studio.framer/app.coffee
@@ -28,7 +28,6 @@

 # Sheet
 class ShareComponent
-
 	constructor: (@shareInfo) ->
 		@render()
 		@_startListening()
@@ -42,7 +41,8 @@
 		@_renderDownload() if !@shareInfo.local

 		# Evaluate content and set height accordingly
-		@_updateHeight()
+		@_updateHeight()
+		@_checkCanvasSize()

 	# Render main sheet
 	_renderSheet: ->
@@ -215,6 +215,9 @@
 				color: "#FFF"

 		@_showPointer(@download)
+
+	_checkCanvasSize: ->
+		if Canvas.width < 1080 then @_closeSheet() else @_openSheet()

 	_startListening: ->
 		# Show regular cursor on sheet
@@ -225,6 +228,11 @@
 		# Toggle sheet
 		@close.onClick => @_closeSheet()
 		@framerButton.onClick => @_openSheet()
+
+		# When the window resizes evaluate if the sheet needs to be hidden
+		Canvas.onResize (event) => @_checkCanvasSize()
+
+

 	_showPointer: (layer) ->
 		layer.onMouseOver ->


Render emoji in markdown headers

Render emoji in markdown headers

--- angular/core/services/render_service.coffee
+++ angular/core/services/render_service.coffee
@@ -6,7 +6,12 @@
     renderer.paragraph = @cook('p')
     renderer.listitem  = @cook('li')
     renderer.tablecell = @cook('td')
+    renderer.heading   = @headerCook
     renderer
+
+  headerCook: (text, level) ->
+    text = emojione.shortnameToImage(text)
+    "<h#{level}>#{text}</h#{level}>"

   cook: (tag) ->
     (text) ->


add parseMosulesMap

add parseMosulesMap

--- src/require.class.coffee
+++ src/require.class.coffee
@@ -4,19 +4,27 @@
 module.exports = class Require
 	constructor: (modulesMap, @supportList = /gulp|grunt|broccoli/g) ->
 		map = @parseMosulesMap modulesMap
-		packagesName = @makePackagesName packages
-		nodeModulesName = @makeNodeModulesName packages
+		console.log map
+		packagesName = @makePackagesName map.packages
+		nodeModulesName = @makeNodeModulesName map.fullPaths
 		@collection = zipObject packagesName, nodeModulesName

 	parseMosulesMap: (modulesMap) ->
-		packages: ->
-			for path, modules of modulesMap
-				console.log modules

-		fullPaths: -> console.dir modulesMap
+		packagesList = []
+		fullPathsList = []
+
+		for modulePath, modules of modulesMap
+			for one in modules
+				packagesList.push one
+				fullPathsList.push modulePath + one
+
+		packages: packagesList
+		fullPaths: fullPathsList

 	makeNodeModulesName: (files) ->
-		require file for file in files
+		for file in files
+			return require path.resolve file

 	makePackagesName: (files) ->
 		packages = []


Fix screenshot carousel when focused on the carousel control and pressing arrow keys

--- app/views/AboutView.coffee
+++ app/views/AboutView.coffee
@@ -67,11 +67,14 @@
     @scrollToLink('#contact')

   onRightPressed: (event) ->
+    # Special handling, otherwise after you click the control, keyboard presses move the slide twice
+    return if event.type is 'keydown' and $(document.activeElement).is('.carousel-control')
     if $('#screenshot-lightbox').data('bs.modal')?.isShown
       event.preventDefault()
       $('#screenshot-carousel').carousel('next')

   onLeftPressed: (event) ->
+    return if event.type is 'keydown' and $(document.activeElement).is('.carousel-control')
     if $('#screenshot-lightbox').data('bs.modal')?.isShown
       event.preventDefault()
       $('#screenshot-carousel').carousel('prev')


fixed logic which was broken after we switched insertion order.

--- core/app/backbone/views/interactors_view.coffee
+++ core/app/backbone/views/interactors_view.coffee
@@ -42,10 +42,15 @@
     @fetch()

   insertItemSeperator: (itemView, index) ->
-    if index == 1 and @collection.totalRecords == @collection.length
+    if @is_penultimate(index) and @we_see_all_interactors()
       itemView.$el.append ' and '
-    else if index != 0
+    else if @isnt_last(index)
       itemView.$('a').after ','
+
+  is_penultimate: (index)-> index is last_index - 1
+  we_see_all_interactors: (index)-> @collection.totalRecords is @collection.length
+  isnt_last: (index)-> index is last_index
+  last_index: -> @collection.length - 1

   appendHtml: (collectionView, itemView, index) =>
     @insertItemSeperator itemView, index


Bug fix.

Bug fix.

--- app/assets/javascripts/web/counts_behavior.js.coffee
+++ app/assets/javascripts/web/counts_behavior.js.coffee
@@ -113,15 +113,12 @@
       if _.contains specialCollections, feedID
         selectFirst = true
       else
-        $("[data-behavior~=feed_link]", selected).click()
+        $("[data-behavior~=feed_link]", selected).first().click()
         feedbin.hideQueue.push(feedID) if !selected.is(":visible")
-
     feedbin.applyCounts(false)

     if selectFirst
       $('[data-behavior~=feeds_target] li:visible').first().find('a')[0].click();
-    # else
-    #   selected[0].scrollIntoView({behavior: "smooth", block: "center"}) if selected[0]

     $('[data-behavior~=change_view_mode]').blur()


Make the sorting and groupby underscore a little more DRY

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -81,6 +81,7 @@
     currentCat.get()
     Meteor.defer -> initColorPicker($('.color-picker-area'))

+
 Template._bootstrap_magic.helpers

   # "groups" : ->
@@ -106,8 +107,10 @@

   'click .menu-secondary-list' : ->
     currentCat.set(@category)
-    if (_.map _.where(bootstrap_magic_variables, { category: currentCat.get() }), (obj) -> obj).length is 1
-      currentSubCat.set(_.where(bootstrap_magic_variables, { category: currentCat.get() })[0].keyName)
+    myCat = currentCat.get()
+    objByCat = _.where(bootstrap_magic_variables, { category: myCat }
+    if (_.map objByCat, (obj) -> obj).length is 1
+      currentSubCat.set(objByCat)[0].keyName)

   'click .menu-tertiary-list' : -> currentSubCat.set(@keyName)


remove log statement

remove log statement

--- bokehjs/src/coffee/models/glyphs/segment.coffee
+++ bokehjs/src/coffee/models/glyphs/segment.coffee
@@ -75,7 +75,7 @@
       maxX: @renderer.xmapper.map_from_target(hr.max),
       maxY: @renderer.ymapper.map_from_target(vr.max)
     })
-    console.log (candidates)
+
     for i in candidates
       if v0[i]<=val<=v1[i] or v1[i]<=val<=v0[i]
         hits.push(i)


Restack context panel whenever it is (re)shown

--- src/coffee/cilantro/ui/workflows/results.coffee
+++ src/coffee/cilantro/ui/workflows/results.coffee
@@ -120,6 +120,7 @@
             @ui.contextContainer.css('display', 'block')
             @ui.resultsContainer.addClass('span9')
             @$('#toggle-context-panel-button').html('Hide Filters...')
+            @$('.context').stacked('restack')

         hideContextPanel: =>
             @areFiltersHidden = true


[LFTILI-196] removing time difference calculation conversion to second

--- src/coffee/directives/device_status_icon.coffee
+++ src/coffee/directives/device_status_icon.coffee
@@ -22,7 +22,7 @@
         else
           diff = current_time - timestamp
           $scope.last_heard = new Date timestamp
-          $scope.time_diff = Math.round diff * 0.001
+          $scope.time_diff = diff
           $scope.status = to_i ping_response['player:state']

       $scope.$on '$destroy', () ->


resove bokehjs merge

resove bokehjs merge

--- subtree/bokehjs/static/coffee/renderers/glyphs.coffee
+++ subtree/bokehjs/static/coffee/renderers/glyphs.coffee
@@ -63,4 +63,5 @@
 exports.segment           = segment.Segment
 exports.text              = text.Text
 exports.triangle          = triangle.Triangle
+exports.wedge             = wedge.Wedge
 exports.x                 = x.X


modify: show data from local storage if it's not empty

--- assets/coffee/index.coffee
+++ assets/coffee/index.coffee
@@ -2,20 +2,47 @@

 window.App = {}

-# test for backbone.js
-$ () ->
+# make dummy Note models
+initializeNotes = () ->
   noteCollection = new App.NoteCollection [
     title: 'テスト1'
-    body : 'テスト1だお'
+    body : 'テスト1だってばよ'
   ,
     title: 'テスト2'
-    body : 'テスト2だってばよ'
+    body : 'テスト2だよん'
+  ,
+    title: 'テスト3'
+    body : 'テスト3だよおおおお'
   ]

-  mainContainer = new App.Container
+  noteCollection.each (note) ->
+    note.save()
+
+  return noteCollection.models
+
+$ () ->
+  # intialize NoteCollection
+  # make reference on 'App' because
+  # you use this collection on another js file later
+  App.noteCollection = new App.NoteCollection
+
+  # initialize App.Container for the space
+  # that shows view of notes list
+  # make reference on 'App' in the same way
+  App.mainContainer = new App.Container
     el: '#main-container'

-  noteListView = new App.NoteListView
-    collection: noteCollection
+  # receive data of NoteCollection
+  # it's from local storage by Backbone.LocalStorage
+  App.noteCollection.fetch().then (notes) ->
+    # if data is empte, overwrite collection by dummy data
+    if notes.length == 0
+      models = initializeNotes()
+      App.noteCollection.reset models

-  mainContainer.show noteListView
+    # initialize parent view by collection
+    noteListView = new App.NoteListView
+      collection: App.noteCollection
+
+    # show notes list
+    App.mainContainer.show noteListView


change interval

change interval

--- atmospheric/002-accumulated-heat/csmain.coffee
+++ atmospheric/002-accumulated-heat/csmain.coffee
@@ -78,10 +78,9 @@
             lights = starlight.evolve(au.fromSI_T(tao))
             data = accumulator.accumulate(tao, time, lights[0], lights[1])

-            if counter == 0
-                viewer.paint(transformer.target(frame, (time / planet.period)), map, data)
+            viewer.paint(transformer.target(frame, (time / planet.period)), map, data)
             counter = (counter + 1) % 30
             time += tao
-        handle = setInterval(evolve, 1000)
+        handle = setInterval(evolve, 10000)

        true


Small improvements.

Small improvements.

--- lib.coffee
+++ lib.coffee
@@ -16,7 +16,7 @@
       throw new Error "Component '#{ componentName }' already registered under the name '#{ componentClass.componentName() }'."

     componentClass.componentName componentName
-    assert componentClass.componentName() is componentName
+    assert.equal componentClass.componentName(), componentName

     @components[componentName] = componentClass

@@ -43,6 +43,7 @@

   # We allow access to the component name through a method so that it can be accessed in templates in an easy way.
   componentName: ->
+    # Instance method is just a getter, not a setter as well.
     @constructor.componentName()

   @renderComponent: ->


write to the same buffer when encoding fields

--- src/FieldEncoder.coffee
+++ src/FieldEncoder.coffee
@@ -19,53 +19,47 @@

   encodeFieldBE: (buffer, obj, fieldSpec) ->
-    outBuffer = new Buffer(buffer)
-
     if obj[fieldSpec.name]?
       switch fieldSpec.type
-        when 'int8'   then outBuffer.writeInt8(obj[fieldSpec.name], fieldSpec.start)
-        when 'uint8'  then outBuffer.writeUInt8(obj[fieldSpec.name], fieldSpec.start)
-        when 'int16'  then outBuffer.writeInt16BE(obj[fieldSpec.name], fieldSpec.start)
-        when 'uint16' then outBuffer.writeUInt16BE(obj[fieldSpec.name], fieldSpec.start)
-        when 'float'  then outBuffer.writeFloatBE(obj[fieldSpec.name], fieldSpec.start)
-        when 'double' then outBuffer.writeDoubleBE(obj[fieldSpec.name], fieldSpec.start)
-        when 'ascii'  then outBuffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'ascii')
-        when 'utf8'   then outBuffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'utf8')
-        when 'uint32' then outBuffer.writeUInt32BE(obj[fieldSpec.name], fieldSpec.start)
-        when 'int32'  then outBuffer.writeInt32BE(obj[fieldSpec.name], fieldSpec.start)
+        when 'int8'   then buffer.writeInt8(obj[fieldSpec.name], fieldSpec.start)
+        when 'uint8'  then buffer.writeUInt8(obj[fieldSpec.name], fieldSpec.start)
+        when 'int16'  then buffer.writeInt16BE(obj[fieldSpec.name], fieldSpec.start)
+        when 'uint16' then buffer.writeUInt16BE(obj[fieldSpec.name], fieldSpec.start)
+        when 'float'  then buffer.writeFloatBE(obj[fieldSpec.name], fieldSpec.start)
+        when 'double' then buffer.writeDoubleBE(obj[fieldSpec.name], fieldSpec.start)
+        when 'ascii'  then buffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'ascii')
+        when 'utf8'   then buffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'utf8')
+        when 'uint32' then buffer.writeUInt32BE(obj[fieldSpec.name], fieldSpec.start)
+        when 'int32'  then buffer.writeInt32BE(obj[fieldSpec.name], fieldSpec.start)
         when 'bit'
           if obj[fieldSpec.name] is true # protection from type problems
-            outBuffer.writeUInt8(Math.pow(2, fieldSpec.position), fieldSpec.start)
+            buffer.writeUInt8(Math.pow(2, fieldSpec.position), fieldSpec.start)
           else
-            outBuffer.writeUInt8(0, fieldSpec.start)
-
+            buffer.writeUInt8(0, fieldSpec.start)
         #TODO error case

-    return outBuffer
+    return buffer

   encodeFieldLE: (buffer, obj, fieldSpec) ->
-    outBuffer = new Buffer(buffer)
-
     if obj[fieldSpec.name]?
       switch fieldSpec.type
-        when 'int8'   then outBuffer.writeInt8(obj[fieldSpec.name], fieldSpec.start)
-        when 'uint8'  then outBuffer.writeUInt8(obj[fieldSpec.name], fieldSpec.start)
-        when 'int16'  then outBuffer.writeInt16LE(obj[fieldSpec.name], fieldSpec.start)
-        when 'uint16' then outBuffer.writeUInt16LE(obj[fieldSpec.name], fieldSpec.start)
-        when 'float'  then outBuffer.writeFloatLE(obj[fieldSpec.name], fieldSpec.start)
-        when 'double' then outBuffer.writeDoubleLE(obj[fieldSpec.name], fieldSpec.start)
-        when 'ascii'  then outBuffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'ascii')
-        when 'utf8'   then outBuffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'utf8')
-        when 'uint32' then outBuffer.writeUInt32LE(obj[fieldSpec.name], fieldSpec.start)
-        when 'int32'  then outBuffer.writeInt32LE(obj[fieldSpec.name], fieldSpec.start)
+        when 'int8'   then buffer.writeInt8(obj[fieldSpec.name], fieldSpec.start)
+        when 'uint8'  then buffer.writeUInt8(obj[fieldSpec.name], fieldSpec.start)
+        when 'int16'  then buffer.writeInt16LE(obj[fieldSpec.name], fieldSpec.start)
+        when 'uint16' then buffer.writeUInt16LE(obj[fieldSpec.name], fieldSpec.start)
+        when 'float'  then buffer.writeFloatLE(obj[fieldSpec.name], fieldSpec.start)
+        when 'double' then buffer.writeDoubleLE(obj[fieldSpec.name], fieldSpec.start)
+        when 'ascii'  then buffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'ascii')
+        when 'utf8'   then buffer.write(obj[fieldSpec.name], fieldSpec.start, fieldSpec.length, 'utf8')
+        when 'uint32' then buffer.writeUInt32LE(obj[fieldSpec.name], fieldSpec.start)
+        when 'int32'  then buffer.writeInt32LE(obj[fieldSpec.name], fieldSpec.start)
         when 'bit'
           if obj[fieldSpec.name] is true # protection from type problems
-            outBuffer.writeUInt8(Math.pow(2, fieldSpec.position), fieldSpec.start)
+            buffer.writeUInt8(Math.pow(2, fieldSpec.position), fieldSpec.start)
           else
-            outBuffer.writeUInt8(0, fieldSpec.start)
+            buffer.writeUInt8(0, fieldSpec.start)
         #TODO error case

-    return outBuffer
-
+    return buffer

 module.exports = FieldEncoder


Store tab views into an array in TabBarView

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -14,6 +14,7 @@
     @setAttribute("tabindex", -1)

   initialize: (@pane) ->
+    @tabs = []
     @subscriptions = new CompositeDisposable

     @subscriptions.add atom.commands.add atom.views.getView(@pane),
@@ -105,22 +106,30 @@
       @pane.moveItem(item, @pane.getItems().length - 1) unless @isItemMovingBetweenPanes

   moveItemTabToIndex: (item, index) ->
-    if tab = @tabForItem(item)
+    tabIndex = @tabs.findIndex((t) -> t.item is item)
+    if tabIndex isnt -1
+      tab = @tabs[tabIndex]
       tab.remove()
+      @tabs.splice(tabIndex, 1)
       @insertTabAtIndex(tab, index)

   insertTabAtIndex: (tab, index) ->
-    followingTab = @tabAtIndex(index) if index?
+    followingTab = @tabs[index] if index?
     if followingTab
       @insertBefore(tab, followingTab)
+      @tabs.splice(index, 0, tab)
     else
       @appendChild(tab)
+      @tabs.push(tab)

     tab.updateTitle()
     @updateTabBarVisibility()

   removeTabForItem: (item) ->
-    @tabForItem(item)?.destroy()
+    tabIndex = @tabs.findIndex((t) -> t.item is item)
+    if tabIndex isnt -1
+      @tabs[tabIndex].destroy()
+      @tabs.splice(tabIndex, 1)
     tab.updateTitle() for tab in @getTabs()
     @updateTabBarVisibility()

@@ -140,13 +149,13 @@
       @classList.remove('hidden')

   getTabs: ->
-    tab for tab in @querySelectorAll(".tab")
+    @tabs.slice()

   tabAtIndex: (index) ->
-    @querySelectorAll(".tab")[index]
+    @tabs[index]

   tabForItem: (item) ->
-    _.detect @getTabs(), (tab) -> tab.item is item
+    @tabs.find((t) -> t.item is item)

   setActiveTab: (tabView) ->
     if tabView? and not tabView.classList.contains('active')


Use new IPC API for sending event to window

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -1,4 +1,4 @@
-BrowserIpc = null # Defer require until actually used
+BrowserWindow = null # Defer require until actually used
 RendererIpc = require('ipc')

 {$, View} = require 'atom'
@@ -252,10 +252,18 @@

         if not isNaN(fromProcessId) and not isNaN(fromProcessId)
           # Let the window where the drag started know that the tab was dropped
-          BrowserIpc ?= require('remote').require('ipc')
-          BrowserIpc.sendChannel(fromProcessId, fromRoutingId, 'tab:dropped', fromPaneId, fromIndex)
+          browserWindow = @browserWindowForProcessIdAndRoutingId(fromProcessId, fromRoutingId)
+          browserWindow?.webContents.send('tab:dropped', fromPaneId, fromIndex)

       atom.focus()
+
+  browserWindowForProcessIdAndRoutingId: (processId, routingId) ->
+    BrowserWindow ?= require('remote').require('browser-window')
+    for browserWindow in BrowserWindow.getAllWindows()
+      if browserWindow.getProcessId() is processId and browserWindow.getRoutingId() is routingId
+        return browserWindow
+
+    null

   moveItemBetweenPanes: (fromPane, fromIndex, toPane, toIndex, item) ->
     if toPane is fromPane


test: fix test for status code validation

--- test/unit/mixins/validatable-http-message-test.coffee
+++ test/unit/mixins/validatable-http-message-test.coffee
@@ -1139,7 +1139,7 @@
           assert.isFalse instance.isValid()

         it 'should set beutiful error message', () ->
-          assert.equal instance.validation.statusCode.results[0].message, "Status code is not '201'"
+          assert.equal instance.validation.statusCode.results[0].message, "Status code is '200' instead of '201'"

     describe '#isJsonContentType',  () ->


Remove load-more button if end of timeline.

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -14,8 +14,11 @@
   timeline = button.parent().siblings('ul.timeline')
   $.getJSON timeline.data('next-url'), (data) ->
     loading.stop()
-    html = $.parseHTML(data.html)[0].innerHTML
-    timeline.append(html).data('next-url', data.next)
+    if data.items.length > 0
+      html = $.parseHTML(data.html)[0].innerHTML
+      timeline.append(html).data('next-url', data.next)
+    else
+      button.hide()

 # # # search # # #


[backport] fix watchers close problems

[backport] fix watchers close problems

--- app/coffee/modules/common/lightboxes.coffee
+++ app/coffee/modules/common/lightboxes.coffee
@@ -75,12 +75,15 @@
         docEl = angular.element(document)
         docEl.off(".lightbox")
         docEl.off(".keyboard-navigation") # Hack: to fix problems in the WYSIWYG textareas when press ENTER
-        $el.one "transitionend", =>
-            $el.removeAttr('style')
-            $el.removeClass("open").removeClass('close')

         @animationFrame.add ->
             $el.addClass('close')
+
+            $el.one "transitionend", =>
+                $el.removeAttr('style')
+                $el.removeClass("open").removeClass('close')
+
+

         if $el.hasClass("remove-on-close")
             scope = $el.data("scope")
@@ -658,7 +661,7 @@
             render(users)
             $el.find("input").focus()

-        $el.on "click", ".user-list-single", debounce 2000, (event) ->
+        $el.on "click", ".user-list-single", debounce 200, (event) ->
             closeLightbox()

             event.preventDefault()


clean up code in ujsLinks

clean up code in ujsLinks

clean up update code style to not use $.proxy,
like we talked about in our meeting and a couple
other minor fixes

Change-Id: I9537f7e40bc63fe17b14fac20e80111fbd48af24
Reviewed-on: https://gerrit.instructure.com/14021
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Zach Pendleton <514dbb4d54d4f29e454990df7d075678f43f92aa@instructure.com>

--- app/coffeescripts/behaviors/ujsLinks.coffee
+++ app/coffeescripts/behaviors/ujsLinks.coffee
@@ -5,10 +5,10 @@

   ##
   # Handles "data-method" on links such as:
-  # Delete
+  # Delete
   handleMethod = (link) ->
     link.data 'handled', true
-    href = link.attr('href')
+    href = link.data('url') || link.attr('href')
     method = link.data('method')
     target = link.attr('target')
     form = $("<form method='post' action='#{href}'></form>")
@@ -65,14 +65,16 @@
     # bind the 'beforeremove' and 'remove' events if you want to handle this with your own code
     # if you stop propigation this will not remove it
     $elToRemove.bind
-      beforeremove: $.proxy($elToRemove, 'hide')
-      remove: $.proxy($elToRemove, 'remove')
+      beforeremove: -> $elToRemove.hide()
+      remove: -> $elToRemove.remove()

     $elToRemove.trigger 'beforeremove'
+
     triggerRemove = -> $elToRemove.trigger 'remove'
+    revert = -> $elToRemove.show()

     if url
-      $.ajaxJSON url, "DELETE", triggerRemove, $.proxy($elToRemove, 'show')
+      $.ajaxJSON url, "DELETE", {}, triggerRemove, revert
     else
       triggerRemove()


Open scripts to depth 5 at first in level editor scripts tab

--- app/views/editor/level/scripts/ScriptsTabView.coffee
+++ app/views/editor/level/scripts/ScriptsTabView.coffee
@@ -94,7 +94,7 @@
     #@scriptTreema?.destroy() # TODO: get this to work
     @scriptTreema = @$el.find('#script-treema').treema treemaOptions
     @scriptTreema.build()
-    @scriptTreema.childrenTreemas?.noteChain?.open()
+    @scriptTreema.childrenTreemas?.noteChain?.open(5)
     @selectedScriptPath = newPath

   getThangIDs: ->


Checks for relatedTarget If related target is undefined then we can't complete the below code

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -217,11 +217,12 @@
       if e.which is 27 # Escape key
         $('.dropdown-menu-close', @dropdown).trigger 'click'
     @dropdown.on 'blur', 'a', (e) =>
-      $relatedTarget = $(e.relatedTarget)
-      $dropdownMenu = $relatedTarget.closest('.dropdown-menu')
-
-      if $dropdownMenu.length is 0
-        @dropdown.removeClass('open')
+      if e.relatedTarget?
+        $relatedTarget = $(e.relatedTarget)
+        $dropdownMenu = $relatedTarget.closest('.dropdown-menu')
+
+        if $dropdownMenu.length is 0
+          @dropdown.removeClass('open')

     if @dropdown.find(".dropdown-toggle-page").length
       @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>


Remove dangling function

Remove dangling function

--- src/packages/command-panel/lib/preview-list.coffee
+++ src/packages/command-panel/lib/preview-list.coffee
@@ -7,7 +7,7 @@
 module.exports =
 class PreviewList extends ScrollView
   @content: ->
-    @ol class: 'preview-list', tabindex: -1, ->
+    @ol class: 'preview-list', tabindex: -1

   selectedOperationIndex: 0
   operations: null


:lipstick:

:lipstick:

--- src/packages/command-panel/src/preview-list.coffee
+++ src/packages/command-panel/src/preview-list.coffee
@@ -83,7 +83,7 @@
     editSession = @rootView.open(operation.getPath())
     bufferRange = operation.execute(editSession)
     editSession.setSelectedBufferRange(bufferRange, autoscroll: true) if bufferRange
-    @.focus()
+    @focus()
     false

   getPathCount: ->


Added definitions for AMD & CommonJS modules

--- src/Overloadable.coffee
+++ src/Overloadable.coffee
@@ -103,7 +103,6 @@
       return false unless compiledMatcher.match(argument)

     true
-

 class MatcherFactory
   constructor: () ->
@@ -187,7 +186,7 @@
         hasProperty = propertyName of argument
       else
         propertyName = property
-        hasProperty = Object.prototype.hasOwnProperty.call(argument, propertyName)
+        hasProperty = Object::hasOwnProperty.call(argument, propertyName)

       if hasProperty isnt true then return false

@@ -199,7 +198,6 @@

   matcherFactory.registerMatcher("object", PropertyMatcher)

-
 class CompiledMatcher
   constructor: (@_matcher, @_value) ->

@@ -209,4 +207,10 @@

   match: (argument) -> @getMatcher().match(argument, @getValue())

-@Overloadable = Overloadable+
+if define?.amd?
+  define(-> Overloadable)
+else if module?.exports?
+  module.exports = Overloadable
+else
+  @Overloadable = Overloadable


:lipstick:

:lipstick:

--- src/packages/snippets/spec/snippets-spec.coffee
+++ src/packages/snippets/spec/snippets-spec.coffee
@@ -218,7 +218,7 @@
       snippets.loaded = false
       snippets.loadAll()

-      waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
+      waitsFor "all snippets to load", 5000, -> snippets.loaded

       runs ->
         expect(syntax.getProperty(['.test'], 'snippets.test')?.constructor).toBe Snippet
@@ -232,7 +232,7 @@
       snippets.loaded = false
       snippets.loadAll()

-      waitsFor((-> snippets.loaded), "Waiting for all snippets to load", 5000)
+      waitsFor "all snippets to load", 5000, -> snippets.loaded

       runs ->
         snippet = syntax.getProperty(['.source.js'], 'snippets.fun')


fix statement constant complexities

fix statement constant complexities

--- chuck.coffee
+++ chuck.coffee
@@ -31,24 +31,25 @@
     when "throw"
       cc = calculateExpressionComplexity statement.throws
     when "declaration"
-      cc = calculateExpressionComplexity statement.initializer
-      cc = if cc then cc + 1 else 0
+      cc = if statement.initializer then 1 + calculateExpressionComplexity statement.initializer else 0
     when "assignment", "prefix", "postfix", "methodCall"
       cc = calculateExpressionComplexity statement.expression
     when "if"
-      cc = if statement.elseBlock then 1 else 0
+      cc = if statement.elseBlock then 2 else 1
       cc += calculateExpressionComplexity statement.condition
       cc += calculateStatementComplexity statement.block
     when "while", "do_while"
-      cc = calculateExpressionComplexity statement.condition
+      cc = 1
+      cc += calculateExpressionComplexity statement.condition
       cc += calculateStatementComplexity statement.block
     when "for"
+      cc = 1
       if statement.initializer?
-        cc = calculateExpressionComplexity statement.initializer
+        cc += calculateExpressionComplexity statement.initializer
         cc += calculateExpressionComplexity statement.condition
         cc += calculateExpressionComplexity statement.increment
       else
-        cc = calculateExpressionComplexity statement.domain
+        cc += calculateExpressionComplexity statement.domain
       cc += calculateStatementComplexity statement.block
     when "dml"
       if statement.operation is 'merge'


add comment

add comment

--- src/scripts/helpers/backbone/views/editable.coffee
+++ src/scripts/helpers/backbone/views/editable.coffee
@@ -93,6 +93,7 @@
                   alohaEditable = Aloha.getEditableById(alohaId)

                   if 'content' == value
+                    # See aloha.coffee for where this is used
                     window.GLOBAL_UPOADER_HACK = () =>
                       editableBody = alohaEditable.getContents()
                       @model.set(value, editableBody)


Modify browserify path

Modify browserify path

--- gulp/tasks/browserify.coffee
+++ gulp/tasks/browserify.coffee
@@ -1,3 +1,4 @@
+path = require 'path'
 gulp = require 'gulp'
 map = require 'vinyl-map'
 source = require 'vinyl-source-stream'
@@ -24,13 +25,16 @@
   distPath = if isWatch then '.tmp/scripts' else 'dist/scripts'
   map( (contents, filename)->
     fname = get_name(filename)
+    dirSet = {
+      vendor: path.join(__dirname, '../../app/bower_components')
+      utils: path.join(__dirname, '../../app/scripts/reactjs/utils')
+      stores: path.join(__dirname, '../../app/scripts/reactjs/stores')
+      components: path.join(__dirname, '../../app/scripts/reactjs/components')
+    }
     pathOpts = {
-      mods: [
-        pathmodify.mod.dir('vendor', '../bower_components')
-        pathmodify.mod.dir('utils', './reactjs/utils')
-        pathmodify.mod.dir('stores', './reactjs/stores')
-        pathmodify.mod.dir('components', './reactjs/components')
-      ]
+      mods: Object.keys(dirSet).map((key) ->
+        pathmodify.mod.dir(key, dirSet[key])
+      )
     }
     b = browserify(
       cache: {}


fixes for spectrogram

fixes for spectrogram

--- examples/audio/static/coffee/spectrogram.coffee
+++ examples/audio/static/coffee/spectrogram.coffee
@@ -204,8 +204,8 @@
     @yrange = Collections('Range1d').create({start: 0, end: MAX_FREQ})

     @model = Collections('Plot').create(
-      x_range: @xrange
-      y_range: @yrange
+      x_range: @xrange.ref()
+      y_range: @yrange.ref()
       border_fill: "#fff"
       canvas_width: options.width + options.border + 80
       canvas_height: options.height + 2*options.border
@@ -223,7 +223,7 @@
         location: 'min'
         bounds: 'auto'
       }
-      parent: @model
+      plot: @model.ref()
     )

     yaxis = Collections('GuideRenderer').create(
@@ -234,7 +234,7 @@
         bounds: 'auto'
       }
       major_label_standoff: 40
-      parent: @model
+      plot: @model.ref()
     )

     glyph = Collections('GlyphRenderer').create({
@@ -303,8 +303,8 @@
     @range = Collections('Range1d').create({start: -20, end: 20})

     @model = Collections('Plot').create(
-      x_range: @range
-      y_range: @range
+      x_range: @range.ref()
+      y_range: @range.ref()
       border_fill: "#fff"
       canvas_width:  options.width
       canvas_height: options.height
@@ -386,8 +386,8 @@
     @yrange = Collections('Range1d').create({start: options.y0, end: options.y1})

     @model = Collections('Plot').create(
-      x_range: @xrange
-      y_range: @yrange
+      x_range: @xrange.ref()
+      y_range: @yrange.ref()
       border_fill: "#fff"
       canvas_width:  options.width  + 2*options.border
       canvas_height: options.height + 2*options.border
@@ -403,7 +403,7 @@
         location: 'min'
         bounds: 'auto'
       }
-      parent: @model
+      plot: @model.ref()
     )

     yaxis = Collections('GuideRenderer').create(
@@ -413,22 +413,22 @@
         location: 'min'
         bounds: 'auto'
       }
-      parent: @model
+      plot: @model.ref()
     )

     glyph = Collections('GlyphRenderer').create({
-      data_source: @source
-      xdata_range: @xrange
-      ydata_range: @yrange
+      data_source: @source.ref()
+      xdata_range: @xrange.ref()
+      ydata_range: @yrange.ref()
       glyphspec: {
-        type: 'line'
+        type: 'multi_line'
         line_color: 'darkblue'
         xs: 'idx'
         ys: 'ys'
       }
     })

-    @model.add_renderers([glyph, xaxis, yaxis])
+    @model.add_renderers([glyph.ref(), xaxis.ref(), yaxis.ref()])
     @view = new @model.default_view(model: @model)

   update: (ys) ->


Fixing width issues in ie11

Fixing width issues in ie11

fixes #4

--- ekko-lightbox.coffee
+++ ekko-lightbox.coffee
@@ -112,7 +112,7 @@
 		@modal_body.html '
Loading..
'

 	showYoutubeVideo : (id) ->
-		@resize(560, 315)
+		@resize(560)
 		@modal_body.html ''

 	error : ( message ) ->
@@ -123,11 +123,10 @@
 		img = new Image()
 		if !onLoadShowImage? || onLoadShowImage == true
 			img.onload = =>
+				width = img.width
 				@checkImageDimensions(img)
 				@modal_body.html img
-				i = @modal_body.find('img').first()
-				width = if i && i.width() > 0 then i.width() else img.width
-				@resize width, i.height()
+				@resize width
 			img.onerror = =>
 				@error 'Failed to load image: ' + src

@@ -136,9 +135,8 @@
 	close : ->
 		@modal.modal('hide');

-	resize : ( width, height ) ->
+	resize : ( width ) ->
 		width = width + @padding.left + @padding.right
-		#height = height + @padding.top + @padding.bottom
 		@modal.find('.modal-content').css {
 			'width' : width
 		}


Updated rank my game check, fixes #666

Updated rank my game check, fixes #666

--- app/views/play/ladder/my_matches_tab.coffee
+++ app/views/play/ladder/my_matches_tab.coffee
@@ -112,9 +112,18 @@

   readyToRank: (session) ->
     return false unless session?.get('levelID')  # If it hasn't been denormalized, then it's not ready.
+
     c1 = session?.get('code')
     c2 = session?.get('submittedCode')
-    c1 and not _.isEqual(c1, c2)
+    team = session?.get('team')
+    thangSpellArr = (s.split("/") for s in session?.get('teamSpells')[team])
+    return false unless c1
+
+    for item in thangSpellArr
+      thang = item[0]
+      spell = item[1]
+      return true if c1[thang][spell] isnt c2?[thang][spell]
+    return false

   rankSession: (e) ->
     button = $(e.target).closest('.rank-button')


Fixing BBox (swapping lat/lon). Setting footprint in record.

--- src/sources/eowcs.coffee
+++ src/sources/eowcs.coffee
@@ -17,7 +17,7 @@

     fetch: (start, end, params, callback) ->
         url = kvp.describeEOCoverageSetURL(
-            @options.url, @params.eoid or @options.eoid, {
+            @options.url, params?.eoid or @options.eoid, {
                 subsetTime: [
                     @formatDate(start), @formatDate(end)
                 ]
@@ -38,16 +38,22 @@
             if response.coverageDescriptions? and response.coverageDescriptions.length > 0
                 for coverage in response.coverageDescriptions
                     bbox = [
+                        coverage.bounds.lower[1],
                         coverage.bounds.lower[0],
-                        coverage.bounds.lower[1],
-                        coverage.bounds.upper[0],
-                        coverage.bounds.upper[1]
+                        coverage.bounds.upper[1],
+                        coverage.bounds.upper[0]
                     ]
+                    footprint = []
+                    for i in [0...coverage.footprint.length] by 2
+                        footprint.push(
+                            [coverage.footprint[i+1], coverage.footprint[i]]
+                        )
                     records.push([
                         new Date(coverage.timePeriod[0]),
                         new Date(coverage.timePeriod[1]), {
-                            title: coverage.coverageId,
-                            bbox: bbox
+                            id: coverage.coverageId,
+                            bbox: bbox,
+                            footprint: footprint
                         }
                     ])


weather script

weather script

--- scripts/weather.coffee
+++ scripts/weather.coffee
@@ -43,9 +43,9 @@
                 minTemp = dailyData.temperatureMin
                 deg = '°F'
                 msg.send(address + '\n' +
-                        'Currently: ' + Math.round(currentTemp) + deg  + '\n' +
-                        'High:       ' + Math.round(maxTemp) + deg + '\n' +
-                        'Low:         ' + Math.round(minTemp) + deg )
+                        'Currently:   ' + Math.round(currentTemp) + deg  + '\n' +
+                        'High:            ' + Math.round(maxTemp) + deg + '\n' +
+                        'Low:             ' + Math.round(minTemp) + deg )
               )
               .catch( (err) ->
                 msg.send('No weather today. Stay indoors and don\'t look outside.')


avoid exception when elements in pdf view have been removed

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -169,6 +169,11 @@
 		@computeOffset = (page, position) ->
 			# console.log 'computing offset for', page, position
 			element = page.element
+			#console.log 'element =', $(element), 'parent =', $(element).parent()
+			t1 = $(element).offset()?.top
+			t2 = $(element).parent().offset()?.top
+			if not (t1? and t2?)
+				return $q((resolve, reject) -> reject('elements destroyed'))
 			pageTop = $(element).offset().top - $(element).parent().offset().top
 			# console.log('top of page scroll is', pageTop, 'vs', page.elemTop)
 			# console.log('inner height is', $(element).innerHeight())


Use thread id for comment id

Use thread id for comment id

--- services/web/public/coffee/ide/review-panel/RangesTracker.coffee
+++ services/web/public/coffee/ide/review-panel/RangesTracker.coffee
@@ -107,7 +107,7 @@
 		addComment: (op, metadata) ->
 			# TODO: Don't allow overlapping comments?
 			@comments.push comment = {
-				id: @newId()
+				id: op.t or @newId()
 				op: # Copy because we'll modify in place
 					c: op.c
 					p: op.p


Fix merge

Fix merge

--- client/main.coffee
+++ client/main.coffee
@@ -47,4 +47,3 @@

 Router.route '/profile', ->
 	this.render 'profile'
->>>>>>> origin/master


Implement Categories, clean up old code

Implement Categories, clean up old code

--- client/pack.coffee
+++ client/pack.coffee
@@ -1,6 +0,0 @@
-Pack.set "bitly", "homeTitle", "Shorten your URLs with Bitly"
-Pack.set "bitly", "welcomeTemplate", "packWelcome"
-
-# objects must be created anew, unlike scalar values which can be reused
-Pack.set "bitly", "getAppsSelector", ->
-  {key: {$in: ["bitly"]}}


Fixed micro dom

Fixed micro dom

--- source/micro.dom.coffee
+++ source/micro.dom.coffee
@@ -25,7 +25,6 @@
   find: (selector) ->
     results = new u()
     this.each ->
-      window.el = this
       push.apply results, slice.call(this.querySelectorAll selector)
     return results


Refactors and additional methods

Refactors and additional methods

--- src/eventEmitter.coffee
+++ src/eventEmitter.coffee
@@ -25,41 +25,108 @@
   # Will probably want to put this on something other than document.
   document.dispatchEvent(sdkEvent)

-emit = (data) ->
-  eventFactory('teamsnap-sdk-event', data)
-
-requestResponse = (method, xhr) ->
+getIdForItemData = (itemData) ->
+  id = null
+  itemData.some (field) ->
+    if field.name is 'id'
+      parsedId = parseInt(field.value)
+      id = parsedId if parsedId
+      return true
+    else
+      return false
+  return id
+
+buildEventDataFromCollection = (collection, method, refIds) ->
+  eventData = null
+
+  if collection.items?.length
+    refIds = collection.items.map (item) -> getIdForItemData(item.data)
+    refIds = refIds.filter (id) -> id?
+
+    if refIds?
+      unless Array.isArray(refIds)
+        refIds = [refIds]
+      if refIds.length
+        eventData = {
+          method: method,
+          collection: collection.rel,
+          refIds: refIds
+        }
+  return eventData
+
+emit = (data, eventType = 'event') ->
+  eventName = 'teamsnap-sdk-' + eventType
+  eventFactory(eventName, data)
+
+requestBuilder = (requestId, method, url, data, error) ->
+  eventName = 'request-start'
+  if error?
+    eventName = 'request-error'
+
+  urlParts = url.split('/')
+  lastUrlPart = urlParts[urlParts.length - 1]
+
+  if method.toUpperCase() is 'DELETE'
+    # Since `DELETE` requests have no response body, we need to get the
+    # collection / id from the request URL.
+    id = parseInt(urlParts[urlParts.length - 1])
+    collection = urlParts[urlParts.length - 2]
+
+    eventData = {
+      requestId: requestId,
+      method: method,
+      collection: collection,
+      data: {
+        id: id
+      }
+    }
+
+    eventData.error = error if error?
+
+    emit(eventData, eventName)
+
+  else if lastUrlPart is 'search'
+    types = data.types
+
+    types.forEach (type) ->
+      eventData = {
+        requestId: requestId,
+        method: method,
+        collection: urlParts[urlParts.length - 2],
+        data: data
+      }
+
+      eventData.error = error if error?
+
+      emit(eventData, eventName)
+
+  else
+    type = types.getSingularType(lastUrlPart)
+    # is this a valid collection?
+    if type
+      eventData = {
+        requestId: requestId,
+        method: method,
+        collection: lastUrlPart,
+        data: data
+      }
+
+      eventData.error = error if error?
+
+    emit(eventData, eventName)
+
+## -- REQUEST START -- ##
+requestStart = (requestId, method, url, data) ->
+  requestBuilder(requestId, method, url, data)
+
+## -- REQUEST ERROR -- ##
+requestError = (requestId, method, url, data, error) ->
+  requestBuilder(requestId, method, url, data, error)
+
+## -- REQUEST RESPONSE -- ##
+requestResponse = (requestId, method, xhr) ->
   data = xhr.data
   eventData = null
-
-  buildEventDataFromCollection = (collection) ->
-    eventData = null
-
-    getIdForItemData = (itemData) ->
-      id = null
-      itemData.some (field) ->
-        if field.name is 'id'
-          parsedId = parseInt(field.value)
-          id = parsedId if parsedId
-          return true
-        else
-          return false
-      return id
-
-    if collection.items?.length
-      refIds = collection.items.map (item) -> getIdForItemData(item.data)
-      refIds = refIds.filter (id) -> id?
-
-      if refIds?
-        unless Array.isArray(refIds)
-          refIds = [refIds]
-        if refIds.length
-          eventData = {
-            method: method,
-            collection: collection.rel,
-            refIds: refIds
-          }
-    return eventData

   if method.toUpperCase() is 'DELETE'
     # Since `DELETE` requests have no response body, we need to get the
@@ -68,24 +135,34 @@
     refIds = [parseInt(urlParts[urlParts.length - 1])]
     collection = urlParts[urlParts.length - 2]

-    eventData = {
-      method: method,
-      collection: collection,
-      refIds: refIds,
-    }
-    emit(eventData)
+    type = types.getSingularType(collection)
+    # is this a valid collection?
+    if type
+      eventData = {
+        requestId: requestId,
+        method: method,
+        collection: collection,
+        refIds: refIds,
+      }
+      emit(eventData, 'request-response')
+
   else if Array.isArray(data)
     # loop over collections in array
     data.forEach (response) ->
       # if items exist, return event for each item
       # (this shouldn't really happen very often)
-      eventData = buildEventDataFromCollection(response.collection)
-      emit(eventData) if eventData
+      type = types.getSingularType(collection.collection)
+      # is this a valid collection?
+      if type
+        eventData = buildEventDataFromCollection(response.collection, method, refIds)
+        if eventData
+          eventData.requestId = requestId
+          emit(eventData, 'request-response')
   else if data.collection.rel is 'bulk_load'
     # When bulk_load, group items by collection and emit
     # events for each collection
     collections = {}
-    collectionItems = data.collection.items
+    collectionItems = data.collection?.items
     if collectionItems?.length
       collectionItems.forEach (item) ->
         itemType = null
@@ -105,14 +182,24 @@

       Object.keys(collections).forEach (collectionName) ->
         collection = collections[collectionName]
-
-        eventData = buildEventDataFromCollection(collection)
-        emit(eventData) if eventData
+        type = types.getSingularType(collectionName)
+        # is this a valid collection?
+        if type
+          eventData = buildEventDataFromCollection(collection, method, refIds)
+          if eventData
+            eventData.requestId = requestId
+            emit(eventData, 'request-response')
   else
-    eventData = buildEventDataFromCollection(data.collection)
-    emit(eventData) if eventData
+    eventData = buildEventDataFromCollection(data.collection, method, refIds)
+    type = types.getSingularType(data.collection)
+    # is this a valid collection?
+    if type and eventData
+      eventData.requestId = requestId
+      emit(eventData, 'request-response')

 module.exports = {
   emit: emit,
-  requestResponse: requestResponse
+  requestStart: requestStart,
+  requestError: requestError,
+  requestResponse: requestResponse,
 }


Revert "Only attach/start sending after runtime started"

This reverts commit 5d812382f13b1a16cae789ee478ed6299d0c4433.

--- bin/simulatePortals.coffee
+++ bin/simulatePortals.coffee
@@ -26,14 +26,7 @@

       inst.once 'ready', ->
         console.log 'Graph is ready'
-        inst.start()
-
-      callbackRan = false
-      inst.runtime.on 'execution', (state) ->
-        console.log 'execution state changed', state
-        if not callbackRan and state.running
-          callbackRan = true
-          callback null, inst
+        callback null, inst

 loadGraph (err, inst) ->
   if err
@@ -44,6 +37,7 @@
   show = noflo.internalSocket.createSocket()
   inst.inPorts.pixel.attach pixel
   inst.inPorts.show.attach show
+  inst.start()

   # TODO: don't send until runtime hits running state


Add some comments about behavior

Add some comments about behavior

--- lib/list-edit.coffee
+++ lib/list-edit.coffee
@@ -42,6 +42,9 @@

   cut: ->
     console.log 'Executing command list-edit-cut'
+    #  1 elt:  remove, including whitespace
+    # >1 elts: if not last, remove, put pre whitespace on pre whitespace of next element
+    #         if last, put post whitespace on previous element

     # Not cut, but some test code to easily visualize ranges
     editor = atom.workspace.getActiveTextEditor()
@@ -74,9 +77,30 @@
   copy: ->
     console.log 'Executing command list-edit-copy'

+    # Select, strip whitespace and copy to clipboard
+
   paste: ->
     console.log 'Executing command list-edit-paste'

+    # On element, replace element with stripped clipboard, while preserving whitespace
+    #   1 elt,  make up bracket space, just use sep+' ' for now
+    # > 1 elts, before first element: newElt.whitespace = element[0].whitespace, element[0].whitespace.pre =element[1].whitespace.pre
+    #           after last element:   newElt.whitespace = element[n-1].whitespace, element[n-1].whitespace.post =element[n-2].whitespace.post
+    #           between i and i+1:    newElt.whitespace.pre = element[i+1].whitepace.pre, newElt.whitespace.post = element[i].whitepace.post
+    # maybe always using third rule and having special cases for i=0 and i = n-1 is elegant? (patching the existing elt's whitespace in those cases may make it less elegant)
+
+    # maybe declare bracketRightWhitespace bracketLeftWhitespace, separatorLeftWhitespace and separatorRightWhitespace
+    # and use those to format the necessary elements? Or maybe not, as explicit distinction may not be necessary
+
+    # for testing, use digits as whitespace
+
+    # need some function to determing if we're on the element, before, or after
+    # setPreWhitespace
+    # getPreWhitespace
+    #           patch next or previous element's whitespace
+
+    # [bracket-space-1ELTbracket-space2]
+    # [bracket-space-1ELTsep-space-1,sep-space-2ELTbracketspace2]
   delete: ->
     console.log 'Executing command list-edit-delete'


Don’t need the absolute path, just `variables`

`static` is already on the path

--- src/theme-manager.coffee
+++ src/theme-manager.coffee
@@ -181,8 +181,8 @@
     try
       if importFallbackVariables
         baseVarImports = """
-        @import "#{path.join(@resourcePath, 'static', 'variables', 'ui-variables')}";
-        @import "#{path.join(@resourcePath, 'static', 'variables', 'syntax-variables')}";
+        @import "variables/ui-variables";
+        @import "variables/syntax-variables";
         """
         less = fs.readFileSync(lessStylesheetPath, 'utf8')
         @lessCache.cssForFile(lessStylesheetPath, [baseVarImports, less].join('\n'))
@@ -190,7 +190,7 @@
         @lessCache.read(lessStylesheetPath)
     catch e
       console.error """
-        Error compiling less stylesheet: #{importFallbackVariables} #{lessStylesheetPath}
+        Error compiling less stylesheet: #{lessStylesheetPath}
         Line number: #{e.line}
         #{e.message}
       """


need to check for if all taskings are given from server before assuming is all, since all but one tasking could be disabled and give a false common tasking

--- src/flux/tasking.coffee
+++ src/flux/tasking.coffee
@@ -141,6 +141,11 @@

   _.extend(commonOpenDate, commonDueDate)

+hasTaskings = (taskings, compareTaskings) ->
+  _.every taskings, (tasking) ->
+    taskingIndex = toTaskingIndex(tasking)
+    compareTaskings[taskingIndex]?
+
 # sample _defaults
 # "#{courseId}": {
 #   "all": {
@@ -247,12 +252,16 @@
   loadTaskings: (taskId, taskings) ->

     blankTaskings = @exports._getBlankTaskings.call(@, taskId)
-    commonTasking = getCommonTasking(taskings)
-
-    isAll = commonTasking?
+    isAll = false
+
+    taskingsToLoad = transformTaskings(taskings)
+    hasAllTaskings = hasTaskings(blankTaskings, taskingsToLoad)
+
+    if hasAllTaskings
+      commonTasking = getCommonTasking(taskings)
+      isAll = commonTasking?
+
     @updateTaskingsIsAll(taskId, isAll)
-
-    taskingsToLoad = transformTaskings(taskings)

     baseTaskingToLoad = getCommonDates(taskingsToLoad)
     disabledBaseTasking = _.extend({disabled: true}, baseTaskingToLoad)


fix top info menu (and more mobile friendly)

--- src/smc-webapp/code-editor/frame-title-bar.cjsx
+++ src/smc-webapp/code-editor/frame-title-bar.cjsx
@@ -19,15 +19,16 @@
     borderRight   : '1px solid rgb(204,204,204)'
     verticalAlign : 'middle'
     lineHeight    : '20px'
-    overflow      : 'hidden'
     textOverflow  : 'ellipsis'
     minHeight     : '24px'

 path_style =
-    whiteSpace  : 'nowrap'
-    fontSize    : '13px'
-    paddingLeft : '5px'
-    color       : '#333'
+    whiteSpace   : 'nowrap'
+    fontSize     : '13px'
+    paddingLeft  : '5px'
+    paddingRight : '15px'
+    color        : '#333'
+    float        : 'right'

 button_size = 'small'
 if IS_TOUCH
@@ -67,6 +68,8 @@
     button_size: ->
         if @props.is_only or @props.is_full
             return
+        else if IS_IPAD
+            return 'small'
         else
             return 'xsmall'

@@ -228,7 +231,7 @@
                 bsSize   = {@button_size()}
                 disabled = {disabled}
                 onClick  = {=>@props.actions.save(true)} >
-                <Icon name='save' /> {if @props.read_only then 'Readonly' else 'Save'}
+                <Icon name='save' /> <VisibleMDLG>{if @props.read_only then 'Readonly' else 'Save'}</VisibleMDLG>
                 {<UncommittedChanges has_uncommitted_changes={@props.has_uncommitted_changes} delay_ms={8000} /> if not disabled}
             </Button>
             <Button
@@ -267,13 +270,14 @@
             filename  = {@props.path}
             actions   = {redux.getProjectActions(@props.project_id)}
             is_public = {false}
+            label     = {'File'}
             bsSize    = {@button_size()}
         />

     render_buttons: ->
         # On touch or full show all buttons.
         extra = IS_TOUCH or @props.is_only or @props.is_full
-
+
             {@render_file_info() if extra}
             {<Space/> if extra}
             {@render_save_timetravel_group()}
@@ -311,7 +315,7 @@

-            {@render_path()}
+            {@render_buttons() if is_active}
             {@render_x()}
-            {@render_buttons() if is_active}
+            {### @render_path() -- do not need for now; will need later....###}


remove unnecessary check from contributors calculation

--- src/node_modules/round-model/round-model.coffee
+++ src/node_modules/round-model/round-model.coffee
@@ -178,7 +178,7 @@

           contributor = contributors[userId]

-          if not contributors or not contributor
+          if not contributor
             return

           if not contributor.contribution


remove unnecessary file

remove unnecessary file

--- server/use/news.coffee
+++ server/use/news.coffee
@@ -1,27 +0,0 @@
-
-# https://newsapi.org/docs/get-started
-# https://newsapi.org/docs
-# note requries attribution for dev account, and may be better to write my own instead
-
-# no use, full of garbage, stop using this and replace with one, some or all of options listed on wikipedia
-# https://en.wikipedia.org/wiki/List_of_news_media_APIs
-
-API.use ?= {}
-API.use.news = {}
-
-API.add 'use/news', get: () -> return API.use.news.search this.queryParams
-
-# https://newsapi.org/docs/endpoints/everything
-API.use.news.search = (q,sort='publishedAt',from,endpoint='everything') ->
-  # sort defaults to publishedAt anyway, can also be relevancy, popularity
-  if not API.settings.use?.news?.apikey?
-    return false
-  else
-    u = 'http://newsapi.org/v2/' + endpoint + '?sortBy=' + sort + '&q=' + q
-    u += '&from=' + from if from # from can be like 2020-03-21
-    u += '&apiKey=' + API.settings.use.news.apikey
-    API.log 'Using news API for ' + u
-    res = HTTP.call 'GET', u
-    # should add some caching, can only go up to 500 reqs per day on free account
-    return res.data
-


Tweak GET /db/course/:handle/levels/:levelOriginal/next

Do not return an assessment level if there is no next main level.
The UI isn't set up to handle that edge case where a student finishes
a level before an assessment level after having completed the main
levels in the course.

--- server/middleware/course-instances.coffee
+++ server/middleware/course-instances.coffee
@@ -192,7 +192,7 @@
     nextLevelOriginal = courseLevels[nextLevelIndex]?.original
     nextAssessmentIndex = utils.findNextAssessmentForLevel(levels, currentIndex)
     nextAssessmentOriginal = courseLevels[nextAssessmentIndex]?.original
-    unless nextLevelOriginal or nextAssessmentOriginal then return res.status(200).send({
+    unless nextLevelOriginal then return res.status(200).send({
       level: {}
       assessment: {}
     })


everything except the stuff that matter

everything except the stuff that matter

--- src/public/javascripts/lib/models/worker.coffee
+++ src/public/javascripts/lib/models/worker.coffee
@@ -105,7 +105,7 @@
   get_mapping_code: () ->
     #TODO figure out where this is

-    @map_code = null
+    @map_code = "pass"

   run_map_job: () ->
     @mappings = []
@@ -117,6 +117,7 @@
       eval(@map_code)

     map(@data.split('\n'))
+    #todo will new lines work? jagnew says hrm

   map_done: () ->
     msg = {name: "MAPPER_DONE", id: @_id}
@@ -149,10 +150,14 @@
     @job_id = start_reduce_message.job_id
     @number_of_mappers = start_reduce_message.number_of_mappers
     @reduce_data = {}
-    @mapper_done = []
-    for i in @number_of_mappers
-      mapper_done[i] = false
-
+    @mapper_done = (false for i in @number_of_mappers)
+
+    @get_reduce_code()
+
+  get_reduce_code: () ->
+    #todo something with job ids?
+
+    @reduce_code = "pass"

   add_data_src: (map_data_msg) ->
@@ -168,10 +173,30 @@
     @reduce_data[map_data_msg.index].append(map_data_msg.key)

   do_reduce: (themsg_by_grandmaster_flash_and_the_furious_five) ->
-
+    collected_data = {}
+    for list in reduce_data
+      for item in list
+        if item[0] not in collected_data_data.keys
+          collected_data[item[0]] = []
+        for value in item[1]
+          collected_data[item[0]].append(value)
+
+    reduce = (key, list_of_objects) =>
+      eval(@reduce_code)
+
+    data_for_jimmy = {}
+    for key in collected_data.keyset
+      data_for_jimmy[key] = []
+      emit = (line) =>
+        data_for_jimmy[key].push(line)
+      reduce(key, collected_data[key])
+
+    finish_reduce

   finish_reduce: () ->
+    #todo hi-5 the backend with some reduce judo
+

   hashval (s) =>


close #23

close #23

--- assets/javascripts/ember-couchdb-revs-adapter.coffee
+++ assets/javascripts/ember-couchdb-revs-adapter.coffee
@@ -13,6 +13,8 @@

     App.History = DS.Model.extend
       tasks: DS.hasMany('App.Task', {key: "tasks", embedded: true})
+      prev_task: DS.belongsTo('App.Task', {key: "prev_task", embedded: true})
+

     App.Store.registerAdapter('App.History', DS.CouchDBRevsAdapter.extend({db: 'docs'}))
     ```


client: use dev proxy if we are in sandbox or dev env

--- client/app/lib/util/proxifyTransportUrl.coffee
+++ client/app/lib/util/proxifyTransportUrl.coffee
@@ -14,12 +14,15 @@
   #           or
   #      http://localhost:8090/-/userproxy/54.164.243.111/kite

-  proxy = {
-    dev        : 'devproxy'
-    production : 'prodproxy'
-    sandbox    : 'sandboxproxy'
-  }[globals.config.environment] or 'devproxy'
-
   {protocol} = global.document.location

-  return "#{protocol}//p.koding.com/-/#{proxy}/#{parser.hostname}/kite"
+
+  proxy = if globals.config.environment is 'production'
+  then 'prodproxy'
+  else 'devproxy'
+
+  subdomain = if globals.config.environment is 'production'
+  then 'p'
+  else 'dev-p'
+
+  return "#{protocol}//#{subdomain}.koding.com/-/#{proxy}/#{parser.hostname}/kite"


video: add a utility to check if a session is mine

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -1,6 +1,7 @@
 kd              = require 'kd'
 _               = require 'lodash'
 getNick         = require 'app/util/nick'
+isMyChannel     = require 'app/util/isMyChannel'

 OpenTokService  = require './services/opentok'
 ParticipantType = require './types/participant'
@@ -60,6 +61,9 @@
   getChannel: -> @channel

+  isMySession: -> isMyChannel @channel
+
+
   ###*
    * Connects to service, then delegates to necessary method.
    *


Router: fix for contentdisplays if landing was on a slug which requires a contentdisplay

--- client/landing/app/MainApp/kodingrouter.coffee
+++ client/landing/app/MainApp/kodingrouter.coffee
@@ -39,10 +39,10 @@

   handleRoute:(route, options={})->
     {entryPoint} = options
-    if entryPoint?.slug?
+    if entryPoint?.slug? and entryPoint.type is "group"
       entrySlug = "/" + entryPoint.slug
-      # indexOf = if sending route prefixed with groupname or entrySlug is the route
-      # also we dont want to koding group name
+      # if incoming route is prefixed with groupname or entrySlug is the route
+      # also we dont want koding as group name
       if not ///^#{entrySlug}///.test(route) and entrySlug isnt '/koding'
         route =  entrySlug + route

@@ -96,38 +96,51 @@
         else                      "#{model.title}#{getSectionName model}"
     , maxLength: 100) # max char length of the title

-  openContent:(name, section, state, route, query)->
-    KD.getSingleton("appManager").tell section, 'createContentDisplay', state,
+  openContent:(name, section, models, route, query)->
+
+    [model] = models
+    KD.getSingleton("appManager").tell section, 'createContentDisplay', model,
       (contentDisplay)=>
         @openRoutes[route] = contentDisplay
         @openRoutesById[contentDisplay.id] = route
         contentDisplay.emit 'handleQuery', query

   loadContent:(name, section, slug, route, query)->
+
     routeWithoutParams = route.split('?')[0]
-    KD.remote.api.JName.one {name: routeWithoutParams}, (err, name)=>
-      if err
-        new KDNotificationView title: err?.message or 'An unknown error has occured.'
-      else if name?
-        models = []
-        name.slugs.forEach (slug, i)=>
-          {constructorName, usedAsPath} = slug
-          selector = {}
-          konstructor = KD.remote.api[constructorName]
-          selector[usedAsPath] = slug.slug
-          konstructor?.one selector, (err, model)=>
-            error err if err?
-            unless model
-              @handleNotFound route
-            else
-              models[i] = model
-              if models.length is name.slugs.length
-                @openContent name, section, models, routeWithoutParams, query
-      else
-        @handleNotFound route
+    # return log name, ">>>>>"
+
+    onSuccess = (models)=> @openContent name, section, models, route, query
+    onError   = (err)=>
+      new KDNotificationView title: err?.message or 'An unknown error has occured.'
+      @handleNotFound route
+
+    if name
+      KD.remote.cacheable name or routeWithoutParams, (err, models)=>
+        if models?
+        then onSuccess models
+        else onError err
+    else
+      KD.remote.api.JName.one {name: routeWithoutParams}, (err, jName)=>
+        if err then onError err
+        else if jName?
+          models = []
+          jName.slugs.forEach (aSlug, i)=>
+            {constructorName, usedAsPath} = aSlug
+            selector = {}
+            konstructor = KD.remote.api[constructorName]
+            selector[usedAsPath] = aSlug.slug
+            konstructor?.one selector, (err, model)=>
+              return onError err if err?
+              if model
+                models[i] = model
+                if models.length is jName.slugs.length
+                  onSuccess models
+        else onError()

   createContentDisplayHandler:(section)->
-    ({params:{name, slug}, query}, state, route)=>
+    ({params:{name, slug}, query}, models, route)=>
+
       route = name unless route
       contentDisplay = @openRoutes[route.split('?')[0]]
       if contentDisplay?
@@ -135,7 +148,7 @@
           .hideAllContentDisplays contentDisplay
         contentDisplay.emit 'handleQuery', query
       else if state?
-        @openContent name, section, state, route, query
+        @openContent name, section, models, route, query
       else
         @loadContent name, section, slug, route, query

@@ -193,8 +206,8 @@
       '/:name?/Dashboard'               : (routeInfo, state, route)->
         {name} = routeInfo.params
         n = name ? 'koding'
-        KD.remote.cacheable n, (err, [group], nameObj)=>
-          @openContent name, 'Groups', group, route
+        KD.remote.cacheable n, (err, groups, nameObj)=>
+          @openContent name, 'Groups', groups, route

       # content
       '/:name?/Topics/:slug'            : createContentHandler 'Topics'