Make it so we're further taking advantage of the "multi-return" of coffeescript.  Gotta say, this is actually making the code a lot cleaner.

--- guts/routing.coffee
+++ guts/routing.coffee
@@ -19,16 +19,7 @@
                 # NOTE: I really hate that the value comes before the key.
                 _.each configuredRoutes, (totalString, route) ->
                         # routes are stored like METHOD /route, so we'll split on spaces.
-                        routeComponent = route.split ' '
-
-                        # THe first item should be the method, so we'll grab that.  The toLowerCase
-                        # function is there to make the routing a bit more dev friendly in case
-                        # they want to use upper-case
-                        method = routeComponent[0].toLowerCase()
-
-                        #The second item in that array should be the endpoint. Lets' define a quick
-                        # helper-variable.
-                        endpoint = routeComponent[1]
+                        [method, endpoint] = route.split ' '

                         # This bigass thing here is for dev-friendliness.  I don't want to punish people
                         # for separating controller calls to multiple lines if they want, and I don't want
@@ -37,15 +28,15 @@
                         # sign so I can make the dichotomy between application logic and error-handling logic.
                         # Utilizing Coffee's "multi-return" thing, I can set the values pretty cleanly,
                         # but not before I make sure the values are trimmed.
-                        console.log totalString
                         [actionString, catchString] = totalString
                                                       .replace(/(?:\r\n|\r|\n)/g, ' ')
                                                       .replace( /\s\s+/g, ' ' )
                                                       .trim().split('@')
                                                       .map (i) -> i.trim()

-                        #Again, just make it cleaner, we'll utilize the "multi-return" thing coffeescript does.
-                        console.log actionString
+                        # Again, just make it cleaner, we'll utilize the "multi-return" thing coffeescript does.
+                        # Since I don't want a hard requirement for a catch to exist, I will put a conditional to
+                        # see if the catch is defined.
                         [catchController,catchFunction] = catchString.split '.' if catchString?

@@ -57,19 +48,22 @@
                         #
                         # So as to save a "push" command we'll use a map
                         actionHandles = _.map allRoutes, (a) ->
-                                # We want to make it so that there's an option to handle
-                                # errors on an individual level.
-                                #
-                                # We use the questions mark becauae we wanna make it optional
-                                # to have an error handler
-                                errorStuff = a.split('!')?[1]?.split('.')
+                                # First we need to separate out the error handling stuff from the
+                                # actual program, so let's split that out, and utilize the multi-return
+                                # value feature of coffeescript.
+                                [actionStuff, errorStuff] = a.split '!'
+
+                                # Since I don't want to make a hard requirement to provide an error-handler,
+                                # Let's put a put quick "if" at the end of this to make sure we have errorstuff
+                                # to assign.
+                                [errorController, errorAction] = errorStuff.split '.' if errorStuff?

                                 # We need to parse out the actual funtions being used to handle errors
-                                error = controllerObject[errorStuff?[0]]?[errorStuff?[1]]
+                                error = controllerObject[errorController]?[errorAction]

                                 # Since the actinos are written like controller.action, we need to split
                                 # on the '.' to separate them.
-                                [myController, myAction] = a.split '.'
+                                [myController, myAction] = actionStuff.split '.'

                                 return {action: controllerObject[myController][myAction], errorHandler: error}

@@ -117,5 +111,6 @@

                         # As stated above, wrapper will return a new function based on what
-                        # we send in for "allRoutes"
-                        app[method] endpoint, wrapper
+                        # we send in for "allRoutes".  We're adding the "toLowerCase()" to make
+                        # this a bit more dev-friendly.
+                        app[method.toLowerCase()] endpoint, wrapper


Bubble the error to top if we don't handle it in Ember.Route

A proper thing to do when an error is not handled is to bubble it up
rather than using throw (which we were using before this commit).

--- assets/scripts/app/routes.coffee
+++ assets/scripts/app/routes.coffee
@@ -20,7 +20,7 @@
         authController.set('redirected', true)
         @transitionTo('auth')
       else
-        throw(error)
+        return true

     renderNoOwnedRepos: ->
       @render('no_owned_repos', outlet: 'main')


Make menu's checked property flipped automatically, fixes #349.

--- atom/browser/api/lib/menu.coffee
+++ atom/browser/api/lib/menu.coffee
@@ -30,6 +30,7 @@

   unless @delegate?
     @commandsMap = {}
+    @groupsMap = {}
     @items = []
     @delegate =
       isCommandIdChecked: (commandId) => @commandsMap[commandId]?.checked
@@ -38,9 +39,21 @@
       getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator
       executeCommand: (commandId) =>
         activeItem = @commandsMap[commandId]
-        activeItem.click() if activeItem?
+        if activeItem?
+          switch activeItem.type
+            when 'checkbox'
+              activeItem.checked = !activeItem.checked
+            when 'radio'
+              for item in @groupsMap[activeItem.groupId]
+                item.checked = false
+              activeItem.checked = true
+          activeItem.click()
   @items.splice pos, 0, item
   @commandsMap[item.commandId] = item
+
+  if item.groupId?
+    @groupsMap[item.groupId] ?= []
+    @groupsMap[item.groupId].push item

 applicationMenu = null
 Menu.setApplicationMenu = (menu) ->


Fixed delete().

Fixed delete().

--- src/Typeform.coffee
+++ src/Typeform.coffee
@@ -40,6 +40,7 @@
     @_handleHttpResponse(df, 'crating typeform')

   delete: (data) ->
+    df = Q.defer()
     urls = @getUrlsFromData(data)
     HTTP.del Paths.join(urls.self), {
       headers: @_getHeaders()


add usable

add usable

--- src/compilers/amd.coffee
+++ src/compilers/amd.coffee
@@ -31,7 +31,7 @@

 NG_PREFIX = 'ng'
 FN_ARGS_REG = '(\\b\\s*[^\\(]*\\(\\s*[^\\)]*\\))'
-authorisedFunctions = ['run', 'config', 'module', 'factory', 'filter', 'directive', 'controller', 'service', 'value', 'constant', 'decorator', 'provider']
+authorisedFunctions = ['usable', 'run', 'config', 'module', 'factory', 'filter', 'directive', 'controller', 'service', 'value', 'constant', 'decorator', 'provider']
 selectorReg = '(' + NG_PREFIX + authorisedFunctions.join( '|' + NG_PREFIX) + ')'
 NG_REGEXP = new RegExp "^(?:#{selectorReg}\\s*=|function\s+#{selectorReg}\\b|factory\\s*=\\s*function#{FN_ARGS_REG}|function\\s+factory#{FN_ARGS_REG})", 'mg'


🎨 Remove duplicate "Note:"

🎨 Remove duplicate "Note:"

--- src/config-schema.coffee
+++ src/config-schema.coffee
@@ -271,4 +271,4 @@
   module.exports.core.properties.useCustomTitleBar =
     type: 'boolean'
     default: false
-    description: 'Use custom, theme-aware title-bar.
Note: Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect.'
+    description: 'Use custom, theme-aware title bar.
Note: This currently does not include a proxy icon.
This setting will require a relaunch of Atom to take effect.'


support hidden fields and larger result sets

--- query_runner.coffee
+++ query_runner.coffee
@@ -128,15 +128,32 @@
     @replyContext.looker.storeBlob(imageData, success, error)

   postResult: (query, result, options = {}) ->
-    if result.data.length == 0
+
+    # Handle hidden fields
+    hiddenFields = query.vis_config?.hidden_fields || []
+    if hiddenFields?.length > 0
+      for k, v of result.fields
+        result.fields[k] = v.filter((field) -> hiddenFields.indexOf(field.name) == -1)
+
+    calcs = result.fields.table_calculations || []
+    dimensions = result.fields.dimensions || []
+    measures = result.fields.measures || []
+    measure_like = measures.concat(calcs.filter((c) -> c.is_measure))
+    dimension_like = dimensions.concat(calcs.filter((c) -> !c.is_measure))
+
+    if result.pivots
+      @reply("#{query.share_url}\n _Can't currently display tables with pivots in Slack._")
+
+    else if result.data.length == 0
       if result.errors?.length
         txt = result.errors.map((e) -> "#{e.message}```#{e.message_details}```").join("\n")
         @reply(":warning: #{query.share_url}\n#{txt}")
       else
         @reply("#{query.share_url}\nNo results.")
-    else if result.fields.dimensions.length == 0
+
+    else if dimension_like.length == 0 && measure_like.length > 0
       attachment = _.extend({}, options, {
-        fields: result.fields.measures.map((m) ->
+        fields: measure_like.map((m) ->
           {title: m.label, value: result.data[0][m.name].rendered, short: true}
         )
       })
@@ -144,12 +161,13 @@
         attachments: [attachment]
         text: if @showShareUrl() then query.share_url else ""
       )
-    else if result.fields.dimensions.length == 1 && result.fields.measures.length == 0
+
+    else if dimension_like.length == 1 && measure_like.length == 0
       attachment = _.extend({}, options, {
         fields: [
-          title: result.fields.dimensions[0].label
+          title: dimension_like[0].label
           value: result.data.map((d) ->
-            d[result.fields.dimensions[0].name].rendered
+            d[dimension_like[0].name].rendered
           ).join("\n")
         ]
       })
@@ -157,14 +175,13 @@
         attachments: [attachment]
         text: if @showShareUrl() then query.share_url else ""
       )
-    else if (result.fields.dimensions.length == 1 && result.fields.measures.length == 1) || (result.fields.dimensions.length == 2 && result.fields.measures.length == 0)
-      dim = result.fields.dimensions[0]
-      mes = result.fields.measures[0] || result.fields.dimensions[1]
+    else
+      fields = dimension_like.concat(measure_like)
       attachment = _.extend({}, options, {
         fields: [
-          title: "#{dim.label} – #{mes.label}"
+          title: fields.map((f) -> f.label).join(" – ")
           value: result.data.map((d) ->
-            "#{d[dim.name].rendered} – #{d[mes.name].rendered}"
+            fields.map((f) -> d[f.name].rendered).join(" – ")
           ).join("\n")
         ]
       })
@@ -172,8 +189,6 @@
         attachments: [attachment]
         text: if @showShareUrl() then query.share_url else ""
       )
-    else
-      @reply("#{query.share_url}\n_Result table too large to display in Slack._")

   work: ->
     @replyContext.looker.client.get("queries/slug/#{@querySlug}", (query) =>


namespaced unbind

namespaced unbind

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -93,6 +93,7 @@
         else
           eventCallbacks[event] = []
     else if namespace
+
       for key, callbacks of eventCallbacks
         eventCallbacks[key] = callbacks.select (callback) ->
           !callback.__PIXIE?[namespace]?


s/unobserver()/unobserve()

s/unobserver()/unobserve()

--- src/bindings.coffee
+++ src/bindings.coffee
@@ -94,7 +94,7 @@
     @binder.unbind?.call @, @el
     @observer.unobserve()

-    observer.unobserver() for observer in @dependencies
+    observer.unobserve() for observer in @dependencies
     @dependencies = []

   # Updates the binding's model from what is currently set on the view. Unbinds


Credentials: use Vendors information from ComputeProvider.vendors

--- client/Account/views/credentials.coffee
+++ client/Account/views/credentials.coffee
@@ -1,87 +1,3 @@
-Vendors =
-
-  custom                   :
-    title                  : "Custom Credential"
-    description            : """Custom credentials can include meta
-                               credentials for any service"""
-    credentialFields       :
-      credential           :
-        label              : "Credential"
-        placeholder        : "credential in JSON format"
-        type               : "textarea"
-
-  amazon                   :
-    title                  : "AWS Credential"
-    description            : "Amazon Web Services"
-    credentialFields       :
-      accessKeyId          :
-        label              : "Access Key"
-        placeholder        : "aws access key"
-      secretAccessKey      :
-        label              : "Secret Key"
-        placeholder        : "aws secret key"
-        type               : "password"
-      region               :
-        label              : "Region"
-        placeholder        : "aws region"
-        defaultValue       : "us-east-1"
-
-  koding                   :
-    title                  : "Koding Credential"
-    description            : "Koding rulez."
-    credentialFields       :
-      username             :
-        label              : "Username"
-        placeholder        : "koding username"
-      password             :
-        label              : "Password"
-        placeholder        : "koding password"
-        type               : "password"
-
-  google                   :
-    title                  : "Google Cloud Credential"
-    description            : "Google compute engine"
-    credentialFields       :
-      projectId            :
-        label              : "Project Id"
-        placeholder        : "project id in gce"
-      clientSecretsContent :
-        label              : "Client secrets"
-        placeholder        : "content of the client_secrets.xxxxx.json"
-        type               : "textarea"
-      privateKeyContent    :
-        label              : "Private Key"
-        placeholder        : "content of the xxxxx-privatekey.pem"
-        type               : "textarea"
-      zone                 :
-        label              : "Zone"
-        placeholder        : "google zone"
-        defaultValue       : "us-central1-a"
-
-  engineyard               :
-    title                  : "EngineYard Credential"
-    description            : "EngineYard"
-    credentialFields       :
-      accountId            :
-        label              : "Account Id"
-        placeholder        : "account id in engineyard"
-      secret               :
-        label              : "Secret"
-        placeholder        : "engineyard secret"
-        type               : "password"
-
-  digitalocean             :
-    title                  : "Digitalocean Credential"
-    description            : "Digitalocean droplets"
-    credentialFields       :
-      clientId             :
-        label              : "Client Id"
-        placeholder        : "client id in digitalocean"
-      apiKey               :
-        label              : "API Key"
-        placeholder        : "digitalocean api key"
-
-
 class AccountCredentialListController extends AccountListViewController

   constructor:(options = {}, data)->
@@ -116,6 +32,8 @@
     view.on "ShowShareCredentialFormFor", @bound "showShareCredentialFormFor"

     vendorList = { }
+
+    Vendors = ComputeProvider.vendors

     Object.keys(Vendors).forEach (vendor)=>
       vendorList[Vendors[vendor].title] =
@@ -234,6 +152,8 @@
         label       : "Title"
         placeholder : "title for this credential"

+    Vendors = ComputeProvider.vendors
+
     Object.keys(Vendors[vendor].credentialFields).forEach (field)->
       fields[field] = _.clone Vendors[vendor].credentialFields[field]
       fields[field].required = yes


MachineSettingsModal: Use computeController.setDomain to update domain record.

--- client/Main/machinesettingsmodal.coffee
+++ client/Main/machinesettingsmodal.coffee
@@ -260,10 +260,14 @@
     unless KD.utils.domainWithTLDPattern.test domain
       return new KDNotificationView title: 'Invalid domain name'

-    @machine.jMachine.setDomain domain, (err) =>
-      unless err
+
+    KD.getSingleton('computeController').setDomain @machine, domain, (err, res) =>
+      if err
+        title = 'An error occured, please try again.'
+        warn err
+      else
         @machine.jMachine.domain = domain
         @machine.updateLocalData()
-        @machine.emit 'MachineDomainUpdated'
-
-      new KDNotificationView title : err?.message or 'Domain settings updated'
+        title = 'Domain settings updated'
+
+      new KDNotificationView { title }


Was not updating the window title on profile update, fixed.

--- client/app/CommonViews/FormViews.coffee
+++ client/app/CommonViews/FormViews.coffee
@@ -134,7 +134,10 @@
         new KDNotificationView
           title     : "Success!"
           duration  : 500
-        @unsetClass 'active'
+
+        @unsetClass "active"
+        # Set window title too
+        document.title = "#{firstName} #{lastName}"

 class PersonalFormAboutView extends AbstractPersonalFormView


LinkViews: fix cosmetic views of headings

--- client/app/CommonViews/LinkViews.coffee
+++ client/app/CommonViews/LinkViews.coffee
@@ -20,8 +20,8 @@
 class FollowedModalView extends KDModalView

   titleMap = ->
-    account : "members"
-    tag     : "topics"
+    account : "Members"
+    tag     : "Topics"

   listControllerMap = ->
     account : MembersListViewController


Make sure state exists before reading

Make sure state exists before reading

--- client/lanes/react/mixins/Access.coffee
+++ client/lanes/react/mixins/Access.coffee
@@ -13,7 +13,7 @@
     comp.setState({accessRight}) if accessRight

 Lanes.React.Mixins.Access = {
-
+    getInitialState: -> {}
     componentDidMount: ->
         calculateAccess(this, @props)


Test:Myteam coffelint error fixed

Test:Myteam coffelint error fixed

--- client/test/lib/dashboard/myteam.coffee
+++ client/test/lib/dashboard/myteam.coffee
@@ -15,6 +15,46 @@
       done()

   teamSettings: (browser) ->
+
+    sectionSelector = '.HomeAppView--section.team-settings'
+    buttonSelector = "#{sectionSelector} .uploadInputWrapper .HomeAppView--button.custom-link-view"
+    uploadLogoButton = "#{buttonSelector}.primary"
+    removeLogoButton = "#{buttonSelector}.remove"
+    teamNameSelector = "#{sectionSelector} .half input[type=text]"
+    saveChangesButton = "#{sectionSelector} .HomeAppView--ActionBar .HomeAppView--button.custom-link-view.primary.fr"
+    kodingLogo = "#{sectionSelector} .HomeAppView--uploadLogo img"
+    defaultLogoPath = "#{helpers.getUrl(yes)}/a/images/logos/sidebar_footer_logo.svg"
+
+    localImage = "#{__dirname}/upload.png"
+    localImage = require('path').resolve(localImage)
+
+    teamLogo = "#{sectionSelector} .kdinput.file"
+    successMessage = 'Team settings has been successfully updated.'
+
+    user = teamsHelpers.loginTeam browser
+    executeCommand = "document.querySelector('.teamLogo').setAttribute('src', 'some_path');"
+
+    browser
+      .url myTeamLink
+      .waitForElementVisible sectionSelector, 20000
+      .click saveChangesButton
+      .waitForElementNotPresent '.kdnotification', 5000
+      .clearValue               teamNameSelector
+      .pause 1000
+      .setValue teamNameSelector, 'random'
+      .click saveChangesButton
+
+    teamsHelpers.assertConfirmation browser, successMessage
+    browser
+      .clearValue teamNameSelector
+      .setValue teamNameSelector, user.teamSlug
+      .click saveChangesButton
+
+    teamsHelpers.assertConfirmation browser, successMessage
+
+    browser.end()
+
+  teamSettingsMember: (browser) ->

     sectionSelector      = '.HomeAppView--section.team-settings'
     welcomeView          = '.WelcomeStacksView'
@@ -27,18 +67,6 @@
     passwordSelector     = 'input[name=password]'
     forgotPasswordButton = '.kdbutton.solid.light-gray'
     confirmButton        = 'button[type=submit]'
-
-    # kodingLogo = "#{sectionSelector} .HomeAppView--uploadLogo img"
-    # defaultLogoPath = "#{helpers.getUrl(yes)}/a/images/logos/sidebar_footer_logo.svg"
-
-    # localImage = "#{__dirname}/upload.png"
-    # localImage = require('path').resolve(localImage)
-
-    # teamLogo = "#{sectionSelector} .kdinput.file"
-    # successMessage = 'Team settings has been successfully updated.'
-
-    # user = teamsHelpers.loginTeam browser
-    # executeCommand = "document.querySelector('.teamLogo').setAttribute('src', 'some_path');"

     targetUser1 = utils.getUser no, 1
     teamsHelpers.logoutTeam browser, (res) ->
@@ -79,164 +107,148 @@
         .setValue passwordSelector, targetUser1.password
         .click confirmButton
         .assert.urlContains helpers.getUrl(yes)
-
-      # .click saveChangesButton
-      # .waitForElementNotPresent '.kdnotification', 5000
-      # .clearValue               teamNameSelector
-      # .pause 1000
-      # .setValue teamNameSelector, 'random'
-      # .click saveChangesButton
-
-    # teamsHelpers.assertConfirmation browser, successMessage
-    # browser
-    #   .clearValue teamNameSelector
-    #   .setValue teamNameSelector, user.teamSlug
-    #   .click saveChangesButton
-
-    # teamsHelpers.assertConfirmation browser, successMessage
-
-    # browser.end()
-
-
-#   inviteAndJoinUsersToTeam: (browser) ->
-
-#     section = '.kdcustomscrollview.HomeAppView--scroller.my-team'
-#     sectionSelector = '.HomeAppView--section.teammates'
-#     filterSelector = "#{sectionSelector} .kdinput.text.hitenterview"
-#     scrollElement = "#{sectionSelector} .ListView"
-
-#     host = teamsHelpers.loginTeam browser
-#     { invitations, index } = utils.getInvitationData()
-#     index = if index is 0 then 1 else index
-#     indexOfTargetUser1 = if 1 % index isnt 0 then 1 else 2
-#     indexOfTargetUser2 = if 3 % index isnt 0 then 3 else 4
-#     indexOfTargetUser3 = if 5 % index isnt 0 then 5 else 6
-
-#     browser
-#       .url myTeamLink, (result) ->
-#         if result.status is 0
-#           teamsHelpers.inviteUsers browser, invitations, (res) ->
-#             teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser1], (res) ->
-#               teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser2], (res) ->
-#                 teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser3], (res) ->
-#                   browser
-#                     .url myTeamLink
-#                     .waitForElementVisible section, 20000
-#                     .waitForElementVisible sectionSelector, 20000
-#                     .scrollToElement scrollElement
-#                     .click selector 'role', indexOfTargetUser1 + 1
-#                     .pause 1000
-#                     .click nthItem 2
-#                     .pause 1000
-#                     .click selector 'role', indexOfTargetUser1 + 1
-#                     .pause 1000
-#                     .click nthItem 2
-#                     .pause 1000
-#                     .assert.containsText selector('role', indexOfTargetUser1 + 1), 'Member'
-#                     .pause 1000
-#                     .click selector 'role', indexOfTargetUser2 + 1
-#                     .pause 1000
-#                     .click nthItem 2
-#                     .pause 1000
-#                     .assert.containsText selector('role', indexOfTargetUser2 + 1), 'Admin'
-#                     .end()
-
-#   searchAndChangeRoleOfTeamMates: (browser) ->
-
-#     { invitations, index } = utils.getInvitationData()
-
-#     index1 = if index is 0 then 1 else index
-#     indexOfTargetUser1 = if 1 % index1 isnt 0 then 1 else 2
-#     indexOfTargetUser2 = if 3 % index1 isnt 0 then 3 else 4
-#     indexOfTargetUser3 = if 5 % index1 isnt 0 then 5 else 6
-
-#     invitations[indexOfTargetUser1].accepted = 'Member'
-#     invitations[indexOfTargetUser2].accepted = 'Admin'
-#     invitations[indexOfTargetUser3].accepted = 'Member'
-#     invitations[index].accepted = 'Owner'
-
-#     lastPendingInvitationIndex = 0
-#     invitations.forEach (invitation, i) ->
-#       unless invitation.accepted
-#         lastPendingInvitationIndex = i
-
-#     section = '.kdcustomscrollview.HomeAppView--scroller.my-team'
-#     sectionSelector = '.HomeAppView--section.teammates'
-#     filterSelector = "#{sectionSelector} .kdinput.text.hitenterview"
-#     listViewSelector = "#{sectionSelector} .ListView .ListView-row"
-#     scrollElement = "#{sectionSelector} .ListView"
-
-#     user = teamsHelpers.loginTeam browser
-#     browser
-#       .url myTeamLink
-#       .waitForElementVisible section, 2000
-#       .waitForElementVisible sectionSelector, 20000
-#       .scrollToElement scrollElement
-#       .waitForElementVisible selector('role', 1), 20000
-#       .click selector('role', 1), ->
-#         teamsHelpers.checkTeammates browser, invitations[0], nthItem(1), nthItem(2), selector('role', 1), no, ->
-#           browser.waitForElementVisible selector('role', 1), 20000
-#           browser.click selector('role', 2), ->
-#             teamsHelpers.checkTeammates browser, invitations[1], nthItem(1), nthItem(2), selector('role', 2), no, ->
-#               browser.expect.element(selector('role', index + 1)).text.to.contain 'Owner'
-#               browser.click selector('role', lastPendingInvitationIndex + 1), ->
-#                 teamsHelpers.checkTeammates browser, invitations[lastPendingInvitationIndex], nthItem(1), nthItem(2), selector('role', lastPendingInvitationIndex + 1), yes, ->
-#                   browser
-#                     .waitForElementNotPresent selector('fullname', lastPendingInvitationIndex + 1), 20000
-#                     .end()
-
-
-#   sendInvites: (browser) ->
-
-#     user = teamsHelpers.loginTeam browser
-#     browser.url myTeamLink
-#     teamsHelpers.inviteUser browser, 'member'
-#     browser.pause 5000
-#     teamsHelpers.inviteUser browser, 'admin'
-#     browser.end()
-
-
-#   sendInvitesToAll: (browser) ->
-
-#     user = teamsHelpers.loginTeam browser
-#     browser.url myTeamLink
-#     teamsHelpers.inviteAll browser
-#     browser.end()
-
-
-#   checkUploadCSV: (browser) ->
-
-#     user = teamsHelpers.loginTeam browser
-#     browser.url myTeamLink
-#     teamsHelpers.uploadCSV browser
-#     browser.end()
-
-
-#   resendInvitation: (browser) ->
-
-#     user = teamsHelpers.loginTeam browser
-#     browser.url myTeamLink
-#     teamsHelpers.resendInvitation browser, 'member'
-#     browser.pause 5000
-#     teamsHelpers.resendInvitation browser, 'admin'
-#     browser.end()
-
-
-#   newInviteFromResendModal: (browser) ->
-
-#     user = teamsHelpers.loginTeam browser
-#     browser.url myTeamLink
-#     teamsHelpers.newInviteFromResendModal browser, 'member'
-#     browser.pause 5000
-#     teamsHelpers.newInviteFromResendModal browser, 'admin'
-#     browser.end()
-
-
-# selector = (type, index) ->
-#   sectionSelector = '.HomeAppView--section.teammates'
-#   memberSelector = '.kdview.kdlistitemview.kdlistitemview-member'
-#   "#{sectionSelector} .ListView-row:nth-of-type(#{index}) #{memberSelector} .#{type}"
-
-
-# nthItem = (index) ->
-#   ".menu-class ul.ButtonWithMenuItemsList li:nth-of-type(#{index})"
+        .end
+
+
+  inviteAndJoinUsersToTeam: (browser) ->
+
+    section = '.kdcustomscrollview.HomeAppView--scroller.my-team'
+    sectionSelector = '.HomeAppView--section.teammates'
+    filterSelector = "#{sectionSelector} .kdinput.text.hitenterview"
+    scrollElement = "#{sectionSelector} .ListView"
+
+    host = teamsHelpers.loginTeam browser
+    { invitations, index } = utils.getInvitationData()
+    index = if index is 0 then 1 else index
+    indexOfTargetUser1 = if 1 % index isnt 0 then 1 else 2
+    indexOfTargetUser2 = if 3 % index isnt 0 then 3 else 4
+    indexOfTargetUser3 = if 5 % index isnt 0 then 5 else 6
+
+    browser
+      .url myTeamLink, (result) ->
+        if result.status is 0
+          teamsHelpers.inviteUsers browser, invitations, (res) ->
+            teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser1], (res) ->
+              teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser2], (res) ->
+                teamsHelpers.acceptAndJoinInvitation host, browser, invitations[indexOfTargetUser3], (res) ->
+                  browser
+                    .url myTeamLink
+                    .waitForElementVisible section, 20000
+                    .waitForElementVisible sectionSelector, 20000
+                    .scrollToElement scrollElement
+                    .click selector 'role', indexOfTargetUser1 + 1
+                    .pause 1000
+                    .click nthItem 2
+                    .pause 1000
+                    .click selector 'role', indexOfTargetUser1 + 1
+                    .pause 1000
+                    .click nthItem 2
+                    .pause 1000
+                    .assert.containsText selector('role', indexOfTargetUser1 + 1), 'Member'
+                    .pause 1000
+                    .click selector 'role', indexOfTargetUser2 + 1
+                    .pause 1000
+                    .click nthItem 2
+                    .pause 1000
+                    .assert.containsText selector('role', indexOfTargetUser2 + 1), 'Admin'
+                    .end()
+
+  searchAndChangeRoleOfTeamMates: (browser) ->
+
+    { invitations, index } = utils.getInvitationData()
+
+    index1 = if index is 0 then 1 else index
+    indexOfTargetUser1 = if 1 % index1 isnt 0 then 1 else 2
+    indexOfTargetUser2 = if 3 % index1 isnt 0 then 3 else 4
+    indexOfTargetUser3 = if 5 % index1 isnt 0 then 5 else 6
+
+    invitations[indexOfTargetUser1].accepted = 'Member'
+    invitations[indexOfTargetUser2].accepted = 'Admin'
+    invitations[indexOfTargetUser3].accepted = 'Member'
+    invitations[index].accepted = 'Owner'
+
+    lastPendingInvitationIndex = 0
+    invitations.forEach (invitation, i) ->
+      unless invitation.accepted
+        lastPendingInvitationIndex = i
+
+    section = '.kdcustomscrollview.HomeAppView--scroller.my-team'
+    sectionSelector = '.HomeAppView--section.teammates'
+    filterSelector = "#{sectionSelector} .kdinput.text.hitenterview"
+    listViewSelector = "#{sectionSelector} .ListView .ListView-row"
+    scrollElement = "#{sectionSelector} .ListView"
+
+    user = teamsHelpers.loginTeam browser
+    browser
+      .url myTeamLink
+      .waitForElementVisible section, 2000
+      .waitForElementVisible sectionSelector, 20000
+      .scrollToElement scrollElement
+      .waitForElementVisible selector('role', 1), 20000
+      .click selector('role', 1), ->
+        teamsHelpers.checkTeammates browser, invitations[0], nthItem(1), nthItem(2), selector('role', 1), no, ->
+          browser.waitForElementVisible selector('role', 1), 20000
+          browser.click selector('role', 2), ->
+            teamsHelpers.checkTeammates browser, invitations[1], nthItem(1), nthItem(2), selector('role', 2), no, ->
+              browser.expect.element(selector('role', index + 1)).text.to.contain 'Owner'
+              browser.click selector('role', lastPendingInvitationIndex + 1), ->
+                teamsHelpers.checkTeammates browser, invitations[lastPendingInvitationIndex], nthItem(1), nthItem(2), selector('role', lastPendingInvitationIndex + 1), yes, ->
+                  browser
+                    .waitForElementNotPresent selector('fullname', lastPendingInvitationIndex + 1), 20000
+                    .end()
+
+
+  sendInvites: (browser) ->
+
+    user = teamsHelpers.loginTeam browser
+    browser.url myTeamLink
+    teamsHelpers.inviteUser browser, 'member'
+    browser.pause 5000
+    teamsHelpers.inviteUser browser, 'admin'
+    browser.end()
+
+
+  sendInvitesToAll: (browser) ->
+
+    user = teamsHelpers.loginTeam browser
+    browser.url myTeamLink
+    teamsHelpers.inviteAll browser
+    browser.end()
+
+
+  checkUploadCSV: (browser) ->
+
+    user = teamsHelpers.loginTeam browser
+    browser.url myTeamLink
+    teamsHelpers.uploadCSV browser
+    browser.end()
+
+
+  resendInvitation: (browser) ->
+
+    user = teamsHelpers.loginTeam browser
+    browser.url myTeamLink
+    teamsHelpers.resendInvitation browser, 'member'
+    browser.pause 5000
+    teamsHelpers.resendInvitation browser, 'admin'
+    browser.end()
+
+
+  newInviteFromResendModal: (browser) ->
+
+    user = teamsHelpers.loginTeam browser
+    browser.url myTeamLink
+    teamsHelpers.newInviteFromResendModal browser, 'member'
+    browser.pause 5000
+    teamsHelpers.newInviteFromResendModal browser, 'admin'
+    browser.end()
+
+
+selector = (type, index) ->
+  sectionSelector = '.HomeAppView--section.teammates'
+  memberSelector = '.kdview.kdlistitemview.kdlistitemview-member'
+  "#{sectionSelector} .ListView-row:nth-of-type(#{index}) #{memberSelector} .#{type}"
+
+
+nthItem = (index) ->
+  ".menu-class ul.ButtonWithMenuItemsList li:nth-of-type(#{index})"


add manifold checks in some tests

add manifold checks in some tests

--- client/test/lib_board-shape_test.coffee
+++ client/test/lib_board-shape_test.coffee
@@ -156,17 +156,19 @@
       points = gatherPoints [
         'M', 0, 0, 'L', 1, 0, 'M', 1, 1, 'L', 1, 0, 'M', 0, 0, 'L', 1, 1
       ]
-      result = traversePoints(points).path
-      expect(result).to.eql ['M', 0, 0, 'L', 1, 1, 'L', 1, 0, 'Z']
+      result = traversePoints points
+      expect(result.path).to.eql ['M', 0, 0, 'L', 1, 1, 'L', 1, 0, 'Z']
+      expect(result.manifold).to.be.true

     it 'should work with arcs', ->
       points = gatherPoints [
         'M', 0, 0, 'A', 1, 1, 0, 0, 1, 1, 1, 'M', 0, 0, 'A', 1, 1, 0, 0, 0, 1, 1
       ]
-      result = traversePoints(points).path
-      expect(result).to.eql [
+      result = traversePoints points
+      expect(result.path).to.eql [
         'M', 0, 0, 'A', 1, 1, 0, 0, 0, 1, 1, 'A', 1, 1, 0, 0, 0, 0, 0
       ]
+      expect(result.manifold).to.be.true

     describe 'manifold detection', ->


adding check for only one tag per entry

adding check for only one tag per entry

--- app/coffee/tag.coffee
+++ app/coffee/tag.coffee
@@ -101,13 +101,21 @@
       @add(tagView)

   findByTagId: (tagId)->
-    for tagView in @tagViews
-      log tagView.tag.id
     return tagView for tagView in @tagViews when tagView.tag.id == tagId

   add: (tagView)->
-    @tagViews.push(tagView)
-    tagView.fadeIn()
+    if !@tagAlreadyExists(tagView)
+      @tagViews.push(tagView)
+      log @tagViews
+      tagView.fadeIn()
+
+  tagAlreadyExists: (tagView) ->
+    tagString = tagView.tag.name
+    for tagViewz in @tagViews
+      if tagViewz.tag.name is tagString
+        tagView.removeFromView()
+        return true
+    return false

   removeTag: (tagId) ->
     #log tagId


Add aliases

Add aliases

--- src/commands.coffee
+++ src/commands.coffee
@@ -53,6 +53,12 @@
         promptly.choose "open which? ", [1..pullRequests.length], (err, value) =>
           open pullRequests[value - 1].html_url

+  # Aliases
+  l: @::list
+  c: @::count
+  p: @::ping
+  o: @::open
+
   _list: (fn) =>
     async.map @config.repos, (repo, callback) =>
       [u, r] = repo.split('/')


Allow both Jade and HTML

Allow both Jade and HTML

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -370,7 +370,7 @@
     manifest = grunt.file.readJSON('component.json')
     manifest.scripts = grunt.file.expand('app/**/*.js')
     manifest.styles = grunt.file.expand('app/**/*.styl')
-    manifest.templates = grunt.file.expand('app/**/*.jade')
+    manifest.templates = grunt.file.expand('app/**/*.(jade|html)')
     manifest.images = grunt.file.expand('app/assets/images/**/*.*')
     manifest.fonts = grunt.file.expand('app/assets/fonts/**/*.*')


Drop Bower support

Drop Bower support

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -180,8 +180,6 @@
     concat:
       js:
         src: [
-          # Include external libraries first
-          'bower_components/**/*.js'
           # Bootloader needs to be built first
           'components/pixbi-bootloader/public/script.js'
           'components/**/public/script.js'


further bug fixing

further bug fixing

--- client/client.coffee
+++ client/client.coffee
@@ -77,7 +77,7 @@
     else @render()
   onBeforeAction: ->
     if getUser()
-      if amIValid() is no then @redirect 'verifyEmail' else Router.redirect 'notes'
+      if amIValid() is no then @redirect 'verifyEmail' else @redirect 'notes'
     @next()

 # Page Routing
@@ -91,7 +91,7 @@
   onBeforeAction: ->
     # Dispatch user to the right landing page based on his account status
     if getUser()
-      if amIValid() is yes then @redirect 'notes' else Router.redirect 'verifyEmail'
+      if amIValid() is yes then @redirect 'notes' else @redirect 'verifyEmail'
     @next()
 Router.route '/login', controller: guestController
 Router.route '/register', controller: guestController
@@ -107,7 +107,7 @@
   action: ->
     if Meteor.status().connected is no
       @render 'reconnect'
-    else @render()
+    else @render(); @render 'nothing', to: 'outside'
   onBeforeAction: ->
     if getUser()
       if amIValid()
@@ -123,6 +123,7 @@
             showErr type:'success', msg:'Verification complete'
             Router.go 'home'
           @next()
+      @next()
     else
       @redirect 'home'
       @next()


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -1,5 +1,5 @@
 ###*
-The Movable module automatically updates the position and velocity of
+The Cool module automatically updates the position and velocity of
 GameObjects based on the velocity and acceleration. It does not check
 collisions so is probably best suited to particle effect like things.


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -23,7 +23,7 @@

 player.I.update()

-player
+player.I.shoo
 </pre></code>

 @name Cooldown


added a few colloquial squiggle names

added a few colloquial squiggle names

--- client/keymap.coffee
+++ client/keymap.coffee
@@ -84,7 +84,7 @@
     × cross times multiply sgn signum direction
     ÷ divide reciprocal obelus
     * star asterisk power exponential
-    ⍟ logarithm naturallogarithm circlestar starcircle
+    ⍟ logarithm naturallogarithm circlestar starcircle splat
     ⌹ domino matrixdivide quaddivide
     ○ pi circular trigonometric
     ! exclamation bang shriek factorial binomial combinations
@@ -105,7 +105,7 @@
     ≡ match equalunderbar identical
     ≢ notmatch equalunderbarslash notidentical tally
     ∧ and conjunction lcm logicaland lowestcommonmultiple caret
-    ∨ or disjunction gcd vel logicalor greatestcommondivisor
+    ∨ or disjunction gcd vel logicalor greatestcommondivisor hcf highestcommonfactor
     ⍲ nand andtilde logicalnand carettilde
     ⍱ nor ortilde logicalnor
     ↑ uparrow mix take


more debugging for #703

more debugging for #703

--- resource/lib/collections.coffee
+++ resource/lib/collections.coffee
@@ -17,6 +17,7 @@

   collections = {}
   for collection in raw
+    debug('found collection', collection.fields.name, 'with parent', collection.fields.parentKey)
     collections[collection.primary.key] = {
       key: collection.primary.key
       name: collection.fields.name
@@ -27,6 +28,9 @@
     }

   for key, collection of collections
+    if collection.parent && !collections[collection.parent]
+      debug(collection.name, '/', key, 'has non-existing parent', {parent: collection.parent})
+
     collections[collection.parent].collections.push(collection) if collection.parent
     delete collection.parent
   debug('got collections:', collections)


Add support for SILENT env variable

Add support for SILENT env variable

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -28,7 +28,7 @@
   next()

 app.use(express.favicon())
-app.use(express.logger(':date - :method :url - :res[content-type]')) if app.get('env') isnt 'test'
+app.use(express.logger(':date - :method :url - :res[content-type]')) if not process.env.SILENT
 app.set('json spaces', 0) if app.get('env') is 'production'
 app.use(express.compress())
 app.use(express.methodOverride())


Suppression de la comparaison aux frames précédentes

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -1,25 +1,19 @@
 server = document.querySelector("embed")
-lastState = null
 server.addEventListener "message", ((e) ->
 	game = e.data
-	if game isnt null and lastState isnt null
-		if game.x isnt lastState.x or game.y isnt lastState.y
-			ball.move game.x, game.y
-		if game.pos isnt lastState.pos or game.size isnt lastState.size
-			paddle.move game.pos, game.size
-		if game.brick
+	if game isnt null
+		ball.move game.x, game.y
+		paddle.move game.pos, game.size
+		if game.brick isnt null
 			i = 0
 			while i < 8
-					j = 0
-					while j < 10
-						bricks[i][j].type game.brick[(i * 8) + j]
-						j++
+				j = 0
+				while j < 10
+					bricks[i][j].type game.brick[(i * 8) + j]
+					j++
 				i++
-		if(game.win)
+		if game.win
 			alert 'You win !'
-	lastState = game
 	serverStats.update()
-	if typeof game is 'number'
-		console.log game
 	return
 ), false


Make Editor.getPath return null if edit session is null

--- src/app/editor.coffee
+++ src/app/editor.coffee
@@ -321,7 +321,7 @@
   checkoutHead: -> @getBuffer().checkoutHead()
   setText: (text) -> @getBuffer().setText(text)
   getText: -> @getBuffer().getText()
-  getPath: -> @getBuffer().getPath()
+  getPath: -> @activeEditSession?.getPath()
   getLineCount: -> @getBuffer().getLineCount()
   getLastBufferRow: -> @getBuffer().getLastRow()
   getTextInRange: (range) -> @getBuffer().getTextInRange(range)


AppStorage: fetchStorage: simplify queue flow

--- client/app/lib/appstorage.coffee
+++ client/app/lib/appstorage.coffee
@@ -29,7 +29,9 @@

       { mainController } = kd.singletons

-      return  if (queue.push callback) > 1
+      queue.push callback
+
+      return  if queue.length > 1

       mainController.ready =>


Increase resource waiting timeout to ensure enough time

--- src/document.coffee
+++ src/document.coffee
@@ -15,7 +15,7 @@
       orientation: 'portrait'
       border:      '0cm'

-  timeout: 500
+  timeout: 1500

   constructor: ( @server ) ->
     phantom.create ( @phantomInstance ) =>


fixing the transform

fixing the transform

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -36,30 +36,30 @@
       I.height = sprite.height
     )

-  self.bind 'draw', (canvas) ->
-    #TODO: Not sure if setting the transform here is the best
-    # but we want to make sure it's adjusted after *all* the updates
-    center = self.center()
+  self.extend
+    getTransform: ->
+      center = self.center()

-    if I.rotation
-      I.transform = Matrix.translation(center.x.round(), center.y.round())
-      I.transform = I.transform.concat(Matrix.rotation(I.rotation))
-      I.transform = I.transform.concat(Matrix.HORIZONTAL_FLIP) if I.hflip
-      I.transform = I.transform.concat(Matrix.VERTICAL_FLIP) if I.vflip
-      I.transform = I.transform.concat(Matrix.translation(-I.width/2, -I.height/2))
-    else
-      # Assumes I.x and I.y are top-left
-      I.transform = Matrix.translation(I.x.round(), I.y.round())
-
-      if I.hflip || I.vflip
+      if I.rotation
         I.transform = Matrix.translation(center.x.round(), center.y.round())
+        I.transform = I.transform.concat(Matrix.rotation(I.rotation))
         I.transform = I.transform.concat(Matrix.HORIZONTAL_FLIP) if I.hflip
         I.transform = I.transform.concat(Matrix.VERTICAL_FLIP) if I.vflip
         I.transform = I.transform.concat(Matrix.translation(-I.width/2, -I.height/2))
+      else
+        # Assumes I.x and I.y are top-left
+        I.transform = Matrix.translation(I.x.round(), I.y.round())

-      if I.spriteOffset
-        I.transform = I.transform.concat(Matrix.translation(I.spriteOffset.x, I.spriteOffset.y))
+        if I.hflip || I.vflip
+          I.transform = Matrix.translation(center.x.round(), center.y.round())
+          I.transform = I.transform.concat(Matrix.HORIZONTAL_FLIP) if I.hflip
+          I.transform = I.transform.concat(Matrix.VERTICAL_FLIP) if I.vflip
+          I.transform = I.transform.concat(Matrix.translation(-I.width/2, -I.height/2))

+        if I.spriteOffset
+          I.transform = I.transform.concat(Matrix.translation(I.spriteOffset.x, I.spriteOffset.y))
+
+  self.bind 'draw', (canvas) ->
     if I.sprite
       if I.sprite.draw?
         I.sprite.draw(canvas, 0, 0)


Add printing argument string and reading in passwords for the bots

--- bots/libs/HexBot.coffee
+++ bots/libs/HexBot.coffee
@@ -2,6 +2,7 @@

 require = patchRequire(global.require)
 utils = require('utils')
+sys = require('system')

 exports.createCasper = (casperOptions = {}) ->
   require('casper').create(
@@ -14,10 +15,10 @@
     )
   )

-
 #argumentData = [{name:'argName1',default:'optionalArg'}, {name:'argName2',csv:true}, ...]
-exports.parseArgs = (casper, argumentData) ->
-  ARGS = []; missingRequired = []; posArg = -1; argCount = 0
+exports.parseArgs = (casper, argumentData, argumentString) ->
+  ARGS = []; posArg = -1; argCount = 0
+  missingRequired = []; missingIndex = -1

   for arg in argumentData # get the arguments from the command line
     if casper.cli.raw.has arg.name
@@ -28,12 +29,18 @@
       ARGS[argCount] = arg.default
     else # if no default value is given then it is a required argument
       missingRequired.push arg.name
+      missingIndex = argCount

     if arg.csv and ARGS[argCount]?
       ARGS[argCount] = ARGS[argCount].split(',')
     argCount++

-  if missingRequired.length > 0 # if any required arguments were missing
+  if missingRequired.length is 1 and missingRequired[0] is 'password'
+    sys.stdout.write 'Password (will get printed): '
+    ARGS[missingIndex] = sys.stdin.readLine()
+  else if missingRequired.length > 0 # if any required arguments were missing
+    if argumentString?
+      casper.echo "args  : #{argumentString}"
     casper.echo "argErr: #{missingRequired} are required parameters."
     casper.exit 1
   ARGS # return


Make preloading async.

Make preloading async.

--- browser/frontend.coffee
+++ browser/frontend.coffee
@@ -10,10 +10,19 @@
   dataType: 'text'
   beforeSend: (jqXHR) -> jqXHR.overrideMimeType('text/plain; charset=x-user-defined')
   success: (data) ->
-    untar util.bytestr_to_array(data), (path, file) ->
+    file_count = 0
+    done = false
+    untar util.bytestr_to_array(data), ((path, file) ->
+      file_count++
       cls = /third_party\/classes\/([^.]*).class/.exec(path)[1]
-      class_cache[cls] = new ClassFile file
-      console.log "Loading #{cls}"
+      setTimeout (->
+        class_cache[cls] = new ClassFile file
+        controller.message "Loaded #{cls}\n", 'success', true
+        controller.reprompt() if --file_count == 0 and done
+      ), 0),
+      ->
+        done = true
+        controller.reprompt() if file_count == 0
   error: (jqXHR, textStatus, errorThrown) ->
     console.error errorThrown
 }


Remove superfluous logging.

Remove superfluous logging.

--- routes/cfapi.coffee
+++ routes/cfapi.coffee
@@ -96,12 +96,10 @@
         resolve
           status : response.statusCode
       else if(!error)
-        console.log("statusCode="+response.statusCode)
         reject
           status : 500
           body: response
       else
-        console.log("statusCode="+response.statusCode + " error="+error);
         reject
           status : 500
           body : error


return some stuff

return some stuff

--- routes/index.coffee
+++ routes/index.coffee
@@ -16,14 +16,15 @@
     end tell
   """
   applescript.execString script, (err, rtn) ->
-    # Something went wrong!
+    retHash = {success: false}
     if err
       console.log err
     else
+      retHash["success"] = true
       if (Array.isArray(rtn))
-        rtn.forEach (retVal) ->
-          console.log(retVal)
-    return
+        retHash["slideIndex"] = rtn[0]
+        retHash["slideTotal"] = rtn[1]
+    return retHash

@@ -42,8 +43,8 @@
 router.post "/go/:direction", (req, res) ->
   direction = req.params.direction
   if direction in ['next', 'previous', 'first', 'last']
-    goToSlide(direction)
-    res.json({success: true, pageNumber: direction})
+    pptResult = goToSlide(direction)
+    res.json(pptResult)
     return
   else
     console.error("Invalid parameter: #{direction}")


watcher: added detailed error reporting to Stylus compile step

--- builders/Watcher.coffee
+++ builders/Watcher.coffee
@@ -217,9 +217,11 @@
             writeCacheFile file.path,css
             callback file
           else
-            log.info "error with styl file at #{file.path}"
+            lines = err.message.split('\n') or ['0','No details supplied.']
+            log.error "\nStylus Compile Error in #{file.path} on line #{lines[0].match(/\d*$/)[0]}"
+            log.info line for line in lines[1...]
+
       when "coffee"
-
         try
           file.contentsCs = newContent
           # if file.section is "Client"
@@ -229,8 +231,6 @@
           file.contents = cs.compile newContent,bare:yes
           writeCacheFile file.path,file.contents

-
-
           #file.contents = @uglify js:file.contents,mangle:no,noMangleFunctions:yes,squeeze:no #,beautify:beautify
         catch error
           errd = yes


Correct hours formatting in upcoming shows

--- src/js/menus.coffee
+++ src/js/menus.coffee
@@ -301,7 +301,7 @@
         items:
           {
             title: item.show.title
-            subtitle: "S#{item.episode.season}E#{item.episode.number} | #{moment(item.airs_at).format('HH:MM')}"
+            subtitle: "S#{item.episode.season}E#{item.episode.number} | #{moment(item.airs_at).format('HH:mm')}"
             data:
               showID: item.show.ids.trakt
               seasonNumber: item.episode.season


Edu: added video to s3

Edu: added video to s3

--- client/Education/AppView.coffee
+++ client/Education/AppView.coffee
@@ -49,7 +49,7 @@

             https://koding.com/Terminal
             <video autoplay loop
-              src='/a/movies/terminal.final.webmsd.webm'
+              src='https://koding-cdn.s3.amazonaws.com/movies/terminal.final.webmsd.webm'
               preload='auto'>
             </video>


remove debug

remove debug

--- client/app/lib/colorhash.coffee
+++ client/app/lib/colorhash.coffee
@@ -25,16 +25,8 @@

         h = (hash% 100)/100
         s = (hash% 1000)/1000
-        console.log h
         l = 0.5 + 0.2*(hash% 2)/ 2
         colour = hslToRgb h, s, l
         return colour
     else
         return '#008AF6'
-
-module.exports.test = ->
-    random = require 'lib/random'
-    for i in [0..200]
-        $('<span>').text('HELLO')
-        .css('background-color', module.exports random.randomString())
-        .appendTo $('body')


Do not re-highlight on submit not changed

--- client/components/Submit.coffee
+++ client/components/Submit.coffee
@@ -36,8 +36,9 @@
     componentDidMount: ->
         @doHighlight()

-    componentDidUpdate: ->
-        @doHighlight()
+    componentDidUpdate:  (prevProps, prevState) ->
+        if @props.submit._id != prevProps.submit._id
+            @doHighlight()

     doHighlight: ->
         for el in document.getElementsByClassName("sourcecode")


Remove obsolete comments

Remove obsolete comments

--- client/src/scripts/steam.coffee
+++ client/src/scripts/steam.coffee
@@ -1,60 +1,3 @@
-Steam.Types = (_) ->
-
-  TypeAddress =
-    anthing: t_any
-    boolean: t_bool
-    date: t_date
-    element: t_el
-    function: t_function
-    number: t_num
-    integer: t_int
-    regexp: t_regexp
-    string: t_str
-    error: t_error
-    undef: t_undef
-    optional_string: t_opt t_str
-    array_int: [ t_int ]
-    string_or_int: [ t_str, t_int ]
-    tuple_3: t_tuple t_str, t_int, t_num
-    custom_div_by_10: t_int (x) -> x % 10 is 0
-    enumeration: t_str [10, 20, 30]
-    opt_args: t_str archetype: 'email', maxlength: 100
-    # each t_* for native types should accept variadic forms:
-    # t_str = (array) ->
-    # t_str = (function...) ->
-    # t_str = (array, function...) ->
-    # t_str = (obj) ->
-    # t_str = (array, obj) ->
-    # t_str = (array, obj, function...) ->
-    #
-    # -> array implies enum
-    # -> object implies opts, including json-schema properties
-    # -> function implies a validation function
-    # Every schema/type definition should start with one and only
-    # one property that indicates the name of the type.
-    # e.g. validate a scalar:
-    #     rtt.check 42, answer: t_int
-    # e.g. validate an array:
-    #     rtt.check countries, countries: [ t_string ]
-    # e.g. validate multiple things:
-    #     rtt.check arguments, answer: t_int, countries: [t_string]
-    # e.g. validate an address:
-    #
-    #     rtt.check mailingAddress,
-    #       Address:
-    #         line1: t_str
-    #         line2: t_str
-    #         city: t_str
-    #         state: t_str
-    #         zip: t_int
-    #
-    # things you can do with it:
-    # Using faker.js
-    #     fakeAddress = rtt.fake t_address
-    # Generate json-schema
-    #     addressSchema = rtt.toJsonSchema t_address
-    #
-
 # TODO
 # link$ _.onRouteFailed, (address) -> console.error "Not found: #{address}"
 $ ->


Use relative paths when saving the ToC

Use relative paths when saving the ToC

--- scripts/gh-book/opf-file.coffee
+++ scripts/gh-book/opf-file.coffee
@@ -148,8 +148,9 @@

         switch model.mediaType
           when XhtmlFile::mediaType
+            path = Utils.relativePath(@navModel.id, model.id)
             $node = $('')
-            .attr('href', model.id)
+            .attr('href', path)
           else
             $node = $('')
             $li.attr(model.htmlAttributes or {})


add image to 'so good'

add image to 'so good'

--- scripts/seal-of-approval.coffee
+++ scripts/seal-of-approval.coffee
@@ -47,7 +47,8 @@
  "http://i.imgur.com/bhKp2.gif",
  "http://i.imgur.com/3w1ej.gif",
  "http://i.imgur.com/JSBTl.gif",
- "http://i.imgur.com/NnBHS.gif"
+ "http://i.imgur.com/NnBHS.gif",
+ "http://i.imgur.com/59KTQ.gif"
 ]

 module.exports = (robot) ->


Enable push bay default and improve settings organization

--- packages/rocketchat-lib/server/startup/settings.coffee
+++ packages/rocketchat-lib/server/startup/settings.coffee
@@ -100,18 +100,18 @@

 RocketChat.settings.addGroup 'Push'
 RocketChat.settings.add 'Push_debug', false, { type: 'boolean', group: 'Push', public: true }
-RocketChat.settings.add 'Push_enable', false, { type: 'boolean', group: 'Push', public: true }
+RocketChat.settings.add 'Push_enable', true, { type: 'boolean', group: 'Push', public: true }
 RocketChat.settings.add 'Push_enable_gateway', true, { type: 'boolean', group: 'Push' }
 RocketChat.settings.add 'Push_gateway', 'https://rocket.chat', { type: 'string', group: 'Push' }
-RocketChat.settings.add 'Push_production', false, { type: 'boolean', group: 'Push', public: true }
-RocketChat.settings.add 'Push_apn_passphrase', '', { type: 'string', group: 'Push' }
-RocketChat.settings.add 'Push_apn_key', '', { type: 'string', multiline: true, group: 'Push' }
-RocketChat.settings.add 'Push_apn_cert', '', { type: 'string', multiline: true, group: 'Push' }
-RocketChat.settings.add 'Push_apn_dev_passphrase', '', { type: 'string', group: 'Push' }
-RocketChat.settings.add 'Push_apn_dev_key', '', { type: 'string', multiline: true, group: 'Push' }
-RocketChat.settings.add 'Push_apn_dev_cert', '', { type: 'string', multiline: true, group: 'Push' }
-RocketChat.settings.add 'Push_gcm_api_key', '', { type: 'string', group: 'Push' }
-RocketChat.settings.add 'Push_gcm_project_number', '', { type: 'string', group: 'Push', public: true }
+RocketChat.settings.add 'Push_production', true, { type: 'boolean', group: 'Push', public: true }
+RocketChat.settings.add 'Push_apn_passphrase', '', { type: 'string', group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_apn_key', '', { type: 'string', multiline: true, group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_apn_cert', '', { type: 'string', multiline: true, group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_apn_dev_passphrase', '', { type: 'string', group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_apn_dev_key', '', { type: 'string', multiline: true, group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_apn_dev_cert', '', { type: 'string', multiline: true, group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_gcm_api_key', '', { type: 'string', group: 'Push', section: 'Certificates_and_Keys' }
+RocketChat.settings.add 'Push_gcm_project_number', '', { type: 'string', group: 'Push', public: true, section: 'Certificates_and_Keys' }

 RocketChat.settings.addGroup 'Layout'
 RocketChat.settings.add 'Layout_Home_Title', 'Home', { type: 'string', group: 'Layout', public: true, section: 'Content' }


Change date format.

Change date format.

--- server/lib/crawler/index.coffee
+++ server/lib/crawler/index.coffee
@@ -20,7 +20,7 @@
   months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
   monthName = months[month]
-  return "#{day} #{monthName} #{year} #{hour}:#{minute}"
+  return "#{monthName} #{day}, #{year} #{hour}:#{minute}"

 module.exports =
   crawl: (bongo, req, res, slug)->


fix closing tag

fix closing tag

--- snippets/openwebicons.cson
+++ snippets/openwebicons.cson
@@ -61,67 +61,67 @@
 		body: "<${1:i} class=\"openwebicons-cc-zero$2\"></${1:i}>"
 	"openwebicons-cc":
 		prefix: "openwebicons-cc"
-		body: "<${1:i} class=\"openwebicons-cc$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-cc$2\"></${1:i}>"
 	"openwebicons-cccs":
 		prefix: "openwebicons-cccs"
-		body: "<${1:i} class=\"openwebicons-cccs$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-cccs$2\"></${1:i}>"
 	"openwebicons-connectivity":
 		prefix: "openwebicons-connectivity"
-		body: "<${1:i} class=\"openwebicons-connectivity$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-connectivity$2\"></${1:i}>"
 	"openwebicons-copyleft":
 		prefix: "openwebicons-copyleft"
-		body: "<${1:i} class=\"openwebicons-copyleft$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-copyleft$2\"></${1:i}>"
 	"openwebicons-css3":
 		prefix: "openwebicons-css3"
-		body: "<${1:i} class=\"openwebicons-css3$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-css3$2\"></${1:i}>"
 	"openwebicons-dataportability":
 		prefix: "openwebicons-dataportability"
-		body: "<${1:i} class=\"openwebicons-dataportability$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-dataportability$2\"></${1:i}>"
 	"openwebicons-device-access":
 		prefix: "openwebicons-device-access"
-		body: "<${1:i} class=\"openwebicons-device-access$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-device-access$2\"></${1:i}>"
 	"openwebicons-diaspora":
 		prefix: "openwebicons-diaspora"
-		body: "<${1:i} class=\"openwebicons-diaspora$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-diaspora$2\"></${1:i}>"
 	"openwebicons-epub-colored":
 		prefix: "openwebicons-epub-colored"
-		body: "<${1:i} class=\"openwebicons-epub-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-epub-colored$2\"></${1:i}>"
 	"openwebicons-epub":
 		prefix: "openwebicons-epub"
-		body: "<${1:i} class=\"openwebicons-epub$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-epub$2\"></${1:i}>"
 	"openwebicons-federated":
 		prefix: "openwebicons-federated"
-		body: "<${1:i} class=\"openwebicons-federated$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-federated$2\"></${1:i}>"
 	"openwebicons-feed-colored":
 		prefix: "openwebicons-feed-colored"
-		body: "<${1:i} class=\"openwebicons-feed-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-feed-colored$2\"></${1:i}>"
 	"openwebicons-feed-simple":
 		prefix: "openwebicons-feed-simple"
-		body: "<${1:i} class=\"openwebicons-feed-simple$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-feed-simple$2\"></${1:i}>"
 	"openwebicons-feed":
 		prefix: "openwebicons-feed"
-		body: "<${1:i} class=\"openwebicons-feed$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-feed$2\"></${1:i}>"
 	"openwebicons-foaf":
 		prefix: "openwebicons-foaf"
-		body: "<${1:i} class=\"openwebicons-foaf$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-foaf$2\"></${1:i}>"
 	"openwebicons-geo-colored":
 		prefix: "openwebicons-geo-colored"
-		body: "<${1:i} class=\"openwebicons-geo-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-geo-colored$2\"></${1:i}>"
 	"openwebicons-geo":
 		prefix: "openwebicons-geo"
-		body: "<${1:i} class=\"openwebicons-geo$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-geo$2\"></${1:i}>"
 	"openwebicons-ghost":
 		prefix: "openwebicons-ghost"
-		body: "<${1:i} class=\"openwebicons-ghost$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ghost$2\"></${1:i}>"
 	"openwebicons-git-colored":
 		prefix: "openwebicons-git-colored"
-		body: "<${1:i} class=\"openwebicons-git-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-git-colored$2\"></${1:i}>"
 	"openwebicons-git":
 		prefix: "openwebicons-git"
-		body: "<${1:i} class=\"openwebicons-git$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-git$2\"></${1:i}>"
 	"openwebicons-gnu":
 		prefix: "openwebicons-gnu"
-		body: "<${1:i} class=\"openwebicons-gnu$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-gnu$2\"></${1:i}>"
 	"openwebicons-gnusocial":
 		prefix: "openwebicons-gnusocial"
 		body: "<${1:i} class=\"openwebicons-gnusocial$2\"></${1:i}>"
@@ -130,250 +130,250 @@
 		body: "<${1:i} class=\"openwebicons-gnusocial-simple$2\"></${1:i}>"
 	"openwebicons-hatom":
 		prefix: "openwebicons-hatom"
-		body: "<${1:i} class=\"openwebicons-hatom$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hatom$2\"></${1:i}>"
 	"openwebicons-hcard-add":
 		prefix: "openwebicons-hcard-add"
-		body: "<${1:i} class=\"openwebicons-hcard-add$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hcard-add$2\"></${1:i}>"
 	"openwebicons-hcard-download":
 		prefix: "openwebicons-hcard-download"
-		body: "<${1:i} class=\"openwebicons-hcard-download$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hcard-download$2\"></${1:i}>"
 	"openwebicons-hresume":
 		prefix: "openwebicons-hresume"
-		body: "<${1:i} class=\"openwebicons-hresume$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hresume$2\"></${1:i}>"
 	"openwebicons-html5-colored":
 		prefix: "openwebicons-html5-colored"
-		body: "<${1:i} class=\"openwebicons-html5-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-html5-colored$2\"></${1:i}>"
 	"openwebicons-html5":
 		prefix: "openwebicons-html5"
-		body: "<${1:i} class=\"openwebicons-html5$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-html5$2\"></${1:i}>"
 	"openwebicons-hubbub-colored":
 		prefix: "openwebicons-hubbub-colored"
-		body: "<${1:i} class=\"openwebicons-hubbub-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hubbub-colored$2\"></${1:i}>"
 	"openwebicons-hubbub":
 		prefix: "openwebicons-hubbub"
-		body: "<${1:i} class=\"openwebicons-hubbub$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-hubbub$2\"></${1:i}>"
 	"openwebicons-indie":
 		prefix: "openwebicons-indie"
-		body: "<${1:i} class=\"openwebicons-indie$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-indie$2\"></${1:i}>"
 	"openwebicons-indiehosters":
 		prefix: "openwebicons-indiehosters"
-		body: "<${1:i} class=\"openwebicons-indiehosters$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-indiehosters$2\"></${1:i}>"
 	"openwebicons-indieweb":
 		prefix: "openwebicons-indieweb"
-		body: "<${1:i} class=\"openwebicons-indieweb$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-indieweb$2\"></${1:i}>"
 	"openwebicons-indiewebcamp":
 		prefix: "openwebicons-indiewebcamp"
-		body: "<${1:i} class=\"openwebicons-indiewebcamp$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-indiewebcamp$2\"></${1:i}>"
 	"openwebicons-info-card-colored":
 		prefix: "openwebicons-info-card-colored"
-		body: "<${1:i} class=\"openwebicons-info-card-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-info-card-colored$2\"></${1:i}>"
 	"openwebicons-info-card":
 		prefix: "openwebicons-info-card"
-		body: "<${1:i} class=\"openwebicons-info-card$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-info-card$2\"></${1:i}>"
 	"openwebicons-javascript-colored":
 		prefix: "openwebicons-javascript-colored"
-		body: "<${1:i} class=\"openwebicons-javascript-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-javascript-colored$2\"></${1:i}>"
 	"openwebicons-javascript":
 		prefix: "openwebicons-javascript"
-		body: "<${1:i} class=\"openwebicons-javascript$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-javascript$2\"></${1:i}>"
 	"openwebicons-json-ld":
 		prefix: "openwebicons-json-ld"
-		body: "<${1:i} class=\"openwebicons-json-ld$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-json-ld$2\"></${1:i}>"
 	"openwebicons-known":
 		prefix: "openwebicons-known"
-		body: "<${1:i} class=\"openwebicons-known$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-known$2\"></${1:i}>"
 	"openwebicons-markdown":
 		prefix: "openwebicons-markdown"
-		body: "<${1:i} class=\"openwebicons-markdown$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-markdown$2\"></${1:i}>"
 	"openwebicons-microformats-colored":
 		prefix: "openwebicons-microformats-colored"
-		body: "<${1:i} class=\"openwebicons-microformats-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-microformats-colored$2\"></${1:i}>"
 	"openwebicons-microformats":
 		prefix: "openwebicons-microformats"
-		body: "<${1:i} class=\"openwebicons-microformats$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-microformats$2\"></${1:i}>"
 	"openwebicons-mozilla":
 		prefix: "openwebicons-mozilla"
-		body: "<${1:i} class=\"openwebicons-mozilla$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-mozilla$2\"></${1:i}>"
 	"openwebicons-multimedia":
 		prefix: "openwebicons-multimedia"
-		body: "<${1:i} class=\"openwebicons-multimedia$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-multimedia$2\"></${1:i}>"
 	"openwebicons-oauth":
 		prefix: "openwebicons-oauth"
-		body: "<${1:i} class=\"openwebicons-oauth$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-oauth$2\"></${1:i}>"
 	"openwebicons-odata-colored":
 		prefix: "openwebicons-odata-colored"
-		body: "<${1:i} class=\"openwebicons-odata-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-odata-colored$2\"></${1:i}>"
 	"openwebicons-odata":
 		prefix: "openwebicons-odata"
-		body: "<${1:i} class=\"openwebicons-odata$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-odata$2\"></${1:i}>"
 	"openwebicons-offline-storage":
 		prefix: "openwebicons-offline-storage"
-		body: "<${1:i} class=\"openwebicons-offline-storage$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-offline-storage$2\"></${1:i}>"
 	"openwebicons-ofl-attribution":
 		prefix: "openwebicons-ofl-attribution"
-		body: "<${1:i} class=\"openwebicons-ofl-attribution$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl-attribution$2\"></${1:i}>"
 	"openwebicons-ofl-embedding":
 		prefix: "openwebicons-ofl-embedding"
-		body: "<${1:i} class=\"openwebicons-ofl-embedding$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl-embedding$2\"></${1:i}>"
 	"openwebicons-ofl-renaming":
 		prefix: "openwebicons-ofl-renaming"
-		body: "<${1:i} class=\"openwebicons-ofl-renaming$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl-renaming$2\"></${1:i}>"
 	"openwebicons-ofl-selling":
 		prefix: "openwebicons-ofl-selling"
-		body: "<${1:i} class=\"openwebicons-ofl-selling$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl-selling$2\"></${1:i}>"
 	"openwebicons-ofl-share":
 		prefix: "openwebicons-ofl-share"
-		body: "<${1:i} class=\"openwebicons-ofl-share$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl-share$2\"></${1:i}>"
 	"openwebicons-ofl":
 		prefix: "openwebicons-ofl"
-		body: "<${1:i} class=\"openwebicons-ofl$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ofl$2\"></${1:i}>"
 	"openwebicons-open-share-colored":
 		prefix: "openwebicons-open-share-colored"
-		body: "<${1:i} class=\"openwebicons-open-share-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-open-share-colored$2\"></${1:i}>"
 	"openwebicons-open-share-simple":
 		prefix: "openwebicons-open-share-simple"
-		body: "<${1:i} class=\"openwebicons-open-share-simple$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-open-share-simple$2\"></${1:i}>"
 	"openwebicons-open-share":
 		prefix: "openwebicons-open-share"
-		body: "<${1:i} class=\"openwebicons-open-share$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-open-share$2\"></${1:i}>"
 	"openwebicons-open-web":
 		prefix: "openwebicons-open-web"
-		body: "<${1:i} class=\"openwebicons-open-web$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-open-web$2\"></${1:i}>"
 	"openwebicons-opendesign":
 		prefix: "openwebicons-opendesign"
 		body: "<${1:i} class=\"openwebicons-opendesign$2\"></${1:i}>"
 	"openwebicons-opengraph":
 		prefix: "openwebicons-opengraph"
-		body: "<${1:i} class=\"openwebicons-opengraph$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opengraph$2\"></${1:i}>"
 	"openwebicons-openid":
 		prefix: "openwebicons-openid"
-		body: "<${1:i} class=\"openwebicons-openid$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-openid$2\"></${1:i}>"
 	"openwebicons-opensearch-colored":
 		prefix: "openwebicons-opensearch-colored"
-		body: "<${1:i} class=\"openwebicons-opensearch-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opensearch-colored$2\"></${1:i}>"
 	"openwebicons-opensearch":
 		prefix: "openwebicons-opensearch"
-		body: "<${1:i} class=\"openwebicons-opensearch$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opensearch$2\"></${1:i}>"
 	"openwebicons-opensource-colored":
 		prefix: "openwebicons-opensource-colored"
-		body: "<${1:i} class=\"openwebicons-opensource-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opensource-colored$2\"></${1:i}>"
 	"openwebicons-opensource":
 		prefix: "openwebicons-opensource"
-		body: "<${1:i} class=\"openwebicons-opensource$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opensource$2\"></${1:i}>"
 	"openwebicons-opensourcedesign":
 		prefix: "openwebicons-opensourcedesign"
 		body: "<${1:i} class=\"openwebicons-opensourcedesign$2\"></${1:i}>"
 	"openwebicons-opml-colored":
 		prefix: "openwebicons-opml-colored"
-		body: "<${1:i} class=\"openwebicons-opml-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opml-colored$2\"></${1:i}>"
 	"openwebicons-opml":
 		prefix: "openwebicons-opml"
-		body: "<${1:i} class=\"openwebicons-opml$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-opml$2\"></${1:i}>"
 	"openwebicons-osi-colored":
 		prefix: "openwebicons-osi-colored"
-		body: "<${1:i} class=\"openwebicons-osi-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-osi-colored$2\"></${1:i}>"
 	"openwebicons-osi":
 		prefix: "openwebicons-osi"
-		body: "<${1:i} class=\"openwebicons-osi$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-osi$2\"></${1:i}>"
 	"openwebicons-ostatus-colored":
 		prefix: "openwebicons-ostatus-colored"
-		body: "<${1:i} class=\"openwebicons-ostatus-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ostatus-colored$2\"></${1:i}>"
 	"openwebicons-ostatus-simple":
 		prefix: "openwebicons-ostatus-simple"
-		body: "<${1:i} class=\"openwebicons-ostatus-simple$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ostatus-simple$2\"></${1:i}>"
 	"openwebicons-ostatus":
 		prefix: "openwebicons-ostatus"
-		body: "<${1:i} class=\"openwebicons-ostatus$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-ostatus$2\"></${1:i}>"
 	"openwebicons-owl":
 		prefix: "openwebicons-owl"
-		body: "<${1:i} class=\"openwebicons-owl$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-owl$2\"></${1:i}>"
 	"openwebicons-owncloud":
 		prefix: "openwebicons-owncloud"
-		body: "<${1:i} class=\"openwebicons-owncloud$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-owncloud$2\"></${1:i}>"
 	"openwebicons-perfintegration":
 		prefix: "openwebicons-perfintegration"
-		body: "<${1:i} class=\"openwebicons-perfintegration$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-perfintegration$2\"></${1:i}>"
 	"openwebicons-persona":
 		prefix: "openwebicons-persona"
-		body: "<${1:i} class=\"openwebicons-persona$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-persona$2\"></${1:i}>"
 	"openwebicons-pub-colored":
 		prefix: "openwebicons-pub-colored"
-		body: "<${1:i} class=\"openwebicons-pub-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-pub-colored$2\"></${1:i}>"
 	"openwebicons-pub":
 		prefix: "openwebicons-pub"
-		body: "<${1:i} class=\"openwebicons-pub$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-pub$2\"></${1:i}>"
 	"openwebicons-pubsubhubbub":
 		prefix: "openwebicons-pubsubhubbub"
-		body: "<${1:i} class=\"openwebicons-pubsubhubbub$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-pubsubhubbub$2\"></${1:i}>"
 	"openwebicons-qr":
 		prefix: "openwebicons-qr"
-		body: "<${1:i} class=\"openwebicons-qr$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-qr$2\"></${1:i}>"
 	"openwebicons-rdf":
 		prefix: "openwebicons-rdf"
-		body: "<${1:i} class=\"openwebicons-rdf$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-rdf$2\"></${1:i}>"
 	"openwebicons-rdfa":
 		prefix: "openwebicons-rdfa"
-		body: "<${1:i} class=\"openwebicons-rdfa$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-rdfa$2\"></${1:i}>"
 	"openwebicons-remote-storage-colored":
 		prefix: "openwebicons-remote-storage-colored"
-		body: "<${1:i} class=\"openwebicons-remote-storage-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-remote-storage-colored$2\"></${1:i}>"
 	"openwebicons-remote-storage":
 		prefix: "openwebicons-remote-storage"
-		body: "<${1:i} class=\"openwebicons-remote-storage$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-remote-storage$2\"></${1:i}>"
 	"openwebicons-semantic-web":
 		prefix: "openwebicons-semantic-web"
-		body: "<${1:i} class=\"openwebicons-semantic-web$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-semantic-web$2\"></${1:i}>"
 	"openwebicons-semantics":
 		prefix: "openwebicons-semantics"
-		body: "<${1:i} class=\"openwebicons-semantics$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-semantics$2\"></${1:i}>"
 	"openwebicons-share-colored":
 		prefix: "openwebicons-share-colored"
-		body: "<${1:i} class=\"openwebicons-share-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-share-colored$2\"></${1:i}>"
 	"openwebicons-share-simple":
 		prefix: "openwebicons-share-simple"
-		body: "<${1:i} class=\"openwebicons-share-simple$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-share-simple$2\"></${1:i}>"
 	"openwebicons-share":
 		prefix: "openwebicons-share"
-		body: "<${1:i} class=\"openwebicons-share$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-share$2\"></${1:i}>"
 	"openwebicons-sub-colored":
 		prefix: "openwebicons-sub-colored"
-		body: "<${1:i} class=\"openwebicons-sub-colored$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-sub-colored$2\"></${1:i}>"
 	"openwebicons-sub":
 		prefix: "openwebicons-sub"
-		body: "<${1:i} class=\"openwebicons-sub$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-sub$2\"></${1:i}>"
 	"openwebicons-svg":
 		prefix: "openwebicons-svg"
-		body: "<${1:i} class=\"openwebicons-svg$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-svg$2\"></${1:i}>"
 	"openwebicons-tent":
 		prefix: "openwebicons-tent"
-		body: "<${1:i} class=\"openwebicons-tent$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-tent$2\"></${1:i}>"
 	"openwebicons-tosdr":
 		prefix: "openwebicons-tosdr"
-		body: "<${1:i} class=\"openwebicons-tosdr$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-tosdr$2\"></${1:i}>"
 	"openwebicons-unhosted":
 		prefix: "openwebicons-unhosted"
-		body: "<${1:i} class=\"openwebicons-unhosted$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-unhosted$2\"></${1:i}>"
 	"openwebicons-web-intents":
 		prefix: "openwebicons-web-intents"
-		body: "<${1:i} class=\"openwebicons-web-intents$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-web-intents$2\"></${1:i}>"
 	"openwebicons-webfinger":
 		prefix: "openwebicons-webfinger"
-		body: "<${1:i} class=\"openwebicons-webfinger$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-webfinger$2\"></${1:i}>"
 	"openwebicons-webhooks":
 		prefix: "openwebicons-webhooks"
-		body: "<${1:i} class=\"openwebicons-webhooks$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-webhooks$2\"></${1:i}>"
 	"openwebicons-webmention":
 		prefix: "openwebicons-webmention"
-		body: "<${1:i} class=\"openwebicons-webmention$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-webmention$2\"></${1:i}>"
 	"openwebicons-webplattform":
 		prefix: "openwebicons-webplattform"
 		body: "<${1:i} class=\"openwebicons-webplattform$2\"></${1:i}>"
 	"openwebicons-wordpress":
 		prefix: "openwebicons-wordpress"
-		body: "<${1:i} class=\"openwebicons-wordpress$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-wordpress$2\"></${1:i}>"
 	"openwebicons-wtfpl":
 		prefix: "openwebicons-wtfpl"
-		body: "<${1:i} class=\"openwebicons-wtfpl$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-wtfpl$2\"></${1:i}>"
 	"openwebicons-xmpp":
 		prefix: "openwebicons-xmpp"
-		body: "<${1:i} class=\"openwebicons-xmpp$2\"><//${1:i}>"
+		body: "<${1:i} class=\"openwebicons-xmpp$2\"></${1:i}>"


Fix multiple, but break recursion.

Fix multiple, but break recursion.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -73,9 +73,11 @@
     self = this
     @estraverse().replace root,
       enter: (node, parent) ->
+        console.log "#{node.type}"
         fn = self["#{node.type}"]
         fn.apply(self, [ node, parent, (=> @skip()), (=> @break()) ]) if fn
       leave: (node, parent) ->
+        console.log "#{node.type}Exit"
         fn = self["#{node.type}Exit"]
         fn.apply(self, [ node, parent, (=> @skip()), (=> @break()) ]) if fn
     root
@@ -97,9 +99,9 @@
     @consolidateBodies node
     @removeEmptyStatementsFromBody node
   FunctionDeclaration: (node, parent) ->
-    @pushStack node.body
     @removeUndefinedParameter node
     @moveDeclarationToTopOfScope node, parent
+    { type: 'EmptyStatement' }
   FunctionDeclarationExit: (node) ->
     @popStack()
   FunctionExpression: (node) ->
@@ -135,15 +137,14 @@
     @addExplicitUndefinedInitializer node, parent, skip

   consolidateBodies: (node) ->
-    console.log 'consolidating prebody of', node.type
     if node._prebody
-      console.log "√"
       node.body = node._prebody.concat(node.body)
       delete node._prebody

-  moveDeclarationToTopOfScope: (node, paren) ->
-    @parent._prebody ?= []
-    @parent._prebody.unshift
+  moveDeclarationToTopOfScope: (node, parent) ->
+    @block._prebody ?= []
+    # @recurse node.body <- ??
+    @block._prebody.push
       type: 'ExpressionStatement'
       expression:
         type: 'AssignmentExpression'
@@ -153,7 +154,6 @@
           type: 'FunctionExpression'
           params: node.params
           body: node.body
-    { type: 'EmptyStatement' }

   removeEmptyStatementsFromBody: (node) ->
     node.body = node.body.filter (n) ->


fix inline result copying

fix inline result copying

--- keymaps/ink.cson
+++ keymaps/ink.cson
@@ -15,10 +15,16 @@
   'shift-space': 'unset!'

 '.platform-win32 .ink, .platform-linux .ink':
-  'ctrl-c': 'core:copy'
+  'ctrl-c': 'native!'

 '.platform-darwin .ink':
-  'cmd-c': 'core:copy'
+  'cmd-c': 'native!'
+
+'ink-console .platform-win32 .ink, .platform-linux .ink':
+  'ctrl-c': 'core:copy!'
+
+'ink-console .platform-darwin .ink':
+  'cmd-c': 'core:copy!'

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


Fixed spelling typo

Fixed spelling typo

--- project-3-solar-system/skeleton/app/scripts/app.coffee
+++ project-3-solar-system/skeleton/app/scripts/app.coffee
@@ -2,7 +2,7 @@

 class App
   constructor: ->
-    console.log "Welcoe"
+    console.log "Welcome"

 new App()


Implement real Torment message.

Implement real Torment message.

--- data/bw/moves.coffee
+++ data/bw/moves.coffee
@@ -1200,8 +1200,7 @@
 extendMove 'torment', ->
   @afterSuccessfulHit = (battle, user, target) ->
     if target.attach(Attachment.Torment)
-      # TODO: Real message
-      battle.message "#{target.name} fell under Torment!"
+      battle.message "#{target.name} was subjected to torment!"
     else
       @fail(battle)


Cats message missing cute face

Cats message missing cute face

--- scripts/cats.coffee
+++ scripts/cats.coffee
@@ -20,7 +20,7 @@
       return callback "Sorry, couldn't find #{greyhound} 😕"

     if greyhounds[greyhound].cats is catsafe
-      return callback "#{capitalize(greyhound)} is already #{toString(catsafe)}"
+      return callback "#{capitalize(greyhound)} is already #{toString(catsafe)} 😝"

     greyhounds[greyhound].cats = catsafe
     git.dumpGreyhounds greyhounds, callback


Hyping Hype

Hyping Hype

Took suggestions and implemented them.

--- scripts/hype.coffee
+++ scripts/hype.coffee
@@ -1,21 +1,25 @@
 module.exports = (robot) ->

-total = 0;
-lastTime = Date.now();
+#perryIsNoob = true
+timeDecay = 60000
+hypeIncrement = 15
+maxHype = 100
+total = 0
+lastTime = Date.now()
 gettingHyped = "༼ʘ̚ل͜ʘ̚༽ Hype level rising: "
 overHyped = "ヽヽ༼༼ຈຈل͜ل͜ຈຈ༽༽ﾉﾉ TOO MUCH HYPE TO HANDLE! ヽヽ༼༼ຈຈل͜ل͜ຈຈ༽༽ﾉﾉ"

 resetHype ->
-    total = total - ((Date.now() - lastTime)%60000)*15
+    total = total - ((Date.now() - lastTime)%timeDecay)*hypeIncrement
     lastTime = Date.now()
     if total < 0
         total = 0

-obot.hear /hype/i, (res) ->
+robot.hear /hype/i, (res) ->

     resetHype
-    if hype < 100
-        hype += 15
+    if hype < maxHype
+        hype += hypeIncrement
         res.send "#{gettingHyped} : Hype Level #{total}%"
     else
         res.send"#{overHyped}"


refactor: comment

refactor: comment

--- jquery.store.coffee
+++ jquery.store.coffee
@@ -30,12 +30,12 @@
           localStorage.setItem(test_key, 'valid')
           value = localStorage.getItem(test_key)
           if value and value == 'valid'
-            # localStorage is valid
+            # localStorage is available
             @storage_valid = true
         if @storage_valid
           @storage = localStorage
         else
-          # if localStorage is invalid, use just a hash
+          # if localStorage is NOT available, use just a hash
           @storage = {}
         if window.JSON and window.JSON.stringify
           @json_object = window.JSON


translate map script

translate map script

--- scripts/maps.coffee
+++ scripts/maps.coffee
@@ -2,11 +2,11 @@
 #   Interacts with the Google Maps API.
 #
 # Commands:
-#   hubot map me <query> - Returns a map view of the area returned by `query`.
+#   hubot карту мне <query> - Возвращает гуглокарту искомого региона.

 module.exports = (robot) ->

-  robot.respond /(?:(satellite|terrain|hybrid)[- ])?map me (.+)/i, (msg) ->
+  robot.respond /(?:(satellite|terrain|hybrid)[- ])?(?:map|карту)(?: me| мне) (.+)/i, (msg) ->
     mapType  = msg.match[1] or "roadmap"
     location = msg.match[2]
     mapUrl   = "http://maps.google.com/maps/api/staticmap?markers=" +


The best kind of votes are the ones that start.

--- scripts/vote.coffee
+++ scripts/vote.coffee
@@ -207,6 +207,7 @@
             Twitter.tweet arg, (err, tweet, url) ->
               return msg.reply "It can't be done, #{err}" if err
               msg.send "I hope you're all proud - #{url}"
+          vote.start()
         catch error
           msg.reply error


:green_heart:

--- spec/display-buffer-spec.coffee
+++ spec/display-buffer-spec.coffee
@@ -68,17 +68,17 @@
     describe "rendering of soft-wrapped lines", ->
       describe "when there are double width characters", ->
         it "takes them into account when finding the soft wrap column", ->
-          buffer.setText("私たちのフ是一个地方，数千名学生12345业余爱们的板作为우리포럼hello world this is a pretty long latin line")
-          displayBuffer.setDefaultCharWidth(1, 5)
+          buffer.setText("私たちのフ是一个地方，数千名学生12345业余爱们的板作为hello world this is a pretty long latin line")
+          displayBuffer.setDefaultCharWidth(1, 5, 0, 0)

           expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方")
           expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("，数千名学生12345业余爱")
-          expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为우리포럼hello ")
-          expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("world this is a pretty long latin line")
+          expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为hello world this is a ")
+          expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("pretty long latin line")

       describe "when there are half width characters", ->
         it "takes them into account when finding the soft wrap column", ->
-          displayBuffer.setDefaultCharWidth(1, 0, 5)
+          displayBuffer.setDefaultCharWidth(1, 0, 5, 0)
           buffer.setText("abcﾪﾫﾬﾈﾇﾈﾉﾊﾋﾌﾋﾌﾇﾴ￮￮￮hello world this is a pretty long line")

           expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("abcﾪﾫﾬﾈﾇﾈﾉﾊﾋ")


Fix spec when no buffer is open

Fix spec when no buffer is open

--- spec/find-view-spec.coffee
+++ spec/find-view-spec.coffee
@@ -1514,10 +1514,16 @@
         expect(findView.model.getFindOptions().useRegex).not.toBe true
         expect(findView.findEditor.getModel().getGrammar().scopeName).toBe 'text.plain.null-grammar'
         expect(findView.replaceEditor.getModel().getGrammar().scopeName).toBe 'text.plain.null-grammar'
-  describe "when there isn't any editor", ->
-    it "try to toogle regex", ->
-      atom.commands.dispatch(editorView, 'setting-view:open')
-      expect(findView.anyMarkersAreSelected()).toEqual(false)
-      expect(findView.selectFirstMarkerAfterCursor()).toEqual(undefined)
-      expect(findView.selectFirstMarkerStartingFromCursor()).toEqual(undefined)
-      expect(findView.selectFirstMarkerBeforeCursor()).toEqual(undefined)
+
+  describe "when no buffer is open", ->
+    it "toggles regex via an event and finds text matching the pattern", ->
+      atom.commands.dispatch editorView, 'find-and-replace:show'
+      editor.destroy()
+
+      waitsForPromise ->
+        activationPromise
+
+      runs ->
+        findView.findEditor.setText 'items'
+        atom.commands.dispatch(findView.findEditor.element, 'find-and-replace:toggle-regex-option')
+        expect(findView.descriptionLabel.text()).toContain "No results"


fix for bug that prevented adding new point in ie11

Former-commit-id: e4dbb51f78a768e3555ad6fb52528e4d16abe488 [formerly 9ca0436e1c6461a40721c24cfd9ff20f0572c7ec] [formerly c0f135ca3f5c45256497e4f40f68d16feb3b4318 [formerly f4f43cf4389e468e76a4b59b87987c9f33dbd87c]] [formerly b0d7fd6dfe69b49a557e7168cb8374a40e6bc98a [formerly bedc1013394594f3a41b0aa5f34b61fad92dbe6a] [formerly 93c9c77197de70f6103350600db3ceb2f48c6e6f [formerly 93c9c77197de70f6103350600db3ceb2f48c6e6f [formerly 5a41dec1985c0b0d836725092bb954fb91b05d93]]]]
Former-commit-id: df2a57de22b554300c892430f2794c815232446c [formerly 418ac6be9980f7ed44d32e3481436207ebab5646] [formerly 93d1c178d8abd2b5907b6c4c9a800e37ad9a5bd5 [formerly 865609a58632e2d2fd40c72921187904518d82eb]]
Former-commit-id: b01ccf3ae4fb5eb021c8ce61b3b318eb2268ca68 [formerly bfa0d8e094728d5028b1e19e4cbd63a936963fc1]
Former-commit-id: 5bba000d63f7bc12f34c31ffd92750882d58e3cb
Former-commit-id: 68ccbb06293e87e5921adde593dbe5426aec11e7

--- @client/franklin.coffee
+++ @client/franklin.coffee
@@ -804,6 +804,8 @@
       style:
         width: DECISION_BOARD_WIDTH()

+
+
       SliderBubblemouth()

       [DIV
@@ -813,6 +815,8 @@
         onClick: =>
           if get_proposal_mode() == 'results'
             updateProposalMode('crafting', 'give_opinion_button')
+
+

         DIV null,

@@ -949,31 +953,38 @@
     else
       final_state['.your_points, .save_opinion_button, .below_save'].display = ''

-    if @last_proposal_mode != mode && speed > 0
-      perform initial_state
-
-      # wait for css transitions to complete
-      @transitioning = true
-      _.delay =>
-        if @isMounted()
-          perform final_state
-          @transitioning = false
-
-          s = fetch('reasons_height_adjustment')
-          s.opinion_region_height = $(@getDOMNode()).height()
-          save s
-
-      , speed + 200
-
-    else if !@transitioning
-      perform initial_state
-      perform final_state
-
-      s = fetch('reasons_height_adjustment')
-      s.opinion_region_height = $(@getDOMNode()).height()
-      save s
-
-    @last_proposal_mode = mode
+    if @last_proposal_mode != mode
+
+      if speed > 0
+        perform initial_state
+
+        # wait for css transitions to complete
+        @transitioning = true
+        _.delay =>
+          if @isMounted()
+            perform final_state
+            @transitioning = false
+
+            s = fetch('reasons_height_adjustment')
+            s.opinion_region_height = $(@getDOMNode()).height()
+            save s
+
+        , speed + 200
+
+      else if !@transitioning
+        if !@hi? || @hi < 10
+          console.trace()
+          @hi = if !@hi? then 1 else (@hi + 1)
+          console.log @hi
+
+        perform initial_state
+        perform final_state
+
+        s = fetch('reasons_height_adjustment')
+        s.opinion_region_height = $(@getDOMNode()).height()
+        save s
+
+      @last_proposal_mode = mode

 saveOpinion = (proposal) ->
   root = fetch('root')


Added test case

Added test case

--- spec/test_case_spec.coffee
+++ spec/test_case_spec.coffee
@@ -88,6 +88,18 @@
     expect(failureElement.cdata).toHaveBeenCalledWith('This is a stacktrace')

+  it 'should add a failure node with message and stacktrace when both provided', ->
+    testCase.failure 'Failure message'
+    testCase.stacktrace 'This is a stacktrace'
+
+    testCase.build parentElement
+
+    expect(testCaseElement.ele).toHaveBeenCalledWith('failure', {
+      message: 'Failure message'
+    })
+    expect(failureElement.cdata).toHaveBeenCalledWith('This is a stacktrace')
+
+
   it 'should add a skipped node when test failed', ->
     testCase.skipped()


Replace . with : in invalid sizzle selector

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -253,7 +253,7 @@
       expect(treeView.root.find('> .entries > li:contains(dir1) > .entries > li:contains(sub-dir1) > .entries').length).toBe 1

       # collapsed descendants remain collapsed
-      expect(treeView.root.find('> .entries > li.contains(dir2) > .entries')).not.toExist()
+      expect(treeView.root.find('> .entries > li:contains(dir2) > .entries')).not.toExist()

     it "when collapsing a directory, removes change subscriptions from the collapsed directory and its descendants", ->
       child = treeView.root.entries.find('li:contains(dir1)').view()


Don't use deprecated APIs

Don't use deprecated APIs

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -16,7 +16,7 @@
   [treeView, root, sampleJs, sampleTxt, workspaceElement] = []

   selectEntry = (pathToSelect) ->
-    treeView.selectEntryForPath atom.project.resolve pathToSelect
+    treeView.selectEntryForPath atom.project.getDirectories()[0].resolve pathToSelect

   beforeEach ->
     fixturesPath = atom.project.getPaths()[0]
@@ -998,7 +998,7 @@
               splitPane = atom.workspace.getActivePane()
               splitPaneItem = atom.workspace.getActivePaneItem()
               expect(previousPane).not.toBe splitPane
-              expect(splitPaneItem.getPath()).toBe atom.project.resolve('tree-view.txt')
+              expect(splitPaneItem.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')
               expect(atom.views.getView(splitPaneItem)).toHaveFocus()

           describe "when a directory is selected", ->
@@ -1037,7 +1037,7 @@
             pane = atom.workspace.getPanes()[index]
             item = atom.workspace.getActivePaneItem()
             expect(atom.views.getView(pane)).toHaveFocus()
-            expect(item.getPath()).toBe atom.project.resolve('tree-view.txt')
+            expect(item.getPath()).toBe atom.project.getDirectories()[0].resolve('tree-view.txt')

   describe "file modification", ->
     [dirView, fileView, dirView2, fileView2, fileView3, rootDirPath, dirPath, filePath, dirPath2, filePath2, filePath3] = []


Update spec to use new panel item getter methods

--- spec/tree-view-spec.coffee
+++ spec/tree-view-spec.coffee
@@ -29,7 +29,7 @@

     runs ->
       atom.commands.dispatch(workspaceElement, 'tree-view:toggle')
-      treeView = $(atom.workspace.getLeftPanels()[0].getItemView()).view()
+      treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view()

       root = $(treeView.root)
       sampleJs = treeView.find('.file:contains(tree-view.js)')
@@ -175,7 +175,7 @@
         atom.packages.activatePackage("tree-view")

       runs ->
-        treeView = $(atom.workspace.getLeftPanels()[0].getItemView()).view()
+        treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view()
         expect(treeView).toExist()
         expect(treeView.selectedEntry()).toMatchSelector(".file:contains(tree-view.js)")
         expect(treeView.find(".directory:contains(dir1)")).toHaveClass("expanded")
@@ -190,7 +190,7 @@
         atom.packages.activatePackage("tree-view")

       runs ->
-        treeView = $(atom.workspace.getLeftPanels()[0].getItemView()).view()
+        treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view()
         expect(treeView.list).toMatchSelector ':focus'

     it "restores the scroll top when toggled", ->
@@ -971,7 +971,7 @@

       runs ->
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle')
-        treeView = $(atom.workspace.getLeftPanels()[0].getItemView()).view()
+        treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view()
         root = $(treeView.root)
         dirView = $(treeView.root.entries).find('.directory:contains(test-dir):first')
         dirView[0].expand()
@@ -1245,7 +1245,7 @@

         runs ->
           atom.commands.dispatch(treeView.element, "tree-view:add-file")
-          addDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+          addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

       describe "when a file is selected", ->
         it "opens an add dialog with the file's current directory path populated", ->
@@ -1332,7 +1332,7 @@
           addDialog.cancel()
           dirView.click()
           atom.commands.dispatch(treeView.element, "tree-view:add-file")
-          addDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+          addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

           expect(addDialog).toExist()
           expect(addDialog.promptText.text()).toBeTruthy()
@@ -1346,7 +1346,7 @@
           addDialog.cancel()
           root.click()
           atom.commands.dispatch(treeView.element, "tree-view:add-file")
-          addDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+          addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

           expect(addDialog.miniEditor.getText().length).toBe 0

@@ -1357,7 +1357,7 @@
           root.removeClass('selected')
           expect(treeView.selectedEntry()).toBeNull()
           atom.commands.dispatch(treeView.element, "tree-view:add-file")
-          addDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+          addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

           expect(addDialog.miniEditor.getText().length).toBe 0

@@ -1372,7 +1372,7 @@

         runs ->
           atom.commands.dispatch(treeView.element, "tree-view:add-folder")
-          addDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+          addDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

       describe "when a file is selected", ->
         it "opens an add dialog with the file's current directory path populated", ->
@@ -1451,7 +1451,7 @@

           runs ->
             atom.commands.dispatch(treeView.element, "tree-view:move")
-            moveDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+            moveDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

         afterEach ->
           waits 50 # The move specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
@@ -1541,7 +1541,7 @@

           runs ->
             atom.commands.dispatch(treeView.element, "tree-view:move")
-            moveDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+            moveDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

         it "selects the entire file name", ->
           expect(moveDialog).toExist()
@@ -1566,7 +1566,7 @@

           runs ->
             atom.commands.dispatch(treeView.element, "tree-view:duplicate")
-            copyDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+            copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

         afterEach ->
           waits 50 # The copy specs cause too many false positives because of their async nature, so wait a little bit before we cleanup
@@ -1661,7 +1661,7 @@

           runs ->
             atom.commands.dispatch(treeView.element, "tree-view:duplicate")
-            copyDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+            copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

         it "selects the entire file name", ->
           expect(copyDialog).toExist()
@@ -1684,7 +1684,7 @@
           runs ->
             editorElement = atom.views.getView(atom.workspace.getActivePaneItem())
             atom.commands.dispatch(editorElement, "tree-view:duplicate")
-            copyDialog = $(atom.workspace.getModalPanels()[0].getItemView()).view()
+            copyDialog = $(atom.workspace.getModalPanels()[0].getItem()).view()

         it "duplicates the current file", ->
           expect(copyDialog.miniEditor.getText()).toBe('tree-view.js')
@@ -1895,7 +1895,7 @@

       runs ->
         atom.commands.dispatch(workspaceElement, 'tree-view:toggle')
-        treeView = $(atom.workspace.getLeftPanels()[0].getItemView()).view()
+        treeView = $(atom.workspace.getLeftPanels()[0].getItem()).view()
         dirView = $(treeView.root.entries).find('.directory:contains(test-dir)')
         dirView[0].expand()
         fileView1 = treeView.find('.file:contains(test-file1.txt)')