preparing to make in-document have an effect on preamble (i.e. package options)

--- src/tex-writer.coffee
+++ src/tex-writer.coffee
@@ -114,7 +114,7 @@
 @compile_options()

 #-----------------------------------------------------------------------------------------------------------
-@write_mkts_master = ( layout_info, handler ) ->
+@write_mkts_master = ( S, layout_info, handler ) ->
   self = @
   step ( resume ) ->
     lines             = []
@@ -2183,7 +2183,7 @@
   f = => step ( resume ) ->
     # handler                ?= ->
     layout_info             = HELPERS.new_layout_info self.options, source_route
-    yield self.write_mkts_master layout_info, resume
+    yield self.write_mkts_master S, layout_info, resume
     source_locator          = layout_info[ 'source-locator'  ]
     content_locator         = layout_info[ 'content-locator' ]
     file_output             = njs_fs.createWriteStream content_locator
@@ -2212,6 +2212,7 @@
       options:              self.options
       layout_info:          layout_info
       paragraph_nr:         0
+      configuration:        {}
     #.......................................................................................................
     ### TAINT should read MD source stream ###
     md_source               = njs_fs.readFileSync source_locator, encoding: 'utf-8'


correct first_par / within_ul logic

correct first_par / within_ul logic

--- src/tex-writer.coffee
+++ src/tex-writer.coffee
@@ -642,13 +642,16 @@
   ### TAINT S.is_first_par, S.within_ul could / should be local variables ###
   return $ ( event, send ) =>
     # debug '73632', jr event
-    if select event, '(', [ 'h', 'multi-columns', 'blockquote', ], true then  S.is_first_par  = true
-    else if select event, ')', [ 'blockquote', 'ul', ], true            then  S.is_first_par  = true
-    else if select event, '(', [ 'ul', ], true                          then  S.within_ul     = true
-    else if select event, ')', [ 'ul', ], true                          then  S.within_ul     = false
+    if select event, '(', [ 'h', 'multi-columns', 'blockquote', ],  true then S.is_first_par  = true
+    if select event, ')', [ 'blockquote', ],                        true then S.is_first_par  = true
+    if select event, '(', [ 'ul', ],                                true then S.within_ul     = true
+    if select event, ')', [ 'ul', ],                                true then S.within_ul     = false
     #.......................................................................................................
     ### TAINT doesn't use `select`? ###
-    if event[ 0 ] is '~' and event[ 1 ] is 'start-paragraph'
+    debug '77633', ( jr event ), S.is_first_par, S.within_ul
+    if select event, ')', [ 'ul', ],                               true
+      urge '77768', ( jr event ), S.is_first_par, S.within_ul
+    if select event, '~', 'start-paragraph', true
       within_paragraph  = yes
       seen_text_event   = no
       S.paragraph_nr   += +1


Fixed a problem when thang uses non changed default position component and don't have config and pos (#599)

--- app/views/editor/level/thangs/ThangsTabView.coffee
+++ app/views/editor/level/thangs/ThangsTabView.coffee
@@ -683,8 +683,10 @@
     else
       components = _.cloneDeep thangType.get('components') ? []
     components = @createEssentialComponents(thangType.get('components')) unless components.length
-    physical = _.find components, (c) -> c.config?.pos?
-    physical.config.pos = x: pos.x, y: pos.y, z: physical.config.pos.z if physical
+    positionComponent = _.find(components or [], (c) => c.original in LevelComponent.positionIDs)
+    if positionComponent
+      positionComponent.config ?= {}
+      positionComponent.config.pos = {x: pos.x, y: pos.y, z: positionComponent.config?.pos?.z || 0}
     thang = thangType: thangType.get('original'), id: thangID, components: components
     if batchInsert
       @thangsBatch.push thang


parallelise sshkeys update

parallelise sshkeys update

--- client/code/model/tool.coffee
+++ client/code/model/tool.coffee
@@ -11,8 +11,8 @@
         # Update ssh keys, quite hacky
         # TODO: remove when we have moved the box creation endpoint
         # into custard
-        @_update_sshkeys().complete (ajaxObj, status) =>
-          @exec("cd; rm -r http && git clone #{@get 'gitUrl'} tool --depth 1 && ln -s tool/http http").complete callback
+        @_update_sshkeys()
+        @exec("cd; rm -r http && git clone #{@get 'gitUrl'} tool --depth 1 && ln -s tool/http http").complete callback

   _create_box: ->
     @_generateBoxName()


Force ssl on staging

Force ssl on staging

--- client/lib/setup/index.coffee
+++ client/lib/setup/index.coffee
@@ -34,7 +34,7 @@
     res.send 200, { nodejs: true }

   # Mount generic middleware & run setup modules
-  if 'production' is NODE_ENV
+  if 'production' is NODE_ENV or 'staging' is NODE_ENV
     app.set('forceSSLOptions', { trustXFPHeader: true }).use forceSSL
   app.use sharify
   setupEnv app


"New Key" → "New Shortcut"

"New Key" → "New Shortcut"

--- client/prefs-shortcuts.coffee
+++ client/prefs-shortcuts.coffee
@@ -39,7 +39,7 @@

 getKeystroke = (callback) ->
   $d = $ '<p><input class=shortcuts-input placeholder=...>'
-    .dialog title: 'New Key', modal: 1, buttons: Cancel: -> $d.dialog 'close'; callback k; return
+    .dialog title: 'New Shortcut', modal: 1, buttons: Cancel: -> $d.dialog 'close'; callback k; return
   $ 'input', $d
     .focus -> $(@).addClass    'shortcuts-input'; return
     .blur  -> $(@).removeClass 'shortcuts-input'; return


onboarding: reposition throbber when window is resized

--- client/app/lib/onboarding/throbberview.coffee
+++ client/app/lib/onboarding/throbberview.coffee
@@ -119,6 +119,8 @@
       left : throbberX
       top  : throbberY

+    @tooltip?.setPositions()
+

   click: (event) ->

@@ -129,6 +131,9 @@
   show: ->

     super
+
+    @listenWindowResize yes
+
     #reinit tooltip if it was hidden before
     @showTooltip()  if @tooltip

@@ -136,6 +141,8 @@
   hide: ->

     super
+
+    @listenWindowResize no
     @tooltip?.hide()

@@ -150,8 +157,12 @@

   destroy: ->

+    @listenWindowResize no
     @unsetTooltip()
     super
+
+
+  _windowDidResize: -> @setPosition()

   ###


Fix server/server/data

Fix server/server/data

--- admin/static/coffee/servers/server.coffee
+++ admin/static/coffee/servers/server.coffee
@@ -42,7 +42,7 @@
                             )
                         ).filter( (table) ->
                             table("shards").isEmpty().not()
-                        )
+                        ).coerceTo("ARRAY")
                     )
                 )
             )


updated docs

updated docs

--- src/scripts/cleverbot.coffee
+++ src/scripts/cleverbot.coffee
@@ -1,8 +1,8 @@
 # Description:
-#   Makes your Hubot even more Clever™
+#   "Makes your Hubot even more Clever™"
 #
 # Dependencies:
-#   cleverbot-node: 0.1.1
+#   "cleverbot-node": "0.1.1"
 #
 # Configuration:
 #   None


revert unnessecary changes

revert unnessecary changes

--- src/scripts/directive.coffee
+++ src/scripts/directive.coffee
@@ -16,8 +16,8 @@
   scope: true
   controller: [ "$scope", "$timeout", ($scope, $timeout) ->
     $scope.params = $scope.params or
-    page: 1
-    count: 10
+      page: 1
+      count: 10

     # update result every time when chenge filters
     $scope.$watch('params.filter', ((value) ->


Not needed to be log to see room content

--- src/server/Application.coffee
+++ src/server/Application.coffee
@@ -111,13 +111,15 @@
 		return RoomManager.getList()

 	onRoomRequest: (request, response) =>
-		session = @getAndTestSession(request)
 		room = RoomManager.get(request.params.room);
 		if (!room?)
 			throw HttpErrors.invalidRoomName()
 		data = room.getData();
-		data.admin = room.isAdmin(session.getUser());
-		room.addUser(session.getUser());
+		data.admin = false;
+		try
+			session = @getAndTestSession(request)
+			data.admin = room.isAdmin(session.getUser());
+			room.addUser(session.getUser());
 		return data;

 	onRoomUserRequest: (request, response) =>


better message syntax

better message syntax

--- src/scripts/pagerduty.coffee
+++ src/scripts/pagerduty.coffee
@@ -425,17 +425,17 @@
   generateIncidentString = (incident, hookType) ->
     console.log "hookType is " + hookType
     if hookType == "incident.trigger"
-      "Incident #" + incident.incident_number + " : \n" + incident.status + " and assigned to " + getUserForIncident(incident) + "\n " + incident.html_url + "\n" + "To acknowledge: " + "@#{robot.name}" + " pager me ack " + incident.incident_number  + "\n " + "To resolve: " + "@#{robot.name}" + " pager me resolve " + incident.incident_number  + "\n"
+      "Incident # #{incident.incident_number} :" + " \n" + "#{incident.status} and assigned to #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}" + "\n" + "To acknowledge: @#{robot.name} pager me ack #{incident.incident_number}" + "\n " + "To resolve: @#{robot.name} pager me resolve #{incident.incident_number}" + "\n"
     else if hookType == "incident.acknowledge"
-      "Incident #" + incident.incident_number + " : \n" + incident.status + " and assigned to " + getUserForIncident(incident) + "\n " + incident.html_url + "\n" + "To resolve: " + "@#{robot.name}" + " pager me resolve " + incident.incident_number  + "\n"
+      "Incident # #{incident.incident_number} :" + " \n" + "#{incident.status} and assigned to #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}" + "\n" + "To resolve: @#{robot.name} pager me resolve #{incident.incident_number}" + "\n"
     else if hookType == "incident.resolve"
-      "Incident #" + incident.incident_number + " has been resolved by " +  getUserForIncident(incident) + "\n " + incident.html_url + "\n"
+      "Incident # #{incident.incident_number} has been resolved by #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}" + "\n"
     else if hookType == "incident.unacknowledge"
-      "Incident #" + incident.incident_number + " : \n" + incident.status + " unacknowledged and assigned to " + getUserForIncident(incident) + "\n " + incident.html_url + "\n" + "To acknowledge: " + "@#{robot.name}" + " pager me ack " + incident.incident_number  + "\n " + "To resolve: " + "@#{robot.name}" + " pager me resolve " + incident.incident_number  + "\n"
+      "Incident # #{incident.incident_number} :" + " \n" + "#{incident.status} , unacknowledged and assigned to #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}" + "\n" + "To acknowledge: @#{robot.name} pager me ack ack #{incident.incident_number}" +  "\n " + "To resolve: @#{robot.name} pager me resolve #{incident.incident_number}" + "\n"
     else if hookType == "incident.assign"
-      "Incident #" + incident.incident_number + " : \n" + incident.status + " reassigned to " + getUserForIncident(incident) + "\n " + incident.html_url + "\n" + "To resolve: " + "@#{robot.name}" + " pager me resolve " + incident.incident_number  + "\n"
+      "Incident # #{incident.incident_number} :" + " \n" + "#{incident.status} , reassigned to #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}" + "\n" + "To resolve: @#{robot.name} pager me resolve #{incident.incident_number}" + "\n"
     else if hookType == "incident.escalate"
-      "Incident #" + incident.incident_number + " : \n" + incident.status + " was escalated and assigned to " + getUserForIncident(incident) + "\n " + incident.html_url + "\n" + "To acknowledge: " + "@#{robot.name}" + " pager me ack " + incident.incident_number  + "\n " + "To resolve: " + "@#{robot.name}" + " pager me resolve " + incident.incident_number  + "\n"
+      "Incident # #{incident.incident_number} :" + " \n" + "#{incident.status} , was escalated and assigned to #{getUserForIncident(incident)}" + "\n " + "#{incident.html_url}"+ "\n" + "To acknowledge: @#{robot.name} pager me ack #{incident.incident_number}" + "\n " + "To resolve: @#{robot.name} pager me resolve #{incident.incident_number}" + "\n"

   parseIncidents = (messages) ->
     returnMessage = []


idunnolol

idunnolol

--- api/apps/artworks/test/integration.coffee
+++ api/apps/artworks/test/integration.coffee
@@ -8,6 +8,7 @@

   beforeEach (done) ->
     fabricate 'users', {}, (err, @user) =>
+      return done err if err
       @server = app.listen 5000, ->
         console.log 'listening'
         done()


Respond to more insults

Respond to more insults

--- src/scripts/sensitive.coffee
+++ src/scripts/sensitive.coffee
@@ -26,7 +26,7 @@
   msg.send msg.random messages

 module.exports = (robot) ->
-  pejoratives = "stupid|buggy|useless|dumb"
+  pejoratives = "stupid|buggy|useless|dumb|suck|crap|shitty|idiot"

   r = new RegExp "\\b(you|u|is)\\b.*(#{pejoratives})", "i"
   robot.respond r, hurt_feelings


use prefix

use prefix

--- app/app.coffee
+++ app/app.coffee
@@ -3,9 +3,8 @@
 .config ($routeProvider, $locationProvider, $mdThemingProvider) ->
   $mdThemingProvider.theme('octo-blue')

-  $locationProvider.html5Mode
-    enabled: true
-    requireBase: false
+  html5Options = enabled: true, requireBase: false
+  $locationProvider.html5Mode(html5Options).hashPrefix '!'

   $routeProvider
     .when '/',


revert stupid change

revert stupid change

--- app/assets/javascripts/pregnancies.coffee
+++ app/assets/javascripts/pregnancies.coffee
@@ -1,7 +1,6 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
-

 $(document).on "click", "#toggle-call-log", ->
   $(".old-calls").toggleClass("hidden")


chore: remoce stale line

chore: remoce stale line

--- app/components/namespace-component.coffee
+++ app/components/namespace-component.coffee
@@ -14,7 +14,6 @@
     addNamespace: ->
       data =
         namespace: @get "namespace"
-      postUrl = [ENV.APP.API_BASE, ].join '/'
       that = @
       @get("ajax").post ENV.endpoints.namespaceAdd, data: data
       .then ->


Moved hide to CreateButton

Moved hide to CreateButton

--- app/js/jail_iframe/classes/buttons.coffee
+++ app/js/jail_iframe/classes/buttons.coffee
@@ -24,10 +24,6 @@
     @_updatePosition()

   _updatePosition: =>
-
-  hide: =>
-    @frame.fadeOut()
-    @_visible = false

 class FactlinkJailRoot.CreateButton extends Button
@@ -84,6 +80,10 @@
       top: top
       left: left

+  hide: =>
+    @frame.fadeOut()
+    @_visible = false
+

 class IconButton extends Button


add scheme support to the editor

add scheme support to the editor

--- src/smc-webapp/editor.coffee
+++ src/smc-webapp/editor.coffee
@@ -119,9 +119,11 @@
     sage   : 'python'
     sagews : 'sagews'
     scala  : 'text/x-scala'
+    scm    : 'text/x-scheme'
     sh     : 'shell'
     spyx   : 'python'
     sql    : 'text/x-sql'
+    ss     : 'text/x-scheme'
     sty    : 'stex2'
     txt    : 'text'
     tex    : 'stex2'


CollaborationController: Remove previous fix

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -1219,8 +1219,6 @@
       content    : 'This will end your session and all participants will be removed from this session.'

     modal = @showModal modalOptions, => @stopCollaborationSession callback
-    modal.once 'KDObjectWillBeDestroyed', =>
-      @statusBar.share.button.enable()

   showKickedModal: ->


Invoke unset social channel method

Invoke unset social channel method

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -731,7 +731,7 @@
       envHelpers.detachSocialChannel @workspaceData, (err) =>
         throwError err  if err

-    @workspaceData.channelId = null
+    @unsetSocialChannel()
     callback()


improve terminal reconnect a little

improve terminal reconnect a little

--- src/smc-webapp/console.coffee
+++ src/smc-webapp/console.coffee
@@ -285,10 +285,12 @@

         @session.on 'reconnecting', () =>
             #console.log('terminal: reconnecting')
+            @_reconnecting = new Date()
             @element.find(".salvus-console-terminal").css('opacity':'.5')
             @element.find("a[href=\"#refresh\"]").addClass('btn-success').find(".fa").addClass('fa-spin')

         @session.on 'reconnect', () =>
+            delete @_reconnecting
             partial_code = false
             @_needs_resize = true  # causes a resize when we next get data.
             @_connected = true
@@ -911,6 +913,13 @@
         $(@terminal.element).addClass('salvus-console-blur').removeClass('salvus-console-focus')

     focus: (force) =>
+        if @_reconnecting? and new Date() - @_reconnecting > 10000
+            # reconnecting not working, so try again.  Also, this handles the case
+            # when terminal switched to reconnecting state, user closed computer, comes
+            # back later, etc. Without this, would not attempt to reconnect until
+            # user touches keys.
+            @reconnect_if_no_recent_data()
+
         if @is_focused and not force
             return


webapp/account: send welcome email to an anonymous user signing up

--- src/smc-webapp/r_account.cjsx
+++ src/smc-webapp/r_account.cjsx
@@ -19,6 +19,8 @@
 #
 ###############################################################################

+async = require('async')
+
 {React, ReactDOM, rtypes, rclass, redux}  = require('./app-framework')

 {Button, ButtonToolbar, Checkbox, Panel, Grid, Row, Col, FormControl, FormGroup, Well, Modal, ProgressBar, Alert, Radio} = require('react-bootstrap')
@@ -39,6 +41,7 @@

 {log} = require("./user-tracking")

+{alert_message} = require('./alerts')

 md5 = require('md5')

@@ -156,6 +159,7 @@
     displayName : 'Account-EmailAddressSetting'

     propTypes :
+        account_id    : rtypes.string
         email_address : rtypes.string
         redux         : rtypes.object
         disabled      : rtypes.bool
@@ -186,23 +190,45 @@
             return
         @setState
             state : 'saving'
-        webapp_client.change_email
-            new_email_address : @state.email_address
-            password          : @state.password
-            cb                : (err, resp) =>
-                if not err and resp.error?
-                    err = resp.error
-                if err
-                    @setState
-                        state    : 'edit'
-                        error    : "Error -- #{err}"
-                else
-                    if @props.is_anonymous
-                        log("email_sign_up", {source: "anonymous_account"});
-                    @setState
-                        state    : 'view'
-                        error    : ''
-                        password : ''
+        async.series([
+            (cb) =>
+                webapp_client.change_email
+                    new_email_address : @state.email_address
+                    password          : @state.password
+                    cb                : (err, resp) =>
+                        if not err and resp.error?
+                            err = resp.error
+                        if err
+                            @setState
+                                state    : 'edit'
+                                error    : "Error -- #{err}"
+                        else
+                            if @props.is_anonymous
+                                log("email_sign_up", {source: "anonymous_account"});
+                            @setState
+                                state    : 'view'
+                                error    : ''
+                                password : ''
+                        cb(err)
+
+            (cb) =>
+                # send a welcome email to an anonymous user, possibly including an email verification link
+                if not @props.is_anonymous
+                    cb()
+                webapp_client.send_verification_email
+                    account_id         : @props.account_id
+                    only_verify        : false   # hence the "welcome" email will be sent
+                    cb                 : (err, resp) =>
+                        @setState(disabled_button: true)
+                        if not err and resp.error?
+                            err = resp.error
+                        if err
+                            err_msg = "Error sending welcome email: #{err}"
+                            console.log(err_msg)
+                            alert_message(type:"error", message:err_msg)
+                        cb(err)
+        ])
+

     is_submittable: ->
         return @state.password != '' and @state.email_address != @props.email_address
@@ -722,6 +748,7 @@
                 disabled  = {@props.is_anonymous and not @state.terms_checkbox}
                 />
             <EmailAddressSetting
+                account_id    = {@props.account_id}
                 email_address = {@props.email_address}
                 redux         = {@props.redux}
                 ref           = 'email_address'
@@ -1602,7 +1629,7 @@
 ugly_error = (err) ->
     if typeof(err) != 'string'
         err = misc.to_json(err)
-    require('./alerts').alert_message(type:"error", message:"Settings error -- #{err}")
+    alert_message(type:"error", message:"Settings error -- #{err}")

 # returns password score if password checker library
 # loaded; otherwise returns undefined and starts load


Mudando pra diretiva ter o bzz.auth

Mudando pra diretiva ter o bzz.auth

--- app/scripts/directives/auth-button.coffee
+++ app/scripts/directives/auth-button.coffee
@@ -10,7 +10,7 @@
     else
       console.log 'Invalid provider'

-angular.module('bzzAngularApp')
+angular.module('bzz.auth')
   .directive('authButton', (AuthService) ->
     templateUrl: 'views/directives/auth-button.html'
     restrict: 'E'


don't attempt to process non-jpg files

don't attempt to process non-jpg files

--- assets/src/site.coffee
+++ assets/src/site.coffee
@@ -76,7 +76,7 @@
     @className = ''
     for file in e.dataTransfer.files then do (file) ->
       unless file.type is jpgType
-        alert 'Sorry, ExifExodus only works with JPG files.'
+        return alert 'Sorry, ExifExodus only works with JPG files.'

       cleanImage file, (blob) ->
         img     = document.createElement 'img'


only bounce service + worker on request detail page

--- SingularityUI/app/models/Request.coffee
+++ SingularityUI/app/models/Request.coffee
@@ -32,7 +32,7 @@

         data.hasActiveDeploy = data.activeDeploy? or data.requestDeployState?.activeDeploy?
         data.canBeRunNow = data.state is 'ACTIVE' and data.type in ['SCHEDULED', 'ON_DEMAND'] and data.hasActiveDeploy
-        data.canBeBounced = data.state in ['ACTIVE', 'SYSTEM_COOLDOWN']
+        data.canBeBounced = data.state in ['ACTIVE', 'SYSTEM_COOLDOWN'] and data.type in ['WORKER', 'SERVICE']
         data.canBeScaled = data.state in ['ACTIVE', 'SYSTEM_COOLDOWN'] and data.hasActiveDeploy and data.type in ['WORKER', 'SERVICE']

         data


change subscribed status for paypal based on lastPaymentDate

--- app/views/account/SubscriptionView.coffee
+++ app/views/account/SubscriptionView.coffee
@@ -314,6 +314,7 @@
             @nextPaymentDate = new Date(lastPayment.get('created'))
             @nextPaymentDate.setUTCMonth(@nextPaymentDate.getUTCMonth() + 1)
             @cost = "$#{(lastPayment.get('amount')/100).toFixed(2)}"
+            @subscribed = @nextPaymentDate > Date.now()
             render()
           else
             console.error("No subscription payments found!")


Adapt to use the new import API

Adapt to use the new import API

--- HARImporter.coffee
+++ HARImporter.coffee
@@ -1,6 +1,46 @@
 HARImporter = ->
-  @importString = (context, stringToImport) ->
-    har = JSON.parse(stringToImport)
+
+  @canImport = (context, items) ->
+    a = 0
+    b = 0
+    for item in items
+      a += @_canImportItem(context, item)
+      b += 1
+    return if b > 0 then a/b else 0
+
+  @_canImportItem = (context, item) ->
+    try
+      har = JSON.parse(item.content)
+    catch error
+      return 0
+    if not (har and har.log and har.log.entries)
+      return 0
+    if har.log.entries.length > 0 and not har.log.entries[0].request
+      return 0
+    if har.log.version == '1.2'
+      return 1
+    return 0.9
+
+  @import = (context, items, options) ->
+    order = options?.order or null
+    parent = options?.parent or null
+    for item in items
+      @_importStringToGroup(context, item.content, {
+        parent: parent,
+        order: order,
+        groupName: item.file?.name or 'HAR file'
+      })
+      order += 1
+    return true
+
+  @importString = (context, str) ->
+    return @_importStringToGroup(context, str)
+
+  @_importStringToGroup = (context, str, options={}) ->
+    try
+      har = JSON.parse(str)
+    catch e
+      throw new Error('Invalid JSON for HAR import')

     # If the HAR was not valid JSON
     unless har or har.log or har.log.entries
@@ -8,7 +48,13 @@
       throw new Error('Invalid input format (see https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html)')

     # Create a request group
-    group = context.createRequestGroup('HAR import')
+    group = context.createRequestGroup(options.groupName or 'HAR file')
+
+    # Set parent and order of group
+    if options.parent
+      options.parent.appendChild(group)
+    if options.order and options.order >= 0
+      group.order = options.order

     for entry in har.log.entries
       # Create a request


Update markdown config

Update markdown config

--- atom/.atom/config.cson
+++ atom/.atom/config.cson
@@ -61,8 +61,7 @@
     userId: "b9301e45-0712-4ee7-ba90-a7e3f3260eb9"
   "file-icons":
     coloured: false
-  "markdown-preview-plus":
-    useGitHubStyle: true
+  "markdown-preview-plus": {}
   "tree-view":
     hideIgnoredNames: true
     squashDirectoryNames: true


AceAppView: use file full path when removing from opened tabs

--- client/Ace/aceappview.coffee
+++ client/Ace/aceappview.coffee
@@ -197,7 +197,7 @@

   clearFileRecords: (view) ->
     file = view.getData()
-    delete @aceViews[file.path]
+    delete @aceViews[FSHelper.getFullPath file]

   attachAppMenuEvents: ->
     @on "saveMenuItemClicked", => @getActiveAceView().ace.requestSave()


Fix IE compatibility on setService click.

--- src/views/unit-details.coffee
+++ src/views/unit-details.coffee
@@ -259,7 +259,7 @@
         showServicesOnMap: (event) ->
             event.preventDefault()
             app.commands.execute 'setService',
-                new models.Service(id: event.currentTarget.dataset.id)
+                new models.Service(id: $(event.currentTarget).data('id'))

         scrollToExpandedSection: (event) ->
             $container = @$el.find('.content').first()


Remove debug statement

Remove debug statement

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -435,7 +435,6 @@

 	'click .delete-room': (event) ->
 		event.preventDefault()
-		console.log 'click delete room: '
 		roomData = Session.get('roomData' + this._id)
 		switch roomData.t
 			when 'c'


pubsub_client: <actor/> placement fix

pubsub_client: <actor/> placement fix

--- src/xmpp/pubsub_client.coffee
+++ src/xmpp/pubsub_client.coffee
@@ -91,7 +91,7 @@
 class PubsubRequest extends Request
     xmlns: NS.PUBSUB
     requestIq: ->
-        pubsubEl = @pubsubChild()
+        pubsubEl = @pubsubChild().root()
         if @opts.actor
             pubsubEl.c('actor', xmlns: NS.BUDDYCLOUD_V1).
                 t(@opts.actor)


backporting backbone.view make

backporting backbone.view make


don't share options between inline example cells

--- app/markdown.coffee
+++ app/markdown.coffee
@@ -36,7 +36,8 @@
     example_component = Components.SourceComponent value: @props.value, language: 'coffeescript'

     # TODO should use new context
-    nested_context = Context.create_nested_context @state.ctx
+    # TODO current_options is a Context internal
+    nested_context = Context.create_nested_context(@state.ctx, current_options: _.clone(@state.ctx.options()))
     value = @props.value
     fn = CoffeeScriptCell.create_fn value
     Context.apply_to nested_context, ->


Add Planet Four: Terrains to homepage

Add Planet Four: Terrains to homepage

--- app/pages/home.cjsx
+++ app/pages/home.cjsx
@@ -8,7 +8,7 @@
 OwnedCard = require '../partials/owned-card'

 FEATURED_PRODUCT_IDS = if process.env.NODE_ENV is 'production'
-  ['11', '6', '3']
+  ['11', '6', '3', '17']
 else
   ['231', '405', '272', '76']


pricing: obsolete slider handles option

pricing: obsolete slider handles option

--- client/Pricing/views/planselection.coffee
+++ client/Pricing/views/planselection.coffee
@@ -19,7 +19,6 @@

     options.slider       or= {}
     options.slider.drawBar = no
-    options.slider.handles = [options.slider.initialValue]

     {unitPrice} = options


refactor

refactor

--- lib/md.coffee
+++ lib/md.coffee
@@ -115,7 +115,7 @@
 md = new remarkable('full', defaults)

 DISABLE_SYNC_LINE = false
-HEIGHTS_DELTA = [] # [[start, height], ...] for import files
+HEIGHTS_DELTA = [] # [[realStart, start, height, acc], ...] for import files

 # fix data-line after import external files
 getRealDataLine = (lineNo)->
@@ -858,16 +858,7 @@
   {table:frontMatterTable, content:inputString, data:yamlConfig} = processFrontMatter(inputString, option.hideFrontMatter)

   # check document imports
-  {outputString:newInputString, heightsDelta: HEIGHTS_DELTA} = fileImport(inputString, {filesCache: markdownPreview?.filesCache, rootDirectoryPath: option.rootDirectoryPath, projectDirectoryPath: option.projectDirectoryPath, editor: markdownPreview?.editor})
-
-  ###
-  if inputString.length == newInputString.length
-    DISABLE_SYNC_LINE = false
-  else # there is file import
-    DISABLE_SYNC_LINE = true
-  ###
-
-  inputString = newInputString
+  {outputString:inputString, heightsDelta: HEIGHTS_DELTA} = fileImport(inputString, {filesCache: markdownPreview?.filesCache, rootDirectoryPath: option.rootDirectoryPath, projectDirectoryPath: option.projectDirectoryPath, editor: markdownPreview?.editor})

   # overwrite remark heading parse function
   md.renderer.rules.heading_open = (tokens, idx)=>


ChatSettingsPane: Add/remove user views to chat participant list.

--- client/ide/views/chat/settingspane.coffee
+++ client/ide/views/chat/settingspane.coffee
@@ -14,6 +14,9 @@

     @on 'CollaborationStarted', => @toggleButtons 'started'
     @on 'CollaborationEnded',   => @toggleButtons 'ended'
+
+    @on 'ParticipantJoined', @bound 'addParticipant'
+    @on 'ParticipantLeft',   @bound 'removeParticipant'

   createElements: ->
@@ -132,6 +135,19 @@
     @everyone.addSubView view

+  removeParticipant: (username) ->
+
+    @participantViews[username]?.destroy()
+    delete @participantViews[username]
+
+
+  addParticipant: (nickname) ->
+
+    return  if @participantViews[nickname]
+
+    @createParticipantView { nickname }
+
+
   viewAppended: JView::viewAppended

   setTemplate: JView::setTemplate


Set inverse name of association

Set inverse name of association

--- client/lanes/models/AssociationMap.coffee
+++ client/lanes/models/AssociationMap.coffee
@@ -56,6 +56,8 @@
         options.filter ||= {}
         options.filter[fk] = this.get(pk)

+        options.inverse_name = definition.inverse
+
         if true == target_class::isCollection
             new target_class(options.models || [], options)
         else
@@ -72,12 +74,11 @@
             defaultCreator
         { fn: _.partial(createFn, args...) }

-
     # Sets the assocations for "model"
     set: (model, data, options) ->
         this._set(model, data, options, 'set')
         for name, value of data
-            if @definitions[name] && Lanes.u.isModel(value) && !value.isNew()
+            if @exists(name) && Lanes.u.isModel(value) && !value.isNew()
                 model[@pk(name)] = if value then value.getId() else null

     setFromServer: (model, data, options) ->
@@ -89,7 +90,7 @@

     _set: (model, data, options, fn_name) ->
         for name, value of data
-            continue unless @definitions[name]
+            continue unless @exists(name)
             association = model[name]
             attributes = if _.isFunction(value?.serialize) then value.serialize() else value
             if attributes then association[fn_name]( attributes ) else association.clear()
@@ -134,6 +135,9 @@
     nonLoaded: (model, names) ->
         list = []
         for name in names
-            if _.has(@definitions, name) && (Lanes.u.isCollection(model[name]) || model[name].isNew())
+            if @exists(name) && (Lanes.u.isCollection(model[name]) || model[name].isNew())
                 list.push(name)
         list
+
+    exists: (name) ->
+        _.has(@definitions, name)


update stats url

update stats url

--- lib/julia-client.coffee
+++ lib/julia-client.coffee
@@ -53,7 +53,7 @@

     try
       if id = localStorage.getItem 'metrics.userId'
-        http.get "http://mikeinn.es/hit?id=#{id}&app=atom-julia"
+        http.get "http://data.junolab.org/hit?id=#{id}&app=atom-julia"

   deactivate: ->
     @subscriptions.dispose()


Support for comma separated

Support for comma separated

Added support for comma separated cites are refs, comma seperateds refs
are supported by the cleveref package

--- lib/latexer-hook.coffee
+++ lib/latexer-hook.coffee
@@ -5,8 +5,8 @@
 module.exports =
   class LatexerHook
     beginRex: /\\begin{([^}]+)}/
-    refRex: /\\(ref|eqref|[cCvV]ref){$/
-    citeRex: /\\(cite|textcite|citet|citep|citet\*|citep\*)(\[[^\]]+\])?{$/
+    refRex: /\\(ref|eqref|[cCvV]ref)({|{[^}]+,)$/
+    citeRex: /\\(cite|textcite|citet|citep|citet\*|citep\*)(\[[^\]]+\])?({|{[^}]+,)$/
     constructor: (@editor) ->
       @disposables = new CompositeDisposable
       @disposables.add @editor.onDidChangeTitle => @subscribeBuffer()


Track current skin

Track current skin

--- packages/shrub-skin/client/index.coffee
+++ packages/shrub-skin/client/index.coffee
@@ -8,6 +8,9 @@

 exports.pkgmanRegister = (registrar) ->

+  currentSkinKey = null
+  defaultSkinKey = config.get 'packageConfig:shrub-skin:default'
+
   # #### Implements hook `shrubAngularDirectiveAlter`.
   registrar.registerHook 'shrubAngularDirectiveAlter', (directive, path) -> [

@@ -17,9 +20,6 @@
       # Ensure ID is a candidate.
       directive.candidateKeys ?= []
       directive.candidateKeys.unshift 'id'
-
-      currentSkinKey = null
-      defaultSkinKey = config.get 'packageConfig:shrub-skin:default'

       # Proxy link function to add our own directive retrieval and compilation
       # step.
@@ -129,15 +129,16 @@
               )

         applySkin = (skinKey) ->
+          return if currentSkinKey is skinKey
           currentSkinKey = skinKey
+
+          candidateHooksInvoked = {}
           recalculateCandidate()

         applySkin defaultSkinKey

         # Relink again every time the skin changes.
-        $rootScope.$on 'shrub-skin.changed', (event, skinKey) ->
-          candidateHooksInvoked = {}
-          applySkin skinKey
+        $rootScope.$on 'shrub-skin.changed', (_, skinKey) -> applySkin skinKey

         # Set watches for all candidate-related values.
         keysSeen = {}
@@ -187,7 +188,7 @@
             element = $window.document.createElement 'link'
             element.type = 'text/css'
             element.rel = 'stylesheet'
-            element.href = "/skin/shrub-skin-strapped/#{href}"
+            element.href = "/skin/#{currentSkinKey}/#{href}"
             element.className = 'skin'
             angular.element('head').append element

@@ -281,9 +282,8 @@
           #
           # (String) `skinKey` - The key of the skin to change to *Change
           # skin.*
-          #
-          # ###### TODO: Need to track current, this should be a nop in that case.
           service.change = (skinKey) ->
+            return if skinKey is currentSkinKey

             # Cloak the body.
             addBodyCloak()


Remove log

Remove log

--- packages/shrub-skin/client/index.coffee
+++ packages/shrub-skin/client/index.coffee
@@ -127,7 +127,6 @@
 							for hook in invocations
 								continue if candidateHooksInvoked[hook]
 								candidateHooksInvoked[hook] = true
-								console.log hook
 								for f in pkgman.invokeFlat hook
 									f topLevelArgs...


dont require fibers

dont require fibers

--- packages/tower-model/server/sync.coffee
+++ packages/tower-model/server/sync.coffee
@@ -1,4 +1,4 @@
-Future = require('fibers/future')
+Future = try require('fibers/future')

 Tower.ModelCursorSync =
   find: ->


:fire: Remove jQuery from ::getPlaceholder

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -283,7 +283,7 @@

     tabBar = @getTabBar($(event.target)[0])
     sortableObjects = tabBar.querySelectorAll(".sortable")
-    placeholder = @getPlaceholder()[0]
+    placeholder = @getPlaceholder()
     return unless placeholder?

     if newDropTargetIndex < sortableObjects.length
@@ -447,7 +447,11 @@
       elementIndex + 1

   getPlaceholder: ->
-    @placeholderEl ?= $('<li/>', class: 'placeholder')
+    return @placeholderEl if @placeholderEl?
+
+    @placeholderEl = document.createElement("li")
+    @placeholderEl.classList.add("placeholder")
+    @placeholderEl

   removePlaceholder: ->
     @placeholderEl?.remove()


loading for promises

loading for promises

--- lib/util/loading.coffee
+++ lib/util/loading.coffee
@@ -27,6 +27,12 @@
       if not @isWorking()
         @emitter.emit 'done'

+  monitor: (p) ->
+    @working()
+    done = => @done()
+    p.then done, done
+    p
+
   reset: ->
     if @isWorking()
       @status = 0


set objective: don't confirm in preview

set objective: don't confirm in preview

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -488,11 +488,11 @@

   preview: ->
     @preventSyncToEditor = true
-    @confirm(keepOpen: true, preview: true).then ({editor, point}) =>
-      if editor.isAlive()
-        @setRowMarker(editor, point)
-        @focus()
-      @preventSyncToEditor = false
+    # @confirm(keepOpen: true, preview: true).then ({editor, point}) =>
+    #   if editor.isAlive()
+    #     @setRowMarker(editor, point)
+    #     @focus()
+    #   @preventSyncToEditor = false

   isNormalItem: (item) ->
     item? and not item.skip


rename insertQuery to setQuery for explicitness

--- lib/ui.coffee
+++ lib/ui.coffee
@@ -231,7 +231,7 @@
     pane.activate()

     @grammar.activate()
-    @insertQuery(@query)
+    @setQuery(@query)
     @controlBar.show()
     @moveToPrompt()

@@ -317,7 +317,7 @@
   narrowClose: (event) ->
     if @protected
       event.stopImmediatePropagation()
-      @insertQuery() # clear query
+      @setQuery() # clear query
       @activateProviderPane()

   # Just setting cursor position works but it lost goalColumn when that row was skip item's row.
@@ -440,7 +440,7 @@
     @withIgnoreChange =>
       if @editor.getLastBufferRow() is 0
         # Need to recover query prompt
-        @insertQuery()
+        @setQuery()
         @moveToPrompt()
         @controlBar.show()
       itemArea = new Range(@itemAreaStart, @editor.getEofBufferPosition())
@@ -467,7 +467,7 @@
           # Destroy cursors on prompt to protect query from mutation on 'find-and-replace:select-all'( cmd-alt-g ).
           for selection in @editor.getSelections() when onPrompt(selection.getBufferRange())
             selection.destroy()
-          @withIgnoreChange => @insertQuery(@lastQuery) # Recover query
+          @withIgnoreChange => @setQuery(@lastQuery) # Recover query
         else
           @refresh(selectFirstItem: true).then =>
             if @autoPreviewOnQueryChange and @isActive()
@@ -614,7 +614,7 @@
     @editor.bufferRangeForBufferRow(0)

   # Return range
-  insertQuery: (text='') ->
+  setQuery: (text='') ->
     @editor.setTextInBufferRange([[0, 0], @itemAreaStart], text + "\n")

   startSyncToEditor: (editor) ->


Conditional mustache auto cache template

--- lib/zen.response.coffee
+++ lib/zen.response.coffee
@@ -102,7 +102,7 @@

 __mustache = (name) ->
   dir = "#{__dirname}/../../../www/mustache/"
-  if __cachedMustache[name]
+  if __cachedMustache[name] and (global.ZEN.mustache?.cache or not global.ZEN.mustache)
     __cachedMustache[name]
   else if fs.existsSync file = "#{dir}#{name}.mustache"
     __cachedMustache[name] = fs.readFileSync file, "utf8"


No need to actually listen on a port for these tests

--- test/RemoteIntegration.coffee
+++ test/RemoteIntegration.coffee
@@ -4,7 +4,6 @@
 app = require('connect').createServer()
 assets = require('../lib/assets.js')
 app.use assets src: 'http://mycdn.com'
-app.listen 3590

 exports['options.src can be a URL'] = (test) ->
   jsTag = "<script src='http://mycdn.com/js/jquery.min.js'></script>"


Use CC port in callApi

Use CC port in callApi

--- client/lib/callApi.coffee
+++ client/lib/callApi.coffee
@@ -1,6 +1,6 @@
 import fetch from 'isomorphic-fetch';

-port = (process.env.OPENSHIFT_NODEJS_PORT || '3000')
+port = (process.env.OPENSHIFT_NODEJS_PORT || process.env.PORT || '3000')
 ip = (process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1')
 export API_URL = (if (typeof window == 'undefined') then ('http://' + ip + ':' + port) else '') + '/api/'


KDFramework: minor comment

KDFramework: minor comment

--- client/Framework/components/list/KDListView.coffee
+++ client/Framework/components/list/KDListView.coffee
@@ -58,6 +58,7 @@
       item[0].destroy()
       return
     else
+      # fix it w/ indexOf
       for item,i in @items
         if itemInstance is item or itemData is item.getData()
           @emit 'ItemIsBeingDestroyed', { view : item, index : i }


bugfix: took the wrong line while i was merging

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -98,7 +98,7 @@
           callback message: "No such VM!"

   info:(vm, callback)->
-    [callback, vm] = [vm, callback]  unless callback
+    [callback, vm] = [vm, callback]  if 'function' is typeof callback
     @_runWrapper 'vm.info', vm, (err, info)=>
       warn "[VM-#{vm}]", err  if err
       if err?.name is "UnderMaintenanceError"


StackTemplateListItem: Define a method that updates access level view

--- client/app/lib/stacks/stacktemplatelistitem.coffee
+++ client/app/lib/stacks/stacktemplatelistitem.coffee
@@ -76,13 +76,16 @@
         'Only you can use this template'

-  # updateLabels: ->
+  updateAccessLevel: (accessLevel) ->

-  #   @isDefaultView?.destroy()
-  #   @inUseView?.destroy()
-  #   @notReadyView?.destroy()
-  #   @accessLevelView?.destroy()
-  #   @buildLabels()
+    title = @getAccessLevelTooptipTitle accessLevel
+
+    @accessLevelView.tooltip.update { title }
+    @accessLevelView.tooltip.hide()
+    @accessLevelView.updatePartial accessLevel.toUpperCase()
+
+    @accessLevelView.unsetClass 'private'
+    @accessLevelView.setClass accessLevel

   _itemSelected: (data) ->


pass new dockerCompose and bigboatCompose to onSaveApp handler

--- imports/ui/pages/meteor/AppsDetailPage.coffee
+++ imports/ui/pages/meteor/AppsDetailPage.coffee
@@ -15,6 +15,7 @@
   title: "#{name}:#{version}"
   dockerCompose: app?.dockerCompose
   bigboatCompose: app?.bigboatCompose
+  onSaveApp: (dockerCompose, bigboatCompose)-> App.emit 'save app', app, dockerCompose, bigboatCompose
   onRemoveApp: -> App.emit 'remove app', app
   onStartApp: -> App.emit 'start app', app


Remove timeout when downloading specs.

Remove timeout when downloading specs.

--- src/Witness/Scripts/witness/SimpleRunner.coffee
+++ src/Witness/Scripts/witness/SimpleRunner.coffee
@@ -53,6 +53,7 @@
 					new AsyncAction -> helper.download (=> @done()), (=> @fail())

 			modelDownload = new AsyncAction -> model.download (=> @done()), (=> @fail())
+			modelDownload.timeout = -1

 			downloadSequence = new Sequence helperDownloads.concat(modelDownload)
 			downloadSequence.run {},


QUALITY: Remove existing file uploader code

--- src/app/api/models/portfolion-submission.coffee
+++ src/app/api/models/portfolion-submission.coffee
@@ -56,74 +56,5 @@
   PortfolioSubmission.getPortfolioUrl = (project) ->
     "#{api}/submission/project/#{project.project_id}/portfolio?auth_token=#{currentUser.authenticationToken}"

-  return PortfolioSubmission
-
-  this.openPortfolio = (project) ->
-    $window.open this.getTaskUrl(task), "_blank"
-
-  # TODO: Kill below
-
-  this.fileUploader = (scope, project) ->
-    # per scope or task
-    uploadUrl =
-    fileUploader = new FileUploader {
-      scope: scope,
-      url: uploadUrl
-      method: "POST",
-      queueLimit: 1
-    }
-
-    fileUploader.project = project
-
-    extWhitelist = (name, exts) ->
-      # no extension
-      parts = name.toLowerCase().split('.')
-      return false if parts.length == 0
-      ext = parts.pop()
-      ext in exts
-
-    fileFilter = (acceptList, type, item) ->
-      valid = extWhitelist item.name, acceptList
-      if not valid
-        alertService.add("info", "#{item.name} is not a valid #{type} file (accepts <code>#{_.flatten(acceptList)}</code>)", 6000)
-      valid
-
-    fileUploader.filters.push {
-      name: 'is_code'
-      fn: (item) ->
-        fileFilter ['pas', 'cpp', 'c', 'cs', 'h', 'java'], "code" , item
-    }
-    fileUploader.filters.push {
-      name: 'is_document'
-      fn: (item) ->
-        fileFilter ['pdf'], "document" , item
-    }
-    fileUploader.filters.push {
-      name: 'is_image'
-      fn: (item) ->
-        fileFilter ['png', 'gif', 'bmp', 'tiff', 'tif', 'jpeg', 'jpg'], "image" , item
-    }
-
-    fileUploader.onBeforeUploadItem = (item) ->
-      # ensure this item will be uploading for this unit it...
-      item.formData = fileUploader.formData
-
-    fileUploader.uploadPortfolioPart = (name, kind) ->
-      fileUploader.formData = [ {name: name}, {kind: kind} ]
-      fileUploader.uploadAll()
-
-    fileUploader.onSuccessItem = (item, response)  ->
-      alertService.add("success", "File uploaded successfully!", 2000)
-      if not _.find(fileUploader.project.portfolio_files, response)
-        fileUploader.project.portfolio_files.push(response)
-      fileUploader.clearQueue()
-
-    fileUploader.onErrorItem = (response) ->
-      alertService.add("danger", "File Upload Failed: #{response.error}")
-      fileUploader.clearQueue()
-
-    fileUploader.api = resourcePlus "/submission/project/:id/portfolio", { id: "@id" }
-
-    fileUploader
-  return this
+  PortfolioSubmission
 )


Fixed CORs auth server response bug in IE 11 [#149863936]

Changed the file download to specify the access_token as a query parameter instead of request header.  This avoids a CORS OPTIONS request which fails in IE 11 because the server response for the accept-header from Google is longer than 1024 bytes.

--- src/code/providers/google-drive-provider.coffee
+++ src/code/providers/google-drive-provider.coffee
@@ -247,14 +247,15 @@
             provider: @
             providerData:
               id: file.parents[0].id
+        url = file.downloadUrl
+        # use access_token as query parameter instead of header to avoid CORS request which breaks in IE 11
+        url += "#{if url.indexOf("?") is -1 then "?" else "&"}access_token=#{encodeURIComponent(@authToken.access_token)}"
         xhr = new XMLHttpRequest()
-        xhr.open 'GET', file.downloadUrl
-        if @authToken
-          xhr.setRequestHeader 'Authorization', "Bearer #{@authToken.access_token}"
+        xhr.open 'GET', url
         xhr.onload = ->
           callback null, cloudContentFactory.createEnvelopedCloudContent xhr.responseText
         xhr.onerror = ->
-          callback "Unable to download #{url}"
+          callback "Unable to download file content"
         xhr.send()
       else
         callback @_apiError file, 'Unable to get download url'


Update to support custom form options

Update to support custom form options

--- src/coffee/cilantro/ui/concept/workspace.coffee
+++ src/coffee/cilantro/ui/concept/workspace.coffee
@@ -89,17 +89,19 @@
             if @currentView and model.id is @currentView.model.id
                 return

-            # Determine if this is registered as a custom concept
-            customForm = c.config.get("concepts.forms.#{ model.id }")
-
             options =
                 model: model
                 context: @data.context

+            # Check if custom options or a module has been defined for
+            # this concept.
+            if (customForm = c.config.get("concepts.forms.#{ model.id }"))
+                module = customForm.module
+                options = _.extend({}, customForm.options, options)
+
             # Load external module, catch error if it doesn't exist
-            if customForm?
-                require [customForm.module], (CustomForm) =>
-                    options = _.extend {}, customForm.options, options
+            if module
+                require [module], (CustomForm) =>
                     @createView(CustomForm, options)
                 , (err) =>
                     @showErrorView(model)


Don't print function(..) for an implicit var -- Related to #1894

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -31,6 +31,17 @@
         return new Func {}, (x) -> val

     return val
+
+hasImplicit = (args) ->
+    # args is an array of (strings and arrays)
+    # We recurse to look for `r.row` which is an implicit var
+    if Array.isArray(args)
+        for arg in args
+            if hasImplicit(arg) is true
+                return true
+    else if args is 'r.row'
+        return true
+    return false

 # AST classes

@@ -791,7 +802,10 @@
         return super(optargs, argsArr, body)

     compose: (args) ->
-        ['function(', (Var::compose(arg) for arg in args[0][1...-1]), ') { return ', args[1], '; }']
+        if hasImplicit(args[1])
+            [args[1]]
+        else
+            ['function(', (Var::compose(arg) for arg in args[0][1...-1]), ') { return ', args[1], '; }']

 class Asc extends RDBOp
     tt: "ASC"


Clock#remove should clear the interval

Clock#remove should clear the interval

--- components/clock/view.coffee
+++ components/clock/view.coffee
@@ -56,3 +56,7 @@
           </li>
         """
     )).join '<li>:</li>'
+
+  remove: ->
+    clearInterval @interval
+    super


Update# fix about description position

Update# fix about description position

--- coffeescripts/main.coffee
+++ coffeescripts/main.coffee
@@ -141,7 +141,7 @@

   section.on "progress", (percent)->
     from = screenHeight
-    to = screenHeight / 2 + 60
+    to = screenHeight / 2 - aboutDescriptionHeight / 2 + 60
     if percent <= 100
       aboutBG.y((to - from) / 100 * percent + from)

@@ -151,7 +151,7 @@
     end: 100
     key: 'transform'
     from: screenHeight
-    to: screenHeight / 2 + 24
+    to: screenHeight / 2 - aboutDescriptionHeight / 2 + 24
     format: "translateY(%spx)"
   }


passing tests

passing tests

--- components/favorites2/test/client/favorites.coffee
+++ components/favorites2/test/client/favorites.coffee
@@ -23,7 +23,8 @@
         )
         mod.__set__ 'mediator', @mediator = trigger: sinon.stub(), on: sinon.stub()
         CurrentUser = mod.__get__ 'CurrentUser'
-        CurrentUser.orNull = -> new CurrentUser fabricate 'user'
+        sinon.stub CurrentUser, 'orNull'
+        CurrentUser.orNull.returns new CurrentUser fabricate 'user'
         stubChildClasses mod, this,
           ['ArtworkColumnsView', 'SuggestedGenesView', 'ShareView']
           ['appendArtworks', 'render']
@@ -31,6 +32,7 @@
         done()

   afterEach ->
+    CurrentUser.orNull.restore()
     benv.teardown()
     Backbone.sync.restore()


fixed comment, we use topics now

fixed comment, we use topics now

--- core/app/backbone/views/topics/sidebar_view.coffee
+++ core/app/backbone/views/topics/sidebar_view.coffee
@@ -63,7 +63,7 @@
       @unsetActive()

   # we use setActive to indicate which type is active
-  # this is to be used when something other than a channel is
+  # this is to be used when something other than a topic is
   # activated. If the type has no specific element to activate,
   # everything is deactivated
   setActive: (type) ->


Start tracks in lower positions

Start tracks in lower positions

--- components/Pulsate.coffee
+++ components/Pulsate.coffee
@@ -6,7 +6,7 @@

   constructor: ->
     @value = 0
-    @tracks = [0, 70, 50, 15]
+    @tracks = [0, 40, 30, 10]
     @maxSteps = 100
     @colors = [255, 255, 255]
     @inPorts = new noflo.InPorts


Mesh: Fix LoD with vmath.

Mesh: Fix LoD with vmath.

--- engine/mesh.coffee
+++ engine/mesh.coffee
@@ -362,7 +362,8 @@
             distance_to_camera = vec3.dist p, cam_pos

             # world scale: assuming all three axes have same scale as X
-            world_scale = vec3.len @world_matrix # [0...3] implied
+            {m00, m01, m02} = @world_matrix
+            world_scale = Math.sqrt m00*m00, m01*m01, m02*m02

             # number that converts a length to screen pixels
             poly_length_to_visual_size = (viewport.units_to_pixels/distance_to_camera) * world_scale


Fix safebooru links (3)

Fix safebooru links (3)

--- modules/danbooru.coffee
+++ modules/danbooru.coffee
@@ -33,8 +33,8 @@
           Core.log e, 2
         return
       url =
-        if r.file_url.match(/^http/) then r.file_url.replace('//data', '/')
-        else "https://danbooru.donmai.us#{r.file_url.replace('//data', '/')}"
+        if r.file_url.match(/^http/) then r.file_url.replace('//data', '/data')
+        else "https://danbooru.donmai.us#{r.file_url.replace('//data', '/data')}"
       # Send the picture
       msg.reply '', embed: {
         title: l.generic.sauceBtn
@@ -53,8 +53,8 @@
           Core.log e, 2
         return
       url =
-        if r.file_url.match(/^http/) then r.file_url.replace('//data', '/')
-        else "https://danbooru.donmai.us#{r.file_url.replace('//data', '/')}"
+        if r.file_url.match(/^http/) then r.file_url.replace('//data', '/data')
+        else "https://danbooru.donmai.us#{r.file_url.replace('//data', '/data')}"
       # Send the picture
       msg.reply '', embed: {
         title: l.generic.sauceBtn


fixed handler for refresh button

fixed handler for refresh button

--- app/client/navbar/navbar.coffee
+++ app/client/navbar/navbar.coffee
@@ -21,7 +21,7 @@
 		UserSession.set 'effects', if v == 'cool' then 'static' else 'cool'

 	'click .btn-refresh': ->
-		Meteor.call 'onLogin', Meteor.userId()
+		Meteor.call 'on_login', Meteor.userId()

 	'click .btn-dashboard': ->
 		Session.set 'perspective', 'dashboard'


Position tutorial on activate

Position tutorial on activate

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -100,6 +100,9 @@
     Subject.on 'get-next', @onSubjectGettingNext
     Subject.on 'select', @onSubjectSelect

+  activate: ->
+    setTimeout (=> @tutorial.attach() if @tutorial._current?), 500
+
   onClickFavorite: ->
     @classification.favorite = !@classification.favorite
     @favoriteButton.toggleClass 'active', @classification.favorite


Remove old method

Remove old method

--- app/home/home-controller.coffee
+++ app/home/home-controller.coffee
@@ -18,16 +18,6 @@
     .catch (@error) =>
       @errorMsg = @error

-  nextColor: =>
-    [
-      'blue'
-      'purple'
-      'green'
-      'orange'
-      'red'
-    ][@colorIndex++ % 5]
-
-
   triggerTheTrigger: (trigger) =>
     trigger.triggering = true
     @TriggerService.trigger(trigger.flow, trigger.id, @cookies.uuid, @cookies.token).then ()=>


Better redirection when creating VM(s)

Better redirection when creating VM(s)

--- app/modules/new-vm/index.coffee
+++ app/modules/new-vm/index.coffee
@@ -334,7 +334,7 @@
         $scope.createVM(name)
       )
       # Send the client on the tree view
-      $state.go 'tree'
+      $state.go 'index'

     xenDefaultWeight = 256
     $scope.weightMap = {
@@ -483,14 +483,13 @@
       .then () ->
         if $scope.bootAfterCreate
           xo.vm.start id
-        if resourceSet
-          # FIXME When using self service, ACL permissions are not updated fast enough to access VM view right after creation
-          $state.go 'list'
-        else
-          if !$scope.multipleVmsActive
+        if !$scope.multipleVmsActive
+          if resourceSet
+            # FIXME When using self service, ACL permissions are not updated fast enough to access VM view right after creation
+            $state.go 'index'
+          else
             # Send the client on the VM view
             $state.go 'VMs_view', { id }
-          else $state.go 'tree'
       .catch (error) ->
         notify.error {
           title: 'VM creation'


Added a preview task to gulp.

Added a preview task to gulp.

--- .metalsmith/gulpfile.coffee
+++ .metalsmith/gulpfile.coffee
@@ -54,6 +54,16 @@
 gulp.task 'metalsmith', -> metal()

+# ## preview
+#
+# Run a small http server pointed at the build directory, to preview
+# the compiled pages.
+gulp.task 'preview', ['build'], ->
+  connect()
+    .use connect.static path.join __dirname, 'build'
+    .listen 8000
+
+
 # ## sass
 #
 # Compile our sass to css in the build dir.


Update P2SH Litecoin prefix

Update P2SH Litecoin prefix

--- app/src/bitcoin/networks.coffee
+++ app/src/bitcoin/networks.coffee
@@ -235,7 +235,7 @@
     handleSegwit: yes
     version:
       regular: 48
-      P2SH: 5
+      P2SH: 50
       XPUB: 0x19DA462
     bitcoinjs: bitcoin.networks.litecoin
     dust: 10000


Prevent drawing if there is no player

Prevent drawing if there is no player

--- app/views/home-page-view.coffee
+++ app/views/home-page-view.coffee
@@ -126,7 +126,9 @@
     # get attributes
     actions = @inputManager.get 'actions'
     moveDir = new Vec2 0, 0
-    player = mediator.getActiveLevel().player
+    player = mediator.getActiveLevel()?.player
+
+    return unless player?

     if actions['move-up']
       moveDir.y -= 1


jshint force option no longer needed

jshint force option no longer needed

Since `lineman run` now runs in `--force` mode, setting `jshint.options.force` to true when in run mode is superfluous and redundant.

--- config/application.coffee
+++ config/application.coffee
@@ -64,7 +64,6 @@
   jshint:
     files: ["<%= files.js.app %>"]
     options:
-      force: process.env['LINEMAN_RUN'] || false
       curly: true
       eqeqeq: true
       latedef: true


Add autosave to disabled packages and `.hg` to ignored files

--- atom/config.cson
+++ atom/config.cson
@@ -20,9 +20,12 @@
   'fontSize': 14
 'core':
   'projectHome': '/Volumes/Data/Users/Lee/Source'
-  'disabledPackages': []
+  'disabledPackages': [
+    'autosave'
+  ]
   'ignoredNames': [
     '.git'
+    '.hg'
     '.svn'
     '.DS_Store'
     '.keep'
@@ -94,3 +97,5 @@
   'microBreakIntervalInSeconds': 900
   'microBreakLengthInSeconds': 30
   'macroBreakIntervalInMicroAmount': 4
+'package-generator':
+  'createInDevMode': true


Update atom config

Update atom config

--- atom/config.cson
+++ atom/config.cson
@@ -7,6 +7,7 @@
   "autocomplete-python":
     useKite: false
   core:
+    automaticallyUpdate: false
     customFileTypes:
       "source.ini": [
         ".hgrc"


Fix bug where pull would not install new packages

--- atom/init.coffee
+++ atom/init.coffee
@@ -51,6 +51,28 @@
     return pkgList

+# Uninstalls the given list of packages
+uninstallPkgs = (pkgs, callback) ->
+  if pkgs.length isnt 0
+    console.log('Uninstalling packages: ' + pkgs.join(', '))
+    child = spawn(APM_PATH, ['uninstall'].concat(pkgs))
+    child.stderr.on('data', (data) -> console.log(String(data)))
+    child.on('close', (code) -> callback?())
+  else
+    callback?()
+
+
+# Installs the given list of packages
+installPkgs = (pkgs, callback) ->
+  if pkgs.length isnt 0
+    console.log('Installing packages: ' + pkgs.join(', '))
+    child = spawn(APM_PATH, ['install'].concat(pkgs))
+    child.stderr.on('data', (data) -> console.log(String(data)))
+    child.on('close', (code) -> callback?())
+  else
+    callback?()
+
+
 # Brings local package list into sync with remote package list
 pullPkgList = (callback) ->
   localPkgList = getLocalPkgList()
@@ -58,28 +80,17 @@
   # Only push if local package list differs from remote package list
   if localPkgList.join(',') isnt remotePkgList.join(',')
     console.log('Pulling package list from remote...')
+    removedPkgs = getArrayDiff(localPkgList, remotePkgList)
+    addedPkgs = getArrayDiff(remotePkgList, localPkgList)
     # Uninstall local packages that are missing on remote
-    removedPkgs = getArrayDiff(localPkgList, remotePkgList)
-    console.log('Uninstalling packages:', removedPkgs)
-    if removedPkgs.length isnt 0
-      child = spawn(APM_PATH, ['uninstall'].concat(removedPkgs))
-      child.stdout.on('data', (data) -> console.log(data))
-      child.stderr.on('data', (data) -> console.log(data))
-      child.on('close', (code) ->
-        # Install remote packages that are missing on local
-        addedPkgs = getArrayDiff(remotePkgList, localPkgList)
-        console.log('Installing packages:', addedPkgs)
-        if addedPkgs.length isnt 0
-          child = spawn(APM_PATH, ['install'].concat(addedPkgs))
-          child.stdout.on('data', (data) -> console.log(data))
-          child.stderr.on('data', (data) -> console.log(data))
-          child.on('close', (code) -> callback())
-        else
-          callback()
-      )
+    uninstallPkgs(removedPkgs, ->
+      # Install remote packages that are missing on local
+      installPkgs(addedPkgs)
+      callback?()
+    )
   else
     console.log('No changes to pull')
-    callback()
+    callback?()

 # Pushes local package list to remote


Move most Atom key bindings under single selector

Since Atom 1.12.0, user key bindings take precedence over core
and package key bindings. Because of this, most keybindings can
be reside under a single selector rather than being split across
several similar-but-slightly-different selectors.

--- atom/keymap.cson
+++ atom/keymap.cson
@@ -7,35 +7,43 @@
   'ctrl-shift-tab ^ctrl': 'unset!'
   'ctrl-shift-tab': 'pane:show-previous-item'

-'.platform-darwin atom-workspace atom-text-editor:not([mini])':
+'.platform-darwin atom-text-editor:not([mini])':
+
   # Breaks current selection/paragraph into lines no longer than 80 chars
   'alt-cmd-q': 'unset!'
   'alt-cmd-r': 'autoflow:reflow-selection'
+
   # Toggles grammar (language) selection panel
   'ctrl-shift-L': 'unset!'
   'shift-cmd-L': 'grammar-selector:show'
+
   # Toggles character encoding selection panel
   # 'ctrl-shift-U': 'unset!'
   'cmd-shift-U': 'encoding-selector:show'
+
   # Toggles line jump panel
   'cmd-j': 'go-to-line:toggle'
+
   # Deletes an entire line without copying it
   'ctrl-shift-K': 'unset!'
   'shift-cmd-X': 'editor:delete-line'
+
   # Selects all occurences of current selection (complements cmd-d)
   'alt-cmd-d': 'find-and-replace:select-all'
+
   # Selects all text between outer matching pair of parens/brackets
   'alt-cmd-b': 'bracket-matcher:select-inside-brackets'

-'atom-text-editor':
   # Resizes spaces-per-indent amount for entire document
   # 'ctrl-{': 'unset!'
   # 'ctrl-}': 'unset!'
   'ctrl-cmd-[': 'resize-indent:halve'
   'ctrl-cmd-]': 'resize-indent:double'
+
   # Convert between tabs and spaces used for indentation
   'ctrl-alt-cmd-]': 'whitespace:convert-spaces-to-tabs'
   'ctrl-alt-cmd-[': 'whitespace:convert-tabs-to-spaces'
+
   # Disable all alt-* key bindings
   'alt-b': 'unset!'
   'alt-shift-B': 'unset!'
@@ -45,34 +53,38 @@
   'alt-h': 'unset!'
   'alt-shift-S': 'unset!'

-'atom-text-editor:not([mini])':
   # Toggles invisible characters and guides
   'cmd-i': 'window:toggle-invisibles'
   'shift-cmd-I': 'editor:toggle-indent-guide'
+
   # Auto-indents the selection according to the file's designated grammar
   'alt-cmd-tab': 'editor:auto-indent'
+
   # Toggles list of snippets that apply to current document
   'ctrl-cmd-s': 'snippets:available'
+
   # Advances to next Linter error in document
   'alt-cmd-l': 'linter:next-error'
+
   # Expands/contracts selection to next outermost/innermost set of tokens
   'alt-up': 'expand-region:expand'
   'alt-down': 'expand-region:shrink'

-'.platform-darwin atom-text-editor:not([mini])':
   # Aligns values to same column if list of key-value pairs is selected
   # 'ctrl-cmd-a': 'unset!'
   'shift-cmd-A': 'atom-alignment:alignMultiple'
+
   # Toggles Markdown Preview (even if editor is unfocused)
   'shift-cmd-M': 'markdown-preview:toggle'
   'ctrl-w': 'unset!'
+
   # Expands Emmet HTML abbreviations
   'cmd-e': 'emmet:expand-abbreviation'

-'.platform-darwin atom-workspace':
   # Opens inline URL in default web browser if cursor is within URL
   # 'ctrl-shift-O': 'unset!'
   'alt-cmd-o': 'link:open'
+
   # Create new files using advanced-open-file rather than open existing files
   'alt-cmd-n': 'advanced-open-file:toggle'


more debugging for #699

more debugging for #699

--- content/keymanager.coffee
+++ content/keymanager.coffee
@@ -78,7 +78,11 @@

   rescan: co((clean)->
     if @scanning
-      flash('Scanning still in progress', "Scan is still running, #{@scanning} items left")
+      if Array.isArray(@scanning)
+        left = ", #{@scanning.length} items left"
+      else
+        left = ''
+      flash('Scanning still in progress', "Scan is still running#{left}")
       return

     @scanning = true
@@ -109,7 +113,10 @@
       start = new Date()
       items = yield Zotero.Items.getAsync(@scanning.map((key) -> key.itemID))
       for key, progress in @scanning
-        @update(items[progress], key)
+        try
+          @update(items[progress], key)
+        catch err
+          debug('KeyManager.rescan: update', progress, 'failed:', err)

         if progress && !(progress % 200)
           left = (((new Date()) - start) / progress) * (@scanning.length - progress)


Remove spurious logging.

Remove spurious logging.

--- assets/coffee/blueprints.coffee
+++ assets/coffee/blueprints.coffee
@@ -23,7 +23,6 @@
         @dtOptions = @DTOptionsBuilder.newOptions()
           .withBootstrap()
           .withOption('responsive', true)
-        console.log @dtOptions
         @dtColumnDefs = [
           type: 'numeric-inf'
           targets: 2


Load commit from build:started event payload

Commit fields come in with build:started payload. We should split it
into 2 separate keys in the payload, like:

    {
      build:  { .... },
      commit: { .... }
    }

but I can't change pusher v1 API at the moment, due to backwards
compatibility with the old client. We will be able to remove this manual
extraction as soon as we switch to pusher v2 API.

--- assets/scripts/app/store.coffee
+++ assets/scripts/app/store.coffee
@@ -67,11 +67,24 @@
     mappings = @adapter.get('mappings')
     type = mappings[name]

-    # TODO: we should definitely handle such things in some kind of adapters
-    # when we get 'commit' value, ember thinks that this is actually a commit object
-    # that it should sideload, let's just delete it for now
     if event == 'build:started' && data.build.commit
+      # TODO: commit should be a sideload record on build, not mixed with it
+      build = data.build
+      commit = {
+        id:              build.commit_id
+        author_email:    build.author_email
+        author_name:     build.author_name
+        branch:          build.branch
+        committed_at:    build.committed_at
+        committer_email: build.committer_email
+        committer_name:  build.committer_name
+        compare_url:     build.compare_url
+        message:         build.message
+        sha:             build.commit
+      }
       delete(data.build.commit)
+      @loadIncomplete(Travis.Commit, commit)
+

     if event == 'job:log'
       if job = @find(Travis.Job, data['job']['id'])


fix release script

fix release script

--- Dev/tutorial/release.coffee
+++ Dev/tutorial/release.coffee
@@ -41,6 +41,5 @@
 info 'copy to', dest
 fs.removeSync dest
 fs.copySync 'build', dest
-glob.sync( `#{examples}*.html` ).forEach rmf

 glob.sync( examples ).forEach cpf


Subscribe to collapse event in PathView

Subscribe to collapse event in PathView

--- src/packages/command-panel/lib/path-view.coffee
+++ src/packages/command-panel/lib/path-view.coffee
@@ -17,7 +17,7 @@

   initialize: ->
     @on 'mousedown', @onPathSelected
-    rootView.command 'command-panel:collapse-result', (e) =>
+    @subscribe rootView, 'command-panel:collapse-result', =>
       @collapse(true) if @find('.selected').length

   onPathSelected: (event) =>


Make sure the VERSION file exists, otherwise use 0.0.1 as default

--- node_modules_koding/koding-worker/main.coffee
+++ node_modules_koding/koding-worker/main.coffee
@@ -85,7 +85,10 @@
     d.getUTCFullYear() + "-" + pad(d.getUTCMonth() + 1) + "-" + pad(d.getUTCDate()) + "T" + pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes()) + ":" + pad(d.getUTCSeconds()) + "Z"

   getVersion:() ->
-    version = (fs.readFileSync nodePath.join(__dirname, '../../VERSION'), 'utf-8').trim()
+    versionFile = nodePath.join(__dirname, '../../VERSION')
+    if fs.existsSync versionFile
+      version = (fs.readFileSync versionFile, 'utf-8').trim()
+    return version ? "0.0.1"

   getRabbitKey:() ->
     version = (fs.readFileSync nodePath.join(__dirname, '../../KEY'), 'utf-8').trim()


Fake a build ID if API has never been accessed during the session

--- api/tools/gw2api.coffee
+++ api/tools/gw2api.coffee
@@ -305,7 +305,7 @@
                 if state.lastSeenBuild > 0
                     (cb)(null, { id: state.lastSeenBuild, cachedResponse: true })
                 else
-                    (cb)(err, res)
+                    (cb)(null, { id: -1, cachedResponse: true })
     getItems: (ids, cb) ->
         if !(_.isArray(ids) && _.all ids, (id) -> _.isNumber id)
             return (cb) makeError 'API wrapper requires the parameter to be an array of IDs', ErrorCode.INVALID_WRAPPER_CALL


Made first click on set username work.

Made first click on set username work.

--- client/settings.coffee
+++ client/settings.coffee
@@ -27,9 +27,10 @@
 # Username settings
 Template.settingsUsername.events =
   'click button.set-username': (event, template) ->
+    event.stopPropagation()
+    event.preventDefault()
     usernameReadyForValidation = true
     usernameFormMessages.resetMessages()
-    event.preventDefault()
     username = $('#username').val()
     try
       usernameFormMessages.resetMessages()
@@ -45,6 +46,7 @@
     return # Make sure CoffeeScript does not return anything

   'blur input#username': (event, template) ->
+    event.stopPropagation()
     usernameReadyForValidation = usernameFieldModified
     username = $('#username').val()
     validateUsername username, 'username'


change the spec description to make it clear

--- spec/pane-container-element-spec.coffee
+++ spec/pane-container-element-spec.coffee
@@ -3,7 +3,7 @@
 PaneAxis = require '../src/pane-axis'

 describe "PaneResizeHandleElement", ->
-  describe "when add or remove panes in PaneAxisElement", ->
+  describe "when panes are added or removed in PaneAxisElement", ->
     [paneAxisElement, paneAxis] = []

     beforeEach ->
@@ -12,7 +12,7 @@
       paneAxisElement.initialize(paneAxis)
       document.querySelector('#jasmine-content').appendChild(paneAxisElement)

-    it "inserts or remove resize elements", ->
+    it "inserts or removes resize elements", ->
       expectPaneAxisElement = (index) ->
         child = paneAxisElement.children[index]
         expect(child.nodeName.toLowerCase()).toBe('atom-pane-axis')
@@ -34,7 +34,7 @@
       paneAxis.removeChild(models[2])
       expectResizeElement(i) for i in [1]

-  describe "when user drag the resize element", ->
+  describe "when the resize element is dragged ", ->
     [container, containerElement] = []
     [resizeElementMove, getElementWidth, expectPaneScale] = []

@@ -65,7 +65,7 @@
         for paneScale in args
           expect(paneScale[0].getFlexScale()).toBeCloseTo(paneScale[1], 0.1)

-    it "add and remove panes in the same direction", ->
+    it "adds and removes panes in the direction that the pane is being dragged", ->
       leftPane = container.getActivePane()
       middlePane = leftPane.splitRight()

@@ -92,7 +92,7 @@
       leftPane.close()
       expectPaneScale [rightPane, 1]

-    it "split or close panes in orthogonal direction", ->
+    it "splits or closes panes in orthogonal direction that the pane is being dragged", ->
       leftPane = container.getActivePane()
       rightPane = leftPane.splitRight()


add routes to frontend

add routes to frontend

--- js/app.coffee
+++ js/app.coffee
@@ -61,6 +61,7 @@
     @jobs = new CI.outer.Page("jobs", "Work at CircleCI", "View jobs")
     @enterprise = new CI.outer.Page("enterprise", "CircleCI for the enterprise")
     @stories = new CI.outer.Page("stories", "Shopify + CircleCI Success Story")
+    @integrations = new CI.outer.Page("integrations", "CircleCI and Docker")
     @privacy = new CI.outer.Page("privacy", "Privacy", "View Privacy")
     @changelog = new CI.outer.Changelog("changelog", "ChangeLog", "View ChangeLog")
     # @contact = new CI.outer.Page("contact", "Contact us", "View Contact")
@@ -467,6 +468,7 @@
   @get "^/changelog.*", (cx) => VM.changelog.display(cx)
   @get "^/enterprise.*", (cx) => VM.enterprise.display(cx)
   @get "^/stories.*", (cx) => VM.stories.display(cx)
+  @get "^/integrations.*", (cx) => VM.integrations.display(cx)
   # @get "^/contact.*", (cx) => VM.contact.display(cx)
   @get "^/security(#.*)?", (cx) => VM.security.display(cx)
   @get "^/security/hall-of-fame", (cx) => VM.securityHOF.display(cx)


Make Mono tours check tier for Uber

Make Mono tours check tier for Uber

--- js/app.coffee
+++ js/app.coffee
@@ -60,8 +60,7 @@
     NU: 4
     PU: 5

-  getTierCallback = (tier) =>
-    (pokemon) =>
+  @checkTier = (tier, pokemon) ->
       @tiers[pokemon.tier] >= @tiers[tier] and not pokemon.legendary and @checkMega tier

   @checkMega = (pokemon, tier = 'OU') ->
@@ -72,39 +71,39 @@

   @formats = [
     name: 'Regular'
-    callback: getTierCallback 'OU'
+    callback: @checkTier.bind this, 'OU'
   ,
     name: 'Monotype'
     callback: (pokemon) =>
-      not pokemon.legendary and @configuration in pokemon.types and @checkMega pokemon
+      @configuration in pokemon.types and @checkTier 'OU', pokemon
     configuration:
       label: 'Type'
   ,
     name: 'Monocolor'
     callback: (pokemon) =>
-      not pokemon.legendary and pokemon.color is @configuration and @checkMega pokemon
+      pokemon.color is @configuration and @checkTier 'OU', pokemon
     configuration:
       label: 'Color'
   ,
     name: 'Monoregion'
     callback: (pokemon) =>
       regions = @formatNameToFormat.Monoregion.configuration.values
-      not pokemon.legendary and @checkMega(pokemon) and pokemon.gen is 1 + regions.indexOf @configuration
+      pokemon.gen is 1 + regions.indexOf(@configuration) and @checkTier 'OU', pokemon
     configuration:
       label: 'Region'
       values: ['Kanto', 'Johto', 'Hoenn', 'Sinnoh', 'Unova', 'Kalos']
   ,
     name: 'UU'
-    callback: getTierCallback 'UU'
+    callback: @checkTier.bind this, 'UU'
   ,
     name: 'RU'
-    callback: getTierCallback 'RU'
+    callback: @checkTier.bind this, 'RU'
   ,
     name: 'NU'
-    callback: getTierCallback 'NU'
+    callback: @checkTier.bind this, 'NU'
   ,
     name: 'Ubers'
-    callback: getTierCallback 'Uber'
+    callback: @checkTier.bind this, 'Uber'
   ,
     name: 'Legendary'
     callback: (pokemon) =>


Block: Use specified content container selector.

--- client/Block.coffee
+++ client/Block.coffee
@@ -1,7 +1,7 @@
 Metric = require './Metric'

-module.exports = ->
+module.exports = (content_container='.Entry__ ._Content__') ->
     seen_metric     = new Metric('Block')
     blocks          = []
     track_timeout   = null
@@ -59,7 +59,7 @@
                 block.track_timeout = setTimeout ->
                     block.was_seen = true
                     block.el.dataset.seen = true
-                    # Track that a block was visible for at least 1000ms
+                    # Track that a block was visible for at least 2000ms
                     # Record the depth in terms of...
                     seen_metric.track
                         type        : 'seen'
@@ -80,5 +80,5 @@
     # TODO: Block.getVisibleBlocks()
     gatherBlocks()
     if blocks.length > 0
-        entry_content_el = document.querySelector('.Entry__ ._Content__')
+        entry_content_el = document.querySelector(content_container)
         window.addEventListener('scroll', throttle(checkDepth, 100))


adding methods from rest api 0.5 to v1

adding methods from rest api 0.5 to v1

--- packages/rocketchat-api/server/routes.coffee
+++ packages/rocketchat-api/server/routes.coffee
@@ -164,7 +164,10 @@

 		return RocketChat.API.v1.success

-# Create Private Group
+### Create Private Group
+ example data:
+  {"name":"room5","members":["Jeff","Larry","Stephen"]}
+###
 RocketChat.API.v1.addRoute 'groups.create', authRequired: true,
 	post: ->
 		if not @bodyParams.name?
@@ -176,6 +179,7 @@
 		id = undefined
 		try
 			if not @bodyParams.members?
+
 				Meteor.runAsUser this.userId, =>
 					id = Meteor.call 'createPrivateGroup', @bodyParams.name, []
 			else
@@ -187,3 +191,118 @@
 		return RocketChat.API.v1.success
 			group: RocketChat.models.Rooms.findOne({_id: id.rid})

+### Remove rooms
+any type i.e. channels/private/direct message room
+user must also have create-c permission
+pass room _id in body of data (  data='{"name":[" room _id"]}'  )
+###
+RocketChat.API.v1.addRoute 'bulk/removeGroup', authRequired: true,
+	delete: ->
+			if RocketChat.authz.hasPermission(@userId, 'bulk-create-c')
+				try
+					this.response.setTimeout (1000 * @bodyParams.name.length)
+					ids = []
+					Meteor.runAsUser this.userId, () =>
+						(ids[i] = Meteor.call 'eraseRoom', incoming) for incoming,i in @bodyParams.name
+					status: 'success', ids: ids  # need to handle error
+				catch e
+					statusCode: 400    # bad request or other errors
+					body: status: 'fail', message: e.name + ' :: ' + e.message
+			else
+				console.log '[API.v1.] bulk/removePrivateGroups -> '.red, "User does not have 'bulk-create-c' permission"
+				statusCode: 403
+				body: status: 'error', message: 'You do not have permission to do this'
+
+### Retrieve integrations
+**Note make sure you encode all channel/private rooms  because they start with '#' === '%23'
+Example string ('http://your url here : 3000/api/%23testRoom/roomIntegrations',headers={'X-User-Id':'user_token','X-Auth-Token':'user_token'})
+###
+RocketChat.API.v1.addRoute ':channel/roomIntgrations', authRequired: true,
+	get: ->
+		try
+			if not channel?
+				id = RocketChat.models.Integrations.find().fetch()
+				status: 'success', Integrations: id
+			else
+				id = RocketChat.models.Integrations.find({"channel":decodeURIComponent(@urlParams.channel)}).fetch()
+				status: 'success', Integrations: id
+		catch e
+			statusCode: 400    # bad request or other errors
+			body: status: 'Epic fail', message: e.name + ' :: ' + e.message
+
+### retrieve a complete list of all integrations
+Api.addRoute 'roomIntegrations', authRequired: true,
+	get: ->
+		id = RocketChat.models.Integrations.find().fetch()
+		status: 'success', Integrations: id
+###
+
+
+### Create outgoing webhooks,
+user must also have create-c permission because
+creating a push point for messages to be push to from Rocket Chat.
+###
+RocketChat.API.v1.addRoute 'outgoingWebhook', authRequired: true,
+	post: ->
+			if RocketChat.authz.hasPermission(@userId, 'bulk-create-c')
+				try
+					this.response.setTimeout (1000 * @bodyParams.name.length)
+					integration ={ userid:@bodyParams.userid, auth:@bodyParams.authToken, name:@bodyParams.name, enabled:@bodyParams.enabled, name:@bodyParams.roomName, channel:@bodyParams.channel,
+					triggerWords:@bodyParams.triggerWords, urls:@bodyParams.urls, username:@bodyParams.username, alias:@bodyParams.alias, avatar:@bodyParams.avatar, emoji:@bodyParams.emoji, token:@bodyParams.token,
+					scriptEnable:@bodyParams.scriptEnabled, script:@bodyParams.script }
+
+					Meteor.runAsUser this.userId, () =>
+						Meteor.call 'addOutgoingIntegration', integration
+					status: 'success' # need to handle error
+				catch e
+					statusCode: 400    # bad request or other errors
+					body: status: 'yep failled again', message: e.name + ' :: ' + e.message
+			else
+				console.log '[restapi] api/outgoingWebhook -> '.red, "User does not have 'bulk-create-c' permission"
+				statusCode: 403
+				body: status: 'error', message: 'You do not have permission to do this'
+
+### Remove Outgoing Webhook
+user must also have create-c permission
+@apiParam {json} An array of intergration webhooks in the body of the POST. 'integration' is integrations name
+use API 'roomIntegrations' method to aquire a list of all webhooks to capture webhook _ids or use ':channel/roomIntgrations' to aquire
+a list from a specfic channel or private room.
+
+@apiParamExample {json} POST Request Body example:
+  {
+    'integrationId':[ 'integrations_id1','integrations_id2']
+  }
+###
+RocketChat.API.v1.addRoute 'removeOutgoingWebhook', authRequired: true,
+	delete: ->
+			if RocketChat.authz.hasPermission(@userId, 'bulk-create-c')
+				try
+					this.response.setTimeout (1000)
+					ids=[]
+					Meteor.runAsUser this.userId, () =>
+						(ids[i]=Meteor.call 'deleteOutgoingIntegration', incoming) for incoming, i in @bodyParams.integrationId
+					status: 'success', deleted : @bodyParams.integrationId # need to handle error
+				catch e
+					statusCode: 400  # bad request or other errors
+					body: status: 'yep failled again bad request '+ @bodyParams.integrationId, message: e.name + ' :: ' + e.message
+			else
+				console.log '[restapi] api/outgoingWebhooks -> '.red, "User does not have 'bulk-create-c' permission"
+				statusCode: 403
+				body: status: 'error', message: 'You do not have permission to do this'
+
+### Create a direct message
+user must also have create-c permission
+pass in the data the user to connect to. (  data='{"username":"Joe"}'    )  connects the user who's header auth creditials are be used.
+to connect two users you must issue command as that user.
+###
+RocketChat.API.v1.addRoute 'createDirectMessage', authRequired: true,
+	post: ->
+			try
+				this.response.setTimeout (1000)
+				Meteor.runAsUser this.userId, () =>
+					Meteor.call 'createDirectMessage', @bodyParams.username
+				status: 'success', created : @bodyParams.username
+			catch e
+				statusCode:400 # bad request
+				body: status: 'bad request missing pramas ' + @bodyParams.username, message: e.name + ':: ' + e.message
+


cleanup - remove unneccesary presets on js/jsx remove console.log debugging

--- webpack.config.coffee
+++ webpack.config.coffee
@@ -66,7 +66,7 @@
   }
 }

-console.log externalsIndex
+# console.log externalsIndex

 for userExternal of (userConfig.externals || [])
   key = _.keys(userExternal)[0]
@@ -79,7 +79,7 @@

 loaders = [
   #
-  # DON'T USE THIS  if you do a require(`../css/someFile.css`) that file will fail to load in tests
+  # DON'T WEBPACK CSS.  if you do a require(`../css/someFile.css`) that file will fail to load in tests
   # If you need to include CSS for a component of this lib:
   #   - keep it minimal (let our users style and format)
   #   - please don't use inline styles as they are difficult for our user to override
@@ -88,6 +88,7 @@
   #   test: /\.css$/                         # required to write "require("./someFile.css")"
   #   loader: "style-loader!css-loader"
   # ,
+
   # required for bootstrap icons
   test: /\.(woff|woff2)$/
   loader: "url-loader?prefix=font/&limit=5000&mimetype=application/font-woff"
@@ -121,11 +122,7 @@
   loader: 'coffee-loader'
 ,
   test: /\.jsx?$/,
-  exclude: /node_modules/,
   loader: 'babel-loader',
-  query: {
-    presets:[ 'es2015', 'react', 'stage-2' ]
-  }
 ,
   test: /\.(png|jpg|gif)$/
   loader: 'url-loader?limit=8192' # inline base64 URLs for <=8k images, direct URLs for the rest
@@ -185,5 +182,4 @@
     #    except: ['$super', '$', 'exports', 'require']
   ]

-console.log exports
 module.exports = exports


publish remoteUserIdentifier

publish remoteUserIdentifier

--- lib/assets/javascripts/webrtc_rails/main.js.coffee
+++ lib/assets/javascripts/webrtc_rails/main.js.coffee
@@ -4,6 +4,7 @@
   @CALLING: 2

   myUserIdentifier: null
+  remoteUserIdentifier: null

   onWebSocketConnected: ->
   onWebSocketReconnectingStarted: ->
@@ -40,7 +41,7 @@
       return

     @_isCaller = true
-    @_remoteUserIdentifier = remoteUserIdentifier
+    @remoteUserIdentifier = remoteUserIdentifier
     if !@_peerStarted && @_localStream
       @_sendMessage(
         type: 'call'
@@ -112,7 +113,7 @@
       @_sendValue('setMyToken')
       if @_wantWebRTCReconnecting
         @_wantWebRTCReconnecting = false
-        @call(@_remoteUserIdentifier)
+        @call(@remoteUserIdentifier)

     @_webSocket.onclose = (event) =>
       unless @_isWebSocketReconnectingStarted
@@ -128,7 +129,7 @@
         return

       if eventType != 'myUserIdentifier' && eventType != 'call' && eventType != 'webSocketReconnected'
-        if @_remoteUserIdentifier != event['remoteUserIdentifier']
+        if @remoteUserIdentifier != event['remoteUserIdentifier']
           return

       switch eventType
@@ -144,7 +145,7 @@
             @onWebSocketConnected()
             @_webSocketConnected = true
         when 'webSocketReconnected'
-          if @_hangedUp || @_remoteUserIdentifier != event['remoteUserIdentifier']
+          if @_hangedUp || @remoteUserIdentifier != event['remoteUserIdentifier']
             @_sendMessageToOther(type: 'hangUp', event['remoteUserIdentifier'])
         when 'callFailed'
           @_callAnswerReceived = true
@@ -158,8 +159,8 @@
           else if event['reconnect'] && @_hangedUp
             @_sendMessage(type: 'hangUp')
           else
-            @_remoteUserIdentifier = event['remoteUserIdentifier']
-            @onWebRTCCall(@_remoteUserIdentifier)
+            @remoteUserIdentifier = event['remoteUserIdentifier']
+            @onWebRTCCall(@remoteUserIdentifier)
         when 'hangUp'
           @_callAnswerReceived = true
           @_hangUp()
@@ -211,7 +212,7 @@
     )

   _sendMessage: (message) ->
-    @_sendMessageToOther(message, @_remoteUserIdentifier)
+    @_sendMessageToOther(message, @remoteUserIdentifier)

   _startOutput: (localOutput) ->
     isVideo = (@localOutput? && @localOutput.tagName.toUpperCase() == 'VIDEO')
@@ -303,7 +304,7 @@
     if @_isCaller
       @_webRTCReconnecting = true
       if @_webSocket.readyState == WebSocket.OPEN
-        @call(@_remoteUserIdentifier)
+        @call(@remoteUserIdentifier)
       else
         @_wantWebRTCReconnecting = true


Reset Uglify options

Reset Uglify options

--- src/webpack.config.coffee
+++ src/webpack.config.coffee
@@ -468,9 +468,7 @@
     uglifyOptions:
         output:
             comments: new RegExp("This file is part of #{TITLE}","g") # to keep the banner inserted above
-        mangle:
-            properties:
-                reserved       : ['$super', '$', 'exports', 'require']
+        mangle: true
         compress:
             booleans     : true
             comparisons  : true
@@ -478,7 +476,7 @@
             dead_code    : true
             drop_debugger: true
             evaluate     : true
-            hoist_funs   : true
+            hoist_funs   : false # was true
             if_return    : true
             join_vars    : true
             loops        : true
@@ -486,7 +484,7 @@
             properties   : true
             sequences    : true
             side_effects : true
-            unsafe       : true
+            unsafe       : false # was true
             unused       : true
             warnings     : false


more help organization

more help organization

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -38,7 +38,7 @@
   get_fn_documentation = (fn) ->
     Documentation.get_documentation [fn.module_name, fn.name]

-  help_index = (ctx, fns) ->
+  fn_help_index = (ctx, fns) ->
     docs = _.map fns, (fn, name) ->
       if fn?
         doc = get_fn_documentation fn
@@ -47,32 +47,36 @@
     documented_fns = _.sortBy _.filter(docs, _.identity), 'name'
     DocumentationIndexComponent entries: documented_fns, ctx: ctx

+  Documentation.register_documentation 'imported_context_fns', complete: (ctx, doc) -> fn_help_index ctx, ctx.imported_context_fns
+
   modules.create 'builtins', ({fn, cmd, component_fn, component_cmd}) ->
-    component_cmd 'help', 'Shows this help', (cmd) ->
-      ctx = @
-      if arguments.length > 0
-        if _.isString cmd
-          doc = Documentation.get_documentation cmd
+    help_component = (ctx, cmd) ->
+      if _.isString cmd
+        doc = Documentation.get_documentation cmd
+        if doc?
+          return DocumentationItemComponent {ctx, name: cmd, doc}
+        op = @imported_context_fns[cmd]
+        if op?
+          doc = get_fn_documentation op
           if doc?
             return DocumentationItemComponent {ctx, name: cmd, doc}
-          op = @imported_context_fns[cmd]
-          if op?
-            doc = get_fn_documentation op
-            if doc?
-              return DocumentationItemComponent {ctx, name: cmd, doc}
-        else if cmd?._lead_context_name
-          name = cmd._lead_context_name
-          if cmd._lead_context_fn?
-            doc = get_fn_documentation cmd._lead_context_fn
-            return DocumentationItemComponent {ctx, name, doc}
-          else
-            fns = _.object _.map cmd, (v, k) -> [k, v._lead_context_fn]
-        unless fns?
-          # TODO shouldn't be pre
-          return PreComponent value: "Documentation for #{cmd} not found."
+      else if cmd?._lead_context_name
+        name = cmd._lead_context_name
+        if cmd._lead_context_fn?
+          doc = get_fn_documentation cmd._lead_context_fn
+          return DocumentationItemComponent {ctx, name, doc}
+        else
+          fns = _.object _.map cmd, (v, k) -> [k, v._lead_context_fn]
+          fn_help_index ctx fns
+
+      # TODO shouldn't be pre
+      return PreComponent value: "Documentation for #{cmd} not found."
+
+    component_cmd 'help', 'Shows this help', (cmd) ->
+      if arguments.length > 0
+        help_component @, cmd
       else
-        fns = @imported_context_fns
-      help_index @, fns
+        help_component @, 'imported_context_fns'

     KeySequenceComponent = React.createClass
       render: -> React.DOM.span {}, _.map @props.keys, (k) -> React.DOM.kbd {}, k


off test

off test

--- coffee/tests/routes.spec.coffee
+++ coffee/tests/routes.spec.coffee
@@ -3,24 +3,24 @@
 describe(
 	"Routes tests"
 	->
-		it("should respond with error 404", (done) ->
-			request("http://localhost:9999/404", (error, response, body) ->
-				expect(response.statusCode).toEqual 404
-				do done
-			)
-		)
-
-		it("should respond with 'Получить токен'", (done) ->
-			request("http://localhost:9999/", (error, response, body) ->
-				expect(response.statusCode).toEqual 200
-				do done
-			)
-		)
-
-		it("should respond with 'Получить токен'", (done) ->
-			request("http://localhost:9999/setstatus", (error, response, body) ->
-				expect(response.statusCode).toEqual 200
-				do done
-			)
-		)
+		# it("should respond with 404", (done) ->
+		# 	request("http://localhost:9999/404", (error, response, body) ->
+		# 		expect(response.statusCode).toEqual 404
+		# 		do done
+		# 	)
+		# )
+		#
+		# it("should respond with 200", (done) ->
+		# 	request("http://localhost:9999/", (error, response, body) ->
+		# 		expect(response.statusCode).toEqual 200
+		# 		do done
+		# 	)
+		# )
+		#
+		# it("should respond with 200", (done) ->
+		# 	request("http://localhost:9999/setstatus", (error, response, body) ->
+		# 		expect(response.statusCode).toEqual 200
+		# 		do done
+		# 	)
+		# )
 )


Cleanup global code a bit

Cleanup global code a bit

--- www/js/globals.coffee
+++ www/js/globals.coffee
@@ -1,4 +1,4 @@
-define ['zepto', 'localforage', 'moment'], ($, localForage, moment, UserCollection) ->
+define ['zepto', 'localforage', 'moment'], ($, localForage, moment) ->
   mapID = "tofumatt.map-tdyvgkb6"
   # mapID = "mozilla-webprod.g7in06ib"

@@ -16,6 +16,7 @@
         "WebkitOverflowScrolling" in window.document.createElement("div").style
       )()
     HOUR: 3600
+
     # These are the officially supported locales for around. If the user's UA
     # returns a separate locale (or one that doesn't fallback to one like en-ca
     # might fallback to en-us), the "default" locale (of en-US) will be used.
@@ -25,7 +26,6 @@
     ]
     MAP_ID: mapID
     MAP_URL: "http://a.tiles.mapbox.com/v3/#{mapID}/"
-    MAX_DOWNLOADS: 2 # Maximum number of podcast downloads at one time.
     MINUTE: 60
     RECENT_CHECKIN_TIME: 120 # Consider checkins less than two hours old to still be good (i.e. the user is still at that venue). TODO: Tweak this for venue type.
     REDIRECT_URL: "#{window.location.protocol}//#{window.location.host}#{window.location.pathname}" # This needs to be registered as an authorized redirect URI in this app's settings on http://developer.foursquare.com
@@ -36,8 +36,7 @@

   # Determine the locale; fallback to en-US if not available.
   # HACK: Better way for this, I assume?
-  GLOBALS.LANGUAGE = window.navigator.language
-  GLOBALS.LANGUAGE = 'en-US' unless _.contains GLOBALS.LOCALES, GLOBALS.LANGUAGE
+  GLOBALS.LANGUAGE = if _.contains(GLOBALS.LOCALES, GLOBALS.LANGUAGE) then window.navigator.language else 'en-US'

   # We want the moment library available everywhere, especially inside
   # templates.


Reword description in comment

Reword description in comment

--- grammars/roff.cson
+++ grammars/roff.cson
@@ -141,7 +141,7 @@
 			patterns: [{include: "#param-group"}]
 		},{

-			# .class: Assign name "X" to a list of characters. Ranges can be passed like "a-z"
+			# .class: Assign a name to a range of characters (GNU)
 			name: "meta.function.request.assign-class.gnu.roff"
 			begin: "^([.'])[ \t]*(class)\\b[ \t]*(\\S+)?"
 			end:   "(?<!\\\\)(?=$)|(?=\\\\\")"


Cleanup: unused param removed

Cleanup: unused param removed

--- client/Login/AppController.coffee
+++ client/Login/AppController.coffee
@@ -27,7 +27,7 @@
   appIsShown: (params)->
     @handleRoute "/#{params.label}"

-  handleQuery: (label)->
+  handleQuery: ->
     {currentPath} = KD.getSingleton 'router'
     @handleRoute currentPath


limit line length

limit line length

--- client/app/views/home_view.coffee
+++ client/app/views/home_view.coffee
@@ -89,7 +89,8 @@
             @frames.append appIframeTemplate(id: slug, hash:hash)
             frame = @$("##{slug}-frame")
             $(frame.prop('contentWindow')).on 'hashchange', ->
-                newhash = frame.prop('contentWindow').location.hash.replace '#', ''
+                location = frame.prop('contentWindow').location
+                newhash = location.hash.replace '#', ''
                 app?.routers.main.navigate "/apps/#{slug}/#{newhash}", false

         @content.hide()


RTM: Todo for error handling in auth.

RTM: Todo for error handling in auth.

--- client/ide/RealTimeManager.coffee
+++ client/ide/RealTimeManager.coffee
@@ -22,6 +22,7 @@
       url: '/-/google-api',
       dataType: 'JSON'
       success: (authToken) =>
+        # TODO: Error handling please
         gapi.load 'client', =>
           gapi.client.load 'drive', 'v2', =>
             gapi.load 'auth:client,drive-realtime,drive-share', =>


Disable open an existing file and save test

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -83,32 +83,32 @@
     browser.end()

-  openAnExistingFileAndSave: (browser) ->
+  # openAnExistingFileAndSave: (browser) ->

-    activeEditorSelector = '.pane-wrapper .kdsplitview-panel.panel-1 .kdtabpaneview.active'
+  #   activeEditorSelector = '.pane-wrapper .kdsplitview-panel.panel-1 .kdtabpaneview.active'

-    user = helpers.beginTest(browser)
-    helpers.waitForVMRunning(browser)
+  #   user = helpers.beginTest(browser)
+  #   helpers.waitForVMRunning(browser)

-    text     = helpers.getFakeText()
-    fileName = ideHelpers.createAndSaveNewFile(browser, text)
+  #   text     = helpers.getFakeText()
+  #   fileName = ideHelpers.createAndSaveNewFile(browser, text)

-    ideHelpers.closeFile(browser, fileName)
-    ideHelpers.openAnExistingFile(browser, user, fileName, text)
+  #   ideHelpers.closeFile(browser, fileName)
+  #   ideHelpers.openAnExistingFile(browser, user, fileName, text)

-    text = helpers.getFakeText()
+  #   text = helpers.getFakeText()

-    ideHelpers.setTextToEditor(browser, text)
+  #   ideHelpers.setTextToEditor(browser, text)

-    browser
-      .assert.containsText    activeEditorSelector, text # Assertion
+  #   browser
+  #     .assert.containsText    activeEditorSelector, text # Assertion

-    ideHelpers.saveFile(browser)
-    ideHelpers.closeFile(browser, fileName)
-    ideHelpers.openAnExistingFile(browser, user, fileName, text)
+  #   ideHelpers.saveFile(browser)
+  #   ideHelpers.closeFile(browser, fileName)
+  #   ideHelpers.openAnExistingFile(browser, user, fileName, text)

-    browser
-      .assert.containsText    activeEditorSelector, text # Assertion
+  #   browser
+  #     .assert.containsText    activeEditorSelector, text # Assertion

-    browser.end()
+  #   browser.end()


Tests: IDE: Search: Fix findInFIles test.

--- client/test/lib/ide/search.coffee
+++ client/test/lib/ide/search.coffee
@@ -16,7 +16,7 @@
     matchedWordSelector         = '.content-search-result pre p.match'
     activeUntitledFileSelector  = "#{paneSelector} .untitledtxt.active"
     activeEditorSelector        = '.pane-wrapper .kdsplitview-panel.panel-1 .kdtabpaneview.active .ace_content'
-    fileNameSelector            = '.content-search-result .filename:first-child'
+    fileNameSelector            = '.content-search-result .filename:first-child > span'

     ideHelpers.openNewFile(browser)
     ideHelpers.openContextMenu(browser)
@@ -25,7 +25,7 @@
       .waitForElementVisible     findInFilesSelector, 20000
       .click                     findInFilesSelector
       .waitForElementVisible     contentSearchModalSelector, 20000
-      .setValue                  contentSearchModalSelector + ' input[name=findInput]', 'from'
+      .setValue                  contentSearchModalSelector + ' input[name=findInput]', 'Hello World from Python by Koding'
       .click                     contentSearchModalSelector + ' button.search'
       .waitForElementVisible     paneSelector + ' .search-result', 20000 # Assertion
       .waitForElementNotPresent  activeUntitledFileSelector, 20000 # Assertion
@@ -37,7 +37,7 @@

         return browser.end() unless lowercase

-        tabHandleSelector = ".panel-1 div[title='/home/#{user.username}/Web/#{fileName}']"
+        tabHandleSelector = ".panel-1 div[data-file-path='/home/#{user.username}/Web/#{fileName}']"
         editorSelector    = ".panel-1 .kdtabpaneview.active.#{lowercase} .ace_content"

         browser
@@ -46,6 +46,7 @@
           .waitForElementNotPresent  paneSelector + '.search-result .active', 20000 # Assertion
           .waitForElementVisible     tabHandleSelector, 20000
           .waitForElementVisible     editorSelector, 20000
+          .pause                     5000 # wait for content
           .assert.containsText       editorSelector, 'Hello World from Python by Koding'
           .end()