Restart validation on form changes.

Restart validation on form changes.

--- services/web/public/coffee/main/new-subscription.coffee
+++ services/web/public/coffee/main/new-subscription.coffee
@@ -86,14 +86,17 @@
 				$scope.data.year = parsedDateObj.year

 		$scope.validateCardNumber = validateCardNumber = ->
+			$scope.validation.errorFields = {}
 			if $scope.data.number?.length != 0
 				$scope.validation.correctCardNumber = recurly.validate.cardNumber($scope.data.number)

 		$scope.validateExpiry = validateExpiry = ->
+			$scope.validation.errorFields = {}
 			if $scope.data.month?.length != 0 and $scope.data.year?.length != 0
 				$scope.validation.correctExpiry = recurly.validate.expiry($scope.data.month, $scope.data.year)

 		$scope.validateCvv = validateCvv = ->
+			$scope.validation.errorFields = {}
 			if $scope.data.cvv?.length != 0
 				$scope.validation.correctCvv = recurly.validate.cvv($scope.data.cvv)


enable input line on any OS.

enable input line on any OS.

--- salvus/page/console.coffee
+++ salvus/page/console.coffee
@@ -427,9 +427,9 @@

     _init_input_line: () =>

-        if not IS_MOBILE
-            @element.find(".salvus-console-mobile-input").hide()
-            return
+        #if not IS_MOBILE
+        #    @element.find(".salvus-console-mobile-input").hide()
+        #    return

         input_line = @element.find('.salvus-console-input-line')


html editor in sage worksheets - temporarily disabled

--- salvus/page/syncdoc.coffee
+++ salvus/page/syncdoc.coffee
@@ -2271,6 +2271,8 @@
             line : required
             cm   : required

+        return # DISABLED FOR NOW
+
         cm = opts.cm
         scroll_info = cm.getScrollInfo()
         block = @current_input_block(opts.line)


FIX: important bug with ScrollElement that oddly only showed up if the parent was child-size-relative. It should have broken much more than that...

--- source/Art/Engine/Elements/Widgets/ScrollElement.coffee
+++ source/Art/Engine/Elements/Widgets/ScrollElement.coffee
@@ -172,9 +172,9 @@
     # update _spMinusTp
     if contentFits
       if wasTracking
-        @_pendingState._spMinusTp = _scrollPosition
-    else
-      @_pendingState._spMinusTp = _scrollPosition - @trackingPositionFromPendingGeometry
+        @_spMinusTp = _scrollPosition
+    else
+      @_spMinusTp = _scrollPosition - @trackingPositionFromPendingGeometry

     @_updateOnScreenInfo oldChildrenOffset != @_childrenOffset


fixed inappropriate behavior of 'notls' option (previous versions did not register bundled-plugins when notls option was set)

--- src/API.coffee
+++ src/API.coffee
@@ -78,13 +78,14 @@
         # Define a wrapper function, which runs any restify-method-function on all instances
         @server = (type, args..., notls) =>
             for instance in @instances
-                if instance.server.tls? and typeof notls == 'boolean' and notls == true
-                    continue
-                if typeof notls != 'boolean'
+                if typeof notls == 'boolean'
+                    if instance.server.tls? and notls == true
+                        continue
+                else if typeof notls != 'boolean' || notls == false
                     args.push(notls)
-                    instance[type].apply(instance, args)
-
-
+
+                instance[type].apply(instance, args)
+
         # Check whether logger should be used
         if 'logger' of @options
             @server('use', MORGAN(@options['logger']))


breadcrumb marking menu items 2

breadcrumb marking menu items 2

--- app/assets/javascripts/backbone_js/ladas_breadcrumb.js.coffee
+++ app/assets/javascripts/backbone_js/ladas_breadcrumb.js.coffee
@@ -17,7 +17,7 @@

 class Breadcrumbs
   @mark_active_menu_items: ->
-    $('a[data-breadcrumb_id]').each (index, element) =>
+    $('a[data-breadcrumb-id]').each (index, element) =>
       $(element).parent('li').removeClass('active')

     $('.breadcrumb li').each (index, element) =>
@@ -29,8 +29,10 @@
         Breadcrumbs.mark_menu_item(bc.html())

   @mark_menu_item: (val) ->
-    finding_string = "a[data-breadcrumb_id='" + val + "']"
+    finding_string = "li[data-breadcrumb-id='" + val + "']"
+    finding_string += ",a[data-breadcrumb-id='" + val + "']"
+    #console.log $(finding_string)
     found = $(finding_string)
-    found.parent('li').addClass('active') if found
+    found.addClass('active') if found

 window.Breadcrumbs = Breadcrumbs


Update jquery.china_city.js.coffee

Update jquery.china_city.js.coffee

同turbo-links一起使用时插件会失效，如果整个页面刷新就会好，估计原因是原来的插件只在ready的时候被初始化，但page:load或page:change时没有初始化。不知道是不是这样改？我本地改成这样后就好了

--- app/assets/javascripts/china_city/jquery.china_city.js.coffee
+++ app/assets/javascripts/china_city/jquery.china_city.js.coffee
@@ -10,6 +10,6 @@
           $.get "/china_city/#{$(@).val()}", (data) ->
             next_selects.first()[0].options.add(new Option(option[0], option[1])) for option in data

-  $ ->
+  $(document).on 'ready page:load', ->
     $('.city-group').china_city()
 )(jQuery)


make billboard faster;

make billboard faster;

--- _assets/javascripts/billboard.js.coffee
+++ _assets/javascripts/billboard.js.coffee
@@ -18,5 +18,5 @@
                 next = $('#billboard .bb:first')
               next.addClass('active')
               loopslider()
-        , 8000
+        , 4000
   loopslider()


Fixed 'not in' groupings

Fixed 'not in' groupings

--- src/builtin/string.coffee
+++ src/builtin/string.coffee
@@ -65,7 +65,7 @@
       return false
     else
       for char in @value
-        if not char in alnums
+        if not (char in alnums)
           return false
       return true

@@ -75,7 +75,7 @@
       return false
     else
       for char in @value
-        if not char in alphas
+        if not (char in alphas)
           return false
       return true

@@ -85,7 +85,7 @@
       return false
     else
       for char in @value
-        if not char in digits
+        if not (char in digits)
           return false
       return true

@@ -95,7 +95,7 @@
       return false
     else
       for char in @value
-        if not char in lowers
+        if not (char in lowers)
           return false
       return true

@@ -105,7 +105,7 @@
       return false
     else
       for char in @value
-        if not char in spaces
+        if not (char in spaces)
           return false
       return true

@@ -130,7 +130,7 @@
       return false
     else
       for char in @value
-        if not char in uppers
+        if not (char in uppers)
           return false
       return true


mongoimport ensureIndex added

mongoimport ensureIndex added

--- data/mongo-imports2.coffee
+++ data/mongo-imports2.coffee
@@ -1,24 +1,37 @@
 @run = ->

+  console.log ">>>>>>>>>> Importing aiddata >>>>>>>>>>"
+
   fs = require 'fs'
   util = (require '../cakeutils').include()

-  #mongo = require 'mongodb'
+  mongo = require 'mongodb'
   mongoConf = require('../.dbconf').mongodb

   pg = require 'pg'
   pgurl = require('../.dbconf').postgres

-
-  runImportTasks = (importTasks) ->
-    for taskName, importFunc of importTasks
-      console.log ">>>>>>>>>> Importing '#{taskName}' >>>>>>>>>>"
-      importFunc()
+  connectToMongo = (dbName, collName, callback) ->
+    ifnot = (err, callback) -> if not err then callback() else console.error err
+    mongosrv = new mongo.Server(mongoConf.host, mongoConf.port)
+    mongodb = new mongo.Db(dbName, mongosrv)
+    mongodb.open (err, p_client) -> ifnot err, ->
+      mongodb.authenticate mongoConf.user, mongoConf.password, (err) -> ifnot err, ->
+        mongodb.collection collName, (err, coll) ->
+          unless err?
+            callback(mongodb, coll)
+          else
+            console.log err

-  runImportTasks
+  limit = undefined

-    aiddata : ->
+  connectToMongo 'aiddata', 'aiddata', (mongodb, coll) ->
+    coll.ensureIndex { aiddata_id : 1 }, ->
+      console.log "Index for aiddata_id ensured"
+
+      mongodb.close()
+
       tempDir = "data/temp"
       tempFile = tempDir + "/_aiddata.json"
       util.mkdir tempDir
@@ -30,7 +43,6 @@
       #pgclient.on('drain', pgclient.end.bind(pgclient))
       pgclient.connect()

-      #limit = 10

       cntQuery = pgclient.query(
         if limit?
@@ -42,7 +54,7 @@
       cntQuery.on 'row', (row) ->

         totalRecordsNum = row.count
-        console.log "totalRecordsNum: " + totalRecordsNum
+        console.log "Reading #{totalRecordsNum} records from postgres..."

         numProcessed = 0

@@ -67,8 +79,10 @@
           # assuming that stringify produces a one-liner
           jsonFile.write JSON.stringify(row) + "\n"

+
           if numProcessed >= totalRecordsNum
-            console.log "Read in: #{numProcessed}"
+            console.log "#{numProcessed} records were saved temporarily in #{tempFile}"
+            console.log "Now importing into MongoDB"
             util.run "/usr/bin/mongoimport " +
                         "-d aiddata -c aiddata --upsert --upsertFields aiddata_id " +
                         "-u #{mongoConf.user} -p #{mongoConf.password} "+
@@ -77,7 +91,5 @@
                 fs.unlink tempFile
                 console.log "Done"

-
-
         query.on 'end', -> pgclient.end()


Profile: getCurrentGroup method calls are changed with getGroup()

--- client/Social/Members/AppController.coffee
+++ client/Social/Members/AppController.coffee
@@ -6,7 +6,7 @@
       "/:name?/Members" : ({params, query}) ->
         {router, appManager} = KD.singletons
         KD.getSingleton('groupsController').ready ->
-          group = KD.getSingleton('groupsController').getCurrentGroup()
+          group = KD.getGroup()
           KD.getSingleton("appManager").tell 'Members', 'createContentDisplay', group, (contentDisplay) ->
             contentDisplay.emit "handleQuery", {filter: "members"}

@@ -84,7 +84,7 @@
           listCssClass      : "member-related"
           noItemFoundText   : "No one is following #{owner} yet."
           dataSource        : (selector, options, callback)=>
-            options.groupId or= KD.getSingleton('groupsController').getCurrentGroup().getId()
+            options.groupId or= KD.getGroup().getId()
             account.fetchFollowersWithRelationship selector, options, callback

             account.countFollowersWithRelationship selector, (err, count)=>
@@ -96,7 +96,7 @@
           listCssClass      : "member-related"
           noItemFoundText   : "#{owner} #{auxVerb.be} not following anyone."
           dataSource        : (selector, options, callback)=>
-            options.groupId or= KD.getSingleton('groupsController').getCurrentGroup().getId()
+            options.groupId or= KD.getGroup().getId()
             account.fetchFollowingWithRelationship selector, options, callback

             account.countFollowingWithRelationship selector, (err, count)=>


From whole to detail.

From whole to detail.

--- app/client/js/storage.coffee
+++ app/client/js/storage.coffee
@@ -48,7 +48,7 @@
   saveStore: (store) ->
     @localStorage.set store
     if @firebase.userRef
-      if @userStore.user && store instanceof app.user.Store
+      if store instanceof app.user.Store && @userStore.user
         # TODO(steida): JSON.parse JSON.stringify seems to be really stupid,
         # but idk how to workaround this issue better for now.
         # Firebase.set failed: First argument contains a function in property...


Added: comment

Added: comment

--- client/app/views/config_application.coffee
+++ client/app/views/config_application.coffee
@@ -25,6 +25,8 @@
         super

     initialize: ->
+        # only re-render when 'version' changes, because it's the only displayed
+        # field that can change during the update
         @listenTo @model, 'change:version', @render

     afterRender: =>


Fix indent

Fix indent

--- lib/line_wrapper.coffee
+++ lib/line_wrapper.coffee
@@ -59,11 +59,10 @@
             options.lineWidth = @lineWidth
             @emit 'line', buffer, options, this

-        spaceLeft = @lineWidth - indent
-
         while bk = breaker.nextBreak()
             if not last? or last.required
                 @emit 'firstLine', options, this
+                spaceLeft = @lineWidth

             word = text.slice(last?.position or 0, bk.position)
             w = wordWidths[word] ?= width(word, options) + charSpacing + wordSpacing
@@ -86,7 +85,6 @@

                 # reset the space left and buffer
                 if bk.required
-                    spaceLeft = @lineWidth - indent
                     buffer = ''
                     lineWidth = 0
                     wc = 0


Remove experimental react/turbolinks update

--- resources/assets/coffee/_classes/react-turbolinks.coffee
+++ resources/assets/coffee/_classes/react-turbolinks.coffee
@@ -36,12 +36,7 @@
         continue if !component.loaded

         component.loaded = false
-
-        target = component.target[0]
-        dupTarget = target.cloneNode(true)
-        ReactDOM.unmountComponentAtNode target
-
-        target.parentNode.replaceChild dupTarget, target
+        ReactDOM.unmountComponentAtNode component.target[0]

   register: (name, element, propsFunction = ->) =>


don't create a new game automatically

don't create a new game automatically

--- public/javascripts/modules/games/index/controller.coffee
+++ public/javascripts/modules/games/index/controller.coffee
@@ -6,11 +6,11 @@
     game.save(planet: planets.first()).done ->
       Hexa.trigger "games:show", game

-  index = (games) ->
-    currentPlayer = Hexa.request("app:currentUser")
+  index = ->
+    player = Hexa.request "app:currentUser"
+    games  = Hexa.request "entities:games"

-
-    gamesView = new Views.GamesView collection: games.started(), currentPlayer: currentPlayer
+    gamesView = new Views.GamesView collection: games.started(), currentPlayer: player
     Hexa.mainRegion.show gamesView

     gamesView.on "game:new", ->
@@ -22,7 +22,7 @@
   # EXPORTS

   index: ->
-    if (games = Hexa.request "entities:games").isEmpty() then create() else index(games)
+    index()

   new: ->
     create()


Reset progress bar before start.

Reset progress bar before start.

--- app/assets/javascripts/application/pods_progress_bar.js.coffee
+++ app/assets/javascripts/application/pods_progress_bar.js.coffee
@@ -4,7 +4,8 @@
     progress = factor * 100
     @nanobar.go(progress)
   start: ->
+    $container = $('.progress-container').empty()
     @nanobar = new Nanobar
-      target: $('.progress-container').get(0)
+      target: $container.get(0)
       id: 'pods-progress'


implement search_items function

implement search_items function

--- scripts/chat-logger.coffee
+++ scripts/chat-logger.coffee
@@ -132,8 +132,27 @@
   robot.hear /.*/, (msg) -> log_message 'text', msg

   # web interface
-  search_items = (query) ->
-    [] # TODO
+  search_items = (room, query) ->
+    src = chat_data()[room]
+    query = new RegExp query
+    result = []
+
+    try
+      for year in _.keys(src).sort().reverse()
+        src[year].reduceRight (prev, month) ->
+          return unless month
+          month.reduceRight (prev, day) ->
+            return unless day
+            day.reduceRight (prev, msg) ->
+              result.push msg if query.test JSON.stringify msg
+              throw "break" if result.length >= ITEM_COUNT
+            , null
+          , null
+        , null
+    catch e
+      throw e if e != "break"
+
+    result

   generate_time_string = (d) ->
     d = new Date d unless d instanceof Date
@@ -220,8 +239,12 @@
     res.send feed.render 'atom-1.0'

   robot.router.get "/#{base_path}/:room/search", (req, res) ->
-    res.type 'text/html'
-    res.send render_items search_items req.query.q
+    if chat_data()[req.params.room]?
+      res.type 'text/html'
+      res.send render_items \
+        "Search result of #{req.query.q}", \
+        search_items(req.params.room, req.query.q)
+    else not_found

   robot.router.get "/#{base_path}/:room", (req, res) ->
     room = req.params.room


Fix feed items render.

Fix feed items render.

--- scripts/chat-logger.coffee
+++ scripts/chat-logger.coffee
@@ -220,8 +220,11 @@
       author:
         name: title.nick
       link: "#{base_url}/#{room}/#{d.getUTCFullYear()}/#{d.getUTCMonth() + 1}/#{d.getUTCDate()}\##{generate_time_string d}"
-      description: v.reverse().map((v) -> render_item v).join "\n"
-      date: new Date title.date
+      description:
+        v.map (msg) ->
+          render_item msg, room
+        .join "\n"
+      date: d

   robot.router.get "/#{base_path}/:room/feed", (req, res) ->
     feed = new Feed


using object pool for TrigPairs to reduce actual garbage collection

--- evolution.coffee
+++ evolution.coffee
@@ -21,6 +21,7 @@
     @height = @canvas.height
     @maxDim = Math.max @width, @height
     @things = []
+    @trigPool = []
     @dotCache = {}
     @idBase = 0
     @tick   = 0
@@ -97,13 +98,18 @@
     nextThing()
   remThing: (thing) ->
     thing.dead = true
+    for k, v of thing.others
+      @trigPool.push v
     for i in [0...@things.length]
       t = @things[i]
       if t.id == thing.id
         @things.splice i, 1
         break
     for t in @things
-      delete t.others[thing.id]
+      v = t.others[thing.id]
+      if v?
+        @trigPool.push v unless v.tooFar
+        delete t.others[thing.id]
   # moves a thing based on that things velocity, handling reflection off
   # the edges of the universe
   moveThing: (thing) ->
@@ -130,7 +136,9 @@
     return unless thing.angle?
     theta = anglify vx, vy
     thing.angle = theta + thing.jitter()
-    delete thing.marges
+    if thing.marges
+      while thing.marges.length
+        @trigPool.push thing.marges.pop()
   # chooses a random location which does not currently contain anything
   randomLocation: (used) ->
     h = @height
@@ -147,13 +155,23 @@
   run: ->
     @running = true
     @go()
+  tp: ( x, y, minDistance, distance ) ->
+    p = @trigPool.pop() || new TrigPair()
+    p.init( x, y, minDistance, distance )
+    p
   # calculate and store the geometric relationship between two things
   trig: ( t, o ) ->
     base = o.x - t.x
     height = o.y - t.y
-    tp = new TrigPair(base, height, @minDistance)
-    t.others[o.id] = tp
-    o.others[t.id] = tp.opposite()
+    ti = t.id
+    oi = o.id
+    t1 = t.others[oi]
+    t2 = o.others[ti]
+    @trigPool.push t1 if t1?
+    @trigPool.push t2 if t2? && !t2.tooFar
+    tp = @tp(base, height, @minDistance)
+    t.others[oi] = tp
+    o.others[ti] = tp.opposite(@)
   # finds the things within a particular distance of a reference thing and within a certain
   # wedge around the direction of the things movement
   # if the thing has a visual angle of 1, or the thing is motionless, all things within the distance are returned
@@ -363,7 +381,7 @@

 # for easy angle comparison without converting sines and cosines to angles
 class TrigPair
-  constructor: ( x, y, minDistance, distance ) ->
+  init: ( x, y, minDistance, distance ) ->
     if distance?
       sine = @sine = x
       cosine = @cosine = y
@@ -402,9 +420,9 @@
   vector: (magnitude) ->
     [ @width * magnitude, @height * magnitude ]
   # generates the counterpart of @ TrigPair after a 180 degree rotation
-  opposite: ->
+  opposite: (u) ->
     return @ if @tooFar
-    op = new TrigPair( -@sine, -@cosine, null, @distance )
+    op = u.tp( -@sine, -@cosine, null, @distance )
     op.width = -@width
     op.height = -@height
     op
@@ -457,13 +475,15 @@
   touching: ->
     @universe.near @, @radius, 1
   margins: ->
-    @marges ||= do =>
-      fi = Math.PI * @visualAngle() / 2
-      t1 = @angle - fi
-      t1 = new TrigPair( Math.sin(t1), Math.cos(t1) )
-      t2 = @angle + fi
-      t2 = new TrigPair( Math.sin(t2), Math.cos(t2) )
-      [ t1, t2 ]
+    m = @marges ||= []
+    fi = Math.PI * @visualAngle() / 2
+    t1 = @angle - fi
+    t1 = @universe.tp( Math.sin(t1), Math.cos(t1) )
+    t2 = @angle + fi
+    t2 = @universe.tp( Math.sin(t2), Math.cos(t2) )
+    m.push t1
+    m.push t2
+    m

 class Stone extends Thing
   constructor: ( location, options = {} ) ->


don't flash the spinner when we refresh

don't flash the spinner when we refresh

--- js/app.coffee
+++ js/app.coffee
@@ -210,8 +210,8 @@
       @builds((new CI.inner.Build d for d in data))
       @builds_have_been_loaded(true)

-  loadRecentBuilds: () =>
-    @loadBuilds('/api/v1/recent-builds')
+  loadRecentBuilds: (refresh) =>
+    @loadBuilds('/api/v1/recent-builds', refresh)

   loadOrg: (username, refresh) =>
     if !@projects_have_been_loaded() then @loadProjects()
@@ -375,7 +375,6 @@

   loadAdminRecentBuilds: (refresh) =>
-
     @loadBuilds '/api/v1/admin/recent-builds', refresh
     if not refresh
       @renderAdminPage "recent_builds"
@@ -462,7 +461,8 @@

   @get '^/', (cx) =>
     VM.selected
-      refresh_fn: VM.loadRecentBuilds
+      refresh_fn: =>
+        VM.loadRecentBuilds(true)
       mention_branch: true
       favicon_updator: VM.reset_favicon


Test:Collaboration fixes coffeelint error

--- client/test/lib/collaboration/start.coffee
+++ client/test/lib/collaboration/start.coffee
@@ -21,7 +21,6 @@

       browser.waitForElementVisible vmSelector, 20000
       browser.waitForElementVisible '.SidebarMachinesListItem.Running .SidebarListItem-icon', 30000
-
       collaborationHelpers.waitParticipantLeaveAndEndSession(browser)
       browser.end()

@@ -29,7 +28,6 @@
     participantCallback = ->

       browser.waitForElementVisible '.SidebarMachinesListItem.NotInitialized .SidebarListItem-icon', 20000
-
       collaborationHelpers.leaveSession(browser)
       browser.end()


Added assert element present/not present

--- client/test/lib/teams/teamschannels.coffee
+++ client/test/lib/teams/teamschannels.coffee
@@ -54,9 +54,10 @@
     otherChannelsTabSelector = '.ChannelListWrapper .ChannelList-tabs .ChannelList-tab:nth-of-type(2)'
     activeTabSelector        = '.ChannelListWrapper .ChannelList-tabs .ChannelList-tab.active-tab'
     otherChannelsJoinButton  = '.PublicChannelLink.ChannelListItem .Button'
-
-    user = teamsHelpers.loginTeam(browser)
-    teamsHelpers.createChannel(browser, user)
+    channelListSelector      = '.PublicChannelListModal .ChannelListWrapper .SidebarModalThreads'
+
+    user        = teamsHelpers.loginTeam(browser)
+    channelName = teamsHelpers.createChannel(browser, user)
     teamsHelpers.leaveChannel(browser)

     browser
@@ -65,11 +66,15 @@
       .waitForElementVisible  channelTextSelector, 20000
       .assert.containsText    channelTextSelector, 'Channels'
       .waitForElementVisible  otherChannelsTabSelector, 20000
+      .waitForElementVisible  '.PublicChannelListModal .ChannelListWrapper .SidebarModalThreads', 20000
+      .expect.element(channelListSelector).text.to.not.contain(channelName)
+    browser
       .click                  otherChannelsTabSelector
       .waitForElementVisible  activeTabSelector, 20000
       .waitForElementVisible  otherChannelsJoinButton, 20000
       .assert.containsText    otherChannelsJoinButton, 'JOIN'
-      .end()
+      .expect.element(channelListSelector).text.to.contain(channelName)
+    browser.end()

   leaveChannel: (browser) ->


Refactor filters

Refactor filters

--- clients/antranet/app/coffee/filters.coffee
+++ clients/antranet/app/coffee/filters.coffee
@@ -1,52 +1,71 @@
 # Filters
-angular.module('antranet.filters', [])
-    .filter('interpolate', ['version', (version) ->
-        return (text) ->
-            return String(text).replace(/\%VERSION\%/mg, version)
-    ])
-    .filter('getTotals', ['version', (imputations) ->
-        return (imputations) ->
-            totals = {}

-            if (not imputations?)
-                return []
+module = angular.module('antranet.filters', [])

-            firstProjectId = _.keys(imputations)[0]
-            _.each _.keys(imputations[firstProjectId]), (id) ->
+
+interpolate = ->
+    return (text) ->
+        return String(text).replace(/\%VERSION\%/mg, version)
+
+module.filter("interpolate", interpolate)
+
+
+getTotals = ->
+    return (imputations) ->
+        totals = {}
+
+        if (not imputations?)
+            return []
+
+        firstProjectId = _.keys(imputations)[0]
+        _.each _.keys(imputations[firstProjectId]), (id) ->
+            # Removing angular private attributes
+            if id[0] != '$'
+                totals[id] = 0
+
+        for project in _.values(imputations)
+            _.each project, (hours, id) ->
                 # Removing angular private attributes
                 if id[0] != '$'
-                    totals[id] = 0
+                    totals[id] += parseInt(hours, 10)

-            for project in _.values(imputations)
-                _.each project, (hours, id) ->
-                    # Removing angular private attributes
-                    if id[0] != '$'
-                        totals[id] += parseInt(hours, 10)
+        return totals

-            return totals
-    ])
-    .filter('sumDict', ['version', (dict) ->
-        return (dict) ->
-            return _.reduce(_.values(dict), (sum, num) -> parseInt(sum, 10) + parseInt(num, 10))
-    ])
-    .filter('mdate', ['version', (date, format) ->
-        return (date, format) ->
-            if date
-                return moment(date).format(format)
-            else
-                return ""
-    ])
-    .filter('mfromNow', ['version', (date, without_suffix) ->
-        return (date, without_suffix) ->
-            if date
-                return moment(date).fromNow(without_suffix or false)
-            else
-               return ""
-    ])
-    .filter('nl2br', ['version', (msg, is_xhtml) =>
-        return (msg, is_xhtml) =>
-            breakTag = if is_xhtml? then '
' else '
'
+module.filter("getTotals", getTotals)

-            msg = "#{msg}".replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1#{breakTag}$2")
-            return msg
-    ])
+
+sumDict = ->
+    return (dict) ->
+        return _.reduce(_.values(dict), (sum, num) -> parseInt(sum, 10) + parseInt(num, 10))
+
+module.filter("sumDict", sumDict)
+
+
+mdate = ->
+    return (date, format) ->
+        if date
+            return moment(date).format(format)
+        else
+            return ""
+
+module.filter("mdate", mdate)
+
+
+mfromNow = ->
+    return (date, without_suffix) ->
+        if date
+            return moment(date).fromNow(without_suffix or false)
+        else
+           return ""
+
+module.filter("mfromNow", mfromNow)
+
+
+nl2br = ->
+    return (msg, is_xhtml) ->
+        breakTag = if is_xhtml? then '
' else '
'
+
+        msg = "#{msg}".replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1#{breakTag}$2")
+        return msg
+
+module.filter("nl2br", nl2br)


Add guard for controller in ActiveRoute.path function

--- lib/activeroute.coffee
+++ lib/activeroute.coffee
@@ -73,7 +73,7 @@

     if ir
       controller = ir.Router.current()
-      currentPath = controller?.location.get().path if controller.route
+      currentPath = controller?.location.get().path if controller?.route

     if fr
       fr.FlowRouter.watchPathChange()


escape the backslash

escape the backslash

--- scripts/fundraising.coffee
+++ scripts/fundraising.coffee
@@ -22,7 +22,7 @@
           robot.send "I had trouble fetching the total, try again later?"
         else
           data = JSON.parse body
-          robot.send "We've raised $#{data.sum} \o/"
+          robot.send "We've raised $#{data.sum} \\o/"

   robot.respond /fundraising/i, getTotal
   robot.respond /show me the money/i, getTotal


Fix a bug that caused markdown rendering to fail if gfm code language was set to something not supported

--- src/plugins/markdown-page.coffee
+++ src/plugins/markdown-page.coffee
@@ -26,11 +26,14 @@
   for token in tokens
     switch token.type
       when 'code'
-        if token.lang?
-          token.text = hljs.highlight(token.lang, token.text).value
-        else
-          token.text = hljs.highlightAuto(token.text).value
-        token.escaped = true
+        try
+          if token.lang?
+            token.text = hljs.highlight(token.lang, token.text).value
+          else
+            token.text = hljs.highlightAuto(token.text).value
+          token.escaped = true
+        catch error
+          # hljs.highlight fails if lang is unknown

   return marked.parser tokens


fix indent

fix indent

--- sample/core.config.coffee
+++ sample/core.config.coffee
@@ -9,7 +9,7 @@
     cookie_time: 30 * 24 * 3600 * 1000

   i18n:
-		version: '0,1'
+    version: '0,1'
     available_language: ['zh_CN']
     default_language: 'zh_CN'
     default_timezone: 'Asia/Shanghai'


Use proxy_pass'd api

Use proxy_pass'd api

--- app/main.coffee
+++ app/main.coffee
@@ -35,7 +35,10 @@
 siteHeader.el.appendTo document.body

 Api = require 'zooniverse/lib/api'
-api = new Api project: 'milky_way'
+api = if window.location.hostname is 'www.milkywayproject.org'
+  new Api project: 'milky_way', host: 'http://www.milkywayproject.org', path: '/_ouroboros_api/proxy'
+else
+  new Api project: 'milky_way'

 TopBar = require 'zooniverse/controllers/top-bar'
 topBar = new TopBar


Reenabled autorotation for carousel

Reenabled autorotation for carousel

--- src/coffee/presets.coffee
+++ src/coffee/presets.coffee
@@ -13,7 +13,7 @@

     carousel:
       autoRotation:
-        enabled: false
+        enabled: true
         direction: 1
         once: false
         delay: 5000


:lipstick:

:lipstick:

--- lib/wrap-guide-view.coffee
+++ lib/wrap-guide-view.coffee
@@ -29,14 +29,13 @@
     return @getDefaultColumn() unless Array.isArray(customColumns)
     for customColumn in customColumns when typeof customColumn is 'object'
       {pattern, scope, column} = customColumn
-      continue unless pattern or scope
-      if pattern
+      if pattern?
         try
           regex = new RegExp(pattern)
         catch
           continue
         return parseInt(column) if regex.test(path)
-      else
+      else if scope?
         return parseInt(column) if scope is scopeName
     @getDefaultColumn()


Add comment on +2

Add comment on +2

--- resources/assets/coffee/react/_components/comment.coffee
+++ resources/assets/coffee/react/_components/comment.coffee
@@ -84,6 +84,8 @@
       parent = store.comments.get(@props.comment.parentId)
       user = @userFor(@props.comment)
       meta = commentableMetaStore.get(@props.comment.commentableType, @props.comment.commentableId)
+      # Only clip if there are at least CLIP_LINES + 2 lines to ensure there are enough contents
+      # being clipped instead of just single lone line (or worse no more lines because of rounding up).
       longContent = @state.lines? && @state.lines.count >= CLIP_LINES + 2

       modifiers = @props.modifiers?[..] ? []


Removed debug.

Removed debug.

--- scripts/screenshots.coffee
+++ scripts/screenshots.coffee
@@ -19,11 +19,7 @@
   robot.router.post '/brian/screenshot/:room', (req, res) ->
     room = req.params.room

-    console.log "SCREENSHOTS: got #{req}, #{res}"
-
-    data = JSON.parse res.body.payload
-    console.log "SCREENSHOTS: parsed to: #{data}"
-
+    data = JSON.parse req.body.payload
     screenshots = data.screenshots ? []

     for screenshot in screenshots
@@ -31,6 +27,11 @@

       if screenshot_url
         robot.messageRoom room, "#{screenshot.url} in #{screenshot.browser} #{screenshot.browser_version} running under #{screenshot.os} #{screenshot.os_version}: \n#{screenshot_url}"
+
+    robot.messageRoom room, "Done."
+
+    res.writeHead 204, { 'Content-Length': 0 }
+    res.end()

   robot.respond /screenshot( me)? (.*?) under (.*)/i, (msg) ->
     try
@@ -50,5 +51,4 @@
   msg.http(api_url)
     .auth(username, api_key)
     .post(QS.stringify(q)) (err, res, body) ->
-      console.log res, body
       cb()


fix(i18n): fix indentation of one line in App.coffee

--- src/components/App.coffee
+++ src/components/App.coffee
@@ -210,7 +210,7 @@
                         dispatch Actions.cancelRating()
                       ),
                         i className: "fa fa-ban"
-			 t "entryForm.cancel"
+            			      t "entryForm.cancel"
                     li
                       key: "save"
                       className:"pure-u-1-2",


Remove command payload from stored domain events #33

--- src/repository/repository.coffee
+++ src/repository/repository.coffee
@@ -132,7 +132,6 @@

     # TODO: this should be an transaction to guarantee consistency
     @_eventric.eachSeries domainEvents, (domainEvent, next) =>
-      domainEvent.command = @_command
       @_store.saveDomainEvent domainEvent
       .then =>
         @_eventric.log.debug "Saved DomainEvent", domainEvent


Warn when scroll view gets accidentally scrolled and fix it

I want this code to go away once we track down the causes of any
unwanted autoscrolling by the browser

--- src/editor-scroll-view-component.coffee
+++ src/editor-scroll-view-component.coffee
@@ -44,6 +44,11 @@
   componentDidMount: ->
     @getDOMNode().addEventListener 'overflowchanged', @onOverflowChanged
     window.addEventListener('resize', @onWindowResize)
+
+    node.addEventListener 'scroll', ->
+      console.warn "EditorScrollView scroll position changed, and it shouldn't have. If you can reproduce this, please report it."
+      node.scrollTop = 0
+      node.scrollLeft = 0

     @measureHeightAndWidth()


Add in aspell check

Add in aspell check

--- server-ce/Gruntfile.coffee
+++ server-ce/Gruntfile.coffee
@@ -116,9 +116,11 @@
 		Helpers.checkS3 @async()
 	grunt.registerTask "check:fs", "Check that local filesystem options are configured", () ->
 		Helpers.checkFS @async()
+	grunt.registerTask "check:aspell", "Check that aspell is installed", () ->
+		Helpers.checkAspell @async()
 	grunt.registerTask "check:make", "Check that make is installed", () ->
 		Helpers.checkMake @async()
-	grunt.registerTask "check", "Check that you have the required dependencies installed", ["check:redis", "check:latexmk", "check:s3", "check:fs"]
+	grunt.registerTask "check", "Check that you have the required dependencies installed", ["check:redis", "check:latexmk", "check:s3", "check:fs", "check:aspell"]

 	grunt.registerTask "build_deb", "Build an installable .deb file from the current directory", () ->
 		Helpers.buildDeb @async()
@@ -235,6 +237,33 @@
 							"""
 							error = new Error("latexmk is too old")
 				callback(error)
+
+		checkAspell: (callback = (error) ->) ->
+			grunt.log.write "Checking aspell is installed... "
+			exec "aspell dump dicts", (error, stdout, stderr) ->
+				if error? and error.message.match("command not found")
+					grunt.log.error "FAIL."
+					grunt.log.errorlns """
+					Either aspell is not installed or is not in your PATH.
+
+					On Ubuntu you can install aspell with:
+						sudo apt-get install aspell
+
+					Or on a mac:
+						brew install aspell
+
+					This is not a fatal error, but the spell-checker will not work
+					without aspell
+					"""
+					return callback(error)
+				else if error?
+					return callback(error)
+				else
+					grunt.log.writeln "OK."
+					grunt.log.writeln "The following spell check dictionaries are available:"
+					grunt.log.write stdout
+					callback()
+				callback(error)

 		checkS3: (callback = (error) ->) ->
 			Settings = require "settings-sharelatex"
@@ -269,7 +298,7 @@
 						Could not connect to Amazon S3. Please check your credentials.
 						"""
 					else
-						grunt.log.write "OK."
+						grunt.log.writeln "OK."
 					callback()
 			else
 				grunt.log.writeln "Filestore other than S3 configured. Not checking S3."


Fix bug in the callback

Fix bug in the callback

--- lms/static/coffee/src/peer_grading/peer_grading_problem.coffee
+++ lms/static/coffee/src/peer_grading/peer_grading_problem.coffee
@@ -427,7 +427,7 @@

   setup_score_selection: (max_score) =>
     # And now hook up an event handler again
-    $("input[name='score-selection']").change @graded_callback
+    $("input[class='score-selection']").change @graded_callback


Updated Status.*BodyClass function to take variable number of arguments

--- app/assets/javascripts/sprangular/services/status.coffee
+++ app/assets/javascripts/sprangular/services/status.coffee
@@ -20,14 +20,24 @@
       _.find status.cachedProducts, (product) ->
         product.slug == slug

-    addBodyClass: (klass) ->
-      @bodyClasses[klass] = true
+    # addBodyClass('open', 'focused')
+    addBodyClass: ->
+      @_eachClass arguments, (classes, klass) ->
+        classes[klass] = true

-    removeBodyClass: (klass) ->
-      @bodyClasses[klass] = false
+    # removeBodyClass('open', 'focused')
+    removeBodyClass: ->
+      @_eachClass arguments, (classes, klass) ->
+        classes[klass] = false

-    toggleBodyClass: (klass) ->
-      @bodyClasses[klass] = !@bodyClasses[klass]
+    # toggleBodyClass('open', 'focused')
+    toggleBodyClass: ->
+      @_eachClass arguments, (classes, klass) ->
+        classes[klass] = !classes[klass]
+
+    _eachClass: (args, fn) ->
+      self = this
+      _.each args, (klass) -> fn(self.bodyClasses, klass)

   $rootScope.$on '$routeChangeSuccess', ->
     status.bodyClasses = {default: true}


debug info

debug info

--- app/assets/javascripts/zum/controllers/uploads.js.coffee
+++ app/assets/javascripts/zum/controllers/uploads.js.coffee
@@ -17,21 +17,24 @@
     #TODO: smooth progress raising
     # get real progress every second
     # count current progress based on avg speed
-
+
     object.progress ||= {}

     if object.progress.synced_at
       delta = new Date() - object.progress.synced_at

       if object.progress.speed
-        object.progress.value += delta * object.progress.speed
+        object.progress.value += Math.min(delta * object.progress.speed, 100)

       if delta < 1000
+        console.log object.progress
         return $timeout (->
           get_upload_progress(progress_token, object)
         ), 50

     $http.get('/progress', { headers: { 'X-Progress-ID': progress_token } }).then (response) ->
+
+      console.log "get"

       object.progress.state = response.data.state
       object.progress.requests ||= 0
@@ -49,11 +52,14 @@
       else if response.data.state == "done"
         object.progress.value = 100

-
       if response.data.state == "done" or response.data.state == "error"
+        console.log object.progress
         return
       if object.progress.requests > 5 and !object.progress.value
+        console.log object.progress
         return
+
+      console.log object.progress

       $timeout (->
         get_upload_progress(progress_token, object)


MacroList UI tweak

MacroList UI tweak

--- app/coffee/modules/main/dashboard/views/macroList.coffee
+++ app/coffee/modules/main/dashboard/views/macroList.coffee
@@ -97,7 +97,7 @@
       return { active_position }

     else if @model.get('key_up')
-      active_position = { position: 256, css: 'fa-arrow-up', tooltip: 'Wait for key release' }
+      active_position = { position: 255, css: 'fa-arrow-up', tooltip: 'Wait for key release' }
       return { active_position }

     else


fixed word-report template naming

fixed word-report template naming

--- server/methods/word.coffee
+++ server/methods/word.coffee
@@ -217,7 +217,7 @@
         val.reference = Reference.findOne(_id: val.referenceID)
         share.setGenotoxWordFields(val)

-    d.nonMammalianInVitro = _.filter(vals, (v) -> return v.dataClass == "Non-mammalian")
+    d.nonMammalian = _.filter(vals, (v) -> return v.dataClass == "Non-mammalian")
     d.mammalianInVitro = _.filter(vals, (v) -> return v.dataClass == "Mammalian and human in vitro")
     d.animalInVivo = _.filter(vals, (v) -> return v.dataClass == "Animal in vivo")
     d.humanInVivo = _.filter(vals, (v) -> return v.dataClass == "Human in vivo")


bug exists if a top-level object is undefined

--- server/publications.coffee
+++ server/publications.coffee
@@ -72,7 +72,7 @@
           new_collection = new Meteor.Collection(name)
         catch e
           for key, value of root
-            if name == value._name # TODO here - typecheck also?
+            if name == value?._name # TODO here - typecheck also?
               new_collection = value

         if new_collection?  # found it!


no directory changing

no directory changing

--- monocat.coffee
+++ monocat.coffee
@@ -5,7 +5,6 @@

 args = process.argv
-initialDir = process.cwd()
 $ = fileName = null
 len = completed = 0
 log        = console.log.bind   console, '\x1b[32m  '
@@ -22,7 +21,6 @@
   fileName = splitPath.pop()
   dir = splitPath.join '/'

-  process.chdir dir if dir

   fs.readFile fileName, (err, data) ->
     if err
@@ -61,7 +59,6 @@

 deliver = ->
   return unless completed is len
-  process.chdir initialDir
       else
         deliver() if ++completed is total


Remove the bio field and the track record field from where it isn't necessary

--- server/publications.coffee
+++ server/publications.coffee
@@ -1,13 +1,24 @@
-userFields =
-  roles: 1
-  profile: 1
-  'services.google.picture': 1
-  'services.google.email': 1
-  'services.facebook.id': 1
-  'services.facebook.email': 1
-  'services.twitter.profile_image_url': 1
-  'services.github.picture': 1
-  'services.github.email': 1
+userFields = (fields) ->
+  baseline =
+    roles: 1
+    'profile.editing': 1
+    'profile.name': 1
+    'profile.role': 1
+    'profile.submitted': 1
+    'services.google.picture': 1
+    'services.google.email': 1
+    'services.facebook.id': 1
+    'services.facebook.email': 1
+    'services.twitter.profile_image_url': 1
+    'services.github.picture': 1
+    'services.github.email': 1
+  _.extend(baseline, fields)
+
+
+
+# removeBio = (userFields()) ->
+#   cloned = _.extend({}, userFields())
+#   _.extend(cloned, {'profile.bio': 0})

 proposalFields = (userId, minimal)->
   fields =
@@ -35,19 +46,20 @@
   query = {} if not query
   wishes = Wishes.find(_.extend(query, notDeletedPred), options)
   # This can be improved to add only the users that commented or voted or created on the relevant wishes
-  users = User.find({}, {fields: userFields})
+  users = User.find({}, {fields: userFields()})
   [wishes, users]

 Meteor.publish "users",(query, options) ->
   options = {} if not options
   query = {} if not query
-  User.find(query, _.extend(options, {fields: userFields}))
+  User.find(query, _.extend(options, {fields: userFields()}))

 speakerPred = 'profile.submitted': true
 Meteor.publish "speakers", (query, options) ->
   options = {} if not options
   query = {} if not query
-  users = User.find(_.extend(query, speakerPred), _.extend(options, {fields: userFields}))
+  users = User.find(_.extend(query, speakerPred), _.extend(options,
+    {fields: userFields('profile.bio': 1, 'profile.trackRecord': 1)}))
   userIds = users.map((u)->u._id)
   proposals = Proposal.find(_.extend({speaker_ids: {$in: userIds}}, notDeletedPred),
                             {fields: proposalFields(@userId)})
@@ -55,14 +67,14 @@

 Meteor.publish "moderators", ->
   User.find {'roles.moderator': true},
-    fields: userFields
+    fields: userFields('profile.bio': 1)

 Meteor.publish "proposals-min", (query, options) ->
   options = {} if not options
   query = {} if not query
   proposals = Proposal.find(_.extend(query, notDeletedPred), _.extend(options, {fields: proposalFields(@userId, true)}))
   userIds = _.flatten(proposals.map((p) -> p.speaker_ids))
-  users = User.find({_id: $in: userIds}, {fields: userFields})
+  users = User.find({_id: $in: userIds}, {fields: userFields()})
   [proposals, users]

 Meteor.publish "proposals", (query, options) ->
@@ -70,7 +82,7 @@
   query = {} if not query
   proposals = Proposal.find(query, _.extend(options, {fields: proposalFields(@userId)}))
   userIds = _.flatten(proposals.map((p) -> p.speaker_ids))
-  users = User.find({_id: $in: userIds}, {fields: userFields})
+  users = User.find({_id: $in: userIds}, {fields: userFields()})
   [proposals, users]

 Meteor.publish "agenda", ->
@@ -78,7 +90,7 @@
   proposalIds = _.uniq(_.compact(_.flatten(items.map((i) -> [i.class1, i.class2, i.class3]))))
   proposals = Proposal.find({_id: $in: proposalIds}, {fields: proposalFields(@userId, true)})
   userIds = _.flatten(proposals.map((p) -> p.speaker_ids))
-  users = User.find({_id: $in: userIds}, {fields: userFields})
+  users = User.find({_id: $in: userIds}, {fields: userFields()})
   [items, proposals, users]

 Meteor.publish "sponsors", (query, options) ->


Success function optimized

Success function optimized

--- server/tools/mailer.coffee
+++ server/tools/mailer.coffee
@@ -54,7 +54,7 @@
     mailsender.sendMail mailOptions, (error) ->
       if error and onError?
         onError(error)
-      else if onSuccess?
+      else if onSuccess? and onSuccess?
         onSuccess()

   module.exports = Mailer


Add missing import.

Add missing import.

--- application/chrome/content/wesabe/download/Player.coffee
+++ application/chrome/content/wesabe/download/Player.coffee
@@ -4,6 +4,7 @@
 date           = require 'lang/date'
 {trim}         = require 'lang/string'
 func           = require 'lang/func'
+type           = require 'lang/type'
 event          = require 'util/event'
 dateForElement = (require 'dom/date').forElement
 dir            = require 'io/dir'


PostSettings: Add temporary showNotification and showErrorNotification functions to KD.extend

--- client/Main/KD.extend.coffee
+++ client/Main/KD.extend.coffee
@@ -314,6 +314,37 @@
       error err
     err?

+  showNotification: (message, options = {})->
+    return  if not message or message is ""
+
+    # TODO these css/type parameters will be changed according to error type
+    type = 'growl'
+
+    options.duration or= 3500
+    options.title      = message
+    # options.css      or= css
+    options.type     or= type
+
+    options.fn message  if options.fn and typeof options.fn? is 'function'
+
+    new KDNotificationView options
+
+  # TODO after error message handling method is decided replace this function
+  # with showError
+  showErrorNotification: (err, options = {}) ->
+    {message, name} = err  if err
+
+    switch name
+      when 'AccessDenied'
+        options.fn = warn
+        options.type = 'growl'
+        message = options.userMessage
+      else
+        options.userMessage = "Error, please try again later!"
+        options.fn = error
+
+    @showNotification message, options
+
   getPathInfo: (fullPath)->
     return no unless fullPath
     path      = FSHelper.plainPath fullPath


client/ide: fix route handlers for test vm

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -130,8 +130,9 @@
   machine = remote.revive machine
   workspace = remote.revive workspaces[0]

-  return loadIDE { machine, workspace, username: nick }, ->
-    require('ide/test/browser').prepare(machine, workspace)
+  require('app/util/createTestMachine')().then ->
+    loadIDE { machine, workspace, username: nick }, ->
+      require('ide/test/browser').prepare(machine, workspace)

 routeToFallback = ->
@@ -266,7 +267,7 @@
       { params } = info

       if params.workspaceSlug is 'test-workspace'
-        kd.utils.defer -> require('app/util/createTestMachine')().then loadTestIDE
+        return loadTestIDE()

       dataProvider.fetchMachine params.machineLabel, (machine) ->


restore webrtc code removed by recent merge

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -243,12 +243,67 @@
 	selfVideoUrl: ->
 		return Session.get('selfVideoUrl')

+	flexOpenedRTC1: ->
+		console.log 'room.helpers flexOpenedRTC1' if window.rocketDebug
+		return 'layout1' if Session.equals('flexOpenedRTC1', true)
+
+	flexOpenedRTC2: ->
+		console.log 'room.helpers flexOpenedRTC2' if window.rocketDebug
+		return 'layout2' if Session.equals('flexOpenedRTC2', true)
+	rtcLayout1: ->
+		return (Session.get('rtcLayoutmode') == 1 ? true: false);
+
+	rtcLayout2: ->
+		return (Session.get('rtcLayoutmode') == 2 ? true: false);
+
+	rtcLayout3: ->
+		return (Session.get('rtcLayoutmode') == 3 ? true: false);
+
+	noRtcLayout: ->
+		return (!Session.get('rtcLayoutmode') || (Session.get('rtcLayoutmode') == 0) ? true: false);
+
+

 Template.room.events

 	"click .flex-tab .more": (event) ->
 		console.log 'room click .flex-tab .more' if window.rocketDebug
-		Session.set('flexOpened', !Session.get('flexOpened'))
+		if (Session.get('flexOpened'))
+			Session.set('rtcLayoutmode', 0)
+			Session.set('flexOpened',false)
+		else
+			Session.set('flexOpened', true)
+
+
+	"click .flex-tab  .video-remote" : (e) ->
+		console.log 'room click .flex-tab .video-remote' if window.rocketDebug
+		if (Session.get('flexOpened'))
+			if (!Session.get('rtcLayoutmode'))
+				Session.set('rtcLayoutmode', 1)
+			else
+				t = Session.get('rtcLayoutmode')
+				t = (t + 1) % 4
+				console.log  'setting rtcLayoutmode to ' + t  if window.rocketDebug
+				Session.set('rtcLayoutmode', t)
+
+	"click .flex-tab  .video-self" : (e) ->
+		console.log 'room click .flex-tab .video-self' if window.rocketDebug
+		if (Session.get('rtcLayoutmode') == 3)
+			console.log 'video-self clicked in layout3' if window.rocketDebug
+			i = document.getElementById("fullscreendiv")
+			if i.requestFullscreen
+				i.requestFullscreen()
+			else
+				if i.webkitRequestFullscreen
+					i.webkitRequestFullscreen()
+				else
+					if i.mozRequestFullScreen
+						i.mozRequestFullScreen()
+					else
+						if i.msRequestFullscreen
+							i.msRequestFullscreen()
+
+

 	'click .chat-new-messages': (event) ->
 		console.log 'room click .chat-new-messages' if window.rocketDebug
@@ -268,7 +323,7 @@
 		event.preventDefault()
 		Meteor.call 'joinRoom', FlowRouter.getParam('_id')

-	"click .burger": ->
+	"click .burger": ->
 		console.log 'room click .burger' if window.rocketDebug
 		chatContainer = $("#rocket-chat")
 		if chatContainer.hasClass("menu-closed")


#95 possibly fixed. by waiting editorElement attached to DOM on initial start

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -173,14 +173,27 @@
           @startSyncToEditor(item)

   start: ->
+    # When initial getItems() take very long time, it means refresh get delayed.
+    # In this case, user see modified icon(mark) on tab.
+    # Explicitly setting modified start here prevent this
+    @setModifiedState(false)
+
+    attachedPromise = new Promise (resolve) =>
+      disposable = @editorElement.onDidAttach ->
+        disposable.dispose()
+        resolve()
+
     activatePaneItemInAdjacentPane(@editor, split: settings.get('directionToOpen'))
-    @grammar.activate()
-    if @input
-      @setPrompt(@input)
-    else
-      @withIgnoreChange => @setPrompt(@input)
-    @moveToPrompt()
-    @refresh()
+
+    attachedPromise.then =>
+      @grammar.activate()
+      if @input
+        @setPrompt(@input)
+      else
+        @withIgnoreChange => @setPrompt(@input)
+      @moveToPrompt()
+      @refresh()
+

   getPane: ->
     paneForItem(@editor)


assignment gotcha

assignment gotcha

--- src/core/container.coffee
+++ src/core/container.coffee
@@ -211,8 +211,8 @@
       # if you define a @getter property as a string on your component
       # we will create a function with that name on this container that
       # allows you to access this component
-      if getter = component.getter and not @[ getter ]?
-        @[getter] = ()-> component
+      if getter = component.getter
+        @[getter] ||= ()-> component

       # if we're using base backbone views, then they don't extend themselves
       # with their passed options, so this is a workaround to get them to


Input type password added to data-bind

Input type password added to data-bind

--- src/cuffs/bindings.coffee
+++ src/cuffs/bindings.coffee
@@ -117,6 +117,7 @@

         @bind 'data-bind'

+        textTypes: ['text','hidden', 'email']
         constructor: (@node)->
             @type = @node.type
             super @node
@@ -127,8 +128,10 @@
             else if not @type or @type == ""?
                 if value?
                     @node.innerHTML = value
-            else if @type == 'text' or @type == "hidden"
+            else if @type in @textTypes
                 @node.value = value
+            else if @type == 'password'
+                @node.value = value or ''
             else if @type == 'checkbox'
                 $(@node).attr 'checked', value
             this
@@ -138,7 +141,9 @@
                 $(@node).val()
             else if not @type or @type == ""?
                 $(@node).html()
-            else if @type == 'text' or @type == "hidden"
+            else if @type in @textTypes
+                @node.value
+            else if @type == 'password'
                 @node.value
             else if @type == 'checkbox'
                 $(@node).is ':checked'


changed "exit 1" to "process.exit 1"

changed "exit 1" to "process.exit 1"

--- src/apl.coffee
+++ src/apl.coffee
@@ -58,7 +58,7 @@
     file = null
     for arg in process.argv[2...]
       if arg in ['-h', '--help'] then (process.stderr.write usage; process.exit 0)
-      else if /^-/.test arg then (process.stderr.write "unrecognized option: #{arg}\n#{usage}"; exit 1)
+      else if /^-/.test arg then (process.stderr.write "unrecognized option: #{arg}\n#{usage}"; process.exit 1)
       else if file? then (process.stderr.write usage; process.exit 1)
       else file = arg
     if file?


space

space

--- lib/Repl/ReplFormat.coffee
+++ lib/Repl/ReplFormat.coffee
@@ -10,4 +10,4 @@
         @cmd = cmd
         @args = args
         @prompt = prompt
-        @endSequence = endSequence
+        @endSequence = endSequence


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_physics.coffee
+++ src/engine_physics.coffee
@@ -6,6 +6,7 @@
   world = new Box2D.Dynamics.b2World(new Box2D.Common.Math.b2Vec2(I.gravity.x, I.gravity.y), true)

   pendingCollisions = []
+  pendingDestructions = []

   world.SetContactListener
     BeginContact: (contact) ->
@@ -27,14 +28,25 @@

     pendingCollisions = []

+  destroyPhysicsBodies = () ->
+    pendingDestructions.each (body) ->
+      world.DestroyBody(body)
+
+    pendingDestructions = []
+
   self.bind "update", ->
     world.Step(1 / I.FPS, 10, 10)
     world.ClearForces()

     fireCollisionEvents()
+    destroyPhysicsBodies()

   self.bind "beforeAdd", (entityData) ->
     entityData.world = world

+  self.bind "afterAdd", (object) ->
+    object.bind "destroy", ->
+      pendingDestructions.push object.body()
+
   return {}


Getting Physical

Getting Physical

--- src/engine_physics.coffee
+++ src/engine_physics.coffee
@@ -1,13 +1,37 @@
 Engine.Box2D = (I, self) ->
   $.reverseMerge I,
     scale: 0.1
-    gravity: new Box2D.Common.Math.b2Vec2(0, 40)
+    gravity: Point(0, 98)

-  world = new Box2D.Dynamics.b2World(I.gravity, true)
+  world = new Box2D.Dynamics.b2World(new Box2D.Common.Math.b2Vec2(I.gravity.x, I.gravity.y), true)
+
+  pendingCollisions = []
+
+  world.SetContactListener
+    BeginContact: (contact) ->
+      a = contact.GetFixtureA().GetBody().GetUserData()
+      b = contact.GetFixtureB().GetBody().GetUserData()
+
+      pendingCollisions.push([a, b])
+
+    EndContact: (contact) ->
+    PreSolve: (contact, oldManifold) ->
+    PostSolve: (contact, impulse) ->
+
+  fireCollisionEvents = () ->
+    pendingCollisions.each (event) ->
+      [a, b] = event
+
+      a.trigger "collision", b
+      b.trigger "collision", a
+
+    pendingCollisions = []

   self.bind "update", ->
     world.Step(1 / I.FPS, 10, 10)
     world.ClearForces()
+
+    fireCollisionEvents()

   self.bind "beforeAdd", (entityData) ->
     entityData.world = world


Fail on windows if specs fail

Fail on windows if specs fail

--- build/tasks/spec-task.coffee
+++ build/tasks/spec-task.coffee
@@ -105,7 +105,4 @@
       failures.push "atom core" if coreSpecFailed

       grunt.log.error("[Error]".red + " #{failures.join(', ')} spec(s) failed") if failures.length > 0
-      if process.platform is 'win32'
-        done() # TODO remove once all specs consistently pass
-      else
-        done(!coreSpecFailed and failedPackages.length == 0)
+      done(!coreSpecFailed and failedPackages.length == 0)


Fixed the Couchbase server to shutdown the connection!

--- server/src/Component/Builtin/DataSource/Couchbase.coffee
+++ server/src/Component/Builtin/DataSource/Couchbase.coffee
@@ -29,6 +29,6 @@

     # Close the database connection
     destroy: ->
-        @_db?.shutdown()?
+        @_db?.disconnect()?

 module.exports = Couchbase


Unify logging

Unify logging

--- services/real-time/app/coffee/WebsocketController.coffee
+++ services/real-time/app/coffee/WebsocketController.coffee
@@ -38,6 +38,7 @@
 			client.set("login_count", user?.loginCount)

 			callback null, project, privilegeLevel, WebsocketController.PROTOCOL_VERSION
+			logger.log {user_id, project_id, client_id: client.id}, "user joined project"

 			# No need to block for setting the user as connected in the cursor tracking
 			ConnectedUsersManager.updateUserPosition project_id, client.id, user, null, () ->
@@ -72,13 +73,12 @@

 	joinDoc: (client, doc_id, fromVersion = -1, callback = (error, doclines, version, ops) ->) ->
 		Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
+			return callback(error) if error?
+			return callback(new Error("no project_id found on client")) if !project_id?
 			logger.log {user_id, project_id, doc_id, fromVersion, client_id: client.id}, "client joining doc"

-		AuthorizationManager.assertClientCanViewProject client, (error) ->
-			return callback(error) if error?
-			client.get "project_id", (error, project_id) ->
+			AuthorizationManager.assertClientCanViewProject client, (error) ->
 				return callback(error) if error?
-				return callback(new Error("no project_id found on client")) if !project_id?
 				DocumentUpdaterManager.getDocument project_id, doc_id, fromVersion, (error, lines, version, ops) ->
 					return callback(error) if error?
 					# Encode any binary bits of data so it can go via WebSockets
@@ -93,6 +93,7 @@
 						escapedLines.push line
 					client.join(doc_id)
 					callback null, escapedLines, version, ops
+					logger.log {user_id, project_id, doc_id, fromVersion, client_id: client.id}, "client joined doc"

 	leaveDoc: (client, doc_id, callback = (error) ->) ->
 		Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
@@ -128,29 +129,31 @@

 	getConnectedUsers: (client, callback = (error, users) ->) ->
 		Utils.getClientAttributes client, ["project_id", "user_id"], (error, {project_id, user_id}) ->
+			return callback(error) if error?
+			return callback(new Error("no project_id found on client")) if !project_id?
 			logger.log {user_id, project_id, client_id: client.id}, "getting connected users"
-
-		AuthorizationManager.assertClientCanViewProject client, (error) ->
-			return callback(error) if error?
-			client.get "project_id", (error, project_id) ->
+			AuthorizationManager.assertClientCanViewProject client, (error) ->
 				return callback(error) if error?
-				return callback(new Error("no project_id found on client")) if !project_id?
 				ConnectedUsersManager.getConnectedUsers project_id, (error, users) ->
 					return callback(error) if error?
 					callback null, users
+					logger.log {user_id, project_id, client_id: client.id}, "got connected users"
+

 	applyOtUpdate: (client, doc_id, update, callback = (error) ->) ->
-		AuthorizationManager.assertClientCanEditProject client, (error) ->
-			if error?
-				logger.error {err: error, doc_id, client_id: client.id, version: update.v}, "client is not authorized to make update"
-				setTimeout () ->
-					# Disconnect, but give the client the chance to receive the error
-					client.disconnect()
-				, 100
-				return callback(error)
-
-			Utils.getClientAttributes client, ["user_id", "project_id"], (error, {user_id, project_id}) ->
-				return callback(error) if error?
+		Utils.getClientAttributes client, ["user_id", "project_id"], (error, {user_id, project_id}) ->
+			return callback(error) if error?
+			return callback(new Error("no project_id found on client")) if !project_id?
+			# Omit this logging for now since it's likely too noisey
+			#logger.log {user_id, project_id, doc_id, client_id: client.id, update: update}, "applying update"
+			AuthorizationManager.assertClientCanEditProject client, (error) ->
+				if error?
+					logger.error {err: error, doc_id, client_id: client.id, version: update.v}, "client is not authorized to make update"
+					setTimeout () ->
+						# Disconnect, but give the client the chance to receive the error
+						client.disconnect()
+					, 100
+					return callback(error)
 				update.meta ||= {}
 				update.meta.source = client.id
 				update.meta.user_id = user_id
@@ -165,3 +168,4 @@
 						logger.error {err: error, project_id, doc_id, client_id: client.id, version: update.v}, "document was not available for update"
 						client.disconnect()
 					callback(error)
+					#logger.log {user_id, project_id, doc_id, client_id: client.id}, "applied update"


add gradients back to shaders

add gradients back to shaders

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -145,8 +145,10 @@
     if @shading
       topShader = shader.cloneNode()
       topShader.className = 'oridomi-shader-top'
+      topShader.style.background = '-webkit-linear-gradient(top, rgba(0, 0, 0, .5) 0%, rgba(255, 255, 255, .35) 100%)'
       bottomShader = shader.cloneNode()
       bottomShader.className = 'oridomi-shader-bottom'
+      bottomShader.style.background = '-webkit-linear-gradient(bottom, rgba(0, 0, 0, .5) 0%, rgba(255, 255, 255, .35) 100%)'
       hMask.appendChild topShader
       hMask.appendChild bottomShader


Better integration with Angular models.

Better integration with Angular models.

--- services/web/public/coffee/directives/creditCards.coffee
+++ services/web/public/coffee/directives/creditCards.coffee
@@ -90,6 +90,8 @@
 			month = parseInt(month, 10)
 			year  = parseInt(year, 10)

+			return unless !isNaN(month) and !isNaN(year)
+
 			month: month, year: year

 		return {
@@ -271,8 +273,14 @@

 		parseExpiry = (value) ->
 			if value?
-				dateAsObj = ccUtils.parseExpiry(value);
+				dateAsObj = ccUtils.parseExpiry(value)
+
+				console.log dateAsObj
+
+				return unless dateAsObj?
+
 				expiry = new Date dateAsObj.year, dateAsObj.month - 1
+
 				return $filter('date')(expiry, 'MM/yyyy')

 		# Format CVC


Only import url once

Only import url once

--- lib/package-view.coffee
+++ lib/package-view.coffee
@@ -1,3 +1,4 @@
+url = require 'url'
 Package = require 'package'
 semver = require 'semver'
 packageManager = require './package-manager'
@@ -83,7 +84,7 @@
         repoUrl = @metadata.repository?.url
       if repoUrl
         repoUrl = repoUrl.replace(/.git$/, '')
-        homepage = repoUrl if require('url').parse(repoUrl).host is 'github.com'
+        homepage = repoUrl if url.parse(repoUrl).host is 'github.com'
     if homepage
       @homepage.find('a').attr('href', homepage)
     else


Change require syntax in server api

Change require syntax in server api

--- app/scripts/ServerAPI.coffee
+++ app/scripts/ServerAPI.coffee
@@ -1,10 +1,13 @@
-define ["sp-utils-serverclient","utils/stub","cookies"],(ServerClient, stub ,cookie)->
+define (require)->
+  ServerClient = require "sp-utils-serverclient"
+  stub = require "utils/stub"
+  cookies = require "cookies"

   class ServerClientPatched extends ServerClient
     _ajax: (options, async)->
       unless options.type is 'GET'
         options.headers = {
-          'X-CSRF-Token': cookie.get('CSRF-Token')
+          'X-CSRF-Token': cookies.get('CSRF-Token')
         }
       super options, async


file version fixing

file version fixing

--- server/src/Component/Builtin/DataSource/ElasticSearch.coffee
+++ server/src/Component/Builtin/DataSource/ElasticSearch.coffee
@@ -78,4 +78,4 @@
     createIndex: (dataSource, params, callback) ->
         @client(dataSource).indices.create params, callback

-module.exports = ElasticSearch
+module.exports = ElasticSearch


Closes #19 makes the console server to send JSON files with correct content type

--- cli/commands/server.coffee
+++ cli/commands/server.coffee
@@ -90,6 +90,7 @@
         when 'eot'  then return "application/vnd.ms-fontobject"
         when 'ttf'  then return "application/x-font-ttf"
         when 'woff' then return "application/x-font-woff"
+        when 'json' then return "application/json"
         else             return 'text/html'

     if req.method is 'POST'


If serializer returns empty array don't send it to client.

--- src/server/browser_server.coffee
+++ src/server/browser_server.coffee
@@ -198,6 +198,7 @@
                               @resources,
                               this,
                               @browser.window.document)
+            return if nodes.length == 0
             # 'before' tells the client where to insert the top level node in
             # relation to its siblings.
             # We only need it for the top level node because nodes in its tree


add Daemon button #2

add Daemon button #2

--- lib/scan-deamons.coffee
+++ lib/scan-deamons.coffee
@@ -26,16 +26,29 @@

 module.exports =
 class ScanDeamons
+  scanFunction : null
   constructor: (@daemonManagement) ->
+    @defineScanFunction()
+
+  defineScanFunction: ->
+    if process.platform == "darwin" #mac
+      @scanFunction = ScanDaemonsBrew
+    else #if /^win/.test(process.platform) #win
+      #atom.notifications.addInfo "There is no scan algorithm for your #{process.platform} platform, plaese add this!"
+      #atom.notifications.addInfo "Add manual daemons with CMD+SHIFT+P -> Smart Daemon Control: New Daemon"
+      #@daemonManagement.newDaemon()
+    #else if /^linux/.test(process.platform) #linux
+
+  run: ->
+    new @scanFunction()
+
+  thereIsScanDaemonForOs: ->
+    if @scanFunction == null
+      false
+    else true

   run: () ->
-    if process.platform == "darwin" #mac
-      new ScanDaemonsBrew(this)
-    else #if /^win/.test(process.platform) #win
-      atom.notifications.addInfo "There is no scan algorithm for your #{process.platform} platform, plaese add this!"
-      atom.notifications.addInfo "Add manual daemons with CMD+SHIFT+P -> Smart Daemon Control: New Daemon"
-      #@daemonManagement.newDaemon()
-    #else if /^linux/.test(process.platform) #linux
+

   addDaemon: (daemonName,filePath,fileNameWithoutAfterDot) ->
     #console.log fileNameWithoutAfterDot


A bit more in features.coffee

A bit more in features.coffee

--- coffeescript/features.coffee
+++ coffeescript/features.coffee
@@ -160,3 +160,20 @@
 console.log x
 console.log y
 console.log z
+
+####################
+### multi-assignments
+####################
+console.log '\n## multi-assignments ##'
+
+[a, b] = [5, 8]
+console.log a,b
+[a, b] = [b, a]
+console.log a,b
+
+rect =
+  x: 100
+  y: 200
+{x, y} = rect
+console.log "x = #{x}"
+console.log "y = #{y}"


Dataexplorer all set for 1.4

Dataexplorer all set for 1.4

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -1442,43 +1442,6 @@
                     error: true
                 }

-        # Callback used by the cursor when the user hit 'more results'
-        callback_query: (data) =>
-            # If we get a run time error
-            if data instanceof rethinkdb.errors.RuntimeError or data instanceof rethinkdb.errors.BadQuery or data instanceof rethinkdb.errors.ClientError or data instanceof rethinkdb.errors.ClientError
-                @.$('.loading_query_img').css 'display', 'none'
-                @.results_view.render_error(@query, data)
-                return false
-
-            # if it's a valid result and we have not reach the maximum of results displayed
-            # A valid result can be null, so we don't use the coffescript's existential operator
-            if data isnt undefined and  @current_results.length < @limit
-                @current_results.push data
-                return true
-            else
-                # Else we are going to display what we have
-                @.$('.loading_query_img').css 'display', 'none'
-
-                # Save the last executed query and the last displayed results
-                @saved_data.query = @query
-                @saved_data.results = @current_results
-
-                @saved_data.metadata =
-                    limit_value: @current_results.length
-                    skip_value: @skip_value
-                    execution_time: new Date() - @start_time
-                    query: @query
-                    has_more_data: (true if data?) # if data is undefined, it means that there is no more data
-                @results_view.render_result
-                    results: @current_results # The first parameter null is the query because we don't want to display it.
-                    metadata: @saved_data.metadata
-
-                if data isnt undefined #there is nore data
-                    @skip_value += @current_results.length
-                    @current_results = []
-                    @current_results.push data
-                return false
-
         # Function triggered when the user click on 'more results'
         show_more_results: (event) =>
             event.preventDefault()
@@ -1486,102 +1449,13 @@
             try
                 @current_results = []
                 @start_time = new Date()
-                @saved_data.cursor.next(@callback_query)
+                @saved_data.cursor.next @get_result_callback
                 $(window).scrollTop(@.$('.results_container').offset().top)
             catch err
                 @.$('.loading_query_img').css 'display', 'none'
                 @results_view.render_error(@query, err)

-        ###
-        show_more_results: (event) =>
-            try
-                event.preventDefault()
-                @start_time = new Date()
-                @saved_data.cursor.next(@callback_query)
-                $(window).scrollTop(@.$('.results_container').offset().top)
-            catch err
-                @.$('.loading_query_img').css 'display', 'none'
-                @results_view.render_error(@query, err)
-        ###
-
-        # Callback for the query executed
-        callback_multiple_queries: (data) =>
-            # Check if the data sent by the server is an error
-            if data instanceof rethinkdb.errors.RuntimeError or data instanceof rethinkdb.errors.BadQuery or data instanceof rethinkdb.errors.ClientError or data instanceof rethinkdb.errors.ClientError
-                @.$('.loading_query_img').css 'display', 'none'
-                @.results_view.render_error(@query, data)
-                return false
-
-            # Look for the next query
-            @current_query_index++
-
-            # If we are dealing with the last query
-            if @current_query_index >= @queries.length
-                # If we get a run time error
-                if data instanceof rethinkdb.errors.RuntimeError
-                    @.$('.loading_query_img').css 'display', 'none'
-                    @.results_view.render_error(@query, data)
-                    return false
-
-                # if it's a valid result and we have not reach the maximum of results displayed
-                if data isnt undefined and  @current_results.length < @limit
-                    @current_results.push data
-                    return true
-                else
-                    # Else we are going to display what we have
-                    @.$('.loading_query_img').css 'display', 'none'
-
-                    # Save the last executed query and the last displayed results
-                    @saved_data.query = @query
-                    @saved_data.results = @current_results
-                    @saved_data.metadata =
-                        limit_value: @current_results.length
-                        skip_value: @skip_value
-                        execution_time: new Date() - @start_time
-                        query: @query
-                        has_more_data: (true if data?) # if data is undefined, it means that there is no more data
-
-                    @results_view.render_result
-                        results: @current_results # The first parameter is null ( = query, so we don't display it)
-                        metadata: @saved_data.metadata
-
-                    if data isnt undefined #there is nore data
-                        @skip_value += @current_results.length
-                        @current_results = []
-                        @current_results.push data
-
-                    # Successful query, let's save it in the history
-                    @save_query @raw_query
-
-                    return false
-            else #  Else if it's not the last query, we just execute the next query
-                try
-                    #For safety only.
-                    if @cursor?.close?
-                        @cursor.close()
-
-                    @current_results = []
-                    @skip_value = 0
-
-                    @cursor = eval(@queries[@current_query_index])
-                    @saved_data.cursor = @cursor # Saving the cursor so the user can call more results later
-                    @cursor.next(@callback_multiple_queries)
-                catch err
-                    @.$('.loading_query_img').css 'display', 'none'
-                    @results_view.render_error(@query, err)
-            return false
-
         # Function that execute the query
-        # Current behavior for user
-        # - User execute q1
-        # - User execute q2 (we stop listening to q1)
-        # - User retrieve results for q2
-        #
-        # In case of multiple queries
-        # - User execute q1_1, q1_2
-        # - User execute q2
-        # - We don't execute q1_3
-        # - User retrieve results for q2
         execute_query: =>
             # The user just executed a query, so we reset cursor_timed_out to false
             @saved_data.cursor_timed_out = false
@@ -1641,8 +1515,9 @@
                     #TODO hack run in the driver
                     @skip_value = 0
                     @start_time = new Date()
+                    @current_results = []
                     #TODO That's not synchronous? Find why.
-                    rdb_query.run @driver_handler.connection, @rdb_global_callback
+                    rdb_query.run @driver_handler.connection, @rdb_global_callback # @rdb_global_callback can be fire more than once
                     return true
                 else
                     @non_rethinkdb_query += @queries[@index-1]
@@ -1659,10 +1534,9 @@
                 if cursor?
                     @saved_data.cursor = @cursor

-
+                debugger
                 #TODO Check for empty array?
                 if cursor?.hasNext?
-                    @current_results = []
                     @cursor = cursor
                     if cursor.hasNext() is true
                         cursor.next @get_result_callback
@@ -1675,11 +1549,11 @@
                     @saved_data.query = @query
                     @saved_data.results = @current_results
                     @saved_data.metadata =
-                        limit_value: 0
+                        limit_value: @current_results.length
                         skip_value: @skip_value
                         execution_time: new Date() - @start_time
                         query: @query
-                        has_more_data: (true if data?) # if data is undefined, it means that there is no more data
+                        has_more_data: false

                     @results_view.render_result
                         results: @current_results # The first parameter is null ( = query, so we don't display it)
@@ -1702,10 +1576,11 @@
                     return true

             # if data is undefined or @current_results.length is @limit
+            @saved_data.cursor = @cursor # Let's save the cursor, there may be mor edata to retrieve
             @saved_data.query = @query
             @saved_data.results = @current_results
             @saved_data.metadata =
-                limit_value: 0
+                limit_value: @current_results.length
                 skip_value: @skip_value
                 execution_time: new Date() - @start_time
                 query: @query
@@ -1720,7 +1595,6 @@

         evaluate: (query) =>
-            console.log query
             "use strict"
             return eval(query)


Fix suggestions when using comments

Fix suggestions when using comments

--- admin/static/coffee/dataexplorer.coffee
+++ admin/static/coffee/dataexplorer.coffee
@@ -491,6 +491,8 @@
             comma: /^(\s)*,(\s)*/
             semicolon: /^(\s)*;(\s)*/
             number: /^[0-9]+\.?[0-9]*/
+            inline_comment: /^(\s)*\/\/.*\n/
+            multiple_line_comment: /^(\s)*\/\*[^]*\*\//
             get_first_non_white_char: /\s*(\S+)/
             last_char_is_white: /.*(\s+)$/
         stop_char: # Just for performance
@@ -550,7 +552,18 @@
                             element.type = 'string'
                             start = i
                         continue
-
+
+                    result_inline_comment = @regex.inline_comment.exec query.slice i
+                    if result_inline_comment?
+                        to_skip = result_inline_comment[0].length-1
+                        start += result_inline_comment[0].length
+                        continue
+                    result_multiple_line_comment = @regex.multiple_line_comment.exec query.slice i
+                    if result_multiple_line_comment?
+                        to_skip = result_multiple_line_comment[0].length-1
+                        start += result_multiple_line_comment[0].length
+                        continue
+
                     if element.type is null
                         if start is i
                             result_white = @regex.white_start.exec query.slice i