groups: more avatar wip

groups: more avatar wip

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -130,18 +130,19 @@
         forms     :
           "Avatar":
             title : "Select an Avatar"
-            partial : ""
+            partial : ""
             callback :(formData)=>
               {inputs, buttons} = modal.modalTabs.forms["Avatar"]
+
               fileData = inputs['Drop Image here'].fileList.items[0]?.data
-              log fileData
+              log 'fileData is',fileData

               if fileData
-                log 'uploading to s3'
+                log 'uploading to s3 should happen here. IMPLEMENT ME!'
                 # upload file to S3

                 # add image URL to data.avatar
-                log 'closing modal'
+                log 'closing modal now.'
                 modal.destroy()

             buttons:


Use templateHelpers function

Use templateHelpers function

--- src/scripts/modules/media/title/title.coffee
+++ src/scripts/modules/media/title/title.coffee
@@ -7,7 +7,9 @@
   require('less!./title')

   return class MediaTitleView extends BaseView
-    template: (data) ->
+    template: template
+
+    templateHelpers: (data) ->
       data.share =
         url: window.location.href.split('#')[0] # Get the current URL without a hash string
         source: data.source or data.currentPage.source or 'OpenStax College'
@@ -19,7 +21,7 @@
       _.each data.share, (value, key, list) ->
         list[key] = encodeURI(value)

-      return template(data)
+      return {share: data.share}

     onRender: () ->
       $share = @$el.find('.share')


Removing a lot of dead code, and updating spec

--- dashboard_poller_spec.coffee
+++ dashboard_poller_spec.coffee
@@ -1,29 +0,0 @@
-#= require lib/dashboard_poller
-#= require mock-ajax
-
-describe "Dashboard Poller", ->
-  beforeEach ->
-    jasmine.Ajax.useMock()
-    @dashboard = ttm.lib.DashboardPoller.build()
-
-  describe "when the report is ready on the first iteration", ->
-    it "should return the dashboard data", ->
-      that = this
-
-      @dashboard.getData { type: 'lesson_leaders' }, (data) ->
-        that.data = data
-
-      request = mostRecentAjaxRequest()
-      expect(request.url).toBe('/dashboard/queries')
-      expect(request.method).toBe('POST')
-      request.response { status: 200, responseText: '{"id": "REPORT_ID" }' }
-
-      waits(1000)
-
-      runs ->
-        request = mostRecentAjaxRequest()
-        expect(request.url).toBe('/dashboard/queries/REPORT_ID/ready')
-        expect(request.method).toBe('GET')
-        request.response { status: 200, responseText: '{"is_ready": true, "data":"{ \\"key\\": \\"DATA\\" }" }' }
-
-        expect(that.data.key).toEqual 'DATA'


Move clearTimeout to the top

Move clearTimeout to the top

--- select.coffee
+++ select.coffee
@@ -10,12 +10,12 @@
 lastKeysTimeout = undefined

 $(window).on 'keydown keypress', (e) ->
+    clearTimeout lastKeysTimeout
+
     $focusedTarget = $('.drop-select-target-focused:first')
     return unless $focusedTarget.length and $focusedTarget.data('select')

     select = $focusedTarget.data('select')
-
-    clearTimeout lastKeysTimeout

     if select.dropSelect.isOpened() and e.keyCode is ESCAPE
         select.dropSelect.close()


max-height change for modal

max-height change for modal

--- public/models/modal.coffee
+++ public/models/modal.coffee
@@ -12,7 +12,7 @@
         "position": "fixed"
         "left": "50%"
         "top": "10%"
-        "max-height": "100%"
+        "max-height": "80%"
         "max-width": "1000px"
         "min-width": "200px"
         "overflow": "scroll"


fixed issue with closing by updating menu file

--- menus/script.cson
+++ menus/script.cson
@@ -9,9 +9,9 @@
     'submenu': [
       'label': 'Script'
       'submenu': [
-        { 'label': 'Run Script', 'command': 'script:run-selection' },
+        { 'label': 'Run Script', 'command': 'script:run' },
         { 'label': 'Stop Script', 'command': 'script:kill-process' },
-        { 'label': 'Close Window and Stop Script', 'command': 'script:close-selection' }
+        { 'label': 'Close Window and Stop Script', 'command': 'script:close-view' }
       ]
     ]
   }


remove unused url

remove unused url

--- app.coffee
+++ app.coffee
@@ -35,9 +35,6 @@
 app.get '/', (req, res) ->
   res.sendfile __dirname + '/public/index.html'

-app.get '/hello.html', (req, res) ->
-  res.sendfile __dirname + '/public/hello.html'
-
 class ThriftService
   constructor: (@serviceCls, @handlerClass) ->


Different settings for dev vs production

--- app.coffee
+++ app.coffee
@@ -46,22 +46,28 @@
 #   res.render '500.ejs', { status: 500, message: "TBD" }

+# Development settings
 app.configure 'development', ->
+
   app.use express.errorHandler({ dumpExceptions: true, showStack: true })
-  console.log 'development configuration'

+  app.set "dataOptions", {
+      dataPath: __dirname + "/data"
+      createDataFileIfNotFound: false
+      showDrafts: false
+    }
+
+
+# Production settings
+# TODO: make showDrafs depending on user logged in
+# TODO: Read settings from external file so that we don't need to hard-code them here.
 app.configure 'production', ->
   app.use express.errorHandler()
-  console.log 'production configuration'
-
-
-# Application settings
-# TODO: make showDrafs depending on user logged in
-app.set "dataOptions", {
-  dataPath: __dirname + "/data"
-  createDataFileIfNotFound: false
-  showDrafts: false
-}
+
+  app.set "dataOptions", {
+      dataPath: __dirname + "/../data"
+      createDataFileIfNotFound: false
+  }

 # Routes
@@ -96,6 +102,7 @@

 app.get '*', siteRoutes.notFound

+
 # Fire it up!
 server = http.createServer(app)
 port = app.get('port')
@@ -104,3 +111,4 @@
   machine = os.hostname()
   environment = app.settings.env
   Logger.info "Express server listening on #{address} in #{environment} mode (#{machine})"
+  console.log app.settings.dataOptions


clear

clear

--- app.coffee
+++ app.coffee
@@ -79,6 +79,7 @@

 app.get '/add/:tagname', (req, res) -> #
 	console.log 'Notification for', req.params.tagname
+	last_set = []
 	tagName = req.params.tagname
 	res.send 'OK LISTENING... ' + req.params.tagname


reinsert vendor specific fullscreen code in thyme for older browsers

--- app/assets/javascripts/thyme.coffee
+++ app/assets/javascripts/thyme.coffee
@@ -552,15 +552,44 @@
     return

   # Event listener for the full-screen button
+  # unfortunately, lots of brwoser specific code
   fullScreenButton.addEventListener 'click', ->
     if fullScreenButton.dataset.status == 'true'
-      document.exitFullscreen() if document.exitFullscreen
-    else
-      thymeContainer.requestFullscreen() if thymeContainer.requestFullscreen
+      if document.exitFullscreen
+        document.exitFullscreen()
+      else if document.mozCancelFullScreen
+        document.mozCancelFullScreen()
+      else if document.webkitExitFullscreen
+        document.webkitExitFullscreen()
+    else
+      if thymeContainer.requestFullscreen
+        thymeContainer.requestFullscreen()
+      else if thymeContainer.mozRequestFullScreen
+        thymeContainer.mozRequestFullScreen()
+      else if thymeContainer.webkitRequestFullscreen
+        thymeContainer.webkitRequestFullscreen()
     return

   document.onfullscreenchange = ->
     if document.fullscreenElement != null
+      fullScreenButton.innerHTML = 'fullscreen_exit'
+      fullScreenButton.dataset.status = 'true'
+    else
+      fullScreenButton.innerHTML = 'fullscreen'
+      fullScreenButton.dataset.status = 'false'
+    return
+
+  document.onwebkitfullscreenchange = ->
+    if document.webkitFullscreenElement != null
+      fullScreenButton.innerHTML = 'fullscreen_exit'
+      fullScreenButton.dataset.status = 'true'
+    else
+      fullScreenButton.innerHTML = 'fullscreen'
+      fullScreenButton.dataset.status = 'false'
+    return
+
+  document.onmozfullscreenchange = ->
+    if document.mozFullScreenElement != null
       fullScreenButton.innerHTML = 'fullscreen_exit'
       fullScreenButton.dataset.status = 'true'
     else


Don't build all neft targets on CI

Don't build all neft targets on CI

--- scripts/ensure-bundle.coffee
+++ scripts/ensure-bundle.coffee
@@ -4,6 +4,6 @@

 BUNDLE_PATH = path.join(__dirname, '../cli/bundle')

-unless fs.existsSync(BUNDLE_PATH)
+if not process.env.CI and not fs.existsSync(BUNDLE_PATH)
     fs.mkdirSync BUNDLE_PATH
     cp.execSync 'npm install && npm run bundle', stdio: [0, 1, 2]


Added file extension for exports.

Added file extension for exports.

--- PHPGuzzleCodeGenerator.coffee
+++ PHPGuzzleCodeGenerator.coffee
@@ -121,8 +121,9 @@

 PHPGuzzleCodeGenerator.identifier =
-    "com.luckymarmot.PawExtensions.PHPGuzzleCodeGenerator";
+    "com.luckymarmot.PawExtensions.PHPGuzzleCodeGenerator"
 PHPGuzzleCodeGenerator.title =
-    "PHP (Guzzle)";
+    "PHP (Guzzle)"
+PHPGuzzleCodeGenerator.fileExtension = "php"

 registerCodeGenerator PHPGuzzleCodeGenerator


Added beforeRun and afterRun callbacks.

Added beforeRun and afterRun callbacks.

--- server.coffee
+++ server.coffee
@@ -221,6 +221,8 @@
     @_id = nextId++
     @_onInvalidateCallbacks = []
     @_onStopCallbacks = []
+    @_beforeRunCallbacks = []
+    @_afterRunCallbacks = []
     @_recomputing = false

     @_trackerInstance = Tracker._trackerInstance()
@@ -263,6 +265,16 @@
     else
       @_onStopCallbacks.push f

+  beforeRun: (f) ->
+    throw new Error "beforeRun requires a function" unless typeof f is "function"
+
+    @_beforeRunCallbacks.push f
+
+  afterRun: (f) ->
+    throw new Error "afterRun requires a function" unless typeof f is "function"
+
+    @_afterRunCallbacks.push f
+
   invalidate: ->
     # TODO: Why some tests freeze if we wrap this method into FiberUtils.synchronize?
     if not @invalidated
@@ -286,10 +298,10 @@

       delete Tracker._computations[@_id]

-      for callback in @_onStopCallbacks
+      while @_onStopCallbacks.length
+        callback = @_onStopCallbacks.shift()
         Tracker.nonreactive =>
           callback @
-      @_onStopCallbacks = []

   # Runs an arbitrary function inside the computation. This allows breaking many assumptions, so use it very carefully.
   _runInside: (func) ->
@@ -312,7 +324,18 @@
     FiberUtils.synchronize guard, @_id, =>
       @invalidated = false

-      @_runInside @_func
+      @_runInside (computation) =>
+        while @_beforeRunCallbacks.length
+          callback = @_beforeRunCallbacks.shift()
+          Tracker.nonreactive =>
+            callback @
+
+        @_func.call null, @
+
+        while @_afterRunCallbacks.length
+          callback = @_afterRunCallbacks.shift()
+          Tracker.nonreactive =>
+            callback @

   _needsRecompute: ->
     @invalidated and not @stopped


bug fix for saving data to redis

bug fix for saving data to redis

--- server.coffee
+++ server.coffee
@@ -336,11 +336,12 @@
         if err
           res.send 500, { error: "API call failed" }
           return
-        rc.hmget "communities", repo.name, (err, reply) ->
+        rc.hmget "communities", community, (err, reply) ->
           if not err
             repo = JSON.parse reply
             repo.members.push req.user
-            repo.members_count = repo.members.length + repo.admins.length
+            repo.members_count = repo.members.length + repo.admins.length
+            rc.hmset "communities", repo.name, JSON.stringify(repo)
           res.json resp
     else
       res.send 500, {error: "Internal error"}


moved spacing to line 100 for stream filter

--- public/dreamcatcher/controllers/application_controller.coffee
+++ public/dreamcatcher/controllers/application_controller.coffee
@@ -86,6 +86,17 @@
       fade: 250
     );

+
+
+
+
+
+
+
+
+
+
+
     # STREAM FILTER - entry type
     $('#entry-filter').selectmenu(
       style: 'popup'


removed log call

removed log call

--- public/dreamcatcher/controllers/application_controller.coffee
+++ public/dreamcatcher/controllers/application_controller.coffee
@@ -8,7 +8,6 @@
     @element = $(el)
     @publish 'app.initUi'

-    log 'doing chrome frame check'
     CFInstall.check { mode: "overlay", destination: "http://dreamcatcher.net" }

     $('#metaMenu').metaMenu()


killer of whitespace, reimplementing the activity owner check

--- client/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
+++ client/app/Applications/Activity.kdapplication/ContentDisplays/Discussion.coffee
@@ -93,11 +93,11 @@
         href      : '#'
       cssClass    : 'edit-link hidden'

-    # activity = @getDelegate().getData()
+    activity = @getData()
     bongo.cacheable data.originId, "JAccount", (err, account)=>
       loggedInId = KD.whoami().getId()
       if loggedInId is data.originId or       # if comment owner
-         # loggedInId is activity.originId or   # if activity owner
+         loggedInId is activity.originId or   # if activity owner
          KD.checkFlag "super-admin", account  # if super-admin

         @listenTo
@@ -121,9 +121,7 @@
                       @emit "DiscussionWasEdited", discussion
                       @editDiscussionForm.setClass "hidden"
                       @$(".discussion-body .data").show()
-
               , data
-

               @addSubView @editDiscussionForm, "p.discussion-body", yes
               @$(".discussion-body .data").hide()
@@ -169,9 +167,9 @@
                 type     : "mini"
                 cssClass : "error editor"
                 title     : "Error, please try again later!"
+
   render:->
     super()
-    # @$("code").addClass "prettyprint"
     @$("pre").addClass "prettyprint"
     prettyPrint()

@@ -180,19 +178,8 @@
     @setTemplate @pistachio()
     @template.update()

-    # @$("code").addClass "prettyprint"
     @$("pre").addClass "prettyprint"
     prettyPrint()
-
-    # temp for beta
-    # take this bit to comment view
-    # this is useful when the snaphots don't work
-    # if @getData().repliesCount? and @getData().repliesCount > 0
-    #   opinionController = @opinionBox.opinionController
-    #   opinionController.fetchRelativeOpinions 5, 0, (err, opinions)=>
-    #     for opinion in opinions
-    #       @opinionBox.opinionList.addItem opinion, null, {type : "slideDown", duration : 100}
-    #     @opinionBox.opinionList.emit "RelativeOpinionsWereAdded"

   pistachio:->
     """
@@ -228,8 +215,8 @@
     </div>
     </div>
     <div class="discussion-nav">
+      {{> @staticLinkBox}}
       {{> @jumpToReplyLink}}
-      {{> @staticLinkBox}}
     </div>

     """


a bit of cleanup

a bit of cleanup

--- app/classify.cjsx
+++ app/classify.cjsx
@@ -39,18 +39,14 @@
     @main = document.getElementsByClassName('main')[0]

   componentWillReceiveProps: (nextProps) ->
-    console.log 'receiving props'
     if nextProps.user isnt @props.user
       if nextProps.user?.preferences?.chimp?.skip_first_step is "true"
         @setState skipImages: true
         @refs.skipCheckbox?.getDOMNode().checked = true

     if nextProps.user is null
-      console.log 'no user'
       @setState skipImages: false
       @refs.skipCheckbox?.getDOMNode().checked = false
-
-      # @openTutorial 'general'

     unless nextProps.user?.classification_count > 0
       @openTutorial 'general'


reduced complecity on computeproviders/machine.coffee

--- workers/social/lib/social/models/computeproviders/machine.coffee
+++ workers/social/lib/social/models/computeproviders/machine.coffee
@@ -558,6 +558,25 @@
         @update $set: { label }, (err) -> kallback err, slug

+  shareWithHelper =
+
+    validateTarget : (target, user, callback) ->
+      # At least one target is required
+      if not target or target.length is 0
+        return callback new KodingError 'A target required.'
+
+      # Max 9 target can be passed
+      if target.length > 9
+        return callback new KodingError \
+          'It is not allowed to change more than 9 state at once.'
+
+      # Owners cannot unassign them from a machine
+      # Only another owner can unassign any other owner
+      if user.username in target
+        return callback \
+          new KodingError 'You are not allowed to change your own state!'
+
+
   # .shareWith can be used like this:
   #
   # JMachineInstance.shareWith {
@@ -586,20 +605,7 @@

       { target, permanent, asUser } = options

-      # At least one target is required
-      if not target or target.length is 0
-        return callback new KodingError 'A target required.'
-
-      # Max 9 target can be passed
-      if target.length > 9
-        return callback new KodingError \
-          'It is not allowed to change more than 9 state at once.'
-
-      # Owners cannot unassign them from a machine
-      # Only another owner can unassign any other owner
-      if user.username in target
-        return callback \
-          new KodingError 'You are not allowed to change your own state!'
+      shareWithHelper.validateTarget target, user, callback

       # For Koding provider credential field is username
       # and we don't allow them to be removed from users


Forgot about closing the uploader using button

--- resources/assets/coffee/react/profile-page/header-main.coffee
+++ resources/assets/coffee/react/profile-page/header-main.coffee
@@ -84,21 +84,24 @@
       div
         className: 'profile-header__actions',
         if @props.withEdit
-          div null,
+          div
+            ref: (el) =>
+              @coverSelector = el
             a
               className: 'btn-circle'
               onClick: @toggleEdit
               el Icon, name: 'pencil'
             if @state.editing
-              el ProfilePage.CoverSelector, canUpload: @props.user.isSupporter, cover: @props.user.cover
+              el ProfilePage.CoverSelector,
+                canUpload: @props.user.isSupporter
+                cover: @props.user.cover

   closeEdit: (e) =>
     return unless @state.editing

     if e?
-      return if $(e.target).closest('.profile-cover-change-popup').length
-      return if $(e.target).closest('.js-profile-header__change-cover-button').length
+      return if $(e.target).closest(@coverSelector).length

     return if $('#overlay').is(':visible')
     return if document.body.classList.contains('modal-open')


Add renaming strategy, but it's buggy right now.

--- lambda.coffee
+++ lambda.coffee
@@ -24,7 +24,7 @@
   # Log indented messages.
   log = (msg) ->
     ind = ''
-    for i in [0...indent] then ind += '  '
+    for i in [0...indent] then ind += '| '
     console.log "#{ind}#{msg}"

   makeTracing = (name, fn) ->
@@ -40,6 +40,13 @@
     for own name, fn of arg
       @prototype[name] = if traceEnabled then makeTracing name, fn else fn

+# Utility functions:
+removeAll = (arr, x) ->
+  y for y in arr when y isnt x
+
+print = (obj) ->
+  console.log JSON.stringify obj, null, 2
+
 class Node
   leftApplyString: -> '' + @
   rightApplyString: -> '' + @
@@ -49,10 +56,11 @@
 class VarNode extends Node
   constructor: (@name) ->
   toString: -> @name
-  @trace reduce: -> @
-  @trace apply: -> no
-  @trace replace: (varName, node) ->
+  reduce: -> @
+  apply: -> no
+  replace: (varName, node) ->
     if varName is @name then node else @
+  freeVars: -> [@name]

 class AbsNode extends Node
   constructor: (@varName, @body) ->
@@ -62,12 +70,36 @@
     new AbsNode @varName, @body.reduce()
   @trace apply: (node) ->
     @body.replace @varName, node
+  # TODO Renaming is not correct as of now. Consider the case:
+  # (λy.λy1.x y y1)[x := y z]
+  # (λy.λy1.x y y1) must be alpha-renamed because y is free on (y z) and x is
+  # free on (x y y1). The current algorithm now chooses to rename y to y1, but
+  # is invalid, because it'll make the y variable, free in (λy1.x y y1) be bound
+  # to a new abstraction.
   @trace replace: (varName, node) ->
-    # TODO rename this abstraction's @varName in @body if @varName is in "node"'s free vars.
     # The parameter of this abstraction makes a new context, so no replacement
     # is needed.
     return @ if varName is @varName
-    new AbsNode @varName, @body.replace varName, node
+    # Avoid name collisions by renaming the @varName if it conflicts with any of
+    # the node's free vars.
+    freeVars = node.freeVars()
+    if @varName in freeVars
+      (@renameVar freeVars).replace varName, node
+    else
+      new AbsNode @varName, @body.replace varName, node
+
+  renameVar: (freeVars) ->
+    # The the letters part of the var in case it's an already renamed var.
+    base = @varName.replace /\d+/, ''
+    n = 1
+    name = base
+    while name in freeVars
+      name = base + n++
+    # A new AbsNode with the new name and the body with the renamed var.
+    new AbsNode name, (@body.replace @varName, new VarNode name)
+
+  freeVars: ->
+    removeAll @body.freeVars(), @varName

 class ApplyNode extends Node
   constructor: (@left, @right) ->
@@ -75,11 +107,14 @@
     "#{@left.leftApplyString()} #{@right.rightApplyString()}"
   rightApplyString: -> "(#{@})"
   @trace reduce: ->
-    left = @left.reduce()
-    (left.apply @right) or new ApplyNode left, @right.reduce()
+    leftReduced = @left.reduce()
+    rightReduced = @right.reduce()
+    (leftReduced.apply rightReduced) or new ApplyNode leftReduced, rightReduced
   @trace apply: -> no
   @trace replace: (varName, node) ->
     new ApplyNode (@left.replace varName, node), (@right.replace varName, node)
+  freeVars: ->
+    @left.freeVars().concat @right.freeVars()

 # Global namespace.
 Lambda = exports ? (@Lambda = {})
@@ -91,5 +126,6 @@
   nodes = parser.parse expr
   console.log JSON.stringify nodes, null, 2

-console.log (Lambda.parse '(λx.λy.x y x) y b').reduce().toString()
-#Lambda.printAst 'λx y z. y z'
+print (Lambda.parse '(λx.λy.x z y x) y b').reduce().toString()
+print (Lambda.parse '(λx.λy.(λz.λy.z y) (x y)) y').reduce().toString()
+#Lambda.printAst 'λx y z. y z'


bug fix

bug fix

--- JSKit/01_JSUserCookies.coffee
+++ JSKit/01_JSUserCookies.coffee
@@ -8,7 +8,7 @@
         super()

     setObject:(value, forKey)->
-        $.cookie forKey, encodeURIComponentvalue), {expires:365}
+        $.cookie forKey, encodeURIComponent(value), {expires:365}

     stringForKey:(forKey)->
         ret = decodeURIComponent($.cookie(forKey))


Remove mouse event handler attributes from the DOM

--- layout.coffee
+++ layout.coffee
@@ -103,7 +103,7 @@
 	    for key, value of obj when key not in moduleKeywords
 #		    	console.log key, obj, @::
 	      @::[key] = value
-	    obj.included?.apply(this, [obj])
+	    obj.included?.call(this, obj)
 	    this

@@ -256,9 +256,9 @@
 		setStyle: (name, value) ->
 			value ?= ''
 			name = stylemap[name] if name of stylemap
-	#		console.log('setStyle', name, value)
+			console.log('setStyle', name, value)
 			@jqel.css(name, value)
-		setParent: (parent) ->
+		set_parent: (parent) ->
 			if parent instanceof Sprite
 				parent = parent.jqel

@@ -305,7 +305,7 @@
 				parent.subviews.push(@)
 				parent.trigger('subviews', @) if parent.events?['subviews']
 				parent = parent.sprite
-			@sprite.setParent parent
+			@sprite.set_parent parent

 		set_id: (@id) ->
 			@sprite.set_id(id)
@@ -324,11 +324,12 @@

 		attributes = {}
 		for i in el.attributes
-	#				console.log 'option', i.name, i.value
+	#				console.log 'attribute', i.name, i.value
 			attributes[i.name] = i.value

-		# swallow attributes
-		delete el.removeAttribute('onclick')
+		# swallow builtin attributes
+		for event in mouseEvents
+			el.removeAttribute('on' + event)

 		parent ?= el.parentNode
 		attributes.parent = parent
@@ -365,12 +366,12 @@
 			el.innerHTML = ''
 #			console.log('new class', name, classattributes)
 			console.warn 'class exists, overwriting', name if name of lz
-			lz[name] = (instanceel, overrides) ->
+			lz[name] = (instanceel, instanceattributes) ->
 				attributes = {}
 				for key, value of classattributes
-					attributes[key] = value;
-				for key, value of overrides
-#  				console.log 'overriding class option', key, value
+					attributes[key] = instanceattributes[key] ? value
+				for key, value of instanceattributes
+#  				console.log 'overriding class attribute', key, value
 					attributes[key] = value
 #				console.log 'creating class instance', name, attributes
 				parent = new lz[ext](instanceel, attributes)
@@ -467,9 +468,12 @@

 	# singleton that listens for mouse position and holds the most recent left and top coordinates
+	mouseEvents = ['click', 'mouseover', 'mouseout', 'mousedown', 'mouseup']
 	class Mouse extends Module
 		constructor: () ->
-		  @docSelector = $(document).on('click mouseover mouseout mousedown mouseup', @handler)
+		  @docSelector = $(document)
+		  for event in mouseEvents
+		  	@docSelector.on(event, @handler)
 	  sender: () ->
 	    trigger("mousemove", left, top)
 	  handler: (event) ->


[fix] Fix EDF connector

[fix] Fix EDF connector

--- server/konnectors/edf.coffee
+++ server/konnectors/edf.coffee
@@ -3,9 +3,6 @@
 async = require 'async'
 moment = require 'moment'
 cozydb = require 'cozydb'
-localization = require '../lib/localization_manager'
-
-
 localization = require '../lib/localization_manager'

 fetcher = require '../lib/fetcher'
@@ -1244,13 +1241,8 @@
     name: 'EDF'
     slug: 'edf'
     description: 'konnector description edf'
-<<<<<<< HEAD
     vendorLink: 'https://particulier.edf.fr/fr'
     category: 'energy',
-=======
-    vendorLink: 'https://particulier.edf.fr'
-
->>>>>>> ebe8132... EDF konnector: Fix error with nom mensualities accounts. (#617)
     fields:
         email:
             type: "text"
@@ -1265,7 +1257,6 @@
         'consumption'
     ]

-<<<<<<< HEAD
     # TODO : get one edeliaClientId: 'text'

     models: [
@@ -1276,9 +1267,6 @@
         EDFConsumptionStatement,
         EDFBill
     ]
-=======
-    models: [Client, Contract, PaymentTerms, Home, ConsumptionStatement, Bill]
->>>>>>> ebe8132... EDF konnector: Fix error with nom mensualities accounts. (#617)

     fetchOperations: [
         prepareEntries


fix crash when html conversation fail

fix crash when html conversation fail

--- server/models/message.coffee
+++ server/models/message.coffee
@@ -584,7 +584,7 @@
                     tables: true
                     wordwrap: 80
             catch err
-                log.error "Error converting HTML to text", err, row.html
+                log.error "Error converting HTML to text", err, raw.html

         return raw


Added score skipping for under 30

Added score skipping for under 30

--- server/queues/scoring.coffee
+++ server/queues/scoring.coffee
@@ -225,6 +225,9 @@
       submittedCode:
         $exists: true
       team: opposingTeam
+
+    if opponentSessionTotalScore < 30
+      queryParameters["totalScore"]["$gt"] = opponentSessionTotalScore + 1

     limitNumber = 1


Change error state check on model.

Change error state check on model.

--- t2-people/app/controllers/person/edit.coffee
+++ t2-people/app/controllers/person/edit.coffee
@@ -8,7 +8,7 @@
   actions:
     save: (->
       model = @get('model')
-      if model.get('errors')
+      if model.get('isError')
         model.send('becameValid')
       model.save().then (=> @transitionToRoute 'people'), ((error) ->)
     )


Query result can be navigated

Query result can be navigated

--- src/bang.coffee
+++ src/bang.coffee
@@ -1,15 +1,15 @@
 # Created by Robert Xue on 1/18/15.
-bang = {}
+bang = null
+bangUri = null
+queryResult = null
 render = ->
   console.log "Bang will make your life with JSON easier!"
   root = d3.select("body").text("").append("div").attr("class", "container")
   renderHeader root.append("div").attr("class", "navbar navbar-default")
   queryRow = root.append("div").attr("class", "row")
-  navigatorRow = root.append("div").attr("class", "row")
   responseRow = root.append("div").attr("class", "row")
   renderQuery queryRow.append("div").attr("class", "col-lg-6 col-md-6 col-sm-12 col-xs-12").append("div").attr("class", "panel panel-default").attr("id", "queryPanel")
-  renderResult queryRow.append("div").attr("class", "col-lg-6 col-md-6 col-sm-12 col-xs-12").append("div").attr("class", "panel panel-default").attr("id", "resultPanel")
-  renderNavigator navigatorRow.append("div").attr("class", "col-lg-12 col-md-12 col-sm-12 col-xs-12").append("div").attr("class", "panel panel-success").attr("id", "navigatorPanel")
+  renderNavigator queryRow.append("div").attr("class", "col-lg-6 col-md-6 col-sm-12 col-xs-12").append("div").attr("class", "panel panel-success").attr("id", "navigatorPanel")
   renderResponse responseRow.append("div").attr("class", "col-lg-12 col-md-12 col-sm-12 col-xs-12").append("div").attr("class", "panel panel-success")
   $(".panel-heading").css({cursor: "pointer", "word-break": "break-all"}).click (ev)->
     $(ev.currentTarget).siblings(".panel-body").toggle()
@@ -26,44 +26,47 @@
   """

 renderResponse = (root)->
-  {data, uri} = bang
-  header = root.append("div").attr("class", "panel-heading").html("Response from #{uri} stored into bang.data")
-  root.append("div").attr("class", "panel-body").append("pre").text(JSON.stringify(data, null, 4))
+  header = root.append("div").attr("class", "panel-heading").html("Response from #{bangUri} stored into bang")
+  root.append("div").attr("class", "panel-body").append("pre").text(JSON.stringify(bang, null, 4))

 renderQuery = (root)->
   root.append("div").attr("class", "panel-heading").text("Query")
   renderQueryForm root.append("div").attr("class", "panel-body")
-
-renderResult = (root)->
-  root.append("div").attr("class", "panel-heading").text("Result")
-  root.append("div").attr("class", "panel-body").append("pre").attr("id", "queryResult")

 renderNavigator = (root)->
   root.append("div").attr("class", "panel-heading").text("Response Navigator")
   panelBody = root.append("div").attr("class", "panel-body")
   panelBody.append("ul").attr("class", "breadcrumb")
   panelBody.append("div").attr("class", "form-inline")
-  panelBody.append("pre")
+  panelBody.append("pre").style("display", "none")
   root.append("ul").attr("class", "list-group")
   root.append("table").attr("class", "table")
-  updateNavigator ["bang.data"]
+  updateNavigator ["bang"]

 didRunQuery = ->
   query = $("#query").val()
-  runQuery query
+  navigator = d3.select("#navigatorPanel .breadcrumb").text ""
+  arrayNavigatior = d3.select("#navigatorPanel .form-inline").text ""
+  autocomplete = d3.select("#navigatorPanel .list-group").text ""
+  autocompleteTable = d3.select("#navigatorPanel table").text ""
+  codeBlock = d3.select("#navigatorPanel pre").style("display", "none").text ""
+  { error, result } = runQuery query
+  if error
+    codeBlock.text error
+  else
+    queryResult = result
+    updateNavigator ["queryResult"]

-runQuery = (query)->
+runQuery = (query, options)->
   try
-    $("#query").val query
+    unless options and options.silent
+      $("#query").val query
     result = eval query
     if result is undefined
-      $("#queryResult").text "(undefined)"
       return {error: "(undefined)"}
     else
-      $("#queryResult").text JSON.stringify(result, null, 4)
       return {result}
   catch ex
-    $("#queryResult").text ex
     return {error: ex}

 updateNavigator = (path)->
@@ -71,48 +74,53 @@
   arrayNavigatior = d3.select("#navigatorPanel .form-inline").text ""
   autocomplete = d3.select("#navigatorPanel .list-group").text ""
   autocompleteTable = d3.select("#navigatorPanel table").text ""
-  codeBlock = d3.select("#navigatorPanel pre").text ""
+  codeBlock = d3.select("#navigatorPanel pre").style("display", "none").text ""
   query = getQueryFromPath path
   console.log "Update Navigator", path, query
-  {error, result} = runQuery query
+  {error, result} = runQuery query, {silent: true}
   return navigator.text JSON.stringify(error, null, 4) if error
   navigator.selectAll("li").data(path).enter().append("li").each (pathFragment, i)->
-    if i is path.length - 1
-      d3.select(this).attr("class", "active").text(pathFragment)
-    else
-      d3.select(this).append("a").attr("href", "#").text(pathFragment).on "click", (currentPathFragment)->
-        console.log path, i
+    d3.select(this).append("a").attr("href", "#").text(pathFragment).on "click", (currentPathFragment)->
+      d3.event.preventDefault()
+      if node = pathFragment.match(/^(.*)\[(\d+)]$/)
+        updateNavigator path.slice(0, i).concat node[1] + "[]"
+      else
         updateNavigator path.slice(0, i + 1)
   if result instanceof Array
     arrayNavigatior.append("div").attr("class", "form-group").html """
-    Name
+    Index (0 - #{result.length-1})
     <input type='number' class='form-control' id='arrayIndex' value='0' min='0' max='#{result.length-1}'>
     """
     arrayNavigatior.append("button").attr("type", "submit").attr("class", "btn btn-default").text("Go")
     .on("click", ->
       d3.event.preventDefault()
       arrayIndex = $("#arrayIndex").val()
-      node = path[path.length - 1].match /(\w*)\[(\d*)]$/
-      updateNavigator path.slice(0, path.length - 1).concat node[1] + "[#{arrayIndex}]"
+      node = path[path.length - 1].match(/^(.*)\[(\d*)]$/)
+      pathFragment = if node then node[1] else path[path.length - 1]
+      updateNavigator path.slice(0, path.length - 1).concat pathFragment + "[#{arrayIndex}]"
     )
   else if result instanceof Object
     autocomplete.selectAll("li").data(Object.keys(result)).enter()
-    .append("li").attr("class", "list-group-item").append("a").attr("href", "#").text((key)-> getPathFragmentForKey(result, key))
-    .on("click", (key, i)->
-      d3.event.preventDefault()
-      pathFragment = getPathFragmentForKey result, key
-      updateNavigator path.concat(pathFragment)
-    )
+    .append("li").attr("class", "list-group-item").each (key)->
+      if not (result[key] instanceof Array or result[key] instanceof Object)
+        d3.select(this).append("span").text key
+        d3.select(this).append("span").attr("class", "pull-right").text result[key]
+      else
+        pathFragment = getPathFragmentForKey(result, key)
+        d3.select(this).append("a").attr("href", "#").text(pathFragment)
+        .on("click", ->
+          d3.event.preventDefault()
+          updateNavigator path.concat(pathFragment)
+        )
   else
-    codeBlock.text JSON.stringify(result, null, 4)
+    codeBlock.style("display", null).text JSON.stringify(result, null, 4)

 getQueryFromPath = (path)->
   query = path.reduce ((pv, cv, index, array)->
     if index isnt 0
       pv += "."
-    if node = cv.match /(\w*)#$/
-      pv += node[1]
-    else if node = cv.match /(\w*)\[(\d*)]$/
+    if node = cv.match /^(.*)\[(\d*)]$/
+      console.log node
       pv += node[1]
       pv += "[#{node[2]}]" if node[2]
       pv
@@ -145,10 +153,10 @@

 """)
-  if bang.data instanceof Array
-    $("#query").val("_.size(bang.dat)")
+  if bang instanceof Array
+    $("#query").val("_.size(bang)")
   else
-    $("#query").val("_.keys(bang.data)")
+    $("#query").val("_.keys(bang)")

   $("#runQuery").click didRunQuery

@@ -157,8 +165,8 @@
     return unless document.body && (document.body.childNodes[0] && document.body.childNodes[0].tagName == "PRE" || document.body.children.length == 0)
     data = if document.body.children.length then $("pre").text() else document.body
     return unless data
-    bang.data = JSON.parse data
-    bang.uri = document.location.href
+    bang = JSON.parse data
+    bangUri = document.location.href
   catch ex
     console.warn "Document not valid json, bang will not work: #{ex}"
   console.warn "Bang can't work on HTML and XML pages"


fix palette events test by clearing test-main

--- test/coffee/tests.coffee
+++ test/coffee/tests.coffee
@@ -582,6 +582,7 @@
         start()

   asyncTest 'Controller: palette events', ->
+    document.getElementById('test-main').innerHTML = ''
     editor = new droplet.Editor document.getElementById('test-main'), {
       mode: 'coffeescript'
       palette: [{


Dedupe location matches in galleries typeahead by slug

--- src/desktop/apps/galleries_institutions/components/filter_facet/partner_filter_facet.coffee
+++ src/desktop/apps/galleries_institutions/components/filter_facet/partner_filter_facet.coffee
@@ -31,7 +31,7 @@
       matches = [@allItemsSuggestion].concat _.select @countItems, ({id}) =>
         id in @emptyStateItemIDs

-    callback(matches)
+    callback _.uniq(matches, 'id')

   isMatched: (query, string) ->
     escape= (s) ->


add LayoutIt! to Bootstrap

add LayoutIt! to Bootstrap

--- data/points/bootstrap.coffee
+++ data/points/bootstrap.coffee
@@ -8,4 +8,5 @@
 reference:
   "Bootstrap 官网": "http://twitter.github.io/bootstrap/"
   "Bootswatch": "http://bootswatch.com/"
+  "LayoutIt!": "http://www.layoutit.com/"
   "Font Awesome": "http://fortawesome.github.io/Font-Awesome/"


removing unused @name

removing unused @name

--- nodes.coffee
+++ nodes.coffee
@@ -919,7 +919,7 @@
   # constructor, property assignments, and inheritance getting built out below.
   compileNode: (o) ->
     decl  = @determineName()
-    name  = decl or @name or '_Class'
+    name  = decl or '_Class'
     name = "_#{name}" if name.reserved
     lname = new Literal name


allowing implicit returns to be pushed down into each branch of computation, even when there's an explicit return nested more deeply. Issue #401

--- nodes.coffee
+++ nodes.coffee
@@ -179,7 +179,7 @@
     last: @expressions[idx]
     last: @expressions[idx: - 1] if last instanceof CommentNode
     return this if not last or last instanceof ReturnNode
-    @expressions[idx]: last.makeReturn() unless last.containsPureStatement()
+    @expressions[idx]: last.makeReturn()
     this

   # An **Expressions** is the only node that can serve as the root.
@@ -263,6 +263,9 @@

   topSensitive: ->
     true
+
+  makeReturn: ->
+    this

   compileNode: (o) ->
     expr: @expression.makeReturn()


Netsniff.coffee example should exit with an error when fails to load the adress

Issue #11333 https://github.com/ariya/phantomjs/issues/11333

--- examples/netsniff.coffee
+++ examples/netsniff.coffee
@@ -103,6 +103,7 @@
   page.open page.address, (status) ->
     if status isnt 'success'
       console.log 'FAIL to load the address'
+      phantom.exit(1)
     else
       page.endTime = new Date()
       page.title = page.evaluate ->
@@ -110,4 +111,4 @@

       har = createHAR page.address, page.title, page.startTime, page.resources
       console.log JSON.stringify har, undefined, 4
-    phantom.exit()
+      phantom.exit()


Revert "update pdfjs to v1.8.188"

Revert "update pdfjs to v1.8.188"

This reverts commit 189e48dcc00f530eff674cc4a2b87c06814d45ff.

--- services/web/app/coffee/infrastructure/PackageVersions.coffee
+++ services/web/app/coffee/infrastructure/PackageVersions.coffee
@@ -1,5 +1,5 @@
 version = {
-	"pdfjs": "1.8.188"
+	"pdfjs": "1.7.225"
 	"moment": "2.9.0"
 	"ace": "1.2.5"
 }


add the driver field to email config

add the driver field to email config

--- server-ce/settings.coffee
+++ server-ce/settings.coffee
@@ -396,6 +396,7 @@
 	settings.email =
 		fromAddress: process.env["SHARELATEX_EMAIL_FROM_ADDRESS"]
 		replyTo: process.env["SHARELATEX_EMAIL_REPLY_TO"] or ""
+		driver: process.env["SHARELATEX_EMAIL_DRIVER"]
 		parameters:
 			#AWS Creds
 			AWSAccessKeyID: process.env["SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID"]


housekeeping

housekeeping

--- app/js/app.coffee
+++ app/js/app.coffee
@@ -3,8 +3,8 @@
   Collections: {}
   Views: {}
   Routers: {}
+
   init: ->
-    'use strict'
     new this.Views.Authenticate()

   ready: ->
@@ -30,12 +30,12 @@
     localStorage['dev']?

 $ ->
-  'use strict'
   app.init()

 $.ajaxSetup
   headers:
     'Accept': 'application/vnd.github.v3.html+json'
+
   # the Notifications API makes heavy use of the If-Modified-Since header for
   # determining what to respond with. This disables any HTTP caching until
   # proper local caching is implemented.


FIX: Correct student list sort order

FIX: Correct student list sort order

--- src/app/common/services/unit-service.coffee
+++ src/app/common/services/unit-service.coffee
@@ -336,18 +336,12 @@
       { value: 0, key: taskService.statusKeys[9]  }
     ]

-    # Returns the task statistic value for the provided key
-    student.taskStatValue = (key) ->
-      student.task_stats[taskService.statusSeq[key]].value
-
     # Returns the student's progress sorting order
     student.progressSortOrder = ->
-      20 * student.taskStatValue('complete') +
-      15 * (student.taskStatValue('discuss') + student.taskStatValue('demonstrate')) +
-      10 * (student.taskStatValue('ready_to_mark')) +
-      5 * (student.taskStatValue('fix_and_resubmit')) +
-      2 * (student.taskStatValue('working_on_it')) +
-      1 * (student.taskStatValue('need_help'))
+      -1 * student.task_stats[0].value +
+      20 * student.task_stats[4].value +
+      7 * student.task_stats[3].value +
+      student.task_stats[2].value

     # Returns the student's portfolio submission URL
     student.portfolioUrl = ->


basic tests for respond listeners

basic tests for respond listeners

--- test/ingress_test.coffee
+++ test/ingress_test.coffee
@@ -11,3 +11,11 @@
       hear: sinon.spy()

     require('../src/ingress')(@robot)
+
+  describe 'respond listener', ->
+
+    it 'registers AP per level response', ->
+      expect(@robot.respond).to.have.been.calledWith(/AP\s+(?:to|(?:un)?til)\s+L?(\d{1,2})/i)
+
+    it 'registers AP for all levels response', ->
+      expect(@robot.respond).to.have.been.calledWith(/AP all/i)


Better matching against keys.

Better matching against keys.

Allows for successive matching as you type.

--- services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
+++ services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee
@@ -49,7 +49,8 @@
 						range = new Range(pos.row, 0, pos.row, pos.column)
 						lineUpToCursor = editor.getSession().getTextRange(range)
 						commandFragment = getLastCommandFragment(lineUpToCursor)
-						if commandFragment == '\\cite{'
+						# if commandFragment == '\\cite{'
+						if commandFragment.match(/^~?\\cite{\w*/)
 							result = references.keys.map (key) -> {
 								caption: "\\cite{#{key}",
 								snippet: "\\cite{#{key}",


Switch to native Promises in File

Switch to native Promises in File

--- src/file.coffee
+++ src/file.coffee
@@ -6,7 +6,6 @@
 fs = require 'fs-plus'
 Grim = require 'grim'

-Q = require 'q'
 runas = null # Defer until used
 iconv = null # Defer until used

@@ -134,7 +133,7 @@
   # Public: Returns a promise that resolves to a {Boolean}, true if the file
   # exists, false otherwise.
   exists: ->
-    Q.Promise (resolve, reject) =>
+    new Promise (resolve) =>
       fs.exists @getPath(), resolve

   # Public: Returns a {Boolean}, true if the file exists, false otherwise.
@@ -145,10 +144,10 @@
   #
   # Returns a promise that resolves to a {String}.
   getDigest: ->
-    return Q(@digest) if @digest
-    @read().then (contents) =>
-      # read sets digest
-      @digest
+    if @digest?
+      Promise.resolve(@digest)
+    else
+      @read().then => @digest # read assigns digest as a side-effect

   # Public: Get the SHA-1 digest of this file
   #
@@ -199,10 +198,14 @@
   # Public: Returns a promise that resolves to the file's completely resolved {String} path.
   getRealPath: ->
     if @realPath?
-      Q(@realPath)
+      Promise.resolve(@realPath)
     else
-      Q.nfcall(fs.realpath, @path).then (realPath) =>
-        @realPath = realPath
+      new Promise (resolve, reject) =>
+        fs.realpath @path, (err, result) =>
+          if err?
+            reject(err)
+          else
+            resolve(@realPath = result)

   # Public: Return the {String} filename without any directory information.
   getBaseName: ->
@@ -251,32 +254,28 @@
   # Returns a promise that resolves to a String.
   read: (flushCache) ->
     if @cachedContents? and not flushCache
-      promise = Q(@cachedContents)
+      promise = Promise.resolve(@cachedContents)
     else
-      deferred = Q.defer()
-      promise = deferred.promise
-      content = []
-      bytesRead = 0
-      encoding = @getEncoding()
-      if encoding is 'utf8'
-        readStream = fs.createReadStream(@getPath(), {encoding})
-      else
-        iconv ?= require 'iconv-lite'
-        readStream = fs.createReadStream(@getPath()).pipe(iconv.decodeStream(encoding))
-
-      readStream.on 'data', (chunk) ->
-        content.push(chunk)
-        bytesRead += chunk.length
-        deferred.notify(bytesRead)
-
-      readStream.on 'end', ->
-        deferred.resolve(content.join(''))
-
-      readStream.on 'error', (error) ->
-        if error.code == 'ENOENT'
-          deferred.resolve(null)
+      promise = new Promise (resolve, reject) =>
+        content = []
+        encoding = @getEncoding()
+        if encoding is 'utf8'
+          readStream = fs.createReadStream(@getPath(), {encoding})
         else
-          deferred.reject(error)
+          iconv ?= require 'iconv-lite'
+          readStream = fs.createReadStream(@getPath()).pipe(iconv.decodeStream(encoding))
+
+        readStream.on 'data', (chunk) ->
+          content.push(chunk)
+
+        readStream.on 'end', ->
+          resolve(content.join(''))
+
+        readStream.on 'error', (error) ->
+          if error.code == 'ENOENT'
+            resolve(null)
+          else
+            reject(error)

     promise.then (contents) =>
       @setDigest(contents)
@@ -309,10 +308,20 @@
   writeFile: (filePath, contents) ->
     encoding = @getEncoding()
     if encoding is 'utf8'
-      Q.nfcall(fs.writeFile, filePath, contents, {encoding})
+      new Promise (resolve, reject) ->
+        fs.writeFile filePath, contents, {encoding}, (err, result) ->
+          if err?
+            reject(err)
+          else
+            resolve(result)
     else
       iconv ?= require 'iconv-lite'
-      Q.nfcall(fs.writeFile, filePath, iconv.encode(contents, encoding))
+      new Promise (resolve, reject) ->
+        fs.writeFile filePath, iconv.encode(contents, encoding), (err, result) ->
+          if err?
+            reject(err)
+          else
+            resolve(result)

   # Writes the text to specified path.
   #
@@ -365,10 +374,12 @@
             console.error newError

         try
-          @read(true).catch(handleReadError).done (newContents) =>
+          handleResolve = (newContents) =>
             unless oldContents is newContents
               @emit 'contents-changed' if Grim.includeDeprecatedAPIs
               @emitter.emit 'did-change'
+
+          @read(true).then(handleResolve, handleReadError)
         catch error
           handleReadError(error)


check if file already exists before doing anything

--- app/scripts/controllers/main.coffee
+++ app/scripts/controllers/main.coffee
@@ -12,7 +12,7 @@
 Datastore = remote.require "nedb"

 angular.module 'gummyApp'
-    .controller 'MainCtrl', ($scope, $rootScope, $timeout, $trakt, $rotten) ->
+    .controller 'MainCtrl', ($scope, $rootScope, $timeout, $q, $trakt, $rotten) ->

         ###*
@@ -95,8 +95,6 @@

             for file in files

-                console.log file
-
                 # Check for ext
                 filename = file.name.split(".")
                 ext = filename.pop()
@@ -107,37 +105,60 @@
                 # dots in name)
                 filename = filename.join " "

-                # Find movie infos
-                $trakt
-                    .search filename
-                    .then (data) ->
+                # Check if movie is already in user's library
+                $scope
+                    .check_filepath file.path
+                    .then () ->

-                        # Add filepath to db
-                        movie = data[0]
-                        movie.path = file.path
+                        # Find movie infos
+                        $trakt
+                            .search filename
+                            .then (data) ->

-                        # Save data to DB
-                        $scope.db.insert movie, (e, item) ->
-                            if e then return console.error e
+                                # Add filepath to db
+                                movie = data[0]
+                                movie.path = file.path

-                            # Add data to main grid
-                            $scope.add_movie movie
+                                # Save data to DB
+                                $scope.db.insert movie, (e, item) ->
+                                    if e then return console.error e

+                                    # Add data to main grid
+                                    $scope.add_movie movie
+
+                            .catch (e) ->
+                                console.error e

                     .catch (e) ->
-                        console.error e
+                        console.warn e

         ###*
          # Add a movie and getting rid of angular fucking digest cycle
+         # @method add_movie
+         # @param {Object} movie - Your movie object
         ###
         $scope.add_movie = (movie) ->
             $timeout () ->
                 $scope.grid.push movie

+        ###*
+         # Check if movie already exists in db (based on filepath)
+         # @method check_movie
+         # @param {String} filepath - Wanted filename
+         # @return {Object} - $q promise
+        ###
+        $scope.check_filepath = (filepath) ->
+            q = $q.defer()

-        #$scope.check = (filepath) ->
+            $scope.db.find { path: filepath }, (e, items) ->
+                if e then q.reject e
+                else if items.length > 0 then q.reject "Movie already in library"
+                else q.resolve items
+
+            q.promise
+

         # Init app
         $scope.init()


update unit tests for incoming project versions

--- services/document-updater/test/unit/coffee/ProjectManager/updateProjectTests.coffee
+++ services/document-updater/test/unit/coffee/ProjectManager/updateProjectTests.coffee
@@ -3,6 +3,7 @@
 should = chai.should()
 modulePath = "../../../../app/js/ProjectManager.js"
 SandboxedModule = require('sandboxed-module')
+_ = require('underscore')

 describe "ProjectManager", ->
 	beforeEach ->
@@ -49,16 +50,19 @@
 					@ProjectManager.updateProjectWithLocks @project_id, @user_id, @docUpdates, @fileUpdates, @version, @callback

 				it "should rename the docs in the updates", ->
+					firstDocUpdateWithVersion = _.extend({}, @firstDocUpdate, {version: "#{@version}.0"})
+					secondDocUpdateWithVersion = _.extend({}, @secondDocUpdate, {version: "#{@version}.1"})
 					@DocumentManager.renameDocWithLock
-						.calledWith(@project_id, @firstDocUpdate.id, @user_id, @firstDocUpdate)
+						.calledWith(@project_id, @firstDocUpdate.id, @user_id, firstDocUpdateWithVersion)
 						.should.equal true
 					@DocumentManager.renameDocWithLock
-						.calledWith(@project_id, @secondDocUpdate.id, @user_id, @secondDocUpdate)
+						.calledWith(@project_id, @secondDocUpdate.id, @user_id, secondDocUpdateWithVersion)
 						.should.equal true

 				it "should rename the files in the updates", ->
+					firstFileUpdateWithVersion = _.extend({}, @firstFileUpdate, {version: "#{@version}.2"})
 					@ProjectHistoryRedisManager.queueRenameEntity
-						.calledWith(@project_id, 'file', @firstFileUpdate.id, @user_id, @firstFileUpdate)
+						.calledWith(@project_id, 'file', @firstFileUpdate.id, @user_id, firstFileUpdateWithVersion)
 						.should.equal true

 				it "should not flush the history", ->
@@ -117,16 +121,19 @@
 					@ProjectManager.updateProjectWithLocks @project_id, @user_id, @docUpdates, @fileUpdates, @version, @callback

 				it "should add the docs in the updates", ->
+					firstDocUpdateWithVersion = _.extend({}, @firstDocUpdate, {version: "#{@version}.0"})
+					secondDocUpdateWithVersion = _.extend({}, @secondDocUpdate, {version: "#{@version}.1"})
 					@ProjectHistoryRedisManager.queueAddEntity
-						.calledWith(@project_id, 'doc', @firstDocUpdate.id, @user_id, @firstDocUpdate)
+						.calledWith(@project_id, 'doc', @firstDocUpdate.id, @user_id, firstDocUpdateWithVersion)
 						.should.equal true
 					@ProjectHistoryRedisManager.queueAddEntity
-						.calledWith(@project_id, 'doc', @secondDocUpdate.id, @user_id, @secondDocUpdate)
+						.calledWith(@project_id, 'doc', @secondDocUpdate.id, @user_id, secondDocUpdateWithVersion)
 						.should.equal true

 				it "should add the files in the updates", ->
+					firstFileUpdateWithVersion = _.extend({}, @firstFileUpdate, {version: "#{@version}.2"})
 					@ProjectHistoryRedisManager.queueAddEntity
-						.calledWith(@project_id, 'file', @firstFileUpdate.id, @user_id, @firstFileUpdate)
+						.calledWith(@project_id, 'file', @firstFileUpdate.id, @user_id, firstFileUpdateWithVersion)
 						.should.equal true

 				it "should not flush the history", ->


ext/edit_object: Minor cleanup.

ext/edit_object: Minor cleanup.

--- oghma/ext/edit_object.coffee
+++ oghma/ext/edit_object.coffee
@@ -34,7 +34,7 @@
   # - auto
   types: {}

-  # Function to call with edited object if save is pressed. (Required0)
+  # Function to call with edited object if save is pressed. (Required)
   onSave: false

   # Function to call if cancel is pressed. (Required)


time tests

time tests

--- test/functional/druidFunctional.mocha.coffee
+++ test/functional/druidFunctional.mocha.coffee
@@ -35,6 +35,7 @@
           }
           attributes: [
             { name: 'time', type: 'TIME' }
+            { name: 'sometimeLater', type: 'TIME' }
             { name: 'channel', type: 'STRING' }
             { name: 'page', type: 'STRING' }
             { name: 'page_unique', special: 'unique' }
@@ -627,6 +628,92 @@
         testComplete()
       ).done()

+    it "can timebucket a time column that is the timeAttribute one", (testComplete) ->
+    ex = ply()
+    .apply('Time',
+      $("wiki").split($("time").timeBucket('PT1H', 'Etc/UTC'), 'TimeCol')
+      .sort('$TimeCol', 'descending')
+      .limit(2)
+
+    )
+
+    basicExecutor(ex).then((result) ->
+      expect(result.toJS()).to.deep.equal([
+        {
+          "Time": [
+            {
+              "TimeCol": {
+                "end": "new Date('2015-09-13T00:00:00.000Z')",
+                "start": "new Date('2015-09-12T23:00:00.000Z')",
+                "type": "TIME_RANGE"
+              }
+            },
+            {
+              "TimeCol": {
+                "end": "new Date('2015-09-12T23:00:00.000Z')",
+                "start": "new Date('2015-09-12T22:00:00.000Z')",
+                "type": "TIME_RANGE"
+              }
+            }
+          ]
+        }
+      ])
+      testComplete()
+    ).done()
+
+    it "can timebucket a time column thats not the timeAttribute one", (testComplete) ->
+      ex = ply()
+      .apply('TimeLater',
+        $("wiki").split($("sometimeLater").timeBucket('PT1H', 'Etc/UTC'), 'SometimeLater')
+        .limit(5)
+      )
+
+      basicExecutor(ex).then((result) ->
+        expect(result.toJS()).to.deep.equal([
+          {
+            "TimeLater": [
+              {
+                "SometimeLater": {
+                  "end": new Date('2016-09-12T01:00:00.000Z'),
+                  "start": new Date('2016-09-12T00:00:00.000Z'),
+                  "type": "TIME_RANGE"
+                }
+              },
+              {
+                "SometimeLater": {
+                  "end": new Date('2016-09-12T02:00:00.000Z'),
+                  "start": new Date('2016-09-12T01:00:00.000Z'),
+                  "type": "TIME_RANGE"
+                }
+              },
+              {
+                "SometimeLater": {
+                  "end": new Date('2016-09-12T03:00:00.000Z'),
+                  "start": new Date('2016-09-12T02:00:00.000Z'),
+                  "type": "TIME_RANGE"
+                }
+              },
+              {
+                "SometimeLater": {
+                  "end": new Date('2016-09-12T04:00:00.000Z'),
+                  "start": new Date('2016-09-12T03:00:00.000Z'),
+                  "type": "TIME_RANGE"
+                }
+              },
+              {
+                "SometimeLater": {
+                  "end": new Date('2016-09-12T05:00:00.000Z'),
+                  "start": new Date('2016-09-12T04:00:00.000Z'),
+                  "type": "TIME_RANGE"
+                }
+              }
+            ]
+          }
+          ]
+        )
+        testComplete()
+      ).done()
+
     it "works multi-dimensional GROUP BYs", (testComplete) ->
       ex = ply()
         .apply("wiki", $('wiki').filter($("channel").isnt('en')))


feat(babel6): remove eslinter fixer task

--- build/tasks/eslint-fixer-task.coffee
+++ build/tasks/eslint-fixer-task.coffee
@@ -1,17 +0,0 @@
-fs = require('fs')
-
-module.exports = (grunt) ->
-  grunt.registerMultiTask "eslintFixer", "Fixes eslint issues", ->
-    done = @async()
-    for fileset in @files
-      for file in fileset.src
-        content = fs.readFileSync(file, encoding: "utf8")
-        re1 = /(.*[^ ])=> (.*)/g
-        re2 = /(.*) =>([^ ].*)/g
-        eolRe = /\ +$/gm
-        replacer = (fullMatch, parens, rest) ->
-          return "#{parens} => #{rest}"
-        newContent = content.replace(re1, replacer)
-        newContent = newContent.replace(re2, replacer)
-        newContent = newContent.replace(eolRe, "")
-        fs.writeFileSync(file, newContent, encoding: 'utf8')


log error when there are plugins with wrong path

--- app-loader/app-loader.coffee
+++ app-loader/app-loader.coffee
@@ -33,7 +33,7 @@

 loadPlugin = (pluginPath) ->
     return new Promise (resolve, reject) ->
-        $.getJSON(pluginPath).then (plugin) ->
+        success = (plugin) ->
             window.taigaContribPlugins.push(plugin)

             if plugin.css
@@ -44,6 +44,11 @@
                 ljs.load(plugin.js, resolve)
             else
                 resolve()
+
+        fail = () ->
+            console.error("error loading", pluginPath);
+
+        $.getJSON(pluginPath).then(success, fail)

 loadPlugins = (plugins) ->
     promises = []


Add two more pending tests.

Add two more pending tests.

--- test/pending_test.coffee
+++ test/pending_test.coffee
@@ -2,3 +2,5 @@
     it 'Should test skipHeader option'
     it 'Should test initWith option'
     it 'Should embed plain JS files'
+    it 'Should take a logging callback'
+    it 'Should test verbose option'


Build document declaratively for newline test

--- test/src/system/block_formatting_test.coffee
+++ test/src/system/block_formatting_test.coffee
@@ -329,22 +329,26 @@
             done()

   test "inserting newline before heading", (done) ->
-    clickToolbarButton attribute: "heading", ->
-      typeCharacters "abc", ->
-        moveCursor direction: "left", times: 3, ->
-          typeCharacters "\n", ->
-            moveCursor direction: "left", ->
-              typeCharacters "\n\n\n", ->
-
-                document = getDocument()
-                assert.equal document.getBlockCount(), 2
-
-                block = document.getBlockAtIndex(0)
-                assert.deepEqual block.getAttributes(), []
-                assert.equal block.toString(), "\n\n\n"
-
-                block = document.getBlockAtIndex(1)
-                assert.deepEqual block.getAttributes(), ["heading"]
-                assert.equal block.toString(), "abc\n"
-
-                done()
+
+    document = new Trix.Document [
+        new Trix.Block(Trix.Text.textForStringWithAttributes("\n"), [])
+        new Trix.Block(Trix.Text.textForStringWithAttributes("abc"), ["heading"])
+
+      ]
+
+    replaceDocument(document)
+    getEditorController().setLocationRange([{index: 0, offset: 0}, {index: 0, offset: 0}])
+
+    typeCharacters "\n", ->
+      document = getDocument()
+      assert.equal document.getBlockCount(), 2
+
+      block = document.getBlockAtIndex(0)
+      assert.deepEqual block.getAttributes(), []
+      assert.equal block.toString(), "\n\n\n"
+
+      block = document.getBlockAtIndex(1)
+      assert.deepEqual block.getAttributes(), ["heading"]
+      assert.equal block.toString(), "abc\n"
+
+      done()


Linter fix

Linter fix

--- assets/script/Resources.coffee
+++ assets/script/Resources.coffee
@@ -10,9 +10,6 @@
 			"[#{(Object.keys dict).join ', '}]"

 		fail "No #{dictName} is named #{key}. Did you mean one of #{keys}?"
-
-loadTexture = (name) ->
-	(new three.JSONLoader).load "model/#{name}.js", (geometry, materials) =>

 module.exports = class Resources
 	constructor: (allResources) ->


Fix new tasks

Fix new tasks

--- app/controllers/tasks.coffee
+++ app/controllers/tasks.coffee
@@ -16,9 +16,17 @@

   constructor: ->
     super
-    Task.bind "create", @addOne
+    Task.bind "create", @create
     Task.bind "refresh", @render
     List.bind "changeList", @render
+
+  create: (task) =>
+    taskItem = new TaskItem
+      task: task
+    @tasks
+      .find(".p#{ task.priority }")
+      .first()
+      .before taskItem.render().el

   addOne: (task) =>
     taskItem = new TaskItem


Better align the initial position.

Better align the initial position.

--- app/javascripts/image.coffee
+++ app/javascripts/image.coffee
@@ -100,7 +100,7 @@
           volumeCtls.open()

         # Adjust the camera position.
-        renderer.camera.position = [120, 80, 160]
+        renderer.camera.position = [120, 20, 20]

         # Render the image.
         renderer.render()


set window height as maximum height of div_resizer

This fixes the issue where a user can resize the div so that grippie is
no longer visible and is not reize the div back.

--- app/assets/javascripts/discourse/components/div_resizer.js.coffee
+++ app/assets/javascripts/discourse/components/div_resizer.js.coffee
@@ -29,6 +29,7 @@
     thisMousePos = mousePosition(e).y
     size = originalDivHeight + (originalPos - thisMousePos)
     lastMousePos = thisMousePos
+    size = Math.min(size, $(window).height())
     size = Math.max(min, size)
     div.height size + "px"
     endDrag e,opts if size < min


DevTools: AppController: require a machine from user on load, and pass the machine object to new app instance, if not selected go back to /Activity/Public and once selected no need to ask for second time

--- client/DevTools/AppController.coffee
+++ client/DevTools/AppController.coffee
@@ -1,15 +1,38 @@
 class DevToolsController extends AppController

+  APPNAME = "DevTools"
+  VERSION = "0.1"
+
   KD.registerAppClass this,
-    name            : 'DevTools'
-    version         : '0.1'
-    route           : '/:name?/DevTools'
-    behavior        : 'application'
-    preCondition    :
-      condition     : (options, cb)-> cb KD.isLoggedIn()
-      failure       : (options, cb)->
-        KD.singletons.appManager.open 'DevTools', conditionPassed : yes
+    name              : APPNAME
+    version           : VERSION
+    route             : '/:name?/DevTools'
+    behavior          : 'application'
+    preCondition      :
+
+      checkOnLoadOnly : yes
+      condition       : (options, cb)->
+
+        if KD.isLoggedIn()
+
+          KD.singletons.computeController.requireMachine
+            app       :
+              name    : APPNAME
+              version : VERSION
+          , (err, machine)->
+
+            if err or not machine then cb no
+            else cb yes, { machine }
+
+        else
+
+          cb no
+
+      failure         : (options, cb)->
+
+        KD.singletons.router.handleRoute "/Activity/Public"
         KD.showEnforceLoginModal()
+
     menu            :
       hiddenOnStart : yes
       items         : [


Do not show Modal to save Draft when component is Unmounted

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -111,7 +111,9 @@
         delete @props.lastUpdate

     componentWillUnmount: ->
-        @closeSaveDraft @state, hasChanged: @hasChanged(@props, @state)
+        @closeSaveDraft @state,
+            hasChanged: @hasChanged(@props, @state)
+            silent: true

     closeSaveDraft: (state, options={}) ->
         fetch = (error, message) =>


client: home: open new kd utilities

client: home: open new kd utilities

--- client/home/lib/welcome/boxes.coffee
+++ client/home/lib/welcome/boxes.coffee
@@ -34,7 +34,7 @@

   installKd :
     """
-
+

Install KD

kd  is a CLI tool that allows you to use your local IDEs.


Update HumanReadableLogsRules.coffee

Update HumanReadableLogsRules.coffee

--- services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
+++ services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee
@@ -12,7 +12,7 @@
 		"""
 	,
 		regexToMatch: /Display math should end with \$\$/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Display_math_should_end_with_$$."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Display_math_should_end_with_$$"
 		humanReadableHint: """
 			You have forgotten a $ sign at the end of 'display math' mode. When writing in display math mode, you must always math write inside $$ \u2026 $$. Check that the number of $s match around each math expression.
 		"""
@@ -24,67 +24,67 @@
 		"""
 	,
 		regexToMatch: /(undefined )?[rR]eference(s)?.+(undefined)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_undefined_references"
 		humanReadableHint: """
 			You have referenced something which has not yet been labelled. If you have labelled it already, make sure that what is written inside \\ref{...} is the same as what is written inside \\label{...}.
 		"""
 	,
 		regexToMatch: /Citation .+ on page .+ undefined on input line .+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Citation_XXX_on_page_XXX_undefined_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Citation_XXX_on_page_XXX_undefined_on_input_line_XXX"
 		humanReadableHint: """
 			You have cited something which is not included in your bibliography. Make sure that the citation (\\cite{...}) has a corresponding key in your bibliography, and that both are spelled the same way.
 		"""
 	,
 		regexToMatch: /(Label .+)? multiply[ -]defined( labels)?/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_multiply-defined_labels."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/There_were_multiply-defined_labels"
 		humanReadableHint: """
 			You have used the same label more than once. Check that each \\label{...} labels only one item.
 		"""
 	,
 		regexToMatch: /`!?h' float specifier changed to `!?ht'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/%60!h%27_float_specifier_changed_to_%60!ht%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/%60!h%27_float_specifier_changed_to_%60!ht%27"
 		humanReadableHint: """
 			The float specifier 'h' is too strict of a demand for LaTeX to place your float in a nice way here. Try relaxing it by using 'ht', or even 'htbp' if necessary. If you want to try keep the float here anyway, check out the float package.
 		"""
 	,
 		regexToMatch: /No positions in optional float specifier/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/No_positions_in_optional_float_specifier"
 		humanReadableHint: """
 			You have forgotten to include a float specifier, which tells LaTeX where to position your figure. Find out more about float specifiers here.
 		"""
 	,
 		regexToMatch: /Undefined control sequence/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/Undefined_control_sequence"
 		humanReadableHint: """
 			The compiler is having trouble understanding a command you have used. Check that the command is spelled correctly. If the command is part of a package, make sure you have included the package in your preamble using \\usepackage{...}.
 		"""
 	,
 		regexToMatch: /File .+ not found/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/File_XXX_not_found_on_input_line_XXX"
 		humanReadableHint: """
 			The compiler cannot find the file you want to include. Make sure that you have uploaded the file and specified the file location correctly.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown graphics extension: \..+/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.gif."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_graphics_extension:_.XXX"
 		humanReadableHint: """
 			The compiler does not recognise the file type of one of your images. Make sure you are using a supported image format for your choice of compiler, and check that there are no periods (.) in the name of your image.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `H'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27"
 		humanReadableHint: """
 			The compiler isn't recognizing the float option 'H'. Include \\usepackage{float} in your preamble to fix this.
 		"""
 	,
 		regexToMatch: /LaTeX Error: Unknown float option `.+'/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_Unknown_float_option_%60H%27"
 		humanReadableHint: """
 			You have used a float specifier which the compiler does not understand. You can learn more about the different float options available for placing figures here.
 		"""
 	,
 		regexToMatch: /LaTeX Error: \\math.+ allowed only in math mode/
-		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode."
+		extraInfoURL: "https://www.sharelatex.com/learn/Errors/LaTeX_Error:_%5Cmathrm_allowed_only_in_math_mode"
 		humanReadableHint: """
 			You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
 		"""


Check for non-existant exercises

Check for non-existant exercises

--- src/components/reference-book/exercise.cjsx
+++ src/components/reference-book/exercise.cjsx
@@ -14,8 +14,9 @@
   displayName: 'ReferenceBookExercise'
   render: ->
     {exerciseAPIUrl} = @props
-    {items} = ReferenceBookExerciseStore.get(exerciseAPIUrl)
-
+    exercise = ReferenceBookExerciseStore.get(exerciseAPIUrl)
+    return null unless exercise
+    {items} = exercise
     unless items?.length
       # warning about missing exercise --
       # is there a need to show the reader anything?


Unhide the 'open in explorer' link now that we have an explorer again

--- dist/gae/scripts/view.coffee
+++ dist/gae/scripts/view.coffee
@@ -771,7 +771,7 @@

         exploreLink = ExploreLink.fromPartialHistory @calls
         exploreLink.textContent = "open in bid explorer"
-        #@appendChild exploreLink
+        @appendChild exploreLink

     @fromBoardAndCalls: (board, calls) ->
         return alloc @, board, calls


Sets categorySlug to null.

Sets categorySlug to null.

--- app/assets/javascripts/voluntary_feedback/routes/community/feedbacks_route.js.coffee
+++ app/assets/javascripts/voluntary_feedback/routes/community/feedbacks_route.js.coffee
@@ -15,6 +15,8 @@
     if @get('routeName').split('.').length == 3
       categorySlug = if @modelFor('community.category') then @modelFor('community.category').slug else null
       @controllerFor('community.feedbacks').set 'categorySlug', categorySlug
+    else
+      @controllerFor('community.feedbacks').set 'categorySlug', null

     if feedbackType == 'feedbacks'
       @store.query 'feedback', community_slug: @modelFor('community').slug, category_slug: categorySlug, page: params.page


fix curly braces count

fix curly braces count


swoole_server->close()

--- snippets/language-swoole.cson
+++ snippets/language-swoole.cson
@@ -32,6 +32,18 @@
   'swoole_server->shutdown':
     'prefix':'swservshutdown'
     'body':'shutdown();$0'
+  'swoole_server->after':
+    'prefix':'swservafter'
+    'body':'after(\$after_time_ms, \$callback_function);$0'
+  'swoole_server->defer':
+    'prefix':'swservdefer'
+    'body':'defer(\$callback_function);$0'
+  'swoole_server->clearTimer':
+    'prefix':'swservclearTimer'
+    'body':'clearTimer(\$timer_id);$0'
+  'swoole_server->close':
+    'prefix':'swservclose'
+    'body':'close(\$fd, false);$0'

   'swoole_client':
     'prefix':'nswclit'


MISC: minor reformatting.

MISC: minor reformatting.

--- spec/closer-core-spec.coffee
+++ spec/closer-core-spec.coffee
@@ -3,20 +3,11 @@
 helpers = require './closer-core-helpers'
 evaluate = helpers.evaluate

-beforeEach ->
-  @addMatchers toDeepEqual: global_helpers.toDeepEqual
-
-eq = (src, expected) ->
-  expect(evaluate src).toDeepEqual expected
-
-throws = (src) ->
-  expect(-> evaluate src).toThrow()
-
-truthy = (src) ->
-  eq src, types.Boolean.true
-
-falsy = (src) ->
-  eq src, types.Boolean.false
+beforeEach -> @addMatchers toDeepEqual: global_helpers.toDeepEqual
+eq = (src, expected) -> expect(evaluate src).toDeepEqual expected
+throws = (src) -> expect(-> evaluate src).toThrow()
+truthy = (src) -> eq src, types.Boolean.true
+falsy = (src) -> eq src, types.Boolean.false

 describe 'Closer core library', ->


Shell.Model: deep cloning test

Shell.Model: deep cloning test

The previous test does not properly test deep cloning:

    modelInstance.set 'b', {foo: 'bar'}
    modelClone = modelInstance.clone()
    modelInstance.get('b').foo = 'biz'
    expect(modelInstance.get('b').foo).toBe 'biz'
    expect(modelClone.get('b').foo).toBe 'bar'      # Fails

@ali01

--- coffee/test/shells/shell.spec.coffee
+++ coffee/test/shells/shell.spec.coffee
@@ -14,7 +14,7 @@
     _.each requiredProperties, (property) ->
       expect(Shell[property]).toBeDefined()
       expect(_.isString Shell[property]).toBe true
-
+
   it 'should contain required Model and View classes', ->
     requiredClasses = [ 'Model', 'ContentView', 'RemixView' ]
     _.each requiredClasses, (property) ->
@@ -42,11 +42,16 @@
       expect(modelInstance.save).toThrow()
       expect(modelInstance.sync).toThrow()

-    it 'should correctly support clone', ->
-      modelClone = modelInstance.clone()
-      modelClone.set 'a', 42
-      expect(modelInstance.get 'a').toBe 2
-      expect(modelClone.get 'a').toBe 42
+    it 'should be clonable (with deep-copies)', ->
+      deep = {'a': 5}
+      m = new Shell.Model {shellid:'Shell', a: b: c: deep}
+      expect(m.clone().attributes.a.b.c).toEqual deep
+      expect(m.clone().attributes.a.b.c).not.toBe deep
+
+      # ensure the same thing breaks on Backbone's non-deep copy
+      b = new Backbone.Model {shellid:'Shell', a: b: c: deep}
+      expect(b.clone().attributes.a.b.c).toEqual deep
+      expect(b.clone().attributes.a.b.c).toBe deep

     it 'should have a toJSONString function', ->
       expect(typeof Shell.Model::toJSONString).toBe 'function'


Write default CSS immediately

Write default CSS immediately

--- layout.coffee
+++ layout.coffee
@@ -400,13 +400,15 @@
     return

-  # init all views in the DOM recursively
-  init = (selector = $('view')) ->
+  # write default CSS to the DOM
+  writeDefaultStyle = () ->
     style = document.createElement('style')
     style.type = 'text/css'
     style.innerHTML = '.sprite{position:absolute;} .hidden{display:none;}'
     document.getElementsByTagName('head')[0].appendChild(style)

+  # init all views in the DOM recursively
+  init = (selector = $('view')) ->
     for el, i in selector
       initFromElement(el) unless el.$defer or el.$view
     # listen for jQuery style changes
@@ -654,10 +656,12 @@
     layout: Layout,
     simplelayout: SimpleLayout,
     initViews: init
+    writeDefaultStyle: writeDefaultStyle
   }

+lz.writeDefaultStyle()
 $(window).on('load', () ->
   lz.initViews()
   canvas = new lz.view(null, {x: 100, y: 100, bgcolor: 'red', width: 100, height: 100, transform: 'rotate(45deg)', parent: $('#canvas')})
   aview = new lz.view(null, {x: 50, y:50, width:20, height:20, bgcolor: 'green', 'border-radius': '4px', parent:canvas})
-)
+)


[options] fixed bug

[options] fixed bug

--- src/coffee/options.coffee
+++ src/coffee/options.coffee
@@ -90,7 +90,7 @@
                     $elem.append $('<p class="error">').text err
         else
             Object.each errors, (key, value) ->
-                reloadManifestErrorsHelper value, $elem.find '[data-key="' + key + '"]'
+                reloadManifestErrors.helper value, $elem.find '[data-key="' + key + '"]'

     reloadManifests = ->
         module.log.log 'Reloading manifests...'


Fix comments. Related to #18

Fix comments. Related to #18

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -313,52 +313,51 @@
             return User.sortByLevelAndRating(a.user, b.user)

         table = (userList, table) ->
-                data = []
-                users = await User.findByList(userList)
-                tables = await getTables(table)
-                #[users, tables] = await awaitAll([users, tables])
-                getTableResults = (user, tableName, tables) ->
-                    sumTable = await TableResults.findByUserAndTable(user._id, tableName)
-                    if sumTable
-                        return
-                            results: sumTable?.data?.results
-                            total : sumTable?.data?.total
-                    else
-                        return getUserResult(user._id, tables, 1)
-                for user in users
-                    data.push getTableResults user, table, tables
-                results = await awaitAll(data)
-                results = ({r..., user: users[i]} for r, i in results when r)
-                results = results.sort(if table == "main" then sortByLevelAndRating else sortBySolved)
-                return results
-        res.json(await table(req.params.userList, req.params.table))
+        userList = req.params.userList
+        table = req.params.table
+        data = []
+        users = await User.findByList(userList)
+        tables = await getTables(table)
+        #[users, tables] = await awaitAll([users, tables])
+        getTableResults = (user, tableName, tables) ->
+            sumTable = await TableResults.findByUserAndTable(user._id, tableName)
+            if sumTable
+                return
+                    results: sumTable?.data?.results
+                    total : sumTable?.data?.total
+            else
+                return getUserResult(user._id, tables, 1)
+        for user in users
+            data.push getTableResults user, table, tables
+        results = await awaitAll(data)
+        results = ({r..., user: users[i]} for r, i in results when r)
+        results = results.sort(if table == "main" then sortByLevelAndRating else sortBySolved)
+        res.json(results)

     app.get '/api/fullUser/:id', wrap (req, res) ->
-        fullUser = (userId) ->
-            tables = []
-            for t in allTables when t != 'main'
-              tables.push(getTables(t))
-            tables = await awaitAll(tables)
-
-            user = await User.findById(userId)
-            calendar = await Calendar.findById(userId)
-            if not user
-                return null
-            results = []
-            for t in tables
-                results.push(getUserResult(user._id, t, 1))
-            results = await awaitAll(results)
-            results = (r.results for r in results when r)
-            return
-                user: user.toObject()
-                results: results
-                calendar: calendar?.toObject()
-
-        id = req.params.id
-        result = await fullUser(id)
+        userId = req.params.id
+        tables = []
+        for t in allTables when t != 'main'
+          tables.push(getTables(t))
+        tables = await awaitAll(tables)
+
+        user = await User.findById(userId)
+        calendar = await Calendar.findById(userId)
+        if not user
+            return null
+        results = []
+        for t in tables
+            results.push(getUserResult(user._id, t, 1))
+        results = await awaitAll(results)
+        results = (r.results for r in results when r)
+        result =
+            user: user.toObject()
+            results: results
+            calendar: calendar?.toObject()
+
         userPrivate = {}
-        if req.user?.admin or ""+req.user?.userKey() == ""+req.params.id
-            userPrivate = (await UserPrivate.findById(id))?.toObject() || {}
+        if req.user?.admin or ""+req.user?.userKey() == ""+userId
+            userPrivate = (await UserPrivate.findById(userId))?.toObject() || {}
         result.user = {result.user..., userPrivate...}
         res.json(result)


Remove/update logs

Remove/update logs

--- server/api/setupApi.coffee
+++ server/api/setupApi.coffee
@@ -1224,7 +1224,6 @@
                     currency: "RUB"
                 description:
                     value: "algoprog.ru"
-        console.log data
         try
             result = await download(url, undefined, {
                 json: data
@@ -1235,17 +1234,13 @@
             })
         catch e
             throw "Can't download xsolla api"
-        console.log result
         res.json({token: result.token})

     app.post '/xsollaHook', bodyParser.raw({type: "*/*"}), wrap (req, res) ->
-        console.log req.body
-        console.log req.body.toString() + XSOLLA_SECRET_KEY
         hash = sha1(req.body.toString() + XSOLLA_SECRET_KEY)
         signature = req.get("Authorization")
-        console.log hash, signature
         if signature != "Signature " + hash
-            logger.error("xsollaHook: wrong hash, expected " + hash)
+            logger.error("xsollaHook: wrong hash")
             res.status(400).json
                 error:
                     code: "INVALID_SIGNATURE",
@@ -1256,22 +1251,27 @@
             userId = data.user.id
             user = await User.findById(userId)
             if user
+                logger.error("xsollaHook: ok user " + userId)
                 res.status(204).send('')
             else
+                logger.error("xsollaHook: bad user " + userId)
                 res.status(400).json
                     error:
                         code: "INVALID_USER",
                         message: "Invalid user"
             return
         if data.notification_type == "refund"
+            logger.error("xsollaHook: refund")
             res.status(204).send('')
             return
         if data.notification_type != "payment"
+            logger.error("xsollaHook: unsupported notification type")
             res.status(400).json
                 error:
                     code: "INVALID_PARAMETER",
                     message: "Unsupported notification type"
             return
+        logger.error("xsollaHook: payment success")
         success = true
         orderId = data.transaction.external_id
         amount = data.purchase.checkout.amount


clean up comments

clean up comments

--- apps/fair_info/client/events.coffee
+++ apps/fair_info/client/events.coffee
@@ -3,10 +3,4 @@
 module.exports = class FairInfoEvents extends Backbone.View

   initialize: (options) ->
-    # console.log 'fair event: ', options.fairEvent
-    # options.fairEvent.fetch
-    #   cache: true
-    #   error: console.log "there was an error!"
-    #   success: ->
-    #     console.log 'fetched fairEvent'
-    # @fairEvent = options.fairEvent
+


Process image on inputs changes from the original data pixels

--- assets/js/gimme/gimme.coffee
+++ assets/js/gimme/gimme.coffee
@@ -30,6 +30,8 @@

 	onColorParamsChanged = () ->
 		if @type == "text" then colorValue = @value else intensityValue = parseInt @value || 0
+		camanImage.revert false
+		camanImage.colorize(colorValue, intensityValue).render(_onImageProcessed);

 	["paste", "change"].forEach (eventName) ->
 		colorInput.addEventListener eventName, onColorParamsChanged, false


add background colour enabler

add background colour enabler

Signed-off-by: Huang Cheng <8ef188e1917e6f607a1ed78972b8f4f0faf9504a@gmail.com>

--- lib/highlight-line-view.coffee
+++ lib/highlight-line-view.coffee
@@ -7,7 +7,7 @@

 module.exports =
   configDefaults:
-    enable: true
+    enableBackgroundColor: true
     backgroundRgbColor: "100, 100, 100"
     opacity: "50%"
     underline:
@@ -80,28 +80,34 @@

   updateSelectedLine: =>
     @resetBackground()
-    if atom.config.get('highlight-line.enable')
-      @showHighlight()
+    @showHighlight()

   resetBackground: ->
     $('.line').css('background-color', '')
               .css('border-bottom','')
               .css('margin-bottom','')

+  makeLineStyleAttr: ->
+    styleAttr = ''
+    if atom.config.get('highlight-line.enableBackgroundColor')
+      bgColor = @wantedColor('backgroundRgbColor')
+      bgRgba = "rgba(#{bgColor}, #{@wantedOpacity()})"
+      styleAttr += "background-color: #{bgRgba};"
+    if underlineStyleInUsed isnt
+      ulColor = @wantedColor('underlineRgbColor')
+      ulRgba = "rgba(#{ulColor},1)"
+      styleAttr += "border-bottom: 1px #{underlineStyleInUsed} #{ulRgba};"
+      styleAttr += "margin-bottom: #{@marginHeight}px;"
+    styleAttr
+
   showHighlight: =>
-    bgColor = @wantedColor('backgroundRgbColor')
-    ulColor = @wantedColor('underlineRgbColor')
-    bgRgba = "rgba(#{bgColor}, #{@wantedOpacity()})"
-    ulRgba = "rgba(#{ulColor},1)"
-    cursorViews = @editorView.getCursorViews()
-    for cursorView in cursorViews
-      range = cursorView.getScreenPosition()
-      lineElement = @editorView.lineElementForScreenRow(range.row)
-      $(lineElement).attr(
-        'style',
-        "background-color: #{bgRgba};" +
-          "border-bottom: 1px #{underlineStyleInUsed} #{ulRgba};" +
-          "margin-bottom: #{@marginHeight}px;")
+    styleAttr = @makeLineStyleAttr()
+    if styleAttr
+      cursorViews = @editorView.getCursorViews()
+      for cursorView in cursorViews
+        range = cursorView.getScreenPosition()
+        lineElement = @editorView.lineElementForScreenRow(range.row)
+        $(lineElement).attr 'style', styleAttr

   wantedColor: (color) ->
     wantedColor = atom.config.get("highlight-line.#{color}")


Fix error being thrown when moving tabs to different panes

--- lib/highlight-line-view.coffee
+++ lib/highlight-line-view.coffee
@@ -37,8 +37,7 @@
     @updateSelectedLine()

   # Tear down any state and detach
-  destroy: (pane) =>
-    return if @editorView.getPane().getItems().length isnt 0
+  destroy: =>
     @unsubscribe()
     @remove()
     @detach()


multistep

multistep

--- src/config/turtles.coffee
+++ src/config/turtles.coffee
@@ -60,13 +60,13 @@
     counter = counter + 1
     counter= 0 if counter >= program.length
     world.put('program_counter', counter)
-    world.call(action['do'], action) if action?
+    turtles = world.find_parent 'turtles'
+    turtles.map_children (child) ->
+      child.call(action['do'], action) if action?

   reset: (world, args) ->
+    world.put 'program_counter', 0
     turtles = world.find_parent 'turtles'
-    assert turtles, "can't find parent"
-    console.log 'reset', world, turtles
-    world.put 'program_counter', 0
     turtles.map_children (child) ->
       child.put 'i', child.get('i_0')
       child.put 'j', child.get('j_0')


Add OSX queue

Add OSX queue

--- assets/scripts/travis.coffee
+++ assets/scripts/travis.coffee
@@ -55,6 +55,7 @@
     { name: 'common',  display: 'Common' }
     { name: 'php',     display: 'PHP, Perl and Python' }
     { name: 'linux',   display: 'Linux' }
+    { name: 'mac_osx', display: 'Queue: Mac and OSX' }
   ]

   INTERVALS: { sponsors: -1, times: -1, updateTimes: 1000 }


quick and dirty approach for #37

quick and dirty approach for #37

--- lib/linter-languagetool.coffee
+++ lib/linter-languagetool.coffee
@@ -24,6 +24,11 @@
     fallbackToPublicApi:
       title: 'Fallback to public Languagetool server API'
       description: 'Fallback to public Languagetool server in case the local languagetool-server.jar fails to start up or is missing.'
+      type: 'boolean'
+      default: false
+    disableStatusIcon:
+      title: 'Disables Icon in bottom bar'
+      description: ''
       type: 'boolean'
       default: false
     grammerScopes:
@@ -85,7 +90,8 @@
     @subscriptions = null

   consumeStatusBar: (statusBar) ->
-    @statusBarTile = statusBar.addRightTile(item: @ltInfo.element, priority: 400)
+    if not atom.config.get 'linter-languagetool.disableStatusIcon'
+      @statusBarTile = statusBar.addRightTile(item: @ltInfo.element, priority: 400)

   provideLinter: ->
     LinterProvider = require './linter-provider'


It's Mogotest.

It's Mogotest.

--- assets/scripts/data/sponsors.coffee
+++ assets/scripts/data/sponsors.coffee
@@ -42,7 +42,7 @@
   { type: 'silver', link: "Pivotal Labs"}
   { type: 'silver', link: "Fiksu"}
   { type: 'silver', link: "Sauce Labs"}
-  { type: 'silver', link: "MogoNever be embarrassed by a visually broken site again."}
+  { type: 'silver', link: "MogotestNever be embarrassed by a visually broken site again."}
 ]

 @Travis.WORKERS = {


Fix the binder propagation

Fix the binder propagation

--- library/membrane/duplex.coffee
+++ library/membrane/duplex.coffee
@@ -157,7 +157,6 @@
     # protective nature. It also exposes some goodies for the provider.
     # Such as Socket.IO handle, session if available and the context.
     @covering: (method, socket, context, binder) ->
-        assert socket.handshake.binder = binder
         assert _.isFunction o = Marshal.serialize
         assert _.isFunction i = Marshal.deserialize
         socket.on "disconnect", -> try guarded.dispose()
@@ -172,6 +171,7 @@
             respond.socket = socket; respond.context = context
             _.extend respond, socket.handshake or new Object
             _.extend socket, socket.handshake or new Object
+            assert respond.binder = socket.binder = binder
             return execute parameters..., respond

     # This server side method is called on the context prior to the


import single connector instead of all of them

--- server/lib/importer.coffee
+++ server/lib/importer.coffee
@@ -4,24 +4,13 @@
     date: true
 Konnector = require '../models/konnector'

-module.exports = (done) ->
-    log.info "fetching data ..."
+module.exports = (konnector) ->
+    log.debug "fetching data ..."
+    log.debug konnector

-    Konnector.all (err, konnectors) ->
-
-        async.eachSeries konnectors, (konnector, callback) ->
-
-            # if the konnector fields are not empty and its not already importing
-            if konnector.fieldValues? and konnector.isImporting is false
-                log.info "Processing #{konnector.slug}"
-
-                model = require "../konnectors/#{konnector.slug}"
-                konnector.import konnector.fieldValues, model.fields, (err) ->
-                    if err
-                        console.log err
-
-                callback()
-            # Empty konnectorr
-            else
-                callback()
-        , done
+    if konnector.fieldValues? and konnector.isImporting is false
+        log.debug "Importing #{konnector.slug}"
+        model = require "../konnectors/#{konnector.slug}"
+        konnector.import konnector, model.fields, (err) ->
+            if err
+                log.info err


rewrite sort order using multiple levels

--- server/methods/word.coffee
+++ server/methods/word.coffee
@@ -139,33 +139,55 @@
     # builds hierarchy of organSites -> studies -> results
     tbl = Tables.findOne(tbl_id)

+    # fetch the data that we need
+    organSites = []
+    epiDescs = EpiDescriptive.find({"tbl_id": tbl_id}).fetch()
+    epiResults = EpiResult.find({"tbl_id": tbl_id}).fetch()
+
+    # get fields ready for reporting
+    _.map(epiDescs, prepareEpiDescriptive)
+    _.map(epiResults, prepareEpiResult)
+
+    # get unique study-types
+    studyTypes = _.chain(epiDescs)
+                  .pluck("populationSize")
+                  .unique(false)
+                  .value()
+
     # get unique sites
-    organSites = []
-    epiResults = EpiResult.find({"tbl_id": tbl_id}, {sort: {"sortIdx": 1}}).fetch()
-    epiDescs = EpiDescriptive.find({"tbl_id": tbl_id}, {sort: {"sortIdx": 1}}).fetch()
-
-    sites = _.chain(epiResults).pluck("organSite").unique(false).value()
-
-    # loop through unique sites
-    for site in sites
-
-        desc_ids =  _.chain(epiResults)
-                     .where({"organSite": site})
-                     .pluck("parent_id")
-                     .uniq(false)
-                     .value()
-
-        studies = []
-        for desc_id in desc_ids
-            study = _.findWhere(epiDescs, {"_id": desc_id})
-            prepareEpiDescriptive(study)
-            study.results = _.chain(epiResults)
-                             .where({"organSite": site, "parent_id": desc_id})
-                             .map(prepareEpiResult)
-                             .value()
-            studies.push study
-
-        organSites.push({"organSite": site, "studies": studies})
+    sites = _.chain(epiResults)
+             .pluck("organSite")
+             .unique(false)
+             .value()
+
+    for st in studyTypes
+
+        valid_studies = _.chain(epiDescs)
+                   .where({"populationSize": st})
+                   .pluck("_id")
+                   .value()
+
+        # loop through unique sites
+        for site in sites
+
+            # find results which match sort criteria
+            desc_ids =  _.chain(epiResults)
+                         .filter((d) -> d.parent_id in valid_studies)
+                         .where({"organSite": site})
+                         .sortBy((d) -> return d.eligibilityCriteria)
+                         .pluck("parent_id")
+                         .unique(false)
+                         .value()
+
+            studies = []
+            for desc_id in desc_ids
+                study = JSON.parse(JSON.stringify(_.findWhere(epiDescs, {"_id": desc_id})))
+                study.results = _.where(epiResults, {"organSite": site, "parent_id": desc_id})
+                studies.push(study)
+
+            if studies.length>0
+                caption  = "#{st}: #{site}"
+                organSites.push({"organSite": caption, "studies": studies})

     data =
         "organSites": organSites


No need for sitemap route.

No need for sitemap route.

--- server/routes/index.coffee
+++ server/routes/index.coffee
@@ -39,15 +39,6 @@
       }
     }

-  # Sitemap.xml.
-  app.get '/sitemap.xml', (req, res) ->
-    # Send file.
-    res.sendFile sitemap, {
-      headers: {
-        'Content-Type': 'text/xml',
-      }
-    }
-
   # Robots.txt.
   app.get '/robots.txt', (req, res) ->
     # Send file.


fixed tests

fixed tests

--- spec/jquery.cumin.spec.coffee
+++ spec/jquery.cumin.spec.coffee
@@ -45,6 +45,7 @@
         removeSpy = sinon.spy Cumin, 'remove'

       after ->
+        Cumin.clear()
         server.restore()
         addSpy.restore()
         removeSpy.restore()
@@ -108,8 +109,8 @@

       before ->
         requestSpy = sinon.spy Cumin, 'request'
-        Cumin.add 'processQueue1'
-        Cumin.add 'processQueue2'
+        Cumin.add 'fooId', 'fooRequest'
+        Cumin.add 'barId', 'barRequest'
         Cumin.processQueue()

       it 'should call request for each send call', ->


search dialog for log

search dialog for log

--- lib/actions/views/logs.coffee
+++ lib/actions/views/logs.coffee
@@ -89,7 +89,7 @@
     searchByText("#search", "#log tr", "#searchCount")

 this.searchByText = (input, row, info) ->
-  $(input).changes ->
+  $(input).change ->
     matches = 0
     searchTerm = $(this).val()
     $(row).each ->


add TODO comment

add TODO comment

--- src/fixture-loader.coffee
+++ src/fixture-loader.coffee
@@ -95,7 +95,7 @@
                     fx.data

         try
-            repo = @facade.createPreferredRepository(modelName)
+            repo = @facade.createPreferredRepository(modelName) # TODO: enable to add 2nd argument (noParent: boolean)
         catch e
             console.error e.message
             return


Simplified syntax

Simplified syntax

--- lib/backbone-relations.coffee
+++ lib/backbone-relations.coffee
@@ -49,9 +49,8 @@
         next.on 'change:id', onChangeId
         next.on 'destroy', onDestroy

-    (onChangeMine = ->
+    do onChangeMine = ->
       model.set[name] ctor.cache().get model.get mine
-    )()

     model.on "change:#{mine}", onChangeMine


Fix CommandBusClient bug

Fix CommandBusClient bug

--- lib/command_bus_client.coffee
+++ lib/command_bus_client.coffee
@@ -44,7 +44,7 @@

     stream.write "Content-Disposition": "form-data; name=\"name\"", commandName

-    for arg in args
+    for arg in commandArgs
       headers =
         "Content-Disposition": "form-data; name=\"args[]\""
         "Content-Type": "application/json"


Ignore mutations on parent-less nodes

Ignore mutations on parent-less nodes

--- src/trix/observers/mutation_observer.coffee
+++ src/trix/observers/mutation_observer.coffee
@@ -44,7 +44,7 @@
     false

   nodeIsSignificant: (node) ->
-    node isnt @element and not @nodeIsMutable(node)
+    node?.parentNode and node isnt @element and not @nodeIsMutable(node)

   nodeIsMutable: (node) ->
     findClosestElementFromNode(node, matchingSelector: mutableSelector)


Set source of effects

Set source of effects

Signed-off-by: Ian Macalinao <08370e385f4778cad8fe504ec5445edd3d45bd9a@gmail.com>

--- src/item.coffee
+++ src/item.coffee
@@ -26,6 +26,17 @@
     @passives = params.passives
     @active = params.active

+    # Set source of all effects
+    if @aura
+      @aura.source = this
+
+    if @passives
+      for passive in @passives
+        passive.source = this
+
+    if @active
+      @active.source = this
+
   getRecipe: ->
     recipe = []
     for x in @recipe


fix(nylas-api): When offline, response is undefined

--- src/flux/nylas-api.coffee
+++ src/flux/nylas-api.coffee
@@ -122,9 +122,9 @@
       PriorityUICoordinator.settle.then =>
         Actions.didMakeAPIRequest({request: options, response: response})
         if error? or response.statusCode > 299
-          if response.statusCode is 404 and options.returnsModel
+          if response and response.statusCode is 404 and options.returnsModel
             @_handleModel404(options.url)
-          if response.statusCode is 401
+          if response and response.statusCode is 401
             @_handle401(options.url)
           options.error?(new APIError({error, response, body}))
         else