patch restful client's api prefix

patch restful client's api prefix

--- app/records-interfaces/group-records-interface.coffee
+++ app/records-interfaces/group-records-interface.coffee
@@ -3,4 +3,7 @@
 ### @ngInject ###
 global.cobudgetApp.factory 'GroupRecordsInterface', (BaseRecordsInterface, GroupModel) ->
   class GroupRecordsInterface extends BaseRecordsInterface
-    model: GroupModel+    model: GroupModel
+    constructor: (recordStore) ->
+      @baseConstructor recordStore
+      @restfulClient.apiPrefix = 'http://localhost:3000/api/v1'


fixed an issue with editable region path method returning elements above the region

--- app/assets/javascripts/carmenta/regions/editable.js.coffee
+++ app/assets/javascripts/carmenta/regions/editable.js.coffee
@@ -5,7 +5,7 @@
   type = 'editable'

   constructor: (@element, @options = {}) ->
-    Carmenta.log('making editable', @element, @options)
+    Carmenta.log('building editable', @element, @options)

     @window = @options.window
     @document = @window.document
@@ -194,7 +194,7 @@

   path: ->
     container = @selection().commonAncestor()
-    container.parentsUntil(@element)
+    return if container.get(0) == @element.get(0) then [] else container.parentsUntil(@element)

   currentElement: ->


Updated logout logic to clear cart

Updated logout logic to clear cart

--- app/assets/javascripts/sprangular/resources/account.coffee
+++ app/assets/javascripts/sprangular/resources/account.coffee
@@ -58,7 +58,7 @@
         .success (data) ->
           service.isLogged = false
           service.clear()
-          Cart.reload()
+          Cart.init()

     signup: (data) ->
       params =


Update params payload in Checkout.setPayment

--- app/assets/javascripts/sprangular/services/checkout.coffee
+++ app/assets/javascripts/sprangular/services/checkout.coffee
@@ -47,8 +47,12 @@

     setPayment: ->
       order  = Cart.current
+      paymentMethodId = @_findPaymentMethodId()
+
       params =
-        state: 'payment'
+        'order[payments_attributes][][payment_method_id]': paymentMethodId
+        'order[existing_card]': ''
+        'state': 'payment'

       @put(params)


OOps.

OOps.

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -64,7 +64,7 @@
     content = content.replace(/(\[REDACTED\])/ig,"[REDACTED]")

     # Italic
-    content = content.replace(/(()|(\s))[\/]{1}([^\/\\n]+)[\/]{1}/ig," $4 ")
+    content = content.replace(/((^)|(\s))[\/]{1}([^\/\\n]+)[\/]{1}(($)|(\s))/ig," $4 ")

     elem = $("

")


make selectRepo toggle the selected repo

so if selectRepo is called with X and currently anything else is selected, X will be set. However, if X is already selected, X will get toggled (i.e. deselected).

Signed-off-by: Dominik Richter <b7323add517e187efe0283b76511b7ed762bfe13@gmail.com>

--- coffee/controllers/main.coffee
+++ coffee/controllers/main.coffee
@@ -344,8 +344,12 @@

     $scope.selectRepo = (m)->
       if m? and m.id?
-        console.log("repo #{m.name} (#{m.id}) selected")
-        $scope.repoSelected = m
+        if $scope.repoSelected? and $scope.repoSelected.id is m.id
+          console.log "deselect repos"
+          $scope.repoSelected = null
+        else
+          console.log("repo #{m.name} (#{m.id}) selected")
+          $scope.repoSelected = m
         # deselect repo selection by Label
         $scope.repoSelectedLabel = null
       else


DigitalWrite: don't set value by default

--- components/DigitalWrite.coffee
+++ components/DigitalWrite.coffee
@@ -9,7 +9,6 @@
       pin: new noflo.Port 'number'
       in: new noflo.Port 'boolean'

-    @value = null
     @inPorts.pin.on 'data', (pinNumber) =>
       @stopGpio()
       @pinNumber = pinNumber
@@ -32,8 +31,7 @@
     return unless @pinNumber
     @gpio = gpio.export(@pinNumber, { direction: 'out' })
     @gpio.on 'directionChange', () =>
-      if @value != null
-        @gpio.set(if @value then 1 else 0)
+      @gpio.set(if @value then 1 else 0)

   shutdown: () ->
     @stopGpio()


improved error handling for arbitrator managment page

--- client/arbitrate/manage.coffee
+++ client/arbitrate/manage.coffee
@@ -13,25 +13,25 @@
   if key_priv? then key = Key.from_privkey key_priv
   else if key? key = Key.from_pubkey key
   else throw new Error 'Missing key argument'
+catch e then display_error e

-  # When loaded for the first time, display the headsup message
-  # and remove the _is_new flag from the URL
-  if _is_new then do ->
-    document.location.hash = format_url null, { _: (randomBytes 160), key }
+# When loaded for the first time, display the headsup message
+# and remove the _is_new flag from the URL
+if _is_new then do ->
+  document.location.hash = format_url null, { _: (randomBytes 160), key }

-    dialog = $ headsup_view {
-      url: location.href
-      has_priv: key.priv?
-    }
-    dialog.on 'hidden', -> dialog.remove()
-    dialog.modal()
+  dialog = $ headsup_view {
+    url: location.href
+    has_priv: key.priv?
+  }
+  dialog.on 'hidden', -> dialog.remove()
+  dialog.modal()

-  load_user key.pub, iferr display_error, (user) ->
-    render $ view {
-      user
-      priv: (key.priv? and get_address key.priv, ADDR_PRIV)
-      current_url: document.location.href
-    }
+load_user key.pub, iferr display_error, (user) ->
+  return display_error 'User cannot be found' unless user?
+  render $ view {
+    user
+    priv: (key.priv? and get_address key.priv, ADDR_PRIV)
+  }

-catch e then display_error e


Kite: add kite helper class

Kite: add kite helper class

--- client/landing/includes.coffee
+++ client/landing/includes.coffee
@@ -559,6 +559,7 @@
   "app/MainApp/kite/kitecontroller.coffee",
   "app/MainApp/kite/newkite.coffee"
   "app/MainApp/kite/kontrol.coffee"
+  "app/MainApp/kite/kitehelper.coffee"
   # Virtualization CONTROLLER
   "app/MainApp/VirtualizationController.coffee",


activity: include ImmutableRenderMixin to UserDropup

--- client/activity/lib/components/userdropup/index.coffee
+++ client/activity/lib/components/userdropup/index.coffee
@@ -1,14 +1,18 @@
-kd             = require 'kd'
-React          = require 'kd-react'
-immutable      = require 'immutable'
-classnames     = require 'classnames'
-ActivityFlux   = require 'activity/flux'
-Dropup         = require 'activity/components/dropup'
-UserDropupItem = require 'activity/components/userdropupitem'
-scrollToTarget  = require 'activity/util/scrollToTarget'
+kd                   = require 'kd'
+React                = require 'kd-react'
+immutable            = require 'immutable'
+classnames           = require 'classnames'
+ActivityFlux         = require 'activity/flux'
+Dropup               = require 'activity/components/dropup'
+UserDropupItem       = require 'activity/components/userdropupitem'
+scrollToTarget       = require 'activity/util/scrollToTarget'
+ImmutableRenderMixin = require 'react-immutable-render-mixin'

 module.exports = class UserDropup extends React.Component
+
+  @include [ImmutableRenderMixin]
+

   @defaultProps =
     items        : immutable.List()
@@ -28,7 +32,7 @@
   confirmSelectedItem: ->

     { selectedItem } = @props
-
+
     @props.onItemConfirmed? "@#{selectedItem.getIn ['profile', 'nickname']}"
     @close()


activity: render channelthreadpane for private messages

--- client/activity/lib/routes/singleprivatechannel.coffee
+++ client/activity/lib/routes/singleprivatechannel.coffee
@@ -1,8 +1,8 @@
-kd                       = require 'kd'
-PrivateMessageThreadPane = require 'activity/components/privatemessagethreadpane'
-ActivityFlux             = require 'activity/flux'
-getGroup                 = require 'app/util/getGroup'
-changeToChannel          = require 'activity/util/changeToChannel'
+kd                = require 'kd'
+ActivityFlux      = require 'activity/flux'
+getGroup          = require 'app/util/getGroup'
+changeToChannel   = require 'activity/util/changeToChannel'
+ChannelThreadPane = require 'activity/components/channelthreadpane'

 {
   thread  : threadActions,
@@ -12,7 +12,6 @@
 { selectedChannelThread, channelByName } = ActivityFlux.getters

 SingleMessageRoute = require './singlemessage'
-

 module.exports = class SinglePrivateChannelRoute

@@ -27,7 +26,7 @@
   getComponents: (state, callback) ->

     callback null,
-      content: PrivateMessageThreadPane
+      content: ChannelThreadPane
       modal: null


Return early once specified machine box view is found

--- client/app/lib/activity/sidebar/activitysidebar.coffee
+++ client/app/lib/activity/sidebar/activitysidebar.coffee
@@ -789,14 +789,10 @@

   getMachineBoxByMachineUId: (uid) ->

-    box = null
-
     for machineList in @machineLists
       for machineBox in machineList.machineBoxes
         if machineBox.machine.uid is uid
-          box = machineBox
-
-    return box
+          return machineBox

   machineBuilt: ->


NavigationMachineItem: Make SidebarMachineSharePopup creation a new method.

--- client/app/lib/navigation/navigationmachineitem.coffee
+++ client/app/lib/navigation/navigationmachineitem.coffee
@@ -135,13 +135,20 @@
       kd.utils.wait 1000, @progress.bound 'hide'

+  showSidebarSharePopup: (options = {}) ->
+
+    options.position = @getPopupPosition 20
+
+    new SidebarMachineSharePopup options, @machine
+
+
   click: (e) ->

     m = @machine

     if not m.isMine() and m.isPermanent() and not m.isApproved()
       kd.utils.stopDOMEvent e
-      new SidebarMachineSharePopup { position: @getPopupPosition 20 }, m
+      @showSidebarSharePopup()

     super


Only activate existing tabs

Only activate existing tabs

--- vendor/assets/javascripts/fustrate/components/tabs.coffee
+++ vendor/assets/javascripts/fustrate/components/tabs.coffee
@@ -13,7 +13,7 @@
       @activateTab $('li > a', @tabs).first()

   activateTab: (tab) =>
-    return unless tab
+    return unless tab.length > 0

     $('.active', @tabs).removeClass 'active'
     tab.addClass 'active'


Shell.RemixView: removed superfluous old code.

--- coffee/src/shells/shell.coffee
+++ coffee/src/shells/shell.coffee
@@ -110,32 +110,5 @@

   className: @classNameExtend 'acorn-shell-edit'

-  # Defines the html template for this view.
-  # To be overridden in derived classes.
-  template: _.template('')
-
-  initialize: =>
-    super
-    @eventhub.on 'change:shell', @OnChangeShell
-    @eventhub.on 'swap.shell', @onSwapShell
-
-  render: =>
-    @$el.html @template()
-    @
-
-  isEditing: acorn.util.property false
-
-  # Can prevent saves from happening. For instance, this is useful when
-  # swapping shells. The swapped-out should not save.
-  shouldSave: acorn.util.property false
-
-  onChangeShell: =>
-    @render()
-
-  onSwapShell: =>
-    @shouldSave false
-
-  finalizeEdit: =>
-

 acorn.registerShellModule Shell


koding-apps-controller: Implemented default app settings and update stuff.

--- client/landing/app/MainApp/kodingappscontroller.coffee
+++ client/landing/app/MainApp/kodingappscontroller.coffee
@@ -30,6 +30,9 @@

     @createExtensionToAppMap()
     @fetchUserDefaultAppConfig()
+
+    @on "UpdateDefaultApp", (extension, appName) =>
+      @updateDefaultAppConfig extension, appName

   getAppPath:(manifest, escaped=no)->

@@ -621,9 +624,17 @@
             map[type].push app.name

   fetchUserDefaultAppConfig: ->
-    appConfigStorage = new AppStorage "DefaultAppConfig", "1.0"
-    appConfigStorage.fetchStorage (storage) ->
-      log storage
+    @appConfigStorage = new AppStorage "DefaultAppConfig", "1.0"
+    @appConfigStorage.fetchStorage (storage) =>
+      settings = @appConfigStorage.getValue "settings"
+      for extension, appName of settings
+        @appManager.defaultApps[extension] = appName
+
+  updateDefaultAppConfig: (extension, appName) ->
+    {defaultApps} = @appManager
+    defaultApps[extension] = appName
+    @appConfigStorage.setValue "settings", defaultApps
+

   defaultManifest = (type, name)->
     {profile} = KD.whoami()


adding function to toggleProfile()

adding function to toggleProfile()

--- app/coffee/context_panel.coffee
+++ app/coffee/context_panel.coffee
@@ -1,7 +1,7 @@
-contextController = null
+window.contextController = null

 $(document).ready ->
-  contextController = new ContextController('#contextPanel')
+  window.contextController = new ContextController('#contextPanel')

 class ContextController
   constructor: (containerSelector) ->
@@ -15,11 +15,8 @@
     @$container.find('.cancel').click (event) =>
       @contextView.showProfile()

-    $(containerSelector).find('.context, .profile').click (event) =>
-      if @contextView.profileState() is 'none'
-        @contextView.expandProfile()
-      else
-        @contextView.contractProfile()
+    $(containerSelector).find('.context').click (event) =>
+      @toggleProfile()

     $('form#update_profile').bind 'ajax:beforeSend', (xhr, settings)=>
       @contextView.showProfile()
@@ -37,6 +34,11 @@

     $('form#update_profile').bind 'ajax:error', (xhr, status, error)->
       $('p.alert').text(error)
+  toggleProfile:  ->
+    if @contextView.profileState() is 'none'
+      @contextView.expandProfile()
+    else
+      @contextView.contractProfile()

 class ContextView
   constructor: (containerSelector) ->


スタイルガイド作成時の置換項目を追加

スタイルガイド作成時の置換項目を追加


Change the format of inspected objects a bit.

{}             # => {}
{a: 4}         # => { a: 4 }

class Foo
foo = new Foo  # => { Foo }
foo.a = 4
foo            # => { Foo a: 4 }

--- application/chrome/content/wesabe/util/inspect.coffee
+++ application/chrome/content/wesabe/util/inspect.coffee
@@ -115,10 +115,10 @@
     contentString = inspect content, showHidden, depth-1, opts

   string = "{"
-    string += style 'class', object.constructor.name, opts
-    string += " " if contentString.length
-  string += contentString
   if object.constructor?.name not in ['Object', null, undefined]
+    string += " #{style 'class', object.constructor.name, opts}"
+    string += " " if contentString.length is 0
+  string += " #{contentString} " if contentString.length
   string += "}"

 inspectArray = (object, showHidden, depth, opts) ->


fix ServerClient

fix ServerClient

--- app/templates/app/scripts/coffee/utils/ServerClient.coffee
+++ app/templates/app/scripts/coffee/utils/ServerClient.coffee
@@ -1,16 +1,13 @@
 define ["jquery"],($)->

   class Lock
-    constructor:->
+    constructor:(@accept_logs=false)->
       @_locks = {}
-      @initialize.apply this, arguments
-
-    initialize:->
-      #for overwrite

     trylock:(url, name)->
       key = @hash url, name
       if !!@_locks[key]
+        console.warn "tryLock #{url} #{name}" if @accept_logs
         false
       else
         @_locks[key] = true
@@ -22,6 +19,7 @@
         @_locks[key] = null
         true
       else
+        console.warn "unlock #{url} #{name}" if @accept_logs
         false

     hash:(url,name)->
@@ -30,8 +28,12 @@
       "#{name}#{url}"

   class ServerClient
-    constructor:->
-      @lock = new Lock
+    constructor:(options)->
+      @lock = new Lock {accept_logs:options.accept_logs}
+      @initialize.apply this, arguments
+
+    initialize:->
+      #for overwrite

     _isServer:-> true

@@ -43,10 +45,11 @@
       options.stub = null
       $.ajax(options)
         .done (data)->
-          unless data.result is "error"
+          if not data or data.result is "error"
+            async.reject data
+          else
             async.resolve data
-          else
-            async.reject data
+
         .fail (err)->
           async.reject err
         .always =>
@@ -74,3 +77,8 @@
       options or options = {}
       options.type = "POST"
       @ajax options
+
+    put:(options)->
+      options or options = {}
+      options.type = "PUT"
+      @ajax options


Add test for deeper matching on *

Add test for deeper matching on *

--- spec/DispatchAction.coffee
+++ spec/DispatchAction.coffee
@@ -85,3 +85,16 @@
         chai.expect(data.state).to.equal expected.state
         done()
       sendAction 'foo:baz', expected.payload, expected.state
+    it 'should send it to correct handler also with deeper action paths', (done) ->
+      routes.send 'bar:baz,foo:*'
+      expected =
+        payload: [1, 2]
+        state:
+          hello: 'world'
+      pass.on 'data', (data) ->
+        done new Error 'Received pass'
+      handle[1].on 'data', (data) ->
+        chai.expect(data.payload).to.equal expected.payload
+        chai.expect(data.state).to.equal expected.state
+        done()
+      sendAction 'foo:baz:hello:world', expected.payload, expected.state


remove debounce function on user search

remove debounce function on user search

not ideal but turns out the debounce function breaks the react-select async component similar to what’s described here, https://github.com/JedWatson/react-select/issues/614#issuecomment-159513545.

--- app/components/user-search.cjsx
+++ app/components/user-search.cjsx
@@ -27,4 +27,5 @@
       searchPromptText="Type to search Users"
       className="search standard-input"
       closeAfterClick={true}
-      asyncOptions={debounce(@searchUsers, 200)} />
+      matchProp={'label'}
+      asyncOptions={@searchUsers} />


Preferences don't exist unless one's been set

--- app/controllers/classify.coffee
+++ app/controllers/classify.coffee
@@ -84,7 +84,7 @@
   onUserChange: (e, user) =>
     @el.toggleClass 'signed-in', user?

-    group = user?.preferences.cyclone_center?.storm
+    group = user?.preferences?.cyclone_center?.storm
     unless group in activeStorms
       group = activeStorms[Math.floor Math.random() * activeStorms.length]
     StormStatus::select.call {group}


use normal javascript member syntax

use normal javascript member syntax

--- core/app/backbone/controllers/client_controller.coffee
+++ core/app/backbone/controllers/client_controller.coffee
@@ -31,16 +31,16 @@

     factsNewView = new FactsNewView
       layout: 'client'
-      fact_text: params['fact']
-      title: params['title']
-      url: params['url']
-      csrf_token: params['csrf_token']
+      fact_text: params.fact
+      title: params.title
+      url: params.url
+      csrf_token: params.csrf_token
       guided: FactlinkApp.guided

     factsNewView.on 'render', parent.onModalReady

     factsNewView.on 'factCreated', (fact) ->
-      parent.highlightLastCreatedFactlink(fact.id, params['fact'])
+      parent.highlightLastCreatedFactlink(fact.id, params.fact)

     clientModal.mainRegion.show factsNewView


Wrapping functions in `expect` testing error

If you are calling `problem.answer` method as is, it's scope is
global and can not access instance variables in `problem` object.
Therfore, this test will be always returning success.

--- assignments/coffeescript/wordy/wordy_test.spec.coffee
+++ assignments/coffeescript/wordy/wordy_test.spec.coffee
@@ -60,9 +60,9 @@

   xit 'too advanced', ->
     problem = new WordProblem('What is 53 cubed?')
-    expect(problem.answer).toThrow(problem.ERROR.tooComplicated)
+    expect(-> problem.answer()).toThrow(problem.ERROR.tooComplicated)

   xit 'irrelevant', ->
     problem = new WordProblem('Who is the president of the United States?')
-    expect(problem.answer).toThrow(problem.ERROR.tooComplicated)
+    expect(-> problem.answer()).toThrow(problem.ERROR.tooComplicated)


update redirect

update redirect

--- public/js/baobab.controller.app.coffee
+++ public/js/baobab.controller.app.coffee
@@ -13,7 +13,7 @@

       @inboxAuthURL = $sce.trustAsResourceUrl('https://www.inboxapp.com/oauth/authorize')
       @inboxClientID = $inbox.appId()
-      @inboxRedirectURL = "https://rocky-sands-9698.herokuapp.com/index.html"
+      @inboxRedirectURL = "https://web.mailtime.com/index.html"
       #@inboxRedirectURL = window.location.href.split('/#')[0].replace('index.html', '')
       @loginHint = ''


Tweak Journal.

Tweak Journal.

--- config/jk-navigator-too.coffee
+++ config/jk-navigator-too.coffee
@@ -36,7 +36,7 @@
 configs.push
   name: "The Journal IE Article"
   regexps: "^http://www\\.thejournal\\.ie/."
-  selectors: [ "span#articleContent p", "ul.commentList div.comment" ]
+  selectors: [ "span#articleContent p", "span#articleContent blockquote", "ul.commentList div.comment" ]

 configs.push
   name: "La Fouly Webcam"


Array#flatten

Array#flatten

--- src/array_extensions.coffee
+++ src/array_extensions.coffee
@@ -34,6 +34,18 @@
   this.length = 0

   return this
+
+###*
+Flatten out an array of arrays into a single array of elements.
+
+@name flatten
+@methodOf Array#
+@type Array
+@returns A new array with all the sub-arrays flattened to the top.
+###
+Array::flatten = ->
+  this.inject [], (a, b) ->
+    a.concat b

 ###*
 Invoke the named method on each element in the array


De-lint.

De-lint.

--- src/atom-environment.coffee
+++ src/atom-environment.coffee
@@ -606,7 +606,7 @@
     # The first time the window's loaded we want to use the default dimensions.
     # But after that, e.g., when the window's been reloaded, we want to use the
     # dimensions we've saved for it.
-    if !@isFirstLoad()
+    if not @isFirstLoad()
       dimensions = @state.windowDimensions

     unless @isValidDimensions(dimensions)


Use KDInputView instead

Use KDInputView instead

--- client/Dashboard/views/groupsmembershippolicyeditor.coffee
+++ client/Dashboard/views/groupsmembershippolicyeditor.coffee
@@ -8,7 +8,7 @@
     @editorLabel = new KDLabelView
       title : 'Custom Policy Language'

-    @editor = new KDInputViewWithPreview
+    @editor = new KDInputView
       label           : @editorLabel
       type            : 'textarea'
       defaultValue    : policy.explanation


Changed name to basicContext and removed .context selector dependencies

--- src/coffee/basicContext.coffee
+++ src/coffee/basicContext.coffee
@@ -1,4 +1,9 @@
-this.context =
+this.basicContext =
+
+	_dom: (elem) ->
+
+		if not elem? then return $('.context')
+		else return $('.context').find("#{ elem }")

 	_valid: (data) ->

@@ -27,7 +32,7 @@

 		item = (row) ->

-			return '' if not context._valid row
+			return '' if not basicContext._valid row

 			span = ""
 			span = '' if row.icon is ''
@@ -70,7 +75,7 @@

 	_getPosition: (e) ->

-		e = context._normalizeEvent e
+		e = basicContext._normalizeEvent e

 		x = e.pageX
 		y = e.pageY - $(document).scrollTop()
@@ -94,19 +99,19 @@

 	_bind: (row) ->

-		$(".contextContainer td[data-name='#{ encodeURI(row.title) }']").click row.fn
+		basicContext._dom("td[data-name='#{ encodeURI(row.title) }']").click row.fn

 	show: (data, e, fnClose) ->

 		# Build context
-		$('body').append context._build(data)
+		$('body').append basicContext._build(data)
 		$('body').css	'overflow', 'hidden'

 		# Get info to calculate position
-		mousePosition	= context._getPosition(e)
+		mousePosition	= basicContext._getPosition(e)
 		contextSize		=
-			width:	$('.contextContainer .context').outerWidth true
-			height:	$('.contextContainer .context').outerHeight true
+			width:	basicContext._dom().outerWidth true
+			height:	basicContext._dom().outerHeight true
 		browserSize		=
 			width:	$('html').width()
 			height:	$('html').height()
@@ -118,20 +123,20 @@
 			mousePosition.y -= (mousePosition.y + contextSize.height) - browserSize.height

 		# Set position
-		$('.contextContainer .context').css
+		basicContext._dom().css
 			top:		"#{ mousePosition.y }px"
 			left:		"#{ mousePosition.x }px"
 			opacity:	1

 		# Close fallback
-		fnClose = context.close if not fnClose?
+		fnClose = basicContext.close if not fnClose?

 		# Bind click on background
-		$('.contextContainer').click fnClose if fnClose?
-		$('.contextContainer').click context.close if not fnClose?
+		basicContext._dom().parent().click fnClose				if fnClose?
+		basicContext._dom().parent().click basicContext.close	if not fnClose?

 		# Bind click on items
-		context._bind row for row in data
+		basicContext._bind row for row in data

 		# Do not trigger the default action of the event
 		e.preventDefault()
@@ -141,8 +146,13 @@

 		return true

+	visible: ->
+
+		if basicContext._dom().length is 0 then return false
+		return true
+
 	close: ->

-		$('.contextContainer').remove()
+		basicContext._dom().parent().remove()
 		$('body').css 'overflow', 'scroll'
 		return true


[options] fixed bug in force reload

[options] fixed bug in force reload

--- src/coffee/options/main.coffee
+++ src/coffee/options/main.coffee
@@ -24,7 +24,7 @@
                 $this.button 'loading'

                 module.comm.reloadData (result) ->
-                    showAlert 'reload-' + result
+                    module.showAlert 'reload-' + result
                     $this.button 'reset'

         module.initManifests()


isomorphicJS FTW!

isomorphicJS FTW!

--- apps/about2/client/edit.coffee
+++ apps/about2/client/edit.coffee
@@ -3,6 +3,7 @@
 sectionsTemplates = require '../templates/sections.jade'
 { DATA } = sd = require('sharify').data
 GeminiForm = require '../../../components/gemini_form/view.coffee'
+{ crop } = require '../../../lib/resizer.coffee'

 hulkCallback = (data) ->
   render(data)
@@ -18,7 +19,7 @@

 render = (data) ->
   $('#about2-edit-example').html sectionsTemplates _.extend data,
-    crop: (url, x, y, opts) -> url
+    crop: crop

 initImageUploads = ->
   $('input, textarea').each ->


tests

tests

--- apps/about2/test/routes.coffee
+++ apps/about2/test/routes.coffee
@@ -50,3 +50,24 @@
       @client.putBuffer.args[0][0].toString().should.equal JSON.stringify @req.body
       @client.putBuffer.args[0][3](null, {})
       @res.send.args[0][0].should.equal 200
+
+  describe '#sendSMS', ->
+
+    twilioConstructorArgs = null; twilioSendSmsArgs = null; send = null; next = null
+
+    beforeEach ->
+      twilio = routes.__get__ 'twilio'
+      twilio.RestClient = class TwilioClientStub
+        constructor: -> twilioConstructorArgs = arguments
+        sendSms: -> twilioSendSmsArgs = arguments
+      routes.sendSMS { param: -> '555 111 2222' }, { send: send = sinon.stub() }, next = sinon.stub()
+
+    it 'sends a link with a valid phone number', ->
+      twilioSendSmsArgs[0].to.should.equal '555 111 2222'
+      twilioSendSmsArgs[0].body.should.containEql "Download the new Artsy iPhone app here: "
+      twilioSendSmsArgs[1] null, 'SUCCESS!'
+      send.args[0][0].msg.should.containEql 'success'
+
+    it 'throws an error if twilio doesnt like it', ->
+      twilioSendSmsArgs[1] 'fail', { message: 'You suck!' }
+      next.args[0][0].should.containEql 'fail'


hotfix: sthlm6000 3rd party assets needs to be protocol relative

--- app/_src/components/work/sthlm6000/donut-chart.coffee
+++ app/_src/components/work/sthlm6000/donut-chart.coffee
@@ -24,8 +24,8 @@

 	scripts: [
-		'http://cdn.rawgit.com/mbostock/d3/v3.5.5/d3.min.js',
-		'http://cdn.rawgit.com/tweenjs/tween.js/r14/build/tween.min.js'
+		'//cdn.rawgit.com/mbostock/d3/v3.5.5/d3.min.js',
+		'//cdn.rawgit.com/tweenjs/tween.js/r14/build/tween.min.js'
 	]

 	constructor: (@$context, data) ->


tidy up subject viewer

tidy up subject viewer

--- app/assets/javascripts/components/subject-viewer.cjsx
+++ app/assets/javascripts/components/subject-viewer.cjsx
@@ -49,7 +49,6 @@
       windowInnerHeight: window.innerHeight

   loadImage: (url) ->
-    console.log 'LOADING IMAGE: ', url
     @setState loading: true, =>
       img = new Image()
       img.src = url
@@ -65,32 +64,9 @@
             # console.log @state.loading
             # console.log "Finished Loading."

-  # nextSubject: () ->
-  #   @prepareClassification()
-  #   @sendMarkClassification()
-
-  #   # # DEBUG CODE
-  #   # console.log 'CLASSIFICATION: ', @state.classification
-
-  #   # console.log JSON.stringify @state.classification # DEBUG CODE
-  #   @state.classification.send()
-  #   @setState
-  #     marks: [] # clear marks for next subject
-
-  #   # prepare new classification
-  #   if @state.subjects.shift() is undefined or @state.subjects.length <= 0
-  #     @fetchSubjects(@state.subjectEndpoint)
-  #     return
-  #   else
-  #     @setState subject: @state.subjects[0], =>
-  #       @loadImage ((if @usingFakeSubject() then @state.subject.classification.subject.location.standard else @state.subject.location.standard))
-
-  #   @state.classification = new Classification @state.subject
-
   # VARIOUS EVENT HANDLERS

   handleInitStart: (e) ->
-    console.log "sv PROPS", @props
     @props.annotation["subject_id"] = @props.subject.id
     @props.annotation["workflow_id"] = @props.workflow.id

@@ -111,7 +87,8 @@

     mouseCoords = @getEventOffset e

-    console.log 'PROPS.ANNOTATION: ', @props.annotation
+    # DEBUG CODE
+    # console.log 'PROPS.ANNOTATION: ', @props.annotation

     if markIsComplete
       toolDescription = taskDescription.tools[@props.annotation._toolIndex]
@@ -120,22 +97,19 @@
         tool: @props.annotation._toolIndex
       if toolDescription.details?
         mark.details = for detailTaskDescription in toolDescription.details
-          console.log "!taskTacking", tasks[detailTaskDescription.type]
+          # DEBUG CODE
+          #console.log "!taskTacking", tasks[detailTaskDescription.type]
           tasks[detailTaskDescription.type].getDefaultAnnotation()

     @props.annotation.value.push mark
     @selectMark @props.annotation, mark

     MarkComponent = markingTools[toolDescription.type]
-
-    console.log 'FOO ', markingTools

     if MarkComponent.defaultValues?
       defaultValues = MarkComponent.defaultValues mouseCoords
       for key, value of defaultValues
         mark[key] = value
-
-    console.log 'BAR'

     if MarkComponent.initStart?
       initValues = MarkComponent.initStart mouseCoords, mark, e
@@ -235,7 +209,9 @@
     # console.log 'SUBJECT: ', @state.subject
     viewBox = [0, 0, @state.imageWidth, @state.imageHeight]
     ToolComponent = @state.tool
-    console.log "Rendering #{if @props.active then 'active' else 'inactive'} subj viewer"
+
+    # DEBUG CODE
+    # console.log "Rendering #{if @props.active then 'active' else 'inactive'} subj viewer"

     scale = @getScale()


Make sure CourseDateUtils.isDateValid() returns a boolean

This was returning undefined rather than true. Fixes the broken javascript test.

--- app/assets/javascripts/utils/course_date_utils.coffee
+++ app/assets/javascripts/utils/course_date_utils.coffee
@@ -5,6 +5,7 @@

   isDateValid: (date) ->
     return false unless date.match(/^20\d{2}\-\d{2}\-\d{2}/) && moment(date).isValid()
+    return true

   # Returns an object of minDate and maxDate props for each date field of a course
   dateProps: (course, type) ->


render menu with qtip; freeze qtip when menu shown

--- resources/assets/lib/popup-menu.coffee
+++ resources/assets/lib/popup-menu.coffee
@@ -41,6 +41,7 @@

   componentDidMount: =>
+    @tooltipHideEvent = @tooltipElement().qtip('option', 'hide.event')
     $(window).on 'throttled-resize.#{@uuid}', @resize
     $(document).on "turbolinks:before-cache.#{@uuid}", () =>
       @removePortal()
@@ -62,8 +63,8 @@
     @portal.style.top = "#{Math.floor(top + $element.height() / 2)}px"
     @portal.style.left = "#{Math.floor(left + $element.width())}px"

-    if @context?
-      tooltipElement = $(@context).closest('.qtip')[0]
+    tooltipElement = @tooltipElement()[0]
+    if tooltipElement?
       @portal.style.zIndex = +tooltipElement.style.zIndex + 1

@@ -73,11 +74,15 @@
     if @state.active
       @resize()
       @addPortal()
+      @tooltipElement().qtip 'option', 'hide.event', false

       $(document).on "click.#{@uuid} keydown.#{@uuid}", @hide
       @props.onShow?()
+
     else
       @removePortal()
+      @tooltipElement().qtip 'option', 'hide.event', @tooltipHideEvent
+
       $(document).off "click.#{@uuid} keydown.#{@uuid}", @hide
       @props.onHide?()

@@ -106,6 +111,10 @@

   toggle: =>
     @setState active: !@state.active
+
+
+  tooltipElement: =>
+    $(@context).closest('.qtip')

   addPortal: =>


Clean

Clean

--- src/cli/util/emitter.coffee
+++ src/cli/util/emitter.coffee
@@ -1,11 +1,3 @@
-events = require 'events'
-util = require 'util'
+EventEmitter = require('events').EventEmitter

-Emitter = ->
-  events.EventEmitter.call @
-
-util.inherits Emitter, events.EventEmitter
-
-emitter = new Emitter()
-
-module.exports = emitter+module.exports = new EventEmitter()


Uses before and after to prepare the options

--- src/client/component.coffee
+++ src/client/component.coffee
@@ -75,13 +75,21 @@
           @sandbox.on(refreshOn, (=> @refresh()), @) for refreshOn in (@refreshEvents || [])
         catch e
           console.error("Error loading HullComponent", e.message)
-        sb = @sandbox
-        getId = ()->
-          return @id if @id
-          return sb.util.entity.encode(@uid) if @uid
-          sb.config.entity_id
-        options.id = getId.call(options)
-        app.core.mvc.View.prototype.constructor.apply(@, arguments)
+        app.components.before 'initialize', (options)=>
+          sb = @sandbox
+          getId = ()->
+            return @id if @id
+            return sb.util.entity.encode(@uid) if @uid
+            sb.config.entity_id
+          options.id = getId.call(options)
+          @options = _.extend(@options, options)
+        # Copy/Paste + adaptation of the Backbone.View constructor
+        # TODO remove it whenever possible
+        @cid = _.uniqueId('view')
+        @_configure(options || {})
+        @_ensureElement()
+        @invokeWithCallbacks('initialize', @, options)
+        @delegateEvents()
         @render()

       renderTemplate: (tpl, data)=>


Format

Format

--- assets/js/serviceWorker.coffee
+++ assets/js/serviceWorker.coffee
@@ -42,10 +42,10 @@
 ##
 self.addEventListener 'push', (event) ->

-    title = 'Update From Teamwork.com'
+    title = 'Hey'
     event.waitUntil self.registration.showNotification(title,
         body: 'Click to read the latest update'
-        icon: './assets/images/favicons/196.png'
+        icon: './assets/images/196.png'
         tag: 'new-article')
     return

@@ -99,9 +99,6 @@
 # @return {} none
 ##
 self.addEventListener 'fetch', (event) ->
-
-    return
-
     event.respondWith fetch(event.request).catch(->
         caches.match event.request
     )


Notification: fix regression

Notification: fix regression

--- client/Main/notifications/notificationlistitem.coffee
+++ client/Main/notifications/notificationlistitem.coffee
@@ -67,8 +67,8 @@
         @participants = new options.linkGroupClass {group:actors}
         @avatar       = new options.avatarClass
           size     :
-            width  : 40
-            height : 40
+            width  : 24
+            height : 24
           origin   : @actors[0]
         resolve()

@@ -150,7 +150,7 @@
       if post
         # TODO group slug must be prepended after groups are implemented
         # groupSlug = if post.group is "koding" then "" else "/#{post.group}"
-        router.handleRoute "/Activity/Post/#{post.message.slug}", { state: post }
+        router.handleRoute "/Activity/Post/#{post.slug}", { state: post }
       else
         new KDNotificationView
           title : "This post has been deleted!"
@@ -168,3 +168,5 @@
       when "join", "leave"
         return
         # do nothing
+
+


ActivitySidebar: Make my-workspace-2 slugged Workspaces deletable.

--- client/Social/Activity/sidebar/activitysidebar.coffee
+++ client/Social/Activity/sidebar/activitysidebar.coffee
@@ -540,7 +540,7 @@
             else
               return

-          unless workspace.isDefault
+          if not workspace.isDefault or workspace.slug isnt 'my-workspace'
             title += ""

           treeData.push


Client:Activity: Feed: Added documentation for specific problem

--- client/activity/lib/routes/singlepublicchannel.coffee
+++ client/activity/lib/routes/singlepublicchannel.coffee
@@ -50,9 +50,13 @@

     channel = channelByName channelName

+    # when the route is '/Channels/:channelName/Liked' SingleChannelRoute triggers
+    # first then SinglePublicChannelPopularMessages triggers. this check has been added
+    # to make sure if resultState has to set '/Recent'. In '/Liked' page first
+    # 'Most Liked' tab active then 'Most Recent' because of async operation.
     channelActions.loadChannelByName(channelName).then ({channel}) ->
       if shouldSetResultStateFlag routes
-        channelActions.setChannelResultStateFlag channel.id, ResultStates.RECENT
+        channelActions.changeResultState channel.id, ResultStates.RECENT

     if channelName
       transitionToChannel channelName, done
@@ -68,14 +72,14 @@

 shouldSetResultStateFlag = (routes) ->

-  setResultStateFlagAsRecent = yes
+  shouldSet = yes

   for route in routes
     path = route.path.toUpperCase()
     if ResultStates[path] and path isnt ResultStates.RECENT
-      setResultStateFlagAsRecent = no
+      shouldSet = no

-  return setResultStateFlagAsRecent
+  return shouldSet

 transitionToChannel = (channelName, done) ->


landing: root routes non-existing team redirect fixed

also routes explained better.

--- client/landing/site.landing/coffee/core/routes.coffee
+++ client/landing/site.landing/coffee/core/routes.coffee
@@ -13,17 +13,21 @@
     { router } = kd.singletons
     { groupName, group, environment } = kd.config

-    # root is home if group is koding
+    # root is team selector if group is koding
+    # meaning we're in the parent domain
+    # this works for http://koding.com
     if groupName is 'koding'
       return router.handleRoute '/Teams'

+    # this works for invalid teams like http://<non-existing-team>.koding.com
     # if there is no such group take user to group creation with given group info
     if not group
-      newUrl = "http://#{location.host.replace(groupName + '.', '')}/Teams?group=#{groupName}"
+      newUrl = "http://#{location.host.replace(groupName + '.', '')}/Teams/Create?group=#{groupName}"
       return location.replace newUrl

+    else
+    # this works for valid teams like http://<existing-team>.koding.com
     # if there is a group then take user to group login page
-    else
       return router.openSection 'Team', null, null, (app) -> app.jumpTo 'login'


Only admins and trolls can visit troll's profile page.

--- client/members/lib/contentdisplays/profileview.coffee
+++ client/members/lib/contentdisplays/profileview.coffee
@@ -28,15 +28,15 @@

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

-    options.bind   = "mouseenter mouseleave"
+    options.bind = "mouseenter mouseleave"
     super options, data

-    @memberData        = @getData()
-    { mainController } = kd.singletons
+    @memberData = @getData()
+    { mainController, router } = kd.singletons

     if @memberData.isExempt
-      if not checkFlag 'super-admin'
-        return kd.getSingleton('router').handleRoute "/Activity"
+      if not checkFlag 'super-admin' or not @memberData._id is whoami()._id
+        return router.handleRoute '/Activity'

     @firstName      = new ProfileContentEditableView
       tagName       : "span"


Fix doble submit specs

Fix doble submit specs

We have a custom implementation to avoid doble submissions which causes
some custom problems :relieved:

This commit should fix the following three specs
spec/features/comments/legislation_annotations_spec.rb:367
spec/features/comments/legislation_questions_spec.rb:333
spec/features/custom/probe_option_comments_spec.rb:298

One of the custom problems comes from ajax:success[1] not being
triggered, instead only ajax:complete seems to be triggered

We should remove this custom implementation and use the standard
solution provided in rails[2]

[1]
https://github.com/AyuntamientoMadrid/consul/blob/master/app/assets/java
scripts/prevent_double_submission.js.coffee#L28

[2]
https://stackoverflow.com/questions/9570912/how-to-disable-a-form-submit
-button-a-l%C3%A0-ruby-on-rails-way/9572893#9572893


Only use image loader on images in main_content.

--- app/assets/javascripts/alchemy/alchemy.onload.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.onload.js.coffee
@@ -30,7 +30,7 @@
     return

   # Attaches the image loader on all images
-  Alchemy.ImageLoader('img')
+  Alchemy.ImageLoader('#main_content img')

   # Observes overlay links
   Alchemy.overlayObserver()


Refactor/add caching to Address.getCountryList

--- app/assets/javascripts/sprangular/resources/address.coffee
+++ app/assets/javascripts/sprangular/resources/address.coffee
@@ -35,11 +35,5 @@
         countryId: address.country_id

     this.getCountryList = ->
-      country = [] #<%= Spree::Country.find_by_iso3('USA').to_json %>
-
-    this.getStatesList = ->
-      states = [] #<%= Spree::Country.find_by_iso3('USA').states.order('name').map { |s| { name: s.name, id: s.id } }.to_json %>
-
-    this.countryId = null #<%= Spree::Country.find_by_iso3('USA').id %>
-
-
+      $http.get('/countries', cache: true)
+           .then (response) -> response.data


Fix button_tag with empty string as content argument

--- app/assets/javascripts/ultimate/helpers/form_tag.js.coffee
+++ app/assets/javascripts/ultimate/helpers/form_tag.js.coffee
@@ -70,7 +70,7 @@
     options ||= {}
     options = _.extend({name: 'button', type: 'submit'}, options)
     content_or_options = options  if block
-    Ultimate.Helpers.Tag.content_tag 'button', content_or_options or 'Button', options, not block, block
+    Ultimate.Helpers.Tag.content_tag 'button', content_or_options ? 'Button', options, not block, block

   image_submit_tag: (source, options = {}) ->
     Ultimate.Helpers.Tag.tag 'input', _.extend({type: 'image', src: path_to_image(source)}, options)


ADD stacked-bar chart

ADD stacked-bar chart

--- flask_dashboards/static/javascripts/application.coffee
+++ flask_dashboards/static/javascripts/application.coffee
@@ -16,6 +16,7 @@
         switch div.getAttribute "dash-chart-type"
             when "bar" then chart = nv.models.discreteBarChart()
             when "line" then chart = nv.models.lineChart()
+            when "stacked-area" then chart = nv.models.stackedAreaChart().showControls(false)
             else
                 console.log "Unknown chart type: " +
                     div.getAttribute "dash-chart-type"
@@ -83,7 +84,7 @@

     request = $.get "../jobs/" + data_name + "/data", "",
         (data) ->
-            if div.getAttribute("dash-chart-type") in ["bar", "line"]
+            if div.getAttribute("dash-chart-type") in ["bar", "line", "stacked-area"]
                 create_chart div, data
             else
                 create_table div, data


feat(gridster): enable responsive option

--- app/assets/javascripts/my_dashboard/my_dashboard.coffee
+++ app/assets/javascripts/my_dashboard/my_dashboard.coffee
@@ -12,7 +12,12 @@
     $('.gridster ul:first').gridster
       widget_margins: MyDashboard.widget_margins
       widget_base_dimensions: MyDashboard.widget_base_dimensions
+      autogenerate_stylesheet: true
+      min_cols: 1
+      max_cols: 6
       avoid_overlapped_widgets: !MyDashboard.customGridsterLayout
+      resize:
+        enabled: true
       draggable:
         stop: MyDashboard.showGridsterInstructions
         start: -> MyDashboard.currentWidgetPositions = MyDashboard.getWidgetPositions()


修复大题下移时不时出错BUG

修复大题下移时不时出错BUG

--- app/assets/javascripts/question_bank/test_papers.coffee
+++ app/assets/javascripts/question_bank/test_papers.coffee
@@ -40,7 +40,7 @@
       $this = jQuery(this)
       $section = $this.closest('.section')

-      $sections = $section.parent().find('.section')
+      $sections = $section.parent().find('.section:not(.hidden)')
       index = $sections.index($section)
       if index > 0
         $prev = jQuery($sections.get(index - 1))
@@ -61,11 +61,11 @@

     @$el.on 'click', '.section_move_down', ->
       $this = jQuery(this)
-      $section = $this.parent().parent().parent()
-
-      $section.insertAfter($section.next()) if $section.next().length > 0 and !$section.next().hasClass('empty')
-
-      $sections = $section.parent().find('.section')
+      $section = $this.closest('.section')
+
+      #$section.insertAfter($section.next()) if $section.next().length > 0 and !$section.next().hasClass('empty')
+
+      $sections = $section.parent().find('.section:not(.hidden)')
       index = $sections.index($section)
       $next = jQuery($sections.get(index + 1))
       if $next


fix selecting a login when a single user is found fixes #7900

Change-Id: Iee82fd5bdad1d840c2978f4f7ee196ce1c24df9b
Reviewed-on: https://gerrit.instructure.com/9673
Tested-by: Hudson <dfb64870173313a9a7b56f814c8e3b33e268497a@instructure.com>
Reviewed-by: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/bundles/registration_confirmation.coffee
+++ app/coffeescripts/bundles/registration_confirmation.coffee
@@ -33,12 +33,11 @@

     if $where_to_log_in.length
       $('#merge_if_clicked').click ->
-        $merge_link.trigger('click', true)
+        window.location = $merge_link.attr "href"

-      $merge_link.click (event, useNative) ->
-        unless useNative
-          event.preventDefault()
-          showPane $where_to_log_in
+      $merge_link.click (event) ->
+        event.preventDefault()
+        showPane $where_to_log_in

     $registration_form.find(":text:first").focus().select()
     $registration_form.submit (event) ->


Verify scrollbar detection works

Verify scrollbar detection works

--- app/js/jail_iframe/initializers/scrollbarSuppressor.coffee
+++ app/js/jail_iframe/initializers/scrollbarSuppressor.coffee
@@ -21,5 +21,7 @@
 # rather than  document.documentElement.clientHeight so it works in css compat mode.
 window_has_scrollbar = -> window.innerHeight < document.documentElement.scrollHeight
 FactlinkJailRoot.on 'modalOpened', ->
+  if scrollbarWidth > 0 && window_has_scrollbar()
+    console.log 'has scrollbar!'
   document.documentElement.setAttribute('data-factlink-suppress-scrolling', '')
 FactlinkJailRoot.on 'modalClosed', -> document.documentElement.removeAttribute('data-factlink-suppress-scrolling')


Removed suppress_double_scrollbar feature toggle

--- app/js/jail_iframe/initializers/scrollbarSuppressor.coffee
+++ app/js/jail_iframe/initializers/scrollbarSuppressor.coffee
@@ -1,4 +1,3 @@
 FactlinkJailRoot.on 'modalOpened', ->
-  if FactlinkJailRoot.can_haz.suppress_double_scrollbar
-    document.documentElement.setAttribute('data-factlink-suppress-scrolling', '')
+  document.documentElement.setAttribute('data-factlink-suppress-scrolling', '')
 FactlinkJailRoot.on 'modalClosed', -> document.documentElement.removeAttribute('data-factlink-suppress-scrolling')


Fix loading asset

Fix loading asset

--- app/scripts/scenery/City.coffee
+++ app/scripts/scenery/City.coffee
@@ -380,6 +380,12 @@
   assets: ->
     images: ['horizon-city.png', 'water-horizon.png', 'city.png', 'city-layer2.png']
     sprites:
+      'water.png':
+        tile: 200
+        tileh: 105
+        map:
+          water1: [0, 0]
+          water2: [1, 0]
       'water-front.png':
         tile: 400
         tileh: 90


fix missing import of 'react' in analytics

refs: CNVS-31787

test plan:
analytics should work

Change-Id: Ib71b2b628f844c2d91f5746d722b1daab40870e6
Reviewed-on: https://gerrit.instructure.com/93526
Tested-by: Jenkins
Reviewed-by: Rob Orton <7e09c9d3e96378bf549fc283fd6e1e5b7014cc33@instructure.com>
Product-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>
QA-Review: Ryan Shaw <ea3cd978650417470535f3a4725b6b5042a6ab59@instructure.com>

--- app/coffeescripts/Department/DepartmentGraphView.coffee
+++ app/coffeescripts/Department/DepartmentGraphView.coffee
@@ -1,6 +1,7 @@
 define [
   'Backbone'
   'underscore'
+  'react'
   'analytics/jst/department_graphs'
   'analytics/compiled/graphs/page_views'
   'analytics/compiled/graphs/CategorizedPageViews'
@@ -11,7 +12,7 @@
   'analytics/compiled/jsx/components/ActivitiesByCategory'
   'analytics/compiled/jsx/components/GradeDistributionTable'
   'analytics/compiled/helpers'
-], (Backbone, _, template, PageViews, CategorizedPageViews, GradeDistribution, colors, util, ActivitiesTable, ActivitiesByCategory, GradeDistributionTable, helpers) ->
+], (Backbone, _, React, template, PageViews, CategorizedPageViews, GradeDistribution, colors, util, ActivitiesTable, ActivitiesByCategory, GradeDistributionTable, helpers) ->

   ##
   # Aggregate view for the Department Analytics page.


underscore needed to be loaded by use!, fixes selenium

Change-Id: I58a69ac6ae42f608fe4bfc203227b89a4897eb8c
Reviewed-on: https://gerrit.instructure.com/9623
Tested-by: Hudson <dfb64870173313a9a7b56f814c8e3b33e268497a@instructure.com>
Reviewed-by: Bryan Madsen <0a3ba1af216659500dd8f41e29f3979f42452870@instructure.com>

--- app/coffeescripts/gradebook2/GradebookHeaderMenu.coffee
+++ app/coffeescripts/gradebook2/GradebookHeaderMenu.coffee
@@ -8,7 +8,7 @@
   'compiled/gradebook2/CurveGradesDialog'
   'jst/gradebook2/GradebookHeaderMenu'
   'jst/re_upload_submissions_form'
-  'vendor/underscore'
+  'use!underscore'
   'jquery.instructure_forms'
   'jquery.instructure_jquery_patches'
   'jquery.instructure_misc_helpers'


style fix

style fix

--- browser/node.coffee
+++ browser/node.coffee
@@ -109,7 +109,7 @@

   readdirSync: (path) ->
     for key, file of localStorage when key[..5] == 'file::'
-      (DoppioFile.load key[6..]).name
+      DoppioFile.load(key[6..]).name

 root.path =
   normalize: (path) -> path


Improved set/get log config.

Improved set/get log config.

--- app/assets/javascripts/app/lib/app_init/log.js.coffee
+++ app/assets/javascripts/app/lib/app_init/log.js.coffee
@@ -29,12 +29,14 @@

 class _Singleton
   constructor: ->
-    @moduleColorsMap = {}
-    @currentConfig = {}
+    @moduleColorsMap    = {}
+    @currentConfig      = {}
+    @currentConfigReady = {}
     if window.localStorage
       raw = window.localStorage.getItem('log_config')
       if raw
         @currentConfig = JSON.parse(raw)
+        @configReady()

     # example config to enable debugging
     #@config('module', 'i18n|websocket')
@@ -46,6 +48,13 @@
     if data && (data.browser is 'Chrome' || ( data.browser is 'Firefox' && data.version >= 31.0 ) )
       @colorSupport = true

+  configReady: ->
+    for type, value of @currentConfig
+      if type is 'module' || type is 'content'
+        @currentConfigReady[type] = new RegExp(value, 'i')
+      else
+        @currentConfigReady[type] = value
+
   config: (type = undefined, value = undefined) ->

     # get
@@ -54,23 +63,22 @@
         return @currentConfig[type]
       return @currentConfig

-    # set
-    if type is 'module' || type is 'content'
-      @currentConfig[type] = new RegExp(value, 'i')
-    else
-      @currentConfig[type] = value
+    # set runtime config
+    @currentConfig[type] = value
+    @configReady()
+
     if window.localStorage
       window.localStorage.setItem('log_config', JSON.stringify(@currentConfig))

   log: ( module, level, args ) ->
     if level is 'debug'
-      return if !@currentConfig.module && !@currentConfig.content
-      return if @currentConfig.module && !module.match(@currentConfig.module)
-      return if @currentConfig.content && !args.toString().match(@currentConfig.content)
+      return if !@currentConfigReady.module && !@currentConfigReady.content
+      return if @currentConfigReady.module && !module.match(@currentConfigReady.module)
+      return if @currentConfigReady.content && !args.toString().match(@currentConfigReady.content)
     @_log( module, level, args )

   _log: ( module, level, args ) ->
-    prefixLength = 28
+    prefixLength = 32
     prefix       = "App.#{module}(#{level})"
     if prefix.length < prefixLength
       prefix += Array(prefixLength - prefix.length).join(' ')


Fix dropdown fields javascript

Fix dropdown fields javascript

--- app/assets/javascripts/dropdown_text_fields.js.coffee
+++ app/assets/javascripts/dropdown_text_fields.js.coffee
@@ -16,7 +16,7 @@
 $ ->
   $(document).on 'click', '.dropdown-menu li a.dropdown-field', (event) ->
     event.preventDefault()
-    d = $(this)
+    d = $(this).parent()
     group = d.closest('.input-group-btn')
     group.find('.dropdown-toggle span').first().text(d.find('a').text())
     group.find('input[type="hidden"]').val(d.find('span.hidden').text())


Use new RailsForm behavior on declaration.

--- app/assets/javascripts/overlays/declaration.js.coffee
+++ app/assets/javascripts/overlays/declaration.js.coffee
@@ -14,7 +14,7 @@
     declarations: ->
       {div, input, label, textarea} = React.DOM

-      @state.declarations.map (declaration, index) ->
+      @state.declarations.map (declaration, index) =>
         hiddenField = if 'id' in Object.keys(declaration)
           (input {
             id: "paper_declarations_attributes_#{index}_id"
@@ -28,9 +28,12 @@
           (textarea {
             ref: "declaration_answer_#{index}",
             id: "paper_declarations_attributes_#{index}_answer",
+            onBlur: @submitForm,
+            onChange: @updateContent,
+            "data-declaration-index": index,
             name: "paper[declarations_attributes][#{index}][answer]",
             className: 'form-control', rows: 6
-            defaultValue: declaration['answer']}),
+            value: declaration['answer']}),
           hiddenField])

     render: ->
@@ -39,11 +42,14 @@

       (main {}, [
         (h1 {}, @props.taskTitle),
-        (RailsForm {action:  "#{@props.paperPath}.json"}, @declarations())])
+        (RailsForm {action:  "#{@props.paperPath}.json", ref: 'form'},
+          @declarations())])

-    submitFormsOnChange: (rootNode) ->
-      form = $('form', rootNode)
-      Tahi.setupSubmitOnChange form, $('textarea', form)
+    submitForm: ->
+      @refs.form.submit()

-    componentDidMount: (rootNode) ->
-      @submitFormsOnChange rootNode
+    updateContent: (e) ->
+      index = $(e.target).data("declarationIndex")
+      newDecs = @state.declarations.slice()
+      newDecs[index].answer = e.target.value
+      @setState {declarations: newDecs}


update recognitions

update recognitions

--- app/assets/javascripts/rails_views/visits/show.coffee
+++ app/assets/javascripts/rails_views/visits/show.coffee
@@ -6,21 +6,27 @@
     'keyup #procedure-search': 'search'
     'keyup #med-search': 'search'
     'change #edit_meds': 'editMeds'
+    'change #edit_recognitions': 'editRecognitions'

   search: (e) ->
     $input = $(e.target)
     url = "#{$input.attr('data-action')}?utf8=%E2%9C%93&search=#{$input.val()}"
     $.get url, null, null, "script"

-  searchProcedures: (e) ->
-    value = $(e.target).val().toLowerCase()
-    if value.length > 2 then @searchBase('procedures', value) else @hideBase('procedures')
+  editBase: ($form, object_type) ->
+    values = []
+    $form.find("input:checked").each (index) ->
+      values.push($(@).val())
+    if $form.attr('action')?
+      if object_type == 'meds'
+        $.ajax({url: $form.attr('action'), type: "PUT", data: { visit: { med_ids: values } } })
+      if object_type == 'recognitions'
+        $.ajax({url: $form.attr('action'), type: "PUT", data: { visit: { recognition_ids: values } } })

   editMeds: (e) ->
-    meds = []
     $form = $('#edit_meds')
-    $form.find("input:checked").each (index) ->
-      meds.push($(@).val())
-    if $form.attr('action')? and $form.serialize()?
-      $.ajax({url: $form.attr('action'), type: "PUT", data: { visit: { med_ids: meds } } })
+    @editBase($form, 'meds')

+  editRecognitions: (e) ->
+    $form = $('#edit_recognitions')
+    @editBase($form, 'recognitions')


Deleted old pages.js.coffee feature

Deleted old pages.js.coffee feature

--- app/assets/javascripts/tenon/features/pages.js.coffee
+++ app/assets/javascripts/tenon/features/pages.js.coffee
@@ -1,14 +0,0 @@
-class Tenon.features.Pages
-  index: ->
-    $("#quick-search").quickSearch url: "/tenon/pages"
-    Tenon.features.pages.setLiPageIds()
-    Tenon.features.nestedLists.setSubpageTitleSize()
-
-  setLiPageIds: ->
-    $li = undefined
-    $span = undefined
-    $.each $("li.subpage, li.top"), ->
-      $li = $(this)
-      $span = $li.find("span.item-id")
-      $li.attr "id", $span.data("item-id")
-


getting .path

getting .path

--- app/scripts/directives/nav-bar/nav-bar-directive.coffee
+++ app/scripts/directives/nav-bar/nav-bar-directive.coffee
@@ -3,8 +3,8 @@
   BudgetLoader.loadFromRootScope()

   $scope.$watch 'currentBudgetId', (id) ->
-    #$location.path = "budgets/" + id #something like this?
-    #keep budget loader setting rootscope so we don't need to load later
+    $location.path()
+    $location.path '/budget/+id'
     BudgetLoader.setBudget(id)

   Budget.allBudgets().then (budgets) ->


don't sync dev script

don't sync dev script

--- app/templates/_Gruntfile.coffee
+++ app/templates/_Gruntfile.coffee
@@ -155,6 +155,7 @@
 					".sass-cache",
 					"*.scss",
 					"style.css",
+					"main.js",
 					"*.js.map",
 					"*.coffee",
 					"config-dev.php",


add refresh token on attachment parsed url

--- app/modules/components/wysiwyg/wysiwyg.service.coffee
+++ app/modules/components/wysiwyg/wysiwyg.service.coffee
@@ -58,6 +58,7 @@
         deferred = @q.defer()
         @attachmentsService.get(tokens[0], tokens[1]).then (response) =>
             url = response.data.url
+            url = "#{url}#_taiga-refresh=#{tokens[0]}:#{tokens[1]}"
             el.setAttribute('src', url)
             deferred.resolve(el)


Warn if we drop replication events

Warn if we drop replication events

--- src/packages/collaboration/lib/session.coffee
+++ src/packages/collaboration/lib/session.coffee
@@ -13,6 +13,9 @@
   _.extend @prototype, require('event-emitter')

   constructor: ({@site, @id, @host, @port, @secure}) ->
+    @nextOutgoingEventId = 1
+    @lastEventIdsBySite = {}
+
     @secure ?= config.get('collaboration.secure') ? true
     @host ?= config.get('collaboration.host') ? 'fallout.in'
     @participants = []
@@ -32,7 +35,7 @@
   start: ->
     @channel = @subscribe(@id)

-    @channel.on 'channel:closed', =>
+    @channel.on 'channel:closed', =>
       @listening = false
       @trigger 'stopped'

@@ -116,11 +119,24 @@
     {@clientId} = channel
     channel

+  verifyEvent: (event) ->
+    {site, id} = event
+    lastId = @lastEventIdsBySite[site]
+    if lastId? and id isnt lastId + 1
+      console.error("Expected next event to be #{lastId + 1} but got #{id} for site #{site}")
+    @lastEventIdsBySite[site] = id
+
+  stampEvent: (event) ->
+    event.id = @nextOutgoingEventId++
+    event.site = window.site.id
+
   connectDocument:  ->
     @doc.on 'replicate-change', (event) =>
+      @stampEvent(event)
       @channel.send('document-changed', event)

     @channel.on 'document-changed', (event) =>
+      @verifyEvent(event)
       @doc.applyRemoteChange(event)

   snapshotRepository: (callback) ->


fix programmer error in error predicate x2

--- client/Main/kite/oskite.coffee
+++ client/Main/kite/oskite.coffee
@@ -76,8 +76,10 @@

   vmOn: do ->
     errPredicate = (err) ->
-      KiteError.codeIsnt('ErrQuotaExceeded')(err) or
-      not /ErrQuotaExceeded/.test err.message
+      not (
+        KiteError.codeIs('ErrQuotaExceeded')(err) or
+        /ErrQuotaExceeded/.test err.message
+      )

     (t = 0) ->
       @changeState 'RUNNING', 'vm.progress.start', 'vmOn', @vmPrepareAndStart


Includes: old includes file removed

Includes: old includes file removed


client: welcome: don't show intro video until it's ready

--- client/app/lib/mainview.coffee
+++ client/app/lib/mainview.coffee
@@ -43,7 +43,7 @@
     @createSidebar()
     @createHeader()
     @createPanelWrapper()
-    @checkForIntroVideo()  unless isKoding()
+    # @checkForIntroVideo()  unless isKoding()
     @showRegistrationsClosedWarning()  if isSoloProductLite() and isKoding()
     @checkVersion()
     kd.utils.repeat (5 * 60 * 1000), @bound 'checkVersion'


KodingListView: Remove unused type

KodingListView: Remove unused type

--- client/app/lib/components/kodinglist/kodinglistview.coffee
+++ client/app/lib/components/kodinglist/kodinglistview.coffee
@@ -15,7 +15,7 @@

   askForConfirm: (options) ->

-    { title, description, callback, type, cssClass } = options
+    { title, description, callback, cssClass } = options

     if not title or not description
       return kd.warn 'You should pass title or description for confirm modal'


jupyter: further slight improvements to execution model

--- src/smc-webapp/jupyter/project-actions.coffee
+++ src/smc-webapp/jupyter/project-actions.coffee
@@ -229,6 +229,8 @@
         dbg = @dbg("manager_run_cell(id='#{id}')")
         dbg()

+        delete @_run_again?[id]  # if @_run_again[id] is set on completion of eval, then cell is run again; this is used only when re-running a cell currently running.
+
         @ensure_backend_kernel_setup()

         if not @_jupyter_kernel?
@@ -244,6 +246,8 @@
             # The cell is already running, so we must ensure cell is
             # not already running; this would happen if your run cell,
             # change input while it is still running, then re-run.
+            @_run_again ?= {}
+            @_run_again[id] = true
             @_cancel_run(id)
             return

@@ -266,6 +270,8 @@

         handler.once 'done', =>
             delete @_running_cells[id]
+            if @_run_again?[id]
+                @run_code_cell(id)

         handler.on 'more_output', (mesg, mesg_length) =>
             @set_more_output(id, mesg, mesg_length)


update UI after changing default card

update UI after changing default card

--- app/assets/javascripts/darkswarm/services/credit_cards.js.coffee
+++ app/assets/javascripts/darkswarm/services/credit_cards.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory 'CreditCards', ($http, $filter, savedCreditCards, Messages)->
+Darkswarm.factory 'CreditCards', ($http, $filter, savedCreditCards, Messages, Customers)->
   new class CreditCard
     saved: $filter('orderBy')(savedCreditCards,'-is_default')

@@ -12,6 +12,8 @@
           othercard.is_default = false
         $http.put("/credit_cards/#{card.id}", is_default: true).then (data) ->
           Messages.success(t('js.default_card_updated'))
+          for customer in Customers.index()
+            customer.allow_charges = false
         , (response) ->
           Messages.flash(response.data.flash)
       else


add | live update html when typing text

add | live update html when typing text

--- app/assets/javascripts/edit_i18n_database_translations.js.coffee
+++ app/assets/javascripts/edit_i18n_database_translations.js.coffee
@@ -28,6 +28,9 @@
     $input.val(text)
     $editor.show()

+  $body.on 'keyup', '.js-i18n-text-editor .js-input', ->
+    $text.html($(@).val())
+
   clear = ->
     $editor.hide()
     $text = null


When viewing the app from a smartphone, clicking on the Start link in the sidebar toggles sidebar visibility.

--- app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
+++ app/assets/javascripts/ng-controllers/ng-FeedbunchCtrl.js.coffee
@@ -32,6 +32,7 @@
     currentFeedSvc.unset()
     currentFolderSvc.unset()
     entriesPaginationSvc.set_busy false
+    sidebarVisibleSvc.toggle()

   #--------------------------------------------
   # Unsubscribe from a feed


fix preview tab in widget

fix preview tab in widget

--- app/assets/javascripts/uploadcare/widget/tabs/preview-tab.coffee
+++ app/assets/javascripts/uploadcare/widget/tabs/preview-tab.coffee
@@ -41,12 +41,12 @@
         blob = utils.abilities.Blob
         if source.file and blob and source.file instanceof blob
           tryToLoadImagePreview(file, source.file)
-          .fail () =>
+          .fail =>
             tryToLoadVideoPreview(file, source.file)

       @file.done ifCur (info) =>
         state = if info.isImage then 'image' else 'regular'
-        if info.mimeType == 'video/webm'
+        if info.sourceInfo.source == 'camera'
           state = 'video'
         if (state != 'image' and state != 'video') or state != @__state
           @__setState(state, {file: info})


Fixing issue with UI and state being out of sync with checkboxes

fixes: CNVS-15785

test plan:
  - Go to New Files
  - Place cursor in "Search for files" input (this is for convenience)
  - Use `tab` key until the checkbox shows up for the first file in the list (about 11 key presses)
  - Checkbox should activate by either clicking with mouse, or pressing `spacebar`
  - Checkbox should be checked

Change-Id: Ib5e68f78b0813ff54954ad3d7109a076e27e1c37
Reviewed-on: https://gerrit.instructure.com/46256
Reviewed-by: Dan Minkevitch <2591e5f46f28d303f9dc027d475a5c60d8dea17a@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Derek Hansen <cb1116771307a93b98b6babf698410ed3846296d@instructure.com>
Product-Review: Clay Diffrient <9dff2e5c98626d20d2848250c411e8009465bb87@instructure.com>

--- app/coffeescripts/react_files/mixins/MultiselectableMixin.coffee
+++ app/coffeescripts/react_files/mixins/MultiselectableMixin.coffee
@@ -51,7 +51,6 @@

     toggleItemSelected: (item, event, cb) ->
       return if event and $(event.target).closest(@multiselectIgnoredElements).length
-      event?.preventDefault()

       return @selectRange(item) if event?.shiftKey

@@ -70,4 +69,3 @@

       @setState {selectedItems: selectedItems}, ->
         cb?()
-


Added tab sorting related event handlers and methods for KDTabHandleView.

--- client/Framework/components/tabs/KDTabHandleView.coffee
+++ client/Framework/components/tabs/KDTabHandleView.coffee
@@ -11,6 +11,15 @@
       @dragStartPosX = null

     super options
+
+    @on "DragStarted", (event, dragState) =>
+      @handleDragStart event, dragState
+
+    @on "DragInAction", (x, y) =>
+      @handleDragInAction x, y
+
+    @on "DragFinished", (event) =>
+      @handleDragFinished event

   setDomElement:()->
     c = if @getOptions().hidden then "hidden" else ""
@@ -43,4 +52,85 @@
     @getOptions().hidden

   getWidth: ->
-    @$().outerWidth(no) or 0+    @$().outerWidth(no) or 0
+
+  cloneElement: (x) ->
+
+    return if @$cloned
+
+    {pane}   = @getOptions()
+    tabView  = pane.getDelegate()
+    holder   = tabView.tabHandleContainer
+    @$cloned = @$().clone()
+    holder.$().append @$cloned
+    @$cloned.css marginLeft: -(tabView.handles.length - @index) * @getWidth()
+
+  getTargetTabHandle: (index, x) ->
+    {pane}    = @getOptions()
+    tabView   = pane.getDelegate()
+    {handles} = tabView
+    targetTabHandle = null
+
+    if @isDraggingToLeft
+      targetTabHandle = handles[index - 1] unless index is 0
+    else
+      targetTabHandle = handles[index + 1] unless index is handles.length
+
+    return targetTabHandle
+
+  updateClonedElementPosition: (x) ->
+    @$cloned.css left: if not @targetTabHandle then 0 else x
+
+  reorderTabHandles: (x) ->
+
+    targetTabHandle  = @targetTabHandle = @getTargetTabHandle @index, x
+
+    if targetTabHandle
+      width = @getWidth()
+      diff  = @passedHandleLength * width
+
+      # log x, diff, width, @passedHandleLength, @index, diff + width / 2, @dragState.directionX
+
+      # return
+
+      if x > diff + width / 2 # dragging to right
+        log "to right"
+        @passedHandleLength++
+        @index++
+        @$().insertAfter @targetTabHandle.$()
+      else if x < diff + width / 2 # dragging to left
+        log "to left"
+        @passedHandleLength--
+        @index--
+        @$().insertAfter @targetTabHandle.$()
+
+  handleDragStart: (event, dragState) ->
+    @dragStartPosX      = $(event.delegateTarget).offset().left
+    {pane}              = @getOptions()
+    tabView             = pane.getDelegate()
+    {handles}           = tabView
+    @index              = handles.indexOf @
+    @draggedItemIndex   = @index
+    @passedHandleLength = 0
+
+  handleDragInAction: (x, y) ->
+
+    return unless @dragIsAllowed
+
+    @cloneElement x
+    @isDraggingToLeft = x < 0
+    @$().css opacity: 0.01
+    @updateClonedElementPosition x
+    @reorderTabHandles x
+
+  handleDragFinished: (event) ->
+
+    return unless @$cloned
+
+    @$cloned.remove()
+    @$().css { left: '', opacity: 1, marginLeft: '' }
+    @$().insertBefore @targetTabHandle.domElement if @targetTabHandle
+    @$().css left: 0 if not @targetTabHandle and @draggedItemIndex is 0
+    @targetTabHandle = null
+    @$cloned   = null
+    @emit "HandleIndexHasChanged"


remove simicolon

remove simicolon

--- client/app/components/pagination_links_component.coffee
+++ client/app/components/pagination_links_component.coffee
@@ -18,7 +18,7 @@
     pEnd = curPageNum + 5
     if pEnd > pagesCount
       needAddStart = pEnd - pagesCount
-      pEnd = pagesCount;
+      pEnd = pagesCount

     pStart -= needAddStart
     pEnd += needAddEnd


about: added missing headerview

about: added missing headerview

--- client/landing/site.landing/coffee/about/AppView.coffee
+++ client/landing/site.landing/coffee/about/AppView.coffee
@@ -1,5 +1,7 @@
-JView         = require './../core/jview'
-FooterView    = require './../home/footerview'
+JView          = require './../core/jview'
+FooterView     = require './../home/footerview'
+MainHeaderView = require './../core/mainheaderview'
+
 module.exports = class AboutView extends JView

   constructor:->
@@ -8,10 +10,13 @@

     @once 'viewAppended', -> window.scrollTo 0, 0

+    @header = new MainHeaderView
     @footer = new FooterView
+

   pistachio : ->
     """
+    {{> @header}}

About Koding


site.landing/AppView: use img instead of object

--- client/landing/site.landing/coffee/login/AppView.coffee
+++ client/landing/site.landing/coffee/login/AppView.coffee
@@ -31,7 +31,7 @@
     @logo = new kd.CustomHTMLView
       tagName    : 'a'
       cssClass   : 'koding-logo'
-      partial   : '<object type="image/svg+xml" data="/a/images/logos/header_logo.svg" class="main-header-logo">Koding Logo</object>'
+      partial    : '<img src=/a/images/logos/header_logo.svg class="main-header-logo">'
       attributes : { href : '/' }

     @backToLoginLink = new CustomLinkView


Simplify command parsing code path

Simplify command parsing code path

--- src/enfield.coffee
+++ src/enfield.coffee
@@ -41,12 +41,6 @@
   main: (argv) ->
     parsed = nopt knownOptions, shortHands, argv, 2

-    if parsed.version
-      return exports.version()
-    # No command shows help message
-    if parsed.help or parsed.argv.remain.length is 0
-      return exports.help()
-
     if parsed.log
       log.level = parsed.log
       log.verbose "Set log level: #{parsed.log}"
@@ -76,11 +70,14 @@
           .fail (err) ->
             log.error "enfield", "Could not load configuration: #{err.message}"
             process.exit -1
-      when 'help'
-        exports.help()
+      when 'version'
+        exports.version()
       else
-        log.error "enfield", "Invalid command. Use --help for more information"
-        process.exit -1
+        if command is 'help' or not command
+          exports.help()
+        else
+          log.error "enfield", "Invalid command. Use --help for more information"
+          process.exit -1

   new: (dir) ->
     unless dir


Use 'is' instead of '=='

Use 'is' instead of '=='

Co-Authored-By: daviwil <b23db01176e74adac76f54075bb2364df259e0b4@github.com>

--- src/fs-plus.coffee
+++ src/fs-plus.coffee
@@ -573,7 +573,7 @@
 # application snapshots can be generated successfully.
 isElectron2OrLower = null
 checkIfElectron2OrLower = ->
-  if isElectron2OrLower == null
+  if isElectron2OrLower is null
     isElectron2OrLower =
       process.versions.electron &&
       parseInt(process.versions.electron.split('.')[0]) <= 2


slack team url can be obtained from auth.test

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -54,12 +54,12 @@
   s3_style = process.env.HUBOT_GRAFANA_S3_STYLE if process.env.HUBOT_GRAFANA_S3_STYLE
   s3_region = process.env.HUBOT_GRAFANA_S3_REGION or 'us-standard'
   s3_port = process.env.HUBOT_GRAFANA_S3_PORT if process.env.HUBOT_GRAFANA_S3_PORT
-  slack_url = process.env.HUBOT_SLACK_URL
   slack_token = process.env.HUBOT_SLACK_TOKEN
   site = () ->
+    # prioritize S3 no matter if adpater is slack
     if (s3_bucket && s3_access_key && s3_secret_key)
       's3'
-    else if (slack_url && slack_token && robot.adapterName == 'slack')
+    else if (robot.adapterName == 'slack')
       'slack'
     else
       ''
@@ -329,23 +329,35 @@
             msg.send "#{title} - [Upload Error] - #{link}"
             req.end(body);
     'slack': (msg, title, link, body, res) ->
-      # TODO: try to avoid fs. I'm not familiar with streams in nodejs.
-      # I couldn't find a way to upload the .png from the body response.
-      fs = require 'fs'
-      tempFile = "/tmp/#{crypto.randomBytes(20).toString('hex')}.png"
-      fs.writeFileSync tempFile , body
+      # Obtain slack team URL
       request.post({
-        url: slack_url + '/api/files.upload'
+        url: 'https://slack.com/api/auth.test'
         formData:
-          channels: msg.envelope.room
           token: slack_token
-          # How could I use a readable stream directly from body?
-          file: fs.createReadStream tempFile
-          }, (err, httpResponse, body) ->
-            if err
-              robot.logger.error err
-              msg.send "#{title} - [Upload Error] - #{link}"
-          )
+        }, (err, httpResponse, slackResBody) ->
+          if err
+            robot.logger.error err
+            msg.send "#{title} - [Upload Error - invalid token/can't fetch team url] - #{link}"
+          else
+            slack_url = JSON.parse(slackResBody)["url"]
+            # TODO: try to avoid fs. I'm not familiar with streams in nodejs.
+            # I couldn't find a way to upload the .png from the body response.
+            fs = require 'fs'
+            tempFile = "/tmp/#{crypto.randomBytes(20).toString('hex')}.png"
+            fs.writeFileSync tempFile , body
+            request.post({
+              url: slack_url + '/api/files.upload'
+              formData:
+                channels: msg.envelope.room
+                token: slack_token
+                # How could I use a readable stream directly from body?
+                file: fs.createReadStream tempFile
+                }, (err, httpResponse, body) ->
+                  if err
+                    robot.logger.error err
+                    msg.send "#{title} - [Upload Error] - #{link}"
+                    )
+            )

   # Fetch an image from provided URL, upload it to S3, returning the resulting URL
   uploadChart = (msg, title, url, link, site) ->


Update documentation.

Update documentation.

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -86,12 +86,17 @@
 #   sortByConfig data, config
 #   # => ['1', '2', '3', '4', '5']
 exports.sortByConfig = sortByConfig = (data, config) ->
+  if typeof config isnt 'object'
+    throw new TypeError 'Sorting config is not an object'
+  config.before ?= []
+  config.after ?= []
   # Clone data to a new array because we
   # don't want a side effect here.
-  [data...].sort((a, b) ->
-    compareArrayItems config.before, a, b
-  ).sort (a, b) ->
-    -(compareArrayItems config.after, a, b)
+  [data...]
+    .sort (a, b) ->
+      compareArrayItems config.before, a, b
+    .sort (a, b) ->
+      -(compareArrayItems config.after, a, b)

 # Sorts by pattern.
 #
@@ -104,11 +109,13 @@
 #     ]
 #   }]
 #   config =
-#     'vendor/jquery.js':
-#       before: ['a.coffee']
-#       after: ['b.coffee']
+#     order:
+#       'vendor/jquery.js':
+#         before: ['a.coffee']
+#         after: ['b.coffee']
 #   sortLanguageFiles data, config
-#   # => [{destination: 'vendor/scripts/jquery.js', files: ['a.coffee', 'b.coffee']}]
+#   # => [{path: 'vendor/scripts/jquery.js', sourceFiles: [
+#     {path: 'a.coffee'}, {path: 'b.coffee'}]}]
 exports.sortLanguageFiles = (data, config) ->
   data.map (item) ->
     pathes = (file.path for file in item.sourceFiles)
@@ -129,6 +136,15 @@
     for sourceFile in destinationFile.sourceFiles
       result.push sourceFile.data
   result
+
+writeFiles = (items, config) ->
+  destFiles = helpers.sortLanguageFiles (helpers.groupLanguageFiles items), config
+  for destFile in destFiles
+    data = (sourceFile.data for sourceFile in destFile.sourceFiles).join ''
+    callbacks = (sourceFile.onWrite for sourceFile in destFile.sourceFiles)
+    fs.writeFile destFile.path, data, (error) =>
+      for callback in callbacks
+        callback error

 # Shell color manipulation tools.
 colors =


turned spread() into a macro

turned spread() into a macro

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -42,17 +42,31 @@
   for i in [0...a.length] by 1 then @[offset + i] = a[i]
   return

-spread = (a, i, m, n) -> # repeat the pattern a[i...i+m] so that it covers a[i...i+n]
-  if a instanceof Array
-    for j in [m...n] by 1
-      a[i + j] = a[i + j % m]
-  else
-    a = a.subarray i, i + n
-    while 2 * m < n
-      a.set a.subarray(0, m), m
-      m *= 2
-    a.set a.subarray(0, n - m), m
-  return
+macro spread (a, i, m, n) -> # repeat the pattern a[i...i+m] so that it covers a[i...i+n]
+  macro.tmpCounter ?= 0
+  (macro.codeToNode ->
+    a = a0
+    i = i0
+    m = m0
+    n = n0
+    if a instanceof Array
+      for j in [m...n] by 1
+        a[i + j] = a[i + j % m]
+    else
+      a = a.subarray i, i + n
+      while 2 * m < n
+        a.set a.subarray(0, m), m
+        m *= 2
+      a.set a.subarray(0, n - m), m
+  ).subst
+    a: macro.csToNode "t#{macro.tmpCounter++}"
+    i: macro.csToNode "t#{macro.tmpCounter++}"
+    m: macro.csToNode "t#{macro.tmpCounter++}"
+    n: macro.csToNode "t#{macro.tmpCounter++}"
+    a0: a
+    i0: i
+    m0: m
+    n0: n

 arrayEquals = (a, b) ->
   assert a instanceof Array


Removes dead code

Removes dead code

This is all implemented in the xdotool controller now.

--- src/controllers/core.coffee
+++ src/controllers/core.coffee
@@ -24,13 +24,6 @@
     commander.exec xmacro.string(req.body.text)
     res.redirect "/"

-  mouseMove: (req, res) ->
-    commander.exec "xdotool mousemove_relative -- #{req.body.xrel} #{req.body.yrel}"
-    res.json(200)
-
-  mouseClick: (req, res) ->
-    commander.exec "xdotool click #{req.body.which}"
-
   system: (req, res) ->
     cmd = req.params.cmd
     commander.exec system[cmd].call(this, req.body.options)


Make sure node is on the path when installing

This affects installing packages from within Atom since node-gyp-bin
spawns node directly so it needs to be on the PATH when the top level
install command is kicked off.

--- src/install.coffee
+++ src/install.coffee
@@ -67,6 +67,11 @@

     env = _.extend({}, process.env, HOME: @atomNodeDirectory)
     env.USERPROFILE = env.HOME if config.isWin32()
+    nodeBinFolder = path.resolve(__dirname, '..', 'bin')
+    if env.PATH
+      env.PATH = "#{nodeBinFolder}#{path.delimiter}#{env.PATH}"
+    else
+      env.PATH = nodeBinFolder
     installOptions = {env}

     installGlobally = options.installGlobally ? true


ignore ETIMEDOUT and socket hang up errors

--- src/lib/bot.coffee
+++ src/lib/bot.coffee
@@ -179,7 +179,7 @@
                 timer = null

             stderr = stderr.trim()
-            if stderr.length > 0
+            if stderr.length > 0 and stderr.indexOf('ETIMEDOUT') is -1 and stderr.indexOf('socket hang up') is -1

                 Mail.send
                     subject: '[SH-ENL-BOT] Error when fetching'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -153,7 +153,8 @@
       text: null
       width: 100

-    I[name] ?= 100
+    if not I[name]
+      I[name] = 100

     if not I["max#{name.capitalize()}"]
       if I[name]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -127,7 +127,7 @@
   @param {Object} position An x, y object representing the position of the meter
   @param {Number} radius Border radius of the meter
   @param {Boolean} showName Boolean to toggle whether or not to show the attribute associated with the meter
-  @param {String} text - A String to display over the meter. Overrides default name attribute
+  @param {String} text A String to display over the meter. Overrides default name attribute
     width - How wide the meter is
   ###
   meter: (options={}) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -15,7 +15,7 @@
 enemy.include Metered

 player.meter 'health'
-# => Sets up
+# => Sets up

 player.include(Bounded)


module_name is required

module_name is required

--- src/modules.coffee
+++ src/modules.coffee
@@ -4,10 +4,7 @@
   settings = require 'settings'

   create: (module_name, definition_fn) ->
-    if module_name?
-      module_settings = settings.with_prefix module_name
-    else
-      module_settings = settings
+    module_settings = settings.with_prefix module_name
     context_fns = {}

     cmd = (name, doc, wrapped) ->


re-add the Apple Java defineClass1, for compatibility

also remove some code duplication

--- src/natives.coffee
+++ src/natives.coffee
@@ -166,6 +166,11 @@
     true
   else
     false
+
+# avoid code dup among native methods
+native_define_class = (rs, name, bytes, offset, len, loader) ->
+  raw_bytes = ((256+b)%256 for b in bytes.array[offset...offset+len])  # convert to raw bytes
+  rs.define_class util.int_classname(name.jvm2js_str()), raw_bytes, loader

 native_methods =
   java:
@@ -303,9 +308,10 @@
         o 'getCaller(I)L!/!/Class;', (rs, i) ->
             type = rs.meta_stack().get_caller(i).method.class_type
             rs.jclass_obj(type, true)
+        o 'defineClass1(L!/!/String;[BIIL!/security/ProtectionDomain;L!/!/String;Z)L!/!/Class;', (rs,_this,name,bytes,offset,len,pd,source,unused) ->
+            native_define_class rs, name, bytes, offset, len, _this
         o 'defineClass1(L!/!/String;[BIIL!/security/ProtectionDomain;L!/!/String;)L!/!/Class;', (rs,_this,name,bytes,offset,len,pd,source) ->
-            raw_bytes = ((256+b)%256 for b in bytes.array[offset...offset+len])  # convert to unsigned bytes
-            rs.define_class util.int_classname(name.jvm2js_str()), raw_bytes, _this
+            native_define_class rs, name, bytes, offset, len, _this
         o 'resolveClass0(L!/!/Class;)V', (rs, _this, cls) ->
             rs.load_class cls.$type, true
       ],
@@ -380,8 +386,7 @@
         ]
         Proxy: [
           o 'defineClass0(L!/!/ClassLoader;L!/!/String;[BII)L!/!/Class;', (rs,cl,name,bytes,offset,len) ->
-              raw_bytes = ((256+b)%256 for b in bytes.array[offset...offset+len])  # convert to unsigned bytes
-              rs.define_class util.int_classname(name.jvm2js_str()), raw_bytes, cl
+              native_define_class rs, name, bytes, offset, len, cl
         ]
       Runtime: [
         o 'availableProcessors()I', () -> 1
@@ -944,8 +949,7 @@
         o 'putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V', (rs,_this,obj,offset,new_obj) ->
             obj.set_field_from_offset rs, offset, new_obj
         o 'defineClass(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;', (rs, _this, name, bytes, offset, len, loader, pd) ->
-            raw_bytes = ((256+b)%256 for b in bytes.array[offset...offset+len])
-            rs.define_class util.int_classname(name.jvm2js_str()), raw_bytes, loader
+            native_define_class rs, name, bytes, offset, len, loader
       ]
     reflect:
       ConstantPool: [


:racehorse: Reuse regexes

:racehorse: Reuse regexes

--- src/pattern.coffee
+++ src/pattern.coffee
@@ -1,4 +1,9 @@
 _ = require 'underscore-plus'
+
+AllDigitsRegex = /\\\d+/g
+CaptureIndexRegex = /\$(\d+)/
+CustomCaptureIndexRegex = /\${(\d+):\/(downcase|upcase)}/
+DigitRegex = /\\\d+/

 module.exports =
 class Pattern
@@ -13,7 +18,7 @@
     @scopeName = name ? contentName # TODO: We need special treatment of contentName

     if match
-      if (end or @popRule) and @hasBackReferences ?= /\\\d+/.test(match)
+      if (end or @popRule) and @hasBackReferences ?= DigitRegex.test(match)
         @match = match
       else
         @regexSource = match
@@ -79,7 +84,7 @@
     for {start, end} in beginCaptureIndices
       beginCaptures.push line[start...end]

-    resolvedMatch = @match.replace /\\\d+/g, (match) ->
+    resolvedMatch = @match.replace AllDigitsRegex, (match) ->
       index = parseInt(match[1..])
       _.escapeRegExp(beginCaptures[index] ? "\\#{index}")

@@ -104,7 +109,7 @@
       [this]

   resolveScopeName: (line, captureIndices) ->
-    resolvedScopeName = @scopeName.replace /\${(\d+):\/(downcase|upcase)}/, (match, index, command) ->
+    resolvedScopeName = @scopeName.replace CustomCaptureIndexRegex, (match, index, command) ->
       capture = captureIndices[parseInt(index)]
       if capture?
         replacement = line.substring(capture.start, capture.end)
@@ -115,7 +120,7 @@
       else
         match

-    resolvedScopeName.replace /\$(\d+)/, (match, index) ->
+    resolvedScopeName.replace CaptureIndexRegex, (match, index) ->
       capture = captureIndices[parseInt(index)]
       if capture?
         line.substring(capture.start, capture.end)


amex cards should always have a 4 digit cvc.

--- src/payment.coffee
+++ src/payment.coffee
@@ -8,7 +8,7 @@
       pattern: /^3[47]/
       format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/
       length: [15]
-      cvcLength: [3..4]
+      cvcLength: [4]
       luhn: true
   }
   {
@@ -446,4 +446,4 @@
     return true

 module.exports = Payment
-global.Payment = Payment+global.Payment = Payment


browser compatibility

browser compatibility

--- src/request.coffee
+++ src/request.coffee
@@ -10,7 +10,6 @@
   string.toLowerCase()
     .replace("_", "-")
     .replace /(^|-)(\w)/g, (s) -> s.toUpperCase()
-

 module.exports = class Request

@@ -30,7 +29,7 @@
       method: @method
       headers: @headers

-    if @body
+    if @body && Buffer?
       @headers["Content-Length"] = Buffer.byteLength(@body)

     raw = client.request parameters, (response) =>
@@ -116,7 +115,11 @@
   process: (callback) ->
     # TODO: take encoding into account
     # TODO: check content-length against actual length
-    @buffer = Buffer.concat @chunks, @length
+    if Buffer?
+      @buffer = Buffer.concat @chunks, @length
+    else
+      @buffer = @chunks.join("")
+
     @process_encoding =>
       @process_type callback


Use new for instantiation

Use new for instantiation

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -104,7 +104,7 @@
   start: (callback) ->
     @server = http.createServer()
     @transport = new WebSocketTransport @server
-    @protocol = protocol.Protocol @transport, @coordinator
+    @protocol = new protocol.Protocol @transport, @coordinator

     @server.on 'request', (request, response) =>
       @serveFrontpage request, response


smarter storage upgrading

smarter storage upgrading

--- src/storage.coffee
+++ src/storage.coffee
@@ -8,18 +8,25 @@
     constructor: (config) ->
         @get null, (it) =>
             if not it.ver? or it.ver < config.ver
+
+                # if the previous version is sufficiently recent,
+                # we keep some user specified values
+                if '0.3.0' <= it.ver
+                    keep = [
+                      'kfs'
+                      'keymode'
+                      'mode'
+                      'color'
+                      'auto_opac'
+                    ]
+
+                    for key in keep
+                        do -> config[key] = it[key] if it[key]?
+
                 @clear =>
                     @set config, =>
                         @print()
                         @bind_events()
-            else
-                obj = {}
-                for own k, v of config
-                    do ->
-                        obj[k] = v if not it[k]?
-                @set obj, =>
-                    @print()
-                    @bind_events()

     set: (obj, cb) ->
         if H.contains 'altitude', (k for own k, _ of obj)


Return a JSON ok when successful in creating box

--- code/serv.coffee
+++ code/serv.coffee
@@ -62,12 +62,14 @@
 app.post "/:box_name$", (req, res) ->
   res.header('Content-Type', 'application/json')
   exports.unix_user_add req.params.box_name, (err, stdout, stderr) ->
-    res.send {error: "Error adding user: #{err} #{stderr}"} if err? or stderr?
+    any_stderr = stderr is not ''
+    res.send {error: "Error adding user: #{err} #{stderr}"} if err? or any_stderr
+
     new User({apikey: req.body.apikey}).save()
     User.findOne {apikey: req.body.apikey}, (err, user) ->
       return res.send {error: "User not found" }, 404 unless user?
       new Box({user: user._id, name: req.params.box_name}).save()
-      res.send 'ok'
+      res.send '{"status": "ok"}'

 # Add an SSH key to a box
 app.post "/:box_name/sshkeys$", (req, res) ->


Added convert class snippets

Added convert class snippets

--- completions.cson
+++ completions.cson
@@ -1935,6 +1935,93 @@
     }
   ]

+# Convert
+'Convert value to XML attribute':
+  'prefix': 'convertraw2att'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2att(${1})${0}"
+'Convert value to HTML attribute':
+  'prefix': 'convertraw2htmlatt'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2htmlatt(${1})${0}"
+'Convert value to HTML name':
+  'prefix': 'convertraw2htmlname'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2htmlname(${1})${0}"
+'Convert value to HTML ID':
+  'prefix': 'convertraw2htmlid'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2htmlid(${1})${0}"
+'Convert value to XML':
+  'prefix': 'convertraw2xml'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2xml(${1})${0}"
+'Convert value to javascript':
+  'prefix': 'convertraw2js'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2js(${1})${0}"
+'Convert value to JSON':
+  'prefix': 'convertraw2json'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2json(${1})${0}"
+'Convert array to JSON':
+  'prefix': 'convertarray2json'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::array2json(${1})${0}"
+'Convert value to SQL':
+  'prefix': 'convertraw2sql'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2sql(${1})${0}"
+'Convert symbolic identifier to SQL':
+  'prefix': 'convertsymbol2sql'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::symbol2sql(${1}, ${2:'.'})${0}"
+'Convert XML to raw text':
+  'prefix': 'convertxml2raw'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::xml2raw(${1})${0}"
+'Convert JSON to an object':
+  'prefix': 'convertjson2obj'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::json2obj(${1})${0}"
+'Convert JSON to an array':
+  'prefix': 'convertjson2array'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::json2array(${1})${0}"
+'Convert XML to an array':
+  'prefix': 'convertxml2array'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::xml2array(${1}, {$2:false}, {$3:false})${0}"
+'Convert to link if valid':
+  'prefix': 'convertlinkIfMatch'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::linkIfMatch(${1})${0}"
+'Convert HTML to plain text':
+  'prefix': 'converthtml2raw'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::html2raw(${1})${0}"
+'Convert value to url':
+  'prefix': 'convertraw2url'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': "Convert::raw2url(${1})${0}"
+
 ######
 #
 # TagField


sidebar modals: remove conversationsmodal since it\'s not used anymore

--- client/activity/lib/sidebar/conversationsmodal.coffee
+++ client/activity/lib/sidebar/conversationsmodal.coffee
@@ -1,63 +0,0 @@
-kd = require 'kd'
-KDCustomHTMLView = kd.CustomHTMLView
-KDInputView = kd.InputView
-KDListViewController = kd.ListViewController
-SidebarSearchModal = require 'app/activity/sidebar/sidebarsearchmodal'
-SidebarPinnedItem = require 'app/activity/sidebar/sidebarpinneditem'
-
-
-module.exports = class ConversationsModal extends SidebarSearchModal
-
-  constructor: (options = {}, data) ->
-
-    options.cssClass    or= 'conversations activity-modal'
-    options.width        ?= 330
-    options.height      or= 'auto'
-    options.overlay      ?= yes
-    options.endpoints     =
-      fetch               : kd.singletons.socialapi.channel.fetchPinnedMessages
-      search              : kd.singletons.socialapi.channel.byName
-
-    options.title       or= 'Conversations you follow'
-    options.placeholder or= 'Search'
-    options.noItemFound or= 'You don\'t follow any conversations yet.'
-    options.content     or= ''
-
-    super options, data
-
-    {appManager, router} = kd.singletons
-    appManager.tell 'Activity', 'bindModalDestroy', this, router.visitedRoutes.last
-
-
-  viewAppended: ->
-
-    @addSubView new KDInputView
-      cssClass    : 'hidden'
-      placeholder : 'Search conversations...'
-      keyup       : kd.utils.debounce 300, @bound 'search'
-
-    @listController = new KDListViewController
-      startWithLazyLoader : yes
-      noItemFoundWidget   : new KDCustomHTMLView
-        cssClass          : 'nothing hidden'
-        partial           : 'You didn\'t participate in any conversations yet.'
-      lazyLoadThreshold   : 100
-      lazyLoaderOptions   :
-        spinnerOptions    :
-          size            :
-            width         : 16
-            height        : 16
-        partial           : ''
-      useCustomScrollView : yes
-      viewOptions         :
-        type              : 'activities'
-        itemClass         : SidebarPinnedItem
-        cssClass          : 'activities'
-
-    @addSubView @listController.getView()
-
-    @listController.customScrollView.wrapper.on 'LazyLoadThresholdReached', @bound 'handleLazyLoad'
-
-    @fetch {}, @bound 'populate'
-
-


add whitespace

add whitespace

--- src/vorrang.coffee
+++ src/vorrang.coffee
@@ -139,7 +139,7 @@
       node = this.nodes[x]
       unless node?
         throw new Error "searching whereAllParentsIn of `#{x}` which is not in graph"
-      for key,child of node.children
+      for key, child of node.children
         # dont look at the same node twice
         if resultSet[key]?
           continue


Add support of redux

Add support of redux

--- index.cjsx
+++ index.cjsx
@@ -138,10 +138,13 @@
     return

 initialize = (e) ->
-  return if !localStorage.start2Body?
-  body = JSON.parse localStorage.start2Body
+  if window.getStore?
+    api_mst_shipgraph = window.getStore('const.$shipgraph')
+  else
+    return if !localStorage.start2Body?
+    {api_mst_shipgraph} = JSON.parse localStorage.start2Body
   {_ships, _decks, _teitokuLv} = window
-  shipgraph[ship.api_filename] = ship.api_id for ship in body.api_mst_shipgraph
+  shipgraph[ship.api_filename] = ship.api_id for ship in api_mst_shipgraph
   getSubtitles()

 alert = (text, prior, stickyFor) ->