KodingKontrol: followConnectionStates: fire events only for running machine kites [fixes #107480008]

--- client/app/lib/kite/kodingkontrol.coffee
+++ client/app/lib/kite/kodingkontrol.coffee
@@ -191,9 +191,13 @@
     # Machine.uid is kite correlation name
     cc = kd.singletons.computeController

-    kite.on 'open',      cc.lazyBound 'emit', "connected-#{machineUId}"
-    kite.on 'close',     cc.lazyBound 'emit', "disconnected-#{machineUId}"
-    kite.on 'reconnect', cc.lazyBound 'emit', "reconnecting-#{machineUId}"
+    emit = (status) ->
+      if cc.findMachineFromMachineUId(machineUId)?.isRunning()
+        cc.emit "#{status}-#{machineUId}"
+
+    kite.on 'open',      -> emit 'connected'
+    kite.on 'close',     -> emit 'disconnected'
+    kite.on 'reconnect', -> emit 'reconnecting'

   getKite: (options = {}) ->


ChatMessagePane: Added existence check.

ChatMessagePane: Added existence check.

--- client/ide/views/chat/messagepane.coffee
+++ client/ide/views/chat/messagepane.coffee
@@ -20,7 +20,7 @@
     @input.input.on 'focus', (event) => @handleFocus yes, event

-    @once 'NewParticipantButtonClicked', => @onboarding.destroy()
+    @once 'NewParticipantButtonClicked', => @onboarding?.destroy()

   handleThresholdReached: ->


currentUser should be an Ember object

currentUser should be an Ember object

--- app/assets/javascripts/controllers/session_controller.js.coffee
+++ app/assets/javascripts/controllers/session_controller.js.coffee
@@ -1,10 +1,9 @@
 Bandango.SessionController = Ember.Controller.extend
   logInAs: (user) ->
     Ember.debug "logging in with token #{user.token}"
-    @set "user", user
+    @set "user", Ember.Object.create(user)
     $.ajaxSetup
       headers: { "Authorization": "Token token=\"#{user.token}\""}
-    window.location.hash = "#/facturas"

   loggedIn: (->
     !!@get("user")


Angularjs detecs read feeds by checking if they have <=0 unread entries, instead of ==0.

Normally unread entries should never be negative, but just in case...

--- app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
@@ -106,7 +106,7 @@
     remove_read_feeds: ->
       if !$rootScope.read_feeds_shown
         read_feeds = $filter('filter') $rootScope.feeds, (feed)->
-          return feed.unread_entries == 0
+          return feed.unread_entries <= 0
         for feed in read_feeds
           remove_feed feed


SocialWorker: Tests: add test case for fetchOrCreateAppStorage migration case

--- workers/social/lib/social/models/account.test.coffee
+++ workers/social/lib/social/models/account.test.coffee
@@ -308,6 +308,61 @@
           daisy queue

+    describe 'when an old app storage exists', ->
+
+      it 'should be able migrate it to JCombinedAppStorage', (done) ->
+
+        withConvertedUser ({ account }) ->
+
+          appId               = generateRandomString()
+          version             = '1.0.0'
+          options             = { appId, version }
+          bucket              = {}
+          oldStorage          = null
+          relationshipOptions = {}
+
+          queue = [
+
+            ->
+              # creating an old app storage document
+              options = { account, appId, version }
+              createOldAppStorageDocument options, (err, data) ->
+                expect(err).to.not.exist
+                { storage : oldStorage, relationshipOptions, bucket } = data
+                expect(oldStorage.bongo_.constructorName).to.be.equal 'JAppStorage'
+                queue.next()
+
+            ->
+              # expecting app storage to be migrated
+              account.fetchOrCreateAppStorage options, (err, newStorage) ->
+                expect(err).to.not.exist
+                expect(newStorage.bongo_.constructorName).to.be.equal 'JCombinedAppStorage'
+                expect(newStorage.accountId).to.be.deep.equal account._id
+                expect(newStorage.storage[appId].data).to.be.deep.equal bucket
+                expect(newStorage.storage.bucket).to.not.exist
+                expect(newStorage.storage.version).to.not.exist
+                queue.next()
+
+            ->
+              # expecting old appstorage to be deleted
+              JAppStorage.one { _id : oldStorage._id }, (err, storage) ->
+                expect(err).to.not.exist
+                expect(storage).to.not.exist
+                queue.next()
+
+            ->
+              # expecting relationship to be deleted
+              expectRelation.toNotExist relationshipOptions, (err) ->
+                expect(err).to.not.exist
+                queue.next()
+
+            -> done()
+
+          ]
+
+          daisy queue
+
+
   describe 'migrateOldAppStorageIfExists()', ->

     it 'should return null if storage doesnt exist', (done) ->
@@ -334,17 +389,10 @@

           ->
             # creating an old app storage document
-            bucket =
-              someString  : generateRandomString()
-              someData    :
-                moreData  : { data : {} }
-              anotherData :
-                someArray : [1,2,3]
-
-            options = { account, appId, version, bucket }
+            options = { account, appId, version }
             createOldAppStorageDocument options, (err, data) ->
               expect(err).to.not.exist
-              { storage : oldStorage, relationshipOptions } = data
+              { storage : oldStorage, relationshipOptions, bucket } = data
               expect(oldStorage.bongo_.constructorName).to.be.equal 'JAppStorage'
               queue.next()

@@ -384,7 +432,6 @@
         daisy queue

-
 beforeTests()

 runTests()


Update API to match Hubot docs

Update API to match Hubot docs

--- app/templates/src/template.coffee
+++ app/templates/src/template.coffee
@@ -30,26 +30,26 @@
 module.exports = (robot) ->
   <%= scriptNameCamelized %>Robot = new <%= robotClassName %>Robot robot<% } else { %>module.exports = (robot) -><% } %>

-  robot.respond /hello/, (msg) ->
-    msg.reply "hello!"
+  robot.respond /hello/, (res) ->
+    res.reply "hello!"

-  robot.hear /orly/, (msg) ->
-    if robot.auth.isAdmin msg.envelope.user
+  robot.hear /orly/, (res) ->
+    if robot.auth.isAdmin res.envelope.user
       message = "yarly"
     else
       message = "Sorry, only admins can do that."
-    msg.send message
+    res.send message

-  <% if (needStorage) { %>robot.respond /add (\S*) to the thing/, (msg) ->
-    item = msg.match[1]
+  <% if (needStorage) { %>robot.respond /add (\S*) to the thing/, (res) ->
+    item = res.match[1]
     <%= scriptNameCamelized %>Robot.add(item)
-    msg.send "Alright, I added #{item} to the thing."
+    res.send "Alright, I added #{item} to the thing."

-  robot.respond /remove (\S*) from the thing/, (msg) ->
-    item = msg.match[1]
-    if robot.auth.isAdmin msg.envelope.user
+  robot.respond /remove (\S*) from the thing/, (res) ->
+    item = res.match[1]
+    if robot.auth.isAdmin res.envelope.user
       <%= scriptNameCamelized %>Robot.remove(item)
       message = "Okay, I removed #{item} from the thing."
     else
       message = "Sorry, only admins can remove stuff."
-    msg.send message<% } %>
+    res.send message<% } %>


Added fetchQuery method to ActivityAppController.

--- client/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/app/Applications/Activity.kdapplication/AppController.coffee
@@ -169,6 +169,25 @@
         cache.overview.reverse()  if cache?.overview
         callback null, cache

+  fetchQuery: (options, callback) ->
+    selector    =
+      type      : { $in : [options.type or= 'CNewMemberBucketActivity'] }
+      createdAt :
+        $gt     : options.from
+        $lt     : options.to
+
+    options     =
+      limit     : options.limit
+
+    KD.remote.api.CActivity.some selector, options, (err, activities)=>
+      return if err or activities.length is 0
+
+      activities = clearQuotes activities
+      KD.remote.reviveFromSnapshots activities, (err, teasers)=>
+        return warn err if err
+
+        callback? teasers, activities
+
   continueLoadingTeasers:->

     unless isLoading


default-apps: Code clean-up.

default-apps: Code clean-up.

--- client/app/MainApp/filetree/controllers/findertreecontroller.coffee
+++ client/app/MainApp/filetree/controllers/findertreecontroller.coffee
@@ -494,7 +494,7 @@
   cmUpload:        (nodeView, contextMenuItem)-> @appManager.notify()
   cmDownload:      (nodeView, contextMenuItem)-> @appManager.notify()
   cmGitHubClone:   (nodeView, contextMenuItem)-> @appManager.notify()
-  cmOpenFile:      (nodeView, contextMenuItem)-> @openFile nodeView, contextMenuItem
+  cmOpenFile:      (nodeView, contextMenuItem)-> @openFile nodeView
   cmPreviewFile:   (nodeView, contextMenuItem)-> @previewFile nodeView
   cmCompile:       (nodeView, contextMenuItem)-> @compileApp nodeView
   cmRunApp:        (nodeView, contextMenuItem)-> @runApp nodeView
@@ -834,9 +834,7 @@
       delegate.selectedApp = @

   pistachio: ->
-    data = @getData()
-
-    return """
+    """
       {{> @img}}
-
#{data.name}

+
#{@getData().name}

     """


Double evaluate causes empty list in IE

Double evaluate causes empty list in IE

Signed-off-by: Steven Hoffman <f5bcacb8ce1651b7d1e56f2693431f20e085fdeb@valenciamgmt.com>

--- vendor/assets/javascripts/fustrate/components/autocomplete.coffee
+++ vendor/assets/javascripts/fustrate/components/autocomplete.coffee
@@ -114,7 +114,6 @@
           @items.push @createListItem(datum, source)

         @awesomplete.list = @items
-        @awesomplete.evaluate()

   performSearch: (source) =>
     $.get source.url(search: @value, commit: 1, format: 'json')
@@ -123,7 +122,6 @@
         @items.push @createListItem(datum, source)

       @awesomplete.list = @items
-      @awesomplete.evaluate()

   createListItem: (datum, source) ->
     datum._displayValue = datum[source.displayKey]


teams: creation: navigate to last step / congratulations after success

--- client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamusernametab.coffee
@@ -11,7 +11,8 @@
     { storeNewTeamData, createTeam, joinTeam, getTeamData } = KD.utils
     { join } = getTeamData().signup
     storeNewTeamData 'username', formData
-    if join then joinTeam() else createTeam()
+    if join then joinTeam() else do ->
+      createTeam success : -> KD.singletons.router.handleRoute '/Team/Stacks'

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


JProxyRestriction: Added clear static method.

--- workers/social/lib/social/models/proxy/restriction.coffee
+++ workers/social/lib/social/models/proxy/restriction.coffee
@@ -32,6 +32,8 @@
           (signature Object, Function)
         fetch     :
           (signature Object, Function)
+        clear     :
+          (signature String, Function)
       instance    : {}

   validate = (client, data, callback) ->
@@ -93,3 +95,26 @@
     JProxyRestriction.some query, {}, (err, restrictions) ->
       return callback err, null  if err
       callback null, restrictions
+
+  # When user create a firewall rule in environments, we create a JProxyFilter
+  # document. When user wants to bind that rule to a domain we create a new
+  # JProxyRestriction document. This document is unique for domain.
+  # Inside JProxyRestriction there is a filters array that holds JProxtFilter ids.
+  # This method will remove JProxyFilter id references inside JProxyRestictions.
+  # Also see the comment in EnvrionmentRuleItem::confirmDestroy.
+  @clear: secure (client, filterId, callback) ->
+    filterId    = ObjectId(filterId)
+    selector    =
+      filters   :
+        $in     : [filterId]
+
+    updater     =
+      $pullAll  :
+        filters : [filterId]
+
+    # is there a way to use selector and updater in a single update query
+    # instead of fetching all and looping over them
+    # like db.update selector, updater, callback
+    JProxyRestriction.fetch client, selector, (err, restrictions) ->
+      for restriction in restrictions
+        restriction.update updater, (err) -> callback err


social: add more email domains to blocked list

--- workers/social/lib/social/models/user/emailchecker.coffee
+++ workers/social/lib/social/models/user/emailchecker.coffee
@@ -2,15 +2,19 @@
 KONFIG = require('koding-config-manager').load("main.#{argv.c}")
 redis  = require "redis"
 REDIS_KEY = "social:disposable-email-addresses"
-DOMAINS = [
+DOMAINS   = [
   "0-mail.com",
   "0815.ru",
   "0815.su",
   "0clickemail.com",
   "0wnd.net",
   "0wnd.org",
+  "10minutemail.cf",
   "10minutemail.com",
   "10minutemail.de",
+  "10minutemail.ga",
+  "10minutemail.gq",
+  "10minutemail.ml",
   "123-m.com",
   "126.com",
   "12minutemail.com",
@@ -18,6 +22,7 @@
   "163.com",
   "1ce.us",
   "1chuan.com",
+  "1mail.ml",
   "1pad.de",
   "1zhuan.com",
   "20minutemail.com",
@@ -27,22 +32,35 @@
   "30minutemail.com",
   "33mail.com",
   "3d-painting.com",
+  "3mail.ga",
+  "4mail.cf",
+  "4mail.ga",
   "4warding.com",
   "4warding.net",
   "4warding.org",
+  "5mail.cf",
+  "5mail.ga",
   "60minutemail.com",
-  "60minutemail.com ",
   "675hosting.com",
   "675hosting.net",
   "675hosting.org",
+  "6mail.cf",
+  "6mail.ga",
+  "6mail.ml",
   "6paq.com",
   "6url.com",
   "75hosting.com",
   "75hosting.net",
   "75hosting.org",
   "7days-printing.com",
+  "7mail.ga",
+  "7mail.ml",
   "7tags.com",
+  "8mail.cf",
+  "8mail.ga",
+  "8mail.ml",
   "99experts.com",
+  "9mail.cf",
   "9ox.net",
   "SendSpamHere.com",
   "SpamHereLots.com",
@@ -132,6 +150,10 @@
   "dcemail.com",
   "deadaddress.com",
   "deadchildren.org",
+  "deadfake.cf",
+  "deadfake.ga",
+  "deadfake.ml",
+  "deadfake.tk",
   "deadspam.com",
   "deagot.com",
   "dealja.com",
@@ -142,9 +164,18 @@
   "dharmatel.net",
   "digitalsanctuary.com",
   "dingbone.com",
+  "discard.cf",
   "discard.email",
+  "discard.ga",
+  "discard.gq",
+  "discard.ml",
+  "discard.tk",
   "discardmail.com",
   "discardmail.de",
+  "disposable-email.ml",
+  "disposable.cf",
+  "disposable.ga",
+  "disposable.ml",
   "disposableaddress.com",
   "disposableemailaddresses.com",
   "disposableinbox.com",
@@ -176,7 +207,17 @@
   "easytrashmail.com",
   "einrot.com",
   "einrot.de",
+  "email-fake.cf",
+  "email-fake.ga",
+  "email-fake.gq",
+  "email-fake.ml",
+  "email-fake.tk",
   "email60.com",
+  "emailage.cf",
+  "emailage.ga",
+  "emailage.gq",
+  "emailage.ml",
+  "emailage.tk",
   "emaildienst.de",
   "emailgo.de",
   "emailias.com",
@@ -185,7 +226,13 @@
   "emailisvalid.com",
   "emaillime.com",
   "emailmiser.com",
+  "emails.ga",
   "emailsensei.com",
+  "emailspam.cf",
+  "emailspam.ga",
+  "emailspam.gq",
+  "emailspam.ml",
+  "emailspam.tk",
   "emailtemporar.ro",
   "emailtemporario.com.br",
   "emailthe.net",
@@ -194,8 +241,17 @@
   "emailwarden.com",
   "emailx.at.hm",
   "emailxfer.com",
+  "emailz.cf",
+  "emailz.ga",
+  "emailz.gq",
+  "emailz.ml",
   "emeil.in",
   "emeil.ir",
+  "emkei.cf",
+  "emkei.ga",
+  "emkei.gq",
+  "emkei.ml",
+  "emkei.tk",
   "emz.net",
   "enterto.com",
   "ephemail.net",
@@ -204,10 +260,22 @@
   "etranquil.org",
   "evopo.com",
   "example.com",
+  "explodemail.com ",
   "explodemail.com",
-  "explodemail.com ",
   "eyepaste.com",
+  "facebook-email.cf",
+  "facebook-email.ga",
+  "facebook-email.ml",
+  "facebookmail.gq",
+  "facebookmail.ml",
+  "fake-mail.cf",
+  "fake-mail.ga",
+  "fake-mail.ml",
+  "fakeinbox.cf",
   "fakeinbox.com",
+  "fakeinbox.ga",
+  "fakeinbox.ml",
+  "fakeinbox.tk",
   "fakeinformation.com",
   "fakemail.fr",
   "fakemailgenerator.com",
@@ -239,7 +307,12 @@
   "fornow.eu",
   "fr33mail.info",
   "frapmail.com",
+  "free-email.cf",
+  "free-email.ga",
   "freemail.ms",
+  "freemails.cf",
+  "freemails.ga",
+  "freemails.ml",
   "freundin.ru",
   "friendlymail.co.uk",
   "front14.org",
@@ -249,9 +322,18 @@
   "garliclife.com",
   "gawab.com",
   "gelitik.in",
+  "get-mail.cf",
+  "get-mail.ga",
+  "get-mail.ml"
+  "get-mail.tk",
   "get1mail.com",
   "get2mail.fr",
+  "getairmail.cf",
   "getairmail.com",
+  "getairmail.ga",
+  "getairmail.gq",
+  "getairmail.ml",
+  "getairmail.tk",
   "getmails.eu",
   "getonemail.com",
   "getonemail.net",
@@ -300,6 +382,11 @@
   "hidzz.com",
   "hmamail.com",
   "hochsitze.com",
+  "hot-mail.cf",
+  "hot-mail.ga",
+  "hot-mail.gq",
+  "hot-mail.ml",
+  "hot-mail.tk",
   "hotpop.com",
   "hulapla.de",
   "hushmail.com",
@@ -334,6 +421,8 @@
   "jourrapide.com",
   "jsrsolutions.com",
   "junk1e.com",
+  "junkmail.ga",
+  "junkmail.gq",
   "kasmail.com",
   "kaspop.com",
   "keepmymail.com",
@@ -355,6 +444,10 @@
   "link2mail.net",
   "litedrop.com",
   "loadby.us",
+  "login-email.cf",
+  "login-email.ga",
+  "login-email.ml",
+  "login-email.tk",
   "lol.ovpn.to",
   "lookugly.com",
   "lopl.co.cc",
@@ -379,22 +472,36 @@
   "mailbucket.org",
   "mailcatch.com",
   "maildrop.cc",
+  "maildrop.cf",
+  "maildrop.ga",
+  "maildrop.gq",
+  "maildrop.ml",
   "maileater.com",
   "mailexpire.com",
   "mailfa.tk",
+  "mailfree.ga",
+  "mailfree.gq",
+  "mailfree.ml",
   "mailfreeonline.com",
   "mailguard.me",
   "mailimate.com",
   "mailin8r.com",
   "mailinater.com",
   "mailinator.com",
+  "mailinator.gq",
   "mailinator.net",
   "mailinator.org",
   "mailinator.us",
   "mailinator2.com",
   "mailincubator.com",
   "mailismagic.com",
+  "mailjunk.cf",
+  "mailjunk.ga",
+  "mailjunk.gq",
+  "mailjunk.ml",
+  "mailjunk.tk",
   "mailmate.com",
+  "mailme.gq",
   "mailme.ir",
   "mailme.lv",
   "mailme24.com",
@@ -464,6 +571,7 @@
   "netzidiot.de",
   "neverbox.com",
   "nice-4u.com",
+  "nmail.cf",
   "no-spam.ws",
   "nobulk.com",
   "noclickemail.com",
@@ -531,13 +639,14 @@
   "receiveee.chickenkiller.com",
   "receiveee.com",
   "recode.me",
-  "recursor.net",
   "reconmail.com",
   "recursor.net",
   "recyclemail.dk",
   "regbypass.com",
   "regbypass.comsafe-mail.net",
   "rejectmail.com",
+  "remail.cf",
+  "remail.ga",
   "rhyta.com",
   "rk9.chickenkiller.com",
   "rklips.com",
@@ -611,6 +720,11 @@
   "spamday.com",
   "spamdecoy.net",
   "spamex.com",
+  "spamfighter.cf",
+  "spamfighter.ga",
+  "spamfighter.gq",
+  "spamfighter.ml",
+  "spamfighter.tk",
   "spamfree.eu",
   "spamfree24.com",
   "spamfree24.de",
@@ -622,8 +736,6 @@
   "spamgourmet.com",
   "spamgourmet.net",
   "spamgourmet.org",
-  "SpamHereLots.com",
-  "SpamHerePlease.com",
   "spamhole.com",
   "spamify.com",
   "spaminator.de",
@@ -645,9 +757,13 @@
   "spoofmail.de",
   "squizzy.de",
   "startkeys.com",
+  "stinkefinger.net",
+  "stop-my-spam.cf",
+  "stop-my-spam.com",
+  "stop-my-spam.ga",
+  "stop-my-spam.ml",
+  "stop-my-spam.tk",
   "streetwisemail.com",
-  "stinkefinger.net",
-  "stop-my-spam.com",
   "stuffmail.de",
   "supergreatmail.com",
   "supermailer.jp",
@@ -667,7 +783,6 @@
   "tempemail.biz",
   "tempemail.co.za",
   "tempemail.com",
-  "TempEMail.net",
   "tempinbox.co.uk",
   "tempinbox.com",
   "tempmail.it",
@@ -695,8 +810,13 @@
   "tradermail.info",
   "trash-amil.com",
   "trash-mail.at",
+  "trash-mail.cf",
   "trash-mail.com",
   "trash-mail.de",
+  "trash-mail.ga",
+  "trash-mail.gq",
+  "trash-mail.ml",
+  "trash-mail.tk",
   "trash2009.com",
   "trash2010.com",
   "trash2011.com",
@@ -759,6 +879,7 @@
   "wilemail.com",
   "willselfdestruct.com",
   "winemaven.info",
+  "wmail.cf",
   "wronghead.com",
   "wuzup.net",
   "wuzupmail.net",
@@ -778,7 +899,9 @@
   "yogamaven.com",
   "yopmail.com",
   "yopmail.fr",
+  "yopmail.gq",
   "yopmail.net",
+  "youmail.ga",
   "ypmail.webarnak.fr.eu.org",
   "yuurok.com",
   "za.com",


AccountAccountCredentialListController: Take a parameter and override parent method with it

--- client/account/lib/views/accountcredentiallistcontroller.coffee
+++ client/account/lib/views/accountcredentiallistcontroller.coffee
@@ -179,11 +179,10 @@
       @addListItems credentials

-  loadView: ->
-
-    super
+  loadView: (mainView) ->

     @listView.on 'ShowShareCredentialFormFor', @bound 'showShareCredentialFormFor'
+    super mainView

     { provider, requiredFields, dontShowCredentialMenu } = @getOptions()


kite: this is better, it calls unmount only once

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -43,8 +43,9 @@
     KD.getSingleton("vmController").on "StateChanged", @bound "checkVMState"

     kiteController = KD.getSingleton("kiteController")
-    kiteController.on "KiteConnected", @bound "mountFSKite"
-    kiteController.on "KiteDisconnected", @bound "unmountFSKite"
+    kiteController.on "KiteConnected", (kiteName) =>
+      @mountFSKite kiteName
+      kiteController.once "KiteDisconnected", => @unmountFSKite kiteName

   watchers: {}


[client][app][main][maincontroller] added validation into text box

--- client/landing/app/MainApp/maincontroller/maincontroller.coffee
+++ client/landing/app/MainApp/maincontroller/maincontroller.coffee
@@ -222,9 +222,11 @@
                   rules              :
                     required         : yes
                     minLength        : 2
+                    regExp           : /\d[A-Za-z]+/i
                   messages           :
                     required         : "Please enter a time period"
                     minLength        : "You must enter one pair"
+                    regExp           : "You must enter at least a number and a character e.g : 1y 1h"
                 iconOptions          :
                   tooltip            :
                     placement        : "right"


'Updating for version 0.12.5dev1'

'Updating for version 0.12.5dev1'

--- bokehjs/src/coffee/version.coffee
+++ bokehjs/src/coffee/version.coffee
@@ -1 +1 @@
-export version = '0.12.4'
+export version = '0.12.5dev1'


Minor js changes

Minor js changes

--- app/assets/javascripts/api/comments_utils.js.coffee
+++ app/assets/javascripts/api/comments_utils.js.coffee
@@ -148,12 +148,14 @@

 Juvia.setSubmitting = (container, val) ->
+  $submit_button = container.find(".juvia-submit-button")
+  $submitting_button = container.find(".juvia-submitting-button")
   if val
-    container.find(".juvia-submit-button").hide()
-    container.find(".juvia-submitting-button").show()
-  else
-    container.find(".juvia-submit-button").show()
-    container.find(".juvia-submitting-button").hide()
+    $submit_button.hide()
+    $submitting_button.show()
+  else
+    $submit_button.show()
+    $submitting_button.hide()

 Juvia.virtualAnimate = (options) ->


create focus mask

create focus mask

--- app/assets/javascripts/components/region-focus.cjsx
+++ app/assets/javascripts/components/region-focus.cjsx
@@ -64,22 +64,22 @@
           <rect
             className   = "mark-rectangle"
             x           = 0
-            y           = {-@state.yUpper}
+            y           = { -@state.yUpper }
             viewBox     = {"0 0 @props.imageWidth @props.imageHeight"}
             width       = {( @props.imageWidth - 2*@state.strokeWidth ) }
             height      = {@state.yUpper}
-            fill        = {if @props.selected then "rgba(0,102,255,0.25)" else "rgba(0,0,0,0.5)"}
+            fill        = {if @props.selected then "rgba(0,0,0,0.80)" else "rgba(0,0,0,0)"}
             stroke      = {@state.strokeColor}
             strokeWidth = {@state.strokeWidth}
           />
           <rect
             className   = "mark-rectangle"
             x           = 0
-            y           = 0
+            y           = { @state.markHeight }
             viewBox     = {"0 0 @props.imageWidth @props.imageHeight"}
             width       = {( @props.imageWidth - 2*@state.strokeWidth ) }
-            height      = {@state.markHeight}
-            fill        = {if @props.selected then "rgba(255,102,0,0.25)" else "rgba(0,0,0,0.5)"}
+            height      = { @props.imageHeight - @state.yLower }
+            fill        = {if @props.selected then "rgba(0,0,0,0.90)" else "rgba(0,0,0,0)"}
             stroke      = {@state.strokeColor}
             strokeWidth = {@state.strokeWidth}
           />


Bugfix: icon removed from newly-empty item slots

--- app/assets/javascripts/matches/scoreboard.js.coffee
+++ app/assets/javascripts/matches/scoreboard.js.coffee
@@ -35,22 +35,26 @@
       .data(data[0..4])
     radcells = radrows.selectAll("td")
       .data((d) -> d)
+    radcells.attr("class", "") # clear stale item icons
+
     iconcells = radcells.filter((d) -> typeof d is "string" and (d.indexOf("npc_") is 0 or d.indexOf("item_") is 0))
     iconcells.attr("class", (d) -> "#{d}-icon")
-    othercells = radcells.filter((d) -> typeof d isnt "string" or (d.indexOf("npc_") isnt 0 and d.indexOf("item_") isnt 0))
-    othercells.classed("cell-change", (d) -> this.textContent isnt d and this.textContent isnt "#{d}")
-    othercells.text((d) -> d)
+    textcells = radcells.filter((d) -> typeof d isnt "string" or (d.indexOf("npc_") isnt 0 and d.indexOf("item_") isnt 0))
+    textcells.classed("cell-change", (d) -> this.textContent isnt "#{d}")
+    textcells.text((d) -> d)

     diretable = d3.select("#dire_players")
     direrows = diretable.selectAll("tr")
       .data(data[5..9])
     direcells = direrows.selectAll("td")
       .data((d) -> d)
+    direcells.attr("class", "") # clear stale item icons
+
     iconcells = direcells.filter((d) -> typeof d is "string" and (d.indexOf("npc_") is 0 or d.indexOf("item_") is 0))
     iconcells.attr("class", (d) -> "#{d}-icon")
-    othercells = direcells.filter((d) -> typeof d isnt "string" or (d.indexOf("npc_") isnt 0 and d.indexOf("item_") isnt 0))
-    othercells.classed("cell-change", (d) -> this.textContent isnt d and this.textContent isnt "#{d}")
-    othercells.text((d) -> d)
+    textcells = direcells.filter((d) -> typeof d isnt "string" or (d.indexOf("npc_") isnt 0 and d.indexOf("item_") isnt 0))
+    textcells.classed("cell-change", (d) -> this.textContent isnt "#{d}")
+    textcells.text((d) -> d)
     #direcells.classed("cell-change", (d) -> this.textContent isnt d and this.textContent isnt "#{d}")

   #timer_tick = scoreboards[2]['tick']


TEST Controller scope tests

TEST Controller scope tests

--- test/spec/neck/controller.coffee
+++ test/spec/neck/controller.coffee
@@ -118,4 +118,73 @@

     assert.ok spy.calledTwice
     assert.equal spy.args[0][0].outerHTML, "
This is text 1
"
-    assert.equal spy.args[1][0].outerHTML, "
This is text 2
"+    assert.equal spy.args[1][0].outerHTML, "
This is text 2
"
+
+  # Scope tests
+
+  it "should create independent scope with _context set to self when no parent is set", ->
+    controller = new Neck.Controller()
+    assert.equal controller.scope._context, controller
+
+  it "should set scope properties from prototype", ->
+    class Controller extends Neck.Controller
+      scope:
+        one: 1
+        two: 'Some text'
+
+    # Simple controller
+    controller = new Controller()
+
+    assert.ok controller.scope.hasOwnProperty 'one'
+    assert.equal controller.scope.one, 1
+
+    assert.ok controller.scope.hasOwnProperty 'two'
+    assert.equal controller.scope.two, 'Some text'
+
+  it "should inherit scope properites from 'parent' controller scope", ->
+    class Controller extends Neck.Controller
+      scope:
+        one: 1
+        two: some: 'text'
+
+    controller = new Controller()
+
+    # Child controller with 'parent' setted, will inherit properites
+    childController = new Neck.Controller parent: controller
+    assert.notOk childController.scope.hasOwnProperty 'one'
+    assert.notOk childController.scope.hasOwnProperty 'two'
+    assert.equal childController.scope.one, 1
+    assert.equal childController.scope.two.some, 'text'
+
+    # Changing value of children scope property (not for objects, only String, Number, Boolean)
+    # DOESN't change inherit scope value when they are NOT SETTER/GETTER properites
+    childController.scope.one = true
+    childController.scope.two = newSome: 'text two'
+    assert.equal controller.scope.one, 1
+    assert.equal controller.scope.two.some, 'text'
+
+    # Now tricky part, watching proccess changes properites to SETTER/GETTER
+    # and then inherited properites are changing it's parent
+
+    # Create new instance of Neck.Controller
+    childController = new Neck.Controller parent: controller
+    # Trigger watching proccess
+    controller.watch 'one', ->
+
+    # Now 'one' property of controller is setter/getter
+    # So changind 'one' property of childController will change 'one' property of controller
+    # Hint: setter/getter properties are working as objects
+    childController.scope.one = true
+    assert.equal controller.scope.one, true
+
+  it "should set scope '_resolves' self own property", ->
+    controller = new Neck.Controller()
+    assert.isObject controller.scope._resolves
+
+    # Child controller (with parent) should have '_resolves' own property
+    # It's important because this property can't be inherited from parent
+    # All controller scopes has own '_resolves' property
+    childController = new Neck.Controller parent: controller
+    assert.isObject childController.scope._resolves
+    assert.notEqual controller.scope._resolves, childController.scope._resolves
+


change fetchLineNumber to use the actual document location, controller url does not seem to be updated

--- assets/scripts/lib/travis/line_number_parser.coffee
+++ assets/scripts/lib/travis/line_number_parser.coffee
@@ -1,5 +1,3 @@
 Travis.LineNumberParser = Ember.Mixin.create
   fetchLineNumber: ->
-    url = @container.lookup('router:main').get('url')
-    if match = url.match(/#L(\d+)$/)
-      match[1]
+    match[1] if match = document.location.hash.match(/#L(\d+)$/)


Remove unused test.

Remove unused test.

--- test/unit/controllersSpec.coffee
+++ test/unit/controllersSpec.coffee
@@ -1,13 +0,0 @@
-describe 'The Subject List Controller', () ->
-
-  it 'should do something' () ->
-    true
-
-  # beforeEach () ->
-  #   # Load the controller module.
-  #   module 'qiprofile.controllers'
-  #
-  #   # Initialize the controller and a mock scope.
-  #   inject ($controller, $rootScope) ->
-  #     scope = $rootScope.$new()
-  #     SubjectListCtrl = $controller('SubjectListCtrl', $scope: scope)


Fix root folder not available (#488)

Fix root folder not available (#488)

--- client/app/views/konnector.coffee
+++ client/app/views/konnector.coffee
@@ -250,6 +250,7 @@
                 # Add a widget to select given folder.
                 fieldHtml += """
 <div><select id="#{slug}-#{name}#{index}-input" class="folder"">
+    <option selected value="/">/</option>
 """
                 selectedPath = path: '', id: ''
                 pathName = values[name]
@@ -411,4 +412,3 @@

         @model.set 'importErrorMessage', errorMessage
         @showErrors t errorMessage
-


Added rules

Added rules

--- client/components/Checkins.coffee
+++ client/components/Checkins.coffee
@@ -3,6 +3,7 @@
 import Table from 'react-bootstrap/lib/Table'
 import Button from 'react-bootstrap/lib/Button'
 import Alert from 'react-bootstrap/lib/Alert'
+import Panel from 'react-bootstrap/lib/Panel'

 import UserName from './UserName'

@@ -34,6 +35,36 @@
         wasme = [false, false]

Регистрация на занятие

+

+            Можно записываться на одно любое занятие, занятия ничем не отличаются.
+            Если вы записались, но прийти не сможете — удалите себя из списка.
+

+
+

+            Новичкам (кто не решил в курсе ни одной задачи) записываться не надо,
+            надо просто приходить к 14.00. Или можете начать заниматься сдавать задачи заочно —
+            если хотя бы одну решите, можете записываться и приходить уже не как новички.
+

+
+

+            Можно приходить без записи, но компьютеры будут выдаваться в первую очередь тем,
+            кто записался (даже если они опаздывают).

+
+

+            Записываться сразу на оба занятия нельзя,
+            но если записаться на 14.00, то при наличии свободных мест можно остаться и на второе занятие.
+            Аналогично, можно записаться на 15.30, придти раньше и при наличии мест я вас пущу.
+            Но будьте готовы к тому, что мест может не быть.
+

+
+

+            Тем, кто со своими ноутбуками, записываться не надо, просто приходите
+            и можете оставаться на два занятия.
+

+
+

+            Не забудьте с собой паспорт (если паспорта еще нет, то свидетельство о рождении) -- его могут спросить охранники на входе!
+

             {
             if @state.result?.error
                 <Alert bsStyle="danger">


HBW-72 Change empty element to empty div when there is no user (#158)

--- hbw/app/assets/javascripts/hbw/components/buttons.js.jsx.coffee
+++ hbw/app/assets/javascripts/hbw/components/buttons.js.jsx.coffee
@@ -92,7 +92,7 @@
               {buttons}
             </div>`
         else
-          ``
+          `<div></div>`

       onButtonActivation: (button) ->
         console.log('Clicked button[' + button.title + '], submitting')


Contributed by Lorenzo Milesi <maxxer@ufficyo.com>:

Fixed typo in timezone.coffee

--- app/assets/javascripts/app/controllers/_ui_element/timezone.coffee
+++ app/assets/javascripts/app/controllers/_ui_element/timezone.coffee
@@ -24,5 +24,5 @@
     @selectedOptions(attribute, params)

     attribute.tag =        'searchable_select'
-    attribute.placeholder = App.i18n.translateInline('Enter timzone...')
+    attribute.placeholder = App.i18n.translateInline('Enter timezone...')
     App.UiElement.searchable_select.render(attribute)


Improved shown articles.

Improved shown articles.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -70,17 +70,36 @@
         article = App.TicketArticle.find(article_id)

         # build html body
+        # cleanup body
         article['html'] = article.body.trim()
-        article['html'].replace(/\n\n/m, "\n");
-        article['html'].replace(/\n\r\n\r/m, "\n");
+        article['html'].replace(/\n\r/g, "\n");
+        article['html'].replace(/\n\n/mg, "\n");
+
+        # if body has more then x lines / else search for signature
+        preview       = 15
+        preview_mode  = false
+        article_lines = article['html'].split(/\n/)
+        if article_lines.length > preview
+          preview_mode = true
+          article_lines.splice( preview+1, 1, "----SEEMORE----" )
+          article['html'] = article_lines.join("\n")
         article['html'] = window.linkify( article['html'] )
         notify = "" + T('See more') + ""
-        @article_changed = false
-        article['html'] = article['html'].replace /^(--|__)/m, (match) =>
-          @article_changed = true
-          notify + '
' + match
-        if @article_changed
+
+        # preview mode
+        if preview_mode
+          article['html'] = article['html'].replace /^----SEEMORE----/m, (match) =>
+            notify + '
'
           article['html'] = article['html'] + '
'
+
+        # hide signatures and so on
+        else
+          @article_changed = false
+          article['html'] = article['html'].replace /^(--|__)/m, (match) =>
+            @article_changed = true
+            notify + '
' + match
+          if @article_changed
+            article['html'] = article['html'] + '
'

         @articles.push article


Clean up code

Clean up code

--- app/assets/javascripts/components/todos/components/TodoApp.js.cjsx
+++ app/assets/javascripts/components/todos/components/TodoApp.js.cjsx
@@ -25,12 +25,11 @@

   handleClearCompleted: (e) ->
     e.preventDefault()
-    todos_wrapper = @getStateFromFlux().todos
     if confirm("Clear all the completed items?")
-      for id, todo of todos_wrapper
+      for id, todo of @getStateFromFlux().todos
         @getFlux().actions.deleteTodo(id) if todo.completed

-  handleSelectTodoNav: (selectedKey) ->
+  handleSelectFilter: (selectedKey) ->
     switch selectedKey
       when 1 then @setState(filterMode: 1)
       when 2 then @setState(filterMode: 2)
@@ -69,7 +68,7 @@
     navigation =
       <nav id="todo_navbar" >
         <Button onClick={ @handleClearCompleted } className="pull-right">Clear completed</Button>
-        <Nav bsStyle='pills' activeKey={ @state.filterMode } onSelect={ @handleSelectTodoNav }>
+        <Nav bsStyle='pills' activeKey={ @state.filterMode } onSelect={ @handleSelectFilter }>
           <NavItem eventKey={1}>All</NavItem>
           <NavItem eventKey={2}>Active</NavItem>
           <NavItem eventKey={3}>Completed</NavItem>


properly close video streams on chrome 47

--- app/assets/javascripts/uploadcare/widget/tabs/camera-tab.js.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/camera-tab.js.coffee
@@ -78,6 +78,9 @@
           .removeClass('uploadcare-dialog-camera-ready')
           .addClass('uploadcare-dialog-camera-requested')
       @URL?.revokeObjectURL(@video.prop('src'))
+      if @__stream.getVideoTracks
+        $.each @__stream.getVideoTracks(), ->
+          @stop?()
       @__stream?.stop?()

     __mirror: =>


worker: fix followers_count crashing webserver

--- workers/social/lib/social/models/account.coffee
+++ workers/social/lib/social/models/account.coffee
@@ -719,11 +719,10 @@
     @update ($set: 'counts.lastLoginDate': new Date), ->

     # Twitter follower count
-    JUser.one {username: @profile.nickname}, (err, user)=>
+    JUser.one {username: @profile.nickname}, (err, user) =>
       return if err or not user
-      if user.foreignAuth?.twitter?
-        followerCount = user.foreignAuth.twitter.profile.followers_count
-        @update ($set: 'counts.twitterFollowers': followerCount), ->
+      if followerCount = user.foreignAuth?.twitter?.profile?.followers_count
+        @update $set: 'counts.twitterFollowers': followerCount, ->

   dummyAdmins = [ "sinan", "devrim", "gokmen", "fatihacet", "arslan",


Remove bind_bokeh_events() from RendererView

--- bokehjs/src/coffee/models/renderers/renderer.coffee
+++ bokehjs/src/coffee/models/renderers/renderer.coffee
@@ -189,8 +189,6 @@
       [name, prefix] = spec.split(":")
       prefix ?= ""
       @visuals[prefix+name] = new VISUALS[name]({obj: @model, prefix: prefix})
-
-  bind_bokeh_events: () ->

   request_render: () ->
     @plot_view.request_render()


Handle copying of dev manually

Handle copying of dev manually

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -83,11 +83,6 @@

     'copyComponentAssets'
     'copy:assets'
-    # Copy over the app's params first, then the dev version, which takes
-    # precedence
-    'copy:paramsApp'
-    'copy:paramsDev'
-    'copy:dev'

     'compile:js'
     'compile:stylus'
@@ -100,6 +95,7 @@
     'concat:html'
     'inject:mode'
     'inject:index'
+    'inject:dev'
     'inject:params'
     'concat:params'

@@ -176,13 +172,6 @@
         cwd: 'app/'
         src: '**/*.js'
         dest: 'tmp/'
-
-      paramsApp:
-        src: 'app/params.json'
-        dest: 'tmp/params.json'
-      paramsDev:
-        src: 'dev/params.json'
-        dest: 'tmp/params.json'

     concat:
       js:
@@ -408,8 +397,14 @@
       when 'index'
         grunt.task.run('copy:index')

+      when 'dev'
         if NODE_ENV is 'dev'
           grunt.task.run('copy:dev')
+          whenExists 'dev/params.json', (path) ->
+            grunt.file.copy(path, 'tmp/params.json')
+        else
+          whenExists 'app/params.json', (path) ->
+            grunt.file.copy(path, 'tmp/params.json')

       when 'params'
         whenExists 'tmp/params.json', (path) ->


Clean up basket emptying

Clean up basket emptying

--- app/assets/javascripts/services/basket_service.coffee
+++ app/assets/javascripts/services/basket_service.coffee
@@ -8,21 +8,25 @@
     products = []
     buyer = null

+    INTEGER_REGEXP = /^\d+$/m
+    isProductSearchValue = (value)-> INTEGER_REGEXP.test(value)
+    isNotProductSearchValue = (value)-> !isProductSearchValue(value)
+    isBuyerSearchValue = (value, key)-> key == 'buyer'
+    isNotBuyerSearchValue = (value, key)-> !isBuyerSearchValue
+
     changeAmount = (entry, amount)->
       name = entry.product.name
-      currentAmount = parseInt($location.search()[name])
+      currentAmount = _.parseInt($location.search()[name])
       unless currentAmount + amount < 1
         newAmount = currentAmount + amount
         $location.search(name, newAmount).replace()

     emptyBasketAndRemoveBuyer = (showBasket=true)->
-      searchObj = $location.search()
-      delete searchObj.buyer
-
-      delete searchObj[entry.product.name] for entry in products
-      products.splice(0, products.length)
-
-      searchObj.basket = showBasket
+      searchObj = _.chain($location.search())
+        .pick isNotProductSearchValue
+        .pick isNotBuyerSearchValue
+        .assign basket: (if showBasket then "true" else "false")
+        .value()
       $location.search(searchObj).replace()

     entryAmountReducer = (sum, entry)-> sum+entry.amount
@@ -57,8 +61,6 @@
       $location.search(search)

     ##### Update basket state based on route changes #####
-
-    INTEGER_REGEXP = /^\d+$/m
     resolveProducts = ->
       productMapper = (newProducts, amount, name)->
         amount = _.parseInt(amount)
@@ -72,7 +74,7 @@
         newProducts.push entry

       products = _.chain($location.search())
-        .pick (value)-> INTEGER_REGEXP.test(value)
+        .pick(isProductSearchValue)
         .transform(productMapper, [])
         .value()


french "free crop"

french "free crop"

--- app/assets/javascripts/uploadcare/locale/fr.js.coffee
+++ app/assets/javascripts/uploadcare/locale/fr.js.coffee
@@ -65,6 +65,7 @@
           crop:
             title: 'Recadrer et ajouter cette image'
             done: 'Terminer'
+            free: 'libre'
           error:
             default:
               title: 'Oups!'


Make notifyOnError handler try to parse response as JSON

--- cms/static/coffee/src/main.coffee
+++ cms/static/coffee/src/main.coffee
@@ -19,7 +19,10 @@
     if ajaxSettings.notifyOnError is false
       return
     if jqXHR.responseText
-        message = _.str.truncate(jqXHR.responseText, 300)
+        try
+          message = JSON.parse(jqXHR.responseText).error
+        catch
+          message = _.str.truncate(jqXHR.responseText, 300)
     else
         message = gettext("This may be happening because of an error with our server or your internet connection. Try refreshing the page or making sure you are online.")
     msg = new CMS.Views.Notification.Error(


Make sure we always show selected results on single selects.

--- coffee/lib/abstract-chosen.coffee
+++ coffee/lib/abstract-chosen.coffee
@@ -204,7 +204,7 @@
     return if @options.width? then @options.width else "#{@form_field.offsetWidth}px"

   include_option_in_results: (option) ->
-    return false if not @display_selected_options and option.selected
+    return false if @is_multiple and (not @display_selected_options and option.selected)
     return false if not @display_disabled_options and option.disabled
     return false if option.empty


Wave 12/13 released.

Wave 12/13 released.

--- coffeescripts/cards-common.coffee
+++ coffeescripts/cards-common.coffee
@@ -2,11 +2,6 @@
 exportObj = exports ? this

 exportObj.unreleasedExpansions = [
-    'Alpha-class Star Wing Expansion Pack',
-    'M12-L Kimogila Fighter Expansion Pack',
-    'Phantom II Expansion Pack',
-    'Resistance Bomber Expansion Pack',
-    'TIE Silencer Expansion Pack',
 ]

 exportObj.isReleased = (data) ->


Use sampled tracking in editor recompiles.

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -267,11 +267,9 @@
 			return path

 		$scope.recompile = (options = {}) ->
-			# event_tracking.sendCountly "editor-recompile", options
-
+			return if $scope.pdf.compiling
+
 			event_tracking.sendCountlySampled "editor-recompile", options
-
-			return if $scope.pdf.compiling

 			$scope.pdf.compiling = true


account: profile update queue refactoring

--- client/account/lib/views/accounteditusername.coffee
+++ client/account/lib/views/accounteditusername.coffee
@@ -15,7 +15,7 @@
 Encoder = require 'htmlencode'
 VerifyPINModal = require 'app/commonviews/verifypinmodal'
 VerifyPasswordModal = require 'app/commonviews/verifypasswordmodal'
-sinkrow = require 'sinkrow'
+async = require 'async'
 KodingSwitch = require 'app/commonviews/kodingswitch'

@@ -189,10 +189,9 @@

     {JUser} = remote.api
     {email, password, confirmPassword, firstName, lastName, username, shareLocation} = formData
-    profileUpdated = yes
     skipPasswordConfirmation = no
     queue = [
-      =>
+      (next) ->
         # update firstname and lastname
         me = whoami()

@@ -201,79 +200,67 @@
           "profile.lastName" : lastName
           "shareLocation"    : formData.shareLocation
         }, (err)->
-          return notify err.message  if err
+          return next err.message  if err
           me.shareLocation = formData.shareLocation
-          queue.next()
-      =>
-        return queue.next() if email is @userInfo.email
+          next()
+
+      (next) =>
+        return next() if email is @userInfo.email

         options = {skipPasswordConfirmation, email}
         @confirmCurrentPassword options, (err) =>
-          if err
-            notify err
-            profileUpdated = false
-            return queue.next()
+          return next err  if err

           JUser.changeEmail {email}, (err, result)=>
-            if err
-              @hideSaveButtonLoader()
-              notify err.message
-              return queue.next()
+            return next err.message  if err

             skipPasswordConfirmation = true
             modal = new VerifyPINModal 'Update E-Mail', (pin)=>
               remote.api.JUser.changeEmail {email, pin}, (err)=>
-                if err
-                  notify err.message
-                  profileUpdated = false
-                else
-                  @userInfo.email = email
-                queue.next()
-            modal.once 'ModalCancelled', @bound 'hideSaveButtonLoader'
-      =>
+                return next err.message  if err
+                @userInfo.email = email
+                next()
+            modal.once 'ModalCancelled', -> next 'cancelled'
+
+      (next) =>
         # on third turn update password
         # check for password confirmation
         if password isnt confirmPassword
-          notify "Passwords did not match"
-          @hideSaveButtonLoader()
-          return
+          return next "Passwords did not match"
+
         #check passworg lenght
         if password isnt "" and password.length < 8
-          notify "Passwords should be at least 8 characters"
-          @hideSaveButtonLoader()
-          return
+          return next "Passwords should be at least 8 characters"
+
         # if password is empty than discard operation
         if password is ""
           {token} = kd.utils.parseQuery()
-          if token
-            profileUpdated = no
-            notify "You should set your password"
-          return queue.next()
+          return next "You should set your password"  if token
+          return next()

         JUser.fetchUser (err, user)=>
-          if err
-            notify "An error occurred"
-            return queue.next()
+          return next "An error occurred"  if err

           skipPasswordConfirmation = true  if user.passwordStatus isnt "valid"
           @confirmCurrentPassword {skipPasswordConfirmation}, (err) =>
-            if err
-              notify err
-              profileUpdated = false
-              return queue.next()
+            return next err  if err
             JUser.changePassword password, (err,docs)=>
               @userProfileForm.inputs.password.setValue ""
               @userProfileForm.inputs.confirm.setValue ""
               if err
-                return queue.next()  if err.message is "PasswordIsSame"
-                return notify err.message
-              return queue.next()
-      =>
-        # if everything is OK or didnt change, show profile updated modal
-        notify "Your account information is updated." if profileUpdated
-        @hideSaveButtonLoader()
+                return next()  if err.message is "PasswordIsSame"
+                return next err.message
+              return next()
+
+      (next) ->
+        notify "Your account information is updated."
+        next()
     ]
-    sinkrow.daisy queue
+
+    async.series queue, (err) =>
+      notify err  if err and err isnt 'cancelled'
+      @hideSaveButtonLoader()
+

   verifyUserEmail: ->


AddManagedVMModal: remove nickname from install script line

--- client/app/lib/providers/managed/viewhelpers.coffee
+++ client/app/lib/providers/managed/viewhelpers.coffee
@@ -1,6 +1,5 @@

 kd    = require 'kd'
-nick  = require 'app/util/nick'

 addTo = (parent, views)->
   map = {}
@@ -20,7 +19,7 @@

 contents  =
   install : """#{kontrolUrl}
-    $ curl -sSL s3.amazonaws.com/koding-klient/install.sh | bash -s %%TOKEN%% %%USERNAME%%
+    $ curl -sSL s3.amazonaws.com/koding-klient/install.sh | bash -s %%TOKEN%%
   """

 module.exports   = view =
@@ -48,7 +47,6 @@
     content      = contents[content] ? content
     if view._otatoken
       content    = content.replace '%%TOKEN%%',    view._otatoken
-      content    = content.replace '%%USERNAME%%', nick()
     container    = new kd.View
     addTo container,
       header     : 'Instructions'


deprecated generic AppController

deprecated generic AppController

--- client/landing/app/coffee/core/AppController.coffee
+++ client/landing/app/coffee/core/AppController.coffee
@@ -1,63 +0,0 @@
-class AppController extends KDViewController
-
-  constructor:->
-
-    super
-
-    { name, version } = @getOptions()
-    { mainController } = KD.singletons
-
-    mainController.ready =>
-      # defer should be removed
-      # this should be listening to a different event - SY
-      KD.utils.defer  =>
-        { appStorageController } = KD.singletons
-        @appStorage = appStorageController.storage name, version or "1.0.1"
-
-    @bindKeyCombos()
-
-  bindKeyCombos: ->
-    { globalKeyCombos } = KD.singletons
-
-    @appKeyListener = new KDKeyboardListener
-    @appKeyMap      = new KDKeyboardMap { priority: 10 }
-
-    @appKeyListener
-      .addComboMap globalKeyCombos
-      .addComboMap @appKeyMap
-
-    @registerDeclaredBindings()
-
-    KD.singletons.appManager.on 'AppIsBeingShown', (app) =>
-      @appKeyListener?.listen()  if app is this
-
-  registerAppKeys: (bindings = {}) ->
-    @appKeyMap.addCombo binding, fn  for own binding, fn of bindings
-    @appKeyListener.addComboMap @appKeyMap
-    @appKeyMap
-
-  createContentDisplay:(models, callback)->
-    warn "You need to override #createContentDisplay - #{@constructor.name}"
-
-  handleQuery:(query)->
-    @ready => @feedController?.handleQuery? query
-
-  handleCommand: (command, appName, event) ->
-    { commands } = KD.getAppOptions @getOptions().name
-
-    cmd = commands[command]
-
-    if 'function' is typeof cmd
-      cmd.call this, event
-    else if 'string' is typeof cmd
-      @[cmd]?.call this, event
-    else
-      throw new Error "Unknown command: #{command}"
-
-  registerDeclaredBindings: ->
-    appName = @getOptions().name
-    { keyBindings } = KD.getAppOptions appName
-
-    keyBindings?.forEach (b) =>
-      @appKeyMap.addCombo b.binding, { global: b.global }, (ev) =>
-        @handleCommand b.command, appName, ev


VariablesView: show access denied warning for non-admins

--- client/stacks/lib/views/stacks/variablesview.coffee
+++ client/stacks/lib/views/stacks/variablesview.coffee
@@ -109,6 +109,8 @@

   checkVariableChanges: ->

+    return  if @_pinnedWarning
+
     content   = @getAce().getContents()
     converted = yamlToJson content, silent = yes

@@ -147,7 +149,10 @@
       return  unless credential

       credential.fetchData (err, data) =>
-        return kd.warn err  if err
+
+        if err
+          @pinWarning "You don't have access for custom variables"
+          return kd.warn err

         @_activeCredential = credential

@@ -158,3 +163,13 @@
           else (jsonToYaml meta).content

           @getAce().setContent _.unescape content
+
+
+  pinWarning: (warning) ->
+
+    @_pinnedWarning = yes
+    @indicator.setClass 'red'
+    @indicator.updatePartial '!'
+    @indicator.setTooltip title: warning
+    @indicator.setClass 'in'
+    @parent.tabHandle.setClass 'notification'


Fix js that prevented single step OE problems (ie only self assessment) from working

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -289,6 +289,9 @@
       if @child_type == "openended"
         @submit_button.hide()
         @queueing()
+        if @task_number==1 and @task_count==1
+          @grader_status = $('.grader-status')
+          @grader_status.html("
Response submitted for scoring.
")
     else if @child_state == 'post_assessment'
       if @child_type=="openended"
         @skip_button.show()
@@ -311,6 +314,8 @@
       if @task_number<@task_count
         @next_problem()
       else
+        if @task_number==1 and @task_count==1
+          @show_combined_rubric_current()
         @show_results_current()
         @reset_button.show()


compare8

--- common/lib/xmodule/xmodule/js/src/capa/display.coffee
+++ common/lib/xmodule/xmodule/js/src/capa/display.coffee
@@ -24,7 +24,9 @@
     @$('section.action input.reset').click @reset
     @$('section.action button.show').click @show
     @$('section.action input.save').click @save
-    @$('section.action input.save').click @show
+    @$("section.action input[name='Submit and Compare check']").click @compare
+    if @$("section.action input[name='problem_id']").attr("data") == "Submit and Compare"
+        @inputs.next('.status').hide();

     @bindResetCorrectness()

@@ -36,6 +38,29 @@
     if MathJax?
       @$('input.math').each (index, element) =>
         MathJax.Hub.Queue [@refreshMath, null, element]
+
+  compare: =>
+    console.log(111)
+    $.ajaxSettings.async = false
+
+    @check()
+    if !@el.hasClass 'showed'
+      Logger.log 'problem_show', problem: @id
+      $.postWithPrefix "#{@url}/problem_show", (response) =>
+        answers = response.answers
+        $.each answers, (key, value) =>
+          if $.isArray(value)
+            for choice in value
+              @$("label[for='input_#{key}_#{choice}']").attr correct_answer: 'true'
+          else
+            answer = @$("#answer_#{key}, #solution_#{key}")
+            answer.html(value)
+            @$("#answer_#{key}").hide()
+            @$("input[name='Submit and Compare check']").val("Resubmit")
+            Collapsible.setCollapsibles(answer)
+
+    @inputs.before("
Your Answer:
")
+    $.ajaxSettings.async = true

   renderProgressState: =>
     detail = @el.data('progress_detail')


tweakin'

tweakin'

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -13,7 +13,8 @@
   parser:       require('./parser').parser
   helpers:      require('./helpers').helpers
   helpers.extend global, require './nodes'
-  require.registerExtension '.coffee', (content) -> compile content if require.registerExtension
+  if require.registerExtension
+    require.registerExtension '.coffee', (content) -> compile content
 else
   this.exports: this.CoffeeScript: {}
   Lexer:        this.Lexer


Expanding __filename and __dirname when running with the coffee command -- Issue #771

--- coffee-script.coffee
+++ coffee-script.coffee
@@ -6,13 +6,13 @@
 # If included on a webpage, it will automatically sniff out, compile, and
 # execute all scripts present in `text/coffeescript` tags.

+fs        = require 'fs'
 path      = require 'path'
 {Lexer}   = require './lexer'
 {parser}  = require './parser'

 # TODO: Remove registerExtension when fully deprecated
 if require.extensions
-  fs = require 'fs'
   require.extensions['.coffee'] = (module, filename) ->
     content = compile fs.readFileSync filename, 'utf8'
     module._compile content, filename
@@ -53,7 +53,7 @@
   while root.parent
     root = root.parent
   # Set the filename
-  root.filename = options.fileName
+  root.filename = fs.realpathSync options.fileName
   # Clear the module cache
   root.moduleCache = {} if root.moduleCache
   # Compile
@@ -65,7 +65,7 @@
 # Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
 # The CoffeeScript REPL uses this to run the input.
 exports.eval = (code, options) ->
-  __filename = options.fileName
+  __filename = fs.realpathSync options.fileName
   __dirname  = path.dirname __filename
   eval exports.compile(code, options)


Cancel actions when delayed

Cancel actions when delayed

--- common/action.coffee
+++ common/action.coffee
@@ -8,7 +8,7 @@

   callLater: (delay, args...) ->
     @cancel()
-    @timeoutId = setTimeout (=> @fn args...), delay
+    @timeoutId = setTimeout (=> @callNow args...), delay

   cancel: ->
     clearTimeout @timeoutId


Found and documented the point at which TextQuoteSelector generation is failing

--- annotran/static/scripts/annotator/plugin/sentenceselection.coffee
+++ annotran/static/scripts/annotator/plugin/sentenceselection.coffee
@@ -141,8 +141,12 @@
       event.ranges = ranges
       @annotator.onSuccessfulSelection event

-      # in H's guest.coffee, createAnnotation -> getSelectors is NOT correctly describing the selection made here
+      #TODO:  in H's guest.coffee, createAnnotation -> getSelectors is NOT correctly describing the selection made here
       # it's missing a TextQuoteSelector -- need to work out how to create this and inject it.
+
+      # Bug lies in HTML.coffee and the way that ranges are described.
+      # Need to set the startContainer and endContainer to text objects, or to re-write the anchoring description
+      # function
       @annotator.createAnnotation()

     return null


Better cancel

Better cancel

--- app/assets/javascripts/effective_datatables/inline_crud.js.coffee
+++ app/assets/javascripts/effective_datatables/inline_crud.js.coffee
@@ -117,6 +117,10 @@
   $wrapper = $table.closest('.dataTables_wrapper').addClass('effective-datatables-inline-expanded')
   $table.one 'draw.dt', (event) -> $wrapper.removeClass('effective-datatables-inline-expanded')

+cancel = ($table) ->
+  $wrapper = $table.closest('.dataTables_wrapper')
+  $wrapper.removeClass('effective-datatables-inline-expanded') if $wrapper.find('.effective-datatables-inline-row').length == 0
+
 # Cancel button clicked. Blow away new tr, or restore edit tr
 # No data will have changed at this point
 $(document).on 'click', ".dataTables_wrapper a[data-role='inline-form-cancel']", (event) ->
@@ -130,11 +134,12 @@
       $actions.children('svg').remove()
       $actions.children('a').fadeIn()

-      $table.closest('.dataTables_wrapper').removeClass('effective-datatables-inline-expanded')
       $(this).remove()
+      cancel($table)
     )
   else
     $tr.fadeOut('slow', ->
+      $table = $(this).closest('table')
       $tr.html($tr.data('inline-form-original-html'))

       $td = $tr.children('.col-actions').first()
@@ -144,9 +149,8 @@
       $td.children('.btn-group').show()
       $td.children('a').show()

-      $tr.closest('.dataTables_wrapper').removeClass('effective-datatables-inline-expanded')
-      $tr.removeClass('effective-datatables-inline-row')
-      $tr.fadeIn()
+      $tr.removeClass('effective-datatables-inline-row').fadeIn()
+      cancel($table)
     )

   false


Just crash when fading in an empty view, that makes more sense..

--- core/app/backbone/factlink/cross_fade_region.coffee
+++ core/app/backbone/factlink/cross_fade_region.coffee
@@ -3,7 +3,6 @@
 class Backbone.Factlink.CrossFadeRegion extends Backbone.Marionette.Region

   fadeIn: ->
-    @ensureEl()
     @$el.stop().hide().fadeIn(@_fadeTime())

   fadeOut: (callback=->) ->


Fix $.fn.closestScrollable

Fix $.fn.closestScrollable

--- app/assets/javascripts/evrobone/jquery-additions.js.coffee
+++ app/assets/javascripts/evrobone/jquery-additions.js.coffee
@@ -71,7 +71,7 @@
       callback $(element)

   $.fn.closestScrollable = ->
-    $(_.find(@parents().get(), (p) -> $(p).css('overflowY') is 'auto') or window)
+    $(_.find(@.get().concat(@parents().get()), (p) -> $(p).css('overflowY') is 'auto') or window)

 # RESEARCH
 #  $.fn.blockHide = ->


Set Order.loading to true when updating order

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -114,6 +114,7 @@
         'X-Spree-Order-Token': Cart.current.token

       Cart.current.errors = null
+      Cart.current.loading = true

       deferred = $q.defer()


Removing some console logs

Removing some console logs

--- app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/checkout_controller.js.coffee
@@ -15,13 +15,6 @@
     $scope.accordion[name] = true

   storage.bind $scope, "accordion", {storeName: "accordion_#{$scope.order.id}"}
-  console.log "==============================="
-  console.log "Order.order.id"
-
-  #storage.bind $scope, "accordion.billing", {storeName: "billing_#{Order.order.id}"}
-  #storage.bind $scope, "accordion.shipping", {storeName: "shipping_#{Order.order.id}"}
-  #storage.bind $scope, "accordion.payment", {storeName: "payment_#{Order.order.id}"}
-
   storage.bind $scope, "CheckoutFormState.ship_address_same_as_billing", { defaultValue: true}

   $scope.purchase = (event)->


Don't get user id from possibly deleted session

--- services/web/app/coffee/Features/User/UserInfoController.coffee
+++ services/web/app/coffee/Features/User/UserInfoController.coffee
@@ -12,7 +12,12 @@
 			req.session.destroy()
 		logger.log user: req.user, "reciving request for getting logged in users personal info"
 		return next(new Error("User is not logged in")) if !req.user?
-		UserGetter.getUser req.session.user._id, { first_name: true, last_name: true, role:true, institution:true }, (error, user) ->
+		UserGetter.getUser req.user._id, {
+			first_name: true, last_name: true,
+			role:true, institution:true,
+			email: true, signUpDate: true
+		}, (error, user) ->
+			return next(error) if error?
 			UserController.sendFormattedPersonalInfo(user, res, next)

 	getPersonalInfo: (req, res, next = (error) ->) ->


cleanup

cleanup

--- app/assets/javascripts/monitoring/views/add_on_call.js.coffee
+++ app/assets/javascripts/monitoring/views/add_on_call.js.coffee
@@ -22,5 +22,4 @@
     }

     after_render: ->
-      users = @$("select#users")
-      users.select2()
+      @$("select#users").select2()


Bug fix for page refresh.

Bug fix for page refresh.

--- core/app/backbone/views/channels/add_channel_to_channels_modal_view.coffee
+++ core/app/backbone/views/channels/add_channel_to_channels_modal_view.coffee
@@ -30,9 +30,9 @@
           @collection.add channel, silent: true
       @updateButton()

+  onRender: ->
     @updateButton()

-  onRender: ->
     unless @addToChannelView?
       @addToChannelView = new AutoCompleteChannelsView collection: @collection
       @addToChannelRegion.show @addToChannelView


fixed trailing colon in group url

fixed trailing colon in group url

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -925,7 +925,7 @@
     group.fetchMembershipPolicy (err, policy)=>
       return new KDNotificationView title: 'An error occured, however your group has been created!' if err

-      port = if location.port isnt 80 then ":#{location.port}" else ''
+      port = if location.port then ":#{location.port}" else ''
       groupUrl = "#{location.protocol}//#{location.hostname}#{port}/#{group.slug}"

       if group.privacy is 'public'


Request to join to a private group part implemented for loggedIn users.

--- client/landing/app/MainApp/maincontroller/landingpageviews.coffee
+++ client/landing/app/MainApp/maincontroller/landingpageviews.coffee
@@ -27,7 +27,6 @@
       $("#profile-static-nav").remove()

     @addSubView @nav = @navController.getView()
-

 class LandingPageNavigationController extends NavigationController

@@ -91,6 +90,15 @@
         continue if itemData.loggedIn
       @getListView().addItem itemData

+fetchGroupFirst = (callback)->
+  {groupEntryPoint} = KD.config
+  KD.remote.api.JGroup.one slug: groupEntryPoint, (err, group)=>
+    error err if err
+    if err then new KDNotificationView
+      title : "An error occured, please try again"
+    else unless group?
+      new KDNotificationView title : "No such group!"
+    else callback group

 class LandingPageNavigationLink extends NavigationLink

@@ -122,20 +130,17 @@
       when 'register'
         loginScreen.animateToForm 'register'
       when 'request'
-        loginScreen.animateToForm 'lr'
+        if KD.isLoggedIn()
+          fetchGroupFirst (group)=>
+            @getSingleton('groupsController').openPrivateGroup group
+        else
+          loginScreen.animateToForm 'lr'
       when 'join-group'
-        {groupEntryPoint} = KD.config
-        KD.remote.api.JGroup.one slug: groupEntryPoint, (err, group)=>
-          error err if err
-          if err then new KDNotificationView
-            title : "An error occured, please try again"
-          else unless group?
-            new KDNotificationView title : "No such group!"
-          else group.join (err, response)=>
+        fetchGroupFirst (group)=>
+          group.join (err, response)=>
             error err if err
-            if err
-              new KDNotificationView
-                title : "An error occured, please try again"
+            if err then new KDNotificationView
+              title : "An error occured, please try again"
             else
               new KDNotificationView
                 title : "You successfully joined to group!"


feat(hash): adds the each_key and each_pair method on Hash

--- app/assets/javascripts/awesome_ui/hash.js.coffee
+++ app/assets/javascripts/awesome_ui/hash.js.coffee
@@ -23,3 +23,7 @@
     @values.splice(index, 1)

   each: (block) -> @values.forEach block
+
+  each_key: (block) -> @keys.forEach block
+
+  each_pair: (block) -> @keys.forEach (key) => block? key, @get(key)


add key to dynamically generated navbar links

--- app/assets/javascripts/partials/main-header.cjsx
+++ app/assets/javascripts/partials/main-header.cjsx
@@ -32,7 +32,7 @@
         Transcribe

         { @props.pages.map (page, key) =>
-            {page.name}
+            {page.name}
         }


clean up thanks to coffeescript super

clean up thanks to coffeescript super

--- app/assets/javascripts/views/user_view.js.coffee
+++ app/assets/javascripts/views/user_view.js.coffee
@@ -1,5 +1,9 @@
 (($) ->
   class window.CommonView
+    constructor: ->
+      do @yield
+      do @events
+      do @hide_loading_notification

     update_title: (title) ->
       $("#page-title").html title
@@ -15,9 +19,6 @@
       selector.html('')
       selector.append "
#{key} :

#{error[0]}
" for key, error of errors

-    clear_selector: ->
-      @selector.html('')
-
     hide_loading_notification: ->
       $('.loading').hide()

@@ -29,11 +30,13 @@
       member[0].hash = 'new_post' if member[0].unread == 0
       member[0]

+    events: ->
+      # do nothing, placeholder method
+
   class window.SmileyView extends CommonView
     constructor: ->
       @selector = $('.aside aside')
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       ich.smiley_new {token: @get_token()}
@@ -57,8 +60,7 @@
   class window.UserView extends CommonView
     constructor: (@user) ->
       @selector = $('.aside aside')
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       ich.user @user
@@ -69,8 +71,7 @@
   class window.TopicInfoView extends CommonView
     constructor: (@topic)->
       @selector = $('.topic-info')
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       @topic.member_posts_count = @member(@topic.members).posts_count
@@ -84,9 +85,7 @@
   class window.TopicView extends CommonView
     constructor: (@topic)->
       @selector = $('#contents')
-      do @yield
-      do @events
-      do @hide_loading_notification
+      super

     template: (topic)->
       ich.topic topic
@@ -121,8 +120,7 @@
   class window.PostNewView extends CommonView
     constructor: (@topic)->
       @selector = $('#new_post')
-      do @yield
-      do @hide_loading_notification
+      super

     template: (topic)->
       ich.post_new topic
@@ -134,8 +132,7 @@
     constructor: (@post, @topic) ->
       @selector = $('#posts')
       @post.current = @post.creator_n == Blabbr.current_user.nickname
-      do @yield
-      do @hide_loading_notification
+      super

     template: (post)->
       ich.post post
@@ -146,8 +143,7 @@
   class window.PostEditedView extends CommonView
     constructor: (@post)->
       @selector = $("#p#{@post.pid} .bubble")
-      do @yield
-      do @hide_loading_notification
+      super

     yield: ->
       @selector.html @post.content
@@ -155,8 +151,7 @@
   class window.PostEditView extends CommonView
     constructor: (@post) ->
       @selector = $("#p#{@post.pid}")
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       ich.post_edit @post
@@ -167,29 +162,25 @@
   class window.TopicNewView extends CommonView
     constructor: ->
       @selector = $('.aside aside')
-      do @clear_selector
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       ich.topic_new

     yield: ->
-      @selector.append @template
+      @selector.html @template

   class window.TopicEditView extends CommonView
     constructor: (@topic)->
       @selector = $('.aside aside')
-      do @clear_selector
-      do @yield
-      do @hide_loading_notification
+      super

     template: ->
       @topic.members_list = ("#{member.nickname}," for member in @topic.members)
       ich.topic_edit @topic

     yield: ->
-      @selector.append @template()
+      @selector.html @template()
       @selector.find('#members_list').tagsInput {
           defaultText: 'add a member'
           autocomplete_url:'/users/autocomplete.json',
@@ -198,10 +189,7 @@

   class window.TopicsView extends CommonView
     constructor: (@topics, @selector = $('#contents')) ->
-      do @clear_selector
-      do @yield
-      do @hide_loading_notification
-
+      super

     template: ->
       ich.topics
@@ -220,23 +208,23 @@
         selector.find('.pagination').append pagination

     yield: ->
-      @selector.append @template
+      @selector.html @template
       @selector.find('.topics').append @template_item(topic) for topic in @topics.topics
       @paginate @selector
       @update_title 'Topics'

   class window.TopicsSideView extends TopicsView
+    constructor: ->
+      super
+
     yield: ->
       @selector.append '

'
       @selector.find('section').append @template_item(topic) for topic in @topics.topics
-      do @hide_loading_notification

   class window.SmiliesView extends CommonView
     constructor: (@smilies) ->
       @selector = $('.aside aside')
-      do @yield
-      do @hide_loading_notification
-      do @events
+      super

     template_item: (smiley)->
       ich.smiley_item smiley


JCredential: shadowed helper, don't try to shadow null value

--- workers/social/lib/social/models/computeproviders/credential.coffee
+++ workers/social/lib/social/models/computeproviders/credential.coffee
@@ -332,6 +332,7 @@

   # Poor man's shadow function ~ GG
   shadowed = (c) ->
+    return  unless c
     r = (c) -> Math.ceil c.length / 1.5
     return "*#{Array(r c).join '*'}#{c[(r c)..]}"


travis globs weird

travis globs weird

--- examples/some_rails_app/app/assets/javascripts/application.coffee
+++ examples/some_rails_app/app/assets/javascripts/application.coffee
@@ -1,2 +1,3 @@
 #= require vendor
+#= require init
 #= require_tree .


fix(quoted-text): Better handling for "Wildcard filters" email

--- internal_packages/message-list/lib/email-frame.cjsx
+++ internal_packages/message-list/lib/email-frame.cjsx
@@ -169,7 +169,7 @@
     return email if @props.showQuotedText

     # Split the email into lines and remove lines that begin with > or >
-    lines = email.split(/(\n|
)/)
+    lines = email.split(/(\n|
]*>)/)

     # Remove lines that are newlines - we'll add them back in when we join.
     # We had to break them out because we want to preserve
 elements.
@@ -182,10 +182,14 @@
     ]
     for ii in [lines.length-1..0] by -1
       for regex in regexs
+        # Never remove a line with a blockquote start tag, because it
+        # quotes multiple lines, not just the current line!
+        if lines[ii].match("')?[0] is lines[ii]
           break

     # Return remaining compacted email body


Fix typo

Fix typo

--- lib/assets/javascripts/models/post/status.js.coffee
+++ lib/assets/javascripts/models/post/status.js.coffee
@@ -60,7 +60,7 @@
           fetchPostComplete(mention, res, xhr)
       )

-    mentionsMompleteFn = (res, xhr) =>
+    mentionsCompleteFn = (res, xhr) =>
       if xhr.status == 200
         num_pending_posts = res.mentions.length
         for mention in res.mentions
@@ -81,7 +81,7 @@
         'Cache-Control': 'proxy'
       }

-      callback: mentionsMompleteFn
+      callback: mentionsCompleteFn
     )

     null


Fix timeline pagination

Fix timeline pagination

--- lib/assets/javascripts/unified_collection.js.coffee
+++ lib/assets/javascripts/unified_collection.js.coffee
@@ -41,10 +41,16 @@
           _completeFn?.apply?(null, arguments)

           return unless xhr.status == 200
-          @pagination[cid] = _.extend({
+
+          _pagination = _.extend({
             first: @pagination[cid]?.first
             last: @pagination[cid]?.last
           }, _.clone(res.pages))
+
+          if options.prepend # fetchPrev
+            _pagination.next = @pagination[cid]?.next
+
+          @pagination[cid] = _pagination

           unless @pagination[cid].prev
             model = @constructor.collection.find(cid: cid)?.first()


Environment: MachineItem: show publicUrl only when machine state is running

--- client/Environments/views/scene/environmentmachineitem.coffee
+++ client/Environments/views/scene/environmentmachineitem.coffee
@@ -92,17 +92,21 @@

   pistachio:->

-    {label, provider, ipAddress} = @getData()
+    {label, provider, ipAddress, status:{state} } = @getData()
     title = label or provider
+
+    publicUrl = if ipAddress? then """
+
+        #{ipAddress}
+
+    """ else ""

     """

#{title}

-
-
-
-
+        #{publicUrl}
+        #{state}
         {{> @terminalIcon}}
         {{> @chevron}}


Activity:  PublicFeedComponent: #changelog

--- client/activity/lib/components/publicfeedpane/feedpane.coffee
+++ client/activity/lib/components/publicfeedpane/feedpane.coffee
@@ -93,13 +93,12 @@

   renderFeedInputWidgetAndTabContainer: (channelId, thread, isAnnouncementChannel) ->

-    if not checkFlag('super-admin') and  isAnnouncementChannel
-      return null
-    else
-

-        <FeedInputWidget channelId={ channelId } />
-        <FeedPaneTabContainer thread={ thread } />
-

+    return null  if not checkFlag('super-admin') and isAnnouncementChannel
+
+

+      <FeedInputWidget channelId={ channelId } />
+      <FeedPaneTabContainer thread={ thread } />
+

   renderBody: ->


EnvironmentMachineStateModal: fix no machine modal issue [completes #96316570]

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -471,7 +471,7 @@
         </span>
       "

-    if customState is 'NotFound'
+    if customState is 'NotFound' and not isKoding()
       {groupsController} = kd.singletons
       customState = 'NoTemplate'  unless groupsController.currentGroupHasStack()


Dock: added new app link to navigation

Dock: added new app link to navigation

--- client/Main/maincontroller/dockcontroller.coffee
+++ client/Main/maincontroller/dockcontroller.coffee
@@ -204,6 +204,11 @@
       partial  : 'MY APPS'

     dock.addSubView @navController.getView()
+    dock.addSubView new CustomLinkView
+      icon     : no
+      cssClass : 'add-vm'
+      title    : '+ Add more apps'
+      href     : '/Apps'

     dock.addSubView new KDCustomHTMLView
       tagName  : 'h3'


Update top domain tooltip

Update top domain tooltip

--- client/Main/providers/machinesettingsview.coffee
+++ client/Main/providers/machinesettingsview.coffee
@@ -172,7 +172,7 @@
           defaultValue : topDomain in @machine.aliases
           cssClass     : 'tiny'
           tooltip      :
-            title      : "Use #{topDomain}"
+            title      : "Route #{topDomain} to this vm"
             placement  : 'right'
           callback     : (state) => @emit 'TopDomainStateChange', state
         domains        :


activity: fix indentation

activity: fix indentation

--- client/Social/Activity/views/settingsview.coffee
+++ client/Social/Activity/views/settingsview.coffee
@@ -36,9 +36,9 @@
       fn    = unpin

     @addMenuItem title, ->
-        messageId = post.getId()
-        fn {messageId}, (err)->
-          return KD.showError err  if err
+      messageId = post.getId()
+      fn {messageId}, (err)->
+        return KD.showError err  if err

     return @menu


mixpanel: fix broker teamworkexportmodal

--- client/Teamwork/Views/teamworkexportmodal.coffee
+++ client/Teamwork/Views/teamworkexportmodal.coffee
@@ -87,56 +87,15 @@
       """

   handleExportDone: (shortenUrl) ->
-    KD.mixpanel "Teamwork export done, click"
-
     @destroy()
     fullUrl      = "#{window.location.origin}/Teamwork?importUrl=#{shortenUrl}"
     inputContent = """

I exported my files here, click this link to see them.

#{fullUrl}

     """
-    modal     = new TeamworkShareModal { inputContent, addShareWarning: no }
-    container = new KDCustomHTMLView
-      cssClass: "tw-export-settings"
+    shareWarning = """
+
+
By clicking share this link will be posted publicly on the activity feed. If you just want to send the link privately you can copy the above link.

+    """

-    container.addSubView new KodingSwitch
-      cssClass      : "dark tw-export-switch"
-      defaultValue  : "off"
-      callback      : (state) ->
-        if state
-          modal.destroy()
-          KD.mixpanel "Teamwork export don't share, click"
-          new TeamworkExportedUrlModal {}, fullUrl
-
-    container.addSubView new KDCustomHTMLView
-      tagName       : "span"
-      partial       : "Don't share on Koding Activity, just give me a link"
-      cssClass      : "tw-export-text"
-
-    modal.addSubView container
-
-
-class TeamworkExportedUrlModal extends KDModalView
-
-  constructor: (options = {}, data) ->
-
-    options.content  = "
Here is the link that you can share with others
"
-    options.cssClass = "tw-url-modal tw-modal"
-    options.overlay  = yes
-    options.width    = 600
-    options.buttons  =
-      Done           :
-        cssClass     : "modal-clean-green"
-        title        : "Done"
-        callback     : =>
-          KD.mixpanel "Teamwork export don't share done, click"
-          @destroy()
-
-    super options, data
-
-    @addSubView new KDInputView
-      defaultValue : data
-      cssClass     : "url-input"
-      attributes   :
-        readonly   : "readonly"
-      click        : -> @selectAll()
+    new TeamworkShareModal { inputContent, shareWarning }


add better error messages, other fixes

add better error messages, other fixes

--- client/landing/app/MainApp/monitor_status.coffee
+++ client/landing/app/MainApp/monitor_status.coffee
@@ -89,14 +89,13 @@
     @on "sharedHostingDown", (channel) ->
       @emit "onlyChannelDown", "sharedHosting"

-    @on "internetDown", ->
-      notify "Internet Down"
-
   notify: (reason) ->
     notifications =
       internetUp : "All systems go!"
-      internetDown: "Your internet is down or very slow."
-      kitesDown: "Kites are down"
+      internetDown: "Your internet is down."
+      kitesDown: "Kites are down."
+      sharedHostingDown: "SharedHosting is down."
+      webtermDown: "Webterm is down."
       bongoDown: "Bongo is down"
       brokerDown: "Broker is Down."
       undefined: "Sorry, something went wrong."


Repairing edit button in board tree

Repairing edit button in board tree

--- client/src/app/game/board/boardController.coffee
+++ client/src/app/game/board/boardController.coffee
@@ -66,16 +66,16 @@
   $scope.edit = (path) ->
     chip = getBoardChip(currentGame.version.board, path)
     # Determine type of chip
-    if "action" of chip
+    if "processor" of chip
       showDialog 'game/board/editBoardAction.tpl.html', 'EditBoardActionDialogCtrl', chip, (model) ->
         _.extend(chip, model)
-    else if "process" of chip
+    else if "switch" of chip
       showDialog 'game/board/editBoardAction.tpl.html', 'EditBoardActionDialogCtrl', chip, (model) ->
         _.extend(chip, model)
-    else if "foreach" of chip
+    else if "splitter" of chip
       showDialog 'game/board/editBoardSplitter.tpl.html', 'EditBoardSplitterDialogCtrl', chip, (model) ->
         _.extend(chip, model)
-    else if "send" of chip
+    else if "emitter" of chip
       showDialog 'game/board/editBoardSend.tpl.html', 'EditBoardSendDialogCtrl', chip, (model) ->
         _.extend(chip, model)

@@ -166,16 +166,16 @@
   # Depending on if this is an action or a process, get the right kind of data
   # TODO: move this to calling controller?
   typeDef = null
-  if "action" of liaison.model
-    $scope.kind = "Action"
-    $scope.type = liaison.model.action
-    typeDef = currentGame.version.actions[$scope.type]
-  else if "process" of liaison.model
+  if "processor" of liaison.model
+    $scope.kind = "Processor"
+    $scope.type = liaison.model.processor
+    typeDef = currentGame.version.processors[$scope.type]
+  else if "switch" of liaison.model
     $scope.kind = "Switch"
-    $scope.type = liaison.model.process
-    typeDef = currentGame.version.processes[$scope.type]
+    $scope.type = liaison.model.switch
+    typeDef = currentGame.version.switches[$scope.type]
   else
-    throw new Error("Model is not an action or switch")
+    throw new Error("Model is not a processor or switch")

   # TODO: refactor into function
   $scope.pins = []


alphabetizing player collectibles, closes #34

--- shared/idle/controllers/players/IdleCollectiblesController.coffee
+++ shared/idle/controllers/players/IdleCollectiblesController.coffee
@@ -9,7 +9,7 @@
       PageTitle.setTitle "Idle Lands - #{if $stateParams.playerName then "#{$stateParams.playerName} (Collectibles)" else "Global Collectibles"}"

       $scope.getCollectibles = ->
-        if $scope.playerName then $scope.player?[0].collectibles else $scope.calculatedCollectibles
+        if $scope.playerName then _.sortBy $scope.player?[0].collectibles, (col) -> col.name else $scope.calculatedCollectibles

       $scope.calculateCollectibles = ->
         collectibleFinalList = []


correct coeffcients

correct coeffcients

--- mechanics/001-binary-stars/csmain.coffee
+++ mechanics/001-binary-stars/csmain.coffee
@@ -32,8 +32,8 @@
         x2 = - m1 / M * a

         v  = Math.sqrt(au.G * M / a * (1 + e) / (1 - e))
-        v1 = v / M * m2
-        v2 = - v / M * m1
+        v1 = v / M * m1
+        v2 = - v / M * m2

         ctx =
             keys: _.keys(ps)


StackTemplateListController: mark inuse stack templates before instantiate list

--- client/admin/lib/views/stacks/stacktemplatelistcontroller.coffee
+++ client/admin/lib/views/stacks/stacktemplatelistcontroller.coffee
@@ -10,7 +10,7 @@

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

-    options.noItemFoundText ?= "You have no stack template"
+    options.noItemFoundText ?= "You currently have no stack template"
     super options, data

     @loadItems()
@@ -30,6 +30,13 @@
       return if showError err, \
         KodingError : "Failed to fetch stackTemplates, try again later."

+      { groupsController } = kd.singletons
+      currentGroup = groupsController.getCurrentGroup()
+
+      stackTemplates.map (template) ->
+        console.log template._id, currentGroup.stackTemplates
+        template.inuse = template._id in (currentGroup.stackTemplates or [])
+
       @instantiateListItems stackTemplates


send message instance problem fixed

send message instance problem fixed

--- client/app/Applications/Inbox.kdapplication/AppController.coffee
+++ client/app/Applications/Inbox.kdapplication/AppController.coffee
@@ -20,6 +20,9 @@
     super options, data

     @selection = {}
+
+    @on 'MessageShouldBeSent', ({formOutput,callback})=>
+      @prepareMessage formOutput, callback

   fetchMessages:(options, callback)->
     KD.whoami().fetchMail? options, callback
@@ -133,6 +136,10 @@
       newMessageBar.enableMessageActionButtons()
       @selectMessage data, item, paneView

+    # in constructor we already add another listener to use without views
+    # we have to remove all the listeners and bind a new one to send
+    # after view is loaded.
+    @off 'MessageShouldBeSent'
     @on 'MessageShouldBeSent', ({formOutput,callback})=>
       @prepareMessage formOutput, callback, newMessageBar

@@ -199,5 +206,5 @@
         title     : if err then "There was an error sending your message - try again" else "Message Sent!"
         duration  : 1000
       message.mark 'read'
-      newMessageBar.emit 'RefreshButtonClicked'
+      newMessageBar?.emit 'RefreshButtonClicked'
       callback? err, message


Continuation of triangulation.

Continuation of triangulation.

--- packages/landsofillusions-assets/mesheditor/mesheditor.coffee
+++ packages/landsofillusions-assets/mesheditor/mesheditor.coffee
@@ -12,6 +12,7 @@

     @sprite = new ReactiveField null
     @edges = new ReactiveField null
+    @horizon = new ReactiveField null
     @pixelCanvas = new ReactiveField null
     @sourceImageVisible = new ReactiveField true
     @mesh = new ReactiveField null
@@ -34,6 +35,8 @@

     @pixelGridEnabled = new ReactiveField true
     @planeGridEnabled = new ReactiveField true
+
+    @currentCluster = new ReactiveField null

     @meshId = new ComputedField =>
       AB.Router.getParameter 'meshId'
@@ -85,17 +88,22 @@
     @edges new @constructor.Edges
       mesh: @mesh

+    @horizon new @constructor.Horizon
+      currentNormal: => @shadingSphere()?.currentNormal()
+      cameraAngle: @cameraAngle
+
     @pixelCanvas new LOI.Assets.Components.PixelCanvas
       initialCameraScale: 8
       activeTool: @activeTool
       lightDirection: @lightDirection
       drawComponents: =>
-        return [] unless @sourceImageVisible()
+        return [] unless @sourceImageVisible() and @spriteData()

         [
           @sprite()
           @landmarks()
           @edges()
+          @horizon()
         ]

       symmetryXOrigin: @symmetryXOrigin
@@ -109,6 +117,7 @@
       pixelCanvas: @pixelCanvas
       gridEnabled: @planeGridEnabled
       cameraAngle: @cameraAngle
+      currentCluster: @currentCluster
       currentNormal: => @shadingSphere()?.currentNormal()
       drawComponents: => [
         mesh()
@@ -136,7 +145,14 @@
       setAssetId: setAssetId
       getPaletteId: @paletteId
       setPaletteId: (paletteId) =>
+        # Update mesh palette.
         LOI.Assets.Mesh.update @meshId(), $set: palette: _id: paletteId
+
+        # Also update palettes of all sprites.
+        return unless cameraAngles = @meshData()?.cameraAngles
+
+        for cameraAngle in cameraAngles when cameraAngle.sprite
+          LOI.Assets.Sprite.update cameraAngle.sprite._id, $set: palette: _id: paletteId

     @materials new LOI.Assets.Components.Materials
       assetId: @meshId
@@ -170,13 +186,13 @@
       LOI.Assets.SpriteEditor.Tools.Pencil
       LOI.Assets.SpriteEditor.Tools.Eraser
       LOI.Assets.SpriteEditor.Tools.ColorFill
-      LOI.Assets.SpriteEditor.Tools.ColorPicker
-      LOI.Assets.MeshEditor.Tools.MoveCamera
+      @constructor.Tools.ClusterPicker
+      @constructor.Tools.MoveCamera
       LOI.Assets.SpriteEditor.Tools.PaintNormals
       LOI.Assets.SpriteEditor.Tools.Symmetry
-      LOI.Assets.MeshEditor.Tools.PixelGrid
-      LOI.Assets.MeshEditor.Tools.PlaneGrid
-      LOI.Assets.MeshEditor.Tools.SourceImage
+      @constructor.Tools.PixelGrid
+      @constructor.Tools.PlaneGrid
+      @constructor.Tools.SourceImage
     ]

     tools = for toolClass in toolClasses


Client: Activity: Updated onFollowChannel method to add accountId post parameter

--- client/activity/lib/components/publicchatpane/index.coffee
+++ client/activity/lib/components/publicchatpane/index.coffee
@@ -1,5 +1,6 @@
 kd              = require 'kd'
 React           = require 'kd-react'
+whoami          = require 'app/util/whoami'
 immutable       = require 'immutable'
 ActivityFlux    = require 'activity/flux'
 ChatPane        = require 'activity/components/chatpane'
@@ -38,7 +39,7 @@

   onFollowChannel: ->

-    ActivityFlux.actions.channel.followChannel @channel 'id'
+    ActivityFlux.actions.channel.followChannel @channel('id'), whoami()._id

   render: ->


home/utilities/apiaccess: code improvement

--- client/home/lib/utilities/components/apiaccess/view.coffee
+++ client/home/lib/utilities/components/apiaccess/view.coffee
@@ -3,16 +3,9 @@
 ApiToken = require './apitoken'
 List = require 'app/components/list'
 Toggle = require 'app/components/common/toggle'
-remote = require 'app/remote'
-TeamFlux = require 'app/flux/teams'
-showError = require 'app/util/showError'

 module.exports = class View extends React.Component
-
-  constructor: (props) ->
-
-    super props

   numberOfSections: -> 1
@@ -42,19 +35,6 @@

 No tokens have been created yet. When you create, they will be listed here.

-  addNewApiToken: ->
-
-    remote.api.JApiToken.create (err, apiToken) ->
-      return showError err  if err
-      TeamFlux.actions.addApiToken apiToken
-
-
-  switchToggle: (state) ->
-
-    TeamFlux.actions.disableApiTokens(state)
-    .catch ({err}) ->
-      showError err
-
   render: ->

     toggleState = @props.apiAccessState
@@ -62,7 +42,7 @@

       <Toggle checked={toggleState} className='HomeApp-ApiToken--swicth-toggle' callback={@bound 'switchToggle'} />
-
+

Active API Token List

       <List
         numberOfSections={@bound 'numberOfSections'}
@@ -79,10 +59,9 @@
           <AddNewApiTokenButton toggleState={toggleState} callback={@bound 'addNewApiToken'}/>

-

-Header = ({ callback }) ->
+Header = ->

   label = 'Enable API Access'
   description = 'Allow 3rd party services to securely communicate with Koding.'


Focus message input when screen is shown.

--- web/coffeescripts/systems.coffee
+++ web/coffeescripts/systems.coffee
@@ -236,6 +236,7 @@
     $('#container').show()
     @layout = this.resize()
     this.scroll()
+    $('#message').focus()

     new Filter
       list: '#roster-items'


Max refresh loop at 1 minute instead of 5

--- resources/assets/coffee/react/beatmap-discussions/main.coffee
+++ resources/assets/coffee/react/beatmap-discussions/main.coffee
@@ -161,7 +161,7 @@

   checkNewTimeoutDefault: 10000
-  checkNewTimeoutMax: 300000
+  checkNewTimeoutMax: 60000

   checkNew: ->
     @nextTimeout ?= @checkNewTimeoutDefault


Fix RegEx to work on windows

Fix RegEx to work on windows

Sass on windows creates files with `\r\n` for a new line. Adding \r to
the RegEx will fix it.
/cc @HugoGiraudel

--- dist/bootcamp.coffee
+++ dist/bootcamp.coffee
@@ -12,7 +12,7 @@
     # Get Camp
     ###

-    camp = file.match /\/\* >> Bootcamp >> \*\/((.|\n)*)\/\* << Bootcamp << \*\//
+    camp = file.match /\/\* >> Bootcamp >> \*\/((.|\n|\r)*)\/\* << Bootcamp << \*\//

     unless camp?
       return incomplete: true, error: 'No Test Suite Found'
@@ -23,7 +23,7 @@
     # Get Results
     ###

-    results = camp.match /Test Results \{((.|\n)*)\}/
+    results = camp.match /Test Results \{((.|\n|\r)*)\}/

     unless results?
       return incomplete: true, error: 'No Test Results Found'
@@ -41,7 +41,7 @@
     ###

     getProperty = (property, force) ->
-      values = results.match new RegExp property + ': ((.|\n)*?);', 'g'
+      values = results.match new RegExp property + ': ((.|\n|\r)*?);', 'g'
       property = []

       if force and !values? then return property


docs(deploy): test travis 3

docs(deploy): test travis 3

--- docs/scripts/deploy/deploy.coffee
+++ docs/scripts/deploy/deploy.coffee
@@ -22,10 +22,11 @@
 	switch deployer
 		when 'ci'
 			runTasks = async ->
-				res1 = await decryptKey docsRoot
-				res2 = await addDeployKey docsRoot
+				# res1 = await decryptKey docsRoot
+				# res2 = await addDeployKey docsRoot
 				resX = await testTravis docsRoot
-				res  = [res1, res2, resX]
+				# res  = [res1, res2, resX]
+				res  = [resX]
 				res.filter(Boolean).join '\n'

 				# res1 = await decryptKey docsRoot


thoughts => considerations

thoughts => considerations

--- @client/collapsed_proposal.coffee
+++ @client/collapsed_proposal.coffee
@@ -225,7 +225,7 @@
                       id: "engage.point_count"
                       cnt: proposal.point_count

-                      "{cnt, plural, =0 {} one {# thought} other {# thoughts}}"
+                      "{cnt, plural, one {# consideration} other {# considerations}}"

             if @props.show_category && proposal.cluster


natural: broaden status forms

natural: broaden status forms

--- lib/capabilities/ControlBrowserCapability.coffee
+++ lib/capabilities/ControlBrowserCapability.coffee
@@ -34,9 +34,9 @@
         d.promise
     status:
       forms: [
-        '(what is |describe )?(the )?(status|state) of( the)? <control>'
-        'is( the)? <control> (on|off|open|closed|locked|unlocked)'
-        '<control> (status|state)']
+        '(what is |describe|report )?(the )?(status|state) of( the)? <control>'
+        '(what is |describe|report )?(the )?<control> (status|state)'
+        'is( the)? <control> (on|off|open|closed|locked|unlocked)']
       resolve: (cap, {control}) ->
         control = cap.resolveControlName(control)
         if control? then {control: control} else null


limit indexing

limit indexing

--- elasticsearch.coffee
+++ elasticsearch.coffee
@@ -151,7 +151,9 @@
             console.log "indexed", gist.id
             return gistCb(err)
         else
-          return gistCb()
+          setTimeout ->
+            return gistCb()
+          , Math.random() * 50 + 50
     else
       # post to elastic search
       client.index
@@ -161,7 +163,9 @@
         body: es
       , (err) ->
         console.log "indexed", gist.id
-        return gistCb(err)
+        setTimeout ->
+          return gistCb(err)
+        , Math.random() * 50 + 50

 deleteGist = (gistId, gistCb) ->
   client.delete


Remove stray commented-out code, use mapSeries instead of map.

--- services/web/app/coffee/Features/User/UserSessionsManager.coffee
+++ services/web/app/coffee/Features/User/UserSessionsManager.coffee
@@ -5,7 +5,6 @@
 _ = require('underscore')
 UserSessionsRedis = require('./UserSessionsRedis')

-# rclient = redis.createClient(Settings.redis.web)
 rclient = UserSessionsRedis.client()

 module.exports = UserSessionsManager =
@@ -69,7 +68,7 @@
 				logger.log {user_id: user._id}, "no other sessions found, returning"
 				return callback(null, [])

-			Async.map sessionKeys, ((k, cb) -> rclient.get(k, cb)), (err, sessions) ->
+			Async.mapSeries sessionKeys, ((k, cb) -> rclient.get(k, cb)), (err, sessions) ->
 				if err?
 					logger.err {user_id: user._id}, "error getting all sessions for user from redis"
 					return callback(err)


Fix bug with leading digit classes

Fix bug with leading digit classes

--- vendor/assets/javascripts/ragefaces/rager.js.coffee
+++ vendor/assets/javascripts/ragefaces/rager.js.coffee
@@ -8,6 +8,6 @@
       markup = match.substring(1, match.length-1)
       if markup in @valid_tags
         classname = if /^[0-9]/.test(markup) then "_#{markup}" else markup
-        text = text.replace(match, "")
+        text = text.replace(match, "")
     return text
 window.ragefaces = new RageFaces


Remove overflow hidden

Remove overflow hidden

--- framer/Config.coffee
+++ framer/Config.coffee
@@ -1,11 +1,6 @@
 Utils = require "./Utils"

 FramerCSS = """
-body {
-	overflow: hidden;
-	pointer-events: none
-}
-
 .framerContext {
 	position: absolute
 	left: 0


Create "click" event using an event constructor

The way the click event was being created is deprecated. An event constructor should be used instead:
https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/Events/Creating_and_triggering_events

In addition using the event constructor fixes a scrolling bug where clicking the "OK" button in the
modal caused a scroll to the top of the page.

--- vendor/assets/javascripts/twitter/bootstrap/rails/confirm.coffee
+++ vendor/assets/javascripts/twitter/bootstrap/rails/confirm.coffee
@@ -54,24 +54,22 @@
         if element.get(0).click
           element.get(0).click()
         else if $.isFunction(document.createEvent)
-          evt = document.createEvent "MouseEvents"
-          evt.initMouseEvent(
-            "click",
-            true,   # e.bubbles,
-            true,   #e.cancelable,
-            window, #e.view,
-            0,      #e.detail,
-            0,      #e.screenX,
-            0,      #e.screenY,
-            0,      #e.clientX,
-            0,      #e.clientY,
-            false,  #e.ctrlKey,
-            false,  #e.altKey,
-            false,  #e.shiftKey,
-            false,  #e.metaKey,
-            0,      #e.button,
-            document.body.parentNode #e.relatedTarget
-          )
+          evt = new MouseEvents("click", {
+            bubbles : true,
+            cancelable : true,
+            view : window,
+            detail : 0,
+            screenX : 0,
+            screenY : 0,
+            clientX : 0,
+            clientY : 0,
+            ctrlKey : false,
+            altKey : false,
+            shiftKey : false,
+            metaKey : false,
+            button : 0,
+            relatedTarget : document.body.parentNode
+          })
           element.get(0).dispatchEvent(evt)
         else
           element.trigger "click"


Use https for mapbox tiles

Use https for mapbox tiles

--- app/assets/javascripts/map.coffee
+++ app/assets/javascripts/map.coffee
@@ -8,7 +8,7 @@

   map = L.map(map_div)

-  tiles = L.tileLayer('http://{s}.tiles.mapbox.com/v3/eikes.he4660k4/{z}/{x}/{y}.png', {
+  tiles = L.tileLayer('https://{s}.tiles.mapbox.com/v3/eikes.he4660k4/{z}/{x}/{y}.png', {
     attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
     maxZoom: 18
   })


Fix broken drag on right panel

Fix broken drag on right panel

--- BackofficeBundle/Resources/public/coffee/page/makeSortable.coffee
+++ BackofficeBundle/Resources/public/coffee/page/makeSortable.coffee
@@ -19,9 +19,11 @@
   return

 makeSortable = (el, duplicate) ->
+  handler = '.move-tool'
+  handler = false if $(el).parent().hasClass('js-widget-blockpanel')
   $("ul.ui-model-blocks", el).sortable(
     connectWith: '#content div[role="container"] ul.ui-model-blocks',
-    handle: '.move-tool',
+    handle: handler,
     appendTo: 'body',
     tolerance: 'pointer',
     zIndex: 100000,