Fixed not shown user in ticket zoom if user isn't already available in local collection.

--- app/assets/javascripts/app/controllers/agent_ticket_customer.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_customer.js.coffee
@@ -32,8 +32,13 @@
       customer_id: params['customer_id']
     )

-    # close modal
-    @modalHide()
+    callback = =>

-    # reload zoom view
-    @zoom.render()
+      # close modal
+      @modalHide()
+
+      # reload zoom view
+      @zoom.render()
+
+    # load user if not already exists
+    App.Collection.find( 'User', params['customer_id'], callback )


the rectangle can be resized from all points, however one cannot relocate the rectangle via the lower left point

--- app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
+++ app/assets/javascripts/components/mark/tools/rectangle-tool/index.cjsx
@@ -100,24 +100,15 @@
     @props.mark.y += d.y / @props.yScale
     @props.onChange e

-  handleTopLeftDrag: (e, d) ->
+  handleIndividualPointDrag: (e, d) ->
     console.log "HTLD", e, d
     console.log "HTLD @props", @props
     @props.mark.x += d.x / @props.xScale
     @props.mark.y += d.y / @props.yScale
-    @props.width -= d.x / @props.xScale
-    @props.height -= d.y / @props.yScale
-    @props.onChange e
-
-  handleLowerLeftDrag: (e, d) ->
-    @props.mark.x += d.x / @props.xScale
-    @props.mark.y += d.y / @props.yScale
-    @props.width -= d.x / @props.xScale
-    @props.height -= d.y / @props.yScale
+    @props.mark.width -= d.x / @props.xScale
+    @props.mark.height -= d.y / @props.yScale
     @props.onChange e

-
-
   getDeleteButtonPosition: ->
     theta = (DELETE_BUTTON_ANGLE) * (Math.PI / 180)
     x: (SELECTED_RADIUS / @props.xScale) * Math.cos theta
@@ -165,10 +156,10 @@
         { if @props.selected
           <g>
             <DeleteButton tool={this} x={@state.x + (width * DELETE_BUTTON_DISTANCE)} y={@state.y} />
-            <DragHandle x={@props.mark.x} y={@props.mark.y} onDrag={@handleTopLeftDrag} />
-            <DragHandle x={x2} y={y1} onDrag={@handleUpperRightDrag} />
-            <DragHandle x={x2} y={y2} onDrag={@handleLowerRightDrag} />
-            <DragHandle x={x1} y={y2} onDrag={@handleLowerLeftDrag} />
+            <DragHandle x={@props.mark.x} y={@props.mark.y} onDrag={@handleIndividualPointDrag} />
+            <DragHandle x={x2} y={y1} onDrag={@handleIndividualPointDrag} />
+            <DragHandle x={x2} y={y2} onDrag={@handleIndividualPointDrag} />
+            <DragHandle x={x1} y={y2} onDrag={@handleIndividualPointDrag} />
           </g>
         }
       </g>


Keep the DocumentType in clearDocument

Keep the DocumentType in clearDocument

--- src/client/client_engine.coffee
+++ src/client/client_engine.coffee
@@ -50,8 +50,9 @@
         RPCMethods.disconnect.call(this)

     clearDocument : (doc) ->
-        while doc.hasChildNodes()
-            doc.removeChild(doc.firstChild)
+        # remove all nodes except for the DocumentType
+        while doc.hasChildNodes() and doc.lastChild.nodeType != 10
+            doc.removeChild(doc.lastChild)
         delete doc.__nodeID

     setupRPC : (socket) ->


Wrap also body if HTTP error occurs. Closes #31

--- src/coffee/services/base.coffee
+++ src/coffee/services/base.coffee
@@ -385,6 +385,7 @@
       deferred.reject
         statusCode: 500
         message: error
+        body: body
         originalRequest: originalRequest
     else
       # TODO: check other possible acceptable codes (304, ...)


format result: remove merged at

format result: remove merged at

--- legilimens.coffee
+++ legilimens.coffee
@@ -55,9 +55,10 @@

 printPullRequestsReport = (pullRequests) ->
   console.log "New merged pull requests:"
+  index = 1
   pullRequests.forEach (pullRequest) ->
-    mergedAt = new Date(pullRequest.merged_at)
-    console.log "##{pullRequest.number} #{pullRequest.title} by @#{pullRequest.user.login} (merged at #{mergedAt.toLocaleDateString()} #{mergedAt.toLocaleTimeString()})"
+    console.log "#{index}. ##{pullRequest.number} #{pullRequest.title} by @#{pullRequest.user.login}"
+    index++

 getLastedReleaseTime.then (lastedReleaseTime) ->
   getClosedPullRequestsAfter(lastedReleaseTime)


feat(log): add a checkbox per line

feat(log): add a checkbox per line

--- legilimens.coffee
+++ legilimens.coffee
@@ -65,7 +65,7 @@
   console.log "New merged pull requests:"
   index = 1
   pullRequests.forEach (pullRequest) ->
-    console.log "#{index}. ##{pullRequest.number} #{pullRequest.title} by @#{pullRequest.user.login}"
+    console.log "- [] #{index}. ##{pullRequest.number} #{pullRequest.title} by @#{pullRequest.user.login}"
     index++

 getLastedReleaseTime.then (lastedReleaseTime) ->


set slide completed through arrow navigation

--- app/assets/javascripts/training/components/training_slide_handler.cjsx
+++ app/assets/javascripts/training/components/training_slide_handler.cjsx
@@ -72,6 +72,7 @@
       params = _.extend navParams, slide_id: @state.previousSlide.slug
       @transitionTo 'slide', params
     if e.which == @keys.rightKey && @state.nextSlide?
+      @setSlideCompleted()
       params = _.extend navParams, slide_id: @state.nextSlide.slug
       @transitionTo 'slide', params


changed tootip style

changed tootip style

--- app/assets/javascripts/analysis/views/area_results_view.coffee
+++ app/assets/javascripts/analysis/views/area_results_view.coffee
@@ -30,6 +30,7 @@
       return "#{timeInDays} days"

     return "#{timeInYears} years"
+

   resultsToObj: ->
     if @area.get('results')? && @area.get('results').length > 0
@@ -64,6 +65,7 @@
     else
       return {}

+
   toggleSharePopover: ->
     $('.permalink').toggle()

@@ -78,6 +80,7 @@
     if @$el.find("#tot_area_tip").length > 0 then @initializeTooltips()
     this

+
   onClose: ->
     @area.off('change', @render)

@@ -88,5 +91,7 @@
       options =
         tipJoint: "bottom"
         fixed: true
+        background: "#fff"
+        borderColor: "#384658"
       new Opentip(el, value, options)


Set default state to supergood - issue #645.

--- app/assets/javascripts/app/controllers/_dashboard/stats.coffee
+++ app/assets/javascripts/app/controllers/_dashboard/stats.coffee
@@ -16,6 +16,7 @@
       data.StatsTicketWaitingTime =
         handling_time: 0
         average: 0
+        state: 'supergood'
         average_per_agent: 0
     if !data.StatsTicketEscalation
       data.StatsTicketEscalation =
@@ -61,7 +62,7 @@
     @html App.view('dashboard/stats')(data)

     if data.StatsTicketWaitingTime
-      @renderWidgetClockFace data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent
+      @renderWidgetClockFace(data.StatsTicketWaitingTime.handling_time, data.StatsTicketWaitingTime.state, data.StatsTicketWaitingTime.percent)

   renderWidgetClockFace: (time, state, percent) =>
     canvas = @el.find 'canvas'


Improved activate of controller (only once).

--- app/assets/javascripts/app/lib/app_post/task_manager.js.coffee
+++ app/assets/javascripts/app/lib/app_post/task_manager.js.coffee
@@ -120,7 +120,7 @@

     # create new task if not exists
     task = @get( key )
-    console.log('add', key, callback, params, to_not_show, task)
+    console.log('add', key, callback, params, to_not_show, task, active)
     if !task
       console.log('add, create new taskbar in backend')
       task = new App.Taskbar
@@ -204,24 +204,27 @@

     console.log('controller start try...', callback, key)

-    # activate controller
-    worker = @worker( key )
-    if worker && worker.activate
-      worker.activate()
-      App.Event.trigger('ui:rerender:task')
-
-    # return if controller is already started
-    return if @workersStarted[key]
-    @workersStarted[key] = true
-    console.log('controller start now...', callback, key)
-
-    # create new controller instanz
+    # create params
     params_app = _.clone(params)
     params_app['el']       = $('#content_permanent_' + key )
     params_app['task_key'] = key
-
     if to_not_show
       params_app['doNotLog'] = 1
+
+    # return if controller is already started
+    if @workersStarted[key]
+
+      # activate existing controller
+      worker = @worker( key )
+      if worker && worker.activate && !to_not_show
+        worker.activate(params_app)
+        App.Event.trigger('ui:rerender:task')
+      return
+
+    @workersStarted[key] = true
+    console.log('controller start now...', callback, key)
+
+    # create new controller instanz
     a = new App[callback]( params_app )
     @workers[ key ] = a
     console.log('controller start now 2...', callback, key)
@@ -229,7 +232,7 @@
     # activate controller
     if !to_not_show
       console.log('controller start now 2 activate...', callback, key)
-      a.activate()
+      a.activate(params_app)

     console.log('controller start now 2 return...', callback, key)
     return a


Downcasing it all baby

Downcasing it all baby

--- app/assets/javascripts/darkswarm/filters/print_array.js.coffee
+++ app/assets/javascripts/darkswarm/filters/print_array.js.coffee
@@ -2,5 +2,5 @@
   (array, attr = 'name')->
     array ?= []
     array.map (a)->
-      a[attr]
+      a[attr].toLowerCase()
     .join(", ")


Limit jquery form validation to smart forms only

- ExpressSurvey forms aren't respond to jquery validation

--- app/assets/javascripts/express_admin/form_validation.js.coffee
+++ app/assets/javascripts/express_admin/form_validation.js.coffee
@@ -1,5 +1,5 @@
 $ ->
-  $('form').validate
+  $('form.smart-form').validate
     debug: true
     errorElement: 'span'


Catch billing errors

Catch billing errors

--- client/app/MainApp/paymentcontroller.coffee
+++ client/app/MainApp/paymentcontroller.coffee
@@ -37,7 +37,7 @@
       val.input.giveValidationFeedback no

     for e in err
-      if e.field == 'account.base'
+      if e.field in ['account.base', 'billing_info.base']
         val.input.showValidationError e.message
         if e.message.indexOf('card') > -1
           inputs.cardNumber.giveValidationFeedback yes


StatusBar: Don't put own avatar to status bar.

--- client/ide/views/statusbar/statusbar.coffee
+++ client/ide/views/statusbar/statusbar.coffee
@@ -76,10 +76,11 @@
   showAvatars: (participants) ->

     @avatars.show()
+    myNickname = KD.nick()

     for participant in participants
       {nickname} = participant
-      @createParticipantAvatar nickname
+      @createParticipantAvatar nickname  unless nickname is myNickname

   removeParticipantAvatar: (nickname) ->


tests: fix failing team creation tests for the new landing pages

--- client/test/lib/helpers/teamshelpers.coffee
+++ client/test/lib/helpers/teamshelpers.coffee
@@ -141,16 +141,16 @@

     user = utils.getUser()
     url  = helpers.getUrl(yes)
-    hasNotTeamAccessPage = '.main-wrapper .login-form .email'
+    hasNotTeamAccessPage = '.TeamsModal--select'

     browser.url url
     browser.maximizeWindow()

     browser.element 'css selector', hasNotTeamAccessPage, (result) =>
       if result.status is 0
-        @getInvitationAndCreateTeam(browser)
+        @getInvitationAndCreateTeam browser
       else
-        @loginToTeam(browser, user)
+        @loginToTeam browser, user

     return user


Show download error in UI.

Show download error in UI.

--- src/Witness/Scripts/witness/ViewModels/SpecificationFileViewModel.coffee
+++ src/Witness/Scripts/witness/ViewModels/SpecificationFileViewModel.coffee
@@ -23,8 +23,9 @@
 		@file.on.downloaded.addHandler =>
 			@status "downloaded"
 		@file.on.downloadFailed.addHandler (errors) =>
-			@status "errors"
+			@status "downloadFailed"
 			@errors.push error for error in errors
+			@isOpen true

 		@file.on.ready.addHandler =>
 			@status "ready"


KDObject: return a promise from #ready if there is a promise library

--- core/KDObject.coffee
+++ core/KDObject.coffee
@@ -37,7 +37,12 @@
     @forwardEvent target, eventName, prefix  for eventName in eventNames

   ready:(listener)->
-    if @readyState is READY then @utils.defer listener
+    if Promise?::nodeify
+      new Promise (resolve) =>
+        resolve() if @readyState is READY
+        @once 'ready', resolve
+      .nodeify listener
+    else if @readyState is READY then @utils.defer listener
     else @once 'ready', listener

   registerSingleton:KD.registerSingleton


Fix test

Fix test

--- components/bookmarks/test/collection.coffee
+++ components/bookmarks/test/collection.coffee
@@ -7,8 +7,8 @@
     sinon.stub Backbone, 'sync'

     @bookmarks = new Bookmarks
-    @bookmarks.add id: 'foo', artist: id: 'bar'
-    @bookmarks.add id: 'bar', artist: id: 'baz'
+    @bookmarks.add id: 'foo', interest: id: 'bar'
+    @bookmarks.add id: 'bar', interest: id: 'baz'

   afterEach ->
     Backbone.sync.restore()


Bugfix to JS behaviour

Bugfix to JS behaviour

--- collector/scripts/frontend.coffee
+++ collector/scripts/frontend.coffee
@@ -70,12 +70,12 @@
 	eventTarget.value = ""
 	replySpace.textContent = reply
 	replySpace.className += " show"
-	parent.scrollY(100000) //Hack
+	parent.scroll(0,100000) //Hack
 	nextResponse = parent.querySelector(".robot:not(.show)")
 	if nextResponse?
 		showNext = ->
 			nextResponse.className += " show"
-			parent.scrollY(100000) //Hack
+			parent.scrollY(0,100000) //Hack
 		setTimeout(showNext, 1000)
 	if not parent.querySelector(".tester:not(.show)")?
 		eventTarget.setAttribute("disabled", "disabled")


Added mounVm method as a signature.

Added mounVm method as a signature.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -114,6 +114,8 @@
   openVMWebPage: (vmData) ->
     @activeTabView.emit 'VMWebPageRequested', vmData

+  mountVM: (vmData) ->
+
   collapseSidebar: ->
     panel        = @workspace.getView()
     splitView    = panel.layout.getSplitViewByName 'BaseSplit'


AppController: Moved handle change logic to panes.

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -878,28 +878,30 @@

   handleChange: (change) ->
+    {context, origin} = change
     myWatchMap = @rtm.getFromModel @realTimeDoc, "#{KD.nick()}WatchMap"
-    {origin}   = change
-
-    return if origin is KD.nick()
+
+    return if not context or not origin or origin is KD.nick()

     if not myWatchMap or myWatchMap.keys().length is 0 or origin in myWatchMap.keys()
-      log 'i need to handle this change...', change
-
-      {context} = change
-      return unless context
-
-      if change.type is 'ContentChange'
-        string = @rtm.getFromModel @realTimeDoc, context.file.path
-
-        @forEachSubViewInIDEViews_ 'editor', (editorPane) =>
-          if editorPane.getFile().path is context.file.path
-            editorPane.setContent string.getText(), no
-
-      if change.type is 'DrawingBoardUpdated'
-        @forEachSubViewInIDEViews_ 'drawing', (drawingPane) =>
-          if drawingPane.hash is context.paneHash
-            drawingPane.setCanvasData change.context.data
+      @getPaneByChange(change)?.handleChange? change
+
+
+  getPaneByChange: (change) ->
+    return unless change.context
+
+    targetPane = null
+    {context}  = change
+    {paneType} = context
+
+    @forEachSubViewInIDEViews_ paneType, (pane) =>
+      if paneType is 'editor'
+        if pane.getFile()?.path is context.file?.path
+          targetPane = pane
+      else
+        targetPane = pane  if pane.hash is context.paneHash
+
+    return targetPane

   createPaneFromChange: (change) ->


Modify jquery selector name

Modify jquery selector name

--- BackofficeBundle/Resources/public/coffee/page/NodeFormView.coffee
+++ BackofficeBundle/Resources/public/coffee/page/NodeFormView.coffee
@@ -3,7 +3,7 @@
   onViewReady: ->
     if @options.submitted
       displayRoute = appRouter.generateUrl "showNode",
-              nodeId: $('#node_nodeId', @$el).val()
+              nodeId: $('#oo_node_nodeId', @$el).val()
       Backbone.history.loadUrl(displayRoute)
       displayMenu(displayRoute)


Disable click on drag

Disable click on drag

--- resources/assets/coffee/react/profile-page/main.coffee
+++ resources/assets/coffee/react/profile-page/main.coffee
@@ -94,6 +94,10 @@
       revert: 150
       scrollSpeed: 0
       update: @updateOrder
+      start: =>
+        @draggingTab = true
+      stop: =>
+        Timeout.set 500, => @draggingTab = false

     osu.pageChange()

@@ -345,6 +349,8 @@
   tabClick: (e) =>
     e.preventDefault()

+    return if @draggingTab
+
     @pageJump null, e.currentTarget.dataset.pageId


fix indentation

fix indentation

--- lib/browse.coffee
+++ lib/browse.coffee
@@ -125,8 +125,8 @@
     # Default file manager
     switch process.platform
       when "darwin"
-         exec "open -R #{path}"
-         fileManager = "Finder"
+        exec "open -R #{path}"
+        fileManager = "Finder"
       when "win32"
         exec "explorer /select,#{path}"
         fileManager = "Explorer"


many bugfixes

many bugfixes

--- lib/bucket.coffee
+++ lib/bucket.coffee
@@ -4,6 +4,8 @@
 clone   = require 'clone'
 uuid    = require 'node-uuid'
 _       = require 'underscore'
+md5     = require 'md5'
+

 exports.Bucket = (fileName) -> {
     fileName    : fileName
@@ -11,10 +13,11 @@
     dirty       : {}
     deleted     : []
     tailProcess : null
+    writehashes : []

     obliterate : (cb) ->
-      closing = () ->
-        fs.unlink fileName, (err) ->
+      closing = () =>
+        fs.unlink @fileName, (err) ->
           unless err?
             delete @bucket
             delete @filename
@@ -27,11 +30,9 @@
             cb(err)

       @tailProcess.on 'exit', closing
-#      @tailProcess.on 'close', closing

       @tailProcess.kill()

-#   store callback, should be (err, statusMessage) ->
     store : (cb) ->
       console.log "store called"
       unless @hasChanges()
@@ -49,7 +50,10 @@
         _.each deletesToWrite, (id) -> dirtyToWrite[id] = {deleted:true, id:id}

         dirtyData = JSON.stringify(dirtyToWrite)
+        hash = md5.digest_s(dirtyData)
+        @writehashes.push hash
         dirtyData = "#{dirtyData}\n"
+
         fs.appendFile @fileName, dirtyData, {encoding:'utf8'}, (err) =>
           unless err?
             console.log "Store about to call success callback"
@@ -62,31 +66,35 @@
     close : (cb) ->
       @tailProcess.kill()

-    reader : () ->
-      #inStream = fs.createReadStream(@fileName, {flags:'r', encoding:'utf8'})
+    reader : (cb) ->
+      doneReading  = _.debounce _.once(cb), 200
+      _.delay doneReading, 200
       @tailProcess = cp.spawn 'tail', ['-f', '-n +0', @fileName]
-      inStream     = @tailProcess.stdout
-      dataReader   = carrier.carry inStream, (line) =>
+      inStream = @tailProcess.stdout
+      dataReader = carrier.carry inStream, (line) =>
         console.log "Reader LINE callback..."
-        chunk   = JSON.parse line
-        deleted = _.where chunk, {deleted:true}
-        deleted = _.pluck deleted, 'id'
-        _.each deleted, (id) =>
-          delete chunk[id]
-          delete @bucket[id]
-
-#        _.extend contextBucket, chunk
-        _.extend @bucket, chunk
+        hash = md5.digest_s(line)
+        if _.contains @writehashes, hash
+          console.log "Got match"
+          @writehashes = _.without @writehashes, hash
+        else
+          chunk   = JSON.parse line
+          deleted = _.where chunk, {deleted:true}
+          deleted = _.pluck deleted, 'id'
+          _.each deleted, (id) =>
+            delete chunk[id]
+            delete @bucket[id]
+          _.extend @bucket, chunk
         console.log "Reader LINE callback end!"
+        doneReading()

       dataReader.once 'end', () ->
         console.log "Reader END callback..."
-#        cb() what?
+        doneReading()

     load : (cb) ->
       fs.closeSync(fs.openSync(@fileName, 'a'))
-      @reader()
-      cb()
+      @reader(cb)

     deleteById : (id) ->
@@ -152,4 +160,6 @@
       exports.bucket = new exports.Bucket(filename)
       exports.bucket.load () ->
         cb(exports.bucket)
+    else
+      cb(exports.bucket)


project team and external links

project team and external links

--- project.coffee
+++ project.coffee
@@ -26,6 +26,42 @@

     '''
   }]
+
+  organizations: [{
+    image: '//placehold.it/100.png?text=Example'
+    name: 'Missouri Botanical Garden'
+    description: 'description'
+  },{
+    image: '//placehold.it/100.png?text=Example'
+    name: 'Constructing Scientific Communities'
+    description: 'AHRC research project'
+    url: ['http://conscicom.org/', 'https://twitter.com/conscicom']
+  },{
+    image: '//placehold.it/100.png?text=Example'
+    name: 'Zooniverse'
+    description: 'Citizen Science Platform'
+    url: ['https://www.zooniverse.org/', 'https://twitter.com/the_zooniverse', 'https://github.com/zooniverse']
+  }]
+
+  scientists: [{
+    image: '//placehold.it/100.png?text=Example'
+    name: 'Geoffrey Belknap'
+    location: 'Leicester, U.K.'
+    description: 'Postdoctoral researcher, Constructing Scientific Communities'
+    url: 'http://twitter.com/geoffreybelknap'
+  }]
+
+  developers: [{
+    image: '//placehold.it/100.png?text=Example'
+    name: 'Jim O\'Donnell'
+    location: 'Oxford, UK'
+    description: 'Web Developer'
+    url: 'http://twitter.com/pekingspring'
+  }]
+
+  externalLinks:
+    Blog: 'http://blog.zooniverse.org'
+    Twitter: 'http://twitter.com/the_zooniverse'

   tasks:
     illustrations:


Adjust code to attach event handler at body, not document level

My working theory is that Prototype and/or Chrome has changed to not propogate custom events
past the body (to the document).

--- tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee
+++ tapestry-core/src/test/coffeescript/META-INF/modules/palette-demo.coffee
@@ -1,7 +1,9 @@
-define ["t5/core/dom", "t5/core/events", "underscore"],
-  (dom, events, _) ->
+define ["t5/core/dom", "t5/core/events", "underscore", "t5/core/console"],
+  (dom, events, _, console) ->

-    dom.onDocument events.palette.willChange, (event, memo) ->
+    dom.body.on events.palette.willChange, (event, memo) ->
+
+      console.info "palette-demo, palette willChange"

       values = _.map memo.selectedOptions, (o) -> o.value


added comment

added comment

--- lib/client.coffee
+++ lib/client.coffee
@@ -8,6 +8,9 @@

   connect: (socket) ->
     @status = 'ready'
+
+    # This is a work around for not having an EventEmitter.
+    # I could add one in but not sure if it's worth it.
     for q in queued
       q()
     queued = []


Fixes essence_picture update via remote form.

--- app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee
+++ app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee
@@ -11,7 +11,6 @@
       onSelect: (coords) ->
         crop_from_field.val coords.x + "x" + coords.y
         crop_size_field.val coords.w + "x" + coords.h
-        return
       setSelect: box
       aspectRatio: (if ratio then ratio else `undefined`)
       minSize: [size_x, size_y]
@@ -26,21 +25,17 @@
       Alchemy.ImageCropper.api = $.Jcrop("#imageToCrop", options)
       Alchemy.ImageCropper.initialized = true
     $(".alchemy_overlay").on 'dialogclose', Alchemy.ImageCropper.destroy
-    return

   undo: ->
     Alchemy.ImageCropper.api.setSelect Alchemy.ImageCropper.box
-    return

   reset: ->
     Alchemy.ImageCropper.api.setSelect Alchemy.ImageCropper.default_box
     Alchemy.ImageCropper.crop_from_field.val ""
     Alchemy.ImageCropper.crop_size_field.val ""
-    return

   destroy: ->
-    try
+    if Alchemy.ImageCropper.api
       Alchemy.ImageCropper.api.destroy()
-    finally
-      Alchemy.ImageCropper.initialized = false
-    return
+    Alchemy.ImageCropper.initialized = false
+    true


Fix nanobar updates when there is no nanobar.

--- app/assets/javascripts/application/pods_progress_bar.js.coffee
+++ app/assets/javascripts/application/pods_progress_bar.js.coffee
@@ -2,7 +2,8 @@
 class @PodsProgressBar
   update: (factor) ->
     progress = factor * 100
-    @nanobar.go(progress)
+    if @nanobar
+      @nanobar.go(progress)
   start: ->
     $container = $('.progress-container').empty()
     @nanobar = new Nanobar


Fix scrollTo in results summary links

Fix scrollTo in results summary links

--- app/assets/javascripts/components/search/results.js.jsx.coffee
+++ app/assets/javascripts/components/search/results.js.jsx.coffee
@@ -1,10 +1,16 @@
 SearchResults = React.createClass
-  scrollTo: (event)->
-    console.log(event)
-    window.scrollTo(0,$("activities_bookmark").offsetTop)
+  generateScrollTo: (type)->
+    return (event)->
+      window.scrollTo(0,$("#{type}_bookmark").offsetTop)

   render: ->
-    message =  (@props.results.map (group)-> `<span>{group.pagination.total_items} <GenericLink link={{url: 'javascript:void(0)', onclick: this.scrollTo, text: group.header, className: ''}} /></span>`)
+    message =  @props.results.map (group)=>
+      link = {url: 'javascript:void(0)', onclick: @generateScrollTo(group.type), text: group.header, className: ''}
+      return `(
+        <span>
+          {group.pagination.total_items} <GenericLink link={link} />
+        </span>
+      )`
     if @props.results.length > 1
       for i in [(@props.results.length-1)..1] by -1
         message.splice(i, 0, `<span>, </span>`)


fix drivers/storage test

fix drivers/storage test

--- test/drivers/storage-test.coffee
+++ test/drivers/storage-test.coffee
@@ -1,6 +1,6 @@
 assert = require 'power-assert'
 sinon = require 'sinon'
-Rx = require 'rx'
+{ Observable } = require 'rx'
 { Storage } = require '../../src/utils/storage'
 { makeStorageDriver } = require '../../src/drivers/storage'

@@ -16,10 +16,12 @@
   context 'get initial state', ->
     it 'works', (done) ->
       data = [null]
-      data$ = Rx.Observable.from data
+      data$ = Observable.from data
       driver = makeStorageDriver()
       response$ = driver data$
-      response$.toArray().subscribe ([d1, d2]) =>
+      response$.toArray().subscribe (a) =>
+        assert a.length is 1
+        [d1] = a
         assert.deepEqual d1, {}
         assert @getItem.callCount is 0
         assert @setItem.callCount is 0
@@ -30,10 +32,12 @@
   context 'get initial state (no data)', ->
     it 'works', (done) ->
       data = [null]
-      data$ = Rx.Observable.from data
+      data$ = Observable.from data
       driver = makeStorageDriver()
       response$ = driver data$
-      response$.toArray().subscribe ([d1, d2]) =>
+      response$.toArray().subscribe (a) =>
+        [d1] = a
+        assert a.length is 1
         assert.deepEqual d1, {}
         assert @getItem.callCount is 0
         assert @setItem.callCount is 0
@@ -44,10 +48,12 @@
   context 'get initial state (data exists)', ->
     it 'works', (done) ->
       data = [null]
-      data$ = Rx.Observable.from data
+      data$ = Observable.from data
       driver = makeStorageDriver(baz: 123)
       response$ = driver data$
-      response$.toArray().subscribe ([d1]) =>
+      response$.toArray().subscribe (a) =>
+        [d1] = a
+        assert a.length is 1
         assert.deepEqual d1, baz: 123
         assert @getItem.callCount is 1
         assert.deepEqual @getItem.getCall(0).args, ['baz']
@@ -65,19 +71,13 @@
       ,
         baz: 123
       ]
-      data$ = Rx.Observable.from data
+      data$ = Observable.from data
       driver = makeStorageDriver()
       response$ = driver data$
       response$.toArray().subscribe (a) =>
-        assert a.length is 3
-        [d1, d2, d3] = a
-        assert.deepEqual d1, {}
-        assert.deepEqual d2, foo: 'bar'
-        assert.deepEqual d3, baz: 123
-        assert @getItem.callCount is 0
-        assert @setItem.callCount is 2
-        assert.deepEqual @setItem.getCall(0).args, ['foo', '"bar"']
-        assert.deepEqual @setItem.getCall(1).args, ['baz', '123']
+        [d1] = a
+        assert a.length is 1 # latest only
+        assert.deepEqual d1, baz: 123
         done()
       , done
       null
@@ -89,12 +89,12 @@
       ,
         foo: 'bar'
       ]
-      data$ = Rx.Observable.from data
+      data$ = Observable.from data
       driver = makeStorageDriver()
       response$ = driver data$
       response$.toArray().subscribe (a) =>
+        [d1] = a
         assert a.length is 1
-        [d1] = a
         assert.deepEqual d1, foo: 'bar'
         assert @getItem.callCount is 0
         assert @setItem.callCount is 1


Bug: SamusMotion system was not using updater

--- src/javascript/game/entity/samus2/systems/samus_motion.coffee
+++ src/javascript/game/entity/samus2/systems/samus_motion.coffee
@@ -5,7 +5,7 @@
   update: (comps, input, u) ->
     velocity = comps.get('velocity')
     hitBox = comps.get('hit_box')
-    comps.get('samus').update 'motion', (m) ->
+    u.update comps.get('samus').update 'motion', (m) ->
       if velocity.get('y') < 0
         'jumping'
       else if velocity.get('y') > 0


Store: Added tests for validation and validated set.

--- src/tests/core/test_store.coffee
+++ src/tests/core/test_store.coffee
@@ -8,15 +8,31 @@
   store = null

   beforeEach ->
-    store = new spark.core.Store {},
-      firstName : 'Fatih'
-      lastName  : 'Acet'
-      age       : 27
-      isMarried : yes
+    options =
+      validations :
+        firstName : required: yes, maxLength: 32, minLength: 2, alphabetic: yes
+        lastName  : required: yes, maxLength: 32, minLength: 2, alphabetic: yes
+        age       : numeric : yes
+        email     : email   : yes
+        userId    : length  : 8
+        uniqueKey : equal   : 'unique', notEqual: 'notUnique'
+
+
+    data =
+      firstName   : 'Fatih'
+      lastName    : 'Acet'
+      age         : 27
+      isMarried   : yes
+      email       : 'acetfatih@gmail.com'
+      userId      : '123ab678'
+      uniqueKey   : 'unique'
+
+    store = new spark.core.Store options, data

   it 'should extends spark.core.Object', ->
     expect(store instanceof spark.core.Object).toBeTruthy()
+

   it 'should have default options', ->
     s = new spark.core.Store null, null
@@ -30,6 +46,7 @@

   describe 'get', ->
+

     it 'should return the value of a given key', ->
       expect(store.get('firstName')).toBe 'Fatih'
@@ -45,6 +62,7 @@

   describe 'set', ->

+
     it 'should set a key with value', ->
       store.set 'hasCar', no

@@ -58,14 +76,38 @@
       expect(store.get('isDoctor')).not.toBeDefined()

+    it 'should validate the value', ->
+      passed = 0
+      failed = 0
+
+      store.on 'PropertySet',      -> ++passed
+      store.on 'PropertyRejected', -> ++failed
+
+      store.set 'email', 'acet'
+      expect(failed).toBe 1
+
+      store.set 'firstName', 'Fatih Acet'
+      expect(passed).toBe 1
+
+      store.set 'age', 'acet'
+      expect(failed).toBe 2
+
+      store.set 'userId', '10ak10ak'
+      expect(passed).toBe 2
+
+      store.set 'isMarried', yes
+      expect(passed).toBe 3
+
+
   it 'should remove a key from store', ->
+    keysLength = store.getKeys().length
+
     expect(store.has('age')).toBeTruthy()
-    expect(store.getKeys().length > 3).toBeTruthy()

     store.unset('age')

     expect(store.has('age')).toBeFalsy()
-    expect(store.getKeys().length > 3).toBeFalsy()
+    expect(store.getKeys().length + 1 is keysLength).toBeTruthy()

   it 'should remove all keys from store', ->
@@ -98,3 +140,59 @@
     expect(values.length > 3).toBeTruthy()
     expect(values.indexOf('Fatih') > -1).toBeTruthy()
     expect(values.indexOf('32') is -1).toBeTruthy()
+
+
+  describe 'validate', ->
+
+
+    it 'should return true when the given data is valid', ->
+      expect(store.validate 'firstName', 'acet').toBeTruthy()
+      expect(store.validate 'lastName', 'Fatih').toBeTruthy()
+      expect(store.validate 'email', 'fatih@fatihacet.com').toBeTruthy()
+      expect(store.validate 'age', 29).toBeTruthy()
+      expect(store.validate 'age', '12').toBeTruthy()
+      expect(store.validate 'userId', '911ef212').toBeTruthy()
+      expect(store.validate 'uniqueKey', 'unique').toBeTruthy()
+
+
+    it 'should return false when the given data is invalid', ->
+      expect(store.validate 'firstName', '').toBeFalsy()
+      expect(store.validate 'firstName', 'f').toBeFalsy()
+      expect(store.validate 'firstName', 'f4t1h4c3t').toBeFalsy()
+      expect(store.validate 'firstName', 'thisisverylongfirstnameitshouldnotvalid').toBeFalsy()
+      expect(store.validate 'firstName', 23).toBeFalsy()
+
+      expect(store.validate 'lastName', '').toBeFalsy()
+      expect(store.validate 'lastName', 'a').toBeFalsy()
+      expect(store.validate 'lastName', 'thisisverylonglastnameitshouldnotvalid').toBeFalsy()
+      expect(store.validate 'lastName', 42).toBeFalsy()
+
+      expect(store.validate 'age', '').toBeFalsy()
+      expect(store.validate 'age', {}).toBeFalsy()
+      expect(store.validate 'age', 'fatih').toBeFalsy()
+
+      expect(store.validate 'email', '12').toBeFalsy()
+      expect(store.validate 'email', 'fatih').toBeFalsy()
+      expect(store.validate 'email', 'fatih@fatihacet').toBeFalsy()
+      expect(store.validate 'email', '').toBeFalsy()
+
+      expect(store.validate 'userId', '12').toBeFalsy()
+      expect(store.validate 'userId', 'fatih').toBeFalsy()
+      expect(store.validate 'userId', 123123123123).toBeFalsy()
+      expect(store.validate 'userId', '').toBeFalsy()
+
+      expect(store.validate 'uniqueKey', 'notUnique').toBeFalsy()
+      expect(store.validate 'uniqueKey', 'notValid').toBeFalsy()
+
+
+    it 'should assume the value is valid if there is not validation rule for it', ->
+      expect(store.validate 'notExistKey', 'itdoesnotmatter').toBeTruthy()
+
+
+    it 'should throw error when no validator found', ->
+      error   = new Error 'Validation type foo does not exist.'
+      options = validations: name: foo:  yes
+      data    = name: 'Acetz'
+      store   = new spark.core.Store options, data
+
+      expect( -> store.validate('name', 'fatih')).toThrow error


change public API names

change public API names

--- src/tex-writer-mkts-table.coffee
+++ src/tex-writer-mkts-table.coffee
@@ -108,8 +108,9 @@
     @cellspacing  = ( raw_parts ) -> MKTS_TABLE.cellspacing me, raw_parts.join ''
     @unitwidth    = ( raw_parts ) -> MKTS_TABLE.unitwidth   me, raw_parts.join ''
     @unitheight   = ( raw_parts ) -> MKTS_TABLE.unitheight  me, raw_parts.join ''
-    @cellgrid     = ( raw_parts ) -> MKTS_TABLE.cellgrid    me, raw_parts.join ''
-    @cell         = ( raw_parts ) -> MKTS_TABLE.cell        me, raw_parts.join ''
+    @quadgrid     = ( raw_parts ) -> MKTS_TABLE.quadgrid    me, raw_parts.join ''
+    @cellquads    = ( raw_parts ) -> MKTS_TABLE.cellquads   me, raw_parts.join ''
+    @cellborder   = ( raw_parts ) -> MKTS_TABLE.cellborder  me, raw_parts.join ''
     return @
   #.........................................................................................................
   return [ me, ( f.apply {} ), ]


make sure previous table contents are erased when table is materialized


Break out state updates explicitly when editor content changes

--- src/text-editor-presenter.coffee
+++ src/text-editor-presenter.coffee
@@ -30,7 +30,14 @@
     @emitter.on 'did-update-state', callback

   observeModel: ->
-    @disposables.add @model.onDidChange(@updateState.bind(this))
+    @disposables.add @model.onDidChange =>
+      @updateHeightState()
+      @updateVerticalScrollState()
+      @updateHorizontalScrollState()
+      @updateContentState()
+      @updateLinesState()
+      @updateGutterState()
+      @updateLineNumbersState()
     @disposables.add @model.onDidChangeSoftWrapped(@updateState.bind(this))
     @disposables.add @model.onDidChangeGrammar(@updateContentState.bind(this))
     @disposables.add @model.onDidChangePlaceholderText(@updateContentState.bind(this))


Exclude TVP from test when on TDS 7.2 and lower

--- test/integration/tvp-test.coffee
+++ test/integration/tvp-test.coffee
@@ -4,13 +4,16 @@
 TYPES = require('../../src/tedious').TYPES
 fs = require('fs')

+config = JSON.parse(fs.readFileSync(process.env.HOME + '/.tedious/test-connection.json', 'utf8')).config
+if config.options.tdsVersion < '7_3_A'
+  console.log "TVP is not supported on TDS #{config.options.tdsVersion}."
+  return
+
 TEST_SETUP_1 = 'BEGIN TRY DROP PROCEDURE __tediousTvpTest DROP TYPE TediousTestType END TRY BEGIN CATCH END CATCH'
 TEST_SETUP_2 = 'CREATE TYPE TediousTestType AS TABLE (a bit, b tinyint, c smallint, d int, e bigint, f real, g float, h varchar (100), i nvarchar (100), j datetime);'
 TEST_SETUP_3 = 'CREATE PROCEDURE __tediousTvpTest (@tvp TediousTestType readonly) AS BEGIN select * from @tvp END'

 getConfig = ->
-  config = JSON.parse(fs.readFileSync(process.env.HOME + '/.tedious/test-connection.json', 'utf8')).config
-
   config.options.debug =
     packet: true
     data: true


Debug methods all return strings

Debug methods all return strings

--- src/trix/debug_controller.coffee
+++ src/trix/debug_controller.coffee
@@ -6,39 +6,40 @@
     @element.appendChild(document.createTextNode(@renderDebugOutput()))

   renderDebugOutput: ->
-    output = []
+    strings = []
     for section in ["PositionOrRange", "Selections", "TextRuns"]
       if result = @["render#{section}"].call(this)
-        output.push(result)
-    output.join("\n\n")
+        strings.push(result)
+    strings.join("\n\n")

   renderPositionOrRange: ->
-    positionOrRange = if selectedRange = @textController.getSelectedRange()
+    string = if selectedRange = @textController.getSelectedRange()
       "Selected range: #{JSON.stringify(selectedRange)}"
     else
       position = @textController.getPosition() ? 0
       "Cursor position: #{position}"

-    positionOrRange += " (locked)" if @textController.textView.lockedRange?
-    positionOrRange
+    string += " (locked)" if @textController.textView.lockedRange?
+    string

   renderSelections: ->
     position = @textController.getPosition()
     return unless position?
-    lines = []

     [container, offset] = @textController.textView.findContainerAndOffsetForPosition(position)
-    lines.push("textView#findContainerAndOffsetForPosition:")
-    lines.push(indent(@renderSelection(container, offset)))
+    string = """
+    textView#findContainerAndOffsetForPosition:
+    #{indent(@renderSelection(container, offset))}
+    """

     selection = window.getSelection()
     if selection.rangeCount > 0
       {startContainer, startOffset} = selection.getRangeAt(0)
-      lines.push("")
-      lines.push("window#getSelection:")
-      lines.push(indent(@renderSelection(startContainer, startOffset)))
-
-    lines.join("\n")
+      string += """\n
+      window#getSelection:
+      #{indent(@renderSelection(startContainer, startOffset))}
+      """
+    string

   renderSelection: (container, offset) ->
     position = @textController.textView.findPositionFromContainerAtOffset(container, offset)


Dispatch old and new position on cursor moves

--- lib/cursor.coffee
+++ lib/cursor.coffee
@@ -34,8 +34,9 @@
   getValue: -> @tableEditor.getValueAtScreenPosition(@getPosition())

   setPosition: (position, resetSelection=true) ->
+    oldPosition = @position
     @position = Point.fromObject(position)
-    @cursorMoved(resetSelection)
+    @cursorMoved(oldPosition, resetSelection)

   getRange: ->
     new Range(@position, {
@@ -44,20 +45,23 @@
     })

   moveUp: (delta=1) ->
+    oldPosition = @position.copy()
     newRow = @position.row - delta
     newRow = @tableEditor.getScreenRowCount() - 1 if newRow < 0

     @position.row = newRow
-    @cursorMoved()
+    @cursorMoved(oldPosition)

   moveDown: (delta=1) ->
+    oldPosition = @position.copy()
     newRow = @position.row + delta
     newRow = 0 if newRow >= @tableEditor.getScreenRowCount()

     @position.row = newRow
-    @cursorMoved()
+    @cursorMoved(oldPosition)

   moveLeft: (delta=1) ->
+    oldPosition = @position.copy()
     newColumn = @position.column - delta

     if newColumn < 0
@@ -68,9 +72,10 @@
       @position.row = newRow

     @position.column = newColumn
-    @cursorMoved()
+    @cursorMoved(oldPosition)

   moveRight: (delta=1) ->
+    oldPosition = @position.copy()
     newColumn = @position.column + delta
     if newColumn >= @tableEditor.getScreenColumnCount()
       newColumn = 0
@@ -80,7 +85,7 @@
       @position.row = newRow

     @position.column = newColumn
-    @cursorMoved()
+    @cursorMoved(oldPosition)

   moveToTop: ->
     @moveUp(@position.row)
@@ -95,27 +100,35 @@
     @moveRight(@tableEditor.getScreenColumnCount() - @position.column - 1)

   pageUp: ->
+    oldPosition = @position.copy()
     newRow = @position.row - atom.config.get('table-edit.pageMovesAmount')
     @position.row = Math.max 0, newRow
-    @cursorMoved()
+    @cursorMoved(oldPosition) unless @position.isEqual(oldPosition)

   pageDown: ->
+    oldPosition = @position.copy()
     newRow = @position.row + atom.config.get('table-edit.pageMovesAmount')
     @position.row = Math.min @tableEditor.getLastRowIndex(), newRow
-    @cursorMoved()
+    @cursorMoved(oldPosition) unless @position.isEqual(oldPosition)

   pageLeft: ->
+    oldPosition = @position.copy()
     newColumn = @position.column - atom.config.get('table-edit.pageMovesAmount')
     @position.column = Math.max 0, newColumn
-    @cursorMoved()
+    @cursorMoved(oldPosition) unless @position.isEqual(oldPosition)

   pageRight: ->
+    oldPosition = @position.copy()
     newColumn = @position.column + atom.config.get('table-edit.pageMovesAmount')
     @position.column = Math.min @tableEditor.getLastColumnIndex(), newColumn
-    @cursorMoved()
+    @cursorMoved(oldPosition) unless @position.isEqual(oldPosition)

-  cursorMoved: (resetSelection=true) ->
+  cursorMoved: (oldPosition, resetSelection=true) ->
     @selection.resetRangeOnCursor() if resetSelection
-    eventObject = cursor: this
+    eventObject = {
+      cursor: this
+      newPosition: @position
+      oldPosition
+    }
     @emitter.emit 'did-change-position', eventObject
     @tableEditor.emitter.emit 'did-change-cursor-position', eventObject


Refactor

Refactor

--- lib/entity.coffee
+++ lib/entity.coffee
@@ -21,13 +21,11 @@
     @constructor._checkDependencies()
     event = new Event eventName, attributes
     @applyEvent event, (err) =>
-      if err?
-        callback err
-      else
-        event.aggregateUid = @uid
-        @appliedEvents.push event
-        @domainRepository.add @
-        callback null
+      return callback err if err?
+      event.aggregateUid = @uid
+      @appliedEvents.push event
+      @domainRepository.add @
+      callback null

   applyEvent: (event, callback) ->
     @constructor._checkDependencies()


Remove url from config.

Remove url from config.

url already defaults to this

--- js-library/app/js/publisher_poc/publisher_plugin.user.coffee
+++ js-library/app/js/publisher_poc/publisher_plugin.user.coffee
@@ -15,7 +15,6 @@
   api: "http://localhost:3000"
   lib: "http://localhost:8000/lib/dist"
   srcPath: "/factlink.core.js"
-  url: window.location.href
   minified: ''

 # testserverConfig =


bugfix old parser warning shown on empty file

--- lib/folder.coffee
+++ lib/folder.coffee
@@ -142,6 +142,10 @@
   updateFolder: (parserOutput) ->
     parsed = JSON.parse parserOutput

+    # maybe the file does not have any entries? Ignore it
+    if !parsed.Entries?
+      return
+
     if parsed.Entries not instanceof Array

       # for the first time this happens per session


refactor extractParams

refactor extractParams

--- lib/atom-ternjs-helper.coffee
+++ lib/atom-ternjs-helper.coffee
@@ -157,22 +157,10 @@
         params.push type.substring(start, i)
         start = i + 1
         continue
-      if type[i] is '['
+      if type[i].match(/[{\[\(]/)
         inside++
         continue
-      if type[i] is ']'
-        inside--
-        continue
-      if type[i] is '('
-        inside++
-        continue
-      if type[i] is ')'
-        inside--
-        continue
-      if type[i] is '{'
-        inside++
-        continue
-      if type[i] is '}'
+      if type[i].match(/[}\]\)]/)
         inside--
     params


fixes issue with template name after moving blocks into sub-folders

--- vendor/assets/javascripts/tangram/blocks/headline/headline.js.coffee
+++ vendor/assets/javascripts/tangram/blocks/headline/headline.js.coffee
@@ -3,7 +3,7 @@

   isTangramHeadline: true

-  templateName: 'tangram_blocks_headline_template'
+  templateName: 'tangram_blocks_headline_headline_template'
   classNames: ['headline-block block']
   classNameBindings: ['isEditing']


add handler

add handler

--- lib/generators/webrtc_rails/install/templates/main.js.coffee
+++ lib/generators/webrtc_rails/install/templates/main.js.coffee
@@ -1,4 +1,8 @@
 class @WebRTC
+  onConnected: ->
+  onHangedUp: ->
+  onReconnectingStarted: ->
+
   constructor: (userID, @localOutput, @remoteOutput) ->
     @_startOutput(@localOutput.tagName.toUpperCase() == 'VIDEO')
     @wsRails = new WebSocketRails(location.host + "/websocket?webrtc=true&user_identifier=" + userID)
@@ -8,6 +12,13 @@
         switch event['type']
           when 'call'
             @remoteUserID = event['remoteUserID']
+          when 'hangUp'
+            @onHangedUp()
+            @_hangedUp = true
+            @_sendMessage(JSON.stringify(type: 'hangUpAnswer'))
+            @_stop()
+          when 'hangUpAnswer'
+            @_stop()
           when 'offer'
             @_onOffer(event)
           when 'answer'
@@ -44,10 +55,13 @@
       track.enabled = enabled

   hangUp: ->
-    @_stop()
+    @onHangedUp()
+    @_sendMessage(JSON.stringify(type: 'hangUp'))
+    @_hangedUp = true

   # private

+  _hangedUp: true
   _localStream: null
   _peerConnection: null
   _peerStarted: false
@@ -125,6 +139,15 @@
           candidate: event.candidate.candidate
         )

+    peer.oniceconnectionstatechange = (event) =>
+      switch peer.iceConnectionState
+        when 'disconnected'
+          @onReconnectingStarted()
+        when 'connected', 'completed'
+          if @_hangedUp
+            @onConnected()
+          @_hangedUp = false
+
     peer.addStream(@_localStream)
     peer.addEventListener('addstream', onRemoteStreamAdded, false)
     peer.addEventListener('removestream', onRemoteStreamRemoved, false)


explicitly load video if player ready

explicitly load video if player ready

--- app/assets/javascripts/app/views/tracks/track_video_form.js.coffee
+++ app/assets/javascripts/app/views/tracks/track_video_form.js.coffee
@@ -25,18 +25,19 @@
     @init_player()

   init_player: ->
-    @player ||= new YT.Player('player', {
-      height: '390',
-      width: '640',
-      videoId: @video_code()
-      playerVars: {
-        fs: 0,
-        iv_load_policy: 3,
-        rel: 0
-      }
-    })
-
-    @player.loadVideoById(videoId: @video_code())
+    if @player
+      @player.loadVideoById(videoId: @video_code())
+    else
+      @player = new YT.Player('player', {
+        height: '390',
+        width: '640',
+        videoId: @video_code()
+        playerVars: {
+          fs: 0,
+          iv_load_policy: 3,
+          rel: 0
+        }
+      })

   init_chart: ->
     @$('#chart').highcharts(


7839: Fixed ajax replace

7839: Fixed ajax replace

--- app/assets/javascripts/backbone/views/work/assignments_view.coffee
+++ app/assets/javascripts/backbone/views/work/assignments_view.coffee
@@ -7,4 +7,4 @@
     $.ajax
       url: window.location.href
       cache: false
-      success: (html) => @$(".assignments-main").html(html)
+      success: (html) => @$(".assignments-main").replaceWith(html)


Updated comments and Permission case

Updated comments and Permission case

--- snippets/php.cson
+++ snippets/php.cson
@@ -4,7 +4,7 @@
     'body': """
     /**
      * Singular name for CMS
-     * @return string
+     * @var string
      */
     private static $singular_name = \'${1}\';
     $0
@@ -14,7 +14,7 @@
     'body': """
     /**
      * Plural name for CMS
-     * @return string
+     * @var string
      */
     private static $plural_name = \'${1}\';
     $0
@@ -24,7 +24,7 @@
     'body': """
     /**
      * Database fields
-     * @return array
+     * @var array
      */
     private static \\$db = array(
     \t\'${1}\' => \'${2}\',
@@ -36,7 +36,7 @@
     'body': """
     /**
      * Default sort ordering
-     * @return string
+     * @var string
      */
     private static $default_sort = \'${1} {2:ASC}\';
     $0
@@ -52,7 +52,7 @@
     'body': """
     /**
      * Has_one relationship
-     * @return array
+     * @var array
      */
     private static \\$has_one = array(
     \t\'${1}\' => \'${2}\',
@@ -64,7 +64,7 @@
     'body': """
     /**
      * Has_many relationship
-     * @return array
+     * @var array
      */
     private static \\$has_many = array(
     \t\'${1}\' => \'${2}\',
@@ -76,7 +76,7 @@
     'body': """
     /**
      * Many_many relationship
-     * @return array
+     * @var array
      */
     private static \\$many_many = array(
     \t\'${1}\' => \'${2}\',
@@ -88,7 +88,7 @@
     'body': """
     /**
      * \{@inheritdoc \}
-     * @return array
+     * @var array
      */
     private static \\$many_many_extraFields = array(
     \t\'${1}\' => array(
@@ -102,7 +102,7 @@
     'body': """
     /**
      * Belongs_to relationship
-     * @return array
+     * @var array
      */
     private static \\$belongs_to = array(
     \t\'${1}\' => \'${2}\',
@@ -114,7 +114,7 @@
     'body': """
     /**
      * Belongs_many_many relationship
-     * @return array
+     * @var array
      */
     private static \\$belongs_many_many = array(
     \t\'${1}\' => \'${2}\',
@@ -178,5 +178,5 @@
   'permissionCheck':
     'prefix': 'permissioncheck'
     'body': """
-    permission::check(${1:string|array}, ${2:'any'}, ${3:$member})$0
+    Permission::check(${1:string|array}, ${2:'any'}, ${3:$member})$0
     """


only display alert one time if find conditional logic problem

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -4,6 +4,7 @@

   #scan page and find all objects with conditional logic rules
   findRules: ->
+    problemWithCL = false
     $("[data-rule!=''][data-rule]").each ->
       $ruleContainer = $(this)
       #if $ruleElement.attr('data-rule').length > 0
@@ -22,15 +23,7 @@
           $conditionContainer = $("[data-question-id=" + conditionQuestionId + "]")

         if $conditionContainer.length == 0 || $conditionContainer.length > 1
-          e = new Error("conditional Logic # findRules")
-          e.name = 'FAILED: conditional logic'
-          Honeybadger.notify e, context:
-            type: "FAILED: conditional logic => condition container not found or found more than once"
-            details: window.location.href
-
-          alert("we found a problem with the hide and show logic and we've been notified.")
-          return true
-
+          problemWithCL = true

         # the condition element, which we need to check value for conditional logic
         $conditionElement = $conditionContainer.find(".form-control")
@@ -39,13 +32,7 @@
           $conditionElement = $conditionContainer.find(".form-control-static")

         if $conditionElement.length == 0
-          e = new Error("conditional Logic # findRules")
-          e.name = 'FAILED: conditional logic'
-          Honeybadger.notify e, context:
-            type: "FAILED: conditional logic => condition element not found or found more than once"
-            details: window.location.href
-
-          alert("we found a problem with the hide and show logic and we've been notified.")
+          problemWithCL = true

         # deal with any condition, once we get a hide_questions = false then we dont need to run through the rules
         hideQuestions = self.evaluateCondition(condition.operator, condition.answer, self.getValue($conditionElement))
@@ -76,6 +63,15 @@
           self.checkConditionsAndHideShow(rule.conditions, ancestorId, $ruleContainer, $ruleContainer, inRepeater, matchType)
         else
           self.hideShowQuestions(hideQuestions, ancestorId, $ruleContainer, $ruleContainer, inRepeater)
+
+    if problemWithCL
+      e = new Error("conditional Logic # findRules")
+      e.name = 'FAILED: conditional logic'
+      Honeybadger.notify e, context:
+        type: "FAILED: conditional logic => condition container or element not found or found more than once"
+        details: window.location.href
+
+      alert("We found a problem with the hide and show rules. We've been notified and are looking into it.")
     return

   #bind conditions to question


Implement secondary sorting by minimized/deleted/unpublished. Fix #266

--- lib/groups.coffee
+++ lib/groups.coffee
@@ -293,12 +293,15 @@
           'submessageLastUpdate'
         else
           sort.key
-    options.sort = [[mongosort, if sort.reverse then 'desc' else 'asc']]
+    #options.sort = [[mongosort, if sort.reverse then 'desc' else 'asc']]
     if options.fields
       options.fields[mongosort] = true
       if sort.key == 'subscribe'  ## fields needed for subscribedToMessage
         options.fields.group = true
         options.fields.root = true
+      options.fields.deleted = true
+      options.fields.minimized = true
+      options.fields.published = true
   msgs = Messages.find query, options
   if sort?
     switch sort.key
@@ -309,9 +312,17 @@
       when 'subscribe'
         key = (msg) -> subscribedToMessage msg
       else
-        key = null
+        key = mongosort
+        #key = (msg) -> msg[mongosort]
     if key?
       msgs = msgs.fetch()
       msgs = _.sortBy msgs, key
       msgs.reverse() if sort.reverse
+      msgs = _.sortBy msgs,
+        (msg) ->
+          weight = 0
+          weight += 4 if msg.deleted  ## deleted messages go very bottom
+          weight += 2 if msg.minimized  ## minimized messages go bottom
+          weight -= 1 unless msg.published  ## unpublished messages go top
+          weight
   msgs


kernel: Check message validity

kernel: Check message validity

--- lib/kernel.coffee
+++ lib/kernel.coffee
@@ -198,18 +198,18 @@
     addWatchCallback: (watchCallback) ->
         @watchCallbacks.push(watchCallback)

+
     onShellMessage: (message) ->
         console.log "shell message:", message

-        msg_id = message.parent_header?.msg_id
+        unless @_isValidMessage message
+            return
+
+        msg_id = message.parent_header.msg_id
         if msg_id?
             callback = @executionCallbacks[msg_id]

         unless callback?
-            return
-
-        unless message.content?
-            console.log "onShellMessage: Missing message.content"
             return

         status = message.content.status
@@ -230,7 +230,7 @@
                 stream: 'error'

         else if status is 'ok'
-            msg_type = message.header?.msg_type
+            msg_type = message.header.msg_type

             if msg_type is 'execution_reply'
                 callback
@@ -256,11 +256,10 @@
     onIOMessage: (message) ->
         console.log "IO message:", message

-        unless message.content?
-            console.log "onIOMessage: Missing message.content"
+        unless @_isValidMessage message
             return

-        msg_type = message.header?.msg_type
+        msg_type = message.header.msg_type

         if msg_type is 'error'
             #TODO; produces to much warning & errors, maybe filter?
@@ -275,69 +274,100 @@
                 @watchCallbacks.forEach (watchCallback) ->
                     watchCallback()

-        msg_id = message.parent_header?.msg_id
+        msg_id = message.parent_header.msg_id
         if msg_id?
             callback = @executionCallbacks[msg_id]

-        if callback?
-            resultObject = @getResultObject message
-            if resultObject?
-                callback resultObject
-
-
-    getResultObject: (message) ->
-        msg_type = message.header?.msg_type
+        unless callback?
+            return

         if msg_type == 'pyout' or
            msg_type == 'display_data' or
            msg_type == 'execute_result'
             if message.content.data['text/html']?
-                return {
+                result =
                     data: message.content.data['text/html']
                     type: 'text/html'
                     stream: 'pyout'
-                }
+
             if message.content.data['image/svg+xml']?
-                return {
+                result =
                     data: message.content.data['image/svg+xml']
                     type: 'image/svg+xml'
                     stream: 'pyout'
-                }

             imageKeys = _.filter _.keys(message.content.data), (key) ->
                 return key.startsWith('image')
             imageKey = imageKeys[0]

             if imageKey?
-                return {
+                result =
                     data: message.content.data[imageKey]
                     type: imageKey
                     stream: 'pyout'
-                }
             else
-                return {
+                result =
                     data: message.content.data['text/plain']
                     type: 'text'
                     stream: 'pyout'
-                }
+
         else if msg_type == 'stdout' or
                 message.idents == 'stdout' or
                 message.idents == 'stream.stdout' or
                 message.content.name == 'stdout'
-            return {
+            result =
                 data: message.content.text ? message.content.data
                 type: 'text'
                 stream: 'stdout'
-            }
+
         else if msg_type == 'pyerr' or msg_type == 'error'
             stack = message.content.traceback
             stack = _.map stack, (item) -> item.trim()
             stack = stack.join('\n')
-            return {
+            result =
                 data: stack
                 type: 'text'
                 stream: 'error'
-            }
+
+        if result?
+            callback result
+
+
+    _isValidMessage: (message) ->
+        unless message?
+            console.log "Invalid message: null"
+            return false
+
+        unless message.content?
+            console.log "Invalid message: Missing content"
+            return false
+
+        unless message.parent_header?
+            console.log "Invalid message: Missing parent_header"
+            return false
+
+        unless message.parent_header.msg_id?
+            console.log "Invalid message: Missing parent_header.msg_id"
+            return false
+
+        unless message.parent_header.msg_type?
+            console.log "Invalid message: Missing parent_header.msg_type"
+            return false
+
+        unless message.header?
+            console.log "Invalid message: Missing header"
+            return false
+
+        unless message.header.msg_id?
+            console.log "Invalid message: Missing header.msg_id"
+            return false
+
+        unless message.header.msg_type?
+            console.log "Invalid message: Missing header.msg_type"
+            return false
+
+        return true
+

     destroy: ->
         console.log "sending shutdown"
@@ -365,6 +395,7 @@

         @kernelProcess.kill('SIGKILL')

+
     getGrammarForLanguage: (language) ->
         matchingGrammars = atom.grammars.getGrammars().filter (grammar) ->
             grammar != atom.grammars.nullGrammar and


Documentation side-by-side

Documentation side-by-side

--- src/client.cjsx
+++ src/client.cjsx
@@ -14,20 +14,14 @@

 Home = React.createClass
   render: ->
-

-

-

-

-            <CodeEditor />
-

-

-

-

-

-

-            <Documentation />
-

-

+

+

+

+          <CodeEditor />
+

+

+          <Documentation />
+


fix typo syntax error

fix typo syntax error

--- static/coffee/common/plot.coffee
+++ static/coffee/common/plot.coffee
@@ -69,7 +69,7 @@
       border_right:  options.border_right  ? @mget('border_right')  ? @mget('border')
     })

-    xmapper = new LinearMapper(
+    xmapper = new LinearMapper({
       source_range: Collections('Range1d').create({start: 0, end: 10})
       target_range: @view_state.get('inner_range_horizontal')
     })


enable narrowed search only in incsearch mode

--- lib/motion.coffee
+++ lib/motion.coffee
@@ -879,7 +879,7 @@
       cursor.getBufferPosition()

   getMatchList: (cursor, input) ->
-    if @isMode('visual') and @editorElement.classList.contains('selection-only-mode')
+    if @isMode('visual') and @isIncrementalSearch?() and @editorElement.classList.contains('selection-only-mode')
       scanRanges = @editor.getSelectedBufferRanges()
     else
       scanRanges = []


Rename song local variable.

Rename song local variable.

--- app/client/js/songs/song.coffee
+++ app/client/js/songs/song.coffee
@@ -80,10 +80,10 @@
   ###
   validate: ->
     ['name', 'artist', 'lyrics']
-      .filter (name) => !@[name].trim()
-      .map (name) =>
-        @MSG_PLEASE_FILL_OUT = goog.getMsg 'Please fill out {$name}.', name: name
-        new app.ValidationError name, @MSG_PLEASE_FILL_OUT
+      .filter (prop) => !@[prop].trim()
+      .map (prop) =>
+        @MSG_PLEASE_FILL_OUT = goog.getMsg 'Please fill out {$prop}.', prop: prop
+        new app.ValidationError prop, @MSG_PLEASE_FILL_OUT

   ###*
     Compute urls after set.


Filling out the form to 'chat' and sending it seems to require us to wait for that to complete before starting the next login session, I'm not sure why but this allows the tests to pass.

--- app/coffee/tests/browser.coffee
+++ app/coffee/tests/browser.coffee
@@ -346,7 +346,7 @@
     assertNumGames = (player, players_turn, players_wait) =>
       createLoginSession player
       @assertNumGames test, players_turn, players_wait
-
+
     # player one has two games in which it's her turn, and one other
     assertNumGames ONE_EMAIL, 2, 1
     # player two has no games in which it's her turn, and one other
@@ -376,7 +376,7 @@
     casper.thenClick '#challenge_link', ->
       form_values = 'input[name="opponent"]' : TOUYA_EMAIL
       # The final 'true' argument means that the form is submitted.
-      @fillSelectors 'form', form_values, true
+      @fillSelectors 'form#challenge-form', form_values, true

     # both players load the front page and see links to the same game

@@ -480,6 +480,8 @@
       form_values = 'input[name="comment"]' : MY_CHAT[0]
       # The final 'true' argument means that the form is submitted.
       @fillSelectors 'form#chat-form', form_values, true
+      comments_selector = '.game-chat .chat-comments .chat-comment-black'
+      casper.waitForSelector comments_selector

     createLoginSession TWO_EMAIL
     casper.thenOpen serverUrl, =>
@@ -489,7 +491,7 @@
       # on the game page is a game chat.
       test.assertExists '.game-chat', 'The game chat does exist.'
       comments_selector = '.game-chat .chat-comments .chat-comment-black'
-      comments = test.assertSelectorHasText comments_selector, MY_CHAT[0],
+      test.assertSelectorHasText comments_selector, MY_CHAT[0],
                  "Player one's comment has appeared in the chat area."

       form_values = 'input[name="comment"]' : MY_CHAT[1]


fix: minor fixes

fix: minor fixes

--- app/routes/authenticated.coffee
+++ app/routes/authenticated.coffee
@@ -86,7 +86,7 @@
         store.pushPayload data: data
         if data.type is "files"
           fileId = data.id
-          that.get("notify").info "New scan started Show", htmlContent: true, autoClear: false
+          that.get("notify").info "New scan started Show", htmlContent: true, autoClear: false

       message: (data) ->
         message = data.message


enhance: increase notify timeout

enhance: increase notify timeout

--- app/routes/authenticated.coffee
+++ app/routes/authenticated.coffee
@@ -63,11 +63,11 @@
       message: (data) ->
         message = data.message
         notifyType = data.notifyType
-        that.get("notify").info message if notifyType is ENUMS.NOTIFY.INFO
-        that.get("notify").success message if notifyType is ENUMS.NOTIFY.SUCCESS
-        that.get("notify").warning message if notifyType is ENUMS.NOTIFY.WARNING
-        that.get("notify").alert message if notifyType is ENUMS.NOTIFY.ALERT
-        that.get("notify").error message if notifyType is ENUMS.NOTIFY.ERROR
+        that.get("notify").info message, ENV.notifications if notifyType is ENUMS.NOTIFY.INFO
+        that.get("notify").success message, ENV.notifications if notifyType is ENUMS.NOTIFY.SUCCESS
+        that.get("notify").warning message, ENV.notifications if notifyType is ENUMS.NOTIFY.WARNING
+        that.get("notify").alert message, ENV.notifications if notifyType is ENUMS.NOTIFY.ALERT
+        that.get("notify").error message, ENV.notifications if notifyType is ENUMS.NOTIFY.ERROR

       logout: ->


Set propTypes and add key to ExerciseStep

--- src/task/review.cjsx
+++ src/task/review.cjsx
@@ -7,6 +7,11 @@

 TaskReview = React.createClass
   displayName: 'TaskReview'
+
+  propTypes:
+    moduleUUID:     React.PropTypes.string.isRequired
+    collectionUUID: React.PropTypes.string.isRequired
+
   getInitialState: ->
     @getSteps(@props)

@@ -35,6 +40,7 @@
       completeStepsReview = _.map completeSteps, (step) ->
         <ExerciseStep
           id={step.id}
+          key={step.id}
           pinned={false}
           review='completed'
           focus={false}


アクティビティを読み込むときのアニメーションが動き続けないように修正

アクティビティを読み込むときのアニメーションが動き続けないように修正

--- app/assets/javascripts/v2/base/components/AnnActivities.js.coffee
+++ app/assets/javascripts/v2/base/components/AnnActivities.js.coffee
@@ -40,8 +40,9 @@
         url: "/api/internal/activities"
         data: @requestData()
       .done (data) =>
+        @isLoading = false
         if data.activities.length > 0
-          @isLoading = @isDisabled = false
+          @isDisabled = false
           @activities.push.apply(@activities, data.activities)
         else
           @isDisabled = true


use classnames

use classnames

--- client/component-lab/Notification/NotificationViewContainer.coffee
+++ client/component-lab/Notification/NotificationViewContainer.coffee
@@ -65,7 +65,7 @@
     then ''
     else 'hidden'

-    className = generateClassName [
+    className = classnames [
       styles.kd_notification_list
       stateClass
     ]


some cleanup

some cleanup

--- src/typeahead.coffee
+++ src/typeahead.coffee
@@ -1,4 +1,4 @@
-angular.module("typeahead", []).directive "typeahead", ["$timeout", "$compile", ($timeout, $compile) ->
+angular.module("typeahead", []).directive "typeahead", ["$timeout", ($timeout) ->

 	template: """

<input ng-model="term" type="text" autocomplete="off" />

@@ -7,8 +7,6 @@
 	transclude: true
 	restrict: "E"
 	compile: (element, attributes) ->
-		# copy the directive's attributes into the template's input element
-		templateAttrs = []
 		# $el used to be called $input but:
 		# http://walpurgisriot.github.io/blog/2013/12/16/the-worst-thing-about-coffeescript
 		$el = element.find "input"


also call actions onOpen/onCollapse

also call actions onOpen/onCollapse

--- addon/components/bs-panel/component.coffee
+++ addon/components/bs-panel/component.coffee
@@ -17,6 +17,10 @@

     toggleCollapsed: () ->
       @attrs.onToggleCollapse?(@get('open'))
+      if @get('open')
+        @attrs.onCollapse?()
+      else
+        @attrs.onOpen?()
       @toggleProperty('open')
       return
   }


Only add to parentNode if it's set.

Only add to parentNode if it's set.

--- src/view/view.coffee
+++ src/view/view.coffee
@@ -92,7 +92,7 @@
     parentNode = Batman.DOM.querySelector(superviewNode, parentNode) if typeof parentNode is 'string'
     parentNode = superviewNode if not parentNode

-    @addToParentNode(parentNode)
+    @addToParentNode(parentNode) if parentNode

   addToParentNode: (parentNode) ->
     return if not @get('node')


Move observePaneItems and onDidChangePaneItem to essential

--- src/workspace.coffee
+++ src/workspace.coffee
@@ -103,6 +103,25 @@
     callback(textEditor) for textEditor in @getTextEditors()
     @onDidAddTextEditor ({textEditor}) -> callback(textEditor)

+  # Essential: Invoke the given callback with all current and future panes items in
+  # the workspace.
+  #
+  # * `callback` {Function} to be called with current and future pane items.
+  #   * `item` An item that is present in {::getPaneItems} at the time of
+  #      subscription or that is added at some later time.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  observePaneItems: (callback) -> @paneContainer.observePaneItems(callback)
+
+  # Essential: Invoke the given callback when the active pane item changes.
+  #
+  # * `callback` {Function} to be called when the active pane item changes.
+  #   * `event` {Object} with the following keys:
+  #     * `activeItem` The active pane item.
+  #
+  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
+  onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback)
+
   # Essential: Invoke the given callback whenever an item is opened. Unlike
   # {::onDidAddPaneItem}, observers will be notified for items that are already
   # present in the workspace when they are reopened.
@@ -166,25 +185,6 @@
   #
   # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
   onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback)
-
-  # Extended: Invoke the given callback when the active pane item changes.
-  #
-  # * `callback` {Function} to be called when the active pane item changes.
-  #   * `event` {Object} with the following keys:
-  #     * `activeItem` The active pane item.
-  #
-  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
-  onDidChangeActivePaneItem: (callback) -> @paneContainer.onDidChangeActivePaneItem(callback)
-
-  # Extended: Invoke the given callback with all current and future panes items in
-  # the workspace.
-  #
-  # * `callback` {Function} to be called with current and future pane items.
-  #   * `item` An item that is present in {::getPaneItems} at the time of
-  #      subscription or that is added at some later time.
-  #
-  # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
-  observePaneItems: (callback) -> @paneContainer.observePaneItems(callback)

   # Extended: Invoke the given callback when a text editor is added to the
   # workspace.


Rename var

Rename var

--- static/script.coffee
+++ static/script.coffee
@@ -1,10 +1,9 @@
 define ['jquery', 'semantic', 'utils', 'charts'], ($, semantic, utils, charts) ->

-  city = utils.getCookie('city')
+  currentCity = utils.getCookie('city')
   availableCity = ['Guangzhou', 'Beijing', 'Shanghai', 'Chengdu', 'Shenyang']
-  if city not in availableCity
-    city = 'Guangzhou'
-  charts.renderChart city
+  currentCity = 'Guangzhou' if currentCity not in availableCity
+  charts.renderChart currentCity

   # listen city click
   for city in availableCity


Do two passes better

Do two passes better

--- services/track-changes/app/coffee/ConcatManager.coffee
+++ services/track-changes/app/coffee/ConcatManager.coffee
@@ -19,7 +19,7 @@
 		for update in rawUpdates
 			lastCompressedUpdate = firstPass.pop()
 			if lastCompressedUpdate?
-				firstPass = firstPass.concat ConcatManager._concatTwoUpdatesOfTheSameType lastCompressedUpdate, update
+				firstPass = firstPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, false
 			else
 				firstPass.push rawUpdate

@@ -28,7 +28,7 @@
 		for update in firstPass
 			lastCompressedUpdate = secondPass.pop()
 			if lastCompressedUpdate?
-				secondPass = secondPass.concat ConcatManager._cancelOppositeInsertsAndDeletes lastCompressedUpdate, update
+				secondPass = secondPass.concat ConcatManager._concatTwoUpdates lastCompressedUpdate, update, true
 			else
 				secondPass.push update

@@ -36,7 +36,7 @@

 	MAX_TIME_BETWEEN_UPDATES: oneMinute = 60 * 1000

-	_concatTwoUpdatesOfTheSameType: (firstUpdate, secondUpdate) ->
+	_concatTwoUpdates: (firstUpdate, secondUpdate, mergeInsertsAndDeletes) ->
 		firstUpdate =
 			op: firstUpdate.op
 			meta:
@@ -82,34 +82,8 @@
 					d: strInject(secondOp.d, firstOp.p - secondOp.p, firstOp.d)
 				]
 			]
-		else
-			return [firstUpdate, secondUpdate]
-
-	_cancelOppositeInsertsAndDeletes: (firstUpdate, secondUpdate) ->
-		firstUpdate =
-			op: firstUpdate.op
-			meta:
-				user_id:  firstUpdate.meta.user_id or null
-				start_ts: firstUpdate.meta.start_ts or firstUpdate.meta.ts
-				end_ts:   firstUpdate.meta.end_ts   or firstUpdate.meta.ts
-		secondUpdate =
-			op: secondUpdate.op
-			meta:
-				user_id:  secondUpdate.meta.user_id or null
-				start_ts: secondUpdate.meta.start_ts or secondUpdate.meta.ts
-				end_ts:   secondUpdate.meta.end_ts   or secondUpdate.meta.ts
-
-		if firstUpdate.meta.user_id != secondUpdate.meta.user_id
-			return [firstUpdate, secondUpdate]
-
-		if secondUpdate.meta.start_ts - firstUpdate.meta.end_ts > ConcatManager.MAX_TIME_BETWEEN_UPDATES
-			return [firstUpdate, secondUpdate]
-
-		firstOp = firstUpdate.op[0]
-		secondOp = secondUpdate.op[0]
-
 		# An insert and then a delete
-		if firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length)
+		if mergeInsertsAndDeletes and firstOp.i? and secondOp.d? and firstOp.p <= secondOp.p <= (firstOp.p + firstOp.i.length)
 			offset = secondOp.p - firstOp.p
 			insertedText = firstOp.i.slice(offset, offset + secondOp.d.length)
 			if insertedText == secondOp.d
@@ -128,7 +102,7 @@
 			else
 				# This shouldn't be possible!
 				return [firstUpdate, secondUpdate]
-		else if firstOp.d? and secondOp.i? and firstOp.p  == secondOp.p
+		else if mergeInsertsAndDeletes and firstOp.d? and secondOp.i? and firstOp.p  == secondOp.p
 			offset = firstOp.d.indexOf(secondOp.i)
 			if offset == -1
 				return [firstUpdate, secondUpdate]


Respond to text box change event so that when manual entry is done, the value updated

--- app/assets/javascripts/dashboard.js.coffee
+++ app/assets/javascripts/dashboard.js.coffee
@@ -57,7 +57,7 @@
     $('#btnExportReport').click(Dashboard.exportReport);
     $('#btnExportReportSingle').click(Dashboard.doReportExport);
     $('#btnMeasurementPeriodChange').click(Dashboard.changeMeasurePeriod);
-    $('#measurementPeriodEndDate').calendricalDate({usa: true, changed: Dashboard.updatePeriodEnd});
+    $('#measurementPeriodEndDate').calendricalDate({usa: true, changed: Dashboard.updatePeriodEnd}).change(Dashboard.updatePeriodEnd);
     Page.onFilterChange = (li) ->
       Dashboard.calculateSelected()
     Dashboard.calculateSelected()


Corrected the duplicate test.

Corrected the duplicate test.

--- test/integration/dataset.coffee
+++ test/integration/dataset.coffee
@@ -56,7 +56,7 @@
         @dropdownText.should.include 'Download as spreadsheet'

       it '...the said spreadsheet download tool, only once', ->
-        @dropdownText.match(/Download as spreadsheet/).length.should.equal 1
+        @dropdownText.match(/Download as spreadsheet/g).length.should.equal 1

       it '...the a button to pick more tools', ->
         @dropdownText.toLowerCase().should.include 'more tools'


BF-178 Bug Fix in Flycam

BF-178 Bug Fix in Flycam

--- app/assets/javascripts/libs/flycam2.coffee
+++ app/assets/javascripts/libs/flycam2.coffee
@@ -58,13 +58,6 @@
   getPlaneScalingFactor : (planeID) ->
     Math.pow(2, @zoomSteps[planeID])

-  # Is this ever needed?
-  #getZoomSteps : ->
-  #  @zoomSteps
-
-  #getMatrix : ->
-  #  M4x4.clone @currentMatrix
-
   getDirection : ->
     @direction

@@ -111,21 +104,13 @@

   needsUpdate : (planeID) ->
     area = @getArea planeID
-    #(area[0] < 0 or area[2] < 0 or area[1] > TEXTURE_WIDTH or area[3] > TEXTURE_WIDTH or
+    ind  = @getIndices planeID
     (area[0] < 0 or area[1] < 0 or area[2] > TEXTURE_WIDTH or area[3] > TEXTURE_WIDTH or
+      @globalPosition[ind[2]] != @texturePosition[ind[3]] or
       (@integerZoomSteps[planeID] - @zoomSteps[planeID]) > 0.75)
-    #ind = @getIndices planeID
-    #f   = @getPlaneScalingFactor planeID
-    #( (Math.abs(@globalPosition[ind[0]]-@texturePosition[planeID][ind[0]]))/f>@buffer[planeID] or
-    #  (Math.abs(@globalPosition[ind[1]]-@texturePosition[planeID][ind[1]]))/f>@buffer[planeID] or
-    #  @globalPosition[ind[2]]!=@texturePosition[planeID][ind[2]] ) or
-    #  (((@zoomSteps[planeID]*100) % 100 >= 25) and (@integerZoomSteps[planeID] < @zoomSteps[planeID]) ) or
-    #  @newBuckets[planeID]

   getOffsets : (planeID) ->
     ind = @getIndices planeID
-    #if @needsUpdate planeID                       # because currently, getOffsets is called befor notifyNewTexture
-    #  @notifyNewTexture planeID
     [ (@globalPosition[ind[0]] - @texturePosition[planeID][ind[0]])/Math.pow(2, @integerZoomSteps[planeID]) + @buffer[planeID],
       (@globalPosition[ind[1]] - @texturePosition[planeID][ind[1]])/Math.pow(2, @integerZoomSteps[planeID]) + @buffer[planeID]]


Remove unused code.

Remove unused code.

--- app/assets/javascripts/web/_site.js.coffee
+++ app/assets/javascripts/web/_site.js.coffee
@@ -716,7 +716,6 @@
     placehold = element.children[0]

     image.onload = ->
-      element.className = element.className.replace('is-loading', 'is-loaded')
       element.replaceChild(image, placehold)

     image.onerror = ->


Turn off instagram embeds.

Turn off instagram embeds.

--- app/assets/javascripts/web/_site.js.coffee
+++ app/assets/javascripts/web/_site.js.coffee
@@ -871,12 +871,13 @@
     feedbin.embed(items, feedbin.data.twitter_embed_path, urlFinder)

   formatInstagram: (target = "[data-behavior~=entry_content_wrap]") ->
-    items = $('blockquote.instagram-media', target)
-
-    urlFinder = (item) ->
-      item.data("instgrmPermalink") || $("a", item).last().attr("href")
-
-    feedbin.embed(items, feedbin.data.instagram_embed_path, urlFinder)
+    true
+    # items = $('blockquote.instagram-media', target)
+    #
+    # urlFinder = (item) ->
+    #   item.data("instgrmPermalink") || $("a", item).last().attr("href")
+    #
+    # feedbin.embed(items, feedbin.data.instagram_embed_path, urlFinder)

   checkType: ->
     element = $('.entry-final-content .content-option')


Check error before checking length

Check error before checking length

Closes #5

--- src/cson.coffee
+++ src/cson.coffee
@@ -72,8 +72,8 @@

   readFile: (objectPath, callback) ->
     fs.readFile objectPath, 'utf8', (error, contents) =>
+      return callback?(error) if error?
       return callback?(null, null) if contents.trim().length is 0
-      return callback?(error) if error?

       if csonCache and path.extname(objectPath) is '.cson'
         cachePath = getCachePath(contents)


Repository: Fix indentation, replace Promise.all with Promise.resolve

--- src/repository/repository.coffee
+++ src/repository/repository.coffee
@@ -48,31 +48,31 @@
       callback null, domainEvents

-  create: (params) =>  new Promise (resolve, reject) =>
-    aggregate = new @_eventric.Aggregate @_context, @_eventric, @_aggregateName, @_AggregateRoot
-    aggregate.id = @_eventric.generateUid()
+  create: (params) =>
+    new Promise (resolve, reject) =>
+      aggregate = new @_eventric.Aggregate @_context, @_eventric, @_aggregateName, @_AggregateRoot
+      aggregate.id = @_eventric.generateUid()

-    if typeof aggregate.root.create isnt 'function'
-      err = "No create function on aggregate"
-      @_eventric.log.error err
-      reject new Error err
+      if typeof aggregate.root.create isnt 'function'
+        err = "No create function on aggregate"
+        @_eventric.log.error err
+        reject new Error err

-    aggregate.root.$id = aggregate.id
-    aggregate.root.$save = =>
-      @save aggregate.id
+      aggregate.root.$id = aggregate.id
+      aggregate.root.$save = =>
+        @save aggregate.id

-    # TODO: needs refactoring
-    commandId = @_command.id ? 'nocommand'
-    @_aggregateInstances[commandId] ?= {}
-    @_aggregateInstances[commandId][aggregate.id] = aggregate
+      # TODO: needs refactoring
+      commandId = @_command.id ? 'nocommand'
+      @_aggregateInstances[commandId] ?= {}
+      @_aggregateInstances[commandId][aggregate.id] = aggregate

-    createAggregate = aggregate.root.create params
+      createAggregate = aggregate.root.create params

-    Promise.all [createAggregate]
-    .then ->
-      resolve aggregate.root
-    .catch ([error]) ->
-      reject error
+      Promise.resolve createAggregate
+      .then ->
+        resolve aggregate.root
+      .catch reject

   save: (aggregateId) =>


:art: refactoring further

:art: refactoring further

--- lib/move-selected-text.coffee
+++ lib/move-selected-text.coffee
@@ -1,6 +1,44 @@
 _ = require 'underscore-plus'
 {CompositeDisposable} = require 'atom'
 {inspect} = require 'util'
+
+# Moving strategy
+# -------------------------
+# Two wise. correspoinding submode of visual-mode is below
+#  - Linewise: linewise, characterwise(in case some selection is multi-row)
+#  - Characterwise: characterwise(in case all selection is single-row), blockwise
+#
+# Four direction
+#   ['up', 'down', 'right', 'left']
+#
+# Movability
+#  up
+#    - Linewise: can't move upward if row is 0
+#    - Characterwise: can't move upward if top selection is at row 0
+#  down
+#    - Linewise: always movable since last row is automatically extended.
+#    - Characterwise: always movable since last row is automatically extended.
+#  right
+#    - Linewise: always movable since EOL is automatically extended.
+#    - Characterwise: always movable since EOL is automatically extended.
+#  left
+#    - Linewise: always true since indent/outdent command can handle it.
+#    - Characterwise: can't move if start of selection is at column 0
+#
+# Moving method
+#  up
+#    - Linewise: rotate linewise
+#    - Characterwise: swap single-row selection with upper block
+#  down
+#    - Linewise: rotate linewise
+#    - Characterwise: swap single-row selection with lower block
+#  right
+#    - Linewise: indent line
+#    - Characterwise: rotate charater in single-row selection.
+#  left
+#    - Linewise: outdent line
+#    - Characterwise: rotate charater in single-row selection.
+

 requireFrom = (pack, path) ->
   packPath = atom.packages.resolvePackagePath(pack)
@@ -42,18 +80,53 @@
   isOverwrite: ->
     atom.config.get('vim-mode-plus-move-selected-text.overwrite')

+  isLinewise: ->
+    switch @vimState.submode
+      when 'linewise' then true
+      when 'characterwise', 'blockwise'
+        @editor.getSelections().some (selection) ->
+          not swrap(selection).isSingleRow()
+
+  isCharacterwise: ->
+    not @isLinewise()
+
+  isMovable: (selection) ->
+    {start} = selection.getBufferRange()
+    switch @direction
+      when 'down', 'right' then true
+      when 'up' then start.row isnt 0
+      when 'left'
+        if @isLinewise() then true else start.column isnt 0
+
   execute: ->
     @withUndoJoin =>
       selections = @editor.getSelectionsOrderedByBufferPosition()
-      topSelection = selections[0]
+      return if @isCharacterwise() and (not @isMovable(selections[0]))
       selections.reverse() if @direction is 'down'
       @editor.transact =>
         @countTimes =>
-          return if (not @isLinewise()) and (not @isMovable(topSelection))
-          for selection in selections
-            @mutate(selection) if @isMovable(selection)
-
-  getOverwrittenBySelection: ->
+          @mutateSelections(selections)
+
+  mutateSelections: (selections) ->
+    for selection in selections when @isMovable(selection)
+      switch @direction
+        when 'down' # auto insert new linew at last row
+          endRow = selection.getBufferRange().end.row
+          if endRow >= getVimLastBufferRow(@editor)
+            eof = @editor.getEofBufferPosition()
+            @editor.setTextInBufferRange([eof, eof], "\n")
+        when 'right' # automatically append space at EOL
+          if @isCharacterwise()
+            eol = selection.getBufferRange().end
+            if pointIsAtEndOfLine(@editor, eol)
+              @editor.setTextInBufferRange([eol, eol], " ")
+
+      if @isLinewise()
+        @moveLinewise(selection)
+      else
+        @moveCharacterwise(selection)
+
+  getInitialOverwrittenBySelection: ->
     overwrittenBySelection = new Map
     isLinewise = @isLinewise()
     @editor.getSelections().forEach (selection) ->
@@ -73,7 +146,7 @@
       state.overwrittenBySelection = null

     unless state.overwrittenBySelection?
-      state.overwrittenBySelection = @getOverwrittenBySelection()
+      state.overwrittenBySelection = @getInitialOverwrittenBySelection()

     fn()

@@ -81,50 +154,34 @@
     if isSequential
       @editor.groupChangesSinceCheckpoint(state.checkpoint)

-  getOverwrittenForSelection: (selection, replacedText) ->
+  getOverwrittenForSelection: (selection, disappearing) ->
     {overwrittenBySelection} = stateByEditor.get(@editor)
     overwrittenArea = overwrittenBySelection.get(selection)

+    # up and down is both for characterwise and linewise
+    # left and right is only for characterwise
     overwritten = switch @direction
       when 'up'
-        [overwritten, rest...] = overwrittenArea.split("\n")
-        overwrittenArea = [rest..., replacedText].join("\n")
-        overwritten
+        # console.log 'called! up', inspect(overwrittenArea)
+        [appearing, covered...] = overwrittenArea.split("\n")
+        overwrittenArea = [covered..., disappearing].join("\n")
+        appearing
       when 'down'
-        [rest..., overwritten] = overwrittenArea.split("\n")
-        overwrittenArea = [replacedText, rest...].join("\n")
-        overwritten
+        # console.log 'called! down', inspect(overwrittenArea)
+        [covered..., appearing] = overwrittenArea.split("\n")
+        overwrittenArea = [disappearing, covered...].join("\n")
+        appearing
       when 'left'
-        [rest..., overwritten] = overwrittenArea
-        overwrittenArea = [replacedText, rest...].join("")
-        overwritten
+        [covered..., appearing] = overwrittenArea
+        overwrittenArea = [disappearing, covered...].join("")
+        appearing
       when 'right'
-        [overwritten, rest...] = overwrittenArea
-        overwrittenArea = [rest..., replacedText].join("")
-        overwritten
+        [appearing, covered...] = overwrittenArea
+        overwrittenArea = [covered..., disappearing].join("")
+        appearing

     overwrittenBySelection.set(selection, overwrittenArea)
     overwritten
-
-  isLinewise: ->
-    switch @vimState.submode
-      when 'linewise'
-        true
-      when 'characterwise', 'blockwise'
-        @editor.getSelections().some (selection) ->
-          not swrap(selection).isSingleRow()
-
-  isMovable: (selection) ->
-    switch @direction
-      when 'down', 'right'
-        true
-      when 'up'
-        selection.getBufferRange().start.row isnt 0
-      when 'left'
-        if @isLinewise()
-          true
-        else
-          selection.getBufferRange().start.column isnt 0

   rotateTextForSelection: (selection) ->
     reversed = selection.isReversed()
@@ -138,15 +195,17 @@
     range = selection.getBufferRange().translate(translation...)
     selection.setBufferRange(range)

-    newText = switch @direction
-      when 'up', 'down' # rotate row
-        text = swrap(selection).lineTextForBufferRows()
-        rotated = @rotateText(text, selection, @isOverwrite())[0]
-        rotated.join("\n") + "\n"
-      when 'right', 'left' # rotate column
-        text = selection.getText()
-        rotated = @rotateText(text, selection, @isOverwrite())[0]
-        rotated.join('')
+    # up and down is only for linewise
+    # left and right is only for characterwise
+    text = switch @direction
+      when 'up', 'down' then swrap(selection).lineTextForBufferRows()
+      when 'right', 'left' then selection.getText().split('')
+
+    rotated = @rotateText(text, selection, @isOverwrite())
+    newText = if @isLinewise()
+      rotated.join("\n") + "\n"
+    else
+      rotated.join('')

     range = selection.insertText(newText)
     range = range.translate(translation.reverse()...)
@@ -154,38 +213,30 @@

   # Return [rotatedText, overwrittenText]
   rotateText: (text, selection, considerOverwrite=false) ->
+    # up and down is only for linewise
+    # left and right is only for characterwise
     switch @direction
       when 'up', 'left'
         [overwritten, rest...] = text
         overwritten = @getOverwrittenForSelection(selection, overwritten) if considerOverwrite
-        [[rest..., overwritten], overwritten]
+        [rest..., overwritten]
       when 'down', 'right'
         [rest..., overwritten] = text
         overwritten = @getOverwrittenForSelection(selection, overwritten) if considerOverwrite
-        [[overwritten, rest...], overwritten]
+        [overwritten, rest...]

 class MoveSelectedTextUp extends MoveSelectedText
   direction: 'up'
   flashTarget: false

-  mutate: (selection) ->
-    if @direction is 'down'
-      endRow = selection.getBufferRange().end.row
-      if endRow >= getVimLastBufferRow(@editor)
-        eof = @editor.getEofBufferPosition()
-        @editor.setTextInBufferRange([eof, eof], "\n")
-
-    if @isLinewise()
-      if @vimState.submode is 'linewise'
+  moveLinewise: (selection) ->
+    if @vimState.submode is 'linewise'
+      @rotateTextForSelection(selection)
+    else
+      swrap(selection).switchToLinewise =>
         @rotateTextForSelection(selection)
-      else
-        swrap(selection).switchToLinewise => @rotateTextForSelection(selection)
-      @editor.scrollToCursorPosition({center: true})
-    else
-      @moveCharacterwise(selection)
-
-  # Characterwise
-  # -------------------------
+    @editor.scrollToCursorPosition({center: true})
+
   moveCharacterwise: (selection) ->
     reversed = selection.isReversed()
     # Pre mutate
@@ -221,20 +272,15 @@
   direction: 'right'
   flashTarget: false

-  mutate: (selection) ->
-    if @isLinewise()
-      switch @direction
-        when 'right' then selection.indentSelectedRows()
-        when 'left' then selection.outdentSelectedRows()
-    else
-      if @direction is 'right'
-        # complement space if EOL
-        point = selection.getBufferRange().end
-        if pointIsAtEndOfLine(@editor, point)
-          @editor.setTextInBufferRange([point, point], " ")
-
-      @rotateTextForSelection(selection)
-      @editor.scrollToCursorPosition({center: true})
+  moveLinewise: (selection) ->
+    # console.log @direction, "HELLO"
+    switch @direction
+      when 'right' then selection.indentSelectedRows()
+      when 'left' then selection.outdentSelectedRows()
+
+  moveCharacterwise: (selection) ->
+    @rotateTextForSelection(selection)
+    @editor.scrollToCursorPosition({center: true})

 class MoveSelectedTextLeft extends MoveSelectedTextRight
   direction: 'left'


Make paths relative including home, fix windows file:/// etc)

--- lib/notification-issue.coffee
+++ lib/notification-issue.coffee
@@ -1,4 +1,4 @@
-fs = require 'fs'
+fs = require 'fs-plus'
 path = require 'path'
 StackTraceParser = require 'stacktrace-parser'

@@ -140,8 +140,16 @@
         """
         resolve(@issueBody)

-  normalizedStackPaths: (stack) ->
-    stack.replace /(^\W+at )([\w.]{2,} [(])?(.*)(:\d+:\d+[)]?)/gm, (m, p1, p2, p3, p4) -> p1 + (p2 || '') + p3.replace(/\\/g, '/').replace(/.*(\/(app\.asar|packages\/).*)/, '$1') + p4
+  normalizedStackPaths: (stack) =>
+    stack.replace /(^\W+at )([\w.]{2,} [(])?(.*)(:\d+:\d+[)]?)/gm, (m, p1, p2, p3, p4) => p1 + (p2 || '') +
+      @normalizePath(p3) + p4
+
+  normalizePath: (path) ->
+    path.replace('file:///', '')                         # Randomly inserted file url protocols
+        .replace(/[/]/g, '\\')                           # Temp switch for Windows home matching
+        .replace(fs.getHomeDirectory(), '~')             # Remove users home dir for apm-dev'ed packages
+        .replace(/\\/g, '/')                             # Switch \ back to / for everyone
+        .replace(/.*(\/(app\.asar|packages\/).*)/, '$1') # Remove everything before app.asar or pacakges

   getRepoUrl: ->
     packageName = @getPackageName()


quote fields when inserting values

quote fields when inserting values

--- src/smc-hub/postgres-base.coffee
+++ src/smc-hub/postgres-base.coffee
@@ -335,7 +335,7 @@
                 values = [values]  # just one

             if values.length > 0
-                opts.query += " (#{fields.join(',')}) VALUES " + (" (#{value.join(',')}) " for value in values).join(',')
+                opts.query += " (#{(quote_field(field) for field in fields).join(',')}) VALUES " + (" (#{value.join(',')}) " for value in values).join(',')

         if opts.set?
             v = []


clean up

clean up

--- src/smc-webapp/landing_page.cjsx
+++ src/smc-webapp/landing_page.cjsx
@@ -454,7 +454,7 @@
         heading : 'LaTeX Editor'
         text : 'Write beautiful documents using LaTeX.'

-SMC_Commercial = () ->
+SMC_Commercial = ->

+
+SMC_Quote = ->
+

+
+
+
+
"SageMathCloud provides a user-friendly interface. Students don’t need to install any software at all.
+        They just open up a web browser and go to cloud.sagemath.com and that’s it. They just type code directly
+        in, hit shift+enter and it runs, and they can see if it works. It provides immediate feedback. The course
+        management features work really well."

+
Other quotes…

+

 LandingPageContent = rclass
     displayName : 'LandingPageContent'
@@ -644,14 +656,7 @@
                         <Well style={'textAlign': 'center', 'float':'right'}>
                             <SMC_Commercial />

-
-
-
-
"SageMathCloud provides a user friendly interface. Students don’t need to install any software at all.
-                            They just open up a web browser and go to cloud.sagemath.com and that’s it. They just type code directly
-                            in, hit shift+enter and it runs and they can see if it works. It provides immediate feedback. The course
-                            management features work really well."

-
Read other quotes.

+                            <SMC_Quote />
                         </Well>
                     </Col>
                     <Col sm=5>


added hourly events to week view

added hourly events to week view

--- drm-calendar.coffee
+++ drm-calendar.coffee
@@ -572,8 +572,13 @@
             calendarInner = @calendar.find "div.#{@calendarInnerClass}"
             if @view is 'month'
                 calendarItem = calendarInner.find "td[data-date=#{dates}]"
-            else if @view is 'week'
+            else if @view is 'week' and (events.allDayEvent is true or !events.time?)
                 calendarItem = calendarInner.find "td[data-date=#{dates}][data-hour='All Day Event']"
+            else if @view is 'week' and events.allDayEvent is false
+                # find hour td element
+                re = new RegExp '[0:]','gi'
+                hour = events.time.replace re, ''
+                calendarItem = calendarInner.find "td[data-date=#{dates}][data-hour=#{hour}]"
             eventList = calendarItem.find "ul.#{@eventClass}"
             length = eventList.length

@@ -1262,8 +1267,8 @@
         name: "One Time Event"
         month: "May"
         year: 2014
-        time: '1:00'
-        eventDate: 2
+        time: '1:00pm'
+        eventDate: 4
         type: "test"
         recurrance: "none"
         allDayEvent: false


Add a `getModel` method to editors' hidden input component

...to make it easy to access the editor in response to DOM events or
when using document.activeElement.

Signed-off-by: Nathan Sobo <2e8aa918660411855c6d44d5bb2da677aa033255@github.com>

--- src/text-editor-component.coffee
+++ src/text-editor-component.coffee
@@ -72,6 +72,10 @@

     @hiddenInputComponent = new InputComponent
     @scrollViewNode.appendChild(@hiddenInputComponent.getDomNode())
+    # Add a getModel method to the hidden input component to make it easy to
+    # access the editor in response to DOM events or when using
+    # document.activeElement.
+    @hiddenInputComponent.getDomNode().getModel = => @editor

     @linesComponent = new LinesComponent({@presenter, @domElementPool, @assert, @grammars, @views})
     @scrollViewNode.appendChild(@linesComponent.getDomNode())


making requirements valid as both with "." or "/" (core.views.* evaluates the same that core/views/*)

--- src/toaster/grains/script.coffee
+++ src/toaster/grains/script.coffee
@@ -244,7 +244,7 @@
 				# otherwise if no class is found inside the file, prints
 				# a warning message
 				else
-					console.log "#{'WARNING'}.bold No class found, ".red +
+					console.log "#{'WARNING'.bold} No class found, ".red +
 								"skiping file: #{file}".red
 					continue

@@ -281,7 +281,7 @@

 					dependencies:  dependencies
 				}
-
+
 			# finally executes callback, passing the collected buffer
 			cb buffer

@@ -305,10 +305,10 @@
 					_dead_indexes.push index

 					# and find all classes under that namespace
-					reg = new RegExp dependency
-					props = ["classname", "classpath"]
-					found = ArrayUtil.find_all buffer, reg, props, true
-
+					reg = new RegExp dependency.replace /(\.|\/)/g, "\$1"
+					props = ["classname", "classpath", "filename", "filefolder"]
+					found = ArrayUtil.find_all buffer, reg, props, true, true
+
 					# overwrites its indexes with only the classpath property
 					found[k] = found[k].item.classpath for v, k in found

@@ -357,7 +357,7 @@
 			for dependency, index in klass.dependencies

 				# properties for searching
-				props = ["classname", "classpath"]
+				props = ["classname", "classpath", "filename", "filefolder"]

 				# if it was already initialized, increments
 				# the index and move forward


Rename locationLoaded -> responseLoaded so as not to imply that a location change and a request happen together

--- src/turbolinks/controller.coffee
+++ src/turbolinks/controller.coffee
@@ -10,7 +10,7 @@
     @view = new Turbolinks.View this
     @cache = new Turbolinks.Cache 10
     @location = Turbolinks.Location.box(window.location)
-    @locationLoaded = true
+    @responseLoaded = true

   start: ->
     unless @started
@@ -40,7 +40,7 @@

   loadResponse: (response) ->
     @view.loadHTML(response)
-    @locationLoaded = true
+    @responseLoaded = true
     @notifyApplicationOfPageChange()

   # Current request
@@ -76,7 +76,7 @@
   # Page snapshots

   saveSnapshot: ->
-    if @locationLoaded
+    if @responseLoaded
       snapshot = @view.saveSnapshot()
       @cache.put(@location, snapshot)

@@ -93,7 +93,7 @@

   locationChangedByActor: (location, actor) ->
     @saveSnapshot()
-    @locationLoaded = false
+    @responseLoaded = false
     @location = location
     @adapter.locationChangedByActor(location, actor)