reformatted market.coffee - no more errors in ide

--- app/js/controllers/market.coffee
+++ app/js/controllers/market.coffee
@@ -26,10 +26,13 @@
         { heading: "market.short", route: "market.short", active: false, class: "tab-short" }
     ]

-    $scope.goto_tab = (route) -> $state.go route
-    $scope.active_tab = (route) -> $state.is route
+    $scope.goto_tab = (route) ->
+        $state.go route
+    $scope.active_tab = (route) ->
+        $state.is route
     $scope.$on "$stateChangeSuccess", ->
-        $scope.tabs.forEach (tab) -> tab.active = $scope.active_tab(tab.route)
+        $scope.tabs.forEach (tab) ->
+            tab.active = $scope.active_tab(tab.route)

     Wallet.get_account(account.name).then (acct) ->
         Wallet.set_current_account(acct)
@@ -54,7 +57,8 @@
                         if data[symbol] != value
                             changed = true
                             data[symbol] = value
-            promise.finally -> deferred.resolve(changed)
+            promise.finally ->
+                deferred.resolve(changed)

     market_data_observer =
@@ -73,7 +77,8 @@
     market_name = $stateParams.name
     promise = MarketService.init(market_name)
     promise.then (market) ->
-        $scope.tabs.forEach (tab) -> tab.active = $scope.active_tab(tab.route)
+        $scope.tabs.forEach (tab) ->
+            tab.active = $scope.active_tab(tab.route)
         $scope.market = current_market = market
         $scope.actual_market = market.get_actual_market()
         $scope.market_inverted_url = MarketService.inverted_url
@@ -94,7 +99,7 @@
         if market.shorts_available
             Wallet.get_setting("market.advanced").then (result) ->
                 $scope.advanced = (if result then result.value else false)
-        price_decimals = if market.price_precision > 9 then (market.price_precision+"").length - 2 else market.price_precision - 2
+        price_decimals = if market.price_precision > 9 then (market.price_precision + "").length - 2 else market.price_precision - 2
         Observer.registerObserver(market_data_observer)
         Observer.registerObserver(market_status_observer)
         balances = {}
@@ -106,21 +111,23 @@
             account.quantity_balance = data[market.asset_quantity_symbol] / market.quantity_precision
             account.short_balance = if market.inverted then account.base_balance else account.quantity_balance
         Observer.registerObserver(account_balances_observer)
-        q=[]
+        q = []
         q.push Blockchain.get_info().then (config) ->
             $scope.blockchain_symbol = config.symbol #XTS or BTSX
             WalletAPI.get_transaction_fee($scope.blockchain_symbol).then (blockchain_tx_fee) ->
                 Blockchain.get_asset(blockchain_tx_fee.asset_id).then (blockchain_tx_fee_asset) ->
                     $scope.blockchain_tx_fee = Utils.formatDecimal(
-                        blockchain_tx_fee.amount / blockchain_tx_fee_asset.precision, blockchain_tx_fee_asset.precision)
-
+                            blockchain_tx_fee.amount / blockchain_tx_fee_asset.precision,
+                        blockchain_tx_fee_asset.precision)
+
         q.push WalletAPI.get_transaction_fee(market.asset_base_symbol).then (tx_fee) ->
             Blockchain.get_asset(tx_fee.asset_id).then (tx_fee_asset) ->
                 $scope.tx_fee = Utils.formatDecimal(tx_fee.amount / tx_fee_asset.precision, tx_fee_asset.precision)
-
+
         $q.all(q).then()
-
-    promise.catch (error) -> Growl.error("", error)
+
+    promise.catch (error) ->
+        Growl.error("", error)
     $scope.showLoadingIndicator(promise)

     Wallet.refresh_accounts().then ->
@@ -128,7 +135,8 @@
         for k,a of Wallet.accounts
             $scope.accounts.push a if a.is_my_account

-    $scope.excludeOutOfRange = (item) -> not item.out_of_range
+    $scope.excludeOutOfRange = (item) ->
+        not item.out_of_range

     $scope.$on "$destroy", ->
         $scope.showContextHelp false
@@ -143,40 +151,41 @@
         $state.go('^.buy', {name: $scope.market.inverted_url})

     $scope.flip_advanced = ->
-        $scope.advanced = ! $scope.advanced
+        $scope.advanced = !$scope.advanced
         Wallet.set_setting("market.advanced", $scope.advanced).then()

     $scope.cancel_order = (id) ->
         res = MarketService.cancel_order(id)
         return unless res
-        #res.then -> Growl.notice "", "Your order was canceled."
+    #res.then -> Growl.notice "", "Your order was canceled."

     get_order = ->
         switch $state.current.name
             when "market.buy" then $scope.bid
             when "market.sell" then $scope.ask
             when "market.short" then $scope.short
-            else throw Error("Unknown $state.current.name",$state.current.name)
+            else
+                throw Error("Unknown $state.current.name", $state.current.name)

     # About *_change methods...
     # ng-change is used instead of watch because the fields
-    # form a loop.  So, updating the cost updates the quantity,
+    # form a loop.  So, updating the cost updates the quantity,
     # updating the quantity updates the cost.  Watch did not allow
     # updating the watched field and suppressing the event.
     #
     # https://github.com/angular/angular.js/issues/834
-
+
     $scope.order_change = ->
-        order=get_order()
+        order = get_order()
         TradeData = MarketService.TradeData
         quantity = TradeData.helper.to_float(order.quantity)
         price = TradeData.helper.to_float(order.price)
         cost = quantity * price
         order.cost = if cost == 0 then null else
             Utils.formatDecimal(cost, $scope.market.quantity_precision, true)
-
-     $scope.order_total_change = ->
-        order=get_order()
+
+    $scope.order_total_change = ->
+        order = get_order()
         TradeData = MarketService.TradeData
         price = TradeData.helper.to_float(order.price)
         cost = TradeData.helper.to_float(order.cost)
@@ -192,7 +201,7 @@
         short.cost = short.quantity * short.collateral_ratio
         $scope.short.cost = if short.cost == 0 then null else
             Utils.formatDecimal(short.cost, $scope.market.base_precision, true)
-
+
     $scope.short_total_change = ->
         short = $scope.short.clone_and_normalize()
         if short.cost and short.cost > 0
@@ -200,7 +209,7 @@
             $scope.short.quantity = if short.quantity == 0 then null else
                 Utils.formatDecimal(short.quantity, $scope.market.base_precision, true)
         else
-             $scope.short.quantity = null
+            $scope.short.quantity = null

     # Adds .01% to the price so when posted it overlaps and should match market bid/ask
     get_makeweight = ->
@@ -208,7 +217,8 @@
             when "market.buy" then .0001
             when "market.sell" then -.0001
             when "market.short" then -.0001
-            else throw Error("Unknown $state.current.name",$state.current.name)
+            else
+                throw Error("Unknown $state.current.name", $state.current.name)

     $scope.grid_row_clicked = (row) ->
         #console.log "------ gridRowClicked ------>", row
@@ -227,27 +237,24 @@
                 return null
             else
                 return Utils.formatDecimal(ret, precision)
-
-        order.quantity = coalesce data.quantity,
-            order.quantity, $scope.market.quantity_precision
-
-        collateral_ratio = data.collateral_ratio + data.collateral_ratio *
-            Math.abs(makeweight)\
-            if data.collateral_ratio
-        order.collateral_ratio = coalesce collateral_ratio,
-            order.collateral_ratio, $scope.market.price_precision
-
-        order.short_price_limit = coalesce data.price_limit,
-            order.short_price_limit, $scope.market.price_precision
+
+        order.quantity = coalesce data.quantity, order.quantity, $scope.market.quantity_precision
+
+        collateral_ratio = data.collateral_ratio + data.collateral_ratio * Math.abs(makeweight)
+        if data.collateral_ratio
+            order.collateral_ratio = coalesce collateral_ratio, order.collateral_ratio, $scope.market.price_precision
+
+        order.short_price_limit = coalesce data.price_limit, order.short_price_limit, $scope.market.price_precision

         price = data.price + data.price * makeweight if data.price
         order.price = coalesce price, order.price, $scope.market.price_precision
-
+
         switch $state.current.name
             when "market.buy" then $scope.order_change()
             when "market.sell" then $scope.order_change()
             when "market.short" then $scope.short_change()
-            else throw Error("Unknown $state.current.name",$state.current.name)
+            else
+                throw Error("Unknown $state.current.name", $state.current.name)

     $scope.scroll_buysell = ->
         return null if $("#order_tabs").position().top > 0
@@ -262,15 +269,15 @@
         #make sure user sees the correct cost (ng-change vrs watch work-around)
         previous = $scope.bid.cost
         $scope.order_change()
-        if previous != $scope.bid.cost
+        if previous != $scope.bid.cost
             # This can happen if code forgets to update the cost
             form.bid_total.$error.message = 'market.tip.total_cost_updated'
             return
-
+
         if bid.cost > $scope.account.base_balance
             form.bid_quantity.$error.message = 'market.tip.insufficient_balances'
             return
-
+
         bid.type = "bid_order"
         bid.display_type = "Bid"
         # TODO re-poll api for balance instead (see account_balances_observer below)
@@ -287,11 +294,11 @@
         form = @sell_form
         $scope.clear_form_errors(form)
         ask = $scope.ask.clone_and_normalize()
-
+
         #make sure user sees the correct cost (ng-change vrs watch work-around)
         previous = $scope.ask.cost
         $scope.order_change()
-        if previous != $scope.ask.cost
+        if previous != $scope.ask.cost
             # This can happen if code forgets to update the cost
             form.ask_total.$error.message = 'market.tip.total_cost_updated'
             return
@@ -313,7 +320,7 @@
         $scope.clear_form_errors(form)
         short = $scope.short.clone_and_normalize()
         short.price = $scope.market.shorts_price
-
+
         #make sure user sees the correct cost (ng-change vrs watch work-around)
         previous = $scope.short.cost
         $scope.short_change()
@@ -332,27 +339,6 @@
     $scope.confirm_order = (id) ->
         MarketService.confirm_order(id, $scope.account).then () ->
             # TODO trigger account_balances_observer instead
-            market = $scope.market
-#            order = get_order()
-#            switch $state.current.name
-#                when "market.buy"
-#                    if market.inverted
-#                        $scope.account.base_balance -= order.cost
-#                    else
-#                        $scope.account.quantity_balance -= order.cost
-#
-#                when "market.sell"
-#                    if market.inverted
-#                        $scope.account.base_balance -= order.cost
-#                    else
-#                        $scope.account.base_balance -= order.cost
-#
-#                when "market.short"
-#                    if market.inverted
-#                        $scope.account.base_balance -= order.cost
-#                    else
-#                        $scope.account.quantity_balance -= order.cost
-            #Growl.notice "", "Your order was successfully placed."
         , (error) ->
             Growl.error "", "Order failed: " + error.data.error.message

@@ -372,8 +358,8 @@
                 original_order = order
                 #console.log order
                 order = angular.copy(order)
-#                if !current_market.inverted
-#                    order = order.invert()
+                #                if !current_market.inverted
+                #                    order = order.invert()
                 scope.v = {quantity: order.quantity, total: order.quantity}
                 scope.cancel = ->
                     modalInstance.dismiss "cancel"


Make action understand that timeout is in milliseconds

--- client/redux/actions.coffee
+++ client/redux/actions.coffee
@@ -18,7 +18,7 @@
             if not minAgeToUpdate?
                 return
             # -200 to ensure that on age equal to minAgeToUpdate we re-request data
-            if (new Date() - existingDataTime) < minAgeToUpdate * 1000 - 200
+            if (new Date() - existingDataTime) < minAgeToUpdate - 200
                 return
         dispatch
             type: GET_DATA


Comment clean up.

Comment clean up.

--- src/timber.coffee
+++ src/timber.coffee
@@ -249,7 +249,7 @@
         @canvas.render(@elements)
         @timestamp = now

-        # Loop again, when the stack clear.
+        # Loop again, when the stack clears.
         setTimeout () =>
             @.loop()
         , 0


Removing undo now that it is in EditorBase

--- lib/assets/javascripts/pixie/editor/undo.coffee
+++ lib/assets/javascripts/pixie/editor/undo.coffee
@@ -1,43 +0,0 @@
-# An editor module for editors that support undo/redo
-namespace "Pixie.Editor", (Editor) ->
-
-  Editor.Undo = (I, self) ->
-    commandStack = CommandStack()
-    lastClean = undefined
-
-    dirty = (newDirty) ->
-      if newDirty == false
-        lastClean = commandStack.current()
-        self.trigger('clean')
-
-        return self
-      else
-        return lastClean != commandStack.current()
-
-    updateDirtyState = ->
-      if dirty()
-        self.trigger('dirty')
-      else
-        self.trigger('clean')
-
-    # Set dirty state on save event
-    self.bind 'save', ->
-      dirty(false)
-
-    execute: (command) ->
-      commandStack.execute command
-      updateDirtyState()
-
-      return self
-
-    undo: ->
-      commandStack.undo()
-      updateDirtyState()
-
-      return self
-
-    redo: ->
-      commandStack.redo()
-      updateDirtyState()
-
-      return self


Remove preceding `@` from integer in place edit values

--- app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee
+++ app/assets/javascripts/ab_admin/components/in_place_edit.js.coffee
@@ -6,7 +6,10 @@
       originalUrl.call(@options.scope, params)
     else
       params[@options.model] ||= {}
-      params[@options.model][params.name] = params.value
+      if _.isString(params.value) && params.value.match(/@\d+/)
+        params[@options.model][params.name] = params.value.replace('@', '')
+      else
+        params[@options.model][params.name] = params.value
       params._method = 'PATCH'
       ajax_opts =
         url: originalUrl


fixed run command

fixed run command

--- app/assets/javascripts/states/runbooks/views/run_command.js.coffee
+++ app/assets/javascripts/states/runbooks/views/run_command.js.coffee
@@ -42,7 +42,7 @@
       @ui.command_detail.show()

     "click run": (e) ->
-      @run_command()
+      @with_inputs(@run_command)

     "click schedule.btn": (e) ->
       @with_inputs(@schedule_command)


fix "if ss didn't provide filename, auto detection remains disabled"

--- app/assets/javascripts/uploadcare/widget/tabs/remote-tab.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/remote-tab.js.coffee
@@ -51,7 +51,8 @@
               if message?.type is 'file-selected'

                 file = uploadcare.rawFileFrom 'url', message.url, @settings
-                file.setName message.filename
+                if message.filename
+                  file.setName message.filename
                 @dialogApi.addFiles file.promise()

                 @__sendMessage


Make <a target="_blank"> open window by default

--- atom/browser/api/lib/browser-window.coffee
+++ atom/browser/api/lib/browser-window.coffee
@@ -1,6 +1,7 @@
 EventEmitter = require('events').EventEmitter
 IDWeakMap = require 'id-weak-map'
 app = require 'app'
+ipc = require 'ipc'
 wrapWebContents = require('web-contents').wrap

 BrowserWindow = process.atomBinding('window').BrowserWindow
@@ -23,9 +24,11 @@
     value: BrowserWindow.windows.add(this)
     enumerable: true

-  # Route the "new-window" event to webContents.
-  @on 'new-window', (args...) =>
-    @webContents?.emit 'new-window', args...
+  # Make new windows requested by links behave like "window.open"
+  @on 'new-window', (event, url, frameName) =>
+    event.sender = @webContents
+    options = show: true, width: 800, height: 600
+    ipc.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options

   # Remove the window from weak map immediately when it's destroyed, since we
   # could be iterating windows before GC happened.


Removed watcher emails for overloaded watchers.

--- server/commons/Handler.coffee
+++ server/commons/Handler.coffee
@@ -447,7 +447,8 @@
     docLink = "http://codecombat.com#{editPath}"
     @sendChangedHipChatMessage creator: editor, target: changedDocument, docLink: docLink
     watchers = changedDocument.get('watchers') or []
-    watchers = (w for w in watchers when not w.equals(editor.get('_id')))
+    # Don't send these emails to the person who submitted the patch, or to Nick, George, or Scott.
+    watchers = (w for w in watchers when not w.equals(editor.get('_id')) and not (w + '' in ['512ef4805a67a8c507000001', '5162fab9c92b4c751e000274', '51538fdb812dd9af02000001']))
     return unless watchers.length
     User.find({_id:{$in:watchers}}).select({email:1, name:1}).exec (err, watchers) =>
       for watcher in watchers
@@ -477,9 +478,7 @@
       watchers = [req.user.get('_id')]
       if req.user.isAdmin()  # https://github.com/codecombat/codecombat/issues/1105
         nick = mongoose.Types.ObjectId('512ef4805a67a8c507000001')
-        scott = mongoose.Types.ObjectId('5162fab9c92b4c751e000274')
         watchers.push nick unless _.find watchers, (id) -> id.equals nick
-        watchers.push scott unless _.find watchers, (id) -> id.equals scott
       model.set 'watchers', watchers
     model


more elegant public url retrieval

more elegant public url retrieval

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -387,6 +387,19 @@
                 label: '确定'
                 action: =>
                   msg.hide()
+@action_show_url = (title, url)=>
+  msg = Messenger().post
+    message: "#{title}<pre>#{url}</pre>"
+    actions:
+      ok:
+        label: '确定'
+        action: =>
+          msg.hide()
+      copy:
+        label: '复制到剪切版'
+        action: (ev)=>
+          $(ev.currentTarget).text '已复制到剪切版'
+          @gui.Clipboard.get().set url, 'text'
 @nw_saveas = (filename, cb)=>
   $ dlg = document.createElement 'input'
     .appendTo 'body'
@@ -639,20 +652,13 @@
             .appendTo td
             .attr title: '复制该文件的公共地址(URL)到剪切版'
             .addClass 'btn btn-info btn-xs'
-            .prepend @createIcon 'paste'
+            .prepend @createIcon 'code'
             .data 'url', "http://#{@bucket}.b0.upaiyun.com#{file.url}"
             .data 'filename', file.filename
             .click (ev)=>
               filename = $(ev.currentTarget).data 'filename'
               url = $(ev.currentTarget).data 'url'
-              @gui.Clipboard.get().set url, 'text'
-              msg = Messenger().post
-                message: "已将文件 #{filename} 的公共地址(URL)复制到剪切版"
-                actions:
-                  ok:
-                    label: '确定'
-                    action: =>
-                      msg.hide()
+              @action_show_url "文件 #{filename} 的公共地址(URL)", url
           $ document.createElement 'button'
             .appendTo td
             .attr title: '用文本编辑器打开该文件'


[aphrodite] Add a limit to file upload

[aphrodite] Add a limit to file upload

--- aphrodite/app/assets/javascripts/import.js.coffee
+++ aphrodite/app/assets/javascripts/import.js.coffee
@@ -5,9 +5,5 @@
     dataType: 'json'
     maxFileSize: 10000000
     acceptFileTypes: /(\.|\/)(pdf|txt|doc|docx|html)$/i
-    #limitConcurrentUploads: 5
-    #progressall: (e, data) ->
-      #progress = parseInt(data.loaded / data.total * 100, 10)
-      #$('#progress .bar').css "width", "#{progress}%"
-    #add: (e, data) ->
+    limitConcurrentUploads: 5


fixed a bug in bust where the file path was not being formatted with forward slashes properly. this was causing images that reside two level deep in directories from not being renamed in css files.

--- tasks/bust.coffee
+++ tasks/bust.coffee
@@ -12,14 +12,13 @@
 				else
 					true
 			.map (filePath) ->
-				cwd = path.normalize f.orig.cwd
+				cwd = f.orig.cwd
 				contents = grunt.file.read filePath
 				hash = crypto.createHash('sha1').update(contents).digest('hex').substr(0, 10)
-				dir = path.normalize path.dirname filePath
+				dir = path.dirname filePath
 				ext = path.extname filePath
 				base = path.basename filePath, ext
 				idir = dir.split(cwd)[1]
-				idir = idir.replace '\\', '/'

 				newFileName = "/#{idir}/#{base}.#{hash}#{ext}"
 				newFilePath = path.join dir, "#{base}.#{hash}#{ext}"
@@ -29,6 +28,7 @@
 					if replaceFiles.length > 0
 						contents = grunt.file.read replaceFiles[0]
 						toReplace = "/#{idir}/#{base}#{ext}"
+
 						exp = new RegExp toReplace, "g"
 						contents = contents.replace exp, newFileName
 						grunt.file.write replaceFiles[0], contents


 Merge branch 'master.activeItemTagFix' into 'master'

Master.Active Item Tag Fix

--- workers/social/lib/social/models/activeitems.coffee
+++ workers/social/lib/social/models/activeitems.coffee
@@ -65,7 +65,7 @@
     group = options.group or "koding"
     {select: selector} = nameMapping.topic
     selector.group = group
-    JTag.some selector, {limit:10, "counts.followers":-1}, callback
+    JTag.some selector, {limit:10, sort:"counts.followers":-1}, callback

   @_fetchUsers = (options={}, callback)->
     {client}   = options


JGroup: kickMember: fix trailing callback issue

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1198,7 +1198,7 @@
             JSession.remove {
               username  : account.profile.nickname
               groupName : client.context.group
-            }, callback
+            }, (err) -> callback err

           queue = roles.map (role) => =>
             @removeMember account, role, (err) =>
@@ -1214,7 +1214,6 @@
             @addBlockedAccount account, (err) ->
               return callback err  if err
               queue.fin()
-

           dash queue, kallback
   ###*


SocialWorker: add api token limit for groups

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -22,6 +22,8 @@
     { permission: 'edit groups',     superadmin: yes }
     { permission: 'edit own groups', validateWith: Validators.group.admin }
   ]
+
+  @API_TOKEN_LIMIT = 10

   @trait __dirname, '../../traits/filterable'
   @trait __dirname, '../../traits/followable'


members can see blocked accounts

members can see blocked accounts

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -878,8 +878,8 @@

   fetchBlockedAccountsWithEmail$: permit
     advanced: [
-      { permission: 'grant permissions' }
-      { permission: 'grant permissions', superadmin: yes }
+      { permission: 'list members' }
+      { permission: 'list members', superadmin: yes }
     ]
     success:(client, rest...) ->
       @baseFetcherOfGroupStaff {


fixed coffeelint errors on workers/social/lib/social/models/siftscience

--- workers/social/lib/social/models/siftscience.coffee
+++ workers/social/lib/social/models/siftscience.coffee
@@ -1,96 +1,99 @@
-{argv}   = require 'optimist'
-KONFIG   = require('koding-config-manager').load("main.#{argv.c}")
+{ argv }    = require 'optimist'
+KONFIG      = require('koding-config-manager').load("main.#{argv.c}")
+KodingError = require '../error'

 module.exports = class SiftScience

-  @transaction: (client, raw, callback)->
-    {planTitle, planAmount, binNumber, lastFour, cardName, status} = raw
+  @transaction: (client, raw, callback) ->
+    { planTitle, planAmount, binNumber, lastFour, cardName, status } = raw

-    return callback null  if planTitle is "free"
+    return callback null  if planTitle is 'free'

     data = {
-      "$type"               : "$transaction"
-      "$transaction_status" : "$success"
-      "$currency_code"      : "USD"
-      "$amount"             : parsePrice planAmount
-      "$payment_method"     :
-        "$payment_type"     : "$credit_card"
-        "$payment_gateway"  : "$stripe"
-        "$card_bin"         : binNumber
-        "$card_last4"       : lastFour
-      "$billing_address"    :
-        "$name"             : cardName
+      '$type'               : '$transaction'
+      '$transaction_status' : '$success'
+      '$currency_code'      : 'USD'
+      '$amount'             : parsePrice planAmount
+      '$payment_method'     :
+        '$payment_type'     : '$credit_card'
+        '$payment_gateway'  : '$stripe'
+        '$card_bin'         : binNumber
+        '$card_last4'       : lastFour
+      '$billing_address'    :
+        '$name'             : cardName
     }

-    @send client, "transaction", data, callback
+    @send client, 'transaction', data, callback

-  @createOrder : (client, raw, callback)->
-    {planTitle, planAmount, binNumber, lastFour, cardName} = raw
+  @createOrder : (client, raw, callback) ->
+    { planTitle, planAmount, binNumber, lastFour, cardName } = raw

-    return callback null  if planTitle is "free"
+    return callback null  if planTitle is 'free'

     data = {
-      "$type"               : "$create_order"
-      "$currency_code"      : "USD"
-      "$amount"             : parsePrice planAmount
-      "$payment_methods"    : [
-        "$payment_type"     : "$credit_card"
-        "$payment_gateway"  : "$stripe"
-        "$card_bin"         : binNumber
-        "$card_last4"       : lastFour
+      '$type'               : '$create_order'
+      '$currency_code'      : 'USD'
+      '$amount'             : parsePrice planAmount
+      '$payment_methods'    : [
+        '$payment_type'     : '$credit_card'
+        '$payment_gateway'  : '$stripe'
+        '$card_bin'         : binNumber
+        '$card_last4'       : lastFour
       ]
-      "$billing_address"    :
-        "$name"             : cardName
-      "$items"              : [
-        "$item_id"          : planTitle
-        "$price"            : parsePrice planAmount
+      '$billing_address'    :
+        '$name'             : cardName
+      '$items'              : [
+        '$item_id'          : planTitle
+        '$price'            : parsePrice planAmount
       ]
     }

-    @send client, "create_order", data, callback
+    @send client, 'create_order', data, callback

-  @createAccount: (client, referrer, callback)->
+  @createAccount: (client, referrer, callback) ->
     data = {
-      "$type"             : "$create_account"
-      "$referrer_user_id" : referrer
+      '$type'             : '$create_account'
+      '$referrer_user_id' : referrer
     }

-    @send client, "create_account", data, callback
+    @send client, 'create_account', data, callback

-  @send = (client, event, data, callback)->
+  @send = (client, event, data, callback) ->
     siftScience = require('yield-siftscience') KONFIG.siftScience

-    @fetchUserInfo client, (err, userInfo)->
+    @fetchUserInfo client, (err, userInfo) ->
       return callback err   if err
-      return callback {message: "no user found"}  unless userInfo?
+      return callback new KodingError 'no user found'  unless userInfo?

-      {username, email, sessionToken} = userInfo
+      { username, email, sessionToken } = userInfo

-      data["$user_id"]    = username
-      data["$user_email"] = email
-      data["$session_id"] = sessionToken
+      data['$user_id']    = username
+      data['$user_email'] = email
+      data['$session_id'] = sessionToken

-      siftScience.event[event] data, (err, response)->
-        console.error "Request to SiftScience failed", err, response.body  if err
+      siftScience.event[event] data, (err, response) ->
+        console.error 'Request to SiftScience failed', err, response.body  if err
         callback err, response.body

-  @fetchUserInfo = (client, callback)->
-    {sessionToken, connection : {delegate}} = client
+  @fetchUserInfo = (client, callback) ->
+    { sessionToken, connection : { delegate } } = client

-    if delegate.type isnt "registered"
-      return callback {message:"guests are not allowed"}
+    if delegate.type isnt 'registered'
+      return callback new KodingError 'guests are not allowed'

-    delegate.fetchUser (err, user)->
+    delegate.fetchUser (err, user) ->
       return callback err  if err

-      {username, email} = user
-      callback null, {username, email, sessionToken}
+      { username, email } = user
+      callback null, { username, email, sessionToken }

-  parsePrice = (price)->
-    price = price.slice 1  if price[0] is "$"
-    return parseFloat(price)*1000000
+  parsePrice = (price) ->
+    price = price.slice 1  if price[0] is '$'
+    return parseFloat(price) * 1000000
+
+


[LoadingFree] add activity feed fetching into dash function

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -87,20 +87,28 @@
   generateScript = ->
     html = createHTML()
     return callback null, html
+
+  # do not fetch activity feed for unregistered users
+  if client?.connection?.delegate?.type isnt "registered"
+    return generateScript()
+  queue.push ->
     fetchMembersFromGraph (err, members)->
       prefetchedFeeds['members.main'] = members  if members
       queue.fin()

-  queue.push do ->
+  queue.push ->
     JTag.some {}, defaultOptions, (err, topics)->
       prefetchedFeeds['topics.main'] = topics  if topics
       queue.fin()

-  queue.push do ->
+  queue.push ->
     JApp.some {}, defaultOptions, (err, apps)->
       prefetchedFeeds['apps.main'] = apps  if apps
       queue.fin()

-  dash queue, ->
-    html = createHTML()
-    callback null, html
+  queue.push ->
+    fetchActivityFromGraph (err, activity)->
+      prefetchedFeeds['activity.main'] = activity  if activity
+      queue.fin()
+
+  dash queue, generateScript


check delete status code in copy test

check delete status code in copy test

--- services/filestore/test/acceptence/coffee/SendingFileTest.coffee
+++ services/filestore/test/acceptence/coffee/SendingFileTest.coffee
@@ -80,6 +80,7 @@
 			request opts, (err, response, body)=>
 				response.statusCode.should.equal 200
 				request.del @fileUrl, (err, response, body)=>
+					response.statusCode.should.equal 204
 					request.get newFileUrl, (err, response, body)=>
 						body.should.equal @constantFileContent
 						done()


participants/collab: use bounded version for handlers

--- client/activity/lib/models/collaborationchannelparticipants.coffee
+++ client/activity/lib/models/collaborationchannelparticipants.coffee
@@ -104,8 +104,7 @@
   ###
   addTalkingParticipant: (nickname) ->

-    pushToCollection @state.talkingParticipants, nickname, =>
-      @emitChange()
+    pushToCollection @state.talkingParticipants, nickname, @bound 'emitChange'

   ###*
@@ -115,8 +114,7 @@
   ###
   removeTalkingParticipant: (nickname) ->

-    removeFromCollection @state.talkingParticipants, nickname, =>
-      @emitChange()
+    removeFromCollection @state.talkingParticipants, nickname, @bound 'emitChange'

   ###*
@@ -126,8 +124,7 @@
   ###
   addVideoActiveParticipant: (nickname) ->

-    pushToCollection @state.videoParticipants, nickname, =>
-      @emitChange()
+    pushToCollection @state.videoParticipants, nickname, @bound 'emitChange'

   ###*
@@ -137,8 +134,7 @@
   ###
   removeVideoActiveParticipant: (nickname) ->

-    removeFromCollection @state.videoParticipants, nickname, =>
-      @emitChange()
+    removeFromCollection @state.videoParticipants, nickname, @bound 'emitChange'

   ###*
@@ -148,8 +144,7 @@
   ###
   addVideoConnectedParticipant: (nickname) ->

-    pushToCollection @state.connectedParticipants, nickname, =>
-      @emitChange()
+    pushToCollection @state.connectedParticipants, nickname, @bound 'emitChange'

   ###*
@@ -159,8 +154,7 @@
   ###
   removeVideoConnectedParticipant: (nickname) ->

-    removeFromCollection @state.connectedParticipants, nickname, =>
-      @emitChange()
+    removeFromCollection @state.connectedParticipants, nickname, @bound 'emitChange'

   ###*


the "reset" button should force reload

the "reset" button should force reload

--- client/app/MainApp/filetree/controllers/resourcescontroller.coffee
+++ client/app/MainApp/filetree/controllers/resourcescontroller.coffee
@@ -28,7 +28,7 @@

     vmController = KD.getSingleton("vmController")
     vmController.resetVMData()
-    vmController.fetchVMs (err, vms)=>
+    vmController.fetchVMs yes, (err, vms)=>
       log "Found these vms:", err, vms
       return  unless vms
       # vms.sort cmp


workers: remove unused variables

workers: remove unused variables

--- workers/social/lib/social/render/loggedout/kodinghome.coffee
+++ workers/social/lib/social/render/loggedout/kodinghome.coffee
@@ -1,12 +1,7 @@
 module.exports = (options, callback) ->

-  getTitle = require './../title'
-
-  { campaign, account, bongoModels } = options
-  campaign or= 'landing'
-
+  { account } = options
   userAccount   = JSON.stringify account
-  campaignStats = null

   addSiteScripts = require './sitescripts'
   addSiteTags    = require './sitetags'


fix buildSettings()

fix buildSettings()

--- app/assets/javascripts/uploadcare/utils.js.coffee
+++ app/assets/javascripts/uploadcare/utils.js.coffee
@@ -52,13 +52,13 @@
       settings[key] = ns.normalizeUrl(settings[key])

     # Boolean settings
-    # <... foo>, <... foo="true">, <... foo="foo"> — On
+    # <... foo>, <... foo="true">, <... foo="enabled">, <... foo="foo"> — On
     # <... foo="false">, <... foo="disabled"> - Off
-    # <... > - Default using
+    # <... > - Default used
     for key in ['previewStep', 'multiple', 'imagesOnly']
       if typeof settings[key] is 'string'
-        settings[key] = not ($.trim(settings[key].toLowerCase())
-          in ['false', 'disabled'])
+        value = $.trim(settings[key]).toLowerCase()
+        settings[key] = not (value in ['false', 'disabled'])
       else
         settings[key] = !!settings[key]

@@ -92,7 +92,7 @@
       crop = ''
     settings.crop = crop

-    if settings.__cropParsed.enabled and settings.previewStep is false
+    if settings.__cropParsed.enabled
       settings.previewStep = true

     settings


Align hype and nomination buttons

Align hype and nomination buttons

--- resources/assets/coffee/react/beatmap-discussions/nominations.coffee
+++ resources/assets/coffee/react/beatmap-discussions/nominations.coffee
@@ -125,7 +125,7 @@
                   "#{hypeRaw} / #{requiredHype}"
               @renderLights(hype, requiredHype)

-            if currentUser.id? && currentUser.id != @props.beatmapset.user_id
+            if @props.currentUser.id? && !@userIsOwner()
               div className: "#{bn}__row-right",
                 el BigButton,
                   modifiers: ['full', 'wrap-text']
@@ -148,7 +148,7 @@
                     " #{nominations.current}/#{nominations.required}"
                 @renderLights(nominations.current, nominations.required)

-              if @props.currentUser.id?
+              if @props.currentUser.id? && !@userIsOwner()
                 div className: "#{bn}__row-right",
                   if mapIsQualified && @userCanDisqualify()
                     el BigButton,


fix link

fix link

--- mechanics/002-restricted-3-bodies/csmain.coffee
+++ mechanics/002-restricted-3-bodies/csmain.coffee
@@ -43,7 +43,7 @@
             vals: ps
         list = _.template(
             "

-              <form action='/tutorials/mechanics/001-binary-stars/' method='get'>
+              <form action='/tutorials/mechanics/002-restricted-3-bodies/' method='get'>
               <% _.each(keys, function(key) { %>
<%= key %>: <input type='text' name='<%= key %>' value='<%= vals[key] %>'>
 <% }); %>
               <input type='submit' value='Submit' />
               </form>


DOC: added search term

DOC: added search term

--- source/Art/Engine/Animation/PersistantAnimator.coffee
+++ source/Art/Engine/Animation/PersistantAnimator.coffee
@@ -262,7 +262,7 @@
 If can be "active" or not. If active, that element will get an epoch update each
 frame, and the animator will get a chance to animate the property each frame.

-Options:
+Options: (events)
   on:
     done: ->   # fires when the animation completes
     update: -> # fires every time the target object's animated values updated


Begin moving to class

Begin moving to class

--- assets/javascripts/global.coffee
+++ assets/javascripts/global.coffee
@@ -1,35 +1,47 @@
+# Note sure what the responsiblities of this class should be
+class Jashboard
+  constructor: ->
+    @authorize()
+
+    $("#connectLink").click =>
+      Trello.authorize
+        type: "popup"
+        success: @onAuthorize
+
+  authorize: ->
+    Trello.authorize
+      interactive:false
+      success: @onAuthorize
+
+  onAuthorize: =>
+    $('#connectLink').hide()
+    @getBoards()
+
+  getBoards: ->
+    $content = $('#cards').empty()
+    $boardsContainer = $('#boards')
+    Trello.members.get 'me', (member) ->
+      $cards = $('<div>').text('Loading Cards...').appendTo($content)
+      $boards = $('<div>').text('Loading Boards...').appendTo($boardsContainer)
+
+      # TODO: store this in a promise
+      Trello.get 'members/me/boards', (boards) ->
+        $boards.empty()
+        for board in boards
+          html = ich.board(board)
+          $boards.append(html)
+
+        board = _.find(boards, (board) -> board.name == 'TODO')
+
+        Trello.get "boards/#{board.id}/lists", (lists) ->
+          list = lists.filter( (list) -> list.name == 'Incoming!' )[0]
+
+          Trello.get "lists/#{list.id}/cards", (cards) ->
+            $cards.empty()
+            for card in cards
+              html = ich.card(card)
+              $cards.append(html)
+
 $ ->
-  Trello.authorize
-    interactive:false
-    success: onAuthorize
-
-  $("#connectLink").click ->
-    Trello.authorize
-      type: "popup"
-      success: onAuthorize
-
-onAuthorize = ->
-  $content = $('#cards').empty()
-  $boardsContainer = $('#boards')
-  $('#connectLink').hide()
-  Trello.members.get 'me', (member) ->
-    $cards = $('<div>').text('Loading Cards...').appendTo($content)
-    $boards = $('<div>').text('Loading Boards...').appendTo($boardsContainer)
-
-    # TODO: store this in a promise
-    Trello.get 'members/me/boards', (boards) ->
-      $boards.empty()
-      for board in boards
-        html = ich.board(board)
-        $boards.append(html)
-
-      board = _.find(boards, (board) -> board.name == 'TODO')
-
-      Trello.get "boards/#{board.id}/lists", (lists) ->
-        list = lists.filter( (list) -> list.name == 'Incoming!' )[0]
-
-        Trello.get "lists/#{list.id}/cards", (cards) ->
-          $cards.empty()
-          for card in cards
-            html = ich.card(card)
-            $cards.append(html)
+  jashboard = new Jashboard()
+  console.log "created jashboard"


Social: remove modifiedAt from JDomainAlias

--- workers/social/lib/social/models/domainalias.coffee
+++ workers/social/lib/social/models/domainalias.coffee
@@ -49,10 +49,6 @@
       createdAt       :
         type          : Date
         default       : -> new Date
-      modifiedAt      :
-        type          : Date
-        default       : -> new Date
-

   @one$ = permit 'list domains',
     success : (client, selector, callback) ->


graph: remove unused secure on countMembers

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1445,7 +1445,7 @@
     else
       callback new KodingError "No such VM type: #{data.type}"

-  countMembers: secure (client, callback)->
+  countMembers: (callback)->
     graph = new Graph({config:KONFIG['neo4j']})
     graph.fetchRelationshipCount {groupId:@_id, relName:"member"}, callback


model/group: fetchMembersWithEmail accessible from from backend

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -827,18 +827,21 @@
         rest
       }

-  fetchMembersWithEmail$: permit
-    advanced: [
-      { permission: 'list members' }
-      { permission: 'list members', superadmin: yes }
-    ]
-    success:(client, rest...) ->
-      @baseFetcherOfGroupStaff {
+  fetchMembersWithEmail: (client, rest...) ->
+    @baseFetcherOfGroupStaff {
         method      : @fetchMembers
         fetchEmail  : yes
         client
         rest
       }
+
+  fetchMembersWithEmail$: permit
+    advanced: [
+      { permission: 'list members' }
+      { permission: 'list members', superadmin: yes }
+    ]
+    success:(client, rest...) ->
+      @fetchMembersWithEmail client, rest...

   fetchAdmins$: permit
     advanced: [


SocialWorker: JGroup: replace daisy calls with async.series

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -8,7 +8,7 @@

   { Relationship } = require 'jraphical'

-  { Inflector, ObjectId, ObjectRef, secure, daisy, dash, signature } = require 'bongo'
+  { Inflector, ObjectId, ObjectRef, secure, dash, signature } = require 'bongo'

   JPermissionSet = require './permissionset'
   { permit }     = JPermissionSet
@@ -739,31 +739,39 @@
         { delegate }            = client.connection
         permissionSet         = null
         defaultPermissionSet  = null
-        daisy queue = [
-          => @fetchPermissionSet (err, model) ->
-              if err then callback err
-              else
-                permissionSet = model
-                queue.next()
-          => @fetchDefaultPermissionSet (err, model) =>
-              if err then callback err
-              else if model?
+
+        queue = [
+
+          (next) =>
+            @fetchPermissionSet (err, model) ->
+              return next err  if err
+              permissionSet = model
+              next()
+
+          (next) =>
+            @fetchDefaultPermissionSet (err, model) =>
+              return next err  if err
+
+              if model?
                 console.log 'already had defaults'
                 defaultPermissionSet = model
                 permissionSet = model unless permissionSet
-                queue.next()
+                next()
               else
                 console.log 'needed defaults fixed'
                 fixDefaultPermissions_ this, permissionSet, (err, newModel) ->
                   defaultPermissionSet = newModel
-                  queue.next()
-          -> callback null, {
-              permissionsByModule
-              permissions         : permissionSet.permissions
-              defaultPermissions  : defaultPermissionSet.permissions
-            }
-        ]
-
+                  next()
+
+        ]
+
+        async.series queue, (err) ->
+          return callback err  if err
+          callback null, {
+            permissionsByModule
+            permissions         : permissionSet.permissions
+            defaultPermissions  : defaultPermissionSet.permissions
+          }

   fetchRolesByAccount: (account, callback) ->

@@ -1022,15 +1030,19 @@
     membershipPolicy.approvalEnabled = no  if requestType is 'by-invite'

     queue.push(
-      -> membershipPolicy.save (err) ->
-        if err then callback err
-        else queue.next()
-      => @addMembershipPolicy membershipPolicy, (err) ->
-        if err then callback err
-        else queue.next()
+
+      (next) ->
+        membershipPolicy.save next
+
+      (next) =>
+        @addMembershipPolicy membershipPolicy, next
+
     )
-    queue.push callback  if callback
-    daisy queue
+
+    async.series queue, (err) ->
+      return callback err  if err
+      return callback null, membershipPolicy
+

   destroyMemebershipPolicy:(callback) ->
     @fetchMembershipPolicy (err, policy) ->
@@ -1524,8 +1536,7 @@
         contents[event]       = contents.member

-        next = -> queue.next()
-        queue = admins.map (admin) => =>
+        queue = admins.map (admin) => (next) =>
           contents.recipient = admin
           @notify admin, event, contents, next

@@ -1535,7 +1546,7 @@

         notifyByUsernames usernames, 'NewMemberJoinedToGroup', contents

-        daisy queue
+        async.series queue

   @each$ = (selector, options, callback) ->


JGroup: fetchResources: use same signature

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -194,8 +194,11 @@
           (signature Object, Function)
           (signature Object, Object, Function)
         ]
-        fetchResources:
-          (signature Object, Function)
+        fetchResources: [
+          (signature Function)
+          (signature Object, Function)
+          (signature Object, Object, Function)
+        ]
         searchMembers: [
           (signature String, Object, Function)
         ]
@@ -962,9 +965,12 @@
       { permission: 'grant permissions' }
       { permission: 'grant permissions', superadmin: yes }
     ]
-    success: (client, options, callback) ->
+    success: (client, rest...) ->
+
+      [selector, options, callback] = Module.limitEdges 10, 19, rest
+
       ComputeProvider = require '../computeproviders/computeprovider'
-      ComputeProvider.fetchGroupResources this, options, callback
+      ComputeProvider.fetchGroupResources this, selector, options, callback

   # this method contains copy/pasted code from jAccount.findSuggestions method.


PremiumSupport: fix selector

PremiumSupport: fix selector

--- workers/social/lib/social/render/scriptblock.coffee
+++ workers/social/lib/social/render/scriptblock.coffee
@@ -99,7 +99,7 @@

     if 'function' is typeof delegate?.fetchSubscriptions
       selector = {}
-      fetchOptions = targetOptions:{ tags: $nin: ["nosync"] }
+      fetchOptions = targetOptions: selector :{ tags: $nin: ["nosync"] }

       delegate.fetchSubscriptions selector, fetchOptions, (err, subscriptions)->
         if subscriptions and subscriptions.length


hrmmmmmm

hrmmmmmm

--- workers/social/lib/social/traits/slugifiable.coffee
+++ workers/social/lib/social/traits/slugifiable.coffee
@@ -76,8 +76,9 @@

   @updateSlugsByBatch =(batchSize, konstructors)->
     konstructors = [konstructors]  unless Array.isArray konstructors
-    counter = 0
     konstructors.forEach (konstructor)->
+      counter = 0
+      console.log konstructor.name
       konstructor.updateAllSlugs {batchSize}, (err,slug)->
         if ++counter is batchSize
           process.nextTick -> updateSlugsByBatch batchSize, konstructor


JAccount: fetchAllParticipatedGroups: attach roles into group objects

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -339,11 +339,12 @@
       as: { $in: roles }
     }

-    Relationship.someData selector, { sourceId: 1 }, (err, cursor) ->
+    Relationship.someData selector, { sourceId: 1, as: 1 }, (err, cursor) ->
       return callback err  if err
       return callback null, []  if not cursor

       cursor.toArray (err, arr) ->
+
         return callback err       if err
         return callback null, []  if not arr

@@ -353,7 +354,17 @@
         groupIds = uniq map(arr, (rel) -> rel.sourceId)

         JGroup = require './group'
-        JGroup.some { _id : { $in : groupIds } }, {}, callback
+        JGroup.some { _id : { $in : groupIds } }, {}, (err, groups) ->
+          return callback err  if err
+
+          groups = groups.map (group) ->
+            for rel in arr when group._id.equals rel.sourceId
+              group.roles ?= []
+              group.roles.push rel.as
+            group
+
+          callback null, groups
+

   createSocialApiId: (callback) ->


JSession: prevent JSession to create a valid session for guestuser

--- workers/social/lib/social/models/session.coffee
+++ workers/social/lib/social/models/session.coffee
@@ -54,7 +54,17 @@
           console.error err  if err?

-  @createSession =(callback) ->
+  safeGuestSessionName = (username)->
+
+    if username is "guestuser"
+      console.log "FIXME @gokmen --- denying to create session for guestuser"
+      username = JUser.createGuestUsername()
+      console.log "FIXME @gokmen --- overwritten with #{username}."
+
+    return username
+
+
+  @createSession = (callback) ->

     JUser = require './user'
     clientId = createId()
@@ -70,7 +80,8 @@
       if err then @emit 'error', err
       else
         {nickname: username} = account.profile
-        session = new JSession { clientId, username }
+        username = safeGuestSessionName username
+        session  = new JSession { clientId, username }
         session.save (err)->
           if err
             callback err
@@ -101,8 +112,8 @@
         return callback null, session  if session?

         clientId = createId()
-
-        session = new JSession { clientId, username }
+        username = safeGuestSessionName username
+        session  = new JSession { clientId, username }
         session.save (err)->
           return callback err  if err?
           callback null, session


Solve issue 337

Solve issue 337

--- packages/tower-controller/shared/scopes.coffee
+++ packages/tower-controller/shared/scopes.coffee
@@ -26,7 +26,7 @@

       unless scope
         if typeof name == 'string'
-          chain = Tower.constant(metadata.resourceType)
+          chain = Tower.constantNew(metadata.resourceType)

           unless name == 'all'
             if Tower.isClient


JCredential: Add meta field for created/modified date information

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -77,6 +77,8 @@
         type          : ObjectId
         required      : yes

+      meta            : require 'bongo/bundles/meta'
+
     relationships     :

       data            :


Map scale

Map scale

--- app/assets/javascripts/backbone/routers/validations_router.js.coffee
+++ app/assets/javascripts/backbone/routers/validations_router.js.coffee
@@ -152,6 +152,9 @@
       else
         @polygonDraw.enable()

+    # Scale
+    L.control.scale().addTo(map)
+
     # Layers
     baseMaps =
       'Map': baseMap


payments: group payments loader WIP

payments: group payments loader WIP

--- client/landing/app/Applications/Dashboard.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Dashboard.kdapplication/AppController.coffee
@@ -103,14 +103,15 @@
   policyViewAdded: (pane, view) ->

   paymentViewAdded: (pane, view) ->
+
     paymentController = KD.getSingleton 'paymentController'

     group = @getData()

     group.fetchPaymentMethod (err, paymentMethod) ->
-      console.log { err, paymentMethod }
-    # paymentController.fetchBillingInfo 'group', (err, billingInfo) =>
-      view.setBillingInfo paymentMethod
+      return new KDNotificationView title: err.message  if err
+
+      view.setBillingInfo paymentMethod  if paymentMethod

     @paymentView = view

@@ -139,20 +140,28 @@

   createBillingInfoModal: ->

+    console.trace()
+
     paymentController = KD.getSingleton "paymentController"

     billingInfoModal = paymentController.createBillingInfoModal 'group', {}
+    billingInfoModal.showLoader()

-    paymentController.fetchBillingInfo 'group', (err, groupBillingInfo) ->
+    group = @getData()

-      if groupBillingInfo
-        billingInfoModal.setBillingInfo groupBillingInfo
+    group.fetchPaymentMethod (err, groupBillingMethod) ->
+
+      if groupBillingMethod
+        billingInfoModal.hideLoader()
+        billingInfoModal.setBillingInfo groupBillingMethod

       else
-        paymentController.fetchBillingInfo 'user', (err, personalBillingInfo) ->

-          if personalBillingInfo
-            { billing } = personalBillingInfo
+        KD.whoami().fetchPaymentMethods (err, personalBillingMethods) ->
+
+          billingInfoModal.hideLoader()
+
+          if personalBillingMethods

             useExistingModal = new KDModalView
               title: 'Would you like to link an existing payment method?'
@@ -161,12 +170,17 @@
                   style       : 'modal-cancel'
                   callback    : -> useExistingModal.destroy()

-            billingMethodView = new BillingMethodView {}, billing
-            useExistingModal.addSubView billingMethodView

-            billingMethodView.on 'BillingEditRequested', ->
-              billingInfoModal.setBillingInfo personalBillingInfo.billing
-              useExistingModal.destroy()
+            personalBillingMethods.forEach ({ accountCode, billing }) ->
+
+              billingMethodView = new BillingMethodView {}, billing
+              useExistingModal.addSubView billingMethodView
+              destroy = useExistingModal.bound 'destroy'
+              billingInfoModal.on 'KDObjectWillBeDestroyed', destroy
+
+              billingMethodView.on 'BillingEditRequested', ->
+                console.log { accountCode }
+                # group.addPaymentMethod

     return billingInfoModal


TokenizedInput: remove the menu reference for a consistent behavior

--- src/components/inputs/tokenizedinputview.coffee
+++ src/components/inputs/tokenizedinputview.coffee
@@ -109,7 +109,9 @@
   sanitizeInput: ->

   showMenu:(options = {}, data)->
+
     @menu?.destroy()
+    @menu = null

     return  unless @tokenInput and data.length


undid eyeLevel andgroundLevel changes

undid eyeLevel andgroundLevel changes

--- src/desktop/components/view_in_room/view.coffee
+++ src/desktop/components/view_in_room/view.coffee
@@ -11,11 +11,11 @@

   # Should be visually at about 57" from interstitial
   eyeLevel: ->
-    0.139 * @roomWidth
+    0.132 * @roomWidth

   # Should be visually at about 12" from interstitial
   groundLevel: ->
-    0.099 * @roomWidth
+    0.095 * @roomWidth

   events:
     'click .js-view-in-room-close': 'remove'


JPost: slug needs to be an object, not a string

--- workers/social/lib/social/models/messages/post.coffee
+++ workers/social/lib/social/models/messages/post.coffee
@@ -30,8 +30,8 @@

   schema = extend {}, jraphical.Message.schema, {
     isLowQuality  : Boolean
-    slug          : String
-    slug_         : String # this is necessary, because $exists operator won't work with a sparse index.
+    slug          : Object
+    slug_         : Object # this is necessary, because $exists operator won't work with a sparse index.
     group         : String
     counts        :
       followers   :


Github::fetchUsersRepos: multiple username supported version of fetchUserRepos

--- client/app/lib/extras/github/github.coffee
+++ client/app/lib/extras/github/github.coffee
@@ -21,9 +21,20 @@
     $.ajax req
     null

-  @fetchUserRepos = (username, callback = kd.noop, force)->

-    if force then @resetCache username
+  @fetchUsersRepos = (usernames, callback = kd.noop) ->
+    response = []
+
+    usernames.forEach (username, index) =>
+      @fetchUserRepos username, (err, repos) ->
+        response.push {username, err, repos}
+        callback null, response  if index is usernames.length - 1
+
+
+  @fetchUserRepos = (username, callback = kd.noop, force) ->
+
+    @resetCache username  if force
+
     if @_repoCache[username]?.length > 0
       callback null, @_repoCache[username]
       return
@@ -162,4 +173,3 @@
                            api calls, try again in #{remaining}."""

     callback options
-


Fix temporary error throwing to please linter.

--- client/app/lib/onboarding.coffee
+++ client/app/lib/onboarding.coffee
@@ -101,7 +101,7 @@

     # Trigger a 'done' pseudo-event, corresponding to onboarding end.
     triggerDone: ()->
-        throw 'Not implemented yet';
+        throw new Error 'Not implemented yet'

     # Returns an internal step by its name.


Pricing: team plan teamsize minvalue changed to 5

--- client/landing/Intro/views/teamplan.coffee
+++ client/landing/Intro/views/teamplan.coffee
@@ -16,7 +16,7 @@
       description       : "1x Resource pack contains 1 GB RAM 1x CPU, 1 GB RAM, 50 GB Disk, 2 TB Transfer, 5 total VMs and we shut it off after an hour for obvious reasons"
       unitPrice         : unitPrices.resourcePack
       slider            :
-        minValue        : 1
+        minValue        : 5
         maxValue        : 250
         interval        : 5
         snapOnDrag      : yes


Compound actions

Compound actions

Define compound actions that trigger a list of other actions. Example:

actions:
    mouseScrollUp:
        type: 'mouse'
        action: 'scroll'
        direction: 'up'
        magnitude: 20
    mouseScrollDown:
        type: 'mouse'
        action: 'scroll'
        direction: 'down'
        magnitude: 20
    keyboardButtonADown:
        type: 'keyboard'
        action: 'down'
        button: 'a'
    keyboardButtonAUp:
        type: 'keyboard'
        action: 'up'
        button: 'a'
    mouseMove:
        type: 'mouse'
        action: 'move'
    toggleFreeze:
        type: 'mouse'
        action: 'toggleFreeze'
        feedback:
            audio: 'asset/audio/mallet.ogg'
    keyboardButtonA:
        type: 'keyboard'
        action: 'tap'
        button: 'a'
    keyboardButtonB:
        type: 'keyboard'
        action: 'tap'
        button: 'b'
    keyboardButtonC:
        type: 'keyboard'
        action: 'tap'
        button: 'c'
    compoundActionTest:
        type: 'compound'
        actions:
         - 'keyboardButtonA'
         - 'keyboardButtonB'
         - 'keyboardButtonC'
        feedback:
            audio: 'asset/audio/mallet.ogg'

--- client/src/ActionController.coffee
+++ client/src/ActionController.coffee
@@ -99,6 +99,8 @@
         if(cmd.type == 'keyboard')
             if(cmd.action in ['up', 'down', 'tap'])
                 @keyboard cmd.action, cmd.button
+        if(cmd.type == 'compound')
+            @executeAction action for action in cmd.actions

     activateRecipe: (recipeName) =>
         recipe = @recipes[recipeName]


Check if body is empty before submitting

--- client/views/tasks/add_task.coffee
+++ client/views/tasks/add_task.coffee
@@ -3,21 +3,30 @@
     e.preventDefault()
     body = $('#new-task-text').val()
     $('#new-task-text').val("")
-    now = new Date()
-    priority = 'low'
-    list = 'Home'
-    Tasks.insert
-      body: body
-      dateDue: moment(now).add('w', 1).toDate()
-      dateCreated: now
-      dateCompleted: false
-      modified: now
-      list: list
-      priority: priority
-      completed: false
-      repeating: false
-    list = Lists.findOne
-      name: list
-    Lists.update list._id,
-      $inc:
-        numTodos: 1
+
+    if body
+      addEvent body
+      $('#addTaskInput').removeClass('has-warning')
+    else
+      $('#addTaskInput').addClass('has-warning')
+      $('#new-task-text').focus()
+
+addEvent = (taskTitle) ->
+  now = new Date()
+  priority = 'low'
+  list = 'Home'
+  Tasks.insert
+    body: taskTitle
+    dateDue: moment(now).add('w', 1).toDate()
+    dateCreated: now
+    dateCompleted: false
+    modified: now
+    list: list
+    priority: priority
+    completed: false
+    repeating: false
+  list = Lists.findOne
+    name: list
+  Lists.update list._id,
+    $inc:
+      numTodos: 1


Request listing only after we know RT capabilities

--- components/ListComponents.coffee
+++ components/ListComponents.coffee
@@ -4,9 +4,12 @@
 onRuntimeComponent = null

 subscribe = (runtime, port) ->
-  onRuntimeConnected = ->
+  requestListing = ->
+    return unless runtime.canDo 'protocol:component'
     runtime.sendComponent 'list'
     port.connect()
+
+  onRuntimeConnected = -> do requestListing
   onRuntimeComponent = (message) ->
     return unless message.command is 'component'
     return if message.payload.name in ['Graph', 'ReadDocument']
@@ -36,9 +39,9 @@
     port.send
       componentDefinition: definition

-  runtime.on 'connected', onRuntimeConnected
+  runtime.on 'capabilities', onRuntimeConnected
   runtime.on 'component', onRuntimeComponent
-  runtime.sendComponent 'list'
+  do requestListing if runtime.isConnected()

 unsubscribe = (runtime, port) ->
   port.disconnect()
@@ -56,7 +59,7 @@
     process: (event, payload) ->
       return unless event is 'data'
       unsubscribe c.runtime, c.outPorts.out if c.runtime
-      unless payload.canDo 'protocol:component'
+      if payload.isConnected() and not payload.canDo 'protocol:component'
         return c.error new Error "Runtime #{payload.definition.id} is not able to list components"
       c.runtime = payload
       subscribe c.runtime, c.outPorts.out


Removed window resize listener and event handler in KDTabView.

--- components/tabs/KDTabView.coffee
+++ components/tabs/KDTabView.coffee
@@ -14,7 +14,6 @@
     super options, data

     @setTabHandleContainer options.tabHandleContainer ? null
-    @listenWindowResize()

     @on "PaneRemoved", => @resizeTabHandles type : "PaneRemoved"
     @on "PaneAdded", (pane)=> @resizeTabHandles {type : "PaneAdded", pane}
@@ -254,7 +253,5 @@
       possiblePercent = (100 - containerMarginInPercent) / visibleHandles.length
       handle.setWidth(possiblePercent, "%") for handle in visibleHandles
     else
-
-  _windowDidResize:(event)=> @resizeTabHandles event
       handle.setWidth options.maxHandleWidth for handle in visibleHandles
   , 300


Maintain CSRF authenticity token

Maintain CSRF authenticity token

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -76,9 +76,10 @@
   for own key, value of pageCache
     pageCache[key] = null if key <= currentState.position - limit

-changePage = (title, body, runScripts) ->
+changePage = (title, body, csrfToken, runScripts) ->
   document.title = title
   document.documentElement.replaceChild body, document.body
+  CSRFToken.update csrfToken if csrfToken?
   removeNoscriptTags()
   executeScriptTags() if runScripts
   currentState = window.history.state
@@ -157,8 +158,18 @@

 extractTitleAndBody = (doc) ->
   title = doc.querySelector 'title'
-  [ title?.textContent, doc.body, 'runScripts' ]
-
+  [ title?.textContent, doc.body, CSRFToken.get(doc).token, 'runScripts' ]
+
+CSRFToken =
+  get: (doc = document) ->
+    node:   tag = doc.querySelector 'meta[name="csrf-token"]'
+    token:  tag?.getAttribute? 'content'
+
+  update: (latest) ->
+    current = @get()
+    if current.token? and latest? and current.token isnt latest
+      current.node.setAttribute 'content', latest
+
 browserCompatibleDocumentParser = ->
   createDocumentUsingParser = (html) ->
     (new DOMParser).parseFromString html, 'text/html'


start window.history.state.position with Date.now() at rememberCurrentUrl

As issued #84, opening different assets page using
turbolinks causes continuous the same page link loading.
This stems from(as mentioned #84) in some page loading fllow,
turbolinks caches a page with wrong state.position id.
Assigning Date.now() as starting window.history.state.position
will solve this problem.

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -84,7 +84,7 @@
     window.history.replaceState currentState, '', location

 rememberCurrentUrl = ->
-  window.history.replaceState { turbolinks: true, position: window.history.length - 1 }, '', document.location.href
+  window.history.replaceState { turbolinks: true, position: Date.now() }, '', document.location.href

 rememberCurrentState = ->
   currentState = window.history.state


Added placeholder widget to signature edit.

--- app/assets/javascripts/app/models/signature.coffee
+++ app/assets/javascripts/app/models/signature.coffee
@@ -5,7 +5,24 @@

   @configure_attributes = [
     { name: 'name',           display: 'Name',          tag: 'input',    type: 'text', limit: 100, 'null': false },
-    { name: 'body',           display: 'Text',          tag: 'richtext',               limit: 500, 'null': true, rows: 10 },
+    { name: 'body',           display: 'Text',          tag: 'richtext',               limit: 500, 'null': true, plugins: [
+      {
+        controller: 'WidgetPlaceholder'
+        params:
+          objects: [
+            {
+              prefix: 'ticket'
+              object: 'Ticket'
+              display: 'Ticket'
+            },
+            {
+              prefix: 'user'
+              object: 'User'
+              display: 'Current User'
+            },
+          ]
+      },
+    ]},
     { name: 'note',           display: 'Note',          tag: 'textarea', note: 'Notes are visible to agents only, never to customers.', limit: 250, 'null': true },
     { name: 'active',         display: 'Active',        tag: 'active',    default: true },
     { name: 'created_by_id',  display: 'Created by',    relation: 'User', readonly: 1 },


fixed constructor syntax

fixed constructor syntax

--- manager/app/assets/javascripts/stark.coffee
+++ manager/app/assets/javascripts/stark.coffee
@@ -39,7 +39,7 @@

   add_state: (state) ->
     # console.log "add_state"
-    s = new state
+    s = new state()
     @states[s.name] = state
     state.app = @
     @router.match(s.url, s.name)


try to return exports

try to return exports

--- mechanics/001-binary-stars/simulator.coffee
+++ mechanics/001-binary-stars/simulator.coffee
@@ -4,10 +4,10 @@
   a demo webworker for two-body simulation:
 ###
 define [
-    'underscore',
+    'exports',
     'cs!/wahlque/ode/euler',
     'cs!/wahlque/nbody/bodies2'
-], (_, solver, b2) ->
+], (exports, solver, b2) ->
     handle = 0
     start = (e) ->
         [m1, m2, x1, y1, vx1, vy1, x2, y2, vx2, vy2] = e.data
@@ -24,5 +24,5 @@
         start(e)
         true

-    true
+    exports


hide URL bar on mobile browsers

hide URL bar on mobile browsers

--- app/assets/transit/lib/views/application.js.coffee
+++ app/assets/transit/lib/views/application.js.coffee
@@ -13,6 +13,7 @@

   render: =>
     $(@el).html(@template())
+    window.scrollTo(0, 1) if $.browser.mobile
     this

   toggleFavorite: (evt) =>


oops js error

oops js error

--- app/scripts/controllers/buckets_controllers.coffee
+++ app/scripts/controllers/buckets_controllers.coffee
@@ -36,10 +36,6 @@
 ]).controller('BucketItem', ['$rootScope', '$http', '$scope', '$state', 'Bucket', 'flash', 'Allocation', ($rootScope, $http, $scope, $state, Bucket, flash, Allocation)->
   $scope.b.user_allocation_dollars = 0

-  $scope.selectText = ()->
-      document.querySelector('#myinput').select();
-    , 0
-
   $scope.resetField = ()->
     $scope.b.user_allocation_dollars = 0


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -122,7 +122,7 @@
   ]

   4.circularPoints (p, i) ->
-    equalEnough p.x, points[i].x, 000 "#{p.x} == #{points[i].x}"
-    equalEnough p.y, points[i].y, "#{p.y} == #{points[i].y}"
+    equalEnough p.x, points[i].x, 0.001 "#{p.x} == #{points[i].x}"
+    equalEnough p.y, points[i].y, 00"#{p.y} == #{points[i].y}"

 module undefined


Admin: Handle action and identifier in AppController and pass to views.

--- client/admin/lib/index.coffee
+++ client/admin/lib/index.coffee
@@ -60,7 +60,7 @@
     super options, data

-  openSection: (section, query) ->
+  openSection: (section, query, action, identifier) ->

     targetPane = null

@@ -68,6 +68,7 @@
       @mainView.tabs.panes.forEach (pane) ->
         if pane.getOption('slug') is section
           targetPane = pane
+          targetPane.routerParams = { action, identifier }  if action

       if   targetPane then @mainView.tabs.showPane targetPane
       else kd.singletons.router.handleRoute '/Admin/Settings'


String#underscore

String#underscore

--- test/string_extensions.coffee
+++ test/string_extensions.coffee
@@ -35,6 +35,11 @@
   equals "man from the boondocks".titleize(), "Man From The Boondocks"
   equals "x-men: the last stand".titleize(), "X Men: The Last Stand"

+test "#underscore", ->
+  equals "Pro-tip".underscore(), "pro_tip"
+  equals "Bullet".underscore(), "bullet"
+  equals "FPSCounter".underscore(), "fps_counter"
+
 test "#withoutExtension", ->
   equals "neat.png".withoutExtension(), "neat"
   equals "not a file".withoutExtension(), "not a file"


Fix the DateTimePicker to work when the back button is used

--- SingularityUI/app/components/common/atomicFormItems/DateEntry.cjsx
+++ SingularityUI/app/components/common/atomicFormItems/DateEntry.cjsx
@@ -3,7 +3,7 @@

 DateEntry = React.createClass

-    componentDidMount: ->
+    initializeDateTimePicker: ->
         id = '#' + @props.id
         changeFn = @props.prop.updateFn
         $ -> $(id).datetimepicker({
@@ -39,7 +39,7 @@
                         customClass: @props.prop.customClass
                     }}
                 />
-
+
                     <Glyphicon iconClass="calendar"/>


give id to region

give id to region

--- tutor/src/components/course-calendar/index.cjsx
+++ tutor/src/components/course-calendar/index.cjsx
@@ -21,7 +21,7 @@
     Handler = displayAsHandler[displayAs]

     plansList = if hasPeriods then loadPlansList?() else []
-    <TourRegion courseId={courseId} tourIds={['teach-new-preview']}>
+    <TourRegion id="teacher-calendar" courseId={courseId}>
       <Handler {...@props} plansList={plansList} ref='calendarHandler'/>
     </TourRegion>


mobile adjustments

mobile adjustments

--- client/views/home/home.coffee
+++ client/views/home/home.coffee
@@ -74,8 +74,17 @@
       $('body').empty()
       Blaze.render(Template.home, $('body')[0])

+    width = $(window).width()
+    maxRounds = 15

-    if round < 15
+    switch
+      when width < 386 then maxRounds = 6
+      when width < 459 then maxRounds = 8
+      when width < 532 then maxRounds = 10
+      when width < 605 then maxRounds = 12
+      when width < 678 then maxRounds = 14
+
+    if round < maxRounds
       if rows == cols then rows++ else cols++
       setTimes()
       refreshTemplate()


Improved auto focus of login fields.

Improved auto focus of login fields.

--- app/assets/javascripts/app/controllers/login.js.coffee
+++ app/assets/javascripts/app/controllers/login.js.coffee
@@ -41,14 +41,24 @@

     @html App.view('login')(
       item:           data,
-      auth_providers: auth_providers
+      auth_providers: auth_providers,
     )
-    if $(@el).find('[name="username"]').val()
+
+    # set focus
+    if !$(@el).find('[name="username"]').val()
       $(@el).find('[name="username"]').focus()
-
+    else
+      $(@el).find('[name="password"]').focus()
+
+    # scroll to top
+    @scrollTo()
+
   login: (e) ->
     e.preventDefault()
     params = @formParam(e.target)
+
+    # remember username
+    @username = params['username']

     # session create with login/password
     auth = new App.Auth


fix wording of switchView button

fix wording of switchView button

--- app/assets/javascripts/app/controllers/users.js.coffee
+++ app/assets/javascripts/app/controllers/users.js.coffee
@@ -59,7 +59,7 @@
       header

     callbackAttributes = (value, object, attribute, header, refObject) ->
-      text                  = App.i18n.translateInline('Show as user')
+      text                  = App.i18n.translateInline("View from user's perspective")
       value                 = ' '
       attribute.raw         = ' <svg class="icon icon-switchView"><use xlink:href="#icon-switchView" /></svg> ' + text + ''
       attribute.class       = ''


Resolve unroll working period error

Resolve unroll working period error

--- app/assets/javascripts/backend/interventions.js.coffee
+++ app/assets/javascripts/backend/interventions.js.coffee
@@ -349,9 +349,9 @@

   $(document).on "keyup change dp.change", ".nested-fields.working-period:first-child input.intervention-started-at", (e) ->
     value = $(this).val()
-    $('#intervention_working_periods_attributes_0_stopped_at').val(moment(new Date(value)).add(1, 'hours').format('Y-MM-DD H:m'))
-    $(this).each ->
-      E.interventions.updateAvailabilityInstant()
+    started_at = $('#intervention_working_periods_attributes_0_started_at').val()
+    $(this).each ->
+      E.interventions.updateAvailabilityInstant(started_at)

   $(document).on "selector:change", 'input[data-selector-id="intervention_doer_product_id"], input[data-selector-id="intervention_tool_product_id"]', (event) ->


updated touch device and browser warning copy to be closer to that requested in issue 433

--- app/assets/javascripts/components/browser-warning.cjsx
+++ app/assets/javascripts/components/browser-warning.cjsx
@@ -44,12 +44,12 @@

-
Please note: Your browser/device may not work well with {project.title}.

       { if @state.isTouchDevice
-
Touch interfaces aren't well supported.

+
Welcome! Thanks for your interest in {project.title}. The app is designed for desktops. For the full experience, come back later from your laptop or desktop.

+
         else

-
For the best experience, please use the most recent version of one of the following supported browsers:

+
Welcome! Thanks for your interest in {project.title}. Please note that your browser may not work well here. For the best experience, please use the most recent version of one of the following supported browsers:

Firefox

Chrome


Toggleability for koiHover.

Toggleability for koiHover.

--- app/assets/javascripts/koi/lib/jquery/koi-hover.coffee
+++ app/assets/javascripts/koi/lib/jquery/koi-hover.coffee
@@ -2,7 +2,8 @@

 $.fn.koiHover = ( args... ) ->
   delay = if typeof args[0] is 'number' then args.shift() else 0
-  [ over, leave ] = args
+  over = args.shift()
+  leave = args.shift() or over

   @each =>
     nowRunning = false


Use linear interpolation instead of cardinal splines.

--- app/assets/javascripts/pgdatagraph/datagraph.js.coffee
+++ app/assets/javascripts/pgdatagraph/datagraph.js.coffee
@@ -139,6 +139,7 @@
       preserve: yes
       series: series
       renderer: "multi"
+      interpolation: "linear"
       dotSize: 2
       unstack: @options.unstack

@@ -183,6 +184,7 @@
       preserve: yes
       series: series
       renderer: "multi"
+      interpolation: "linear"
       unstack: @options.unstack
       ticksTreatment: "glow"


Fix ruby like array convert

Fix ruby like array convert

--- app/assets/javascripts/services/product_service.coffee
+++ app/assets/javascripts/services/product_service.coffee
@@ -3,10 +3,15 @@
   '$routeParams'
   ($http, $routeParams)->
     #handle price as a float, not a string
+    convertProduct = (product)->
+      product.price = parseFloat(product.price)
+
     convert = (resp)->
-      products = Array(resp.data.products || resp.data.product)
-      for product in products
-        product.price = parseFloat(product.price)
+      products = resp.data.products
+      if products?
+        convertProduct(product) for product in products
+      else
+        convertProduct(resp.data.product)
       resp

     destructure = (resp)-> resp.data.product || resp.data.products


Add Infinite Placeholder

Add Infinite Placeholder

--- app/components/multi-image-subject-options-editor.cjsx
+++ app/components/multi-image-subject-options-editor.cjsx
@@ -60,8 +60,17 @@

           Flipbook Play Iterations {' '}
-          <input type="number" id="flipbook_play_iterations" value={iterations} name="flipbook_play_iterations" min="1" max="100" step="1" onChange={@toggleInfiniteLoop} />

-          An empty iteration value denotes infinite loop.
+          <input
+            type="number"
+            id="flipbook_play_iterations"
+            placeholder="∞"
+            value={iterations}
+            name="flipbook_play_iterations"
+            min="1"
+            max="100"
+            step="1"
+            onChange={@toggleInfiniteLoop}
+          />

     }</ChangeListener>


Do not compare functions and objects if observeNot is used.

--- app/assets/javascripts/app/controllers/_application_controller_generic.coffee
+++ app/assets/javascripts/app/controllers/_application_controller_generic.coffee
@@ -1117,7 +1117,7 @@
           currentAttributes[key] = object[key]
     if @observeNot
       for key, value of object
-        if !@observeNot[key] && !_.isFunction(value)
+        if !@observeNot[key] && !_.isFunction(value) && !_.isObject(value)
           currentAttributes[key] = value

     if !@lastAttributres


Teamwork: Fixed a typo.

Teamwork: Fixed a typo.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
@@ -121,7 +121,7 @@
       callback   : => @handleRun panel

   getPlaygroundClass: (playground) ->
-    return if playground is "FacebookBook" then FacebookTeamwork else PlaygroundTeamwork
+    return if playground is "Facebook" then FacebookTeamwork else PlaygroundTeamwork

   handleRun: (panel) ->
     console.warn "You should override this method."


Teamwork: Send default size options and fix sharable terminal mode.

--- client/landing/CollaborativeWorkspace/panes/sharableclientterminalpane.coffee
+++ client/landing/CollaborativeWorkspace/panes/sharableclientterminalpane.coffee
@@ -7,13 +7,15 @@
     options.vmRegion = sessionOptions.vmRegion
     options.joinUser = sessionOptions.host
     options.session  = sessionOptions.key
+    options.sizeX    = 100
+    options.sizeY    = 100
     options.delay    = 0

     super options, data

   vmOn: -> Promise.resolve()

-  getMode: -> 'shared'
+  getMode: -> 'resume'

   fetchVm: (callback)->
     {vmName, vmRegion} = @getOptions()


Change 'per' ref in polls page

Change 'per' ref in polls page

--- angular/core/components/polls_page/polls_page.coffee
+++ angular/core/components/polls_page/polls_page.coffee
@@ -30,7 +30,7 @@

   @searchPolls = =>
     if @fragment
-      Records.polls.search(@fragment, per: per)
+      Records.polls.search(@fragment, per: 10)
     else
       $q.when()
   LoadingService.applyLoadingFunction @, 'searchPolls'


allow passing toolbar and status bar options via PageEditor options.

--- app/assets/javascripts/mercury/page_editor.js.coffee
+++ app/assets/javascripts/mercury/page_editor.js.coffee
@@ -24,8 +24,8 @@
     @iframe = jQuery('<iframe>', {id: 'mercury_iframe', class: 'mercury-iframe', frameborder: '0', src: 'about:blank'})
     @iframe.appendTo(jQuery(@options.appendTo).get(0) ? 'body')

-    @toolbar = new Mercury.Toolbar(@options)
-    @statusbar = new Mercury.Statusbar(@options)
+    @toolbar = new Mercury.Toolbar(jQuery.extend(true, {}, @options, @options.toolbarOptions))
+    @statusbar = new Mercury.Statusbar(jQuery.extend(true, {}, @options, @options.statusbarOptions))
     @resize()

     @iframe.on 'load', => @initializeFrame()


BF-119: Changing PullDownloadLimit

BF-119: Changing PullDownloadLimit

--- app/assets/javascripts/model/binary/pullqueue.coffee
+++ app/assets/javascripts/model/binary/pullqueue.coffee
@@ -7,7 +7,7 @@
 PullQueue =

   # Constants
-  PULL_DOWNLOAD_LIMIT : 1
+  PULL_DOWNLOAD_LIMIT : 10

   queue : []
   pullLoadingCount : 0


Fix project persistence

Fix project persistence

--- app/assets/javascripts/models/data/project.js.coffee
+++ app/assets/javascripts/models/data/project.js.coffee
@@ -101,6 +101,7 @@
         write: @_fromQuery
         owner: this
         deferEvaluation: true
+      @_pending = ko.observable(null)

     _computeAllReadyToDownload: ->
       return false for ds in @accessDatasets() when !ds.serviceOptions.readyToDownload()
@@ -191,7 +192,7 @@
         @_datasetsById[id]

     _toQuery: ->
-      return @_pending if @_pending?
+      return @_pending() if @_pending()?
       result = $.extend({}, @query.serialize())
       datasets = [@focus()].concat(@datasets())
       ids = (ds?.id ? '' for ds in datasets)
@@ -213,9 +214,9 @@
           datasetIds = datasetIdStr.split('!')
           focused = !!datasetIds[0]
           datasetIds.shift() unless focused
-          @_pending = value
+          @_pending(value)
           DatasetsModel.forIds datasetIds, @query, (datasets) =>
-            @_pending = null
+            @_pending(null)
             pending = @_pendingAccess ? {}
             for dataset, i in datasets
               query = value["p#{i}"]


Bare styling

Bare styling

--- core/app/backbone/views/sub_comments/sub_comments_list_view.coffee
+++ core/app/backbone/views/sub_comments/sub_comments_list_view.coffee
@@ -22,3 +22,11 @@
 class window.NDPSubCommentsListView extends SubCommentsListView
   itemView: NDPSubCommentView
   addView: NDPSubCommentsAddView
+
+  className: ''
+
+  template:
+    text: """
+

+

+    """


Removed old socketio code

Removed old socketio code

--- app/scripts/services/signal_service_socket_io.coffee
+++ app/scripts/services/signal_service_socket_io.coffee
@@ -1,45 +0,0 @@
-namespace 'camfire', (exports) ->
-  class exports.SignalServiceSocketIo
-    constructor: (@$rootScope, @config, @socketService, @utilService, @peerConnectionService,
-                  @localMediaService, @remoteMediaService) ->
-
-    init: ->
-      @socketService.on "signal", @signal
-
-    signal: (msg) =>
-      debug.debug("Attempting to parse signal: " + msg)
-      json = @utilService.parseSignal(msg)
-      debug.info "'" + json.type + "'" + " signal received..."
-      if json.type is "candidate"
-        debug.debug "Creating ICE candidate from server..."
-        iceCandidate = new RTCIceCandidate(json.data)
-        debug.debug "Adding ICE candidate from server to peer connection..."
-        @peerConnectionService.addIceCandidate iceCandidate
-      else if json.type is "answer"
-        debug.debug "Creating remote description..."
-        remoteDescription = new RTCSessionDescription(json.data)
-        debug.debug "Setting remote description..."
-        debug.debug remoteDescription
-        @peerConnectionService.setRemoteDescription remoteDescription
-      else if json.type is "offer"
-        # TODO: The first part is exactly the same as the answer block, make it DRY
-        debug.debug "Creating remote description..."
-        remoteDescription = new RTCSessionDescription(json.data)
-        @peerConnectionService.setRemoteDescription remoteDescription
-      else if json.type is "offer"
-        @peerConnectionService.createAnswer(@configurationService.sdpConstraints)
-      else if (json.type is "new-stream")
-        debug.debug("Adding new stream info...")
-        @remoteMediaService.addAvailableStreamInfo(json.data)
-      else if (json.type is "remove-stream")
-        debug.debug("Removing stream info...")
-        debug.debug(json.data)
-        @remoteMediaService.removeStreamInfo(json.data)
-      else if (json.type is "session-id-hash")
-        debug.debug json.data
-        @localMediaService.sessionIdHash = json.data
-      else
-        # Unknown message
-        debug.error "Invalid message!"
-
-  exports.app.service 'signalServiceSocketIo', camfire.SignalServiceSocketIo


Do not do finds within the event method. Decreases likelihood of hitting bug in older versions of Chrome. https://edx.lighthouseapp.com/projects/102637/tickets/153

--- common/lib/xmodule/xmodule/js/src/html/edit.coffee
+++ common/lib/xmodule/xmodule/js/src/html/edit.coffee
@@ -10,7 +10,8 @@
       lineWrapping: true
     })

-    $(@advanced_editor.getWrapperElement()).addClass(HTMLEditingDescriptor.isInactiveClass)
+    @$advancedEditorWrapper = $(@advanced_editor.getWrapperElement())
+    @$advancedEditorWrapper.addClass(HTMLEditingDescriptor.isInactiveClass)

 #   This is a workaround for the fact that tinyMCE's baseURL property is not getting correctly set on AWS
 #   instances (like sandbox). It is not necessary to explicitly set baseURL when running locally.
@@ -43,16 +44,21 @@
       theme_advanced_blockformats : "p,pre,h1,h2,h3",
       width: '100%',
       height: '400px',
-      setup : HTMLEditingDescriptor.setupTinyMCE,
+      setup : @setupTinyMCE,
       # Cannot get access to tinyMCE Editor instance (for focusing) until after it is rendered.
       # The tinyMCE callback passes in the editor as a paramter.
       init_instance_callback: @focusVisualEditor
     })

     @showingVisualEditor = true
-    @element.on('click', '.editor-tabs .tab', this, @onSwitchEditor)
+    # Doing these find operations within onSwitchEditor leads to sporadic failures on Chrome (version 20 and older).
+    $element = $(element)
+    @$htmlTab = $element.find('.html-tab')
+    @$visualTab = $element.find('.visual-tab')

-  @setupTinyMCE: (ed) ->
+    @element.on('click', '.editor-tabs .tab', @onSwitchEditor)
+
+  setupTinyMCE: (ed) =>
     ed.addButton('wrapAsCode', {
       title : 'Code',
       image : '/static/images/ico-tinymce-code.png',
@@ -67,22 +73,23 @@
       command.setActive('wrapAsCode', e.nodeName == 'CODE')
     )

-  onSwitchEditor: (e)=>
+    @visualEditor = ed
+
+  onSwitchEditor: (e) =>
     e.preventDefault();

-    if not $(e.currentTarget).hasClass('current')
-      element = e.data.element
+    $currentTarget = $(e.currentTarget)
+    if not $currentTarget.hasClass('current')
+      $currentTarget.addClass('current')
+      @$mceToolbar.toggleClass(HTMLEditingDescriptor.isInactiveClass)
+      @$advancedEditorWrapper.toggleClass(HTMLEditingDescriptor.isInactiveClass)

-      $(e.currentTarget).addClass('current')
-      $(element).find('table.mceToolbar').toggleClass(HTMLEditingDescriptor.isInactiveClass)
-      $(@advanced_editor.getWrapperElement()).toggleClass(HTMLEditingDescriptor.isInactiveClass)
-
-      visualEditor = @getVisualEditor(element)
-      if $(e.currentTarget).attr('data-tab') is 'visual'
-        $(element).find('.html-tab').removeClass('current')
+      visualEditor = @getVisualEditor()
+      if $currentTarget.data('tab') is 'visual'
+        @$htmlTab.removeClass('current')
         @showVisualEditor(visualEditor)
       else
-        $(element).find('.visual-tab').removeClass('current')
+        @$visualTab.removeClass('current')
         @showAdvancedEditor(visualEditor)

   # Show the Advanced (codemirror) Editor. Pulled out as a helper method for unit testing.
@@ -104,20 +111,24 @@
     @focusVisualEditor(visualEditor)
     @showingVisualEditor = true

-  focusVisualEditor: (visualEditor) ->
+  focusVisualEditor: (visualEditor) =>
     visualEditor.focus()
+    if not @$mceToolbar?
+      @$mceToolbar = $(@element).find('table.mceToolbar')

-  getVisualEditor: (element) ->
+  getVisualEditor: () ->
     ###
     Returns the instance of TinyMCE.
     This is different from the textarea that exists in the HTML template (@tiny_mce_textarea.
+
+    Pulled out as a helper method for unit test.
     ###
-    return tinyMCE.get($(element).find('.tiny-mce').attr('id'))
+    return @visualEditor

   save: ->
     @element.off('click', '.editor-tabs .tab', @onSwitchEditor)
     text = @advanced_editor.getValue()
-    visualEditor = @getVisualEditor(@element)
+    visualEditor = @getVisualEditor()
     if @showingVisualEditor and visualEditor.isDirty()
       text = visualEditor.getContent({no_events: 1})
     data: text


JProvisioner: add missing group field

JProvisioner: add missing group field

--- workers/social/lib/social/models/computeproviders/provisioner.coffee
+++ workers/social/lib/social/models/computeproviders/provisioner.coffee
@@ -97,6 +97,8 @@
       originId        :
         type          : ObjectId
         required      : yes
+
+      group           : String

       meta            : require 'bongo/bundles/meta'

@@ -168,6 +170,8 @@
       checkData delegate, data, (err, data)->

         if err? then return callback err
+
+        data.group = client.context.group

         provisioner = new JProvisioner data
         provisioner.save (err)->


Dia: Minor fix

Dia: Minor fix

--- client/Framework/components/dia/kddiascene.coffee
+++ client/Framework/components/dia/kddiascene.coffee
@@ -124,7 +124,7 @@
               @activeJoints.push joint

   setPassiveDia:()->
-
+

   deselectAllDias:->
     joint.off 'DeleteRequested'      for joint in @activeJoints
@@ -173,9 +173,6 @@
         @realContext.strokeStyle = @getOption 'lineColorActive'
       else
         @realContext.strokeStyle = @getOption 'lineColor'
-
-      if (source.dia in @passiveDias) or (target.dia in @passiveDias)
-        @realContext.strokeStyle = @getOption 'lineColorPassive'

       sJoint = source.dia.getJointPos source.joint
       tJoint = target.dia.getJointPos target.joint


styleguide: Removed unneeded fat arrow

styleguide: Removed unneeded fat arrow

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

       # If the snapshot does not exist in mongo, remove it from
       # the machine after confirming with the user
-      askFor 'reinitNoSnapshot', =>
-        jMachine.removeSnapshot (err) =>
+      askFor 'reinitNoSnapshot', ->
+        jMachine.removeSnapshot (err) ->
           return kd.error  if err
           startReinit()


close the group channel before registering

registering will add a member to the koding group, which will in turn cause the channel to cycle, which will in turn cause the channel to be authenticated again, which will then result in a leaked guest account.  To avoid this, we should close the channel first.  I learned that after 2 days of debugging.

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -181,6 +181,11 @@
     @registerForm.notificationsDisabled = yes
     @registerForm.notification?.destroy()

+    # we need to close the group channel so we don't receive the cycleChannel event.
+    # getting the cycleChannel even for our own MemberAdded can cause a race condition
+    # that'll leak a guest account.
+    KD.getSingleton('groupsController').groupChannel.close()
+
     KD.remote.api.JUser.convert formData, (err, replacementToken)=>
       account = KD.whoami()
       @registerForm.button.hideLoader()
@@ -202,7 +207,7 @@
           title     : 'Good to go, Enjoy!'
           # content   : 'Successfully registered!'
           duration  : 2000
-          @showInstructionsBookIfFirstLogin()
+        @showInstructionsBookIfFirstLogin()

         # send information to mixpanel
         KD.track 'UserLogin', 'UserRegistered',


Force IDB schema upgrade before installing/uninstalling all docs

--- assets/javascripts/views/content/offline_page.coffee
+++ assets/javascripts/views/content/offline_page.coffee
@@ -58,6 +58,7 @@
       link.parentNode.innerHTML = "#{link.textContent.replace(/e$/, '')}ing…"
     else if action = link.getAttribute('data-action-all')
       $.stopEvent(event)
+      app.db.migrate()
       el.click() for el in @findAll("a[data-action='#{action}']")
     return


fixed highlighting

fixed highlighting

--- core/app/backbone/views/auto_completes_view.coffee
+++ core/app/backbone/views/auto_completes_view.coffee
@@ -68,7 +68,7 @@

   itemViewOptions: =>
     return {
-      query: @options.mainView._lastKnownSearchValue,
+      query: @search_collection.query
       parent: @options.mainView
     }


Toggle opening and closing popover

Toggle opening and closing popover

--- core/app/backbone/views/facts/top_fact_view.coffee
+++ core/app/backbone/views/facts/top_fact_view.coffee
@@ -8,15 +8,18 @@
   events:
     'click .js-share': 'toggleConversation'

-  initialize: ->
-    @popoverAdd '.js-share',
-      side: 'bottom'
-      align: 'center'
-      contentView: new StartConversationModalWindowView(model: @model)
+  toggleConversation: ->
+    @_startConversationOpened = !@_startConversationOpened

-  toggleConversation: ->
-    # @startConversationRegion.$el.toggle()
-    mp_track "Factlink: Toggle conversation popover"
+    if @_startConversationOpened
+      @popoverAdd '.js-share',
+        side: 'bottom'
+        align: 'center'
+        fadeTime: 100
+        contentView: new StartConversationModalWindowView(model: @model)
+      mp_track "Factlink: Toggle conversation popover"
+    else
+      @popoverRemove '.js-share'

 class window.TopFactView extends Backbone.Marionette.Layout


auto-export display of groups

auto-export display of groups

--- chrome/content/zotero-better-bibtex/preferences.coffee
+++ chrome/content/zotero-better-bibtex/preferences.coffee
@@ -106,11 +106,16 @@
       itemNode = document.createElement('listitem')
       itemNode.setAttribute('value', ae.id)

-      name = if ae.collection == 'library' then 'Library' else (Zotero.Collections.get(ae.collection)?.name || ae.collection)
+      name = switch
+        when ae.collection == 'library' then 'Library'
+        when m = /^group:([0-9]+)$/.match(ae.collection) then Zotero.Groups.get(m[1])?.name || ae.collection
+        else Zotero.Collections.get(ae.collection)?.name || "collection:#{ae.collection}"
+
       itemNode.setAttribute('label', "#{name} -> #{ae.path.replace(/^.*[\\\/]/, '')}")
       itemNode.setAttribute('class', "export-state-#{if Zotero.BetterBibTeX.auto.running == ae.id then 'running' else ae.status}")
       itemNode.setAttribute('tooltiptext', "#{@collectionPath(ae.collection)} -> #{ae.path}")
       exportlist.appendChild(itemNode)
+
   if selectedExport >= 0
     @autoexport.selected(selectedIndex)
   else
@@ -133,7 +138,10 @@
     ae = Zotero.DB.rowQuery('select * from betterbibtex.autoexport ae join betterbibtex.exportoptions eo on ae.exportoptions = eo.id where ae.id = ?', [selectedItem.getAttribute('value')])

     Zotero.BetterBibTeX.pref.display('id-better-bibtex-preferences-auto-export-status', ae.status)
-    name = if ae.collection == 'library' then 'Library' else (Zotero.Collections.get(ae.collection)?.name || ae.collection)
+    name = switch
+      when ae.collection == 'library' then 'Library'
+      when m = /^group:([0-9]+)$/.match(ae.collection) then Zotero.Groups.get(m[1])?.name || ae.collection
+      else Zotero.Collections.get(ae.collection)?.name || "collection:#{ae.collection}"
     Zotero.BetterBibTeX.pref.display('id-better-bibtex-preferences-auto-export-collection', name)
     Zotero.BetterBibTeX.pref.display('id-better-bibtex-preferences-auto-export-target', ae.path)
     Zotero.BetterBibTeX.pref.display('id-better-bibtex-preferences-auto-export-translator', Zotero.BetterBibTeX.translatorName(ae.translatorID))


only open content view if the pointer points to something with one

--- scripts/models/content/toc-pointer-node.coffee
+++ scripts/models/content/toc-pointer-node.coffee
@@ -12,6 +12,14 @@
       @model = options.model
       @mediaType = @model.mediaType
       @accept = @model.accept
+
+      # When contentView is null then clicking on it will not open
+      # the content in the content pane.
+      # A Book has a null contentView but a Module has a contentView
+      # so, only set the contentView if one is set
+      if @model.contentView
+        @contentView = (callback) => @model.contentView(callback)
+

       # Should be used ONLY for serializing to HTML tree
       @id = @model.id
@@ -38,4 +46,4 @@
     # Existence of this method means this is a pointer node
     dereferencePointer: () -> @model

-    contentView: (callback) -> @model.contentView(callback)
+    contentView: null


AdminModal: Copy change for allowed domains.

--- client/admin/lib/views/groupgeneralsettingsview.coffee
+++ client/admin/lib/views/groupgeneralsettingsview.coffee
@@ -70,7 +70,7 @@

     @addInput form,
       label        : 'Allowed Domains'
-      description  : 'Allow sign up with a company email address. Please type a domain or multiple domains separated by commas.'
+      description  : 'Allow anyone to sign up with an email address from a domain you specify here. If you need to enter multiple domains, please separate them by commas. e.g. acme.com, acme-inc.com'
       name         : 'domains'
       placeholder  : 'domain.com, other.edu'
       defaultValue : Encoder.htmlDecode group.allowedDomains?.join(', ') ? ''


Fix replace of searchText

Fix replace of searchText

--- client/ide/views/contentsearch/idecontentsearch.coffee
+++ client/ide/views/contentsearch/idecontentsearch.coffee
@@ -77,7 +77,7 @@
       splitText = splitText.map grepEscapeRegExp
       searchText = splitText.join "\\n"

-    searchText     = searchText.replace new RegExp "\\\'", "g", "'\\''"
+    searchText     = searchText.replace (new RegExp "\\\'", "g"), "'\\''"
     searchText     = searchText.replace /-/g, "\\-"

     flags           = [


Apps: Fixed app source download.

Apps: Fixed app source download.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -610,13 +610,13 @@
         return

       @vmController.run
-        kiteName    : "applications"
-        method      : "downloadApp"
-        withArgs    :
-          owner     : manifest.authorNick
-          appName   : manifest.name
-          appPath   : @getAppPath manifest
-          version   : manifest.version
+        kiteName     : "os"
+        method       : "app.download"
+        withArgs     :
+          owner      : manifest.authorNick
+          identifier : manifest.identifier
+          appPath    : @getAppPath manifest
+          version    : manifest.version
       , (err, res)=>
         if err
           warn err


Fix fair_info route

Fix fair_info route

--- apps/fair_info/routes.coffee
+++ apps/fair_info/routes.coffee
@@ -2,7 +2,7 @@
 Profile = require '../../models/profile'

 @assignFair = (req, res, next) ->
-  res.locals.fair = res.locals.profile.related().owner
+  res.locals.fair = res.locals.profile?.related().owner
   next()

 @index = (req, res) ->


This was causing infinite loop for not valid Develop routes

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -132,7 +132,8 @@
       @_loadedOnce = yes
       if err or not Array.isArray files or files.length is 0
         @putAppsToAppStorage {}
-        callback()  for callback in @_fetchQueue
+        unless err
+          callback()  for callback in @_fetchQueue
         @_fetchQueue = []
       else
         apps = @filterAppsFromFileList files


Moved className and template and inlined template

--- core/app/backbone/views/sub_comments/sub_comments_add_view.coffee
+++ core/app/backbone/views/sub_comments/sub_comments_add_view.coffee
@@ -1,10 +1,6 @@
 class BaseSubCommentsAddView extends Backbone.Marionette.Layout
   _.extend @prototype, Backbone.Factlink.AddModelToCollectionMixin,
                        Backbone.Factlink.AlertMixin
-
-  className: 'evidence-sub-comments-form'
-
-  template: 'sub_comments/add_view'

   events:
     'click .js-submit': 'submit'
@@ -66,5 +62,24 @@
     @ui.submit.prop('disabled',true ).text('Posting...')

 class window.SubCommentsAddView extends BaseSubCommentsAddView
+  className: 'evidence-sub-comments-form'
+  template: 'sub_comments/add_view'

 class window.NDPSubCommentsAddView extends BaseSubCommentsAddView
+
+  template:
+    text: """
+

+        Your comment could not be posted, please try again.
+        x
+

+
+
+

+
+
+

+        <button class="evidence-sub-comments-button btn btn-primary pull-right js-submit">Comment</button>
+

+    """