Alter var name.

Alter var name.

--- tests/specs/SuperfishTest.coffee
+++ tests/specs/SuperfishTest.coffee
@@ -3,11 +3,11 @@
 describe "Superfish", ->

   $menu = null
-  $liHasUl = null
+  $li_with_sub = null

   beforeEach ->
     $menu = $('ul.sf-menu')
-    $liHasUl = $menu.find('li:has(ul):first')
+    $li_with_sub = $menu.find('li:has(ul):first')
     $menu.superfish()

   afterEach ->
@@ -41,10 +41,10 @@
   describe "method access", ->

     it "'show' method should exist", ->
-      expect($liHasUl.superfish('show')).toBeDefined()
+      expect($li_with_sub.superfish('show')).toBeDefined()

     it "'hide' method should exist", ->
-      expect($liHasUl.superfish('hide')).toBeDefined()
+      expect($li_with_sub.superfish('hide')).toBeDefined()

     it "'destroy' method should exist", ->
       expect($menu.superfish('destroy')).toBeDefined()
@@ -57,7 +57,7 @@

     it "should not throw an error when accessing a valid method", ->
       expect( ->
-        $liHasUl.superfish('show')
+        $li_with_sub.superfish('show')
       ).not.toThrow new Error('Method show does not exist on jQuery.fn.superfish')

@@ -74,31 +74,31 @@
   describe "'show' method", ->
     it "should fail silently if Superfish is uninitialised", ->
       $menu.superfish('destroy')
-      expect( $liHasUl.superfish('show') ).toBe 'li'
+      expect( $li_with_sub.superfish('show') ).toBe 'li'

     it "should cause child ul to be visible", ->
-      $submenu = $liHasUl.children('ul')
+      $submenu = $li_with_sub.children('ul')
       expect($submenu).toBeHidden()
-      expect($liHasUl).not.toBe '.sfHover'
-      $liHasUl.superfish('show')
+      expect($li_with_sub).not.toBe '.sfHover'
+      $li_with_sub.superfish('show')
       expect($submenu).toBeVisible()
-      expect($liHasUl).toBe '.sfHover'
+      expect($li_with_sub).toBe '.sfHover'

   describe "'hide' method", ->
     it "should fail silently if Superfish is uninitialised", ->
       $menu.superfish('destroy')
-      expect( $liHasUl.superfish('hide') ).toBe 'li'
+      expect( $li_with_sub.superfish('hide') ).toBe 'li'

     it "should cause child ul to be hidden", ->
-      $submenu = $liHasUl.children('ul')
-      $liHasUl.superfish('show')
-      expect($liHasUl).toBe '.sfHover'
+      $submenu = $li_with_sub.children('ul')
+      $li_with_sub.superfish('show')
+      expect($li_with_sub).toBe '.sfHover'
       expect($submenu).toBeVisible()
       # do an instant hide for now until I figure out why .toBeHidden fails when animated
-      $liHasUl.superfish('hide', true)
+      $li_with_sub.superfish('hide', true)
       expect($submenu).toBeHidden()
-      expect($liHasUl).not.toBe '.sfHover'
+      expect($li_with_sub).not.toBe '.sfHover'

   describe "initialisation", ->
@@ -122,4 +122,4 @@
       $menu.superfish('destroy')
       $menu.superfish
         pathClass: 'current'
-      expect($liHasUl).toBe '.sfHover'
+      expect($li_with_sub).toBe '.sfHover'


migrate the customizations to json permanently

--- @client/customizations.coffee
+++ @client/customizations.coffee
@@ -31,7 +31,6 @@
   __convert obj, []

 __convert = (obj, path) ->
-
   if Array.isArray(obj)
     return (__convert(vv, path) for vv in obj)

@@ -70,7 +69,7 @@
       new Function(subdomain.customization_obj)() # will create window.customization_obj
       stringified = convert_customization window.customization_obj

-      if false && fetch('/current_user').is_super_admin
+      if fetch('/current_user').is_super_admin
         subdomain.customizations = JSON.stringify stringified, null, 2
         save subdomain
     else


drag and drop for changing tags

drag and drop for changing tags

--- @client/dashboard/tags.coffee
+++ @client/dashboard/tags.coffee
@@ -40,7 +40,7 @@
             all_tags[tag][false] = []

     for user in users.users
-
+      user = fetch(user.key or user) # subscribe to changes
       for vals in tags_config
         tag = vals.key
         if tag not of user.tags || user.tags[tag] == "" || user.tags[tag] == 'undefined'
@@ -302,36 +302,71 @@

             else
               for v,users of vals
-
-                DIV
-                  style:
-                    marginBottom: 18
-
-                  DIV
-                    style:
-                      fontWeight: 700
-                      marginRight: 8
-
-                    v
-
-                  UL
-                    style:
-                      listStyle: 'none'
-                      display: 'inline'
-                      fontSize: 0
-                      lineHeight: 0
-
-                    for user in users
-                      do (user) =>
-                        Avatar
-                          key: user.key
-                          style:
-                            width: 40
-                            height: 40
-                            cursor: 'pointer'
-                          alt: "<user>: #{v}"
-                          onClick: => change_selected_user user.key
-
+                do (tag, v) =>
+                  DIV
+                    style:
+                      marginBottom: 18
+                    onKeyDown: (e) =>
+                      if e.which == 18 # alt/opt
+                        @local.control_depressed = true
+                    onKeyUp: (e) =>
+                      @local.control_depressed = false
+
+                    onDragEnter: (e) =>
+                      if @local.control_depressed
+                        e.preventDefault()
+                    onDragOver: (e) =>
+                      if @local.control_depressed
+                        e.preventDefault()
+                    onDragLeave: (e) =>
+                      if @local.control_depressed
+                        e.preventDefault()
+                    onDrop: (e) =>
+                      if @local.control_depressed
+                        e.preventDefault()
+                        user = fetch e.dataTransfer.getData("text/plain")
+                        user.tags[tag] = v
+                        save user
+
+
+                    DIV
+                      style:
+                        fontWeight: 700
+                        marginRight: 8
+
+                      v
+
+                    UL
+                      style:
+                        listStyle: 'none'
+                        display: 'inline'
+                        fontSize: 0
+                        lineHeight: 0
+
+                      for user in users
+                        do (user) =>
+                          SPAN
+                            draggable: true
+                            onDragStart: (ev) =>
+                              if @local.control_depressed
+                                ev.dataTransfer.setData("text/plain", user.key)
+                            style:
+                              width: 40
+                              height: 40
+                              cursor: 'move'
+
+                            Avatar
+                              key: user.key
+                              style:
+                                width: 40
+                                height: 40
+                              alt: "<user>: #{v}"
+                              hide_popover: true
+                              onClick: (e) =>
+                                if !@local.control_depressed
+                                  change_selected_user user.key
+                                  e.stopPropagation()
+                                  e.preventDefault()


Test the the correct Error message is thrown

--- test/mocha/kissmetrics.coffee
+++ test/mocha/kissmetrics.coffee
@@ -70,10 +70,10 @@
 		km = new KM 'apiKey', 'evan@example.com'
 		delete km.apiKey

-		(-> km.record 'foo').should.throw()
+		(-> km.record 'foo').should.throw "API key required"

 	it 'should require person', ->
 		km = new KM 'apiKey', 'evan@example.com'
 		delete km.person

-		(-> km.record 'foo').should.throw()
+		(-> km.record 'foo').should.throw "Person required"


smarter destroy logic

smarter destroy logic

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -741,7 +741,12 @@
       if $
         $.data @el, 'oriDomi', null
       # Remove the oriDomi element from the DOM.
-      @el.parentNode.removeChild @el
+      @el.innerHTML = @cleanEl.innerHTML
+
+      # Reset original styles.
+      for key, value of @originalStyles
+        @el.style[key] = value
+
       # Free up this instance for garbage collection.
       instances[instances.indexOf @] = null
       callback() if typeof callback is 'function'


save copy of original styles

save copy of original styles

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -448,6 +448,13 @@

     # Add a special class to the target element.
     @el.classList.add @settings.oriDomiClass
+
+    # Before overriding styles, save copies of their original values should
+    # the user later call `destroy()`.
+    @originalStyles = {}
+    for key in ['padding', 'width', 'height', 'backgroundColor', 'backgroundImage', 'border', 'outline']
+      @originalStyles[key] = elStyle[key]
+
     # Remove its padding and set a fixed width and height.
     @el.style.padding = '0'
     @el.style.width = @width + 'px'


replaced silent with devMode, added function to trigger devMode

--- oriDomi.coffee
+++ oriDomi.coffee
@@ -11,7 +11,7 @@

 root = window
 $ = root.$ or false
-silent = false
+devMode = false
 oriDomiSupport = true
 testEl = document.createElement 'div'
 prefixList = ['Webkit', 'Moz', 'O', 'ms', 'Khtml']
@@ -37,7 +37,7 @@
 for key, value of css
   css[key] = testProp value
   if !css[key]
-    console?.warn 'oriDomi: Browser does not support oriDomi'
+    devMode and console.warn 'oriDomi: Browser does not support oriDomi'
     oriDomiSupport = false
     break

@@ -74,7 +74,7 @@
 # one dimensional:
 extendObj = (target, source) ->
   if source isnt Object source
-    !silent and console?.warn 'oriDomi: Must pass an object to extend with'
+    devMode and console.warn 'oriDomi: Must pass an object to extend with'
     return target
   if target isnt Object target
     target = {}
@@ -93,7 +93,6 @@
   hardShading: false
   speed: .6
   oriDomiClass: 'oriDomi'
-  silent: false
   smoothStart: true
   shadingIntensity: 1
   easingMethod: ''
@@ -102,19 +101,19 @@
   forceAntialiasing: false

-class root.OriDomi
+class OriDomi

   constructor: (@el, options) ->
-    console.time 'oridomiConstruction'
+    devMode and console.time 'oridomiConstruction'
     return @el if !oriDomiSupport

     if !(@ instanceof OriDomi)
       return new oriDomi @el, @settings

     @settings = extendObj options, defaults
-    silent = true if @settings.silent
+
     if !@el? or @el.nodeType isnt 1
-      return !silent and console?.warn 'oriDomi: First argument must be a DOM element'
+      return devMode and console.warn 'oriDomi: First argument must be a DOM element'

     {@shading, @shadingIntensity, @vPanels, @hPanels} = @settings
     @$el = $ @el if $
@@ -314,7 +313,7 @@
       @el.style.visibility = 'visible'

     @_callback @settings
-    console.timeEnd 'oridomiConstruction'
+    devMode and console.timeEnd 'oridomiConstruction'

   _callback: (options) ->
@@ -351,10 +350,10 @@
     if isNaN angle
       0
     else if angle > 90
-      !silent and console?.warn 'oriDomi: Maximum value is 90'
+      devMode and console.warn 'oriDomi: Maximum value is 90'
       90
     else if angle < -90
-      !silent and console?.warn 'oriDomi: Minimum value is -90'
+      devMode and console.warn 'oriDomi: Minimum value is -90'
       -90
     else
       angle
@@ -548,7 +547,10 @@

-
+OriDomi.devMode = ->
+  devMode = true
+
+root.OriDomi = OriDomi

 # $ BRIDGE
@@ -560,13 +562,13 @@
     if typeof options is 'string'

       if typeof OriDomi::[options] isnt 'function'
-        return !silent and console?.warn "oriDomi: No such method '#{options}'"
+        return devMode and console.warn "oriDomi: No such method '#{ options }'"

       for el in @
         instance = $.data el, 'oriDomi'

         if not instance?
-          return !silent and console?.warn "oriDomi: Can't call #{options}, oriDomi hasn't been initialized on this element"
+          return devMode and console.warn "oriDomi: Can't call #{ options }, oriDomi hasn't been initialized on this element"

         args = Array::slice.call arguments
         args.shift()


simpler bypass of new-less constructor invocations

--- oridomi.coffee
+++ oridomi.coffee
@@ -430,8 +430,8 @@

   constructor: (@el, options = {}) ->
     return unless isSupported
-    # Prevent constructor calls made without `new`.
-    return new OriDomi arguments... unless @ instanceof OriDomi
+    # Fix constructor calls made without `new`.
+    return new OriDomi @el, options unless @ instanceof OriDomi
     # Support selector strings as well as elements.
     @el = document.querySelector @el if typeof @el is 'string'
     # Make sure element is valid.


Fixed controller#redirectTo; now properly supports forwarding options.

--- src/chaplin/controllers/controller.coffee
+++ src/chaplin/controllers/controller.coffee
@@ -36,9 +36,9 @@
     # -----------

     # Redirect to URL.
-    redirectTo: (url, params, options) ->
+    redirectTo: (url, options) ->
       @redirected = true
-      @publishEvent '!router:route', url, {}, (routed) ->
+      @publishEvent '!router:route', url, options, (routed) ->
         unless routed
           throw new Error 'Controller#redirectTo: no route matched'


Test a few cases where chapter and/or section is 0

--- test/task-helpers.spec.coffee
+++ test/task-helpers.spec.coffee
@@ -29,4 +29,7 @@
   it 'converts chapter_sections to numbers', ->
     expect(Helpers.chapterSectionToNumber([1])).to.equal(100)
     expect(Helpers.chapterSectionToNumber([1, 2])).to.equal(102)
+    expect(Helpers.chapterSectionToNumber([1, 0])).to.equal(100)
+    expect(Helpers.chapterSectionToNumber([1, 0, 8])).to.equal(10008)
+    expect(Helpers.chapterSectionToNumber([0, 1])).to.equal(1)
     expect(Helpers.chapterSectionToNumber([31, 88, 42])).to.equal(318842)


fix(view-on-github): Import Actions correctly

--- internal_packages/message-view-on-github/lib/view-on-github-button.cjsx
+++ internal_packages/message-view-on-github/lib/view-on-github-button.cjsx
@@ -1,6 +1,6 @@
 {shell} = require 'electron'
 GithubStore = require('./github-store').default
-{React} = require 'nylas-exports'
+{React, Actions} = require 'nylas-exports'
 {RetinaImg, KeyCommandsRegion} = require 'nylas-component-kit'

 ###


activity: pass private message recipients as a separate option

--- client/Social/Activity/views/privatemessagemodal.coffee
+++ client/Social/Activity/views/privatemessagemodal.coffee
@@ -63,19 +63,13 @@

   submitMessage : ->

-    {body} = @modalTabs.forms.Message.inputs
-    {send} = @modalTabs.forms.Message.buttons
-    val    = body.getValue()
-
+    body       = @modalTabs.forms.Message.inputs.body.getValue()
+    {send}     = @modalTabs.forms.Message.buttons
     recipients = (nickname for {profile:{nickname}} in @autoComplete.getSelectedItemData())
-    recipients = recipients.map (recipient)-> "@#{recipient}"
-    recipients = recipients.join ' '
-
-    val = "#{val} // #{recipients}"

     {router, socialapi, notificationController} = KD.singletons

-    socialapi.message.sendPrivateMessage body : val, (err, channels) =>
+    socialapi.message.sendPrivateMessage {body, recipients}, (err, channels) =>

       send.hideLoader()


Activity: Adding a off page sidebar privatemessage item once created the right way

--- client/Social/Activity/views/privatemessagemodal.coffee
+++ client/Social/Activity/views/privatemessagemodal.coffee
@@ -72,17 +72,15 @@

       return KD.showError err  if err

-      [channel] = channels
-      @getDelegate()._lastMessage = null
+      [channel]            = channels
+      appView              = @getDelegate()
+      {sidebar}            = appView
+      appView._lastMessage = null

-      notificationController.emit 'AddedToChannel', channel
+      sidebar.addToChannel channel
+      router.handleRoute "/Activity/Message/#{channel.id}"

-      # fixme:
-      # can't navigate to newly created conversation
-      # temporary fixed with a timeout
-      KD.utils.wait 1000, =>
-        router.handleRoute "/Activity/Message/#{channel.id}"
-        @destroy()
+      @destroy()

   createUserAutoComplete: ->


making the edit click work

making the edit click work

--- client/app/CommonViews/replies/replylistitemview.coffee
+++ client/app/CommonViews/replies/replylistitemview.coffee
@@ -66,23 +66,7 @@

         # push that into the click even , destroy if exists, create if not

-        @editForm = new OpinionFormView
-          title : "edit-opinion"
-          cssClass : "edit-opinion-form hidden opinion-container"
-          callback : (data)=>
-            @getData().modify data, (err, opinion) =>
-              callback? err, opinion
-              if err
-                new KDNotificationView title : "Your changes weren't saved.", type :"mini"
-              else
-                # new KDNotificationView title : "modified!"
-                @emit "OwnOpinionWasAdded", opinion
-                @editForm.setClass "hidden"
-        , data

-        log "adding editform"
-        @addSubView @editForm, "p.opinion-body-edit", yes
-        log @editForm

         @editLink.unsetClass "hidden"

@@ -90,12 +74,26 @@
           KDEventTypes       : "click"
           listenedToInstance : @editLink
           callback           : =>
-            log "clicked me", @editForm
-            if @editForm.$().hasClass "hidden"
-              log "has hidden"
-              @editForm.unsetClass "hidden"
+            if @editForm?
+              @editForm?.destroy()
+              delete @editForm
             else
-              @editForm.setClass "hidden"
+              # only works once, why!?
+              @editForm = new OpinionFormView
+                title : "edit-opinion"
+                cssClass : "edit-opinion-form opinion-container"
+                callback : (data)=>
+                  @getData().modify data, (err, opinion) =>
+                    callback? err, opinion
+                    if err
+                      new KDNotificationView title : "Your changes weren't saved.", type :"mini"
+                    else
+                      # new KDNotificationView title : "modified!"
+                      @emit "OwnOpinionWasAdded", opinion
+                      @editForm.setClass "hidden"
+              , data
+
+              @addSubView @editForm, "p.opinion-body-edit", yes

         @deleteLink.unsetClass "hidden"
         @listenTo


Update notification messages realted to adding/editing IRs

--- client/controllers/incidentReports/incidentModal.coffee
+++ client/controllers/incidentReports/incidentModal.coffee
@@ -70,7 +70,7 @@
           errorString = error.reason
           if error.details[0].name is 'locations' and error.details[0].type is 'minCount'
             errorString = 'You must specify at least one loction'
-          toastr.error(errorString)
+          notify('error', errorString)

     if @edit
       incident._id = @incident._id
@@ -81,7 +81,7 @@
         if not error
           $('.reactive-table tr').removeClass('open')
           $('.reactive-table tr.details').remove()
-          notify('success', 'Incident report added to event')
+          notify('success', 'Incident report updated')
           Modal.hide('incidentModal')
         else
           toastr.error(error.reason)


improve error logging

improve error logging

--- client/controllers/incidentReports/incidentModal.coffee
+++ client/controllers/incidentReports/incidentModal.coffee
@@ -46,7 +46,10 @@
     # button's default is prevented
     $('#add-incident').submit()
     if not instance.valid.get()
-      console.error "Invalid incident form"
+      console.error "Invalid incident form fields: " + _.map(
+        instance.$('#add-incident .has-error label:first-child'),
+        (x)-> $(x).text()
+      ).join(', ')
       return
     duplicate = $(event.target).hasClass('save-modal-duplicate')
     form = instance.$('form')[0]


NFinderItem: Set indentations

NFinderItem: Set indentations

--- client/finder/lib/filetree/itemviews/nfinderitem.coffee
+++ client/finder/lib/filetree/itemviews/nfinderitem.coffee
@@ -1,15 +1,15 @@
-kd = require 'kd'
-JTreeItemView = kd.JTreeItemView
-KDProgressBarView = kd.ProgressBarView
-FSHelper = require 'app/util/fs/fshelper'
-NBrokenLinkItemView = require './nbrokenlinkitemview'
-NFileItemView = require './nfileitemview'
+kd                    = require 'kd'
+JTreeItemView         = kd.JTreeItemView
+KDProgressBarView     = kd.ProgressBarView
+FSHelper              = require 'app/util/fs/fshelper'
+NBrokenLinkItemView   = require './nbrokenlinkitemview'
+NFileItemView         = require './nfileitemview'
 NFinderItemDeleteView = require '../itemsubviews/nfinderitemdeleteview'
 NFinderItemRenameView = require '../itemsubviews/nfinderitemrenameview'
-NFolderItemView = require './nfolderitemview'
-NMachineItemView = require './nmachineitemview'
-NMountItemView = require './nmountitemview'
-NSectionItemView = require './nsectionitemview'
+NFolderItemView       = require './nfolderitemview'
+NMachineItemView      = require './nmachineitemview'
+NMountItemView        = require './nmountitemview'
+NSectionItemView      = require './nsectionitemview'

 module.exports = class NFinderItem extends JTreeItemView
@@ -92,7 +92,8 @@

   showRenameView:(callback)->

-    return if @renameView
+    return  if @renameView
+
     @setClass "being-edited"
     @beingEdited = yes
     @callback = callback


Fixes for signup on double click

Fixes for signup on double click

--- client/landing/site.landing/coffee/login/AppView.coffee
+++ client/landing/site.landing/coffee/login/AppView.coffee
@@ -300,12 +300,6 @@
       itemClass : KDCustomHTMLView
       partial   : email

-    handleKeyup = (event) =>
-
-      return  unless event.which is ENTER
-      @signupModal.modalTabs.forms.extraInformation.buttons.continue.click()
-
-
     fields.username =
       name               : 'username'
       itemClass          : LoginInputView
@@ -318,7 +312,6 @@
         attributes       :
           testpath       : 'register-form-username'
         focus            : -> @parent.icon.unsetTooltip()
-        keyup            : handleKeyup
         validate         :
           container      : this
           rules          :
@@ -347,13 +340,11 @@
       fields.firstName =
         defaultValue : givenName
         label        : 'First Name'
-        keyup        : handleKeyup

     if familyName
       fields.lastName =
         defaultValue : familyName
         label        : 'Last Name'
-        keyup        : handleKeyup

     @signupModal = new KDModalViewWithForms
@@ -364,25 +355,14 @@
       tabs                            :
         forms                         :
           extraInformation            :
-            callback                  : =>
-
-              {username, firstName, lastName} = @signupModal.modalTabs.forms.extraInformation.inputs
-
-              if USERNAME_VALID and username.input.valid
-                formData.username        = username.input.getValue()
-                formData.passwordConfirm = formData.password
-                formData.firstName = firstName?.getValue()
-                formData.lastName = lastName?.getValue()
-
-                @doRegister formData, @registerForm
-                @signupModal.destroy()
-
+            callback                  : @bound "checkBeforeRegister"
             fields                    : fields
             buttons                   :
               continue                :
                 title                 : 'LET\'S GO'
                 style                 : 'solid green medium'
                 type                  : 'submit'
+    @signupModal.setOption 'userData', formData

     usernameView = @signupModal.modalTabs.forms.extraInformation.inputs.username
     usernameView.setOption 'stickyTooltip', yes
@@ -395,11 +375,14 @@

     @signupModal.once 'KDObjectWillBeDestroyed', =>
       KD.utils.killWait usernameCheckTimer
+      usernameCheckTimer = null
+
       mainView.unsetClass 'blur'
       form.button.hideLoader()
       form.email.icon.unsetTooltip()
       form.password.icon.unsetTooltip()
       usernameView.icon.unsetTooltip()
+
       @signupModal = null

     @signupModal.once 'viewAppended', =>
@@ -413,10 +396,12 @@
   usernameCheckTimer = null

   usernameCheck:(input, event, delay=800)->
+
     return if event?.which is 9
     return if input.getValue().length < 4

     KD.utils.killWait usernameCheckTimer
+    usernameCheckTimer = null
     input.setValidationResult "usernameCheck", null
     username = input.getValue()

@@ -428,13 +413,19 @@
           xhrFields   : withCredentials : yes
           success     : =>

-            return  if not @signupModal
+            usernameCheckTimer = null
+            return  unless @signupModal?

             input.setValidationResult 'usernameCheck', null
             USERNAME_VALID = yes
+
+            if @signupModal.getOption 'pendingSubmit'
+              @checkBeforeRegister()
+
           error       : ({responseJSON}) =>

-            return  if not @signupModal
+            usernameCheckTimer = null
+            return  unless @signupModal?

             {forbidden, kodingUser} = responseJSON

@@ -448,6 +439,8 @@
               input.setValidationResult "usernameCheck", "Sorry, there is a problem with \"#{username}\"!"
               USERNAME_VALID = no

+            @signupModal.unsetOption 'pendingSubmit'
+

   changeButtonState: (button, state) ->

@@ -459,6 +452,28 @@
       button.setClass 'red'
       button.unsetClass 'green'
       button.disable()
+
+
+  checkBeforeRegister: ->
+
+    return  unless @signupModal?
+
+    {username, firstName, lastName} = @signupModal.modalTabs.forms.extraInformation.inputs
+
+    @unsetOption 'pendingSubmit'
+
+    if USERNAME_VALID and username.input.valid
+      formData = @signupModal.getOption "userData"
+
+      formData.username        = username.input.getValue()
+      formData.passwordConfirm = formData.password
+      formData.firstName       = firstName?.getValue()
+      formData.lastName        = lastName?.getValue()
+
+      @doRegister formData, @registerForm
+      @signupModal.destroy()
+    else if usernameCheckTimer?
+      @signupModal.setOption 'pendingSubmit', yes

   doRegister: (formData, form) ->


allow users to open tools in a new tab - fixes #272

--- client/code/view/dataset/tools.coffee
+++ client/code/view/dataset/tools.coffee
@@ -32,9 +32,11 @@
     @

   toolClick: (e) ->
-    $('.tool.active').removeClass("active")
-    $(e.currentTarget).addClass("active")
-    e.preventDefault()
+    # Do nothing if cmd or ctrl keys are held down (allows cmd-click for new tabs).
+    # The global link handler in app.coffee will deal with navigation.
+    unless e.metaKey or e.ctrlKey
+      $('.tool.active').removeClass("active")
+      $(e.currentTarget).addClass("active")

   addToolArchetype: (toolModel) ->
     # The setTimeout thing is because we can't work out Backbone (Relational) model loading:


Pass self as argument vs evaluation context

--- client/lanes/lib/ModuleSupport.coffee
+++ client/lanes/lib/ModuleSupport.coffee
@@ -18,5 +18,5 @@
                 _.extend(@::[key], value)
             else
                 @::[key] = value
-        obj.included?.apply(@)
+        obj.included?(@)
         this


Allow softRemove and restore to use any selector inside stubs

Fixes #8.

--- softremovable.coffee
+++ softremovable.coffee
@@ -140,7 +140,7 @@
   @collection.softRemove = (selector, callback) ->
     return 0 unless selector

-    if Meteor.isClient and not isLocalCollection
+    if Meteor.isClient and not Meteor.isSimulation and not isLocalCollection
       throwIfSelectorIsntId selector, 'softRemove'

     modifier =
@@ -162,7 +162,7 @@
   @collection.restore = (selector, callback) ->
     return 0 unless selector

-    if Meteor.isClient and not isLocalCollection
+    if Meteor.isClient and not Meteor.isSimulation and not isLocalCollection
       throwIfSelectorIsntId selector, 'restore'

     modifier =


Add a success message when saving a user

--- client/source/views/users/edit.coffee
+++ client/source/views/users/edit.coffee
@@ -34,7 +34,8 @@
       data.roles = _.reject data.roles, (r)->
         r.name =='administrator'

-    @submit @model.save(data, wait: yes)
+    @submit(@model.save(data, wait: yes)).done ->
+      toastr.success "Saved #{@model.get('name')}"

   clickRemove: (e) ->
     e.preventDefault()


Aligned indentation with rest of module

Aligned indentation with rest of module

--- src/model/channels.coffee
+++ src/model/channels.coffee
@@ -11,12 +11,12 @@
 	"password": { type: String, required: false }

 ChannelSchema = new Schema
-    "name":			{ type: String, required: true }
-    "urlPattern": 	{ type: String, required: true }
-    "allow": 		[ { type: String, required: true } ]
-    "routes": 		[ RouteSchema ]
-    "properties": 	[ { type: Object, required: false } ]
-
+	"name":			{ type: String, required: true }
+	"urlPattern": 	{ type: String, required: true }
+	"allow": 		[ { type: String, required: true } ]
+	"routes": 		[ RouteSchema ]
+	"properties": 	[ { type: Object, required: false } ]
+
 # compile the Channel and Route Schema into a Model
 exports.Route = mongoose.model 'Route', RouteSchema


Longer highlighting in vote popover

Longer highlighting in vote popover

--- core/app/backbone/views/fact_relations/vote_view.coffee
+++ core/app/backbone/views/fact_relations/vote_view.coffee
@@ -33,7 +33,7 @@

   highlight: ($el) ->
     _.defer (-> $el.addClass 'vote-up-down-highlight')
-    _.delay (-> $el.removeClass 'vote-up-down-highlight'), 700
+    _.delay (-> $el.removeClass 'vote-up-down-highlight'), 1000

   set_fact_relation_opinion: (opinion) ->
     return if @model.current_opinion() == opinion


KDLoader: removed the easter egg

KDLoader: removed the easter egg

--- components/loader/KDLoaderView.coffee
+++ components/loader/KDLoaderView.coffee
@@ -47,12 +47,12 @@
     @canvas.hide() if @canvas

   # easter
-  mouseEnter:->
+  # mouseEnter:->

-    @canvas.setSpeed 2
-    @canvas.setColor @utils.getRandomHex()
+  #   @canvas.setSpeed 2
+  #   @canvas.setColor @utils.getRandomHex()

-  mouseLeave:->
+  # mouseLeave:->

-    @canvas.setColor @getOptions().loaderOptions.color
-    @canvas.setSpeed @getOptions().loaderOptions.speed
+  #   @canvas.setColor @getOptions().loaderOptions.color
+  #   @canvas.setSpeed @getOptions().loaderOptions.speed


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -180,6 +180,6 @@

   engine.collides(Rectangle(0, 0, 10, 10), null)

-test "#" ->
+test "#se" ->

 module()


:sparkles: fixed debugger bug

:sparkles: fixed debugger bug

--- debug/components/debugger-scene-description-transform.coffee
+++ debug/components/debugger-scene-description-transform.coffee
@@ -5,9 +5,9 @@
     super props
     @lastObject = @props.object;
     @state=
-      position:@lastObject.transformer.position
-      rotation:@obtainEular(@lastObject.transformer.rotation)
-      scale:@lastObject.transformer.scale
+      position:@lastObject.__transformer._position
+      rotation:@obtainEular(@lastObject.__transformer._rotation)
+      scale:@lastObject.__transformer._scale
   render:->
     <div>
       <Vector3 title="position" value={@state.position} onValueChanged={@onPositionChange}/>
@@ -16,26 +16,26 @@
     </div>

   onPositionChange:(n,v)=>
-    @props.object.transformer.position[n] = v
-    @props.object.transformer.updateTransform()
+    @props.object.__transformer._position[n] = v
+    @props.object.__transformer.updateTransform()
     @updateObjectState @props.object

   onRotationChange:(n,v)=>
-    eul = @obtainEular @props.object.transformer.rotation
+    eul = @obtainEular @props.object.__transformer._rotation
     eul[n] = v
     @state.rotation[n] = v
     @setState @state
     eul.X = @fromDegree eul.X
     eul.Y = @fromDegree eul.Y
     eul.Z = @fromDegree eul.Z
-    @props.object.transformer.rotation.eularAngles = eul
+    @props.object.__transformer._rotation.eularAngles = eul
     @isRotationChange = true
-    @props.object.transformer.updateTransform()
+    @props.object.__transformer.updateTransform()

   onScaleChange:(n,v)=>
-    @props.object.transformer.scale[n] = v
-    @props.object.transformer.updateTransform()
+    @props.object.__transformer._scale[n] = v
+    @props.object.__transformer.updateTransform()
     @updateObjectState @props.object

   onTransformUpdate:()=>
@@ -43,19 +43,19 @@

   componentWillReceiveProps:(prop)=>
     if @lastObject && @lastObject!=@props.object
-      @lastObject.transformer.onUpdateTransformHandler.removeListener @onTransformUpdate
+      @lastObject.__transformer.onUpdateTransformHandler.removeListener @onTransformUpdate
       @lastObject = @props.object
-      @lastObject.transformer.onUpdateTransformHandler.addListener @onTransformUpdate
+      @lastObject.__transformer.onUpdateTransformHandler.addListener @onTransformUpdate
     @updateObjectState prop.object

   updateObjectState:(obj)=>
     @setState
-      position:obj.transformer.position
-      rotation:@obtainEular obj.transformer.rotation
-      scale:obj.transformer.scale
+      position:obj.__transformer._position
+      rotation:@obtainEular obj.__transformer._rotation
+      scale:obj.__transformer._scale

   obtainEular:(quat)=>
-    eul = quat.FactoringQuaternionZXY()
+    eul = quat.factoringQuaternionZXY()
     {X:@toDegree(eul.x),Y:@toDegree(eul.y),Z:@toDegree(eul.z)}

   copyXYZ:(s)=>


tweak

tweak

--- src/javascript/spike/physijs_spike.coffee
+++ src/javascript/spike/physijs_spike.coffee
@@ -86,7 +86,7 @@
   light.shadowCameraBottom = 60
   light.shadowCameraNear = 20
   light.shadowCameraFar = 200
-  light.shadowBias = -.0001
+  light.shadowBias = -0.0001
   light.shadowMapWidth = light.shadowMapHeight = 2048
   light.shadowDarkness = 0.7
   scene.add( light )


Use Freesheet facade

Use Freesheet facade

--- src/main/js/worksheet/TableWorksheet.coffee
+++ src/main/js/worksheet/TableWorksheet.coffee
@@ -1,6 +1,4 @@
-ReactiveRunner = require('reactive-runner')
-TextLoader = require('text-loader')
-TextParser = require('text-parser')
+Freesheet = require 'freesheet'
 CoreFunctions = require('core-functions')
 PageFunctions = require('page-functions')
 TimeFunctions = require('time-functions')
@@ -28,13 +26,12 @@

   constructor: (el, @changeCallback) ->
-    @runner = new ReactiveRunner()
-    @runner.onChange @changeCallback
-    @runner.addProvidedStreams CoreFunctions
-    @runner.addProvidedStreams TimeFunctions
-    @runner.addProvidedStreams PageFunctions
-    @runner.onChange (name, value) => @_updateTable name, value
-    @loader = new TextLoader(@runner)
+    @sheet = Freesheet.createSheet(el.attr('id') or 'sheet1')
+    @sheet.onChange @changeCallback
+    @sheet.addFunctions CoreFunctions
+    @sheet.addFunctions TimeFunctions
+    @sheet.addFunctions PageFunctions
+    @sheet.onChange (name, value) => @_updateTable name, value
     @data = ({name: null, formula: null, value: null} for i in [1..5] )

     @table = new Handsontable el.get(0), {
@@ -68,22 +65,22 @@
       console.log 'beforeRemoveRow', index, numberOfRows, self.data
       for row in self.data[index...index + numberOfRows]
         console.log 'Removing row', row
-        self.loader.removeFunction row.name
+        self.sheet.remove row.name

   updateFormula: (name, formula, oldName, nextName) ->
     console.log 'updateFormula', name, formula, oldName, nextName
-    if name and formula then @loader.setFunctionAsText name, formula, oldName, nextName
-    if name and not formula then @loader.removeFunction name
-    if not name and oldName then @loader.removeFunction oldName
+    if name and formula then @sheet.update name, formula, oldName, nextName
+    if name and not formula then @sheet.remove name
+    if not name and oldName then @sheet.remove oldName

-  asText: -> @loader.asText()
+  asText: -> @sheet.text()

   loadText: (text) ->
     @loading = true
-    @loader.clear()
+    @sheet.clear()
     @data.length = 0
-    @loader.loadDefinitions text
+    @sheet.load text
     @_newRow null
     @loading = false

@@ -99,7 +96,7 @@
     if @loading then existingRow or @_newRow(name) else existingRow

   _newRow: (name) ->
-    def = (d for d in @loader.functionDefinitions() when d.name == name)[0]
+    def = (d for d in @sheet.formulas() when d.name == name)[0]
     row = {name: def?.name, formula: def?.expr.text, value: null}
     @data.push row
     row


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -108,7 +108,7 @@
             r = labelRadius
         #return "translate(" + (x/h * labelRadius) + "," + (y/h * labelRadius) + ")"
         #return "translate(" + r + "," + y + ")"#(y/h * labelRadius) + ")"
-        return "translate(" + r*mx + "," + y*my + ")"#(y/h * labelRadius) + ")"
+        return "translate(" + (x/h * labelRadius)*mx + "," + y*my + ")"#(y/h * labelRadius) + ")"
         )

         .attr('fill', "#fff")


Agent pie: edit label transition

Agent pie: edit label transition

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -127,7 +127,7 @@

         # Create variables that will be in scope within the path.transition attrTween function
         myInstanceDataId = @instanceDataId
-        lastPieData = window.oldPieData[myInstanceDataId]
+        lastPieData = window.oldPieData[myInstanceDataId] ? d

         path = pathGroup.selectAll('path.pie').data(piedata)
         path.enter().append('path').attr('class', 'pie').attr('fill', (d, i) -> color i)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -15,7 +15,7 @@
     $(@node).children(".more-info").text($(@node).attr("data-moreinfo"))
     $(@node).children(".updated-at").text(@get('updatedAtMessage'))

-    mFontSize = 28
+    #mFontSize = 28
     width = 750 #width
     height = 450 #height
     radiuso = 200 #outer radius
@@ -76,14 +76,14 @@
       sum += val.value

     if !sum
-      arcs.append("svg:text")
+      arcs.append("svg:text").attr("class","cusipie-label")
         .attr('fill', "#fff")
-        .attr("text-anchor", "middle").text("No Results").attr('font-size', mFontSize + 'px')
+        .attr("text-anchor", "middle").text("No Results")#.attr('font-size', mFontSize + 'px')
     else
-      arcs.append("svg:text")
+      arcs.append("svg:text").attr("class","cusipie-sum")
         .attr('fill', "#fff")
         .attr("text-anchor", "middle")
-        .attr("alignment-baseline", "central")
+        .attr("alignment-baseline", "mathematical")
         .attr('font-size', radiusi + 'px')
         .attr('font-weight', 'bold')
         .text(sum)


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -402,7 +402,7 @@
         $(@node).children("svg").remove();

         pie = d3.layout.pie()
-            .sort(null)
+            #.sort(null)
             .value((d) -> d.value)

         arc = d3.svg.arc()


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -461,7 +461,7 @@
             .data([data])
             .attr("width", width)
             .attr("height", height)
-            .append("svg")
+            .append("g")
             .attr("transform", "translate(" + width/2 + "," + height/2 + ")")

         pie = d3.layout.pie()


Only Koding admins can do that.

Only Koding admins can do that.

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -91,27 +91,30 @@
                 account.addDomain domainObj, (err)->
                   console.log err  if err?

-  @fixUserDomains = secure (client, callback)->
-    JDomain = require './domain'
-    JUser   = require './user'
-
-    JVM.each {}, {}, (err, vm)=>
-      return callback err  if err
-      return callback null, null  unless vm
-      {nickname, groupSlug, uid, type} = @parseAlias vm.hostnameAlias
-      hostnameAliases = JVM.createAliases {
-        nickname, type, uid, groupSlug
-      }
-      vmUser = vm.users.filter (u)->
-        return u.owner is yes
-      if vmUser.length > 0
-        JUser.one
-          _id: vmUser[0].id
-        , (err, user)=>
-          if not err and user
-            user.fetchAccount 'koding', (err, account)=>
-              if not err and account
-                @createDomains account, hostnameAliases, hostnameAliases[0]
+  @fixUserDomains = permit 'change bundle',
+    success: (client, callback)->
+      return callback new KodingError "You are not Koding admin."  if client.context.group isnt "koding"
+
+      JDomain = require './domain'
+      JUser   = require './user'
+
+      JVM.each {}, {}, (err, vm)=>
+        return callback err  if err
+        return callback null, null  unless vm
+        {nickname, groupSlug, uid, type} = @parseAlias vm.hostnameAlias
+        hostnameAliases = JVM.createAliases {
+          nickname, type, uid, groupSlug
+        }
+        vmUser = vm.users.filter (u)->
+          return u.owner is yes
+        if vmUser.length > 0
+          JUser.one
+            _id: vmUser[0].id
+          , (err, user)=>
+            if not err and user
+              user.fetchAccount 'koding', (err, account)=>
+                if not err and account
+                  @createDomains account, hostnameAliases, hostnameAliases[0]

   @ensureDomainSettings = ({account, vm, type, nickname, groupSlug})->
     domain = 'kd.io'


resolved conflict

resolved conflict


only show votable teams on the score page

Hours of sleep: 18
Cups of coffee: 0
Cans of soda: 0
Cans of redbull: 0

--- models/team.coffee
+++ models/team.coffee
@@ -75,7 +75,7 @@
     return next err if err
     next null, !count
 TeamSchema.static 'sortedByScore', (next) ->
-  Team.find {}, {}, {sort: [['scores.overall', -1]]}, (error,teams) ->
+  Team.find { 'entry.votable': true, lastDeploy: {$ne:null} }, {}, {sort: [['scores.overall', -1]]}, (error,teams) ->
     return next error if error
     next null, teams


Branches are run in parallel

Branches are run in parallel

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -1473,13 +1473,13 @@
               .deliver(data)
               .then (choice,data) ->
                 choice.branch "sub1-1", (b,data) ->
-                  data += '11-'
+                  data += '11'
                   data
                 choice.branch "sub1-2", (b,data) ->
-                  data += '12-'
+                  data += '12'
                   data
             choice.branch "2",  (b,data) ->
-              data += '-b-'
+              data += 'b-'
               b.tree()
               .deliver(data)
               .then (choice,data) ->
@@ -1493,7 +1493,7 @@
             data

-        test splitter, ['a-11-b-21','a-12-b-21','a-11-v-22','a-12-b-22'], done
+        test splitter, ['a-11','a-12','b-21','b-22'], done

       it 'v6', (done) ->
         splitter = (choice) ->


fixed a fucked up spec, order of branches permutations is off

--- spec/Thenable.coffee
+++ spec/Thenable.coffee
@@ -1228,7 +1228,7 @@
           chai.expect(res).to.eql [15]
           done()

-    describe 'contest via subbranching w/ abortions', ->
+    describe 'contest via subbranching w/ abortions & branch order', ->
       it 'should resolve', (done) ->

         failedComponent = (n,d) ->
@@ -1261,22 +1261,27 @@
           #   d

           .then 'variation', (c, d) ->
-            c.branch 'two', (b, data) ->
-              data * 2
-            c.branch 'three', (b, data) ->
-              data * 3
+            c.branch 'four', (b, data) ->
+              data * 4
+            c.branch 'five', (b, data) ->
+              data * 5

            .all [component]
            .then (n,results) ->
              results[0]
         Root()
-        .deliver 5
+        .deliver 1
         .contest "contest-multiply", [
           multiply
         ], (c, results) ->
+          #console.log "results.length", results
+          #chai.expect(results[0].value).to.eql 8
+          #chai.expect(results[1].value).to.eql 10
+          #chai.expect(results[2].value).to.eql 12
+          #chai.expect(results[3].value).to.eql 15
           results[0]
         .finally (c, res) ->
-          chai.expect(res).to.eql [20]
+          chai.expect(res).to.eql [8]
           done()


Update osu.coffee

Update osu.coffee

Remove unneeded parentheses

--- modules/osu.coffee
+++ modules/osu.coffee
@@ -1,5 +1,5 @@
 osu = require 'node-osu'
-osuApi = new (osu.Api)(process.env.OSU_KEY)
+osuApi = new osu.Api(process.env.OSU_KEY)

 class OsuModule extends BotModule
   init: =>


Closes #58, changing the if logic a bit, separating lga changes and facility summary changes

--- static/coffee/nmis.coffee
+++ static/coffee/nmis.coffee
@@ -1829,6 +1829,7 @@
       if @changing("lga") or @changingToSlug("mode", "facilities")
         repositionMapToDistrictBounds = true
         addIcons = true
+      if @changing "lga"
         createMap = true
       if @changing("sector")
         if next.sector.slug is "overview"


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -1,6 +1,6 @@
 module "ActiveBounds"

-App = {}
+window.App = {}

 App.width = 640
 App.height = 480


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com


added test case for $interface

added test case for $interface

--- test/interfaceSpec.coffee
+++ test/interfaceSpec.coffee
@@ -98,4 +98,22 @@
             types[0].type.should.eql arguType[0]
             types[1].type.should.eql arguType[1]

+    describe "#$interface(define, base)", ->
+        it "should return a new object that prototype is Interface", ->
+            a = M.$interface({})
+            assert M.Interface.isPrototypeOf a

+        it "should return a new object that prototype is given base", ->
+            base = M.$interface()
+            define =
+                t1 : String
+                t2 : Number
+
+            a = M.$interface(define, base)
+
+            assert base.isPrototypeOf a
+            a.should.have.property "t1", define.t1
+            a.should.have.property "t2", define.t2
+
+
+


Nullsafe error.message

Nullsafe error.message


need to specify px

need to specify px

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -123,8 +123,8 @@
   top = target.bottom + subjectDist
   if top + tooltipHeight > limit.bottom and target.top - tooltipHeight - subjectDist > limit.top
     top = target.top - tooltipHeight - subjectDist
-  @tooltip.style.left = left
-  @tooltip.style.top = top + (@tooltip.offsetTop-tooltip.top)
+  @tooltip.style.left = left + 'px'
+  @tooltip.style.top = (top + (@tooltip.offsetTop-tooltip.top)) + 'px'

 class ScribeLinkTooltip


Remove font from script; now in css

Remove font from script; now in css

--- labeled_scatterplot/labeled_scatterplot.coffee
+++ labeled_scatterplot/labeled_scatterplot.coffee
@@ -66,7 +66,6 @@
     return xv+8 if xv < w*0.8
     xv-8)
   .attr("y", (d) -> yScale(d.y))
-  .attr("font-family", "arial")
   .attr("dominant-baseline", "middle")
   .attr("text-anchor", (d) ->
     xv = xScale(d.x)


allow links to begin with mailto:

allow links to begin with mailto:

closes #206

--- src/modules/link-tooltip.coffee
+++ src/modules/link-tooltip.coffee
@@ -94,7 +94,7 @@
       @quill.formatText(range, 'link', false, 'user')

   _normalizeURL: (url) ->
-    url = 'http://' + url unless /^https?:\/\//.test(url)
+    url = 'http://' + url unless /^(https?:\/\/|mailto:)/.test(url)
     return url

   _suggestURL: (range) ->


calculate ground heightmap dynamically from texture

--- src/Landscape.coffee
+++ src/Landscape.coffee
@@ -73,6 +73,22 @@

     @mountain1a = @game.add.sprite(0, 180, 'mountain1')
     @mountain1b = @game.add.sprite(2000, 180, 'mountain1')
+
+    @heightmap = []
+    y = 122
+    for x in [0..@mountain1a.width]
+      solid = @mountain1a.input.checkPixel(x, y)
+      if solid
+        while solid
+          y -= 1
+          solid = @mountain1a.input.checkPixel(x, y)
+        y += 1
+      else
+        until solid
+          y += 1
+          solid = @mountain1a.input.checkPixel(x, y)
+          break if y > 200
+      @heightmap.push(y)

     @mountain1a.alpha = 0.95
     @mountain1b.alpha = 0.95


Added encoding

Added encoding

--- tests/integrated.coffee
+++ tests/integrated.coffee
@@ -272,7 +272,9 @@
     it "The code should be compiled properly", (done) ->
       defer = q.defer()
       g.src(
-        "./tests/data/imports/main.scss"
+        "./tests/data/imports/main.scss", (
+          "encoding": "utf8"
+        )
       ).pipe(
         sourcemaps.init()
       ).pipe(


Fixed bug in controls unbinding mechanism.

--- src/NESCoffee.coffee
+++ src/NESCoffee.coffee
@@ -96,7 +96,6 @@
         @binder.bindControl srcDevice, srcButton, callback

     replaceUnbindCallback: (port, device, button, srcDevice, srcButton, callback) ->
-        @unbindCallbacks1[port]?[device]?[button]?()
         @unbindCallbacks1[port]?[device]?[button] = callback
         @unbindCallbacks2[srcDevice]?[srcButton]?()
         @unbindCallbacks2[srcDevice]?[srcButton] = callback


Make stop unstarted proove

Make stop unstarted proove

--- src/RedisPort.coffee
+++ src/RedisPort.coffee
@@ -144,12 +144,12 @@
 	stop: (cb) ->
 		log.debug "#{@id}: stopping"

-		async.each (Object.keys @ephemerals), ((key, cb) =>
+		async.each (Object.keys @ephemerals or []), ((key, cb) =>
 			clearTimeout @ephemerals[key]
 			@del key, cb
 		), =>
-			@client.end()
-			@subscriber.end()
+			@client?.end()
+			@subscriber?.end()

 			@emit "stopped"
 			log.debug "#{@id}: stopped"


Remove timeout to ensure mutations are dispatched in order after composition events

--- src/trix/observers/mutation_observer.coffee
+++ src/trix/observers/mutation_observer.coffee
@@ -22,14 +22,11 @@
     @observer.disconnect()

   didMutate: (mutations) =>
-    clearTimeout(@debounce)
     @mutations.push(@findSignificantMutations(mutations)...)

-    @debounce = setTimeout =>
-      if @mutations.length
-        @delegate?.elementDidMutate?(@getMutationSummary())
-        @reset()
-    , 1
+    if @mutations.length
+      @delegate?.elementDidMutate?(@getMutationSummary())
+      @reset()

   # Private


Use map instead of iterating

Use map instead of iterating

--- src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee
+++ src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee
@@ -133,10 +133,9 @@
             @miniEditor.setText(currentWord)

   setArray: (paths) ->
-    projectRelativePaths = []
-    for path in paths
+    projectRelativePaths = paths.map (path) ->
       projectRelativePath = project.relativize(path)
-      projectRelativePaths.push({path, projectRelativePath})
+      {path, projectRelativePath}

     super(projectRelativePaths)


Fix link in docs

Fix link in docs

--- lib/assets/javascripts/unpoly/motion.js.coffee
+++ lib/assets/javascripts/unpoly/motion.js.coffee
@@ -2,7 +2,7 @@
 Animation
 =========

-Whenever you [update a page fragment](/up-link) you can animate the change.
+Whenever you [update a page fragment](/up.link) you can animate the change.

 Let's say you are using an [`up-target`](/a-up-target) link to update an element
 with content from the server. You can add an attribute [`up-transition`](/a-up-target#up-transition)


fix replacer

fix replacer

--- lib/grunt/task_helpers/css/replace_urls.coffee
+++ lib/grunt/task_helpers/css/replace_urls.coffee
@@ -68,11 +68,11 @@
         path.push params if params.length > 0
         path = path.join('?')

-        return "url(\"/#{path}\")" if _.isEmpty(@getAssetHost())
+        return "url('/#{path}')" if _.isEmpty(@getAssetHost())

         host = @getAssetHost().replace(/\/$/, '')

-        "url(\"#{host}/#{path}\")"
+        "url('#{host}/#{path}')"

       cfg.options.patterns.push p


Ensure the console.log output in the test won't go to the JSON result. (Fixes #1)

--- lib/guard/jasmine/phantomjs/run-jasmine.coffee
+++ lib/guard/jasmine/phantomjs/run-jasmine.coffee
@@ -1,60 +1,65 @@
-##
-# Wait until the test condition is true or a timeout occurs. Useful for waiting
-# on a server response or for a ui change (fadeIn, etc.) to occur.
+# Wait until the test condition is true or a timeout occurs.
 #
-# @param testFx javascript condition that evaluates to a boolean,
-# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
-# as a callback function.
-# @param onReady what to do when testFx condition is fulfilled,
-# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
-# as a callback function.
-# @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
-##
+# @param [Function] testFx the condition that evaluates to a boolean
+# @param [Function] onReady the action when the condition is fulfilled
+# @param [Number] timeOutMillis the max amount of time to wait
+#
 waitFor = (testFx, onReady, timeOutMillis=3000) ->
   start = new Date().getTime()
   condition = false
-  f = ->
+  wait = ->
     if (new Date().getTime() - start < timeOutMillis) and not condition
-      # If not time-out yet and condition not yet fulfilled
-      condition = (if typeof testFx is 'string' then eval testFx else testFx()) #< defensive code
+      condition = (if typeof testFx is 'string' then eval testFx else testFx())
     else
       if not condition
-        # If condition still not fulfilled (timeout but condition is 'false')
-        console.log JSON.stringify { error: "Timeout requesting Jasmine test runner!" }
+        console.log "JasmineResult: #{ JSON.stringify { error: "Timeout requesting Jasmine test runner!" } }"
         phantom.exit(1)
       else
-        # Condition fulfilled (timeout and/or condition is 'true')
-        if typeof onReady is 'string' then eval onReady else onReady() #< Do what it's supposed to do once the condition is fulfilled
-        clearInterval interval #< Stop this interval
+        if typeof onReady is 'string' then eval onReady else onReady()
+        clearInterval interval

-  interval = setInterval f, 100 #< repeat check every 100ms
+  interval = setInterval wait, 100

+# Check arguments of the script.
+#
 if phantom.args.length isnt 1
-  console.log JSON.stringify { error: "Wrong usage of PhantomJS script!" }
+  console.log "JasmineResult: #{ JSON.stringify { error: "Wrong usage of PhantomJS script!" } }"
   phantom.exit()
+else
+  url = phantom.args[0]

 page = new WebPage()
-page.onConsoleMessage = (msg) -> console.log msg

-url = phantom.args[0]
+# Output the Jasmine test runner result as JSON object.
+# Ignore all other calls to console.log
+#
+page.onConsoleMessage = (msg) -> console.log(RegExp.$1) if /^JasmineResult: (.*)$/.test(msg)

+# Open web page and run the Jasmine test runner
+#
 page.open url, (status) ->
+
   if status isnt 'success'
-    console.log JSON.stringify { error: "Unable to access Jasmine specs at #{ url }" }
+
+    console.log "JasmineResult: #{ JSON.stringify { error: "Unable to access Jasmine specs at #{ url }" } }"
     phantom.exit()

   else
-    waitFor ->
-      page.evaluate ->
-        if document.body.querySelector '.finished-at' then true else false
+    # Wait until the Jasmine test is run
+    waitFor -> page.evaluate -> if document.body.querySelector '.finished-at' then true else false
     , ->
+        # Jasmine test runner has finished, extract the result from the DOM
         page.evaluate ->
+
+          # JSON response to Guard::Jasmine
           result = {
             suites: []
           }

           # Extract runner stats from the HTML
           stats = /(\d+) specs, (\d+) failures? in (\d+.\d+)s/.exec document.body.querySelector('.description').innerText
+
+          # Add stats to the result
           result['stats'] = {
             specs: parseInt stats[1]
             failures: parseInt stats[2]
@@ -64,12 +69,15 @@
           # Extract failed suites
           for failedSuite in document.body.querySelectorAll 'div.jasmine_reporter > div.suite.failed'
             description = failedSuite.querySelector('a.description')
+
+            # Add suite information to the result
             suite = {
               description: description.innerText
               filter: description.getAttribute('href')
               specs: []
             }

+            # Collect information about each **failing** spec
             for failedSpec in failedSuite.querySelectorAll 'div.spec.failed'
               spec = {
                 description: failedSpec.querySelector('a.description').getAttribute 'title'
@@ -79,6 +87,7 @@

             result['suites'].push suite

-          console.log JSON.stringify result, undefined, 2
+          # Write result as JSON string that is parsed by Guard::Jasmine
+          console.log "JasmineResult: #{ JSON.stringify result, undefined, 2 }"

         phantom.exit()


rename _ to u

rename _ to u

--- src/arguments.coffee
+++ src/arguments.coffee
@@ -1,4 +1,4 @@
-define 'arguments', ['utils'], (_)->
+define 'arguments', ['utils'], (u)->
   ->
     args = [].slice.call(arguments)

@@ -7,8 +7,8 @@
         args = args[0..1]

         [
-          _(arg for arg in args when typeof(arg) is 'function').first(),
-          _(arg for arg in _(args).cropFrom(this[0]) when typeof(arg) is 'string').first() || ' '
+          u(arg for arg in args when typeof(arg) is 'function').first(),
+          u(arg for arg in u(args).cropFrom(this[0]) when typeof(arg) is 'string').first() || ' '
         ].reverse()

     }


Cleaning out some carriage returns from the github-merge script

--- src/scripts/github-merge.coffee
+++ src/scripts/github-merge.coffee
@@ -1,36 +1,36 @@
-# Description:
-#   Integrate with GitHub merge API
-#
-# Dependencies:
-#   "githubot": "0.2.0"
-#
-# Configuration:
-#   HUBOT_GITHUB_TOKEN
-#   HUBOT_GITHUB_API
-#   HUBOT_GITHUB_USER
-#
-# Commands:
-#   hubot merge project_name/<head> into <base> - merges the selected branches or SHA commits
-#
-# Notes:
-#   HUBOT_GITHUB_API allows you to set a custom URL path (for Github enterprise users)
-#
-# Author:
-#   maletor
-
-module.exports = (robot) ->
-  github = require("githubot")(robot)
-
-  # http://rubular.com/r/vnnwHvt75L
-  robot.respond /merge ([-_\.0-9a-zA-Z]+)(\/([-_\.a-zA-z0-9\/]+))? into ([-_\.a-zA-z0-9\/]+)$/i, (msg) ->
-    app      = github.qualified_repo msg.match[1]
-    head     = msg.match[3] || "master"
-    base     = msg.match[4]
-    base_url = process.env.HUBOT_GITHUB_API || 'https://api.github.com'
-    url      = "#{base_url}/repos/#{app}/merges"
-
-    github.post url, { base: base, head: head }, (merge) ->
-      if merge.message
-        msg.send merge.message
-      else
+# Description:
+#   Integrate with GitHub merge API
+#
+# Dependencies:
+#   "githubot": "0.2.0"
+#
+# Configuration:
+#   HUBOT_GITHUB_TOKEN
+#   HUBOT_GITHUB_API
+#   HUBOT_GITHUB_USER
+#
+# Commands:
+#   hubot merge project_name/<head> into <base> - merges the selected branches or SHA commits
+#
+# Notes:
+#   HUBOT_GITHUB_API allows you to set a custom URL path (for Github enterprise users)
+#
+# Author:
+#   maletor
+
+module.exports = (robot) ->
+  github = require("githubot")(robot)
+
+  # http://rubular.com/r/vnnwHvt75L
+  robot.respond /merge ([-_\.0-9a-zA-Z]+)(\/([-_\.a-zA-z0-9\/]+))? into ([-_\.a-zA-z0-9\/]+)$/i, (msg) ->
+    app      = github.qualified_repo msg.match[1]
+    head     = msg.match[3] || "master"
+    base     = msg.match[4]
+    base_url = process.env.HUBOT_GITHUB_API || 'https://api.github.com'
+    url      = "#{base_url}/repos/#{app}/merges"
+
+    github.post url, { base: base, head: head }, (merge) ->
+      if merge.message
+        msg.send merge.message
+      else
         msg.send "Merged the crap out of it"


Enable dry-run mode for files deletion. Closes #6

--- src/coffee/commands/files-delete.coffee
+++ src/coffee/commands/files-delete.coffee
@@ -10,6 +10,7 @@
 .option '-c, --credentials <path>', 'set s3 credentials file path', Helpers.loadCredentials, Helpers.loadCredentials()
 .option '-p, --prefix <name>', 'all files matching the prefix will be loaded'
 .option '-r, --regex [name]', 'an optional RegExp used for filtering listed products (e.g.: /(.*)\.jpg/)', ''
+.option '--dry-run', 'list all files that will be deleted, but don\'t delete them', false
 .parse process.argv

 debug 'parsing args: %s', process.argv
@@ -28,20 +29,25 @@
     files = _.filter data.Contents, (content) -> regex.test(content.Key)
     debug 'filtered %s files', files.length

-    if _.size(files) > 0
-      Promise.map files, (file) ->
-        debug 'about to delete file %s', file.Key
-        s3client.deleteFile file.Key
-      , {concurrency: 5}
-      .then ->
-        console.log 'Files successfully deleted'.green
+    if program.dryRun
+      debug 'running is dry-run mode, no files will be deleted'
+      console.log 'Following files will be deleted (if run without dry mode)'.blue
+      console.log JSON.stringify files, null, 2
+    else
+      if _.size(files) > 0
+        Promise.map files, (file) ->
+          debug 'about to delete file %s', file.Key
+          s3client.deleteFile file.Key
+        , {concurrency: 5}
+        .then ->
+          console.log 'Files successfully deleted'.green
+          process.exit 0
+        .catch (error) ->
+          console.log error.message.red
+          process.exit 1
+      else
+        console.log 'No files to be deleted'.green
         process.exit 0
-      .catch (error) ->
-        console.log error.message.red
-        process.exit 1
-    else
-      console.log 'No files to be deleted'.green
-      process.exit 0
 else
   console.log 'Missing required arguments'.red
   program.help()


Deletes default template setting for tabs. Lets jquery assume tab template

Signed-off-by: Akash Manohar J <e4cf979cf717fd295f4e4edc427ee1aee78fa7dc@akash.im>

--- app/assets/javascripts/backbone/kandan.js.coffee
+++ app/assets/javascripts/backbone/kandan.js.coffee
@@ -71,25 +71,13 @@
           chatbox = new Kandan.Views.Chatbox()
           $(".main-area").append(chatbox.render().el)
           $('#channels').tabs({
-            # tabTemplate: '''
-            #

-            #     #{label}
-            #     DudeRemove Tab
-            #

-            # ''',
-
             select: (event, ui)->
               $(document).data('active_channel_id',
                 Kandan.Helpers.Channels.get_channel_id_from_tab_index(ui.index))
               console.log "channel changed to index", ui.index
               Kandan.Data.Channels.run_callbacks('change')
           })
-          $("#channels").tabs('option', 'tabTemplate', '''
-

-                #{label}
-                x
-

-            ''')
+

           Kandan.Widgets.init_all()
       })


Fix pods count.

Fix pods count.

--- app/assets/javascripts/pods/pods_store.js.coffee
+++ app/assets/javascripts/pods/pods_store.js.coffee
@@ -35,7 +35,7 @@
         s = t.objectStore('pods')
         r = s.count()
         r.onsuccess = ->
-          resolve(r.result)
+          resolve(r.result - 1)
         r.onerror = ->
           reject(e)
     promise


improvements

improvements

--- app/assets/javascripts/weather/weather.js.coffee
+++ app/assets/javascripts/weather/weather.js.coffee
@@ -41,6 +41,9 @@
   wheater_proc()

 fill_panel = ->
+  if (window.wheater.length == 0)
+    return
+
   $block = $('.weather_panel').html('')
   space = window.innerHeight - 15 - $('.navbar-fixed-top').outerHeight()
   item_max_height = 50
@@ -84,6 +87,8 @@
 $ ->
   init_weather_data()

+  $(window).on 'resize', fill_panel # need to optimize rebuild of panel on resize
+
   $('body').on 'click', '.weather_panel', ->
     $that = $(@)
     new_pos = 0


Update# fix back animation didn't work correctly


Search completion; add support for Amazon.co.jp completion

--- background_scripts/completion_engines.coffee
+++ background_scripts/completion_engines.coffee
@@ -116,6 +116,17 @@

   parse: (xhr) -> JSON.parse(xhr.responseText)[1]

+class AmazonJp extends BaseEngine
+  constructor: ->
+    super
+      engineUrl: "https://completion.amazon.co.jp/search/complete?method=completion&search-alias=aps&client=amazon-search-ui&mkt=6&q=%s"
+      regexps: "^https?://www\\.amazon\\.co\\.jp/(s/|gp/search)"
+      example:
+        searchUrl: "https://www.amazon.co.jp/s/?field-keywords=%s"
+        keyword: "aj"
+
+  parse: (xhr) -> JSON.parse(xhr.responseText)[1]
+
 class DuckDuckGo extends BaseEngine
   constructor: ->
     super
@@ -169,6 +180,7 @@
   Wikipedia
   Bing
   Amazon
+  AmazonJp
   Webster
   Qwant
   DummyCompletionEngine


english

english

--- src/smc-webapp/project_new.cjsx
+++ src/smc-webapp/project_new.cjsx
@@ -491,7 +491,7 @@
         <Row>
             <Col xs={8} xsOffset={2} style={style}>
                 <Alert bsStyle={"info"}>
-                    Creating files, the library and upload has been moved to the{' '}
+                    Creating files, the library and upload have been moved to the{' '}
                     <Button onClick={=>@show_files_tab()}>
                         Files panel
                     </Button>.


hot fix for ckeditor on create

hot fix for ckeditor on create

--- app/assets/javascripts/locomotive/views/content_entries/new_view.js.coffee
+++ app/assets/javascripts/locomotive/views/content_entries/new_view.js.coffee
@@ -2,7 +2,7 @@

 class Locomotive.Views.ContentEntries.NewView extends Locomotive.Views.ContentEntries.FormView

-  save: (event) ->
-    @save_in_ajax event,
-      on_success: (response, xhr) ->
-        window.location.href = xhr.getResponseHeader('location')
+  # save: (event) ->
+  #   @save_in_ajax event,
+  #     on_success: (response, xhr) ->
+  #       window.location.href = xhr.getResponseHeader('location')


removed regex test on old indicator index page(now tmp themes)

--- server/test/integration/indicator_index.coffee
+++ server/test/integration/indicator_index.coffee
@@ -20,8 +20,8 @@
     }, (err, res, body) ->
       assert.equal res.statusCode, 200

-      for indicator in indicators
-        assert.match body, new RegExp(".*#{indicator.title}.*")
+      #for indicator in indicators
+      #  assert.match body, new RegExp(".*#{indicator.title}.*")

       done()
   ).error((error) ->


delete intermediate xdv files from xelatex

--- services/clsi/app/coffee/ResourceWriter.coffee
+++ services/clsi/app/coffee/ResourceWriter.coffee
@@ -48,7 +48,7 @@
 					should_delete = true
 					if path.match(/^output\./) or path.match(/\.aux$/) or path.match(/^cache\//) # knitr cache
 						should_delete = false
-					if path == "output.pdf" or path == "output.dvi" or path == "output.log"
+					if path == "output.pdf" or path == "output.dvi" or path == "output.log" or path == "output.xdv"
 						should_delete = true
 					if path == "output.tex" # created by TikzManager if present in output files
 						should_delete = true


CartoDB posts are handled on the back end now.

--- src/scripts/controllers/Classifier.coffee
+++ src/scripts/controllers/Classifier.coffee
@@ -185,31 +185,6 @@
       @steps.addClass 'finished'
       @saveClassification()

-    saveClassification: =>
-      super
-
-      # NOTE:
-      # This doesn't work in IE.
-      # But eventually it wil be handled on the back end anyway.
-      unless @workflow.selection[0] is @workflow.tutorialSubjects[0]
-        subject = @workflow.selection[0]
-        annotations = @classification.annotations
-
-        query = "INSERT INTO #{config.cartoTable} (" +
-          'the_geom, user_id, scallops, fish, seastars, crustaceans) ' +
-          'VALUES (' +
-          "ST_SetSRID(ST_Point(#{subject.coords[0]}, #{subject.coords[1]}), 4326), " +
-          "'#{User.current?.id || ''}', " +
-          "#{(annotation for annotation in annotations when annotation.species is 'scallop').length}, " +
-          "#{(annotation for annotation in annotations when annotation.species is 'fish').length}, " +
-          "#{(annotation for annotation in annotations when annotation.species is 'seastar').length}, " +
-          "#{(annotation for annotation in annotations when annotation.species is 'crustacean').length}" +
-          ')'
-
-        $.post "http://#{config.cartoUser}.cartodb.com/api/v2/sql",
-          q: query
-          api_key: config.cartoApiKey
-
     toggleMap: (show) =>
       unless typeof show is 'boolean' then show = (do -> arguments[0])
       @el.toggleClass 'show-map', show


fixed formatting

fixed formatting

--- scripts/sentiment.coffee
+++ scripts/sentiment.coffee
@@ -66,7 +66,6 @@
   #      score: 0
   #    }]
   getAllEntriesForWeek = (recordType, weekNum) ->
-    console.log(calculateKey(recordType, weekNum))
     (robot.brain[calculateKey(recordType, weekNum)] || {}).data || []

@@ -76,7 +75,7 @@
   #   recordName: channel_name or user_name
   #   weekNum: 0-52
   #
-  calculateKey  = (recordType, weekNum) ->
+  calculateKey = (recordType, weekNum) ->
     "sentiment:#{weekNum}:#{recordType}"

   # Description:
@@ -107,15 +106,18 @@
     robot.brain[calculateKey(recordType, weekNum)] = {data: masterRecord}

   # helper method to get sender of the message
-  get_username = (response) ->
+  getUsername = (response) ->
     "@#{response.message.user.name}"

   # helper method to get channel of originating message
-  get_channel = (response) ->
+  getChannel = (response) ->
     if response.message.room == response.message.user.name
       "@#{response.message.room}"
     else
       "##{response.message.room}"
+
+  isPrivateMessage = (response) ->
+    return getUsername(response) == getChannel(response)

   # update sentiment of a record
   # Params:
@@ -123,15 +125,16 @@
   logSentiment = (response) ->
     analysis = sentiment(response.message.text)
     if(response.message.text.length > 2 && analysis && analysis.score)
-      updateEntry('user', get_username(response), getWeekOfYear(), analysis.score)
-      updateEntry('channel', get_channel(response), getWeekOfYear(), analysis.score)
+      updateEntry('user', getUsername(response), getWeekOfYear(), analysis.score)
+      if !isPrivateMessage(response)
+        updateEntry('channel', getChannel(response), getWeekOfYear(), analysis.score)

   prettyPrintList = (entries, emptyMessage) ->
     output = ''
     entries = entries || []
     if entries.length
       for entry, i in entries
-        output += "#{i+1}: #{entry.name}\n"
+        output += "#{i + 1}: #{entry.name}\n"
     output || emptyMessage

   onlyNegative = (list) ->
@@ -142,22 +145,46 @@

   robot.hear /.*/, (msg)->
     # match everything and log it
-    if(!msg.message.text.match(/(who|where)( i|')s( the)? (happy|sad|stress|stressed|happiness)\??/i))
+    if(!msg.message.text.match(/(who|where)( i|')s( the)? (happy|sad|sadness|stress|stressed|happiness)\??/i))
       logSentiment(msg)
+
+  happyPeoplePrompt = "Top happy people:\n"
+  happyChannelPrompt = "\n" + "Top happy channels:\n"
+  sadPeoplePrompt = "\n" + "Top stressed people:\n"
+  sadChannelPrompt = "\n" + "Top stressed channels:\n"
+
+  happyPeopleMessage = " - Nobody... Yet.\n"
+  happyChannelMessage = " - People need more hugs.\n"
+  sadPeopleMessage = " - Nowhere... Yet.\n"
+  sadChannelMessage = " - Everything is dandy!\n"
+
+  robot.respond /sentiment/i, (msg) ->
+    msg.send happyPeoplePrompt + prettyPrintList(onlyPositive(getTopForWeek('descending', 3, 'user', getWeekOfYear())),
+      happyPeopleMessage) +
+      happyChannelPrompt + prettyPrintList(onlyPositive(getTopForWeek('descending', 3, 'channel', getWeekOfYear())),
+      happyChannelMessage) +
+      sadPeoplePrompt + prettyPrintList(onlyNegative(getTopForWeek('ascending', 3, 'user', getWeekOfYear())),
+      sadPeopleMessage) +
+      sadChannelPrompt + prettyPrintList(onlyNegative(getTopForWeek('ascending', 3, 'channel', getWeekOfYear())),
+      sadChannelMessage)

   robot.respond /who( i|')s happy\??/i, (msg) ->
     # responds in the current channel
-    msg.send "Top happy people:\n" + prettyPrintList(onlyPositive(getTopForWeek('descending', 10, 'user', getWeekOfYear())), "Nobody... :(\n")
+    msg.send happyPeoplePrompt + prettyPrintList(onlyPositive(getTopForWeek('descending', 10, 'user', getWeekOfYear())),
+      happyPeopleMessage)

-  robot.respond /who( i|')s (sad|stress)\??/i, (msg) ->
+  robot.respond /where( i|')s( the)? (happy|happiness)\??/i, (msg) ->
     # responds in the current channel
-    msg.send "Top stressed people:\n" + prettyPrintList(onlyNegative(getTopForWeek('ascending', 10, 'user', getWeekOfYear())), "Nobody! :)\n")
+    msg.send happyChannelPrompt + prettyPrintList(onlyPositive(getTopForWeek('descending', 10, 'channel',
+      getWeekOfYear())), happyChannelMessage)

-  robot.respond /where( i|')s( the)? happiness\??/i, (msg) ->
+  robot.respond /who( i|')s (sad|stress(ed)?)\??/i, (msg) ->
     # responds in the current channel
-    msg.send "Top happy channels:\n" + prettyPrintList(onlyPositive(getTopForWeek('descending', 10, 'channel', getWeekOfYear())), "People need more hugs.\n")
+    msg.send sadPeoplePrompt + prettyPrintList(onlyNegative(getTopForWeek('ascending', 10, 'user', getWeekOfYear())),
+      sadPeopleMessage)

-  robot.respond /where( i|')s( the)? (sad|stress)\??/i, (msg) ->
+  robot.respond /where( i|')s( the)? (sadness|stress)\??/i, (msg) ->
     # responds in the current channel
-    msg.send "Top stressed channels:\n" +  prettyPrintList(onlyNegative(getTopForWeek('ascending', 10, 'channel', getWeekOfYear())), "Everything is dandy!\n")
+    msg.send sadChannelPrompt + prettyPrintList(onlyNegative(getTopForWeek('ascending', 10, 'channel',
+      getWeekOfYear())), sadChannelMessage)


Fix the og:image values where it is used

--- site/server/lists.coffee
+++ site/server/lists.coffee
@@ -48,7 +48,7 @@
       description: site.seo_description
       type: 'website'
       url: site.link
-      image: blocks.site_intro?.photo
+      image: "#{site.link}/file/#{blocks.site_intro._id}/#{blocks.site_intro.photo}" if blocks.site_intro?.photo
   }

@@ -122,7 +122,7 @@
         title: collection.name
         description: collection.intro
         type: 'website'
-        image: "#{site.link}/file/#{collection._id}/#{collection.photo}"
+        image: "#{site.link}/file/#{collection._id}/#{collection.photo}" if collection.photo
     }
   else
     return {
@@ -265,7 +265,7 @@
         title: doc.title
         description: doc.intro
         type: 'article'
-        image: "#{site.link}/file/#{doc._id}/#{doc.photo}"
+        image: "#{site.link}/file/#{doc._id}/#{doc.photo}" if doc.photo
         first_name: author?.name.split(' ')[0]
         last_name: author?.name.split(' ')[1]
         published: doc.published_at


Rearrange order for better flow

Rearrange order for better flow

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -187,18 +187,17 @@
   event.which > 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.altKey

 ignoreClick = (event, link) ->
-  crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or
-  noTurbolink(link)     or nonStandardClick(event)
+  crossOriginLink(link) or anchoredLink(link) or nonHtmlLink(link) or noTurbolink(link) or nonStandardClick(event)

 browserSupportsPushState =
   window.history and window.history.pushState and window.history.replaceState and window.history.state != undefined

 if browserSupportsPushState
+  document.addEventListener 'click', installClickHandlerLast, true
+
   window.addEventListener 'popstate', (event) ->
     fetchHistory event.state if event.state?.turbolinks

-  document.addEventListener 'click', installClickHandlerLast, true
-
 # Call Turbolinks.visit(url) from client code
 @Turbolinks = { visit }


:fire: unused variable

:fire: unused variable

--- lib/minimap-find-and-replace-binding.coffee
+++ lib/minimap-find-and-replace-binding.coffee
@@ -56,4 +56,4 @@
       delete @decorationsByMarkerId[marker.id]
       delete @subscriptionsByMarkerId[marker.id]

-  findViewIsVisible: -> res = @findAndReplace()?.findView?.is(':visible')
+  findViewIsVisible: -> @findAndReplace()?.findView?.is(':visible')


no throttling for Webkit browsers

no throttling for Webkit browsers

Former-commit-id: 664726993c542c7da4daca6e54a03c2fb598ded4 [formerly 61068fbd7b1b5536a542dfda6746e6366a0d4683]
Former-commit-id: 2d3255beae981fc646bf1a1337e5a4e975197b40

--- src/views/CanvasSeqBlock.coffee
+++ src/views/CanvasSeqBlock.coffee
@@ -31,9 +31,23 @@
     @cache = new FontCache()

     # throttle the expensive draw function
-    @throttledDraw = _.throttle @throttledDraw, 30
     @throttleTime = 0
     @throttleCounts = 0
+    if document.documentElement.style.webkitAppearance?
+      # webkit browser - no throttling needed
+      @throttledDraw = ->
+        start = +new Date()
+        @draw()
+        @throttleTime += +new Date() - start
+        @throttleCounts++
+        if @throttleCounts > 15
+          tTime = Math.ceil(@throttleTime / @throttleCounts)
+          console.log "avgDrawTime/WebKit", tTime
+          # remove perf analyser
+          @throttledDraw = @draw
+    else
+      # slow browsers like Gecko
+      @throttledDraw = _.throttle @throttledDraw, 30

     @manageEvents()

@@ -48,8 +62,8 @@
     # remove itself after analysis
     if @throttleCounts > 15
       tTime = Math.ceil(@throttleTime / @throttleCounts)
+      console.log "avgDrawTime", tTime
       tTime *=  1.2 # add safety time
-      console.log "avgDrawTime", tTime
       tTime = Math.max 20, tTime # limit for ultra fast computers
       @throttledDraw = _.throttle @draw, tTime


:lipstick: fix the protocol module spec on OS X.

--- spec/api/protocol.coffee
+++ spec/api/protocol.coffee
@@ -114,8 +114,11 @@
             free()
             assert false, 'Got error: ' + errorType + ' ' + error
       protocol.interceptProtocol targetScheme, (request) ->
-        pathInUrl = path.normalize request.url.substr(8)
-        assert.equal pathInUrl, __filename
+        if process.platform is 'win32'
+          pathInUrl = path.normalize request.url.substr(8)
+          assert.equal pathInUrl, __filename
+        else
+          assert.equal request.url, "#{targetScheme}://#{__filename}"

     it 'can override original protocol handler', (done) ->
       handler = remote.createFunctionWithReturnValue 'valar morghulis'


Removed accidental typing

Removed accidental typing

--- main/static/src/coffee/common/upload.coffee
+++ main/static/src/coffee/common/upload.coffee
@@ -63,7 +63,7 @@
     upload_file: (file, url, progress, callback) =>
       @active_files += 1
       xhr = new XMLHttpRequest()
-      xhr.upload.addEventListener 'progress', (event) ->More
+      xhr.upload.addEventListener 'progress', (event) ->
           progress(parseInt(event.loaded / event.total * 99.0))
         , false


Remove done message at the end of windosu execution

--- lib/actions/device.coffee
+++ lib/actions/device.coffee
@@ -194,7 +194,6 @@

 				# Need to escape everypath to avoid errors
 				resinWritePath = "\"#{path.join(__dirname, '..', '..', 'bin', 'resin-write')}\""
-				windosu.exec("node #{resinWritePath} \"#{params.image}\" \"#{params.device}\"").then ->
-					console.log 'Done'
+				windosu.exec("node #{resinWritePath} \"#{params.image}\" \"#{params.device}\"")
 			else
 				return done(error)


Trim username for latest geolog

Trim username for latest geolog

--- storage/lib/geolog/index.coffee
+++ storage/lib/geolog/index.coffee
@@ -23,6 +23,6 @@
             if result.rowCount is 0
                 return null
             else
-                return result.rows[0].id.toUpperCase()
+                return result.rows[0].id.trim().toUpperCase()
         finally
             done()


Fix up app server so it runs

Fix up app server so it runs

--- src/app.coffee
+++ src/app.coffee
@@ -13,23 +13,49 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-fs = require("fs")
-express = require("express")
-routes = require("./routes")
+
+fs = require "fs"
+os = require "os"
+path = require "path"
+
+_ = require "underscore"
+express = require "express"
+Databank = require("databank").Databank
+
+routes = require "./routes"
 localURL = require("./url").localURL
-config = require("./config")
-os = require("os")
-Databank = require("databank").Databank
-globals = require("./globals")
+globals = require "./globals"
 Hub = require("./hub").Hub
 Feed = require("./feed").Feed
-server = config.server or os.hostname()
+
+CONFIG_FILE = "/etc/ofirehose.json"
+
+defaults =
+  key: null
+  cert: null
+  server: os.hostname()
+  driver: "memory"
+  params: {}
+  address: null
+
+if fs.existsSync(CONFIG_FILE)
+  try
+    config = JSON.parse(fs.readFileSync(CONFIG_FILE))
+  catch err
+    console.error err
+    process.exit 1
+else
+  config = {}
+
+config = _.defaults config, defaults
+
+server = config.server
 address = config.address or server
 useHTTPS = (if (config.key) then true else false)
-app = undefined
-bounce = undefined
+
 localURL.server = server
 localURL.protocol = (if (useHTTPS) then "https" else "http")
+
 if useHTTPS
   app = express.createServer(
     key: fs.readFileSync(config.key)
@@ -41,17 +67,18 @@
   )
 else
   app = express.createServer()
+
 module.exports = app

 # Configuration
 app.configure ->
-  app.set "views", __dirname + "/views"
+  app.set "views", path.join __dirname, "..", "views"
   app.set "view engine", "utml"
   app.use express.bodyParser()
   app.use express.methodOverride()
   app.use express.logger()
   app.use app.router
-  app.use express.static(__dirname + "/public")
+  app.use express.static path.join __dirname, "..", "public"

 app.configure "development", ->
   app.use express.errorHandler(
@@ -61,7 +88,6 @@

 app.configure "production", ->
   app.use express.errorHandler()
-

 # Routes
 app.get "/", routes.index
@@ -87,4 +113,3 @@
       bounce.listen 80, address
     else
       app.listen 80, address
-


delay creation of page or wizard

delay creation of page or wizard

--- src/coffee/directives/container.coffee
+++ src/coffee/directives/container.coffee
@@ -9,10 +9,20 @@
 angular.module('CoolFormDirectives').
   directive('coolformContainer', () ->

+    l = (scope) ->
+      scope.$watch('definition', (v) ->
+        if !v.pages then return
+        if v.pages.length > 1
+          scope.wizard = true
+        else
+          scope.page = scope.definition.pages[0]
+      )
+
     return {
       restrict: 'E'
       scope:
         definition: '='
       template: templates.container
+      link: l
     }
   )


Fix typo in usage text

Fix typo in usage text

--- src/cli.coffee
+++ src/cli.coffee
@@ -86,7 +86,7 @@
     'report only numbers of the given keys', list

   .option '-d, --details',
-    'report stats of each analized file'
+    'report stats of each analyzed file'

   .option '-a, --alias <custom ext>=<standard ext>',
     'alias custom ext to act like standard ext', object


change style: width:85% -> flex:1

change style: width:85% -> flex:1

--- views/henseiList.cjsx
+++ views/henseiList.cjsx
@@ -17,7 +17,7 @@
       <TitlesList activeTitle={@state.activeTitle}
                   henseiData={@props.henseiData}
                   handleTitleChange={@handleTitleChange} />
-

+

         {
           if @props.henseiData?
                if @props.henseiData.titles? and @props.henseiData.titles.length >= 1 and @props.henseiData[@state.activeTitle]?


set isWaiting back to proper exporting state

--- src/components/performance/export.cjsx
+++ src/components/performance/export.cjsx
@@ -72,7 +72,7 @@
       <AsyncButton
         bsStyle={exportClass}
         onClick={-> PerformanceExportActions.export(courseId)}
-        isWaiting={true}
+        isWaiting={PerformanceExportStore.isExporting(courseId)}
         isFailed={PerformanceExportStore.isFailed(courseId)}
         waitingText='Exporting…'>
         Export


Scroll active page into view

Scroll active page into view

--- src/components/reference-book/toc.cjsx
+++ src/components/reference-book/toc.cjsx
@@ -27,7 +27,7 @@
         </Router.Link>

       { _.map @props.section.children, (child) =>
-

+

 }

@@ -40,6 +40,22 @@
   propTypes:
     onMenuSelection: React.PropTypes.func

+  componentDidMount:  -> @scrollSelectionIntoView()
+  componentDidUpdate: -> @scrollSelectionIntoView()
+  scrollSelectionIntoView: ->
+    {section} = @context.router.getCurrentParams()
+    return unless section
+
+    root = React.findDOMNode(@)
+    li = root.querySelector("[data-section='#{section}']")
+    return unless li
+
+    beforeTop = li.offsetTop - root.offsetTop < root.scrollTop
+    pastBottom = (li.offsetTop - root.offsetTop + li.clientHeight) >
+      (root.scrollTop + root.clientHeight)
+    li.scrollIntoView() if beforeTop or pastBottom
+
+
   render: ->
     {courseId} = @context.router.getCurrentParams()
     toc = ReferenceBookStore.getToc(courseId)


Allow for accessing data with dot-notation in simple column case as well

--- src/index.cjsx
+++ src/index.cjsx
@@ -21,7 +21,7 @@
         # Columns can either be a simple string or be an object that defines
         # both a displayName and path for accessing the data.
         if typeof column is "string"
-          datum = rowData[column.toLowerCase()]
+          datum = deep(rowData, column.toLowerCase())
           key = i + "-" + column
         else if column.path?
           datum = deep(rowData, column.path)


detect if debuggger is running

detect if debuggger is running

--- src/steroids/servers/BuildServer.coffee
+++ src/steroids/servers/BuildServer.coffee
@@ -1,6 +1,7 @@
 Server = require "../Server"
 Converter = require "../Converter"
 util = require "util"
+request = require "request"

 fs = require "fs"
 Paths = require "../paths"
@@ -23,19 +24,21 @@

       config.archives.push {url: "#{req.protocol}://#{req.host}:4567/appgyver/zips/project.zip"}

-      #TODO detect if debugger is online
-      config.configuration.initial_eval_js_string += """
-      window.addEventListener("load", function(){
-        if (!window.AG_DEBUGGER_INJECTED) {
-          e = document.createElement('script');
-          e.setAttribute('src','#{req.protocol}://#{req.host}:31173/target/target-script-min.js#anonymous');
-          document.getElementsByTagName('body')[0].appendChild(e);
-          window.AG_DEBUGGER_INJECTED = true;
-        }
-      }, false);
-      """
+      request.get { url: "http://127.0.0.1:31173/target/target-script-min.js#anonymous" }, (err, bettereq, betteres)=>
+        unless err
+          #TODO detect if debugger is online
+          config.configuration.initial_eval_js_string += """
+          window.addEventListener("load", function(){
+            if (!window.AG_DEBUGGER_INJECTED) {
+              e = document.createElement('script');
+              e.setAttribute('src','#{req.protocol}://#{req.host}:31173/target/target-script-min.js#anonymous');
+              document.getElementsByTagName('body')[0].appendChild(e);
+              window.AG_DEBUGGER_INJECTED = true;
+            }
+          }, false);
+          """

-      res.json config
+        res.json config

     @app.get "/appgyver/zips/project.zip", (req, res)->
       res.sendfile Paths.temporaryZip


client-side msg debugging ability

client-side msg debugging ability

--- src/mpd.coffee
+++ src/mpd.coffee
@@ -136,9 +136,11 @@
     handler(args...) for handler in handlersList

   handleMessage: (msg) =>
-    @msgHandlerQueue.shift()(msg)
+    @debugMsgConsole?.log "get-: #{@msgHandlerQueue[0].debug_id}: " + JSON.stringify(msg)
+    @msgHandlerQueue.shift().cb(msg)

   send: (msg) =>
+    @debugMsgConsole?.log "send: #{@msgHandlerQueue[@msgHandlerQueue.length - 1].debug_id}: " + JSON.stringify(msg)
     @socket.emit 'ToMpd', msg + "\n"

   getOrCreate: (key, table, list, initObjFunc) =>
@@ -179,12 +181,12 @@
     @socket = io.connect()
     @buffer = ""
     @msgHandlerQueue = []
+    # assign to console to enable message passing debugging
+    @debugMsgConsole = null #console
+    @msgCounter = 0

     # whether we've sent the idle command to mpd
     @idling = false
-
-    @socket.on 'disconnect', =>
-      console.log "disconnected from Mpd"

     @socket.on 'FromMpd', (data) =>
       @buffer += data
@@ -242,7 +244,9 @@
         return

   rawSendCmd: (cmd, cb=noop) =>
-    @msgHandlerQueue.push cb
+    @msgHandlerQueue.push
+      debug_id: @msgCounter++
+      cb: cb
     @send cmd

   handleIdleResultsLoop: (msg) =>


Fix POJO download URL.

Fix POJO download URL.

--- src/ext/components/model-output.coffee
+++ src/ext/components/model-output.coffee
@@ -478,7 +478,7 @@
         _pojoPreview "<pre>#{Flow.Util.highlight result, 'java'}</pre>"

   downloadPojo = ->
-    window.open "/3/Models.java/#{encodeURIComponent _model.model_id.name}", '_blank'
+    window.open "/3/Models.java/#{encodeURIComponent _model.model_id.name}.java", '_blank'

   deleteModel = ->
     _.confirm 'Are you sure you want to delete this model?', { acceptCaption: 'Delete Model', declineCaption: 'Cancel' }, (accept) ->


KDView: ::setSize deprecated since we have getHeight and getWidth and wasn't used anywhere

--- src/core/view.coffee
+++ src/core/view.coffee
@@ -225,7 +225,6 @@
   getTagName:-> @options.tagName || 'div'

   # shortcut method for @getDomElement()
-
   $:(selector)->
     if selector
     then @getDomElement().find(selector)
@@ -344,30 +343,6 @@
     @unsetClass 'hidden'
     # @$().show duration
     #@getDomElement()[0].style.display = "block"
-
-  # setSize: do->
-  #   counter = 0
-  #   isPredefinedSize = (size)->
-  #     # we have predefined classes for 0 to 1000px
-  #     return !isNaN(size) and (1000 >= size >= 0)
-
-  #   (sizes)->
-  #     if sizes.width?
-  #       if isPredefinedSize sizes.width
-  #       then @setClass "w#{sizes.width}"
-  #       else @setWidth sizes.width
-
-  #     if sizes.height?
-  #       if isPredefinedSize sizes.height
-  #       then @setClass "h#{sizes.height}"
-  #       else @setHeight  sizes.height
-
-  setSize: (sizes)->
-    if sizes.width?
-      @setWidth sizes.width
-
-    if sizes.height?
-      @setHeight sizes.height

   setPosition:->
     positionOptions = @getOptions().position


Don't allow tabbing into assets or indicators

Fixes #3848

--- web/volume/spreadsheet.coffee
+++ web/volume/spreadsheet.coffee
@@ -1051,7 +1051,7 @@
             while true
               c = if event.shiftKey then c.previousSibling else c.nextSibling
               return unless c && i = parseId(c)
-              break unless !info.t && info.metric.id == 'id' # skip "delete" actions
+              break unless i.category?.id == 'asset' || i.metric?.name == 'indicator' # skip "delete" actions
             select(i)

           return


SourceIn: Fix removal of header listener

When switching up the parser input in 7a3f354779399, I neglected to also
update the listener removal in `headersComplete`

--- src/streammachine/master/source_in.coffee
+++ src/streammachine/master/source_in.coffee
@@ -35,8 +35,9 @@

         parser = new SourceIn.IcyParser SourceIn.IcyParser.REQUEST

-        sock.on "readable", =>
+        readerF = =>
             parser.execute d while d = sock.read()
+        sock.on "readable", readerF

         parser.on "headersComplete", (headers) =>
             if parser.info.protocol == "ICE" || parser.info.method == "SOURCE"
@@ -44,7 +45,7 @@
                 @_trySource sock, parser.info

                 # get out of the way
-                sock.ondata = null
+                sock.removeListener "readable", readerF

             # TODO: Need to add support for the shoutcast metadata admin URL

@@ -191,6 +192,7 @@

             return if !line?

+            console.log "ICy line is !#{line}!"
             if line
                 match = @headerExp.exec line
                 @info.headers[match[1].toLowerCase()] = match[2]


Utils: update the domainWithTLDPattern for existing domains.


DNDUploader: do not allow more than 20 files at once, warn user about that

--- client/finder/commonviews/DNDUploader.coffee
+++ client/finder/commonviews/DNDUploader.coffee
@@ -151,12 +151,18 @@

     dirReader.readEntries (entries)=>

+      if entries.length > 20
+        KD.showError "It's not allowed to upload more than 20 files at once."
+        return
+
       for entry in entries
         if entry.isFile
           entry.file (file)->
             file.relativePath = relative # + file.name
             callback file
-        else @walkDirectory entry, callback, error
+        else
+          @walkDirectory entry, callback, error
+
     , error

   setPath: (path) ->
@@ -270,4 +276,4 @@

   getMachine: ->

-    @getDelegate().controller.machines.first.machine+    @getDelegate().machines.first.machine


Referral: add redeemReferalPoint modal to utils

--- client/Main/utils.coffee
+++ client/Main/utils.coffee
@@ -476,15 +476,34 @@

     return img

+  redeemReferralPoint:(modal)->
+    {vmToResize, sizes} = modal.modal.modalTabs.forms.Redeem.inputs
+
+    data = {
+      vmName : vmToResize.getValue(),
+      size   : sizes.getValue(),
+      type   : "disk"
+    }
+
+    KD.remote.api.JReferral.redeem data, (err, refRes)->
+      return KD.showError errr if err
+      modal.modal.destroy()
+      KD.getSingleton("vmController").resizeDisk data.vm, (err, res)->
+        return KD.showError err if err
+        KD.getSingleton("vmController").emit "ReferralCountUpdated"
+        KD.notify_ """
+          #{refRes.addedSize} #{refRes.unit} extra #{refRes.type} is successfully added to your #{refRes.vm} VM.
+        """
+
   showRedeemReferralPointModal: ()->
     vmController = KD.getSingleton("vmController")
     vmController.fetchVMs yes, (err, vms)=>
       return KD.showError err if err
-      return @notify_ "You don't have any VMs. Please create one VM" if not vms or vms.length < 1
+      return KD.notify_ "You don't have any VMs. Please create one VM" if not vms or vms.length < 1

       KD.remote.api.JReferral.fetchRedeemableReferrals { type: "disk" }, (err, referals)=>
         return KD.showError err if err
-        return @notify_ "You dont have any referrals" if not referals or referals.length < 1
+        return KD.notify_ "You dont have any referrals" if not referals or referals.length < 1

         @modal = modal = new KDModalViewWithForms
           title                   : "Redeem Your Referral Points"
@@ -498,7 +517,7 @@
               Redeem               :
                 callback          : =>
                   @modal.modalTabs.forms.Redeem.buttons.redeemButton.showLoader()
-                  @emit "RedeemReferralPointSubmitted", @
+                  @redeemReferralPoint @
                 buttons           :
                   redeemButton    :
                     title         : "Redeem"


Resize first split view while file tree's is resizing.

--- client/ide/lib/views/idebasesplitview.coffee
+++ client/ide/lib/views/idebasesplitview.coffee
@@ -3,6 +3,7 @@

 module.exports = class IDEBaseSplitView extends KDSplitView
+

   viewAppended: ->

@@ -11,9 +12,10 @@
     @lastKnownFileTreeSize = @getOption('sizes').first or 250

     @on 'PanelDidResize', kd.utils.debounce 10, =>
-
       return  unless fileTree = @panels.first
       @lastKnownFileTreeSize = fileTree.size
+
+    @resizer.on 'DragInAction', => @getFirstSplitView()?._windowDidResize()

   _resizePanels: ->
@@ -27,3 +29,10 @@
     # even if the first pane reaches the value.
     @resizePanel first, 0, kd.noop, yes

+
+  getFirstSplitView: ->
+
+    { first } = @panels.last.getSubViews().first.getSubViews()
+
+    return first  if first instanceof KDSplitView
+


Fix pagination bug when switching box

Fix pagination bug when switching box

--- client/app/router.coffee
+++ client/app/router.coffee
@@ -60,6 +60,7 @@
                 defaultParameters.accountID = defaultAccountID
                 defaultParameters.mailboxID = defaultMailboxID
                 defaultParameters.sort = '-'
+                defaultParameters.pageAfter = '-'
             when 'account.config'
                 defaultAccount = AccountStore.getDefault()?.get 'id'
                 defaultParameters =


Tests: Environments: Paidaccount: Fixed createSnapshotForNonPaidUser test.

--- client/test/lib/environments/paidaccount.coffee
+++ client/test/lib/environments/paidaccount.coffee
@@ -50,15 +50,20 @@
   createSnapshotForNonPaidUser: (browser) ->

     messageSelector  = '.kdmodal.computeplan-modal .message'
+    message          = 'The Snapshot feature is only available for paid accounts.'

     helpers.beginTest(browser)
     helpers.waitForVMRunning(browser)
     environmentHelpers.attemptCreateSnapshot(browser)

-    browser
-      .waitForElementVisible messageSelector, 20000
-      .assert.containsText   messageSelector, "The Snapshot feature is only available for paid accounts." #Assertion
-      .end()
+    browser.element 'css selector', '.snapshots .snapshot', (result) =>
+      if result.status is 0
+        browser.end()
+      else
+        browser
+          .waitForElementVisible messageSelector, 20000
+          .assert.containsText   messageSelector, message #Assertion
+          .end()

   addVM: (browser) ->


Include base views

Include base views

--- vendor/assets/javascripts/backbone-navigator.js.coffee
+++ vendor/assets/javascripts/backbone-navigator.js.coffee
@@ -1,6 +1,13 @@
+#= require_self
+#= require base_views/collection_view
+#= require base_views/composite_view
+#= require base_views/layout
+
 #= require transition_support
 #= require region
 #= require region_wrapper
 #= require directions
 #= require views_controller
 #= require base
+
+Backbone.Navigator = {}


add Restangular to app

add Restangular to app

--- app/scripts/app.coffee
+++ app/scripts/app.coffee
@@ -1,13 +1,20 @@
 'use strict'

-angular.module('brokenPromisesApp', ["ngRoute", "ui.bootstrap"])
-  .config ["$routeProvider", "$sceProvider", ($routeProvider,$sceProvider) ->
-    $sceProvider.enabled(false)
-    $routeProvider
-      .when '/',
-        templateUrl   : 'views/main.html'
-        controller    : 'MainCtrl'
-        reloadOnSearch: false
-      .otherwise
-        redirectTo: '/'
+angular.module('brokenPromisesApp', ["ngRoute", "ui.bootstrap", 'restangular'])
+  .config ["$routeProvider", "$sceProvider", 'RestangularProvider', '$httpProvider',
+    ($routeProvider, $sceProvider, RestangularProvider, $httpProvider) ->
+      $sceProvider.enabled(false)
+
+      RestangularProvider.setBaseUrl "http://localhost\:5000"
+      RestangularProvider.setListTypeIsArray false
+      RestangularProvider.setRestangularFields
+        id: "_id"
+
+      $routeProvider
+        .when '/',
+          templateUrl   : 'views/main.html'
+          controller    : 'MainCtrl'
+          reloadOnSearch: false
+        .otherwise
+          redirectTo: '/'
   ]