Taking into account 'startAt' param when 'refresh-photos' is used.

--- src/tumblrip.coffee
+++ src/tumblrip.coffee
@@ -76,7 +76,9 @@

   process = (start, init=false) ->
     log.debug "Processing at #{start}...\n"
-    # Get first round separately to know how many posts we will need to retrieve.
+    # We know we only want photos and the maximum item size is 50.
+    # These are not variables for the purpose of this tool, but could easily be
+    # modified to download other stuff from a Tumblr blog ! :)
     api "#{url}?type=photo&num=50&start=#{start}"
     .then (parsed) ->
       # If we have a total number of posts and we are in the first loop, we are going to update..
@@ -243,7 +245,7 @@
       [cacheFile, {}]
   .spread (cacheFile, cache) ->
     # Populate cache with fresh data (insert/update)
-    [cacheFile, getPostsData.call cache]
+    [cacheFile, getPostsData.call cache, blogname]
   .spread (cacheFile, blog) ->
     {title, total, posts, nbNewPosts} = blog
     log.info 'Blog title:', title, '\n'
@@ -270,7 +272,12 @@
     # If we want to refresh photos we can only count those we know they exist
     # in the blog. Any other file previously stored will never have a chance to
     # be downloaded again!
-    nbNewPosts = total if options['refresh-photos']
+    {startAt} = options
+    if options['refresh-photos']
+      if total < startAt
+        throw new Error "Starting at #{startAt} is above maximum blog photos #{total}!"
+      else
+        nbNewPosts = total - startAt

     if nbNewPosts > 0
       log.info "Processing #{nbNewPosts} photos on #{options.threads} threads.\n"
@@ -291,7 +298,7 @@
           else
             threaded start

-      threaded options.startAt
+      threaded startAt
     else
       log.info 'No new picture. If you want to force update, add --refresh-photos [-rp].\n'
       1


Remove accidently commited file

Remove accidently commited file

--- library/test.coffee
+++ library/test.coffee
@@ -1,8 +0,0 @@
-augment = require "./nucleus/augment"
-augment.Augment.installServiceMethods @
-augment.Augment.installStubMethods @
-
-module.exports.test = @GET "/api/test", (request, response) ->
-    response.send test: 123
-
-@resource "/api/test", "/api2/test"


Add auth and init commands

Add auth and init commands

--- picksel.coffee
+++ picksel.coffee
@@ -10,11 +10,14 @@
 md5File = require 'md5-file'
 filedel = require 'filedel'
 fileMove = require 'file-move'
-
-
-# Load config files.
-user = jsonfile.readFileSync './.pickselacc'
-config = jsonfile.readFileSync './picksel.json'
+readline = require 'readline-sync'
+
+
+# Space for config files.
+userPath = './.pickselacc'
+user = null
+configPath = './picksel.json'
+config = null

 # Initialize logging
@@ -80,12 +83,25 @@
 getResolutionCode = (res) -> humanResolutions.indexOf res

-# Persists the currently loaded configuration settings
-#
-persistConfig = () ->
+getReader = () =>
+  readlineSettings =
+    input: process.stdin
+    output: process.stdout
+  readline.createInterface readlineSettings
+
+
+persist = (obj, filename) ->
   options =
     spaces: 4
-  jsonfile.writeFileSync './picksel.json', config, options
+  jsonfile.writeFileSync filename, obj, options
+
+
+# Persists the currently loaded configuration settings
+#
+persistConfig = () -> persist configPath, config
+
+
+persistUser = () -> persist userPath, user

 # Returns true if a request to the Pixabay API was successful.
@@ -218,9 +234,79 @@
     config.images = filteredImages
     persistConfig()

+
+# Attempts to load the user and dependency configuration files.
+#
+loadConfig = () ->
+  if existsFile.sync userPath
+    user = jsonfile.readFileSync userPath
+  else
+    log.error "Couldn't find your '#{userPath}' file to get your API key." \
+      + ' You should probably run \'picksel auth\' to set one up.'
+    return false
+  if existsFile.sync configPath
+    config = jsonfile.readFileSync configPath
+  else
+    log.error "Couldn't find your #{configPath} file with all your" \
+      + ' dependencies. You should probably run \'picksel init\' to set one' \
+      + ' up.'
+    return false
+  true

+
+init = () ->
+
+  # Check we're not going to overwrite an existing project file.
+  if existsFile.sync configPath
+    log.warning 'Looks like this project has already been initialized for' \
+      + ' Picksel.'
+      return false
+
+  newConfig =
+    directory: ''
+    images: []
+
+  console.log 'Let\'s initialize Picksel for this project...'
+
+  directory = readline.question 'Relative to the current directory, where' \
+    + ' would you like to store assets? '
+  newConfig.directory = directory
+
+  config = newConfig
+  persistConfig()
+
+
+auth = () ->
+
+  # Check we're not going to overwrite an existing project file.
+  if existsFile.sync userPath
+    log.warning 'Looks like authentication is already set up for this project.'
+      return false
+
+  newUser =
+    apiKey: ''
+
+  console.log 'Picksel needs your API key in order to work...'
+
+  apiKey = readline.question 'What\'s your Pixabay API key? To find it you' \
+    + ' can log in to the Pixabay website and visit: ' \
+    + 'https://pixabay.com/api/docs/ '
+
+  # TODO: Test API key.
+
+  newUser.apiKey = apiKey
+
+  user = newUser
+
+
 # Interpret commands.
 switch process.argv[2]
-  when 'install' then install()
-  when 'add' then add process.argv
-  when 'remove' then remove process.argv
+  when 'init' then init()
+  when 'auth' then auth()
+  else
+    if loadConfig()
+      switch process.argv[2]
+        when 'install' then install()
+        when 'add' then add process.argv
+        when 'remove' then remove process.argv
+


fix js error introduced by last checkin

fix js error introduced by last checkin

--- app/assets/javascripts/main.coffee
+++ app/assets/javascripts/main.coffee
@@ -1,8 +1,11 @@
 class MainController extends SimpleMVC.Controller
     _navigateToNewBounds: () =>
         # Set not seen flag so we know which ones to remove later
-        for i in this._locations
-            i.notSeen = true
+        if not this._locations?
+            this._locations = []
+        else
+            for i in this._locations
+                i.notSeen = true

         mapBounds = this._map.getBounds()
         mapNE = mapBounds.getNorthEast()


fix typo

fix typo

--- client/methods/updateMessage.coffee
+++ client/methods/updateMessage.coffee
@@ -33,7 +33,7 @@
 			message = RocketChat.callbacks.run 'beforeSaveMessage', message

 			ChatMessage.update
-				_id: message.id
+				_id: message._id
 				'u._id': Meteor.userId()
 			,
 				$set:


use coffee notation

use coffee notation

--- BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
+++ BackofficeBundle/Resources/public/coffee/orchestraLib.coffee
@@ -32,8 +32,6 @@
 displayMenu = (route, refresh) ->
   selectedPath = "#" + (route || Backbone.history.fragment)
   refresh = refresh || route?
-  console refresh
-  console selectedPath
   $.ajax
     url: $("#left-panel nav").data("url")
     type: "GET"


Generate relative paths to items in the OPF

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -47,15 +47,17 @@
       @manifest.on 'add', (model, collection, options) =>
         $manifest = @$xml.find('manifest')

+        relPath = Utils.relativePath(@id, model.id)
+
         # Check if the item is not already in the manifest
-        return if $manifest.find("item[href='#{model.id}']")[0]
+        return if $manifest.find("item[href='#{relPath}']")[0]

         # Create a new `<item>` in the manifest
         item = @$xml[0].createElementNS('http://www.idpf.org/2007/opf', 'item')
         $item = $(item)
         $item.attr
-          href:         model.id
-          id:           model.id # TODO: escape the slashes so it is a valid id
+          href:         relPath
+          id:           relPath # TODO: escape the slashes so it is a valid id
           'media-type': model.mediaType

         $manifest.append($item)


Make calls to Google/Bing async.

Make calls to Google/Bing async.

--- server/translate-helpers.coffee
+++ server/translate-helpers.coffee
@@ -18,7 +18,7 @@
     }

-translateUsingGoogle = (text, source, target) ->
+translateUsingGoogle = (text, source, target, cb) ->
     check(text   , String)
     check(source , String)
     check(target , String)
@@ -26,7 +26,7 @@
     console.log('Translating using Google ... ')

     if Meteor.settings.GOOGLE_TRANSLATE_API?
-        res = HTTP.call(
+        HTTP.call(
             "POST",
             googla_url,
             {
@@ -38,17 +38,28 @@
                     q      : text
                 headers:
                     'X-HTTP-Method-Override': 'GET'
-            }
+            },
+            (err, res) ->
+                translatedMsg = 'Google translation failed.'
+                if not err
+                    console.log('Google translate API response:')
+                    console.log(JSON.stringify(res, null, 2))
+                    try
+                        translatedMsg = res.data.data
+                                           .translations[0].translatedText
+                    catch exept
+                        console.error(
+                            'Google translation failed with exception:',
+                            except
+                        )
+
+                cb(err, translatedMsg)
         )
+    else
+        console.error('Proper settings for Google API not provided!')
+        cb(null, 'No translation from Google available')

-        console.log('Google translate API response:')
-        console.log(JSON.stringify(res, null, 2))
-        res.data.data.translations[0].translatedText
-    else
-        console.error('Proper settings not provided!')
-        'No translate from Google available'
-
-translateUsingBing = (text, source, target) ->
+translateUsingBing = (text, source, target, cb) ->

     check(text   , String)
     check(source , String)
@@ -58,7 +69,7 @@

     if @BingAccessTokenManager?
         token = BingAccessTokenManager.getToken()
-        res = HTTP.call(
+        HTTP.call(
             "GET",
             bing_url,
             {
@@ -71,62 +82,71 @@

                 headers:
                     'Authorization': 'Bearer ' + token
-            }
+            },
+            (err, res) ->
+                translatedMsg = 'Bing translation failed.'
+                if not err
+                    console.log('Bing translate API response:')
+                    console.log(JSON.stringify(res, null, 2))
+
+                    # Data format:
+                    #  {
+                    #  ...
+                    #  "content":
+                    #  '<string xmlns=
+                    #     "http://schemas.microsoft.com/2003/10/Serialization/">
+                    #      Versuch das mal.
+                    #  </string>'
+                    #  }
+                    try
+                        content = (new xmldom.DOMParser())
+                                        .parseFromString(res.content)
+                        translatedMsg = content.firstChild.firstChild.data
+                    catch except
+                        console.error(
+                            'Bing translation failed with exception:',
+                            except
+                        )
+
+                cb(err, translatedMsg)
         )
-
-        console.log('Bing translate API response:')
-        console.log(JSON.stringify(res, null, 2))
-
-        # Data format:
-        #  {
-        #  ...
-        #  "content":
-        #  '<string xmlns=
-        #      "http://schemas.microsoft.com/2003/10/Serialization/">
-        #      Versuch das mal.
-        #  </string>'
-        #  }
-        content = (new xmldom.DOMParser()).parseFromString(res.content)
-        content.firstChild.firstChild.data
     else
         console.error('Could not locate BingAccessTokenManager')
-        'No translate from Bing available'
+        cb(null, 'No translation from Bing available')
+

 _tryTranslation = (methodFunc, methodName, msg, sourceLang, targetLang, _id) ->
-    modifier = {}
-
-    try
-        translatedMsg = methodFunc(msg, sourceLang, targetLang)
-        modifier.translations =
-            createMessage(targetLang , translatedMsg, methodName, false)
+    cb = (error, translatedMsg) ->
+        modifier = {}
+        if not error
+            modifier.translations =
+                createMessage(
+                    targetLang,
+                    translatedMsg,
+                    methodName,
+                    false
+                )
+        else
+            console.error('Could not translate: ', error)
+            modifier.translations =
+                createMessage(
+                    targetLang,
+                    'Could not translate.',
+                    methodName,
+                    true
+                )

         ChanslateMessages.update({ _id: _id }, {
             $push: modifier
         })

-    catch error
-        console.error('Could not translate: ', error)
-        modifier.translations =
-            createMessage(
-                targetLang,
-                'Could not translate.',
-                methodName,
-                true
-            )
-
-        ChanslateMessages.update({ _id: _id }, {
-            $push: modifier
-        })
-
-
+    methodFunc(msg, sourceLang, targetLang, cb)

 ################################################################
 ### Exported methods and values
 ################################################################

 @translateAndPopulate = (msg, sourceLang, targetLang, _id) ->
-    # TODO (UU): These calls should be made in parallel
-    # But doing that for some reason blows the stack.
     _tryTranslation(
         translateUsingGoogle,
         'google',
@@ -145,32 +165,6 @@
         _id
     )

-    # setTimeout(
-    #     () -> _tryTranslation(
-    #         translateUsingGoogle,
-    #         'google',
-    #         msg,
-    #         sourceLang,
-    #         targetLang,
-    #         _id
-    #     ),
-    #     0
-    # )
-
-    # setTimeout(
-    #     () -> _tryTranslation(
-    #         translateUsingBing,
-    #         'bing',
-    #         msg,
-    #         sourceLang,
-    #         targetLang,
-    #         _id
-    #     ),
-    #     0
-    # )
-
-
-
 @createChanslateMsgDoc = (userName, origMsg, origLang) ->
     {
         userName     : userName


Fixed nameless group chats

Fixed nameless group chats

--- lib/atox-toxWorker.coffee
+++ lib/atox-toxWorker.coffee
@@ -129,6 +129,7 @@
     "titles": []
     "NLC": []
     "msgs": []
+    "isCollab": -> false
   }

   groupMessageCB:           (e) ->
@@ -350,13 +351,14 @@
       }

-    addGroup = (counter) =>
+    addGroup = (counter=0) =>
       try
         title = @TOX.old().getGroupchatTitleSync gID
       catch err
         counter++
-        if counter is 20
-          @err "Can't get GC title of GC #{gID}"
+        if counter is 5
+          title = ""
+          @warn "Can't get GC title of GC #{gID}"
         else
           @myTimeout 500, -> addGroup counter
           return


remove debugger statement

remove debugger statement

--- lib/birch-markdown.coffee
+++ lib/birch-markdown.coffee
@@ -37,5 +37,4 @@
   copyAsMarkdown: () ->
     outline = @birchOutlineEditorService?.getActiveOutlineEditor()?.outline
     if outline
-      debugger
       atom.clipboard.write BirchToMarkdown.outlineToMarkdown outline


remove unused utils import from ussd directive

--- static/coffee/flows/widgets.coffee
+++ static/coffee/flows/widgets.coffee
@@ -3,7 +3,7 @@
 #============================================================================
 # Displaying USSD Menu with textarea, menu inputs and char counter
 #============================================================================
-app.directive "ussd", [ "$rootScope", "$log", "Flow", "utils", ($rootScope, $log, Flow, utils) ->
+app.directive "ussd", [ "$rootScope", "$log", "Flow", ($rootScope, $log, Flow) ->
   MESSAGE_LENGTH = 182

   link = (scope, element, attrs) ->


webapp/rmd: showing errors when back-end fails to run and switching path for subdirectories

--- src/smc-webapp/editor-html-md/editor-html-md.coffee
+++ src/smc-webapp/editor-html-md/editor-html-md.coffee
@@ -430,9 +430,11 @@
         cb(undefined, m.s)

     rmd_to_html: (cb) =>
+        split_path = misc.path_split(@filename)
         @to_html_via_exec
             command     : "smc-rmd2html"
-            args        : [@filename]
+            args        : [split_path.tail]
+            path        : split_path.head
             cb          : cb

     rst_to_html: (cb) =>
@@ -451,6 +453,7 @@
             command     : required
             args        : required
             postprocess : undefined
+            path        : undefined  # if set, change working directory to path
             cb          : required   # cb(error, html, warnings)
         html = undefined
         warnings = undefined
@@ -462,13 +465,14 @@
                     project_id  : @project_id
                     command     : opts.command
                     args        : opts.args
+                    path        : opts.path
                     err_on_exit : false
                     cb          : (err, output) =>
                         #console.log("salvus_client.exec ", err, output)
                         if err
                             cb(err)
                         else
-                            html = output.stdout
+                            html     = output.stdout
                             warnings = output.stderr
                             cb()
         ], (err) =>
@@ -491,7 +495,7 @@
         t0 = misc.mswalltime()
         @_update_preview_lock = true
         #console.log("update_preview")
-        @to_html (err, source) =>
+        @to_html (err, source, warnings) =>
             @_update_preview_lock = false
             if err
                 console.log("failed to render preview: #{err}")
@@ -503,22 +507,25 @@
             elt.find('link').remove()
             source = elt.html()

-            # finally set html in the live DOM
-            @preview_content.html(source)
-
-            @localize_image_links(@preview_content)
-
-            ## this would disable clickable links...
-            #@preview.find("a").click () =>
-            #    return false
-            # Make it so preview links can be clicked, don't close SMC page.
-            @preview_content.find("a").attr("target","_blank")
-            @preview_content.find("table").addClass('table')  # bootstrap table
-
-            @preview_content.mathjax()
-
-            #@preview_content.find(".smc-html-cursor").scrollintoview()
-            #@preview_content.find(".smc-html-cursor").remove()
+            if warnings
+                @preview_content.html("
#{warnings}
")
+            else
+                # finally set html in the live DOM
+                @preview_content.html(source)
+
+                @localize_image_links(@preview_content)
+
+                ## this would disable clickable links...
+                #@preview.find("a").click () =>
+                #    return false
+                # Make it so preview links can be clicked, don't close SMC page.
+                @preview_content.find("a").attr("target","_blank")
+                @preview_content.find("table").addClass('table')  # bootstrap table
+
+                @preview_content.mathjax()
+
+                #@preview_content.find(".smc-html-cursor").scrollintoview()
+                #@preview_content.find(".smc-html-cursor").remove()

             #console.log("update_preview time=#{misc.mswalltime(t0)}ms")
             if @_update_preview_redo


Markdown editor- removed keyboard shortcut for center

--- src/smc-webapp/editor-html-md/editor-html-md.coffee
+++ src/smc-webapp/editor-html-md/editor-html-md.coffee
@@ -121,7 +121,7 @@
             underline : 'Cmd-U Ctrl-U'
             comment   : 'Shift-Ctrl-3'
             strikethrough : 'Shift-Cmd-X Shift-Ctrl-X'
-            justifycenter : "Cmd-E Ctrl-E"
+            #justifycenter : "Cmd-E Ctrl-E" # no need to create "div" element in markdown file
             #justifyright  : "Cmd-R Ctrl-R"  # messes up page reload
             subscript     : "Cmd-= Ctrl-="
             superscript   : "Shift-Cmd-= Shift-Ctrl-="


Only adjust scrollTop when deleting lines when using the React editor

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -984,7 +984,7 @@
   handleTokenizedBufferChange: (tokenizedBufferChange) =>
     {start, end, delta, bufferChange} = tokenizedBufferChange
     @updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?)
-    @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if delta < 0
+    @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if @manageScrollPosition and delta < 0

   updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) ->
     startBufferRow = @rowMap.bufferRowRangeForBufferRow(startBufferRow)[0]


:green_heart:

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -195,7 +195,7 @@
     if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth
       @defaultCharWidth = defaultCharWidth
       @doubleWidthCharWidth = doubleWidthCharWidth
-      @updateWrappedScreenLines() if @isSoftWrapped() and @getWidth()?
+      @updateWrappedScreenLines() if @isSoftWrapped() and @getEditorWidthInChars()?
     defaultCharWidth

   getCursorWidth: -> 1


Fix lastScreenRowForBufferRow

Fix lastScreenRowForBufferRow

…which was mistakenly translating screen positions to buffer positions,
and not vice versa.

--- src/display-buffer.coffee
+++ src/display-buffer.coffee
@@ -441,7 +441,7 @@
     if @largeFileMode
       bufferRow
     else
-      @displayLayer.translateScreenPosition(Point(screenRow, 0), clip: 'forward').row
+      @displayLayer.translateBufferPosition(Point(bufferRow, 0), clip: 'forward').row

   # Given a screen row, this converts it into a buffer row.
   #


webserver: send a json success response for Impersonate endpoint

--- servers/lib/server/index.coffee
+++ servers/lib/server/index.coffee
@@ -445,7 +445,7 @@

           res.cookie 'clientId', session.clientId, path : '/'  if session.clientId
           res.clearCookie 'realtimeToken'
-          res.status(200).end()
+          res.status(200).send({success: yes})

 app.post "/:name?/Recover", (req, res) ->


console log cleanup

console log cleanup

--- mrfogg-front/app/coffee/controllers/main.coffee
+++ mrfogg-front/app/coffee/controllers/main.coffee
@@ -49,7 +49,6 @@
     $scope.submit = ->
         email = $scope.form.email
         password = $scope.form.password
-        console.log($scope.form)

         $scope.loading = true


Fix ManagedAttachment method forwarding and set file property

--- src/trix/models/managed_attachment.coffee
+++ src/trix/models/managed_attachment.coffee
@@ -4,13 +4,10 @@
   # Forward all Attachment methods
   for own name, value of Trix.Attachment.prototype when name isnt "constructor" and typeof value is "function"
     do (name, value) =>
-      @::[name] = -> @attachment[name].call(@attachment, arguments)
+      @::[name] = -> @attachment[name].apply(@attachment, arguments)

   constructor: (@attachmentManager, @attachment) ->
-    {@id} = @attachment
-
-  setUploadProgress: (value) ->
-    document.getElementById("trix-progress-#{@id}")?.setAttribute("value", value)
+    {@id, @file} = @attachment

   remove: ->
     @attachmentManager.requestRemovalOfAttachment(@attachment)


[grunt] fix absent `_` in some private variables' names

--- tasks/grunt-nunjucks-2-html.coffee
+++ tasks/grunt-nunjucks-2-html.coffee
@@ -213,8 +213,8 @@
           ###
           env.addGlobal 'expand', (path = '', pattern = '**/*', filter = 'isFile', cwd = buildDir + '/') ->
             _files = []
-            grunt.file.expand({ cwd: cwd + path, filter: filter }, pattern).forEach (file) ->
-              _files.push(file)
+            grunt.file.expand({ cwd: cwd + path, filter: filter }, pattern).forEach (_file) ->
+              _files.push(_file)
             _files

           ###*
@@ -234,8 +234,8 @@
               _position = if (_i > 1) then _position + 2 else _position
               _subbedPath.splice(_position, 0, subName)
               _i++
-            result = _.get(pages, _subbedPath)
-            if result then result else grunt.log.error('[getPage] can\'t find requested `' + _subbedPath + '` inside specified object')
+            _result = _.get(pages, _subbedPath)
+            if _result then _result else grunt.log.error('[getPage] can\'t find requested `' + _subbedPath + '` inside specified object')

           ###*
            * Expose `moment.js` to Nunjucks' for parsing, validation, manipulation, and displaying dates


consistent config capitalization for formatting options

--- lib/package/config.coffee
+++ lib/package/config.coffee
@@ -107,7 +107,7 @@
         default: false
         order: 13
       formattingOptions:
-        title: 'Formatting options'
+        title: 'Formatting Options'
         description: 'Specifies [formatting options for `JuliaFormatter.format_text`](https://domluna.github.io/JuliaFormatter.jl/stable/#Formatting-Options-1).'
         type: 'object'
         order: 14
@@ -566,6 +566,7 @@
         type: 'boolean'
         default: true
         order: 10
+
   remoteOptions:
     type: 'object'
     order: 5


Accept empty options

Accept empty options

--- lib/plugin-manager.coffee
+++ lib/plugin-manager.coffee
@@ -103,12 +103,15 @@
   # @return {Promise<Object>} return the npm config
   selectConfig: (name, enable, check) ->
     @getMirrors().then =>
-      @config_.mirror = @mirrors_[name]
-      config.set "packageManager.mirrorName", name
-      @config_.proxy = enable
-      config.set "packageManager.proxy", enable
-      @config_.betaCheck = check
-      config.set "packageManager.enableBetaPluginCheck", check
+      if name?
+        @config_.mirror = @mirrors_[name]
+        config.set "packageManager.mirrorName", name
+      if enable?
+        @config_.proxy = enable
+        config.set "packageManager.proxy", enable
+      if check?
+        @config_.betaCheck = check
+        config.set "packageManager.enableBetaPluginCheck", check
       new Promise (resolve) =>
         npmConfig =
           prefix: PLUGIN_PATH


Use Atom standard naming for configuration option

--- lib/tabs-to-spaces.coffee
+++ lib/tabs-to-spaces.coffee
@@ -45,9 +45,9 @@
   handleEvents: (editor) ->
     buffer = editor.getBuffer()
     bufferSubscription = @subscribe buffer, 'will-be-saved', =>
-      if atom.config.get('tabs-to-spaces.on-save') == 'untabify'
+      if atom.config.get('tabs-to-spaces.onSave') == 'untabify'
         @untabify()
-      else if atom.config.get('tabs-to-spaces.on-save') == 'tabify'
+      else if atom.config.get('tabs-to-spaces.onSave') == 'tabify'
         @tabify()

     @subscribe editor, 'destroyed', ->


added zoom url to vehicle marker

added zoom url to vehicle marker

--- lib/vehicle_marker.coffee
+++ lib/vehicle_marker.coffee
@@ -14,10 +14,12 @@
   shadowSize: new L.Point(15, 16)
   iconAnchor: new L.Point(8,8)

-SM.PopupContent = Handlebars.compile """
-
{{registration}}

-
Speed: {{speed}}mph

-"""
+SM.PopupContent = SC.View.extend
+  template: SC.Handlebars.compile """
+
{{registration}}

+
Speed: {{speed}}mph

+    Zoom
+  """

 # Initialise this type of marker with a sensor vehicle
 # and the current lat and lon, e.g.
@@ -39,10 +41,12 @@
     @mapObject.on('click', @mapObject.openPopup, @mapObject)
     @_setPopupContent()
   _setPopupContent: ->
-    html = SM.PopupContent
+    view = SM.PopupContent.create
       registration: @getPath('vehicle.registration')
       speed: @getPath('vehicle.speed') || 0
-    @mapObject._popup.setContent(html)
+      url: "#/map/#{@getPath('vehicle.imei')}"
+    view.createElement()
+    @mapObject._popup.setContent(view.get('element'))
   bounds: ->
     latlng = @get('mapObject')._latlng
     new L.LatLngBounds(latlng,latlng)


Fix: wrong ajaxSuccess event listener

Fix: wrong ajaxSuccess event listener

--- vendor/assets/javascripts/auto_awesomplete/ajax.js.coffee
+++ vendor/assets/javascripts/auto_awesomplete/ajax.js.coffee
@@ -32,7 +32,7 @@
 jQuery ($) ->
   autoAwesomplete.ajaxInit()

-  $body = $('body')
+  $body = $(document)
   $body.on 'ajaxSuccess', ->
     autoAwesomplete.ajaxInit()
     return


Integration tests are even more robust against slow app starts

--- storage/test/integration-geocaches.coffee
+++ storage/test/integration-geocaches.coffee
@@ -58,15 +58,20 @@
         token = yield a.getToken()

         tries = 5
+        appRunning = false
         while tries-- > 0
             try
                 response = yield request.get url
                 if response.status is 200
                     console.log "found app at #{url}"
+                    appRunning = true
                     break
-                yield Promise.delay 500
+                yield Promise.delay 1000
             catch err
-                yield Promise.delay 500
+                yield Promise.delay 1000
+
+        if not appRunning
+            throw new Error "App is not running at #{url}"

     beforeEach Promise.coroutine ->
         yield setupTestData []


It's too warm in here

It's too warm in here

--- storage/test/integration-geocaches.coffee
+++ storage/test/integration-geocaches.coffee
@@ -58,10 +58,13 @@
         token = yield a.getToken()

         tries = 5
-        while tries-- > 5
-            response = yield request.get url
-            break if response.status is 200
-            yield Promise.delay 500
+        while tries-- > 0
+            try
+                response = yield request.get url
+                break if response.status is 200
+                yield Promise.delay 500
+            catch err
+                yield Promise.delay 500

     beforeEach Promise.coroutine ->
         yield setupTestData []


backbone no longer supports change, so we're setting attributes in a non-silent manner

--- subtree/bokehjs/static/coffee/base.coffee
+++ subtree/bokehjs/static/coffee/base.coffee
@@ -113,12 +113,6 @@
     if coll
       coll.get(attrs['id']).dinitialize(attrs)

-  # set attributes on old models silently
-  for coll_attrs in oldspecs
-    [coll, attrs] = coll_attrs
-    if coll
-      coll.get(attrs['id']).set(attrs, {'local' : true, 'silent' : true})
-
   # trigger add events on all new models
   for coll_attrs in newspecs
     [coll, attrs] = coll_attrs
@@ -126,12 +120,11 @@
       model = coll.get(attrs.id)
       model.trigger('add', model, coll, {});

-  # trigger change events on all old models
+  # set attributes on old models silently
   for coll_attrs in oldspecs
     [coll, attrs] = coll_attrs
     if coll
-      coll.get(attrs['id']).change()
-
+      coll.get(attrs['id']).set(attrs)
   return null

@@ -673,6 +666,7 @@
   CDXPlotContext : ['./container', 'plotcontexts']
   PlotContext : ['./container', 'plotcontexts']
   ScatterRenderer: ['./schema_renderers', 'scatterrenderers']
+  LineRenderer: ['./schema_renderers', 'linerenderers']
   DiscreteColorMapper :['./mapper', 'discretecolormappers']
   DataTable :['./table', 'datatables']
   PandasPivot :['./pandas/pandas', 'pandaspivots']


Groups: error handlings are added for callbacks in subscribeToPlans function

--- workers/social/lib/social/models/payment/groupplan.coffee
+++ workers/social/lib/social/models/payment/groupplan.coffee
@@ -106,8 +106,10 @@
       return callback err  if err
       JPaymentPack.one tags: "group", (err, pack) ->
         return callback err  if err
-        subscription.debit {pack, shouldCreateNonce: yes}, (err, nonce) ->
-          JPaymentFulfillmentNonce.one {nonce}, (err, nonce) ->
+        subscription.debit {pack, shouldCreateNonce: yes}, (err, nonceStr) ->
+          return callback err  if err
+          JPaymentFulfillmentNonce.one nonce:nonceStr, (err, nonce) ->
             return callback err  if err
+            return callback "nonce not found"  unless nonce
             nonce.addOwner client.connection.delegate, (err) ->
               callback err, subscription, nonce.nonce


book typo fix

book typo fix

--- client/app/MainApp/book/bookdata.coffee
+++ client/app/MainApp/book/bookdata.coffee
@@ -73,7 +73,7 @@

In addition to applications for the database, there are add-ons, and extensions to get your projects personalized, polished, and published faster.
"""
   ,
     title     : "Etiquette"
-    content   : """
Seems like a fancy word, huh? Don’t worry, we’re not going to preach. This is more of a Koding Mission Statement. Sure, Koding is built around cloud development, but it’s second pillar is community.

+    content   : """
Seems like a fancy word, huh? Don’t worry, we’re not going to preach. This is more of a Koding Mission Statement. Sure, Koding is built around cloud development, but its second pillar is community.

So what does that mean? That means that developers of all skill levels are going to grace your activity feed. Some need help, some will help others, some will guide the entire group, whatever your role is it’s important to remember one important word: help.

Help by providing insight and not insult to people asking basic questions. Help by researching your question to see if it has had already been given an answer. And lastly, help us make this service the best it can be!
"""
   ,


IDEView: Update change event name.

IDEView: Update change event name.

--- client/ide/views/tabview/ideview.coffee
+++ client/ide/views/tabview/ideview.coffee
@@ -241,7 +241,7 @@
   handlePaneRemoved: (pane) ->
     file = pane.getData()
     @openFiles.splice @openFiles.indexOf(file), 1
-    @emitChange pane.view, context: {}, 'TabRemoved'
+    @emitChange pane.view, context: {}, 'PaneRemoved'
     @emit 'PaneRemoved', pane

   getDummyFilePath: ->


feat(containers): added value initialisation to mappedobservables

--- src/lib/observable.coffee
+++ src/lib/observable.coffee
@@ -26,12 +26,16 @@
 Computed = (self, property, fn) -> self[property] = ko.computed fn, self
 PureComputed = (self, property, fn) -> self[property] = ko.pureComputed fn, self

-MappedObservable = (self, property, container) ->
-  self[property] = ko.computed
+MappedObservable = (self, property, container, initial_value) ->
+  self[property] = ko.pureComputed
     read: ->
       container[property]()
     write: (value) ->
       container[property](value)
+    deferEvaluation: true
+
+  if initial_value?
+    self[property](initial_value)

 LazyObservable = (self, property, callback, params = [], init_value = null, make_array = false) ->
   unless make_array


Text change Change 'About Illustrated Life' to 'About Science Gossip' Closes #6

--- project.coffee
+++ project.coffee
@@ -20,7 +20,7 @@
       'Periodicals': group_page
     },{
     'About': '''
-
About ‘Illustrated Life’

+
About ‘Science Gossip’

The publication of books and periodicals are key locations for visualizing knowledge about the natural world. The Biodiversity Heritage Library has digitized and catalogued millions of pages of printed text between the 1400's and today related to the investigation of the natural world. Illustrations are a large part of these printed pages, and we need your to help identify, classify and correlate them. The data you create by tagging illustrations and adding artist and engraver information will have a direct impact on the research of historians who are trying to figure out why, how often, and who made images depicting a whole range of natural sciences in the Victorian period.


Use proxy_pass'd api

Use proxy_pass'd api

--- project.coffee
+++ project.coffee
@@ -1,8 +1,16 @@
 FreeDrawTool = require "./free-draw-tool"
 translate = require "t7e"

+[apiHost, apiProxyPath] = if window.location.hostname is 'www.floatingforests.org'
+  ['http://www.floatingforests.org', '/_ouroboros_api/proxy']
+else
+  [null, null]
+
 module.exports =
   id: 'kelp'
+  apiHost: apiHost
+  apiProxyPath: apiProxyPath
+
   subjectGroup: true
   background: './images/kelp-bg.jpg'
   title: translate 'span', 'site.title'


About page updates, from Trish

About page updates, from Trish

--- project.coffee
+++ project.coffee
@@ -18,11 +18,11 @@
     'About': '''

About ‘Illustrated Life’

-
The publication of books and periodicals are key locations for visualizing knowledge about the natural world. The Biodiversity heritage library has digitized and catalogued millions of pages of printed text between x and x related to the investigation of the natural world. Illustrations are a large part of these printed pages, and we need your to help identify, classify and correlate them. The data you create by tagging illustrations and adding artist and engraver information will have a direct impact on the research of historians who are trying to figure out why, how often, and who made images depicting a whole range of natural sciences in the Victorian period.

+
The publication of books and periodicals are key locations for visualizing knowledge about the natural world. The Biodiversity Heritage Library has digitized and catalogued millions of pages of printed text between the 1400's and today related to the investigation of the natural world. Illustrations are a large part of these printed pages, and we need your to help identify, classify and correlate them. The data you create by tagging illustrations and adding artist and engraver information will have a direct impact on the research of historians who are trying to figure out why, how often, and who made images depicting a whole range of natural sciences in the Victorian period.

BHL and Constructing Scientific Communities

-
Illustrated Life is a project collaboration between the Biodiversity Heritage Library (BHL) operated through the Missouri Botanical Garden and an Arts and Humanities Research Council project Constructing Scientific Communities: Citizen Science in the 19th and 21st Centuries (conscicom). The ConSciCom project is investigating the role of the non-professional in the making and communication of science in both the Victorian period and today. Historians at the Universities of Leicester and Oxford are investigating the particular roles of the periodical press in the 19th century as a place for an amateur or non-professional individual to participate in scientific research. Periodicals and books of the Victorian era were heavily illustrated, but little is know about who made the illustrations and how they ended up in print. One of the fundamental aims of the ConSciCom project, through your help on Illustrated Life, is to better understand exactly why and how images were used to produce knowledge about the natural world within printed media. Unlike the 20th and 21st centuries, the barriers to participating in science in the 19th century were not as straightforward as they are now. Better understanding the range of individuals who made science through their images will help us ascertain what constituted a 19th century scientist.. The ultimate goal of the project is to better understand the roots of citizen science – and this is the first Zooniverse project where citizen scientists are both the researchers and the subject of the research. In this way, how you – as a citizen scientist –participate in this project has a direct impact on how we can come to understand historical and modern notions of what it means to do science.

+
Illustrated Life is a project collaboration between an Arts and Humanities Research Council project Constructing Scientific Communities: Citizen Science in the 19th and 21st Centuries (ConSciCom) and the Missouri Botanical Garden who are providing content from the Biodiversity Heritage Library. The ConSciCom project is investigating the role of the non-professional in the making and communication of science in both the Victorian period and today. Historians at the Universities of Leicester and Oxford are investigating the particular roles of the periodical press in the 19th century as a place for an amateur or non-professional individual to participate in scientific research. Periodicals and books of the Victorian era were heavily illustrated, but little is know about who made the illustrations and how they ended up in print. One of the fundamental aims of the ConSciCom project, through your help on Illustrated Life, is to better understand exactly why and how images were used to produce knowledge about the natural world within printed media. Unlike the 20th and 21st centuries, the barriers to participating in science in the 19th century were not as straightforward as they are now. Better understanding the range of individuals who made science through their images will help us ascertain what constituted a 19th century scientist.. The ultimate goal of the project is to better understand the roots of citizen science – and this is the first Zooniverse project where citizen scientists are both the researchers and the subject of the research. In this way, how you – as a citizen scientist –participate in this project has a direct impact on how we can come to understand historical and modern notions of what it means to do science.

     '''
   }]


Add specs for keyup command handler

Add specs for keyup command handler

--- spec/keymap-manager-spec.coffee
+++ spec/keymap-manager-spec.coffee
@@ -4,7 +4,7 @@
 {$$} = require 'space-pencil'
 {appendContent} = require './spec-helper'
 KeymapManager = require '../src/keymap-manager'
-{buildKeydownEvent} = KeymapManager
+{buildKeydownEvent, buildKeyupEvent} = KeymapManager

 describe "KeymapManager", ->
   keymapManager = null
@@ -334,6 +334,48 @@
           keymapManager.handleKeyboardEvent(buildKeydownEvent('g', target: editor))

           expect(events).toEqual ['input:d', 'input:o', 'dog']
+
+    describe "when the a binding specifies a keyup handler", ->
+      [events, elementA] = []
+
+      beforeEach ->
+        elementA = appendContent $$ ->
+          @div class: 'a'
+
+        events = []
+        elementA.addEventListener 'y-command', (e) -> events.push('y-keydown')
+        elementA.addEventListener 'y-command-up', (e) -> events.push('y-keyup')
+
+        keymapManager.add "test",
+          ".a":
+            "ctrl-y": "y-command"
+            "ctrl-y^ctrl": "y-command-up"
+
+      it "dispatches the y-keyup command when a matching keystroke precedes it", ->
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('y', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown', 'y-keyup']
+
+      it "doesn't dispatch the y-keyup command when a non-matching keystroke precedes it", ->
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('y', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeydownEvent('x', ctrl: true, target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown']
+
+        keymapManager.handleKeyboardEvent(buildKeyupEvent('ctrl', target: elementA))
+        expect(events).toEqual ['y-keydown', 'y-keydown']

     it "only counts entire keystrokes when checking for partial matches", ->
       element = $$ -> @div class: 'a'


override factlink uri in test_file

override factlink uri in test_file

--- app/js/loader/loader_common.coffee
+++ app/js/loader/loader_common.coffee
@@ -61,10 +61,19 @@
 # create the factlink config object
 factlink_config = __INLINE_CONFIG_PLACEHOLDER__

-# concatenate the config and jail scripts
-jail_code = 'window.FactlinkConfig = ' + factlink_config + '; ' + jslib_jail_code

 this_env = JSON.parse(factlink_config).env
+
+extra_config =
+  if this_env != 'production' && FactlinkConfig_override_uri
+    'window.FactlinkConfig.base_uri = ' + JSON.stringify(FactlinkConfig_override_uri) + '; '
+  else
+    ''
+
+
+# concatenate the config and jail scripts
+jail_code = 'window.FactlinkConfig = ' + factlink_config + '; ' + extra_config + jslib_jail_code
+

 if window.__internalFactlinkState
   if window.__internalFactlinkState.env != this_env


Exclude hidden files from bundles.

Exclude hidden files from bundles.

--- src/bundle.coffee
+++ src/bundle.coffee
@@ -75,6 +75,11 @@
         @relativeEntrypoint = @entrypoint.slice @root.length
         @files = []

+    shouldInclude: (file) ->
+        name = fs.path.basename file
+        hidden = name.indexOf('.') is 0
+        not hidden
+
     # TODO: merge options against a default
     push: (path, options) ->
         # the bundle works with relative paths
@@ -102,7 +107,8 @@
     create: (callback) ->
         finder = findit.find @root
         finder.on 'file', (file) =>
-            @push file
+            if @shouldInclude file
+                @push file
         finder.on 'end', =>
             callback null, this


call subject.destroy before select when fetching subjects

--- app/models/subject_selector.coffee
+++ app/models/subject_selector.coffee
@@ -40,6 +40,7 @@

     Subject.fetch limit: 1, (subjects) =>
       if subjects?.length
+        Subject.current?.destroy()
         @setGroup(groupToReturnTo)
         subjects[0].select()
       else
@@ -54,6 +55,7 @@
       safes = subjects.filter (s) => s.classification_count > @safeCount

       if safes?.length
+        Subject.current?.destroy()
         safes[0].select()
       else
         @getSafeNext(attempts - 1)


Add missed pikaday module

Add missed pikaday module

--- app/scripts/lib/fix_modules.coffee
+++ app/scripts/lib/fix_modules.coffee
@@ -1,10 +1,16 @@
 # Provide ability to load some vendor libraries via CommonJS require.
 window.require.define
   moment: (require, exports, module) ->
-    module.exports = moment
+    module.exports = window.moment
+
   tinycon: (require, exports, module) ->
-    module.exports = Tinycon
+    module.exports = window.Tinycon
+
   marked: (require, exports, module) ->
-    module.exports = marked
+    module.exports = window.marked
+
   highlight: (require, exports, module) ->
-    module.exports = hljs
+    module.exports = window.hljs
+
+  pikaday: (require, exports, module) ->
+    module.exports = window.Pikaday


Added language-vue

Added language-vue

--- .atom/packages.cson
+++ .atom/packages.cson
@@ -39,6 +39,7 @@
   "language-babel"
   "language-docker"
   "language-powershell"
+  "language-vue"
   "linter"
   "linter-bootlint"
   "linter-csslint"


[balrog-ui] Spec Stuff

[balrog-ui] Spec Stuff

--- ui/spec/services/authentication_service_spec.coffee
+++ ui/spec/services/authentication_service_spec.coffee
@@ -14,4 +14,4 @@

   describe "#logout", ->
     When -> @AuthenticationService.logout()
-    Then -> expect(@$httpPost).toHaveBeenCalled()
+    Then -> expect(@$httpPost).toHaveBeenCalledWith('/logout')


jlee's fix for a tiny bug in history browser

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -2679,6 +2679,7 @@
             @element.find(".salvus-editor-history-revision-number").text("Revision " + @nlines)
             if @nlines == 0
                 @element.find(".salvus-editor-history-revision-time").text("")
+                @back_button.addClass("disabled")
             else
                 @element.find(".salvus-editor-history-revision-time").text(new Date(JSON.parse(@log[1]).time).toLocaleString())
             @history_editor.codemirror.setValue(JSON.parse(@log[0]))


Added redirect for XMAS event

Added redirect for XMAS event

--- Enactus.coffee
+++ Enactus.coffee
@@ -37,6 +37,12 @@

 app.get '/', actions.index;

+##
+## Christmas Bash Facebook Event (Temporary)
+##
+app.get '/xmas', (req, res) ->
+    res.redirect 'https://www.facebook.com/events/233160706853421'
+
 app.get '/:namespace/:page', (req, res) ->
     try
         namespace = routes[req.params.namespace]


teams: activity: minor touch for channel description

--- client/activity/lib/components/chatpane/index.coffee
+++ client/activity/lib/components/chatpane/index.coffee
@@ -2,6 +2,7 @@
 React                = require 'kd-react'
 ChatList             = require 'activity/components/chatlist'
 ActivityFlux         = require 'activity/flux'
+whoami               = require 'app/util/whoami'
 Scroller             = require 'app/components/scroller'
 ScrollerMixin        = require 'app/components/scroller/scrollermixin'
 Link                 = require 'app/components/common/link'
@@ -62,13 +63,21 @@
     'visible': @props.showIntegrationTooltip

+  renderAuthor: ->
+
+    if @props.createdBy._id is whoami()._id
+      you
+    else
+      <ProfileLinkContainer origin={@props.createdBy}>
+        <ProfileText />
+      </ProfileLinkContainer>
+
+
   renderProfileLink: ->

     createdAt = @getChannelCreationDate @channel 'createdAt'
     , created by
-      <ProfileLinkContainer origin={@props.createdBy}>
-        <ProfileText />
-      </ProfileLinkContainer> {createdAt}.

+      {@renderAuthor()} {createdAt}.

   renderChannelName: ->


Ace: ScrollPastEnd is default on now.

Ace: ScrollPastEnd is default on now.

--- client/app/Applications/Ace.kdapplication/ace.coffee
+++ client/app/Applications/Ace.kdapplication/ace.coffee
@@ -60,7 +60,7 @@
       @setFontSize            @appStorage.getValue('fontSize')            ? 12     ,no
       @setTabSize             @appStorage.getValue('tabSize')             ? 4      ,no
       @setKeyboardHandler     @appStorage.getValue('keyboardHandler')     ? "default"
-      @setScrollPastEnd       @appStorage.getValue('scrollPastEnd')       ? no
+      @setScrollPastEnd       @appStorage.getValue('scrollPastEnd')       ? yes

     require ["ace/ext/language_tools"], =>
       @editor.setOptions


Removes user when missing dropping

Removes user when missing dropping

--- coffee/presenters/home_presenter.coffee
+++ coffee/presenters/home_presenter.coffee
@@ -15,6 +15,7 @@

     EventBus.get('RideEngine').register(PickupEvent.NAME, (z) => @onPickup(z))
     EventBus.get('RideEngine').register(DropEvent.NAME, (z) => @onDrop(z))
+    EventBus.get('Zone').register DropZoneVanishedEvent.NAME, (z) => @onMissedDrop(z)
     EventBus.getDefault().register(
                                     OnStartEvent.NAME,
                                     () =>
@@ -60,6 +61,10 @@
     @currentRides[e.getZone().getId()] = o
     @pickupSound.play()

+  onMissedDrop: (e) ->
+    o = @currentRides[e.getId()]
+    @userEngine.hide(o.user)
+    delete @currentRides[id]

   onDrop: (e) ->
     id = e.getZone().getId()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/camera.coffee
+++ src/camera.coffee
@@ -42,7 +42,7 @@

     if objectCenter.y > centerRect.bottom
       I.scroll.y = I.scroll.y.approach(centerRect.bottom, I.maxVelocity.y)
-    else if objectCenter.x < centerRect.top
+    else if objectCenter.y < centerRect.top
       I.scroll.x = I.scroll.x.approach(centerRect.top, I.maxVelocity.y)

     I.scroll.x = I.scroll.x.clamp(I.cameraBounds.left, I.cameraBounds.right - I.screen.width)


chrome compatibility

chrome compatibility

--- app/views/CNeditor/CNeditor.coffee
+++ app/views/CNeditor/CNeditor.coffee
@@ -196,6 +196,7 @@

                 # if chrome => listen to keyup to correct the insertion of the
                 # first caracter of an empty line
+                @isFirefox = `'MozBoxSizing' in document.documentElement.style`
                 @isSafari = Object.prototype.toString.call(window.HTMLElement)
                 @isSafari = @isSafari.indexOf('Constructor') > 0
                 @isChrome = !@isSafari &&
@@ -718,7 +719,7 @@

     ###*
      * This function is called only if in Chrome, because the insertion of a caracter
-     * by the browser may out of a span.
+     * by the browser may be out of a span.
      * This is du to a bug in Chrome : you can create a range with its start
      * break point in an empty span. But if you add this range to the selection,
      * then this latter will not respect your range and its start break point
@@ -920,10 +921,14 @@

         # 6- position selection
         rg = document.createRange()
-        rg.setStart(breakPoints[0].cont,breakPoints[0].offset)
-        rg.setEnd(breakPoints[1].cont,breakPoints[1].offset)
-        @currentSel.sel.removeAllRanges()
-        @currentSel.sel.addRange(rg)
+        rg.setStart(bp1.cont, bp1.offset)
+        rg.setEnd(  bp2.cont, bp2.offset)
+        if @isFirefox
+            sel = this.currentSel.sel
+        else
+            sel = document.getSelection()
+        sel.removeAllRanges()
+        sel.addRange(rg)

         return true


Deprecate default permission usage

Deprecate default permission usage

--- client/ide/lib/views/chat/idechatsettingspane.coffee
+++ client/ide/lib/views/chat/idechatsettingspane.coffee
@@ -33,7 +33,6 @@
     @on 'CollaborationNotInitialized', => @everyone.destroySubViews()
     @on 'ParticipantJoined', @bound 'addParticipant'
     @on 'ParticipantLeft',   @bound 'removeParticipant'
-    @on 'PermissionChanged', @bound 'handlePermissionChange'

     @on 'CollaborationEnded', =>
       @toggleButtons 'ended'
@@ -206,11 +205,8 @@
     {nickname}        = account.profile
     watchList         = @rtm.getFromModel("#{nick()}WatchMap").keys()
     isWatching        = watchList.indexOf(nickname) > -1
-    permissionsMap    = @rtm.getFromModel 'permissions'
-    defaultPermission = permissionsMap.get 'default'
-    permission        = permissionsMap.get(nickname) or defaultPermission
     channel           = @getData()
-    options           = { isOnline, @isInSession, isWatching, permission }
+    options           = { isOnline, @isInSession, isWatching }
     data              = { account, channel }
     participantView   = new IDEChatParticipantView options, data

@@ -218,9 +214,6 @@
     @everyone.addSubView participantView, null, isOnline
     @onboarding?.destroy()

-    participantView.on 'ParticipantPermissionChanged', (permission) =>
-      @rtm.getFromModel('permissions').set nickname, permission
-

   removeParticipant: (username, unshare) ->

@@ -241,29 +234,6 @@

     remote.cacheable nickname, (err, account) =>
       @createParticipantView account.first, yes
-
-
-  updateDefaultPermissions: ->
-
-    permissions = @rtm.getFromModel 'permissions'
-    @defaultPermission.setValue permissions.get 'default'
-
-
-  setDefaultPermission: (value) ->
-
-    @rtm.getFromModel('permissions').set 'default', value
-
-
-  handlePermissionChange: (event) ->
-
-    {newValue, property} = event
-
-    return  unless newValue in ['edit', 'read']
-
-    if property is 'default'
-      @defaultPermission.setValue newValue
-    else
-      @participantViews[property]?.permissions.setValue newValue

   viewAppended: JView::viewAppended


Set true to options.droppable for IDETabHandleView

--- client/ide/lib/views/tabview/idetabhandleview.coffee
+++ client/ide/lib/views/tabview/idetabhandleview.coffee
@@ -6,7 +6,8 @@

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

-    options.bind  = 'dragstart'
+    options.droppable ?= yes
+    options.bind       = 'dragstart'

     super options, data


Keep runtime properties we get by connecting

--- components/GetRemoteRuntimes.coffee
+++ components/GetRemoteRuntimes.coffee
@@ -34,6 +34,9 @@
         knownSeen = new Date known.seen
         if rt.runtime.seen.getTime() is knownSeen.getTime() and rt.runtime.address is known.address and rt.runtime.secret is known.secret
           return false
+        # Copy information we only get by connecting
+        rt.runtime.capabilities = known.capabilities
+        rt.runtime.components = known.components
         true
       return callback() unless updateRts.length
       out.beginGroup '$NOFLO_REGISTRY_SERVICE'


Analytics support

Analytics support

--- components/LoggingMiddleware.coffee
+++ components/LoggingMiddleware.coffee
@@ -1,6 +1,10 @@
 noflo = require 'noflo'
 debugAction = require('debug') 'noflo-ui:action'
 debugActionFull = require('debug') 'noflo-ui:action:full'
+
+sendEvent = (label, action = 'click', category = 'menu') ->
+  return unless typeof window.ga is 'function'
+  window.ga 'send', 'event', category, action, label

 exports.getComponent = ->
   c = new noflo.Component
@@ -18,5 +22,12 @@
     action = groups.join ':'
     debugAction action
     debugActionFull action, data.payload
+
+    switch action
+      when 'user:login'
+        sendEvent 'userLogin'
+      when 'user:logout'
+        sendEvent 'userLogout'
+
     out.send data
     do callback


stubs sticky on article view test

stubs sticky on article view test

--- components/article/test/view.coffee
+++ components/article/test/view.coffee
@@ -26,6 +26,7 @@
       @ArticleView.__set__ 'CurrentUser', { orNull: ->
         new CurrentUser _.extend( fabricate('user') , { 'id' : '4d8cd73191a5c50ce210002a' } ) }
       @ArticleView.__set__ 'imagesLoaded', sinon.stub()
+      @ArticleView.__set__ 'Sticky', -> { add: sinon.stub() }
       stubChildClasses @ArticleView, this,
         ['initCarousel']
         []


Concat files for test

Concat files for test

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -6,10 +6,35 @@
       compile:
         files:
           'lib/backbone.websql.deferred.js': 'src/backbone.websql.deferred.coffee'
+          'test/lib/backbone.websql.deferred-test.js': 'test/src/backbone.websql.deferred-test.coffee'
+
+    concat:
+      options:
+        separator: ';'
+      deps:
+        src: [
+          'node_modules/jquery/dist/jquery.js'
+          'node_modules/underscore/underscore.js'
+          'node_modules/backbone/backbone.js'
+        ]
+        dest: 'vendor/vendor.js'
+      depsForTest:
+        src: [
+          'node_modules/mocha/mocha.js'
+          'node_modules/sinon/lib/sinon.js'
+          'node_modules/chai/chai.js'
+          'node_modules/sinon-chai/lib/sinon-chai.js'
+        ]
+        dest: 'vendor/test-vendor.js'
+      depsForCSS:
+        src: [
+          'node_modules/mocha/mocha.css'
+        ]
+        dest: 'vendor/test.css'

     watch:
       src:
-        files: "src/*.coffee"
+        files: ["src/*.coffee", "test/src/*.coffee"]
         tasks: ["build"]

     simplemocha:
@@ -26,6 +51,7 @@
   grunt.loadNpmTasks "grunt-contrib-coffee"
   grunt.loadNpmTasks "grunt-contrib-watch"
   grunt.loadNpmTasks "grunt-simple-mocha"
+  grunt.loadNpmTasks 'grunt-contrib-concat'

   grunt.registerTask "default", ["watch"]
   grunt.registerTask "build",   ["coffee"]


Get glyphicons from glyphicons.com. Link $QI_DATA, if available.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,3 +1,5 @@
+path = require 'path'
+
 module.exports = (grunt) ->
   grunt.config.init(
     pkg: grunt.file.readJSON('package.json')
@@ -67,33 +69,6 @@
         src: '**/*'
         dest: 'public/'

-      # Note: this task is only used to copy CSS map files. The
-      # CSS style files themselves are copied to the destination
-      # in the concat:css task.
-      #
-      # This task is only used to copy the Bootstrap map file.
-      # Since the non-minimized Bootstrap module references the
-      # CSS map, the map must be colocated with the stylesheets.
-      cssmap:
-        expand: true
-        flatten: true
-        cwd: 'node_modules/'
-        src: ['bootstrap/dist/css/bootstrap.css.map']
-        dest: 'public/stylesheets/'
-
-      fonts:
-        expand: true
-        flatten: true
-        # Note: Due to a grunt bug, the font-awesome example subdirectory,
-        #   e.g. 4.0.4/index.html, can't be excluded
-        #   (cf. https://github.com/gruntjs/grunt-contrib-copy/issues/13).
-        src: [
-          'node_modules/bootstrap/dist/fonts/*'
-          'node_modules/font-awesome/fonts/*'
-          'fonts/google/*'
-        ]
-        dest: 'public/fonts/'
-
       doc:
         expand: true
         src: ['src/**/*.ts']
@@ -113,9 +88,23 @@
           'lib/papaya.css'
           'jspm_packages/npm/nouislider*/distribute/nouislider.min.css'
           'node_modules/bootstrap/dist/css/bootstrap.css'
-          'node_modules/font-awesome/css/font-awesome.css'
         ]
         dest: 'public/stylesheets/vendor.css'
+
+    curl:
+      icons:
+        src: 'http://glyphicons.com/files/glyphicons_free.zip'
+        dest: 'build/glyphicons.zip'
+
+    unzip:
+      icons:
+        router: (filepath) ->
+          # Get rid of the numeric qualifier in the file name.
+          base = path.basename(filepath)
+          if base.match(/glyphicons-.*\.png/)
+            base.replace(/glyphicons-\d+-/, 'glyphicon-')
+        src: 'build/glyphicons.zip'
+        dest: 'public/media'

     concurrent:
       options:
@@ -167,19 +156,6 @@
         configFile: 'karma-conf.coffee'

     exec:
-      # Delete the qirest Anaconda environment.
-      #
-      # Note: conda env remove hangs when executed from a script.
-      # It does not run when executed from a script in background.
-      # There is no known mechanism to make this work.
-      # The work-around is to execute the conda env remove
-      # command below manually.
-      #
-      # cleanqirest:
-      #   command:
-      #     '(source deactivate 2>/dev/null);' +
-      #     '(yes | conda env remove -n qirest)'
-
       # Clean all installation artifacts, including the qirest Anaconda
       # environment.
       cleannpm:
@@ -243,6 +219,10 @@
       updatewebdriver:
         command: './node_modules/protractor/bin/webdriver-manager update'

+      # Links the test data, if available.
+      lndata:
+        command: 'if [ -n "$QI_DATA" ]; then ln -s $QI_DATA ./public; fi'
+
     protractor:
       e2e:
         configFile: 'protractor-conf.coffee'
@@ -285,10 +265,14 @@
   grunt.registerTask 'compile', ['tslint', 'concurrent']

   # Build the application from scratch.
-  grunt.registerTask 'copy:dev', ['copy:static', 'copy:cssmap', 'copy:fonts']
+  grunt.registerTask 'icons', ['curl:icons', 'unzip:icons']
+
+  # Build the application from scratch.
+  grunt.registerTask 'copy:dev', ['copy:static']

   # Build the application server.
-  grunt.registerTask 'build:dev', ['copy:dev', 'concat:css', 'compile']
+  grunt.registerTask 'build:dev', ['icons', 'copy:dev',
+                                   'concat:css', 'compile']

   # Build the application documentation.
   grunt.registerTask 'doc', ['copy:doc', 'coffee:doc', 'yuidoc']


* grunt test use dot reporter

* grunt test use dot reporter

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -35,18 +35,9 @@
           ui: 'bdd'
           quiet: false
           require: 'coffee-script/register'
-          #reporter: 'dot'
+          reporter: 'dot'
           check: ['src/**/*.coffee', 'test/**/*.js', 'test/**/*.coffee']
         src: ['test/**/*.coffee']
-
-    powerbuild:
-      options:
-        sourceMap: true
-        node: false
-        handlers:
-          '.coffee': (src, canonicalName) ->
-            {js, v3SourceMap} = coffee.compile src, sourceMap: true, bare: true
-            return {code: js, map: v3SourceMap}

     watch:
       options:
@@ -72,9 +63,8 @@
   grunt.loadNpmTasks('grunt-contrib-coffee')
   grunt.loadNpmTasks('grunt-contrib-clean')
   grunt.loadNpmTasks('grunt-newer')
-  grunt.loadNpmTasks('powerbuild')

-  grunt.registerTask('build', ['newer:coffee', 'copy'])
+  grunt.registerTask('build', ['newer:coffee', 'newer:copy'])
   grunt.registerTask('rebuild', ['clean', 'build'])
   grunt.registerTask('test', ['build', 'mochaTest'])
   grunt.registerTask('watch', ['watch'])


zip polymer-gestures

zip polymer-gestures

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -152,6 +152,9 @@
             'bower_components/klay-js/klay*.js'
             'bower_components/platform/*.js'
             'bower_components/polymer/*.js'
+            'bower_components/polymer-gestures/polymer-gestures.html'
+            'bower_components/polymer-gestures/src/*.js'
+            'bower_components/hammer.js/hammer.js'
             'bower_components/react/*.js'
             'bower_components/the-graph/**/*.js'
             'bower_components/the-graph/**/*.css'


Grunt: Add hacky target to build MicroFlo for Tiva

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,3 +1,7 @@
+microflo_make = "make -f ./node_modules/microflo/Makefile \
+  MICROFLO_SOURCE_DIR=`pwd`/node_modules/microflo/microflo \
+  MICROFLO=./node_modules/.bin/microflo"
+
 module.exports = ->
   # Project configuration
   @initConfig
@@ -58,7 +62,8 @@
             level: 'warn'

     exec:
-      build_arduino: 'make -f ./node_modules/microflo/Makefile build-arduino-min GRAPH=graphs/PortalLights.fbp MICROFLO_SOURCE_DIR=./node_modules/microflo/microflo'
+      build_arduino: "#{microflo_make} GRAPH=graphs/PortalLights.fbp build-arduino-min"
+      build_tiva: "#{microflo_make} STELLARIS_GRAPH=graphs/TableLights.fbp build-stellaris"

   # Grunt plugins used for building
   @loadNpmTasks 'grunt-exec'


Remove load task for grunt-component.

Remove load task for grunt-component.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -61,7 +61,6 @@
       all: ['spec/runner.html']

   # Grunt plugins used for building
-  @loadNpmTasks 'grunt-component'
   @loadNpmTasks 'grunt-component-build'
   @loadNpmTasks 'grunt-contrib-uglify'


fix(grunt): now also watches Jekyll configuration files

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -82,7 +82,7 @@
           interrupt: true

       jekyll:
-        files: ["<%= config.app %>/**/*", "!_*"]
+        files: ["<%= config.app %>/**/*", "!_*",  "_config*.yml"]
         tasks: ['jekyll:serve']

     uglify:


update gruntfile

update gruntfile

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -117,6 +117,7 @@
     # # To test, run `grunt --no-write -v release`
     release:
       options:
+        file: 'bower.json'
         npm: false

     karma:
@@ -132,6 +133,5 @@
           autoWatch: true

   grunt.registerTask 'default', ['eco:all', 'coffee:all', 'concat:all', 'copy:all', 'stylus:all', 'clean:compiled']
-  grunt.registerTask 'dist', ['default', 'cssmin:dist', 'uglify:dist']
-  grunt.registerTask 'test', ['default', 'karma:main']
-  grunt.registerTask 'release', ['default', 'dist', 'test', 'release']
+  grunt.registerTask 'dist', ['cssmin:dist', 'uglify:dist']
+  grunt.registerTask 'test', ['karma:main']


Added component.json ref to Gruntfile.coffee

To maintain automatically the version in component.json

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -32,7 +32,7 @@

         bump:
             options:
-                files: ['package.json', 'bower.json']
+                files: ['package.json', 'bower.json', 'component.json']
                 commitFiles: ['-a']

         _release:


Move legacy files back to app.

Move legacy files back to app.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -54,13 +54,13 @@
           require('csso-stylus')
         ]
       default:
-        src: ['stylus/app.styl']
+        src: ['app/stylesheets/app.styl']
         dest: 'public/stylesheets/app.css'

     copy:
       static:
         expand: true
-        cwd: 'static/'
+        cwd: 'app/static/'
         src: '**/*'
         dest: 'public/'


[fix] Lint all the files. I don't know why but noone noticed that nothing was linted anymore. This commit fixes the jshint task to lint again.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -25,16 +25,6 @@
       qunit: ['adapter/qunit.src.js']
       grunt: ['grunt.js', 'tasks/*.js']

-    lint:
-      server: '<%= files.server %>'
-      client: '<%= files.client %>'
-      jasmine: '<%= files.jasmine %>'
-      mocha: '<%= files.mocha %>'
-      ngScenario: '<%= files.ngScenario %>'
-      require: '<%= files.require %>'
-      qunit: '<%= files.qunit %>'
-      grunt: '<%= files.grunt %>'
-
     build:
       client: '<%= files.client %>'
       jasmine: '<%= files.jasmine %>'
@@ -58,20 +48,38 @@
     # http://www.jshint.com/options/
     jshint:
       server:
+        files:
+          src: '<%= files.server %>'
         options: JSHINT_NODE
       grunt:
+        files:
+          src: '<%= files.grunt %>'
         options: JSHINT_NODE
       client:
+        files:
+          src: '<%= files.client %>'
         options: JSHINT_BROWSER
       jasmine:
+        files:
+          src: '<%= files.jasmine %>'
         options: JSHINT_BROWSER
       mocha:
+        files:
+          src: '<%= files.mocha %>'
         options: JSHINT_BROWSER
       qunit:
+        files:
+          src: '<%= files.qunit %>'
         options: JSHINT_BROWSER
       ngScenario:
+        files:
+          src: '<%= files.ngScenario %>'
         options: JSHINT_BROWSER
-
+      require:
+        files:
+          src: '<%= files.require %>'
+        options: JSHINT_BROWSER
+
       options:
         quotmark: 'single'
         camelcase: true


feat(grunt): update dependencies

feat(grunt): update dependencies

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -320,6 +320,12 @@
       # Auto commit untracked files sync'ed from sync_local
       sync_commit:
         command: "sh <%= config.deploy.s3_website.dest %>/auto-commit"
+
+      amsf__core__update_deps:
+        command: [
+          "bundle install"
+          "npm install"
+        ].join("&&")

       amsf__theme__to_app:
         command: [
@@ -598,6 +604,7 @@
       ]
     grunt.task.run [
       "copy:amsf__core__to_app"
+      "shell:amsf__core__update_deps"
     ]

   grunt.registerTask "serve", "Fire up a server on local machine for development", [


- added 'update' task to GruntFile : shorthand for:  * git pulling  * bower & npm dependencies installation (npm deactivated for now, not needed)

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -64,6 +64,28 @@
         stdout: true
         stderr: true

+      update_git:
+        command: "git pull"
+        stdout: true
+        stderr: true
+      update_bower1:
+        command: "rm -r components"
+        stdout: true
+        stderr: true
+      update_bower2:
+        command: "bower install"
+        stdout: true
+        stderr: true
+      update_npm1:
+        command: "rm -r node_modules"
+        stdout: true
+        stderr: true
+      update_npm2:
+        command: "npm install"
+        stdout: true
+        stderr: true
+
+
     nodewebkit:
       options:
         version: "0.6.3" #0.8.2 0.6.3 works with polymer but unresolved does not get removed, does not work from 0.7.0 onwards, 0.8.2 works only partially (wrong order of events)
@@ -185,6 +207,14 @@
   #grunt.registerTask "release", ["concat", "uglify", "jasmine_node", "release"]
   grunt.registerTask "core", ["browserify", "uglify:main"]

+  #Project update (dependencies etc)
+  @registerTask 'update', 'update the project s dependencies', () =>
+    @task.run "exec:update_git"
+    @task.run "exec:update_bower1"
+    @task.run "exec:update_bower2"
+    #@task.run "exec:update_npm1"
+    #@task.run "exec:update_npm2"
+
   #Builds
   @registerTask 'build', 'Build usco-viewer for the chosen target/platform etc', (target = 'browser', subTarget='standalone') =>
     minify = grunt.option('minify');


fixed accidental bug

fixed accidental bug

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -294,11 +294,11 @@

       sass:
         files: '_assets/**/*.sass'
-        tasks: [ 'sass', 'cssmin' ]
+        tasks: [ 'sass:' + currentEnvironment, 'cssmin:' + currentEnvironment ]

       coffee:
         files: '_assets/**/*.coffee'
-        tasks: [ 'coffee', 'uglify' ]
+        tasks: [ 'coffee:' + currentEnvironment, 'uglify:' + currentEnvironment ]

       jade:
         files: '_includes/**/*.jade'


Remove redundant gruntfile elements

Remove redundant gruntfile elements

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -80,7 +80,6 @@
   'copy:cca_additional'
   'vulcanize:ccaInline'
   'vulcanize:ccaCsp'
-  'copy:cca'
   'browserify:ccaMain'
   'browserify:ccaContext'
   'browserify:ccaVulcanized'
@@ -590,10 +589,7 @@
               # extra components we use
               'generic_ui/fonts/*'
               'generic_ui/icons/*'
-              'icons/*'
-              '_locales/**'
-
-              # UI for not-connected
+
               # This is not browserified so we use .js instead of .static.js
               'polymer/vulcanized.{html,js}'


Make "spec" reporter the default for mocha

It was in the Gruntfile this way, just in the wrong section -- I'm just
putting it where it belongs (Per Simon M).

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -22,7 +22,6 @@

     mochaTest:
       files: ["test/*.coffee"]
-    mochaTestConfig:
       options:
         reporter: "spec"


remove unused localTaskmanager target

remove unused localTaskmanager target

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -99,13 +99,6 @@
         dest: 'build/third_party/' } ] }

     copy:
-      # This rule is used to build the root level task-manager that is checked
-      # in.
-      localTaskmanager: { files: [ {
-        expand: true, cwd: 'build/taskmanager/'
-        src: ['taskmanager.js']
-        dest: '.' } ] }
-
       crypto: Rule.copyModule 'crypto'

       arraybuffers: Rule.copyModule 'arraybuffers'


Added Jasmine functions to JSHint's predef array

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -42,6 +42,21 @@
 				boss: true
 				eqnull: true
 				browser: true
+				predef: [
+					"jasmine"
+					"describe"
+					"xdescribe"
+					"before"
+					"beforeEach"
+					"after"
+					"afterEach"
+					"it"
+					"xit"
+					"it"
+					"inject"
+					"expect"
+					"spyOn"
+				]

 			globals: {}

@@ -52,10 +67,7 @@

 			unit:
 				port: 9019
-				background: true
-
-			continuous:
-				singleRun: true
+				background: false

 			browsers: [
 				'Chrome'


fixed typo

fixed typo

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -182,7 +182,7 @@
         files: '_assets/**/*.coffee'
         tasks: [ 'coffee' ]

-      coffee:
+      jade:
         files: '_includes/**/*.jade'
         tasks: [ 'jade' ]


Fix `grunt log` task

Fix `grunt log` task

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -128,7 +128,7 @@
         command: "s3cmd sync -rP --guess-mime-type --delete-removed --no-preserve --cf-invalidate --exclude '.DS_Store' <%= core.cfg.static_files %> <%= core.cfg.s3_bucket %>"

       log:
-        command: "git log <%= core.pkg.version %>..HEAD --reverse --format=%B | sed '/^$/d' | sed 's/^/- /'"
+        command: "git log v<%= core.pkg.version %>..HEAD --reverse --format=%B | sed '/^$/d' | sed 's/^/- /'"

     concurrent:
       options:


fix($build): Uninclude iframe.xss.response.js ...

... in custombuild's minified js

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -503,7 +503,7 @@
                 src: ['<%= concat.all.dest %>']
                 dest: "#{paths.build}/all.<%= pkg.name %>.min.js"
             custom:
-                src: ["#{customBuildDest}/src/*.js"]
+                src: ["#{customBuildDest}/src/custom.<%= pkg.name %>-<%= pkg.version %>.js"]
                 dest: "#{customBuildDest}/src/custom.<%= pkg.name %>-<%= pkg.version %>.min.js"

         usebanner:
@@ -556,7 +556,7 @@
                             */ \n\n'''
             customfoot:
                 files:
-                    src: ["#{customBuildDest}/src/*.{js,css}"]
+                    src: ["#{customBuildDest}/*.{js,css}"]
                 options:
                     position: 'bottom'
                     banner: '/*! <%= grunt.template.today("yyyy-mm-dd") %> */\n'


Remove unused imports in Gruntfile.

Remove unused imports in Gruntfile.

--- Gruntfile.coffee
+++ Gruntfile.coffee
@@ -1,8 +1,5 @@
 module.exports = (grunt) ->
     @loadNpmTasks('grunt-contrib-clean')
-    @loadNpmTasks('grunt-contrib-concat')
-    @loadNpmTasks('grunt-contrib-cssmin')
-    @loadNpmTasks('grunt-contrib-uglify')
     @loadNpmTasks('grunt-wintersmith')
     @loadNpmTasks('grunt-zopfli')


stop using cdn.mathjax.org CDN url

stop using cdn.mathjax.org CDN url

Fixes: CNVS-36368

We could probably put this in our package.json
and load it ourselves but I just wanted to change
this in a way that had the least possibility
of side effects so I just updated the url to
the one they suggested to use going forward.

test plan:
1. go to the equation editor
2. confirm there are no errors in the js console
3. confirm that the basic editor works
4. confirm that the advanced editor works (live preview too)

Change-Id: If321662322ae2db1cb61a9dc4b2b00628021bbc8
Reviewed-on: https://gerrit.instructure.com/108810
Reviewed-by: Cody Cutrer <c4fe2b1d90ef8f2548c8aeabfa633434316f0305@instructure.com>
Tested-by: Jenkins
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Jeremy Putnam <b030d6d6f199f844ec424882be473c2ab9adc93b@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/views/tinymce/EquationToolbarView.coffee
+++ app/coffeescripts/views/tinymce/EquationToolbarView.coffee
@@ -43,7 +43,7 @@

       $('#mathjax-view .mathquill-tab-bar li:first-child').addClass('mathquill-tab-selected')

-      $.getScript("//cdn.mathjax.org/mathjax/2.1-latest/MathJax.js?config=TeX-AMS_HTML.js", @addMathJaxEvents)
+      $.getScript("//cdnjs.cloudflare.com/ajax/libs/mathjax/2.1/MathJax.js?config=TeX-AMS_HTML.js", @addMathJaxEvents)

     addMathJaxEvents: =>
       renderPreview = ->


Suppress Uglify warnings

Suppress Uglify warnings

--- webpack/index.coffee
+++ webpack/index.coffee
@@ -57,3 +57,5 @@
   ugly: new ugly
     output:
       max_line_len: 80
+    compress:
+      warnings: false


removed tests for helpers.formatTemplate (removed in 1977c41)

--- test/helpers.coffee
+++ test/helpers.coffee
@@ -77,8 +77,3 @@
       expect(helpers.startsWith 'abc', 'a').to.equal yes
       expect(helpers.startsWith 'abc', 'c').to.equal no
       expect(helpers.startsWith 'cba', 'b').to.equal no
-
-  describe 'formatTemplate()', ->
-    expect(helpers.formatTemplate '{{#camelize}}{{name}}{{/camelize}}', name: 'hitler_user').to.equal 'HitlerUser'
-    expect(helpers.formatTemplate '{{#camelize}}{{name}}{{/camelize}}\n{{#camelize}}{{pluralName}}{{/camelize}}', name: 'hitler_user', pluralName: 'hitler_users')
-      .to.equal 'HitlerUser\nHitlerUsers'


Remove index numbers from front of section titles

--- app/assets/javascripts/me-add-to-playlist.coffee
+++ app/assets/javascripts/me-add-to-playlist.coffee
@@ -22,8 +22,9 @@
           default_title = player.options.playlistItemDefaultTitle
           current_span = $('li i.now-playing').parent().find('a[data-fragmentbegin]')
           if current_span
-            re = /\s*\(.*\)$/ # duration notation at end of section title ' (2:00)'
-            structure_title = current_span.text().replace(re,'').trim()
+            re1 = /^\s*\d\.\s*/ # index number in front of section title '1. '
+            re2 = /\s*\(.*\)$/  # duration notation at end of section title ' (2:00)'
+            structure_title = current_span.text().replace(re1,'').replace(re2,'').trim()
             parent = current_span.closest('ul').closest('li').prev()
             while parent.length > 0
               structure_title = parent.text().trim()+' - '+structure_title


MindpinBucketsProgress，设置回调，以及简单示例

MindpinBucketsProgress，设置回调，以及简单示例

--- app/assets/javascripts/mindpin_buckets.js.coffee
+++ app/assets/javascripts/mindpin_buckets.js.coffee
@@ -1,5 +1,5 @@
 class @MindpinBuckets
-  constructor: (@$path_fix)->
+  constructor: (@$path_fix, @$progress)->
     @_init()

   _init: ->
@@ -7,6 +7,7 @@
       @$path_fix = ""
     @buckets_path = @$path_fix + "/buckets"
     @bucketings_path = @$path_fix + "/bucketings"
+    @$progress ||= new MindpinBucketsProgress(@)

   buckets: (bucket_type) ->
     jQuery.ajax
@@ -16,7 +17,9 @@
         console.log "buckets success"
         console.log res
         if res['result']
-          console.log res.result
+          @$progress.get_buckets_success(res.result)
+        else
+          @$progress.error(res.error)

   create_bucket: (bucket_type, name, desc) ->
     jQuery.ajax
@@ -31,6 +34,9 @@
         console.log res
         if res['result']
           console.log res.result
+          @$progress.create_bucket_success(res.result)
+        else
+          @$progress.error(res.error)

   add_to_bucket: (resource_type, resource_id, bucket_type, bucket_id) ->
     jQuery.ajax
@@ -46,6 +52,9 @@
         console.log res
         if res['result']
           console.log res.result
+          @$progress.add_to_bucket_success(res.result)
+        else
+          @$progress.error(res.error)

   remove_from_bucket: (resource_type, resource_id, bucket_type, bucket_id) ->
     jQuery.ajax
@@ -61,3 +70,53 @@
         console.log res
         if res['result']
           console.log res.result
+          @$progress.remove_from_bucket_success(res.result)
+        else
+          @$progress.error(res.error)
+
+###
+  用于处理MindpinBuckets回调的类
+  里面规定了一些方法，可以根据传输的不同结果，做不同的处理
+###
+###
+  获取buckets            成功、失败
+  创建bucket             成功、失败
+  将resource添加到bucket 成功、失败
+  将resource从bucket删除 成功、失败
+
+  失败均为500，结构也一样，可以统一
+###
+class @MindpinBucketsProgress
+  constructor: (@mindpin_buckets) ->
+    #console.log 'MindpinBucketsProgress'
+    #console.log @mindpin_buckets
+
+  get_buckets_success: (buckets) ->
+    console.log 'get_buckets_success'
+    console.log buckets
+
+  create_bucket_success: (bucket) ->
+    console.log 'create_bucket_success'
+    console.log bucket
+
+  add_to_bucket_success: (bucket) ->
+    console.log 'add_to_bucket_success'
+    console.log bucket
+
+  remove_from_bucket_success: (bucket) ->
+    console.log 'remove_from_bucket_success'
+    console.log bucket
+
+  error: (error) ->
+    console.log 'error'
+    console.log error
+
+# example
+#class @CustomProgress extends MindpinBucketsProgress
+  #constructor: ()->
+
+  #get_buckets_success: (buckets) ->
+    #alert(buckets)
+#
+#buckets = new MindpinBuckets("",new CustomProgress())
+#buckets.buckets("folder")


Add update_display_on_page

Add update_display_on_page

--- app/assets/javascripts/models/active_page.coffee
+++ app/assets/javascripts/models/active_page.coffee
@@ -25,9 +25,10 @@
       if select.length
         select.val(value)

-      span = $template.find("span[data-attr='" + key + "']")
-      if span.length
-        span.html(value)
+      display = $template.find("div[data-attr='" + key + "'], span[data-attr='" + key + "'], p[data-attr='" + key + "']")
+      if display.length
+        new_display = display.html(value)
+        display.replaceWith(new_display)

     @_append_data_model_to_page()

@@ -39,6 +40,11 @@
   update_vals_on_page: ->
     $.each @attributes, (attr, val) =>
       $("[data-k-uuid='" + @uuid + "'][data-attr='" + attr + "']").val(val)
+
+  update_displays_on_page: ->
+    $.each @attributes, (attr, val) =>
+      # TODO make this work with span and p
+      $("[data-k-uuid='" + @uuid + "']div[data-attr='" + attr + "']").html(val)

   dirty_from_page: ->
     dirty = []
@@ -65,9 +71,9 @@
       else
         @set $input.data("attr"), $input.val()

-      model_data = $("[data-kindred-model]").find("[data-k-uuid='" + @uuid + "']")
-      if !isNaN(parseFloat(model_data.data("id"))) && isFinite(model_data.data("id"))
-        @id = model_data.data("id")
+    model_data = $("[data-kindred-model]").find("[data-k-uuid='" + @uuid + "']")
+    if !isNaN(parseFloat(model_data.data("id"))) && isFinite(model_data.data("id"))
+      @id = model_data.data("id")

     $("select[data-k-uuid='" + @uuid + "']").each (i, select) =>
       @set $(select).data("attr"), $(select).val()


Clean up unnecessary info on feedback subject lines

--- app/assets/javascripts/models/ui/feedback.coffee
+++ app/assets/javascripts/models/ui/feedback.coffee
@@ -7,26 +7,10 @@
                   ajax = @edsc.util.xhr.ajax) ->
   class Feedback
     showFeedback: (area, collectionId) =>
-      area = "" unless area?
-      collectionId = "" unless collectionId?
-      prefix = if area == 'General' then "" else "[Metadata]"
-
-      # Generate a new projectId for developer debugging
-      projectId = ""
-      path = urlUtil.cleanPath()
-      data = {path: path}
-      ajax
-        method: 'post'
-        dataType: 'text'
-        url: "/projects?id=#{projectId}"
-        data: data
-        success: (data) ->
-          projectId = data
-          console.log "Created project #{projectId}"
-          console.log "Path: #{path}"
-        complete: ->
-          feedback.showForm({subject: "#{prefix}[#{area}-#{collectionId}-#{projectId}]"})
-
+      if area == 'General'
+        feedback.showForm()
+      else
+        feedback.showForm({subject: "[Metadata][#{area}]"})
       false

   exports = Feedback


use single quotes in main header

use single quotes in main header

--- app/assets/javascripts/partials/main-header.cjsx
+++ app/assets/javascripts/partials/main-header.cjsx
@@ -1,4 +1,4 @@
-React = require("react")
+React = require('react')
 {Link} = require 'react-router'
 Router = require 'react-router'
 # {Navigation, Link} = Router
@@ -12,10 +12,10 @@
     loginProviders: []

   render: ->
-
+

-
-        <Link to="/" activeClassName="selected" className="main-header-item logo">
+
+        <Link to='/' activeClassName='selected' className='main-header-item logo'>
           {
             unless @props.logo?
               @props.short_title
@@ -31,36 +31,36 @@
           workflows = workflows.sort (w1, w2) -> if w1.order > w2.order then 1 else -1
           workflows.map (workflow, key) =>
             title = workflow.name.charAt(0).toUpperCase() + workflow.name.slice(1)
-            <Link key={key} to="/#{workflow.name}" activeClassName="selected" className="main-header-item">{title}</Link>
+            <Link key={key} to="/#{workflow.name}" activeClassName='selected' className='main-header-item'>{title}</Link>
         }
         { # Page tabs, check for main menu
           if @props.menus? && @props.menus.main?
             for item, i in @props.menus.main
               if item.page?
-                <Link key={item.page} to="/#{item.page}" activeClassName="selected" className="main-header-item">{item.label}</Link>
+                <Link key={item.page} to="/#{item.page}" activeClassName='selected' className='main-header-item'>{item.label}</Link>
               else if item.url?
-                {item.label}
+                {item.label}
               else
-                {item.label}
+                {item.label}
           # Otherwise, just list all the pages in default order
           else
             @props.pages?.map (page, key) =>
-              formatted_name = page.name.replace("_", " ")
-              <Link key={key} to="/#{page.name.toLowerCase()}" activeClassName="selected" className="main-header-item">{formatted_name}</Link>
+              formatted_name = page.name.replace('_', ' ')
+              <Link key={key} to="/#{page.name.toLowerCase()}" activeClassName='selected' className='main-header-item'>{formatted_name}</Link>
         }

         { # include feedback tab if defined
           showFeedbackTab = false
           if @props.feedbackFormUrl? and showFeedbackTab
-            Feedback
+            Feedback
         }
         { # include blog tab if defined
           if @props.blogUrl?
-            Blog
+            Blog
         }
         { # include blog tab if defined
           if @props.discussUrl?
-            Discuss
+            Discuss
         }
         <Login user={@props.user} loginProviders={@props.loginProviders} onLogout={@props.onLogout} />


fix to confirm plugin

fix to confirm plugin

--- app/assets/javascripts/pt/tags/confirm.js.coffee
+++ app/assets/javascripts/pt/tags/confirm.js.coffee
@@ -2,15 +2,20 @@

 confirm = ($el, type = 'confirm') ->
     attr = $el.ptAttr()
-    url  = attr.confirmDelete

     switch type
       when 'delete'
         buttonName = attr.confirmButton or 'Delete'
         ajaxType = 'DELETE'
+        url  = attr.confirmDelete
+      when 'post'
+        buttonName = attr.confirmButton or 'Save'
+        ajaxType = 'POST'
+        url  = attr.confirmPost
       else
         buttonName = attr.confirmButton or 'Yes'
         ajaxType = 'PUT'
+        url = attr.confirm

     if attr.confirmContainer
       $confirmContainer = $el.closest(attr.confirmContainer)
@@ -40,7 +45,8 @@
             $confirmContainer.popover 'hide'

       $confirmContainer.on 'click', '.popover-content .cancel', ->
-        $confirmContainer.popover 'hide'
+        $confirmContainer.find('.popover').remove()
+        # $confirmContainer.popover 'hide'

       $confirmContainer.popover
         content: $html
@@ -54,6 +60,10 @@

 pt.tags.push
+  confirmPost:
+    event: 'click'
+    callback: ->
+      confirm $(this), 'post'
   confirmDelete:
     event: 'click'
     callback: ->


ReferralCustomView: define a custom header inline instead keeping logic in items

--- client/account/lib/views/referral/referralcustomviews.coffee
+++ client/account/lib/views/referral/referralcustomviews.coffee
@@ -65,21 +65,20 @@
     list: ({data, itemClass}) ->

       itemClass ?= require './accountreferralsystemlistitem'
-      controller = new kd.ListViewController {
-        itemClass, scrollView: yes
-      }
+      controller = new kd.ListViewController { itemClass, scrollView: yes }
       controller.replaceAllItems data

-      list = controller.getListView()
+      list   = controller.getListView()
+      header = new kd.ListItemView {cssClass: 'referral-item header'}, {}

-      list.addItemView (new itemClass
-        cssClass     : 'header'
-      ,
-        friend       : "Friend"
-        status       : "Status"
-        lastActivity : "Last Activity"
-        spaceEarned  : "Space Earned"
-      ), 0
+      header.partial = -> "
+
Friend

+
Status

+
Last Activity

+
Space Earned

+      "
+
+      list.addItemView header, 0

       __view = controller.getView()
       return { __view, controller }


A bigger sample is required to see the benefit from google closure

Signed-off-by: Clement Escoffier <6397137e57d1f87002962a37058f2a1c76fca9db@gmail.com>

--- documentation/src/main/assets/javascripts/some-coffee.coffee
+++ documentation/src/main/assets/javascripts/some-coffee.coffee
@@ -1,2 +1,18 @@
-fill = (container, liquid = "coffee") ->
-  "Filling the #{container} with #{liquid}..."+song = ["do", "re", "mi", "fa", "so"]
+
+singers = {Jagger: "Rock", Elvis: "Roll"}
+
+bitlist = [
+  1, 0, 1
+  0, 0, 1
+  1, 1, 0
+]
+
+kids =
+  brother:
+    name: "Max"
+    age:  11
+  sister:
+    name: "Ida"
+    age:  9
+


sthlm6000: class selectors on top

sthlm6000: class selectors on top

--- app/_src/components/work/sthlm6000/donut-chart.coffee
+++ app/_src/components/work/sthlm6000/donut-chart.coffee
@@ -12,6 +12,8 @@

   DURATION_NUMBER_MS = 1000
   READY_CLASS = 'ready'
+  DONUT_SVG_SELECTOR = '#js-sthlm6000-donut-svg'
+  STATS_NUMBERS_SELECTOR = '.js-sthlm6000-stats-number'
   TOTAL_NUMBERS = 0
   DATA_NUM = 'num'
   COLORS = ['#8bffb8', '#36ffa1', '#00e587', '#ffffff']
@@ -63,7 +65,7 @@

     grandTotal = 0

-    for $number in @$context.find('.js-sthlm6000-stats-number')
+    for $number in @$context.find(STATS_NUMBERS_SELECTOR)
       $number = jQuery($number)
       $number.text(0)
       @numbers.push
@@ -79,7 +81,7 @@
     TOTAL_NUMBERS = @numbers.length - 1

     if @svg isnt null
-      jQuery('#js-sthlm6000-donut-svg').empty()
+      jQuery(DONUT_SVG_SELECTOR).empty()
       @svg = null

   run: () =>
@@ -151,7 +153,7 @@
    * and prepare the arcs.
   ###
   initSvg: () =>
-    @svg = d3.select('#js-sthlm6000-donut-svg')
+    @svg = d3.select(DONUT_SVG_SELECTOR)

     # Our doughnut pie chart
     # we associate the path d attribute with our


add toggle fullscreen to analysis tool

add toggle fullscreen to analysis tool

--- app/assets/javascripts/analysis/templates/tabs.coffee
+++ app/assets/javascripts/analysis/templates/tabs.coffee
@@ -15,4 +15,32 @@

+
+
+
TOGGLE FULLSCREEN

 """)


Fixes JS initialization on jQuery v3.1+

Fixes JS initialization on jQuery v3.1+

See https://github.com/comfy/comfortable-mexican-sofa/issues/736

--- app/assets/javascripts/comfy/admin/cms/base.js.coffee
+++ app/assets/javascripts/comfy/admin/cms/base.js.coffee
@@ -2,11 +2,11 @@

 window.CMS.code_mirror_instances = [ ]

-$(document).on 'page:load ready', ->
-  window.CMS.current_path = window.location.pathname
-  CMS.init()
+$ -> window.CMS.init()
+$(document).on 'page:load', -> window.CMS.init()

 window.CMS.init = ->
+  window.CMS.current_path = window.location.pathname
   CMS.set_iframe_layout()
   CMS.slugify()
   CMS.wysiwyg()


reset the preview after posting.

reset the preview after posting.

--- app/assets/javascripts/components/post_view.js.coffee
+++ app/assets/javascripts/components/post_view.js.coffee
@@ -56,6 +56,7 @@
         Turbolinks.visit(suc.url)
       else
         @ui.markdownText.val('')
+        @ui.markdownText.change()
         @ui.statusUpdateCheckbox.prop('checked', false)
         @ui.form.before(suc)
     )


Reorganize gulp/tasks/examples.coffee

Reorganize gulp/tasks/examples.coffee

--- bokehjs/gulp/tasks/examples.coffee
+++ bokehjs/gulp/tasks/examples.coffee
@@ -12,8 +12,10 @@
          .js
          .pipe(gulp.dest("./"))

-gulp.task "examples:anscombe", ["scripts:build", "styles:build"], () -> compile("anscombe")
-gulp.task "examples:burtin",   ["scripts:build", "styles:build"], () -> compile("burtin")
-gulp.task "examples:tap",      ["scripts:build", "styles:build"], () -> compile("tap")
+examples = ["anscombe", "burtin", "tap"]

-gulp.task "examples", (cb) -> run(["examples:anscombe", "examples:burtin", "examples:tap"], cb)
+for example in examples
+  gulp.task "examples:#{example}", () -> compile(example)
+
+gulp.task "examples", ["scripts:build", "styles:build"], (cb) ->
+  run(("examples:#{example}" for example in examples), cb)


Fix parameter name.

Fix parameter name.

--- test/timerNotification_test.coffee
+++ test/timerNotification_test.coffee
@@ -46,7 +46,8 @@
       sinon.stub(Platform.notifications, "addOnClickedListener").returns(undefined)
       mock = sinon.mock(Platform.notifications).expects("create")
       mock.once().withArgs(null, {
-        icon: "/images/icon_notification.png",
+        iconUrl: "/images/icon_notification.png",
+        isClickable: true,
         items: [{ message: "testTicket", title: "Ticket" }, { message: "02:00", title: "Hours" }, { message: "test", title: "Activity" }],
         message: "Pomodoro finished.",
         title: "Tracking finished",
@@ -67,7 +68,8 @@
       sinon.stub(Platform.notifications, "addOnClickedListener").returns(undefined)
       mock = sinon.mock(Platform.notifications).expects("create")
       mock.once().withArgs(null, {
-        icon: "/images/icon_notification.png",
+        iconUrl: "/images/icon_notification.png",
+        isClickable: true,
         items: [{ message: "testTicket", title: "Ticket" }, { message: "02:00", title: "Hours" }, { message: 400, title: "HTTP STATUS" }],
         message: "Pomodoro finished.",
         title: "Sending failed...",