Cleaned up plugin.coffee.

Cleaned up plugin.coffee.

--- plugin.coffee
+++ plugin.coffee
@@ -1,13 +1,9 @@

-less = require 'less'
+fs =  require 'fs'
 path = require 'path'
-async = require 'async'
-fs = require 'fs'
-nap = require 'nap'
 hogan = require 'hogan'
 _ = require 'underscore'
 minify = require('html-minifier').minify
-glob = require 'glob'
 Kelvin = require('./kelvin')

 module.exports = (wintersmith, callback) ->
@@ -15,12 +11,6 @@
   isProd = false
   kelvin = new Kelvin(isProd)

-  preprocessPackage = (type, package) ->
-    for name, files of package
-      for file in files
-        console.log file
-    return
-
   class KelvinTemplate extends wintersmith.TemplatePlugin

     constructor: (@tpl) ->
@@ -48,108 +38,5 @@
         catch error
           callback error

-  class KelvinAssets extends wintersmith.ContentPlugin
-
-    constructor: (@_filename, @_base, @assets) ->
-
-    getFilename: ->
-      @_filename
-
-    render: (locals, contents, templates, callback) ->
-      if !@assets
-        return;
-      if @assets.css
-        preprocessPackage 'css', @assets.css
-      return;
-
-  KelvinAssets.fromFile = (filename, base, callback) ->
-    fs.readFile path.join(base, filename), (error, buffer) ->
-      if error
-        callback error
-      else
-        callback null, new KelvinAssets filename, base, JSON.parse(buffer.toString())
-
-  class KelvinJsonPage extends wintersmith.defaultPlugins.JsonPage
-
-    render: (locals, contents, templates, callback) ->
-      if @template == 'none'
-        # dont render
-        return callback null, null
-
-      async.waterfall [
-        (callback) =>
-          template = templates[@template]
-          if not template?
-            callback new Error "page '#{ @filename }' specifies unknown template '#{ @template }'"
-          else
-            callback null, template
-        (template, callback) =>
-          ctx =
-            page: @
-            contents: contents
-            _: _
-            moment: require 'moment'
-          _.extend ctx, locals, parseAssets contents
-          template.render ctx, callback
-      ], callback
-
-  class KelvinMarkdownPage extends wintersmith.defaultPlugins.MarkdownPage
-
-    render: (locals, contents, templates, callback) ->
-      if @template == 'none'
-        # dont render
-        return callback null, null
-
-      async.waterfall [
-        (callback) =>
-          template = templates[@template]
-          if not template?
-            callback new Error "page '#{ @filename }' specifies unknown template '#{ @template }'"
-          else
-            callback null, template
-        (template, callback) =>
-          ctx =
-            page: @
-            contents: contents
-            _: _
-            moment: require 'moment'
-          _.extend ctx, locals, parseAssets contents
-          template.render ctx, callback
-      ], callback
-
-  parseAssets = (contents) ->
-    config = contents['assets.json']['assets']['assets']
-    unless config
-      return
-    config = expandAssetGlobs config
-    assets = {}
-    unless _.isEmpty(config.js)
-      assets.css = {}
-      for name, pkg of config.css
-        assets.css[name] = parsePackage pkg, 'css'
-    assets
-
-  parsePackage = (pkg, type) ->
-    console.log pkg, type
-    {}
-
-  expandAssetGlobs = (config) ->
-    assets = { js: {}, css: {}, jst: {} }
-    appDir = process.cwd().replace(/\\/g, '\/')
-    appDir += '/contents/'
-    for key, obj of config
-      for pkg, patterns of config[key]
-        matches = []
-        for pattern in patterns
-          fnd = glob.sync path.resolve("#{appDir}#{pattern}").replace(/\\/g, '\/')
-          matches = matches.concat(fnd)
-        matches = _.uniq _.flatten matches
-        matches = (file.replace(appDir, '').replace(/^\//, '') for file in matches)
-        assets[key][pkg] = matches
-    assets
-
-  #wintersmith.registerContentPlugin 'pages', '**/*.json', KelvinJsonPage
-  #wintersmith.registerContentPlugin 'pages', '**/*.*(markdown|mkd|md)', KelvinMarkdownPage
-  #wintersmith.registerContentPlugin 'assets', 'assets.json', KelvinAssets
   wintersmith.registerTemplatePlugin '**/*.*(mustache|hogan)', KelvinTemplate
   callback()


Revert "FIX: Remove deprecated header service from common services"

This reverts commit d9cf15a535d8b61e10acfbe7b9884405ca1c422a.

--- src/app/common/services/services.coffee
+++ src/app/common/services/services.coffee
@@ -1,6 +1,7 @@
 angular.module("doubtfire.common.services", [
   'doubtfire.common.services.alerts'
   'doubtfire.common.services.grades'
+  'doubtfire.common.services.header'
   'doubtfire.common.services.projects'
   'doubtfire.common.services.redirect'
   'doubtfire.common.services.tasks'


fix typos

fix typos

--- templates/dev-node-coffee/gulpfile.coffee
+++ templates/dev-node-coffee/gulpfile.coffee
@@ -52,7 +52,7 @@
     .src [ "#{build}/#{test}/**/*", "!#{build}/#{test}/*.js" ]
     .pipe gulp.dest "#{test}"

-gulp.task 'copy-dist', ->
+gulp.task 'copy-dist', (cb) ->
   runSequence [ 'copy-dist-source', 'copy-dist-test' ], cb

 ### Concatenate ###
@@ -105,7 +105,7 @@
   gulp
     .src [ "#{source}/#{test}/**/*.coffee" ]
     .pipe coffee(bare:true).on 'error', util.log
-    pipe gulp.dest build
+    .pipe gulp.dest build

 gulp.task 'build', (cb) ->
   runSequence [ 'build-source', 'build-test', 'copy-build' ], 'concat-build', cb
@@ -139,4 +139,4 @@

 ### Default ###
 gulp.task 'default', (cb) ->
-  runSequence 'build', 'dist', cb+  runSequence 'build', 'dist', cb


LaraProvider should disable autosave with read-only access key [#136032001]

--- src/code/providers/lara-provider.coffee
+++ src/code/providers/lara-provider.coffee
@@ -78,7 +78,7 @@
   can: (capability, metadata) ->
     hasReadOnlyAccess = metadata?.providerData?.accessKeys?.readOnly? and
                         not metadata?.providerData?.accessKeys?.readWrite?
-    requiresWriteAccess = ['save', 'remove', 'rename'].indexOf(capability) >= 0
+    requiresWriteAccess = ['save', 'resave', 'remove', 'rename'].indexOf(capability) >= 0
     super(capability, metadata) and not (requiresWriteAccess and hasReadOnlyAccess)

   load: (metadata, callback) ->


Remove unused dependency

Remove unused dependency

--- src/text-mate-grammar.coffee
+++ src/text-mate-grammar.coffee
@@ -1,6 +1,5 @@
 _ = require 'underscore-plus'
 fs = require 'fs-plus'
-plist = require 'plist'
 Token = require './token'
 {OnigRegExp, OnigScanner} = require 'oniguruma'
 path = require 'path'


refact (Modules) Simplify deployModule by not forking it in the middle

--- src/steroids/module/deploy.coffee
+++ src/steroids/module/deploy.coffee
@@ -11,22 +11,15 @@

 module.exports = deployModule = (argv) ->
   readDeploymentDescription()
-    .then(
-      (deployedModule) ->
-        nextVersion = getNextModuleVersion deployedModule
-        createModuleVersion(deployedModule.id, nextVersion)
-          .then(pushToModuleVersion deployedModule.id)
-          .then ->
-            deployedModule.id
-      (error) ->
-        createModule().then (module) ->
-          createModuleVersion(module.id, 1)
-            .then(pushToModuleVersion module.id)
-            .then ->
-              module.id
-    )
-    .then(findModule)
-    .then(writeDeploymentDescription)
+    .catch((error) -> createModule())
+    .then (module) ->
+      nextVersion = getNextModuleVersion module
+      createModuleVersion(module.id, nextVersion)
+        .then(pushToModuleVersion module.id)
+        .then(->
+          findModule module.id
+        )
+        .then(writeDeploymentDescription)

 createModule = ->
   http.requestAuthenticated(
@@ -58,8 +51,8 @@
   .then (data) ->
     data.module

-getNextModuleVersion = (deployedModule) ->
-  Number(deployedModule?.versions?[0]?.version) + 1
+getNextModuleVersion = (module) ->
+  Number(module?.versions?[0]?.version || 0) + 1

 pushToModuleVersion = (moduleId) -> (moduleVersion) ->
   packageModuleToDist()


i would like this test to pass on travis

--- test/components/tutor-popover.spec.coffee
+++ test/components/tutor-popover.spec.coffee
@@ -36,6 +36,8 @@
       calledOnce = true
       rect
     overlayDOM
+
+  popperElement.updateOverlayPosition()

 PopoverWrapper = React.createClass
   displayName: 'PopoverWrapper'
@@ -153,8 +155,8 @@
       .then ({dom, element}) ->
         {overlay} = element.refs
         {popper} = overlay.refs
+        Testing.actions.click(dom)
         fakePopoverShouldScroll(popper)
-        Testing.actions.click(dom)
         overlayDOM = popper.getOverlayDOMNode()

         expect(overlayDOM.style.cssText).to.contain('height')


初步完成自定义控件的重构

初步完成自定义控件的重构

--- src/widget/template-widget.coffee
+++ src/widget/template-widget.coffee
@@ -56,13 +56,11 @@
 		if path isnt oldPath?.join(".")
 			if oldPath
 				@model.unwatchPath(oldPath)
-				console.log(" - " + oldPath)

 			@dynaPropertyPathMap[property] = if path then path.split(".") else null

 			if path
 				@model.watchPath(path)
-				console.log(" + " + path)
 		return path

 	processMessage: (bindingPath, path, type, arg) ->


fix paths in tests

fix paths in tests

--- tests/test_loader_wd.coffee
+++ tests/test_loader_wd.coffee
@@ -472,7 +472,7 @@
                         <script type="text/javascript">
                         (function() {
                             var hash_func = <%- inline("./hash/" + hash_name + ".js") %>
-                            var decoder_func = <%- inline("./encoding/" + decoder_name + ".js") %>
+                            var decoder_func = <%- inline("./decode/" + decoder_name + ".js") %>
                             <%- inline("./localforage.js") %>
                             <%- inline("./loader.js") %>

@@ -785,7 +785,7 @@
                         <script type="text/javascript">
                         (function() {
                             var hash_func = <%- inline("./hash/" + hash_name + ".js") %>
-                            var decoder_func = <%- inline("./encoding/" + decoder_name + ".js") %>
+                            var decoder_func = <%- inline("./decode/" + decoder_name + ".js") %>
                             <%- inline("./localforage.js") %>
                             <%- inline("./loader.js") %>


Fix typo, restart instead of stop

Fix typo, restart instead of stop


Router is initialized for you

Router is initialized for you

--- test_app/todos/app/javascripts/app.coffee
+++ test_app/todos/app/javascripts/app.coffee
@@ -27,10 +27,7 @@
 # Your application begins here...

 Todos = Em.Application.create
-  rootElement: $("#application"),
-  initialize: ->
-    router = Todos.Router.create()
-    @_super(router)
+  rootElement: $("#application")

 Todos.ApplicationController = Ember.Controller.extend()
 Todos.ApplicationView = Ember.View.extend


added out of bounds helper and added support for angle property

--- src/scene/objects/entity.coffee
+++ src/scene/objects/entity.coffee
@@ -33,6 +33,10 @@
   # Returns a {Fz2D.Entity}.
   clone: () ->
     new Fz2D.Entity(@texture, @x, @y, @w, @h, @tag)
+
+  # Public: Returns true if the entity is out of bounds.
+  isOutOfBounds: () ->
+    @group and not Fz2D.overlap(@group, @)

   # Public: Plays a given animation.
   #
@@ -82,7 +86,10 @@
              @x,
              @y,
              @w,
-             @h)
+             @h,
+             @bounds.hw,
+             @bounds.hh
+             @angle)
     null

   # Public: Updates entity on every frame.


unset shift-space in console

unset shift-space in console

thanks to @pfitzseb

--- keymaps/ink.cson
+++ keymaps/ink.cson
@@ -14,6 +14,7 @@
 'ink-console':
   'alt-up': 'console:previous-in-history'
   'alt-down': 'console:next-in-history'
+  'shift-space': '!unset'

 'atom-text-editor:not([mini])':
   'escape': 'inline:clear-current'


marked "listen" section as not yet implemented

--- client/connect.coffee
+++ client/connect.coffee
@@ -58,7 +58,7 @@

       </fieldset>
       <fieldset>
-        <legend>Listen for connections from interpreter</legend>
+        <legend>Listen for connections from interpreter (not yet implemented)</legend>

           Port <input disabled id="listen-port" class="text-field" value="#{DEFAULT_PORT}" size="5">
           Listen
@@ -74,6 +74,7 @@
     $spawnStatus = $ '#spawn-status' ; $listenPort  = $ '#listen-port'

     $connect.add($new).add($delete).add($save).add($cancel).add($spawn).add($listen).button()
+    $listen.button 'disable'
     $list
       .on 'click', '.fav', -> false
       .on 'dblclick', '.fav', (e) -> $connect.click(); false


warn -> error

warn -> error

--- client/message.coffee
+++ client/message.coffee
@@ -299,7 +299,7 @@
   message = t.data._id
   group = t.data.group
   if files.length != 1
-    console.warn "Attempt to replace #{message} with #{files.length} files -- expected 1"
+    console.error "Attempt to replace #{message} with #{files.length} files -- expected 1"
   else
     file = files[0]
     file.callback = (file2) ->


Cleanup.

Cleanup.

--- client/persons.coffee
+++ client/persons.coffee
@@ -32,4 +32,4 @@
 # displayName to inviters (emails are not stored in subdocuments, but are subscribed to independently
 # and received as invitedEmail field).
 Template.personInlineItem.getDisplayName = ->
-  a = @refresh().getDisplayName true
+  @refresh().getDisplayName true


Update setup.coffee

Update setup.coffee

--- lib/setup.coffee
+++ lib/setup.coffee
@@ -27,10 +27,6 @@
 ensureSSL = require './middleware/ensure_ssl'
 errorHandler = require "../components/error_handler"
 { notFoundError, loginError } = require('./middleware/errors')
-
-
-if REDIS_URL
-  _.extend require('artsy-backbone-mixins/config'), config

 # Setup sharify constants & require dependencies that use sharify data
 sharify.data =


Spelling name correctly helps

Spelling name correctly helps

--- src/scripts/geolocate-ip.coffee
+++ src/scripts/geolocate-ip.coffee
@@ -1,6 +1,6 @@

 # Description:
-#   Geolocate an IP Address based on http://www.hostip.info/use.html
+#   Geolocate an IP Address
 #
 # Dependencies:
 #   None
@@ -20,4 +20,4 @@
     request_string = robot.http("http://api.hostip.info/get_json.php?ip=#{target_ip}")

     .get() (err, res, body) ->
-      msg.send "#{JSON.parse(body).ip} is from #{JSON.parse(body).city}, #{JSON.parse(body).country_nam}."
+      msg.send "#{JSON.parse(body).ip} is from #{JSON.parse(body).city}, #{JSON.parse(body).country_name}."


Refactors #get() method

Refactors #get() method

--- coffee/turbase.coffee
+++ coffee/turbase.coffee
@@ -3,6 +3,7 @@
 #

 mongodb = require 'mongodb'
+ObjectID = mongodb.ObjectID
 database = require './database'

 db = null
@@ -18,11 +19,19 @@

 exports.get = (req, res) ->
   db.collection req.params.object, (err, collection) ->
-    res.jsonp "{'error':#{err}}" if err
-    res.jsonp "{'error': 'Mangler id'}" if not req.params.id
-    collection.find({_id: mongodb.ObjectID(req.params.id), 'eier':req.eier}).toArray (err, result) ->
-      res.jsonp result if result
-      res.jsonp err if err
+    # @TODO move to propper error handling
+    return res.jsonp "{'error':#{err}}" if err
+    return res.jsonp "{'error': 'Mangler id'}" if not req.params.id
+
+    collection.findOne _id: ObjectID.createFromHexString(req.params.id), (err, doc) ->
+      # @TODO move to propper error handling
+
+      delete doc.privat if req.eier.toLowerCase() isnt doc?.eier?.toLowerCase()
+
+      return res.jsonp err if err
+      return res.jsonp doc if doc
+      # @TODO this should 404 Not Found
+      return res.jsonp {}

 exports.list = (req, res) ->
   # @TODO parse arguments in server.coffee


Attempt to address issues that only seem to appear on Travis

--- src/coffee/JuliaProcess.coffee
+++ src/coffee/JuliaProcess.coffee
@@ -82,9 +82,10 @@
   detachProcess: ->
     @_detach()
   evaluate: (code) ->
-    if code.data.length
+    console.log code
+    if code?.data?.length
       @emit.apply @, _.flatten [code.event, code.data]
     else
-      @emit code.event
+      @emit code?.event ? 'noop'
   send: (code) ->
     @process.stdin.write(code+BLOCK_SEPARATOR)


Corrected spelling in error message.

Corrected spelling in error message.

--- src/lib/ComponentLoader.coffee
+++ src/lib/ComponentLoader.coffee
@@ -123,7 +123,7 @@
         instance = implementation.getComponent metadata
       else
         if typeof implementation isnt 'function'
-          throw new Error "Component #{name} is npt loadable"
+          throw new Error "Component #{name} is not loadable"
         instance = implementation metadata
     instance.baseDir = @baseDir if name is 'Graph'
     @setIcon name, instance


version 2 in progress

version 2 in progress

--- 360.coffee
+++ 360.coffee
@@ -735,48 +735,103 @@
   #
   # The html of the view is static, only updated through css-changes.
   #
-  #{{{3 `View` constructor, - create a viw and bind it to a dom element
+  #{{{3 `View` constructor, - create a view and bind it to a dom element
   #
   # Create the view, - and bind it to a dom element
   #
   View = (model, domId) ->
     @model = model
-
+    domElem = document.getElementById(domId)
+    @defaultWidth = domElem.offsetWidth
+    @defaultHeigh = domElem.offsetHeight
+
+    #{{{4 Style
+    extend domElem.style,
+      display: "inline-block"
+      width: @defaultWidth + "px"
+      height: @defaultHeight + "px"
+    @style =
+      root:
+        cursor: "url(res/cursor_rotate.cur),move"
+
+      # NB: order of the following keys needs to be the exactly same as the children of the dom root node
+      zoomLens:
+        display: "block"
+        position: "absolute"
+        overflow: "hidden"
+        width: @model.zoom.lensSize
+        height: @model.zoom.lensSize
+        border: "0px solid black"
+        cursor: "default"
+        backgroundColor: "rgba(100,100,100,0.8)"
+        borderRadius: (@model.zoom.lensSize/2)
+        #borderBottomRightRadius: (zoomSize/5)
+        boxShadow: "0px 0px 40px 0px rgba(255,255,255,.7) inset, 4px 4px 9px 0px rgba(0,0,0,0.5)"
+        backgroundRepeat: "no-repeat"
+      logo:
+        position: "absolute"
+        opacity: "0.7"
+        textShadow: "0px 0px 5px white"
+        color: "#333"
+        transition: "opacity 1s"
+      btnFull:
+        left: "90%"
+      btnZoom:
+        left: "5%"
+      spinner:
+        position: "absolute"
+        top: "49%"
+        left: "49%"
+
+    buttonStyle =
+      position: "absolute"
+      color: "#333"
+      opacity: "0.7"
+      textShadow: "0px 0px 5px white"
+      backgroundColor: "rgba(255,255,255,0)"
+      top: "80%"
+      fontSize: @defaultHeight * .08
+      padding: @defaultHeight * .02
+    extend @style.btnFull, buttonStyle
+    extend @style.btnZoom, buttonStyle
+
+    #{{{4 Dom element creation
     @elems = {}
     @elems.root = document.createElement "div"
-    @elems.root.innerHTML = """<
-      div class="onetwo360-zoom-lens>
<
-      div class="onetwo360-logo>
<
-      div class="onetwo360-fullscreen-button>
<
-      div class="onetwo360-zoom-button>
<
-      div>
"""
-    elemNames = ["zoomLens", "logo", "btnFull", "btnZoom"]
-    for i in [0..elemNames.length]
-      @elems.root.getChildren
-      @elems[key]
-
+    @elems.root.innerHTML =
+      '

' +
+      '
' +
+      '
' +
+      '
' +
+      ''
+    domElem.addChild @elems.root
+
+    elemNames = Object.keys @style
+    for i in [1..elemNames.length]
+      @elems[elemNames[i]] = @elems.root.getChild(i-1)
+
+    #{{{4 Properties that will be initialised later
+    @width = undefined
+    @height = undefined
+    @logoFade = undefined
+
+    #{{{4 Data structure for optimised style update
     @elemStyle = {}
-    @style = {}
     @styleCache = {}
     for key, _ of @elems
-      @style[key] = {}
       @elemStyle[key] = @elems[key].style
       @styleCache[key] = {}

-    domElem = document.getElementById(domId)
-    domElem.addChild @elems.root
-    @defaultWidth = domElem.offsetWidth
-    @defaultHeigh = domElem.offsetHeight
-
-    @width = undefined
-    @height = undefined
-
+    #{{{4 Connect to parent dom node (`domId`), and get its width/height
+
+    #{{{4 Update view
     @update()

   #{{{3 `View#update()` draw the view based on current content of the model
   View.prototype.update = ->
     @_fullscreen()
     @_root()
+    @_logo()
     @_zoomLens()
     @_applyStyle()

@@ -797,13 +852,29 @@
         width: (@width = @defaultWidth)
         height: (@height = @defaultHeight)

-  View.prototype._root= -> #{{{4
+  View.prototype._root = -> #{{{4
     extend @style.root,
       backgroundImage: "url(#{@model.frames.normal.urls[@model.frames.current]})"
       backgroundSize: "#{@width}px #{@height}px"

+  View.prototype._logo = -> #{{{4
+        top: h*.35 + "px"
+        left: w*.25  + "px"
+        fontSize: h*.2 + "px"
   View.prototype._zoomLens = -> #{{{4
-    undefined
+    if @model.zoom.enabled
+      current = @model.frames.current
+      imgs = @model.frames.zoom # TODO only if current is loaded, else use @model.frames.normal
+      display: "none"
+        display: "block"
+        left: 123
+        top: 123
+        backgroundImage: "url(#{imgs.urls[current]})"
+        backgroundSize: "#{imgs.width}px #{imgs.height}px"
+        backgroundPosition: "#{123}px #{123}px"
+    else
+      extend @style.zoomLens,
+        display: "none"

   View.prototype._applyStyle = -> #{{{4
     for elemId, css of @style
@@ -818,3 +889,4 @@

   #{{{2 main
   window.newOneTwo360 = (cfg) ->
+    undefined


add styling to only those tabs that looks wrong

--- src/smc-webapp/desktop_app.cjsx
+++ src/smc-webapp/desktop_app.cjsx
@@ -137,6 +137,7 @@
             label          = {'Admin'}
             label_class    = {nav_class}
             icon           = {'users'}
+            inner_style    = {padding: '10px', display: 'flex'}
             actions        = {@actions('page')}
             active_top_tab = {@props.active_top_tab}
         />
@@ -151,6 +152,7 @@
             label           = 'Sign in'
             label_class     = {nav_class}
             icon            = 'sign-in'
+            inner_style     = {padding: '10px', display: 'flex'}
             on_click        = {@sign_in_tab_clicked}
             actions         = {@actions('page')}
             active_top_tab  = {@props.active_top_tab}
@@ -165,6 +167,7 @@
             label          = {'Help'}
             label_class    = {nav_class}
             icon           = {'medkit'}
+            inner_style    = {padding: '10px', display: 'flex'}
             actions        = {@actions('page')}
             active_top_tab = {@props.active_top_tab}
             on_click       = {=>redux.getActions('support').show(true)}
@@ -188,6 +191,7 @@
                 label          = {'CoCalc'}
                 label_class    = {nav_class}
                 icon           = {'info-circle'}
+                inner_style    = {padding: '10px', display: 'flex'}
                 actions        = {@actions('page')}
                 active_top_tab = {@props.active_top_tab} />
             <NavItem className='divider-vertical hidden-xs' />


Revert to multi-select version of subejct_viewer

--- src/ubret/subject_viewer.coffee
+++ src/ubret/subject_viewer.coffee
@@ -1,31 +1,50 @@
 BaseTool = window.Ubret.BaseTool or require('./base_tool')

 class SubjectViewer extends BaseTool
-  name: 'Subject Viewer'
-  template:
-    """
-

-      <% for(i = 0; i < keys.length; i++) { %>
-

-          <%- keys[i] %>: <%- subject[keys[i]] %>
-

-      <% } %>
-

-    """
+
+  constructor: (opts) ->
+    super
+    @count = 0
+    @div = d3.select(@selector)
+    @start()

   start: =>
-    # data = @dimensions.id.top(Infinity)
-    unless @selectedElements
-      subject = @data[0]
-      # subject = data[0]
+    subjects = new Array
+    if @selectedElements.length isnt 0
+      subjects = @dimensions.uid.top(Infinity).filter (item) =>
+        item.uid in @selectedElements
     else
-      subject = _.find data, (record) =>
-        record.id == @selectedElements[0]
+      subjects = [@dimensions.uid.top(1)[0]]
+      @selectElements subjects
+    @render(subjects)

-    compiled = _.template @template, { subject: subject, keys: @keys }
-    @el.html compiled
+  render: (subjects) =>
+    @div.selectAll('div.subject').remove()

-
+    subject = @div.selectAll('div')
+      .data(subjects).enter()
+        .append('div')
+        .attr('class', 'subject')
+
+    subject.selectAll('img')
+      .append('img')
+        .attr('src', (d) -> d.image)
+
+    subject.selectAll('ul')
+      .append('ul')
+      .data((d) => @toArray(d)).enter()
+        .append('li')
+        .attr('data-key', (d) -> d[0])
+        .text((d) => "#{@formatKey(d[0])}: #{d[1]}")
+
+    subject.select("[data-key=\"#{@selectedKey}\"]")
+      .attr('class', 'selected')
+
+  toArray: (data) =>
+    arrayedData = new Array
+    arrayedData.push [key, data[key]] for key in @keys
+    arrayedData
+
 if typeof require is 'function' and typeof module is 'object' and typeof exports is 'object'
   module.exports = SubjectViewer
 else


better math export for markdown export.

better math export for markdown export.

--- lib/markdown-convert.coffee
+++ lib/markdown-convert.coffee
@@ -11,33 +11,47 @@
 # it has common functions as pandoc-convert.coffee

 processMath = (text)->
-  lines = text.split('\n')
-  inBlock = false
+  line = text.replace(/\\\$/g, '#slash_dollarsign#')

-  lines = lines.map (line)->
-    if line.match(/^\s*```/)
-      inBlock = !inBlock
-      line
-    else if inBlock
-      line
-    else
-      line = line.replace(/\\\$/g, '#slash_dollarsign#')
+  inline = JSON.parse(atom.config.get('markdown-preview-enhanced.indicatorForMathRenderingInline'))
+  block = JSON.parse(atom.config.get('markdown-preview-enhanced.indicatorForMathRenderingBlock'))

-      # display
-      line = line.replace /\$\$([\s\S]+?)\$\$/g, ($0, $1)->
-        $1 = $1.replace(/\n/g, '').replace(/\#slash\_dollarsign\#/g, '\\\$')
-        $1 = escape($1)
-        "<p align=\"center\"><img src=\"https://latex.codecogs.com/gif.latex?#{$1.trim()}\"/></p>"
+  inlineBegin = '(?:' + inline.map (x)-> x[0]
+                      .join('|')
+                      .replace(/\\/g, '\\\\')
+                      .replace(/([\(\)\[\]\$])/g, '\\$1') + ')'
+  inlineEnd = '(?:' + inline.map (x)-> x[1]
+                      .join('|')
+                      .replace(/\\/g, '\\\\')
+                      .replace(/([\(\)\[\]\$])/g, '\\$1') + ')'
+  blockBegin = '(?:' + block.map (x)-> x[0]
+                      .join('|')
+                      .replace(/\\/g, '\\\\')
+                      .replace(/([\(\)\[\]\$])/g, '\\$1') + ')'
+  blockEnd = '(?:' + block.map (x)-> x[1]
+                      .join('|')
+                      .replace(/\\/g, '\\\\')
+                      .replace(/([\(\)\[\]\$])/g, '\\$1') + ')'

-      # inline
-      r = /\$([\s\S]+?)\$/g
-      line = line.replace /\$([\s\S]+?)\$/g, ($0, $1)->
-        $1 = $1.replace(/\n/g, '').replace(/\#slash\_dollarsign\#/g, '\\\$')
-        $1 = escape($1)
-        "<img src=\"https://latex.codecogs.com/gif.latex?#{$1.trim()}\"/>"
+  # display
+  line = line.replace new RegExp("(```(?:[\\s\\S]+?)```\\s*(?:\\n|$))|(?:#{blockBegin}([\\s\\S]+?)#{blockEnd})", 'g'), ($0, $1, $2)->
+    return $1 if $1
+    math = $2
+    math = math.replace(/\n/g, '').replace(/\#slash\_dollarsign\#/g, '\\\$')
+    math = escape(math)
+    "<p align=\"center\"><img src=\"https://latex.codecogs.com/gif.latex?#{math.trim()}\"/></p>"

-      line = line.replace(/\#slash\_dollarsign\#/g, '\\\$')
-  lines.join('\n')
+  # inline
+  console.log "(```(?:[\\s\\S]+?)```\\s*(?:\\n|$))|(?:#{inlineBegin}([\\s\\S]+?)#{inlineEnd})"
+  line = line.replace new RegExp("(```(?:[\\s\\S]+?)```\\s*(?:\\n|$))|(?:#{inlineBegin}([\\s\\S]+?)#{inlineEnd})", 'g'), ($0, $1, $2)->
+    return $1 if $1
+    math = $2
+    math = math.replace(/\n/g, '').replace(/\#slash\_dollarsign\#/g, '\\\$')
+    math = escape(math)
+    "<img src=\"https://latex.codecogs.com/gif.latex?#{math.trim()}\"/>"
+
+  line = line.replace(/\#slash\_dollarsign\#/g, '\\\$')
+  return line

 # convert relative path to project path
 processPaths = (text, fileDirectoryPath, projectDirectoryPath, useAbsoluteImagePath)->


Removing debug log statement

Removing debug log statement

--- lib/metric_collector.coffee
+++ lib/metric_collector.coffee
@@ -76,8 +76,6 @@
       out:
         replace: collectionName

-    console.log 'Mapreduce command:', command
-
     # Execute the mapreduce command.
     mongoose.connection.db.executeDbCommand command, (error, dbResults) =>


Add configuration of monitors script

Add configuration of monitors script

--- scripts/monitors.coffee
+++ scripts/monitors.coffee
@@ -5,11 +5,11 @@
 #   None
 #
 # Configuration:
-#   None
+#   HUBOT_MONITORS_CHANNEL: channel name for sending alert
 #
 # Commands:
-#   hubot <URL>を監視して
-#   hubot 今の稼働状況を教えて
+#   hubot <URL>を監視
+#   hubot 今の稼働状況は？
 #
 # URLS:
 #   None
@@ -24,6 +24,8 @@
 module.exports = (robot) ->

   robot.brain.data.monitors = {} unless robot.brain.data.monitors?
+
+  room = process.env.HUBOT_MONITORS_CHANNEL

   class Monitor
     @schema = {


Syntax error: character for beginning of comment

--- scripts/watsonqa.coffee
+++ scripts/watsonqa.coffee
@@ -1,8 +1,8 @@
-// Description
-//  watson
-//
-// Commands:
-//  watson - returns answer of question
+# Description
+#  watson
+#
+# Commands:
+#  watson - returns answer of question

 url = require 'url'
 https = require 'https'


Add vieo

Add vieo

--- server/bootstrap.coffee
+++ server/bootstrap.coffee
@@ -59,8 +59,9 @@
 			user: getUser('Por Pão dos Pobres')
 			createdAt: new Date
 			mainMedia:
-				type: 'image'
-				url: process.env.SITE_URL + '/01.jpg'
+				type: 'video'
+				url: 'http://img.youtube.com/vi/ZTA8u8oDYUc/hqdefault.jpg'
+				videoUrl: 'ZTA8u8oDYUc'
 			description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Fuga porro nulla quos, numquam quaerat sunt ducimus a nesciunt dolor ad, aut excepturi repellat eveniet! Praesentium ipsa cupiditate placeat et fugit.'
 			type: 'monthly'
 			targetValue: 6000


Don't assume default variant in JS. Let this be controlled by HTML markup.

Fixes #4849

--- frontend/app/assets/javascripts/spree/frontend/product.js.coffee
+++ frontend/app/assets/javascripts/spree/frontend/product.js.coffee
@@ -37,8 +37,9 @@
   radios = ($ '#product-variants input[type="radio"]')

   if radios.length > 0
-    Spree.showVariantImages ($ '#product-variants input[type="radio"]').eq(0).attr('value')
-    Spree.updateVariantPrice radios.first()
+    selectedRadio = ($ '#product-variants input[type="radio"][checked="checked"]')
+    Spree.showVariantImages selectedRadio.attr('value')
+    Spree.updateVariantPrice selectedRadio

   Spree.addImageHandlers()


chore(test): Refactor test - remove redundant code

--- src/spec/integration/import.spec.coffee
+++ src/spec/integration/import.spec.coffee
@@ -894,17 +894,7 @@

     it 'should update a product level info based only on SKU', (done) ->
       newProductNameUpdated = "#{@newProductName}-updated"
-      categories = []
-      for i in [1...4]
-        categories.push(        {
-          "name": {
-            "en": "Catgeory#{i}"
-          },
-          "slug": {
-            "en": "category-#{i}"
-          },
-          "externalId": "#{i}",
-        })
+      categories = TestHelpers.generateCategories(4)

       csv =
       """


ignore more ipython-notebook commands, at least until we don't allow connections from old clients

--- salvus/hub.coffee
+++ salvus/hub.coffee
@@ -2641,6 +2641,10 @@
                     @push_to_client(resp)

     mesg_project_exec: (mesg) =>
+        if mesg.command == "ipython-notebook"
+            # we just drop these messages, which are from old non-updated clients (since we haven't
+            # written code yet to not allow them to connect -- TODO!).
+            return
         @get_project mesg, 'write', (err, project) =>
             if err
                 return


deployment: add a new handler for cached s3 proxy

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -337,6 +337,34 @@
         resolver 8.8.8.8;
       }

+      location ~^/cdn/(koding-client|koding-assets|kodingdev-client|kodingdev-assets)/(.*) {
+        proxy_cache asset_cache;
+        proxy_cache_revalidate on;
+        proxy_cache_lock on;
+        proxy_cache_valid  200 302  60m;
+        proxy_cache_valid  404	  1m;
+        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
+
+        proxy_pass            http://s3.amazonaws.com/$1/$2;
+        proxy_set_header      X-Real-IP       $remote_addr;
+        proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_connect_timeout 1;
+
+        # unset headers
+        proxy_hide_header x-amz-id-2;
+        proxy_hide_header x-amz-request-id;
+        proxy_hide_header x-amz-expiration;
+        proxy_hide_header x-amz-meta-s3cmd-attrs;
+
+        # ignore possible future headers that might break caching
+        proxy_ignore_headers X-Accel-Expires Expires Cache-Control;
+
+        # finally add status
+        add_header X-K-Proxy-Cache $upstream_cache_status;
+
+        resolver 8.8.8.8;
+      }
+
       # no need to send static file serving requests to webserver
       # serve static content from nginx
       location /a/ {


nginx: add gatekeeper location

nginx: add gatekeeper location

--- deployment/nginx.coffee
+++ deployment/nginx.coffee
@@ -286,6 +286,14 @@
         #{if environment is "sandbox" then basicAuth else ""}
       }

+      location ~ /api/gatekeeper/(.*) {
+        proxy_pass            http://gatekeeper/$1;
+        proxy_set_header      X-Real-IP       $remote_addr;
+        proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_next_upstream   error timeout   invalid_header http_500;
+        proxy_connect_timeout 1;
+      }
+
       #{createLocations(KONFIG)}

       #{createUserMachineLocation("userproxy")}


Client: Activity: Feed: Added commentItemMenu to commentListItem

--- client/activity/lib/components/comments/commentlistitem.coffee
+++ client/activity/lib/components/comments/commentlistitem.coffee
@@ -78,12 +78,20 @@
       'edited'          : comment.get('createdAt') isnt comment.get('updatedAt')

+  renderCommentItemMenu: ->
+
+    <MessageItemMenu
+      message={@props.comment}
+      disableAdminMenuItems:{yes} />
+
+
   render: ->

     { comment } = @props
     return null  unless comment

+      {@renderCommentItemMenu()}

         <ProfileLinkContainer origin={comment.get('account').toJS()}>
           <Avatar className='FeedItem-Avatar' width={35} height={35} />


Notify in the browser console if there is an error in the conf.json file

--- app-loader/app-loader.coffee
+++ app-loader/app-loader.coffee
@@ -55,6 +55,9 @@
 promise.done (data) ->
     window.taigaConfig = _.assign({}, window.taigaConfig, data)

+promise.fail () ->
+    console.error "Your conf.json file is not a valid json file, please review it."
+
 promise.always ->
     if window.taigaConfig.contribPlugins.length > 0
         loadPlugins(window.taigaConfig.contribPlugins).then () ->


TBCampaign: Added Mixpanel tracking.

TBCampaign: Added Mixpanel tracking.

--- client/Main/CommonViews/tbcampaigncontroller.coffee
+++ client/Main/CommonViews/tbcampaigncontroller.coffee
@@ -18,8 +18,10 @@
         referrer = userAccount.referrerUsername
         if referrer
           @showModal { userType: "referral", referrer }
+          KD.mixpanel "TBCampaign referral modal shown", { referrer, referral: KD.nick() }
         else
           @showModal { userType: "direct" }
+          KD.mixpanel "TBCampaign direct user registered"
       else
         @checkExistingUserStatus()

@@ -31,8 +33,10 @@
         KD.getSingleton("vmController").resizeDisk targetVMName, (err, res) =>
           return warn err  if err
           @showModal { userType: "under4GB" }
+          KD.mixpanel "TBCampaign old user disk upgraded to 4GB"
       else
         @showModal { userType: "above4GB" }
+        KD.mixpanel "TBCampaign user has more than 4GB storage"

   showModal: (config) ->
     new TBCampaignModal config


chat: add video participant handlers to chat message pane

--- client/ide/lib/views/chat/idechatmessagepane.coffee
+++ client/ide/lib/views/chat/idechatmessagepane.coffee
@@ -55,6 +55,32 @@
   handleVideoEnded: -> @videoActive = no

+  setActiveParticipantAvatar: (account) ->
+
+    for own id, avatar of @participantMap
+      if id is account._id
+      then avatar.setClass 'is-activeParticipant'
+      else avatar.unsetClass 'is-activeParticipant'
+
+
+  setSelectedParticipantAvatar: (account) ->
+
+    for own id, avatar of @participantMap
+      # if account is null all avatars will receive `unsetClass` calls.
+      # `null` account means: "SELECT NO ONE!"
+      if id is account?._id
+      then avatar.setClass 'is-selectedParticipant'
+      else avatar.unsetClass 'is-selectedParticipant'
+
+
+  setAvatarTalkingState: (nickname, state) ->
+
+    for _, avatar of @participantMap when avatar.data.profile.nickname is nickname
+      if state
+      then avatar.setClass 'is-talkingParticipant'
+      else avatar.unsetClass 'is-talkingParticipant'
+
+
   glance: ->

     return  unless @visible


Removed support for svg in positioned region

removed use of getBBox. With the removal of al svg use in our app
(fact_wheel using Raphael) this was no longer needed.

--- core/app/backbone/factlink/positioned_region.coffee
+++ core/app/backbone/factlink/positioned_region.coffee
@@ -72,42 +72,16 @@
         throw "Invalid options.side: #{@options.side}"

   _bindElPosition: ->
-    elOffset = @_offsetOf @$bindEl
-    containerOffset = @_offsetOf @$container
+    elOffset = @$bindEl.offset()
+    containerOffset = @$container.offset()

     left: elOffset.left - containerOffset.left
     top:  elOffset.top  - containerOffset.top

-  # WARNING: This function is a bit of a dirty hack:
-  # Normally, we can just use plain DOM methods, but for SVG...
-  # For SVG, we need to compute the bounding box; but this isn't trivial.
-  # there's a method getBoundingClientRect, but there are cross-browser issues
-  # so instead we use getBBox, which works, but results in the untransformed
-  # svg-space coordinates.  We then simply assume there's no transformation
-  # (this assumption is false for scaled factwheels) and add the BBox info
-  # to the offsetWidth etc dom info of the nearest non-svg ancestor.  This
-  #  assumes that ancestor's block exactly wraps the SVG, which may not be
-  #  the case in general, but is for our raphael-based svgs.

-  # The various not entirely valid assumptions mean that our layout
-  # is a little fuzzy, which isn't really an issue for tooltips, fortunately.
-  # to avoid bad cases of overlap, we add a little extra margin.
   _dimensionsOf: ($el) ->
-    if bbox = $el[0].getBBox?()
-      width: bbox.width + 6
-      height: bbox.height + 6
-    else
-      width: $el.outerWidth()
-      height: $el.outerHeight()
-
-  _offsetOf: ($el) ->
-    if bbox = $el[0].getBBox?()
-      containerOffset = $el.parents().filter(-> !@getBBox?).first().offset()
-
-      top: bbox.y + containerOffset.top - 3
-      left: bbox.x + containerOffset.left - 3
-    else
-      $el.offset()
+    width: $el.outerWidth()
+    height: $el.outerHeight()

   _checkContainer: ->
     unless @$container.css('position') in ['relative', 'absolute', 'fixed']


Implemented: Insert Line now correclty respects indenting

--- web/scripts/playerCodeEditor.coffee
+++ web/scripts/playerCodeEditor.coffee
@@ -310,7 +310,7 @@

         nextLineIndent = @editSession.getMode().getNextLineIndent(
             @editSession.getState(currentRow),
-            text.getLine currentRow,
+            text.getLine(currentRow),
             @editSession.getTabString())
         line = nextLineIndent + line


minor text change

minor text change

--- scripts/TM-Github.coffee
+++ scripts/TM-Github.coffee
@@ -55,7 +55,7 @@
       res.reply 'Request sent, take a look at https://ci.appveyor.com/project/DinisCruz/tm-ci-deploy where a new build will have just started'
       res.reply 'In a bit, the deployed website will be at https://tm-qa-6.azurewebsites.net'

-  robot.hear '[TM_4_0_Design:Dev](.*)commits', (res)->
+  robot.hear 'TM_4_0_Design.Dev(.*)commits', (res)->
     res.reply 'New commit to TM_4_0_Design/Dev detected ...'
     res.reply '...time to deploy to azure'


Added handling of single-feature results

--- scripts/reportTab.coffee
+++ scripts/reportTab.coffee
@@ -18,7 +18,10 @@
       unless data
         throw "Could not find data for sketchClass #{@sketchClassId}"
     else
-      data = @data.value[0]
+      if _.isArray @data.value
+        data = @data.value[0]
+      else
+        data = @data.value
     _.map data.features, (feature) ->
       feature.attributes


[fix] Finish renaming create to schedule in poller

--- server/lib/poller.coffee
+++ server/lib/poller.coffee
@@ -186,7 +186,7 @@
                 konnector.updateAttributes data, (err, body) =>
                     log.error err if err?

-                    @create konnector, @findNextUpdate(konnector)
+                    @schedule konnector, @findNextUpdate(konnector)
                     callback() if callback?

         else


Ensure authentication

Ensure authentication

--- apps/onboarding/index.coffee
+++ apps/onboarding/index.coffee
@@ -1,12 +1,12 @@
 express = require 'express'
 routes = require './routes'
+ensureLoggedIn = require '../../lib/middleware/ensure_logged_in'

 app = module.exports = express()

 app.set 'views', __dirname + '/templates'
 app.set 'view engine', 'jade'

-# TODO: Check if logged in
-app.get '/welcome', routes.index
-app.get '/welcome/:scene', routes.index
-app.get '/welcome/find-friends/callback', routes.findFriendsCallback
+app.get '/welcome', ensureLoggedIn, routes.index
+app.get '/welcome/:scene', ensureLoggedIn, routes.index
+app.get '/welcome/find-friends/callback', ensureLoggedIn, routes.findFriendsCallback


fix bug

fix bug

--- views/components/ship/parts/panebody.cjsx
+++ views/components/ship/parts/panebody.cjsx
@@ -300,7 +300,7 @@
                     </Col>
                   </Grid>

-                Cond. {@state.cond[j]}
+                Cond. {@state.cond[j]}

             ]
         }


Update en.coffee

Update en.coffee

(trivial) translating a fr word lost in the en file. Also removing unwanted spaces before exclamation marks: they are not needed in English while they are mandatory in French.

--- client/app/locales/en.coffee
+++ client/app/locales/en.coffee
@@ -32,7 +32,7 @@
     "Switch to Calendar"                : "Switch to Calendar"
     "time"                              : "time"
     "Today"                             : "Today"
-    'today'                             : 'aujourd\'hui'
+    'today'                             : 'today'
     "What should I remind you ?"        : "What should I remind you?"
     "ICalendar import"                  : "ICalendar import"
     "select an icalendar file"          : "Select an icalendar file"
@@ -55,8 +55,8 @@
     "display previous events"           : "Display previous events"
     "display next events"               : "Display next events"
     "event"                             : "Event"
-    "are you sure"                      : "Are you sure ?"
-    "confirm delete calendar"           : "You are about to delete all the events related to %{calendarName}. Are you sure ?"
+    "are you sure"                      : "Are you sure?"
+    "confirm delete calendar"           : "You are about to delete all the events related to %{calendarName}. Are you sure?"
     "advanced"                          : "More details"
     "enter email"                       : "Enter email"
     "ON"                                : "on"


Don’t assign atom.themes.workspace in spec-helper

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -71,8 +71,6 @@
   documentTitle = null

   atom.project.setPaths([path.join(@specDirectory, 'fixtures')])
-
-  atom.themes.workspace = atom.workspace

   atom.workspaceParentSelectorctor = '#jasmine-content'

@@ -132,8 +130,6 @@
   # unless jasmine.getEnv().currentSpec.results().passed()
   #   jasmine.getEnv().specFilter = -> false
   #
-  atom.themes.workspace = null
-
   atom.reset()

   document.getElementById('jasmine-content').innerHTML = '' unless window.debugContent


Prepare collaboration for guest user

Prepare collaboration for guest user

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -856,7 +856,11 @@
       @removeMachineNode()

     @rtm.once 'RealtimeManagerDidDispose', =>
-      kd.utils.defer @bound 'quit'
+      method = switch
+        when @mountedMachine.isPermanent() then 'prepareCollaboration'
+        else 'quit'
+
+      kd.utils.defer @bound method

     @cleanupCollaboration()


Note for later

Note for later

--- workers/social/lib/social/models/messages/privatemessage.coffee
+++ workers/social/lib/social/models/messages/privatemessage.coffee
@@ -49,6 +49,11 @@
           if err
             fin err
           else
+            # @emit 'NewPrivateMessageHasArrived',
+            #   actorType   : 'sender'
+            #   actionType  : 'newMessage'
+            #   sender      : ObjectRef(sender).data
+            #   subject     : ObjectRef(pm).data
             recipient.sendNotification 'NewPrivateMessageHasArrived',
               actorType   : 'sender'
               actionType  : 'newMessage'


hopefully fix a couple of problems with inbox

--- workers/social/lib/social/models/messages/privatemessage.coffee
+++ workers/social/lib/social/models/messages/privatemessage.coffee
@@ -21,6 +21,7 @@
     relationships : JPost.relationships

   reply: secure (client, comment, callback)->
+    JComment = require './comment'
     JPost::reply.call @, client, JComment, comment, callback

   disown: secure ({connection}, callback)->
@@ -86,6 +87,6 @@
                       callback err
                     else
                       callback null, pm
-                      unless delegate.profile.nickname in to
-                        jraphical.Channel.fetchPrivateChannelById delegate.getId(), (err, channel)->
-                          channel.publish delegate, pm
+#                      unless delegate.profile.nickname in to
+#                        jraphical.Channel.fetchPrivateChannelById delegate.getId(), (err, channel)->
+#                          channel.publish delegate, pm


FMC-495 FMC susceptible to cross site scripting issues

--- fmc/fmc-webui/src/main/webapp/app/frameworks.coffee
+++ fmc/fmc-webui/src/main/webapp/app/frameworks.coffee
@@ -282,6 +282,22 @@
         rc
       )

+    escapeHtml: (str) ->
+      div = document.createElement 'div'
+      div.appendChild(document.createTextNode(str))
+      div.innerHtml
+
+    unescapeHtml: (escapedStr) ->
+      div = document.createElement 'div'
+      div.innerHtml = escapedStr
+      child = div.childNodes[0]
+      if !child
+        ''
+      else
+        child.nodeValue
+
+
+
   class ModelBackedTemplate extends FON.TemplateController
     template_data: -> @model.toJSON()


Add temporary project paths to File-Icons project

These might be sitting here for a while... hopefully I'm wrong.

--- .config/projects.cson
+++ .config/projects.cson
@@ -121,6 +121,8 @@
 	title: "file-icons"
 	paths: [
 		"/Users/johngardner/.atom/packages/file-icons"
+		"/Users/johngardner/Desktop/PathsTest/Root1/Root2"
+		"/Users/johngardner/Desktop/PathsTest"
 	]
 	devMode: true
 "language-viml":


add another timestamp regex

add another timestamp regex

--- SingularityUI/app/reducers/taskGroups.coffee
+++ SingularityUI/app/reducers/taskGroups.coffee
@@ -36,10 +36,14 @@
   _.filter lines, ({data}) -> new RegExp(search).test(data)

 parseLineTimestamp = (line) ->
-  match = line.match(/^\d{2}:\d{2}:\d{2}.\d{3}/)
+  match = line.match(/^\d{2}:\d{2}:\d{2}\.\d{3}/)
   if match
     return moment(match, 'HH:mm:ss.SSS').valueOf()
   else
+    match = line.match(/^[A-Z ]+\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\]/)
+    if match
+      return moment(match[1], 'YYYY-MM-DD HH:mm:ss,SSS').valueOf()
+
     return null

 buildEmptyBuffer = (taskId, offset) -> { offset, taskId, data: '' }


.

Former-commit-id: 26b9f84864bcadfb5c03d8d204af55456143d10b [formerly bf37159084dfdb648406936b489ce602145958c9] [formerly 59f6cf8d15847f97697b4220421a1288dee028a8 [formerly a91d00bce91afc07d85532ecaa37661408501c76]] [formerly 114b1369656cdb76fbf97489a26978d29c1b1cce [formerly 54336278bc78e464eeb5dab17ee0524000f2c32d] [formerly c289e0dbe6275bff43636ecd2c01b074196c1c5e [formerly c289e0dbe6275bff43636ecd2c01b074196c1c5e [formerly 4e837ec6a557ca1987818d1597888a1b923f38d5]]]]
Former-commit-id: a53e72d40a9afbca41015f8c51d893e8b9e71a37 [formerly 76a60d4c293a933d1628fd58c73c6bb77e9ba552] [formerly c653084a473180cd9f5ae3013e22d844c0a108a4 [formerly 4a7844a2a8fc49ec0bbec903782566a438d9a83b]]
Former-commit-id: 8398e378d91dbec98b20872b99fe67af41816541 [formerly ffd5f3f77fee4d01fadf14173fc6b214be7a82e4]
Former-commit-id: de819fa70b7b5852738bbc78beafaed87eb01212
Former-commit-id: bdb86f1b90b226da23c82937172c932b791b31be

--- @client/avatar.coffee
+++ @client/avatar.coffee
@@ -129,7 +129,7 @@

     # IE9 gets confused if there is an image without a src
     # Chrome puts a weird gray border around IMGs without a src
-    tag = if img_size == 'thumb' || add_initials then SPAN else IMG
+    tag = if !@props.src? then SPAN else IMG

     @transferPropsTo tag attrs,


Adding more snippets

Adding more snippets

--- snippets/set.cson
+++ snippets/set.cson
@@ -10,4 +10,7 @@
           '''
   'set3':
     'prefix': 'set the default wait for elements to be available to "time" seconds'
-    'body': 'set the default wait for elements to be available to "$1" seconds'+    'body': 'set the default wait for elements to be available to "$1" seconds'
+  'set4':
+    'prefix': 'set the default keystroke delay to "delay" milliseconds'
+    'body': 'set the default keystroke delay to "$1" milliseconds'


when setting a username, return whether this was needed, or whether the name stayed the same

--- core/app/backbone/collections/channels.coffee
+++ core/app/backbone/collections/channels.coffee
@@ -33,6 +33,9 @@
     if not @_username? or @_username != name
       @reset([])
       @_username = name
+      return true
+    else
+      return false

   shouldReload: ->
     not (typeof localStorage is "object" and localStorage isnt null and localStorage["reload"] is "false")


fix issue with run_for_tab() not running

--- src/smc-webapp/editor_git.cjsx
+++ src/smc-webapp/editor_git.cjsx
@@ -242,7 +242,10 @@

     set_tab : (tab) =>
         @setState(tab:tab)
-        @run_for_tab()
+        t = @
+        run = (t) ->
+            t.run_for_tab()
+        setTimeout(run(t), 1000)

     add_or_removed_checked_files : (name, listing_type) =>
         store = @redux.getStore(@name)


remove a console.log

remove a console.log

--- src/smc-webapp/editor_git.cjsx
+++ src/smc-webapp/editor_git.cjsx
@@ -557,7 +557,6 @@
             @props.actions[action]()

     render : ->
-        console.log(@props.show_create_branch_modal)

Git Repository at {@props.git_repo_root}


Add some description text

Add some description text

--- web/src/views/log-calculator.cjsx
+++ web/src/views/log-calculator.cjsx
@@ -92,9 +92,19 @@

Log Generator

Experimental!

-
This feature is still highly experimental!

-
The uploaded file is only processed inside your browser. Data sent to the server includes the approximate area where you may have searched for a Geocache.

+
This feature is still highly experimental! Use with care and don't trust the data.

+

+                You can use the log generator to analyse GPX tracks from your
+                GPS device to identify possible Geocache finds. It will search
+                for Geocaches near locations where you spent some time and list
+                them below.
+

+

+                The uploaded file is only processed inside your browser. Data
+                sent to the server includes the approximate area where you may
+                have searched for a Geocache.
+

             <FileDrop {...@props}/>
             <ResultList geocaches={@props.geocaches}/>


clean up

clean up

--- app/assets/javascripts/components/button/index.cjsx
+++ app/assets/javascripts/components/button/index.cjsx
@@ -1,7 +1,4 @@
 # @cjsx React.DOM
-
-# creating a button component that could set defaults depending on props.
-# currently modeled on action button.

 React = require 'react'


Only show markings on marked frame #1901

--- app/classifier/tasks/drawing/markings-renderer.cjsx
+++ app/classifier/tasks/drawing/markings-renderer.cjsx
@@ -41,7 +41,7 @@
         taskDescription = @props.workflow.tasks[annotation.task]
         if taskDescription.type is 'drawing'
           <g key={annotation._key} className="marks-for-annotation" data-disabled={isPriorAnnotation || null}>
-            {for mark in annotation.value
+            {for mark in annotation.value when mark.frame is @props.frame
               mark._key ?= Math.random()
               toolDescription = taskDescription.tools[mark.tool]


test(directive): add test for static string

--- spec/tests.coffee
+++ spec/tests.coffee
@@ -7,6 +7,12 @@
   it 'should set the src attribute with Gravatar URL', inject ($rootScope, $compile) ->
     $rootScope.email = "test@example.com"
     element = angular.element ''
+    element = $compile(element) $rootScope
+    $rootScope.$apply()
+    expect(element.attr('src')).toBeTruthy()
+
+  it 'should set the src attribute from static src', inject ($rootScope, $compile) ->
+    element = angular.element ''
     element = $compile(element) $rootScope
     $rootScope.$apply()
     expect(element.attr('src')).toBeTruthy()


Remove console.log of temp atom home directory

--- spec/apm-cli-spec.coffee
+++ spec/apm-cli-spec.coffee
@@ -70,6 +70,5 @@
         callback.callCount > 0

       runs ->
-        console.log atomHome
         expect(fs.existsSync(path.join(atomHome, 'packages', 'node_modules', 'test-module', 'index.js'))).toBeTruthy()
         expect(fs.existsSync(path.join(atomHome, 'packages', 'node_modules', 'test-module', 'package.json'))).toBeTruthy()


NǉÁ

NǉÁ

--- AppStoreClient.coffee
+++ AppStoreClient.coffee
@@ -36,6 +36,10 @@
         for j in [0...element.getElementsByTagName("link").length]
           type = element.getElementsByTagName("link").item(j).getAttribute("type")
           obj.previewUrl = element.getElementsByTagName("link").item(j).getAttribute("href") if type is "audio/x-m4a"
+
+          #リンク
+          obj.trackViewUrl = element.getElementsByTagName("link").item(j).getAttribute("href") if type is "text/html"
+
         arr.push obj
       arr
     catch error


fix homepage test

fix homepage test

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -18,8 +18,11 @@
       wd40.fill '#password', 'testing', -> wd40.click '#login', done # This test relies on a Cheese dataset created in api.coffee!!
   context 'when I click on an Apricot dataset', ->
     before (done) ->
-      browser.elementByPartialLinkText 'Apricot', (err, link) ->
-        link.click done
+      # wait for tiles to fade in
+      setTimeout ->
+        browser.elementByPartialLinkText 'Apricot', (err, link) ->
+          link.click done
+      , 500

     it 'takes me to the Apricot dataset page', (done) ->
       wd40.trueURL (err, result) ->


change copy test to match usage in web, check http status

--- services/filestore/test/acceptence/coffee/SendingFileTest.coffee
+++ services/filestore/test/acceptence/coffee/SendingFileTest.coffee
@@ -70,13 +70,15 @@
 			newProjectID = "acceptence_tests_copyied_project"
 			newFileId = Math.random()
 			newFileUrl = "#{@filestoreUrl}/project/#{newProjectID}/file/#{newFileId}"
-			opts =
+			opts =
+				method: 'put'
 				uri: newFileUrl
 				json:
 					source:
 						project_id:"acceptence_tests"
 						file_id: @file_id
-			request.put opts, (err)=>
+			request opts, (err, response, body)=>
+				response.statusCode.should.equal 200
 				request.del @fileUrl, (err, response, body)=>
 					request.get newFileUrl, (err, response, body)=>
 						body.should.equal @constantFileContent


use indexOf not matches for searching for project by name

matches treats string like regex so chars like ( break it
just doing a simple indexOf == -1 is fine for a string match

will also fix it for projects with . in name which do come up

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -99,7 +99,7 @@
 				visible = true
 				# Only show if it matches any search text
 				if $scope.searchText.value? and $scope.searchText.value != ""
-					if !project.name.toLowerCase().match($scope.searchText.value.toLowerCase())
+					if project.name.toLowerCase().indexOf($scope.searchText.value.toLowerCase()) == -1
 						visible = false
 				# Only show if it matches the selected tag
 				if $scope.filter == "tag" and selectedTag? and project.id not in selectedTag.project_ids


Save and read filtering options from local storage.

--- services/web/public/coffee/main/project-list/project-list.coffee
+++ services/web/public/coffee/main/project-list/project-list.coffee
@@ -2,7 +2,7 @@
 	"base"
 ], (App) ->

-	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout) ->
+	App.controller "ProjectPageController", ($scope, $modal, $q, $window, queuedHttp, event_tracking, $timeout, localStorage) ->
 		$scope.projects = window.data.projects
 		$scope.tags = window.data.tags
 		$scope.notifications = window.data.notifications
@@ -17,6 +17,8 @@
 		$timeout () ->
 			recalculateProjectListHeight()
 		, 10
+
+		storedUIOpts = JSON.parse(localStorage("project_list"))

 		recalculateProjectListHeight = () ->
 			topOffset = $(".project-list-card")?.offset()?.top
@@ -52,6 +54,13 @@
 					project.tags ||= []
 					project.tags.push tag

+		markTagAsSelected = (id) ->
+			for tag in $scope.tags
+				if tag._id == id
+					tag.selected = true
+				else
+					tag.selected = false
+
 		$scope.changePredicate = (newPredicate)->
 			if $scope.predicate == newPredicate
 				$scope.reverse = !$scope.reverse
@@ -126,6 +135,11 @@
 				else
 					# We don't want hidden selections
 					project.selected = false
+
+			localStorage("project_list", JSON.stringify({
+				filter: $scope.filter,
+				selectedTagId: selectedTag?._id
+			}))
 			$scope.updateSelectedProjects()

 		$scope.getSelectedTag = () ->
@@ -424,8 +438,6 @@
 				path = "/project/#{selected_project_ids[0]}/download/zip"

 			window.location = path
-
-		$scope.updateVisibleProjects()

 	App.controller "ProjectListItemController", ($scope) ->
 		$scope.ownerName = () ->


prepare tests for versioning

prepare tests for versioning

--- services/web/test/acceptance/coffee/ProjectStructureTests.coffee
+++ services/web/test/acceptance/coffee/ProjectStructureTests.coffee
@@ -35,7 +35,7 @@
 				done()

 		it "should version creating a doc", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+			{docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 			expect(updates.length).to.equal(2)
 			_.each updates, (update) =>
 				expect(update.userId).to.equal(@owner._id)
@@ -44,7 +44,7 @@
 			expect(_.where(updates, pathname: "/references.bib").length).to.equal 1

 		it "should version creating a file", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+			{fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 			expect(updates.length).to.equal(1)
 			update = updates[0]
 			expect(update.userId).to.equal(@owner._id)
@@ -67,7 +67,7 @@
 				done()

 		it "should version the docs created", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id).docUpdates
+			{docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id)
 			expect(updates.length).to.equal(2)
 			_.each updates, (update) =>
 				expect(update.userId).to.equal(@owner._id)
@@ -76,7 +76,7 @@
 			expect(_.where(updates, pathname: "/references.bib").length).to.equal(1)

 		it "should version the files created", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id).fileUpdates
+			{fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id)
 			expect(updates.length).to.equal(1)
 			update = updates[0]
 			expect(update.userId).to.equal(@owner._id)
@@ -100,13 +100,13 @@
 					if res.statusCode < 200 || res.statusCode >= 300
 						throw new Error("failed to add doc #{res.statusCode}")
 					example_doc_id = body._id
-					ProjectGetter.getProject @example_project_id, (error, newProject) =>
+					ProjectGetter.getProject example_project_id, (error, newProject) =>
 						throw error if error?
 						@project_1 = newProject
 						done()

 		it "should version the doc added", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+			{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 			expect(updates.length).to.equal(1)
 			update = updates[0]
 			expect(update.userId).to.equal(@owner._id)
@@ -134,7 +134,7 @@
 				done()

 		it "should version the dosc created", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id).docUpdates
+			{docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id)
 			expect(updates.length).to.equal(1)
 			update = updates[0]
 			expect(update.userId).to.equal(@owner._id)
@@ -142,7 +142,7 @@
 			expect(update.docLines).to.equal("Test")

 		it "should version the files created", ->
-			updates = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id).fileUpdates
+			{fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id)
 			expect(updates.length).to.equal(1)
 			update = updates[0]
 			expect(update.userId).to.equal(@owner._id)
@@ -178,7 +178,7 @@

 				example_file_id = JSON.parse(body).entity_id

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
@@ -186,7 +186,7 @@
 				expect(update.url).to.be.a('string');
 				@original_file_url = update.url

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# uploading a new file does change the project structure
@@ -211,14 +211,14 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to upload file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/1pixel.png")
 				expect(update.url).to.be.a('string');

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -238,7 +238,7 @@

 		beforeEach (done) ->
 			MockDocUpdaterApi.clearProjectStructureUpdates()
-			ProjectGetter.getProject @example_project_id, (error, project) =>
+			ProjectGetter.getProject example_project_id, (error, project) =>
 				throw error if error?
 				@root_folder_id = project.rootFolder[0]._id.toString()
 				@project_0 = project
@@ -254,14 +254,14 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to move doc #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/new.tex")
 				expect(update.newPathname).to.equal("/foo/new.tex")

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -278,13 +278,13 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to move file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/1pixel.png")
 				expect(update.newPathname).to.equal("/foo/1pixel.png")
-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -309,20 +309,20 @@
 					if res.statusCode < 200 || res.statusCode >= 300
 						throw new Error("failed to move folder #{res.statusCode}")

-					updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+					{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 					expect(updates.length).to.equal(1)
 					update = updates[0]
 					expect(update.userId).to.equal(@owner._id)
 					expect(update.pathname).to.equal("/foo/new.tex")
 					expect(update.newPathname).to.equal("/bar/foo/new.tex")

-					updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+					{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 					expect(updates.length).to.equal(1)
 					update = updates[0]
 					expect(update.userId).to.equal(@owner._id)
 					expect(update.pathname).to.equal("/foo/1pixel.png")
 					expect(update.newPathname).to.equal("/bar/foo/1pixel.png")
-					ProjectGetter.getProject @example_project_id, (error, newProject) =>
+					ProjectGetter.getProject example_project_id, (error, newProject) =>
 						throw error if error?
 						@project_1 = newProject
 						# replacing a file should update the project structure
@@ -332,7 +332,7 @@
 	describe "renaming entities", ->
 		beforeEach (done) ->
 			MockDocUpdaterApi.clearProjectStructureUpdates()
-			ProjectGetter.getProject @example_project_id, (error, project) =>
+			ProjectGetter.getProject example_project_id, (error, project) =>
 				throw error if error?
 				@root_folder_id = project.rootFolder[0]._id.toString()
 				@project_0 = project
@@ -348,14 +348,14 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to move doc #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo/new.tex")
 				expect(update.newPathname).to.equal("/bar/foo/new_renamed.tex")

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -372,14 +372,14 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to move file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo/1pixel.png")
 				expect(update.newPathname).to.equal("/bar/foo/1pixel_renamed.png")

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -396,21 +396,21 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to move folder #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo/new_renamed.tex")
 				expect(update.newPathname).to.equal("/bar/foo_renamed/new_renamed.tex")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo/1pixel_renamed.png")
 				expect(update.newPathname).to.equal("/bar/foo_renamed/1pixel_renamed.png")

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -421,7 +421,7 @@
 	describe "deleting entities", ->
 		beforeEach (done) ->
 			MockDocUpdaterApi.clearProjectStructureUpdates()
-			ProjectGetter.getProject @example_project_id, (error, project) =>
+			ProjectGetter.getProject example_project_id, (error, project) =>
 				throw error if error?
 				@root_folder_id = project.rootFolder[0]._id.toString()
 				@project_0 = project
@@ -435,21 +435,21 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to delete folder #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo_renamed/new_renamed.tex")
 				expect(update.newPathname).to.equal("")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
 				expect(update.pathname).to.equal("/bar/foo_renamed/1pixel_renamed.png")
 				expect(update.newPathname).to.equal("")

-				ProjectGetter.getProject @example_project_id, (error, newProject) =>
+				ProjectGetter.getProject example_project_id, (error, newProject) =>
 					throw error if error?
 					@project_1 = newProject
 					# replacing a file should update the project structure
@@ -493,7 +493,7 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to upload file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
@@ -530,7 +530,7 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to upload file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
@@ -567,7 +567,7 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to upload file #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).fileUpdates
+				{fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)
@@ -595,7 +595,7 @@
 				if res.statusCode < 200 || res.statusCode >= 300
 					throw new Error("failed to delete doc #{res.statusCode}")

-				updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).docUpdates
+				{docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id)
 				expect(updates.length).to.equal(1)
 				update = updates[0]
 				expect(update.userId).to.equal(@owner._id)


flash message fade in / out

flash message fade in / out

--- @client/flash.coffee
+++ @client/flash.coffee
@@ -8,7 +8,6 @@
     justify-content: center;
     z-index: 9999999;
     position: fixed;
-    bottom: 50px;
     font-size: 15px;
   }
   #flash {
@@ -19,6 +18,7 @@
     cursor: default;
     max-width: 400px;
     display: flex;
+    font-weight: 600;
   }

   #flash.error {
@@ -39,12 +39,25 @@
     border: none;
     padding: 12px 24px;
   }
+
+
+  @keyframes flashfadein {
+    from {bottom: 0; opacity: 0;}
+    to {bottom: 50px; opacity: 1;}
+  }
+
+  @keyframes flashfadeout {
+    from {bottom: 50px; opacity: 1;}
+    to {bottom: 0; opacity: 0;}
+  }
+
+
 """

-flash = fetch('flash')

 clear_flash = ->
-  flash.message = flash.args = null
+  flash = fetch('flash')
+  flash.message = flash.time = flash.args = null
   save flash

 window.show_flash_error = (message, time_in_ms) ->
@@ -57,8 +70,10 @@

   flash = fetch('flash')
   flash.message = message
+  flash.time = time_in_ms
   flash.args = args
   save flash
+
   setTimeout ->
     clear_flash()
   , time_in_ms
@@ -69,12 +84,16 @@

   render : ->
     flash = fetch('flash')
-    return SPAN(null) if !flash.message

     is_error = flash.args?.error

     DIV
       id: "flash-container"
+      style:
+        animation: if flash.message then "flashfadein 500ms, flashfadeout 500ms #{flash.time - 500}ms"
+        visibility: if flash.message then 'visible' else 'hidden'
+        opacity: if flash.message then 1 else 0
+        bottom: if flash.message then 50 else 0

       DIV
         id: 'flash'
@@ -85,7 +104,7 @@

         DIV
           className: 'flash-message'
-          dangerouslySetInnerHTML: {__html: flash.message}
+          dangerouslySetInnerHTML: if flash.message then {__html: flash.message}

         DIV
           className: 'flash-close'


more work on bomb

more work on bomb

--- scripts/bomb.coffee
+++ scripts/bomb.coffee
@@ -1,17 +1,17 @@
 module.exports = (robot) ->
   robot.respond /(.*) bomb( (\d+))?/i, (msg) ->
     subject = msg.match[1]
-    count = msg.match[2] || 5
-    for i in [0...count]
-	    imageMe msg, subject, (url) ->
-	    	msg.send url
+    if subject isnt "carson" and subject isnt "corgi" and subject isnt "pug"
+      count = msg.match[2] || 5
+      imageMe msg, count, subject

-imageMe = (msg, query, cb) ->
+imageMe = (msg, count, query) ->
   msg.http('http://ajax.googleapis.com/ajax/services/search/images')
     .query(v: "1.0", rsz: '8', q: query)
     .get() (err, res, body) ->
       images = JSON.parse(body)
       images = images.responseData.results
       if images.length > 0
-        image  = msg.random images
-        cb "#{image.unescapedUrl}#.png"+        for i in [0...count]
+          image = msg.random images
+          msg.send "#{image.unescapedUrl}#.png"


another fix

another fix

--- scripts/bomb.coffee
+++ scripts/bomb.coffee
@@ -1,7 +1,7 @@
 module.exports = (robot) ->
   robot.respond /(.*) bomb( (\d+))?/i, (msg) ->
     subject = msg.match[1]
-    if subject isnt "carson" and subject isnt "corgi" and subject isnt "pug" and subject isnt "maurice"
+    if subject isnt "carson" and subject isnt "corgi" and subject isnt "pug" and subject isnt "maurice" and subject isnt "fierce"
       count = msg.match[2] || 5
       imageMe msg, count, subject


Provide more information in the popupFeedPost callback

--- src/Carrot.coffee
+++ src/Carrot.coffee
@@ -141,12 +141,13 @@
       }
       params['object_instance_id'] = objectInstanceId if objectInstanceId
       @postSignedRequest("/me/feed_post.json", params, (jqXHR) =>
-        FB.ui(jqXHR.responseJSON.fb_data,
-          (response) =>
-            if response and response.post_id
-              @ajaxPost("#{@scheme}://parsnip.gocarrot.com/feed_dialog_post", {platform_id: jqXHR.responseJSON.post_id})
-
-            callback(jqXHR.responseJSON) if callback
+        carrotResponse = $.parseJSON(jqXHR.responseText)
+        FB.ui(carrotResponse.fb_data,
+          (fbResponse) =>
+            if fbResponse and fbResponse.post_id
+              @ajaxPost("#{@scheme}://parsnip.gocarrot.com/feed_dialog_post", {platform_id: carrotResponse.post_id})
+
+            callback(carrotResponse, fbResponse) if callback
         )
       )


PatchView -> PatchElement

PatchView -> PatchElement


Include Backbone dependency

Include Backbone dependency

--- src/scripts/modules/find-content/find-content.coffee
+++ src/scripts/modules/find-content/find-content.coffee
@@ -1,5 +1,6 @@
 define (require) ->
   $ = require('jquery')
+  Backbone = require('backbone')
   router = require('cs!router')
   subjects = require('cs!collections/subjects')
   BaseView = require('cs!helpers/backbone/views/base')


_onChannelOpen() tests for Remote.Peer

_onChannelOpen() tests for Remote.Peer

--- src/tests/spec/public/models/remote.peer.spec.coffee
+++ src/tests/spec/public/models/remote.peer.spec.coffee
@@ -405,3 +405,45 @@
 						return success
 					, 1000)

+		describe 'when the channel is opened', ->
+			it 'should start sending a ping packet every 2500 ms', ->
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+				jasmine.Clock.useMock();
+				spyOn(peer, 'ping')
+				peer._onChannelOpen()
+				jasmine.Clock.tick(1)
+				for i in [0...10]
+					expect(peer.ping.callCount).toBe(i)
+					jasmine.Clock.tick(2500);		# Fake the time passing 2500ms
+
+			it 'should send queries for benchmark, system and isSuperNode', ->
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+				called = []
+				spyOn(peer, 'query').andCallFake( ( query, fn ) ->
+						called.push query
+					)
+				peer._onChannelOpen()
+				expect(called).toEqual([
+						'benchmark'
+						'system'
+						'isSuperNode'
+					])
+
+			it 'should trigger the channel.opened event', ->
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+				success = false
+
+				fakeEvent = {
+					a: 1
+					b: 2
+				}
+				peer.on('channel.opened', ( thePeer, event ) ->
+						expect(thePeer).toBe(peer)
+						expect(event).toBe(fakeEvent)
+						success = true
+					)
+
+				peer._onChannelOpen(fakeEvent)
+				waitsFor(->
+						return success
+					, 1000)


_addChannel() tests for Remote.Peer

_addChannel() tests for Remote.Peer

--- src/tests/spec/public/models/remote.peer.spec.coffee
+++ src/tests/spec/public/models/remote.peer.spec.coffee
@@ -164,7 +164,7 @@

 				expect(peer._connection.close).toHaveBeenCalled()

-		describe 'sending', ->
+		describe 'when sending', ->
 			it 'should check if the channel is open before sending', ->
 				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
 				spyOn(peer, 'isChannelOpen').andReturn(false)
@@ -200,4 +200,24 @@
 				waitsFor(->
 						return peer._send.callCount is 6
 					, 1000)
-
+
+		describe 'when adding a channel', ->
+			it 'should set _channel to the channel', ->
+				fakeChannel = new FakeRTCDataChannel()
+				spyOn(global, 'FakeRTCDataChannel').andReturn(fakeChannel)
+
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+
+				expect(peer._channel).toEqual(fakeChannel)
+
+			it 'should set the channels events to its own callbacks', ->
+				fakeChannel = new FakeRTCDataChannel()
+				spyOn(global, 'FakeRTCDataChannel').andReturn(fakeChannel)
+
+				peer = new Peer(fakeController, '1', true, FakeRTCPeerConnection)
+
+				expect(peer._channel.onmessage).toEqual(peer._onChannelMessage)
+				expect(peer._channel.onopen).toEqual(peer._onChannelOpen)
+				expect(peer._channel.onclose).toEqual(peer._onChannelClose)
+				expect(peer._channel.onerror).toEqual(peer._onChannelError)
+


let's try to hack in some XDomainRequest love

--- lib/assets/javascripts/firehose/long_poll.js.coffee
+++ lib/assets/javascripts/firehose/long_poll.js.coffee
@@ -39,7 +39,6 @@
     # set any HTTP headers for CORS requests.
     data = @config.params
     data.last_message_sequence = @_lastMessageSequence
-    $.support.cors = true
     # TODO: Some of these options will be deprecated in jQuery 1.8
     #       See: http://api.jquery.com/jQuery.ajax/#jqXHR
     $.ajax @config.longPoll.url,
@@ -105,25 +104,27 @@
       # Reconnect with delay
       setTimeout @_request, @_retryDelay

-# NB: This is a stupid hack to deal with CORS short-comings in jQuery in
-# Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
-# Once jQuery is upgraded to this version we can probably remove this, but be
-# sure you test the crap out of Firefox!
-#
 # Its also worth noting that I had to localize this monkey-patch to the
 # Firehose.LongPoll consumer because a previous global patch on
 # jQuery.ajaxSettings.xhr was breaking regular IE7 loading. Better to localize
 # this anyway to solve that problem and loading order issues.
 hackedXHR = ->
-  xhr = jQuery.ajaxSettings.xhr()
-  originalFun = xhr.getAllResponseHeaders
-  xhr.getAllResponseHeaders = ->
-    XHR_HEADERS = [
-      "Cache-Control", "Content-Language", "Content-Type"
-      "Expires", "Last-Modified", "Pragma"
-    ]
-    return allHeaders if (allHeaders = originalFun.call xhr)?
-    lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
-      "#{name}: #{xhr.getResponseHeader name}"
-    lines.join '\n'
-  xhr
+  # HACK: use XDomainRequest instead for IE8+
+  if window.XDomainRequest? then new XDomainRequest()
+  else
+    # NB: This is a stupid hack to deal with CORS short-comings in jQuery in
+    # Firefox. There is a ticket for this: http://bugs.jquery.com/ticket/10338
+    # Once jQuery is upgraded to this version we can probably remove this, but be
+    # sure you test the crap out of Firefox!
+    xhr = jQuery.ajaxSettings.xhr()
+    originalFun = xhr.getAllResponseHeaders
+    xhr.getAllResponseHeaders = ->
+      XHR_HEADERS = [
+        "Cache-Control", "Content-Language", "Content-Type"
+        "Expires", "Last-Modified", "Pragma"
+      ]
+      return allHeaders if (allHeaders = originalFun.call xhr)?
+      lines = for name in XHR_HEADERS when xhr.getResponseHeader(name)?
+        "#{name}: #{xhr.getResponseHeader name}"
+      lines.join '\n'
+    xhr


rearrange Transport just a smidge

rearrange Transport just a smidge

--- lib/assets/javascripts/firehose/transport.js.coffee
+++ lib/assets/javascripts/firehose/transport.js.coffee
@@ -11,16 +11,17 @@

   # Lets rock'n'roll! Connect to the server.
   connect: (delay = 0) =>
+    console?.log "Transport#connect", delay
     setTimeout @_request, delay
     this

-  stop: ->
-
-  # Sub classes need to implement this method to handle requests
-  _request: =>
+  # Hey subclasses:
+  stop: ->     # implement this method to stop receiving messages
+  _request: -> # implement this method to handle requests

   # Default error handler
   _error: (event) =>
+    console?.log "Transport#_error", event
     if @_succeeded
       # Lets try to connect again with delay
       @config.disconnected()


Use 'X-CSRF-Token' in favor of deprecated 'X-Query-Params'

--- lib/assets/javascripts/jack_up/file_uploader.coffee
+++ lib/assets/javascripts/jack_up/file_uploader.coffee
@@ -1,11 +1,3 @@
-railsCSRFData = ->
-  csrfParam = $('meta[name=csrf-param]').attr('content')
-  csrfToken = $('meta[name=csrf-token]').attr('content')
-
-  formData = {}
-  formData[csrfParam] = csrfToken
-  JSON.stringify formData
-
 class @JackUp.FileUploader
   constructor: (@options) ->
     @path = @options.path
@@ -49,7 +41,7 @@

     xhr.setRequestHeader 'Content-Type', file.type
     xhr.setRequestHeader 'X-File-Name', file.name
-    xhr.setRequestHeader 'X-Query-Params', railsCSRFData()
+    xhr.setRequestHeader 'X-CSRF-Token', $('meta[name=csrf-token]').attr('content')

     @trigger 'upload:start', file: file
     xhr.send file


Tests.

Tests.

--- apps/favorites_follows/test/client/favorites.coffee
+++ apps/favorites_follows/test/client/favorites.coffee
@@ -65,12 +65,12 @@
   describe 'with favorites items', ->

     beforeEach (done) ->
-      sinon.stub Backbone, 'sync'
+      @sync = sinon.stub Backbone, 'sync'
       benv.render resolve(__dirname, '../../templates/favorites.jade'), {
         sd: {}
       }, =>
         { FavoritesView, @init } = mod = benv.requireWithJadeify(
-          (resolve __dirname, '../../client/favorites'), ['hintTemplate']
+          (resolve __dirname, '../../client/favorites'), ['hintTemplate','favoritesStatusTemplate']
         )
         @src = [
           { artwork: fabricate 'artwork', id: 'artwork1' },
@@ -99,6 +99,9 @@
         @ArtworkColumnsView.returns @ArtworkColumnsView
         mod.__set__ 'ArtworkColumnsView', @ArtworkColumnsView
         mod.__set__ 'SaveControls', sinon.stub()
+        @FavoritesStatusModal = sinon.stub()
+        @FavoritesStatusModal.returns @FavoritesStatusModal
+        mod.__set__ 'FavoritesStatusModal', @FavoritesStatusModal
         @view = new FavoritesView
           el: $ 'body'
           collection: artworks
@@ -144,3 +147,14 @@
         _.last(@fetchStub.args)[0].data.sort.should.equal '-position'
         @view.loadNextPage()
         _.last(@fetchStub.args)[0].data.sort.should.equal '-position'
+
+    describe '#showStatusDialog', ->
+
+      it 'shows the status dialog when trying to share if the favorites is private', ->
+        @view.savedArtworkCollection.set private: true
+        @view.$el.find('.share-to-facebook').trigger 'click'
+
+      it 'does not show the status dialog when trying to share if the favorites is public', ->
+        @view.savedArtworkCollection.set private: false
+        @view.$el.find('.share-to-facebook').trigger 'click'
+        @FavoritesStatusModal.called.should.not.be.ok


Hide artists component when not enabled

Hide artists component when not enabled

--- apps/partner2/client/overview_layout_factory.coffee
+++ apps/partner2/client/overview_layout_factory.coffee
@@ -163,9 +163,10 @@
 galleryTwoArtists = (partner, profile) ->
   name: 'artists'
   component:
-    switch partner.get('profile_artists_layout')
-      when 'Grid' then ArtistsGridView
-      else ArtistsListView
+    if partner.get('display_artists_section')
+      switch partner.get('profile_artists_layout')
+        when 'Grid' then ArtistsGridView
+        else ArtistsListView
   options: partner: partner
   title: 'Artists' unless partner.get('profile_artists_layout') is 'Grid'
   viewAll: "#{partner.href()}/artists" unless partner.get('profile_artists_layout') is 'Grid'


GW-3912 #comment remove dropdown button from polymorphic object select field

--- app/assets/javascripts/rails_admin/ra.widgets.coffee
+++ app/assets/javascripts/rails_admin/ra.widgets.coffee
@@ -151,8 +151,11 @@
       field = type_select.parents('.control-group').first()
       object_select = field.find('select').last()
       urls = type_select.data('urls')
+
+      # needed these for reusing the single-select without changing too much of the sigle-select code
       object_select.siblings().css('float', 'left')
       object_select.css('display', 'none')
+      field.find('label').last().hide()

       type_select.on 'change', (e) ->
         object_select.data('options', $("##{type_select.val().toLowerCase()}-js-options").data('options'))


open links in new window

open links in new window

--- app/assets/javascripts/views/report_item_view.coffee
+++ app/assets/javascripts/views/report_item_view.coffee
@@ -11,7 +11,7 @@
   template : _.template("""
     <td title="<%= name %>" class="truncate">
       <% if(githubUrl){ %>
-        <a href="<%= githubUrl %>"><%= name %>
+        <a href="<%= githubUrl %>" target="_blank"><%= name %>
       <% } else { %>
         <%= name %>
       <% }%>


Part of previous commit: updating how temporary variables are generated. We no longer need to store the name of the last generated variable, instead we store the index.

--- scope.coffee
+++ scope.coffee
@@ -54,12 +54,9 @@
   # If we need to store an intermediate result, find an available name for a
   # compiler-generated variable. `_var`, `_var2`, and so on...
   freeVariable: (type) ->
-    next = (prev) ->
-      '_' + type + ((prev and Number(prev.match(/\d+$/) or 1) + 1) or '')
-    while @check @tempVars[type] or= next()
-      @tempVars[type] = next @tempVars[type]
-    @variables[@tempVars[type]] = 'var'
-    @tempVars[type]
+    @tempVars[type]++ while @check temp = '_' + type + (if (@tempVars[type] or= 1) > 1 then @tempVars[type] else '')
+    @variables[temp] = 'var'
+    temp

   # Ensure that an assignment is made at the top of this scope
   # (or at the top-level scope, if requested).


<tests> Sensor - Added useless test to clear DB...

--- tests/controllers/sensor.coffee
+++ tests/controllers/sensor.coffee
@@ -209,3 +209,12 @@

         it 'should not have deleted the sensor from the Driver\'s data too', ->
             expect(store["driverModule"].getSensor(fixturesSensor.supportedSensor1.customId)).to.equal store.sensorId
+
+        it 'should be deleted if the Driver allows it that time', (done) ->
+            store["driverModule"].setRemovableFlag true
+            @client.del "sensors/#{store.sensorId}", done
+
+        it 'should return a "success"', ->
+            expect(@err).to.not.exist
+            expect(@response.statusCode).to.equal 200
+            expect(@body.success).to.equal true


Added tests for placeholder attribute

Added tests for placeholder attribute

--- testapp/app/assets/javascripts/spec.coffee
+++ testapp/app/assets/javascripts/spec.coffee
@@ -11,13 +11,14 @@
   describe "looking up attributes in parents", ->
     beforeEach ->
       rip = makeRip """
-

+

           Blubb

"""
-    it "should find the data-url"      , -> expect(rip.url).toEqual('localhorst')
-    it "should find the data-formtype" , -> expect(rip.formType).toEqual('textarea')
-    it "should find the data-object"   , -> expect(rip.objectName).toEqual('person')
-    it "should find the data-attribute", -> expect(rip.attributeName).toEqual('name')
+    it "should find the data-url"        , -> expect(rip.url).toEqual('localhorst')
+    it "should find the data-formtype"   , -> expect(rip.formType).toEqual('textarea')
+    it "should find the data-object"     , -> expect(rip.objectName).toEqual('person')
+    it "should find the data-attribute"  , -> expect(rip.attributeName).toEqual('name')
+    it "should find the data-placeholder", -> expect(rip.placeholder).toEqual('Enter name')

     it "should prefer inner settings over outer", ->
       rip = makeRip """

Blubb

"""
@@ -40,14 +41,15 @@
       expect(rip.formType).toEqual('input')
     it "should take precedence over anything set through parents", ->
       rip = makeRip """
-

-          Blubb
+

+          Blubb

"""
       expect(rip.url).toEqual('localhorst2')
       expect(rip.formType).toEqual('textarea')
       expect(rip.objectName).toEqual('person2')
       expect(rip.attributeName).toEqual('name2')
-
+      expect(rip.placeholder).toEqual('placeholder2')
+
 describe "Server communication", ->
   beforeEach ->
     rip = makeRip '
Blubb
'
@@ -235,3 +237,19 @@
     rip.$element.bind("ready.rest-in-place", handlers.ready)
     rip.activate()
     expect(handlers.ready).toHaveBeenCalled()
+
+describe "Placeholder", ->
+  beforeEach ->
+    rip = makeRip '

'
+
+  it "sets a placeholder when the value is empty", ->
+    expect(rip.$element.html()).toEqual('Enter age')
+
+  it "dosen't set a placeholder when a value is present", ->
+    rip = makeRip '
123
'
+    expect(rip.$element.html()).toNotEqual('Enter age')
+
+  it "switches to placeholder attribute when activated", ->
+    rip.activate()
+    expect(rip.$element.find('input')[0].hasAttribute('placeholder'));
+


Upgraded the notification formatting

Upgraded the notification formatting

--- library/gearing/watch.coffee
+++ library/gearing/watch.coffee
@@ -73,6 +73,8 @@
         return if resolved is entrypoint
         return if resolved of require.cache
         go = => @reviewServices resolved
+        relative = paths.relative process.cwd(), path
+        logger.info("Addition at %s".cyan, relative)
         try require resolved; go() catch error
             message = "Exception in module at #{path}:\r\n%s"
             logger.warn message.red, error.stack
@@ -93,6 +95,8 @@
         return if resolved is entrypoint
         delete require.cache[resolved] if cached
         go = => @reviewServices resolved
+        relative = paths.relative process.cwd(), path
+        logger.info("Changing at %s".cyan, relative)
         try require resolved; go() catch error
             message = "Exception in module at #{path}:\r\n%s"
             logger.warn message.red, error.stack
@@ -107,6 +111,8 @@
         modules = @constructor.EXTENSIONS
         extension = paths.extname absolute
         return unless extension in modules
+        relative = paths.relative process.cwd(), path
+        logger.info("Unlinking at %s".cyan, relative)
         registry = @kernel.router?.registry or []
         originate = (s) -> s.constructor.origin?.filename
         predicate = (s) -> originate(s) is absolute
@@ -176,7 +182,7 @@
         exists = fs.existsSync directory.toString()
         throw new Error(notString) unless _.isString directory
         logger.warn notExists.grey, directory unless exists
-        relative = paths.relative __dirname, directory
+        relative = paths.relative process.cwd(), directory
         logger.info "Watching %s dir for modules".blue, relative
         watcher = chokidar.watch directory.toString()
         watcher.on "unlink", @hotSwappingUnlink.bind @


One more test for amendUrl.

One more test for amendUrl.

--- tests/html/src/unittests.coffee
+++ tests/html/src/unittests.coffee
@@ -44,6 +44,7 @@

     equal(u.amendUrl('//blah:1/abc'), dl.protocol + '//blah:1/abc')
     equal(u.amendUrl('/abc'), dl.protocol + '//' + dl.host + '/abc')
+    equal(u.amendUrl('/'), dl.protocol + '//' + dl.host)
     equal(u.amendUrl('http://a:1/abc'), 'http://a:1/abc')
     equal(u.amendUrl('http://a:1/abc/'), 'http://a:1/abc')
     equal(u.amendUrl('http://a:1/abc//'), 'http://a:1/abc')


CSV.toText: new parameter "add_bom": add a byte order mark to the output.

This allows writing UTF-8 CSV for MS Excel.

--- src/base/CSVData.coffee
+++ src/base/CSVData.coffee
@@ -74,6 +74,10 @@
 				default: String.fromCharCode(10)
 				check: (v) =>
 					CUI.util.isString(v) and v.length > 0
+			add_bom:
+				default: false
+				mandatory: true
+				check: Boolean

 		if opts.equal_columns
 			@giveAllRowsSameNumberOfColumns()
@@ -114,7 +118,10 @@

 			_rows.push(_row.join(""))

-		_rows.join(opts.newline)
+		if opts.add_bom
+			'\ufeff'+_rows.join(opts.newline)
+		else
+			_rows.join(opts.newline)

 	# parse csv info array
 	parse: (_opts={}) ->


adds "awesome print" function

adds "awesome print" function


Stringify booleans

Stringify booleans

--- components/Sanitize.coffee
+++ components/Sanitize.coffee
@@ -18,6 +18,10 @@
       # Ignore if the *placeholder* starts with an ampersand
       if @placeholder?.match? /^&/
         data = data
+
+      # Ignore if boolean
+      else if _.isBoolean data
+        data = "#{data}"

       # Ignore if data starts with an ampersand (an escape), but remove the
       # ampersand of course


Fixed bug (wrong service being called from the controller when loading a single feed title)

--- app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
+++ app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
@@ -8,11 +8,13 @@
 'findSvc', 'userDataSvc', 'userConfigSvc', 'openEntrySvc', 'unreadCountSvc', 'sidebarVisibleSvc', 'menuCollapseSvc',
 'tooltipSvc', 'startPageSvc', 'jobStateSvc', 'socialNetworksSvc', 'cookiesSvc', 'animationsSvc',
 'highlightedEntrySvc', 'highlightedSidebarLinkSvc', 'dialogsSvc', 'keyboardShortcutsSvc', 'refreshFeedSvc', 'openFolderSvc',
+'loadFeedsSvc',
 ($rootScope, $scope, $timeout, $sce, feedsFoldersSvc, importStateSvc, exportStateSvc, timerFlagSvc,
 currentFeedSvc, currentFolderSvc, subscriptionSvc, loadEntriesSvc, folderSvc, entrySvc,
 findSvc, userDataSvc, userConfigSvc, openEntrySvc, unreadCountSvc, sidebarVisibleSvc, menuCollapseSvc,
 tooltipSvc, startPageSvc, jobStateSvc, socialNetworksSvc, cookiesSvc, animationsSvc,
-highlightedEntrySvc, highlightedSidebarLinkSvc, dialogsSvc, keyboardShortcutsSvc, refreshFeedSvc, openFolderSvc)->
+highlightedEntrySvc, highlightedSidebarLinkSvc, dialogsSvc, keyboardShortcutsSvc, refreshFeedSvc, openFolderSvc,
+loadFeedsSvc)->

   #--------------------------------------------
   # APPLICATION INITIALIZATION
@@ -471,7 +473,7 @@
         return feed.title
       else
         # If the requested feed is not in the scope, try to load it from the server
-        feedsFoldersSvc.load_feed feed_id
+        loadFeedsSvc.load_feed feed_id
         return null

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


Remove console log message from decrease job priority button.

--- app/assets/javascripts/rocket_job_mission_control/base.js.coffee
+++ app/assets/javascripts/rocket_job_mission_control/base.js.coffee
@@ -21,5 +21,4 @@
     $('#job_priority').val(parseInt($('#job_priority').val(), 10) + 1)

   $('#decrease_priority').on 'click', ->
-    console.log 'decreasing'
     $('#job_priority').val(parseInt($('#job_priority').val(), 10) - 1)