A little cleaner.

A little cleaner.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -127,10 +127,25 @@

 gulp.task 'main', ->
-  runSequence('mkdir', 'bower_copy', 'atomshell-settings', 'browserify', 'coffee', 'stylus')
+  runSequence(
+    'mkdir',
+    'bower_copy',
+    'atomshell-settings',
+    'browserify',
+    'coffee',
+    'stylus'
+  )

 gulp.task 'dev', ->
-  runSequence('mkdir', 'bower_copy', 'atomshell-settings', 'browserify', 'coffee', 'stylus', 'symlink', 'run-watch')
+  runSequence(
+    'mkdir',
+    'bower_copy',
+    'atomshell-settings',
+    'browserify',
+    'coffee',
+    'stylus',
+    'symlink',
+    'run-watch')

 gulp.task 'setup', ->
   runSequence('bower', 'preen', 'dev')


identation

identation

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -39,7 +39,7 @@
 outdeploy = path.join __dirname, 'dist'

 platformOpts = ['linux', 'darwin', 'win32']
-archOpts =      ['x64','ia32']
+archOpts =     ['x64','ia32']

 deploy_options = {
     dir: path.join __dirname, 'app'


Change forlder for new resized images

Change forlder for new resized images

We change the folders manually to the correct one.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -106,7 +106,7 @@
             width : 1000
             upscale : false
         .pipe(gulp.dest((file) ->
-            file.base.replace('places', 'places-M')
+            file.base.replace('places', 'places-MM')
         )) # Destination in the same folder as source

 # Convert json to


support more cdn providers

support more cdn providers

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -212,7 +212,7 @@
   # TODO currently not that flexible and robust with string replacing
   # TODO currently lib version via CDN is hardcoded

-  unless options.online
+  unless options.cdn?
     return runSequence('noop', cb)

   # 1. index: external libs like lodash, polyfills in index -> jsdelivr
@@ -232,19 +232,21 @@
     .pipe gulp.dest (file) -> file.base

 gulp.task 'cdnize-elements', ->
-  # azure version: https://projectyorucdn.blob.core.windows.net/project-yoru-cdn/
+  switch options.cdn
+    when 'wcdn'
+      cdn_prefix = "https://cdn.wcdn.io/"
+    when 'azure'
+      cdn_prefix = "https://projectyorucdn.blob.core.windows.net/project-yoru-cdn/"
+    when 'polygit'
+      cdn_prefix = "https://polygit.org/components/"

   gulp
     .src paths.elements.internal.pages.compiled
     .pipe $.debug()
     # TODO PreloadJS on jsdelivr is still v0.3, the one on cdnjs is still v0.6.0, while the latest is v0.6.2
     .pipe $.replace "../../bower_components/PreloadJS/lib/preloadjs-0.6.2.combined.js", "https://cdnjs.cloudflare.com/ajax/libs/PreloadJS/0.6.0/preloadjs.min.js"
-    .pipe $.replace "../../bower_components/", "http://cdn.wcdn.io/" # TODO https after wcdn supports https
-    # TODO polygit response with 400 for iron-ajax only
-    # TODO switch back to polygit until this got resolved: https://github.com/PolymerLabs/polygit/issues/6
-    # .pipe $.replace "../../bower_components/", "https://polygit.org/components/"
-    # the same place
-    .pipe gulp.dest (file) -> file.base
+    .pipe $.replace "../../bower_components/", cdn_prefix
+    .pipe gulp.dest (file) -> file.base # the same place

 gulp.task 'bundle-after-build', (cb) ->
   sequence = []
@@ -275,7 +277,7 @@
       .concat paths.app.internal.scripts.folderToCleanup

   # clean up bower dependencies, if they've been bundled, or they will be served via cdn
-  if options.online or options.bundle
+  if options.cdn? or options.bundle?
     src = src.concat paths.dependencies.bower.folderToCleanup

   del src
@@ -335,12 +337,12 @@

 # PARAMS:
 # --bundle: true/false, default to false
-# --online: true/false, default to false
+# --cdn: 'polygit'/'azure'/'wcdn', default to undefined
 # --lint: true/false, default to false

 options =
   bundle: argv.bundle || false
-  online: argv.online || false
+  cdn: argv.cdn
   lint: argv.lint || false

 console.log options


Add LESS to Build and Watch

Add LESS to Build and Watch

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -16,7 +16,7 @@
 LESS_PATH = pathTo 'less'
 COFFEE_PATH = pathTo 'coffee'

-gulp.task 'build', ['jade', 'coffee']
+gulp.task 'build', ['jade', 'less', 'coffee']

 gulp.task 'jade', ->
   gulp.src JADE_PATH
@@ -45,6 +45,7 @@

 gulp.task 'watch', ['build'], ->
   gulp.watch JADE_PATH, ['jade']
+  gulp.watch LESS_PATH, ['less']
   gulp.watch COFFEE_PATH, ['coffee']
   return


remove dead code

remove dead code

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -1,45 +1,17 @@
 {APP_NAME, BOWER_COMPONENTS, LANGUAGES, PROXY_CONFIG, SCRIPTS, STYLES} = require './config.coffee'

-# babel                 = require 'gulp-babel'
 bower                 = require 'bower'
 browserSync           = require 'browser-sync'
 childProcess          = require 'child_process'
-# coffeeScript          = require 'gulp-coffee'
-# coffeeLint            = require 'gulp-coffeelint'
-# concat                = require 'gulp-concat'
 conventionalChangelog = require 'conventional-changelog'
 es                    = require 'event-stream'
-# flatten               = require 'gulp-flatten'
 fs                    = require 'fs'
 gulp                  = require 'gulp'
-# gutil                 = require 'gulp-util'
-# haml                  = require 'gulp-haml'
-# gulpIf                = require 'gulp-if'
-# jade                  = require 'gulp-jade'
-# jsHint                = require 'gulp-jshint'
 karma                 = require 'karma'
-# imagemin              = require 'gulp-imagemin'
-# less                  = require 'gulp-less'
-# liveScript            = require 'gulp-livescript'
-# markdown              = require 'gulp-markdown'
-# minifyCss             = require 'gulp-minify-css'
-# minifyHtml            = require 'gulp-minify-html'
-# newer				  = require 'gulp-newer'
-# ngAnnotate            = require 'gulp-ng-annotate'
-# ngClassify            = require 'gulp-ng-classify'
 path                  = require 'path'
-# plato                 = require 'gulp-plato'
 pkg                   = require './package.json'
 proxy                 = require 'proxy-middleware'
 q                     = require 'q'
-# rev                   = require 'gulp-rev'
-# rimraf                = require 'gulp-rimraf'
-# sass                  = require 'gulp-sass'
-# sourceMaps            = require 'gulp-sourcemaps'
-# template              = require 'gulp-template'
-# templateCache         = require 'gulp-angular-templatecache'
-# typeScript            = require 'gulp-typescript'
-# uglify                = require 'gulp-uglify'
 url                   = require 'url'
 yargs                 = require 'yargs'


made String interpolations more readable by separating business logic from presentation logic

--- source/javascripts/shabbat/vatikin.coffee
+++ source/javascripts/shabbat/vatikin.coffee
@@ -31,13 +31,16 @@
   nishmat = shaharit.nishmatVatikin().format("h:mm")
   hodu = shaharit.hoduVatikin().format("h:mm")
   korbanot = shaharit.korbanotVatikin().format("h:mm")
+  showGregorianYear = (hebrewDate.isRoshHashana() && hebrewDate.is1stDayOfYomTob()) || (0 == momentInstance.month() && momentInstance.date() <= 7)
+  gregorianDate = momentInstance.format("MMMM D#{if showGregorianYear then " YYYY" else ""}")
+  earliestTallit = (new Zmanim(hebrewDate.gregorianDate, window.config)).earliestTallit().format("h:mm:ss")
   """

       #{hebrewDate.dayOfMonth} #{hebrewDate.staticHebrewMonth.name}
-      #{momentInstance.format("MMMM D#{if (hebrewDate.isRoshHashana() && hebrewDate.is1stDayOfYomTob()) || (0 == momentInstance.month() && momentInstance.date() <= 7) then " YYYY" else ""}")}
+      #{gregorianDate}
       #{sedra(hebrewDate)}
       #{korbanot}
-      #{(new Zmanim(hebrewDate.gregorianDate, window.config)).earliestTallit().format("h:mm:ss")}
+      #{earliestTallit}
       #{hodu}
       #{nishmat}
       #{sunrise}


public worksheet: disable split view

public worksheet: disable split view

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -903,6 +903,7 @@
                 if extra_opts.public_access
                     # This is used only for public access to files
                     editor = new CodeMirrorEditor(@, filename, opts.content, extra_opts)
+                    editor.element.find("a[href=#split-view]").hide()  # disable split view for public worksheets
                     if filename_extension_notilde(filename) == 'sagews'
                         editor.syncdoc = new (syncdoc.SynchronizedWorksheet)(editor, {static_viewer:true})
                         editor.once 'show', () =>


Deleted old spec

Deleted old spec

--- spec/javascripts/models/ebook_spec.coffee
+++ spec/javascripts/models/ebook_spec.coffee
@@ -1,231 +0,0 @@
-
-describe "Readium.Models.Ebook", ->
-
-
-	describe "initialization", ->
-
-		beforeEach ->
-			stubFileSystem()
-
-
-
-		describe "without passing a file path", ->
-
-			it "throws an exeption", ->
-				createWithNoPath = -> new Readium.Models.Ebook()
-				expect(createWithNoPath).toThrow("This class cannot be synced without a file path")
-
-
-		describe "with valid params", ->
-
-			it "initializes a paginator", ->
-				ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-				expect(ebook.paginator).toBeDefined()
-
-			it "initializes the package document", ->
-				ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-				expect(ebook.packageDocument).toBeDefined()
-
-			it 'passes a reference to itself to the package document', ->
-				packDoc = new Readium.Models.PackageDocument({book: {}, "file_path": "some/path"})
-				spyOn(Readium.Models, "PackageDocument").andReturn(packDoc)
-				ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-				args = Readium.Models.PackageDocument.mostRecentCall.args
-				expect(args[0].book).toEqual(ebook)
-
-
-			it "calls fetch on the package document", ->
-				packDoc = new Readium.Models.PackageDocument({book: {}, "file_path": "some/path"})
-				spyOn(Readium.Models, "PackageDocument").andReturn(packDoc)
-				spyOn(packDoc, "fetch")
-				ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-				expect(Readium.Models.PackageDocument).toHaveBeenCalled()
-				expect(packDoc.fetch).toHaveBeenCalled()
-
-
-			describe "sets up event handlers", ->
-
-				beforeEach ->
-					@ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-
-				it 'adjustCurrentPage on change:num_pages', ->
-					spyOn(@ebook.adjustCurrentPage, "apply")
-					@ebook.trigger("change:num_pages")
-					expect(@ebook.adjustCurrentPage.apply).toHaveBeenCalled()
-
-				it 'savePosition and setMetaSize on change:num_pages', ->
-					spyOn(@ebook.savePosition, "apply")
-					spyOn(@ebook.setMetaSize, "apply")
-					@ebook.trigger("change:spine_position")
-					expect(@ebook.savePosition.apply).toHaveBeenCalled()
-					expect(@ebook.setMetaSize.apply).toHaveBeenCalled()
-
-
-	describe "defaults", ->
-
-		beforeEach ->
-			@ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-
-		it 'correctly sets default attrs', ->
-			expect(@ebook.get("font_size")).toEqual(10)
-			expect(@ebook.get("current_page")).toEqual([1])
-			expect(@ebook.get("num_pages")).toEqual(0)
-			expect(@ebook.get("two_up")).toEqual(false)
-			expect(@ebook.get("full_screen")).toEqual(false)
-			expect(@ebook.get("toolbar_visible")).toEqual(true)
-			expect(@ebook.get("toc_visible")).toEqual(false)
-			expect(@ebook.get("can_two_up")).toEqual(true)
-			expect(@ebook.get("rendered_spine_items")).toEqual([])
-			expect(@ebook.get("current_theme")).toEqual("default-theme")
-			expect(@ebook.get("current_margin")).toEqual(3)
-
-
-	describe "toJSON", ->
-
-		beforeEach ->
-			@ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-
-		it 'does not serialize attrs that should not be presisted', ->
-			# TODO: add some more
-			@ebook.set("current_page", [1])
-			json = @ebook.toJSON()
-			expect(json.current_page).not.toBeDefined()
-
-		it 'serializes attrs that should be persisted', ->
-			# TODO: add some more
-			json = @ebook.toJSON()
-			expect(json.current_theme).toBeDefined()
-
-
-	describe 'traversing pages', ->
-
-		beforeEach ->
-			@packageDocument = Factory.spy("package_document")
-			spyOn(Readium.Models, "PackageDocument").andReturn(@packageDocument)
-			@ebook = new Readium.Models.Ebook({"package_doc_path": "some/file/path"})
-
-
-		describe 'reflowable section in one up', ->
-
-			beforeEach ->
-				section =
-					isFixedLayout: -> false
-				@ebook.set
-					num_pages: 10
-					current_page: [2]
-					two_up: false
-				spyOn(@ebook, "getCurrentSection").andReturn(section)
-
-
-			describe "nextPage()", ->
-
-				it 'increments the page number if there are more pages', ->
-					@ebook.nextPage()
-					expect(@ebook.get("current_page")).toEqual([3])
-
-				it 'calls goToNextSection if there are no more pages', ->
-					@ebook.set("num_pages", 1)
-					spyOn(@ebook, "goToNextSection")
-					@ebook.nextPage()
-					expect(@ebook.get("current_page")).toEqual([1])
-					expect(@ebook.goToNextSection).toHaveBeenCalled()
-
-
-			describe "prevPage()", ->
-
-				it 'decrements the page number if there are more pages', ->
-					@ebook.prevPage()
-					expect(@ebook.get("current_page")).toEqual([1])
-
-				it 'calls goToPrevSection from page one', ->
-					@ebook.set("current_page", [1])
-					spyOn(@ebook, "goToPrevSection")
-					@ebook.prevPage()
-					expect(@ebook.get("current_page")).toEqual([1])
-					expect(@ebook.goToPrevSection).toHaveBeenCalled()
-
-			describe "toggleTwoUp()", ->
-
-				it 'sets the up_mode member', ->
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("two_up")).toBeTruthy()
-
-				it 'from page #1 sets current page correctly', ->
-					@ebook.set("current_page", [1])
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("current_page")).toEqual([1,2])
-
-				it 'from an even page # sets current page correctly', ->
-					@ebook.set("current_page", [4])
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("current_page")).toEqual([3,4])
-
-				it 'from an odd page # sets current page correctly', ->
-					@ebook.set("current_page", [3])
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("current_page")).toEqual([3,4])
-
-				it 'from an odd last page # sets current page correctly', ->
-					@ebook.set("num_pages", 3)
-					@ebook.set("current_page", [3])
-					@ebook.toggleTwoUp()
-					# we allow for a non-existant page 4, because it will simple be
-					# display as a blank page
-					expect(@ebook.get("current_page")).toEqual([3,4])
-
-
-		describe "reflowable section in two up", ->
-
-			beforeEach ->
-				section =
-					isFixedLayout: -> false
-				@ebook.set
-					num_pages: 10
-					current_page: [3,4]
-					two_up: true
-				spyOn(@ebook, "getCurrentSection").andReturn(section)
-
-
-			describe "nextPage()", ->
-
-				it 'increments both page numbers if there are more pages', ->
-					@ebook.nextPage()
-					expect(@ebook.get("current_page")).toEqual([5,6])
-
-				it 'increments both page numbers if there are more pages', ->
-					@ebook.nextPage()
-					expect(@ebook.get("current_page")).toEqual([5,6])
-
-				it 'calls goToNextSection if there are no more pages', ->
-					@ebook.set("num_pages", 4)
-					spyOn(@ebook, "goToNextSection")
-					@ebook.nextPage()
-					expect(@ebook.goToNextSection).toHaveBeenCalled()
-
-				it 'delegates calculation to ebook.setCurrentPagesForNextPage()', ->
-					spyOn(@ebook, "setCurrentPagesForNextPage")
-					@ebook.nextPage()
-					expect(@ebook.setCurrentPagesForNextPage).toHaveBeenCalledWith(5)
-
-
-			describe "prevPage()", ->
-
-				it 'decrements both page numbers if there are more pages', ->
-					@ebook.prevPage()
-					expect(@ebook.get("current_page")).toEqual([1,2])
-
-				it 'calls goToPrevSection if at the beginning', ->
-					@ebook.set("current_page", [1,2])
-					spyOn(@ebook, "goToPrevSection")
-					@ebook.prevPage()
-					expect(@ebook.goToPrevSection).toHaveBeenCalled()
-
-			describe "toggleTwoUp", ->
-
-				it 'sets the up_mode member', ->
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("two_up")).toBeFalsy()
-
-				it 'shows the lowest page in the array', ->
-					@ebook.toggleTwoUp()
-					expect(@ebook.get("current_page")).toEqual([3])


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -3,10 +3,10 @@
 GameObject's properties.

+
 player = GameObject
   health: 50

-# Health regeneration
 player.cooldown "health",
   target: 100


SharedVM: refactor adding members to shared vm

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -383,18 +383,44 @@
                 callback null, vm

   @addVmUsers = (vm, group, callback)->
-    group.fetchMembers (err, members)->
-      return callback err  if err
-      members.forEach (member)->
-        member.fetchUser (err, user)->
-          return callback err if err
-          return callback new Error "user not found" unless user
-          member.checkPermission group, 'sudoer', (err, hasPermission)->
-            if err then handleError err
-            else
-              vm.update {
-                $addToSet: users: { id: user.getId(), sudo: hasPermission }
-              }, callback
+    # todo - do this operation in batches
+    selector =
+      sourceId    : group.getId()
+      sourceName  : "JGroup"
+      as          : "member"
+
+    # fetch members of the group
+    Relationship.someData selector, {targetId:1}, (err, cursor)->
+      return callback err  if err
+
+      cursor.toArray (err, targetIds)->
+        return callback err  if err
+        targetIds or= []
+
+        # aggregate them into accountIds
+        accountIds = targetIds.map (rec)-> rec.targetId
+
+        selector =
+          targetId   : {$in : accountIds}
+          targetName : "JAccount"
+          as         : 'owner'
+          sourceName : 'JUser'
+
+        # fetch userids of the accounts
+        Relationship.someData selector, {sourceId:1}, (err, cursor)->
+          return callback err  if err
+
+          cursor.toArray (err, sourceIds)->
+            return callback err  if err
+            sourceIds or= []
+            vmUsers = []
+
+            vmUsers = sourceIds.map (rec)->
+              { id: rec.sourceId, sudo: yes }
+
+            return vm.update {
+              $set: users: vmUsers
+            }, callback

   @fetchVmInfo = secure (client, hostnameAlias, callback)->
     {delegate} = client.connection


Added spinner whenn loading documents results

--- app/assets/javascripts/species/routes/documents_route.js.coffee
+++ app/assets/javascripts/species/routes/documents_route.js.coffee
@@ -6,6 +6,7 @@
     if queryParams.geo_entities_ids && queryParams.geo_entities_ids.substring
       queryParams.geo_entities_ids = queryParams.geo_entities_ids.split(',')
     @controllerFor('elibrarySearch').setFilters(queryParams)
+    $(@spinnerSelector).css("visibility", "visible")

   model: (params, queryParams, transition) ->
     controller = @controllerFor('documents')
@@ -17,6 +18,9 @@
       error: (jqXHR, textStatus, errorThrown) ->
         console.log("AJAX Error:" + textStatus)
     )
+
+  afterModel: (queryParams, transition) ->
+    $(@spinnerSelector).css("visibility", "hidden")

   renderTemplate: ->
     # Render the `documents` template into


avoids referencing window so that coffee model tests can pass

--- jsapp/xlform_model_view/view.utils.coffee
+++ jsapp/xlform_model_view/view.utils.coffee
@@ -1,5 +1,6 @@
+rootUrl = @_rootUrl
+
 define 'cs!xlform/view.utils', ['xlform/view.utils.validator'], (Validator)->
-  rootUrl = window._rootUrl

   viewUtils = {}
   viewUtils.Validator = Validator


non capture groups in regex

non capture groups in regex

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -29,7 +29,7 @@
 cssClass = baseName.toLowerCase()
 faceNames = ['front', 'bottom', 'back', 'top', 'left', 'right']
 faceSequence = faceNames[...4]
-urlRx = /^((((https?)|(file)):)?\/\/)|(data:)|((\.\.?)?\/)/i
+urlRx = /^(?:(?:(?:(?:https?)|(?:file)):)?\/\/)|(?:data:)|(?:(?:\.\.?)?\/)/i

 class window.HexaFlip


don't create uuid for new users

don't create uuid for new users

--- modules/userFactory.coffee
+++ modules/userFactory.coffee
@@ -43,7 +43,6 @@
 			vkontakteId: vkProfile.id
 			profile: vkProfile
 			role: 'human'
-			hash: uuid.v4().substr(0, 13)
 			registered: new Date()
 			lastActionDate: new Date()
 		newUser.save (err) ->


top left wtf scale width and height separately (better precision for large images)

--- app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
+++ app/assets/javascripts/uploadcare/ui/crop/crop-widget.js.coffee
@@ -94,14 +94,14 @@
         width: parseInt(raw[1], 10)
         height: parseInt(raw[2], 10)
         center: raw[4] == 'center'
-        top: parseInt(raw[5], 10) or undefined
-        left: parseInt(raw[6], 10) or undefined
+        left: parseInt(raw[5], 10) or undefined
+        top: parseInt(raw[6], 10) or undefined

     croppedImageModifiers: (previewUrl, size, modifiers) ->
       @croppedImageCoords(previewUrl, size, @__parseModifiers modifiers)
         .then (coords) =>
           size = "#{coords.width}x#{coords.height}"
-          topLeft = "#{coords.x},#{coords.y}"
+          topLeft = "#{coords.left},#{coords.top}"

           opts =
             crop: $.extend({}, coords)
@@ -151,11 +151,12 @@

     # Returns last selected area coords
     getCurrentCoords: ->
-      scaleRatio = @__resizedWidth / @__originalWidth
-      fixedCoords = {}
-      for key, value of @__currentCoords
-        fixedCoords[key] = Math.round value / scaleRatio
-      fixedCoords
+      scaleX = @__resizedWidth / @__originalWidth
+      scaleY = @__resizedHeight / @__originalHeight
+      left: Math.round @__currentCoords.left / scaleX
+      top: Math.round @__currentCoords.top / scaleY
+      width: Math.round @__currentCoords.width / scaleX
+      height: Math.round @__currentCoords.height / scaleY

     # Destroys widget completly
     destroy: ->
@@ -228,10 +229,8 @@
           @__currentCoords =
             height: coords.h
             width: coords.w
-            x: coords.x
-            x2: coords.x2
-            y: coords.y
-            y2: coords.y2
+            left: coords.x
+            top: coords.y

       if @__options.preferedSize
         [width, height] = @__options.preferedSize.split 'x'
@@ -249,20 +248,21 @@
           previousCoords.height = @__originalHeight

       if previousCoords.center
-        top = (@__originalWidth - previousCoords.width) / 2
-        left = (@__originalHeight - previousCoords.height) / 2
-      else
+        left = (@__originalWidth - previousCoords.width) / 2
+        top = (@__originalHeight - previousCoords.height) / 2
+      else
+        left = previousCoords.left or 0
         top = previousCoords.top or 0
-        left = previousCoords.left or 0
+
+      scaleX = @__resizedWidth / @__originalWidth
+      scaleY = @__resizedHeight / @__originalHeight
+
       jCropOptions.setSelect = [
-        top
-        left
-        previousCoords.width + top
-        previousCoords.height + left
+        left * scaleX,
+        top * scaleY,
+        (previousCoords.width + left) * scaleX,
+        (previousCoords.height + top) * scaleY,
       ]
-      scaleRatio = @__resizedWidth / @__originalWidth
-      for val, i in jCropOptions.setSelect
-        jCropOptions.setSelect[i] = val * scaleRatio

       @__setState 'loading'
       done = (api) =>


Add Object.assign from react if missing, transferPropsTo -> {...@props}

--- src/ButtonLink.cjsx
+++ src/ButtonLink.cjsx
@@ -1,6 +1,10 @@
 React = require 'react'
 Button = require 'react-bootstrap/Button'
 RouteToMixin = require './RouteToMixin'
+assign = require 'react/lib/Object.assign'
+
+if ! Object.assign
+  Object.assign = assign;

 ADDITIONAL_RESERVED_PROPS =
   key: true
@@ -11,12 +15,11 @@
   mixins: [ RouteToMixin ]
   additionalReservedProps: ADDITIONAL_RESERVED_PROPS
   render: ->
-    @transferPropsTo(
-      <Button
-        href={@getHref()}
-        onClick={@handleRouteTo}>
-        {@props.children}
-      </Button>
-    )
+    <Button
+      {...@props}
+      href={@getHref()}
+      onClick={@handleRouteTo}>
+      {@props.children}
+    </Button>

 module.exports = ButtonLink


fix make case command parsing

fix make case command parsing

--- lib/executable/stream/parse-command.coffee
+++ lib/executable/stream/parse-command.coffee
@@ -40,7 +40,7 @@
                     else @executable.emit 'give-action', tokens[1], tokens[2], false

             when 'agda2-make-case-action'
-                @executable.emit 'make-case-action', content
+                @executable.emit 'make-case-action', tokens[1]

             #
             #   highlighting shit


Added hiding notifications

Added hiding notifications

--- app/assets/javascripts/application.js.coffee
+++ app/assets/javascripts/application.js.coffee
@@ -103,6 +103,9 @@
     window.location.href = new_url
   );

+  $('.notifications a.close').click(->
+    $('div.notifications').addClass('display-none');
+  );
 #  console.log(history_array)

 #  bajb_backdetect.OnBack = ->


make dropbox upload via ajax

make dropbox upload via ajax

--- app/assets/javascripts/file_browse.js.coffee
+++ app/assets/javascripts/file_browse.js.coffee
@@ -1,11 +1,11 @@
 # Copyright 2011-2018, The Trustees of Indiana University and Northwestern
 #   University.  Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
-#
+#
 # You may obtain a copy of the License at
-#
+#
 # http://www.apache.org/licenses/LICENSE-2.0
-#
+#
 # Unless required by applicable law or agreed to in writing, software distributed
 #   under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 #   CONDITIONS OF ANY KIND, either express or implied. See the License for the
@@ -19,6 +19,13 @@
   initialized = false
   $('#browse-btn').browseEverything()
     .show ->
+      alertMsg = $('<div>')
+        .html('Warning! Uploading too many files at once can lead to ingest failures.')
+        .addClass('alert')
+        .addClass('alert-danger')
+        .css('margin','3px')
+        .css('text-align','center')
+      $('.ev-body').prepend(alertMsg)
       unless $('#browse-everything input[name=workflow]').length > 0
         skip_box = $('#web_upload input[name=workflow]').closest('span')
           .clone().removeClass().css('margin-right','10px')
@@ -26,7 +33,8 @@

       $('.ev-providers .ev-container a').click()
       initialized = true
-    .done (data) ->
+    .done (data) ->
       if data.length > 0
+        $('#uploading').modal('show')
         $('#dropbox_form input[name=workflow]').val($('#browse-everything input[name=workflow]:checked').val())
-        $('#dropbox_form').submit()
+        $('#dropbox_form').submit()


spelling correction (detach)

spelling correction (detach)

--- src/art/engine/core/canvas_element.coffee
+++ src/art/engine/core/canvas_element.coffee
@@ -107,7 +107,7 @@
       listener:listener

   # _attach is private and done when the HTMLCanvasElement is set - typically on construction
-  dettach: ->
+  detach: ->
     globalEpochCycle.dettachCanvasElement @
     @_unregister()


Improve conditional

Improve conditional

--- app/assets/javascripts/gl_dropdown.js.coffee
+++ app/assets/javascripts/gl_dropdown.js.coffee
@@ -31,7 +31,7 @@
       .on 'keyup', (e) =>
         keyCode = e.which

-        return if ARROW_KEY_CODES.indexOf(keyCode) >= 0
+        return unless ARROW_KEY_CODES.indexOf(keyCode) is -1

         if @input.val() isnt '' and !$inputContainer.hasClass HAS_VALUE_CLASS
           $inputContainer.addClass HAS_VALUE_CLASS


为DropDown添加超长值的提示框

为DropDown添加超长值的提示框

--- src/widget/edit/dropdown.coffee
+++ src/widget/edit/dropdown.coffee
@@ -140,6 +140,8 @@
 		super()
 		if @_openOnActive and not @_opened and not @_finalReadOnly
 			@open()
+		if @_useValueContent
+			@_showValueTipIfNecessary()
 		return

 	_onBlur: ()->
@@ -149,6 +151,27 @@
 				return
 			, 50)
 		super()
+		@_doms.tipDom and cola.util.cacheDom(@_doms.tipDom)
+		return
+
+	_showValueTipIfNecessary: ()->
+		if @_useValueContent and @_doms.valueContent
+			valueContent = @_doms.valueContent
+			if valueContent.scrollWidth > valueContent.clientWidth
+				tipDom = @_doms.tipDom
+				if not tipDom
+					@_doms.tipDom = tipDom = cola.xCreate({
+						class: "dropdown-value-tip"
+					})
+				if tipDom isnt document.body
+					document.body.appendChild(tipDom)
+
+				tipDom.innerHTML = @_doms.valueContent.innerHTML
+				rect = $fly(@_dom).offset()
+				$fly(tipDom).css(
+					left: rect.left + @_dom.offsetWidth / 2
+					top: rect.top
+				)
 		return

 	_parseDom: (dom)->
@@ -273,6 +296,9 @@

 			if @_useValueContent
 				$fly(@_doms.valueContent).hide()
+
+		if @_focused and @_useValueContent
+			@_showValueTipIfNecessary()
 		return

 	_initValueContent: (valueContent, context)->


Fix multiple drawn features

Fix multiple drawn features

--- app/assets/javascripts/map_display.js.coffee
+++ app/assets/javascripts/map_display.js.coffee
@@ -4,7 +4,6 @@
 class window.LeafletMap
   @CyclestreetsUrl: "https://www.cyclestreets.net"
   @default_marker_anchor: [30 / 2, 42]
-  @drawnItems: new L.FeatureGroup()

   constructor: (center, opts) ->
     @domId = opts.domid || 'map'
@@ -238,7 +237,7 @@

   drawFeatureId: (feature, id) =>
     if @drawnFeatures[id]
-      @constructor.drawnItems.removeLayer @drawnFeatures[id]
+      @drawnItems.removeLayer @drawnFeatures[id]
       @drawnLayerChanged()
     if feature
       feature = L.geoJson(feature).getLayers()[0]
@@ -267,31 +266,30 @@
     @drawFeatureId(circle, "circle")

   drawFeature: (layer) =>
-    @constructor.drawnItems.addLayer layer
+    @drawnItems.addLayer layer
     if @deletePopup
       domelem = document.createElement('a')
       domelem.innerHTML = "#{CONSTANTS.i18n.delete}?</a>"
       domelem.onclick = =>
-        @constructor.drawnItems.removeLayer layer
+        @drawnItems.removeLayer layer
         @drawnLayerChanged()
-      @constructor.drawnItems.bindPopup(domelem)
+      @drawnItems.bindPopup(domelem)
     @drawnLayerChanged()

   drawnLayerChanged: =>
-    @geoInput.changeVal JSON.stringify(@constructor.drawnItems.toGeoJSON())
+    @geoInput.changeVal JSON.stringify(@drawnItems.toGeoJSON())
     $("a[title='Save changes.']")[0].click() if @_editing
     $('.leaflet-draw-edit-edit')[0].click()

   addDraw: (feature) =>
-    drawnItems = @constructor.drawnItems
-
+    @drawnItems = new L.FeatureGroup()
     $('.icon-save').toggle()

-    @map.addLayer drawnItems
+    @map.addLayer @drawnItems
     drawControl = new L.Control.Draw {
       position: 'topright'
       edit: {
-        featureGroup: drawnItems
+        featureGroup: @drawnItems
         edit: { selectedPathOptions: { fillColor: '#00008B', maintainColor: true } }
       }
       draw: {
@@ -325,7 +323,7 @@

     @map.on 'draw:edited draw:editvertex draw:drawvertex draw:editmove draw:deleted', (e) =>
       return unless @_editing
-      @drawFeature(layer) for layer in drawnItems.getLayers()
+      @drawFeature(layer) for layer in @drawnItems.getLayers()

     if feature
       @drawFeature(layer) for layer in L.geoJson(feature).getLayers()
@@ -351,7 +349,7 @@
     $('.edit-clear').click (evt) =>
       evt.preventDefault()
       evt.stopPropagation()
-      drawnItems.clearLayers()
+      @drawnItems.clearLayers()
       $('ul.leaflet-draw-actions a[title^="Cancel"]')[0]?.click()
       $('.tabs').children('li.area, li.route, li.point').css(opacity: 1).prop('disabled', false)
       @geoInput.changeVal(null)
@@ -367,8 +365,8 @@
     $('.edit-undo').click (evt) ->
       evt.preventDefault()
       evt.stopPropagation()
-      if drawnItems.getLayers().slice(-1)[0]?.editing?._enabled
-        drawnItems.removeLayer(drawnItems.getLayers()[-1..][0])
+      if @drawnItems.getLayers().slice(-1)[0]?.editing?._enabled
+        @drawnItems.removeLayer(@drawnItems.getLayers()[-1..][0])
       else
         $('ul.leaflet-draw-actions a[title^="Delete"]')[0].click()


File upload: Beautify progress bar

File upload: Beautify progress bar

--- app/assets/javascripts/modules/upload.coffee
+++ app/assets/javascripts/modules/upload.coffee
@@ -14,10 +14,12 @@
         reader.readAsDataURL file

         # Add Progressbar
+        data.fileinput_button = $(this).closest('.fileinput-button')
+        data.fileinput_button.hide()
         data.progressBar = $('

-

+

-
').insertAfter($(this).closest('.fileinput-button'))
+
').insertAfter(data.fileinput_button)

         # Presign file
         options =
@@ -54,5 +56,6 @@

         # Enable form submit
         $(this).closest('form').find('input[type=submit]').attr('disabled', false)
+        data.fileinput_button.show()

     return


Add Pingu greeting (#79)

Add Pingu greeting (#79)

Noot noot!

--- support/greetings.coffee
+++ support/greetings.coffee
@@ -101,6 +101,7 @@
     [ "Owambo",         "Wa lalapo" ]
     [ "Pashto",         "Sahar de pa Khair" ]
     [ "Persian",        "Subbakhair" ]
+    [ "Pingu",          "Noot noot!" ]
     [ "Pirate",         "Avast, ye scurvy dog" ]
     [ "Polish",         "Witaj" ]
     [ "Polynesian",     "Ia ora na" ]


Update progress bar styles for Bootstrap 3

--- app/assets/javascripts/access_grid.js.coffee
+++ app/assets/javascripts/access_grid.js.coffee
@@ -31,9 +31,9 @@
   #   source: (query, process) ->
   #       $.getJSON($(".autocomplete_query").closest("form").attr("action") + ".json", { query: query }, process);
   # }
-  $("#get_sublibrary_permissions").on 'change', "select", ->
+  $("#get_sublibrary_permissions").on 'change', "select", ->
     $("#permissions_chart").children().hide()
-    $("#permissions_chart").prepend($("<div />").attr({'id': 'permissions_progress'}).addClass("progress progress-striped active").append($("<div />").addClass("bar").css({width: '5%'})));
+    $("#permissions_chart").prepend($("<div />").attr({'id': 'permissions_progress'}).addClass("progress").append($("<div />").addClass("progress-bar progress-bar-striped active").css({width: '5%'})));
     setTimeout( ->
       $("#permissions_progress > div.bar").css({width: "98%"})
     , 0)


remove old code

remove old code

--- app/assets/javascripts/models/user.js.coffee
+++ app/assets/javascripts/models/user.js.coffee
@@ -1,9 +0,0 @@
-(($) ->
-  window.User = Model 'user',
-    @persistence Model.REST, '/users'
-
-    @extend {
-      fetch = @persistence().query
-    }
-
-)(jQuery)


[fix] restore tree_ui.coffee

[fix] restore tree_ui.coffee

--- app/assets/javascripts/ss/lib/tree_ui.coffee
+++ app/assets/javascripts/ss/lib/tree_ui.coffee
@@ -1,63 +1,58 @@
-class @SS_TreeUI
-  @openImagePath  = "/assets/img/tree-open.png"
-  @closeImagePath = "/assets/img/tree-close.png"
-
-  @render: (tree)->
-    root = []
-    $(tree).find("tbody tr").each ->
-      root.push(parseInt($(this).attr("data-depth")))
-    root = Math.min.apply(null, root)
-    return unless Number.isInteger(root) && root > 0
-
-    parent = null
-    $(tree).find("tbody tr").each ->
-      td = $(this).find(".expandable")
-      depth = parseInt($(this).attr("data-depth"))
-
-      td.prepend('')
-      if (parent && depth > parent)
-        $(this).hide()
-        for i in [root...depth]
-          td.prepend('')
-
-      parent = depth unless parent
-      parent = depth if parent > depth
-
-      d = parseInt($(this).next("tr").attr("data-depth")) || 0
-      i = $(this).find(".toggle:first")
-      i.replaceWith('') if (d == 0 || depth >= d)
-
-    $(tree).find(".toggle").on "mousedown mouseup", (e) ->
-      e.stopPropagation()
-      return false
-
-    $(tree).find(".toggle").on "click", (e) ->
-      tr    = $(this).closest("tr")
-      img   = tr.find(".toggle:first")
-      depth = parseInt(tr.attr("data-depth"))
-      SS_TreeUI.toggleImage(img)
-      tr.nextAll("tr").each ->
-        d = parseInt($(this).attr("data-depth"))
-        i = $(this).find(".toggle:first")
-        if (depth >= d)
-          return false
-        if ((depth + 1) == d)
-          $(this).toggle()
-          SS_TreeUI.closeImage(i)
-        else
-          $(this).hide()
-          SS_TreeUI.closeImage(i)
-      e.stopPropagation()
-      return false
-
-  @toggleImage = (img) ->
-    if img.attr("src") == SS_TreeUI.openImagePath
-      SS_TreeUI.closeImage(img)
-    else if img.attr("src") == SS_TreeUI.closeImagePath
-      SS_TreeUI.openImage(img)
-
-  @openImage = (img) ->
-    img.attr("src", SS_TreeUI.openImagePath)
-
-  @closeImage = (img) ->
-    img.attr("src", SS_TreeUI.closeImagePath)
+class @SS_TreeUI
+  @openImagePath  = "/assets/img/tree-open.png"
+  @closeImagePath = "/assets/img/tree-close.png"
+
+  @render: (tree)->
+    root = []
+    $(tree).find("tbody tr").each ->
+      root.push(parseInt($(this).attr("data-depth")))
+    root = Math.min.apply(null, root)
+    return unless Number.isInteger(root) && root > 0
+
+    $(tree).find("tbody tr").each ->
+      td = $(this).find(".expandable")
+      depth = parseInt($(this).attr("data-depth"))
+
+      td.prepend('')
+      $(this).hide() if (depth != root)
+      for i in [root...depth]
+        td.prepend('')
+
+      d = parseInt($(this).next("tr").attr("data-depth")) || 0
+      i = $(this).find(".toggle:first")
+      i.replaceWith('') if (d == 0 || depth >= d)
+
+    $(tree).find(".toggle").on "mousedown mouseup", (e) ->
+      e.stopPropagation()
+      return false
+
+    $(tree).find(".toggle").on "click", (e) ->
+      tr    = $(this).closest("tr")
+      img   = tr.find(".toggle:first")
+      depth = parseInt(tr.attr("data-depth"))
+      SS_TreeUI.toggleImage(img)
+      tr.nextAll("tr").each ->
+        d = parseInt($(this).attr("data-depth"))
+        i = $(this).find(".toggle:first")
+        if (depth >= d)
+          return false
+        if ((depth + 1) == d)
+          $(this).toggle()
+          SS_TreeUI.closeImage(i)
+        else
+          $(this).hide()
+          SS_TreeUI.closeImage(i)
+      e.stopPropagation()
+      return false
+
+  @toggleImage = (img) ->
+    if img.attr("src") == SS_TreeUI.openImagePath
+      SS_TreeUI.closeImage(img)
+    else if img.attr("src") == SS_TreeUI.closeImagePath
+      SS_TreeUI.openImage(img)
+
+  @openImage = (img) ->
+    img.attr("src", SS_TreeUI.openImagePath)
+
+  @closeImage = (img) ->
+    img.attr("src", SS_TreeUI.closeImagePath)


Fix issue #1015: Now show the name on us detail points

--- app/coffee/modules/userstories/detail.coffee
+++ app/coffee/modules/userstories/detail.coffee
@@ -289,9 +289,9 @@
             status = $scope.statusById[us.status]
             rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true)
             _.map rolePoints, (v, k) ->
-                  val = $scope.pointsById[us.points[v.id]].value
-                  val = "?" if not val?
-                  v.points = val
+                  name = $scope.pointsById[us.points[v.id]].name
+                  name = "?" if not name?
+                  v.points = name

             totalTasks = $scope.tasks.length
             totalClosedTasks = _.filter($scope.tasks, (task) => $scope.taskStatusById[task.status].is_closed).length


Renamed addressing modes enum. More instructions.

--- src/CPU.coffee
+++ src/CPU.coffee
@@ -12,15 +12,15 @@
     Accumulator:      2
     Immediate:        3
     ZeroPage:         4
-    IndexedXZeroPage: 5
+    ZeroPageX: 5
     IndexedYZeroPage: 6
     Absolute:         7
-    IndexedXAbsolute: 8
-    IndexedYAbsolute: 9
+    AbsoluteX: 8
+    AbsoluteY: 9
     Relative:         10
     Indirect:         11
-    IndexedXIndirect: 12
-    IndirectIndexedY: 13
+    IndirectX: 12
+    IndirectY: 13

 Instruction =
     ADC:  1
@@ -51,6 +51,10 @@
     INX: 26
     INY: 27
     JMP: 28
+    JSR: 29
+    LDA: 30
+    LDX: 31
+    LDY: 32

 Interrupt =
     IRQ:   1
@@ -246,7 +250,7 @@
         @registerAddressingMode AddressingMode.ZeroPage, ->
             @readNextProgramByte()

-        @registerAddressingMode AddressingMode.IndexedXZeroPage, ->
+        @registerAddressingMode AddressingMode.ZeroPageX, ->
             @getIndexedAddressByte @readNextProgramByte(), @registerX

         @registerAddressingMode AddressingMode.IndexedYZeroPage, ->
@@ -255,10 +259,10 @@
         @registerAddressingMode AddressingMode.Absolute, ->
             @readNextProgramWord()

-        @registerAddressingMode AddressingMode.IndexedXAbsolute, ->
+        @registerAddressingMode AddressingMode.AbsoluteX, ->
             @getIndexedAddressWord @readNextProgramWord(), @registerX

-        @registerAddressingMode AddressingMode.IndexedYAbsolute, ->
+        @registerAddressingMode AddressingMode.AbsoluteY, ->
             @getIndexedAddressWord @readNextProgramWord(), @registerY

         @registerAddressingMode AddressingMode.Relative, ->
@@ -268,11 +272,11 @@
         @registerAddressingMode AddressingMode.Indirect, ->
             @readWord @readNextProgramWord()

-        @registerAddressingMode AddressingMode.IndexedXIndirect, ->
+        @registerAddressingMode AddressingMode.IndirectX, ->
             address = @getIndexedAddressByte @readNextProgramByte(), @registerX
             @readWord address

-        @registerAddressingMode AddressingMode.IndirectIndexedY, ->
+        @registerAddressingMode AddressingMode.IndirectY, ->
             base = @readWord @readNextProgramByte()
             @getIndexedAddressWord base, @registerY

@@ -417,6 +421,26 @@
         @registerInstruction Instruction.JMP, (address) ->
             @programCounter = address

+        @registerInstruction Instruction.JSR, (address) ->
+            @pushWord @programCounter
+            @programCounter = address
+            tick()
+
+        @registerInstruction Instruction.LDA, (address) ->
+            @accumulator = @readByte address
+            @computeZeroFlag @accumulator
+            @computeNegativeFlag @accumulator
+
+        @registerInstruction Instruction.LDX, (address) ->
+            @registerX = @readByte address
+            @computeZeroFlag @registerX
+            @computeNegativeFlag @registerX
+
+        @registerInstruction Instruction.LDY, (address) ->
+            @registerY = @readByte address
+            @computeZeroFlag @registerY
+            @computeNegativeFlag @registerY
+
     registerInstruction: (instruction, execution) ->
         @instructionsTable[instruction] = execution

@@ -447,105 +471,128 @@
     initOperationsTable: ->
         @operationsTable = []

-        @registerOperation 0x69, Instruction.ADC, AddressingMode.Immediate,        no,  no
-        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0x75, Instruction.ADC, AddressingMode.IndexedXZeroPage, no,  no
-        @registerOperation 0x6D, Instruction.ADC, AddressingMode.Absolute,         no,  no
-        @registerOperation 0x7D, Instruction.ADC, AddressingMode.IndexedXAbsolute, no,  no
-        @registerOperation 0x79, Instruction.ADC, AddressingMode.IndexedYAbsolute, no,  no
-        @registerOperation 0x61, Instruction.ADC, AddressingMode.IndexedXIndirect, no,  no
-        @registerOperation 0x71, Instruction.ADC, AddressingMode.IndirectIndexedY, no,  no
-
-        @registerOperation 0x29, Instruction.AND, AddressingMode.Immediate,        no,  no
-        @registerOperation 0x25, Instruction.AND, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0x35, Instruction.AND, AddressingMode.IndexedXZeroPage, no,  no
-        @registerOperation 0x2D, Instruction.AND, AddressingMode.Absolute,         no,  no
-        @registerOperation 0x3D, Instruction.AND, AddressingMode.IndexedXAbsolute, no,  no
-        @registerOperation 0x39, Instruction.AND, AddressingMode.IndexedYAbsolute, no,  no
-        @registerOperation 0x21, Instruction.AND, AddressingMode.IndexedXIndirect, no,  no
-        @registerOperation 0x31, Instruction.AND, AddressingMode.IndirectIndexedY, no,  no
+        @registerOperation 0x69, Instruction.ADC, AddressingMode.Immediate,   no,  no
+        @registerOperation 0x65, Instruction.ADC, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x75, Instruction.ADC, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0x6D, Instruction.ADC, AddressingMode.Absolute,    no,  no
+        @registerOperation 0x7D, Instruction.ADC, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0x79, Instruction.ADC, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0x61, Instruction.ADC, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0x71, Instruction.ADC, AddressingMode.IndirectY,   no,  no
+
+        @registerOperation 0x29, Instruction.AND, AddressingMode.Immediate,   no,  no
+        @registerOperation 0x25, Instruction.AND, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x35, Instruction.AND, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0x2D, Instruction.AND, AddressingMode.Absolute,    no,  no
+        @registerOperation 0x3D, Instruction.AND, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0x39, Instruction.AND, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0x21, Instruction.AND, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0x31, Instruction.AND, AddressingMode.IndirectY,   no,  no

-        @registerOperation 0x0A, Instruction.ASL, AddressingMode.Accumulator,      no,  no
-        @registerOperation 0x06, Instruction.ASL, AddressingMode.ZeroPage,         no,  yes
-        @registerOperation 0x16, Instruction.ASL, AddressingMode.IndexedXZeroPage, no,  yes
-        @registerOperation 0x0E, Instruction.ASL, AddressingMode.Absolute,         no,  yes
-        @registerOperation 0x1E, Instruction.ASL, AddressingMode.IndexedXAbsolute, yes, yes
-
-        @registerOperation 0x90, Instruction.BCC, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0xB0, Instruction.BCS, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0xB0, Instruction.BEQ, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0x24, Instruction.BIT, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0x2C, Instruction.BIT, AddressingMode.Absolute,         no,  no
-
-        @registerOperation 0x30, Instruction.BMI, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0xD0, Instruction.BNE, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0x10, Instruction.BPL, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0x00, Instruction.BRK, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x50, Instruction.BVC, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0x70, Instruction.BVS, AddressingMode.Relative,         no,  no
-
-        @registerOperation 0x18, Instruction.CLC, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0xD8, Instruction.CLD, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x58, Instruction.CLI, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0xB8, Instruction.CLV, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0xC9, Instruction.CMP, AddressingMode.Immediate,        no,  no
-        @registerOperation 0xC5, Instruction.CMP, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0xD5, Instruction.CMP, AddressingMode.IndexedXZeroPage, no,  no
-        @registerOperation 0xCD, Instruction.CMP, AddressingMode.Absolute,         no,  no
-        @registerOperation 0xDD, Instruction.CMP, AddressingMode.IndexedXAbsolute, no,  no
-        @registerOperation 0xD9, Instruction.CMP, AddressingMode.IndexedYAbsolute, no,  no
-        @registerOperation 0xC1, Instruction.CMP, AddressingMode.IndexedXIndirect, no,  no
-        @registerOperation 0xD1, Instruction.CMP, AddressingMode.IndirectIndexedY, no,  no
-
-        @registerOperation 0xE0, Instruction.CPX, AddressingMode.Immediate,        no,  no
-        @registerOperation 0xE4, Instruction.CPX, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0xEC, Instruction.CPX, AddressingMode.Absolute,         no,  no
-
-        @registerOperation 0xC0, Instruction.CPX, AddressingMode.Immediate,        no,  no
-        @registerOperation 0xC4, Instruction.CPX, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0xCC, Instruction.CPX, AddressingMode.Absolute,         no,  no
-
-        @registerOperation 0xC6, Instruction.DEC, AddressingMode.ZeroPage,         no,  yes
-        @registerOperation 0xD6, Instruction.DEC, AddressingMode.IndexedXZeroPage, no,  yes
-        @registerOperation 0xCE, Instruction.DEC, AddressingMode.Absolute,         no,  yes
-        @registerOperation 0xDE, Instruction.DEC, AddressingMode.IndexedXAbsolute, yes, yes
-
-        @registerOperation 0xCA, Instruction.DEX, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x88, Instruction.DEY, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x49, Instruction.EOR, AddressingMode.Immediate,        no,  no
-        @registerOperation 0x45, Instruction.EOR, AddressingMode.ZeroPage,         no,  no
-        @registerOperation 0x55, Instruction.EOR, AddressingMode.IndexedXZeroPage, no,  no
-        @registerOperation 0x4D, Instruction.EOR, AddressingMode.Absolute,         no,  no
-        @registerOperation 0x5D, Instruction.EOR, AddressingMode.IndexedXAbsolute, no,  no
-        @registerOperation 0x59, Instruction.EOR, AddressingMode.IndexedYAbsolute, no,  no
-        @registerOperation 0x41, Instruction.EOR, AddressingMode.IndexedXIndirect, no,  no
-        @registerOperation 0x51, Instruction.EOR, AddressingMode.IndirectIndexedY, no,  no
-
-        @registerOperation 0xC6, Instruction.INC, AddressingMode.ZeroPage,         no,  yes
-        @registerOperation 0xD6, Instruction.INC, AddressingMode.IndexedXZeroPage, no,  yes
-        @registerOperation 0xCE, Instruction.INC, AddressingMode.Absolute,         no,  yes
-        @registerOperation 0xDE, Instruction.INC, AddressingMode.IndexedXAbsolute, yes, yes
-
-        @registerOperation 0xCA, Instruction.INX, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x88, Instruction.INY, AddressingMode.Implied,          no,  no
-
-        @registerOperation 0x4C, Instruction.INY, AddressingMode.Absolute,         no,  no
-        @registerOperation 0x6C, Instruction.INY, AddressingMode.Indirect,         no,  no
+        @registerOperation 0x0A, Instruction.ASL, AddressingMode.Accumulator, no,  no
+        @registerOperation 0x06, Instruction.ASL, AddressingMode.ZeroPage,    no,  yes
+        @registerOperation 0x16, Instruction.ASL, AddressingMode.ZeroPageX,   no,  yes
+        @registerOperation 0x0E, Instruction.ASL, AddressingMode.Absolute,    no,  yes
+        @registerOperation 0x1E, Instruction.ASL, AddressingMode.AbsoluteX,   yes, yes
+
+        @registerOperation 0x90, Instruction.BCC, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0xB0, Instruction.BCS, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0xB0, Instruction.BEQ, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0x24, Instruction.BIT, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x2C, Instruction.BIT, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0x30, Instruction.BMI, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0xD0, Instruction.BNE, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0x10, Instruction.BPL, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0x00, Instruction.BRK, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x50, Instruction.BVC, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0x70, Instruction.BVS, AddressingMode.Relative,    no,  no
+
+        @registerOperation 0x18, Instruction.CLC, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xD8, Instruction.CLD, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x58, Instruction.CLI, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xB8, Instruction.CLV, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0xC9, Instruction.CMP, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xC5, Instruction.CMP, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xD5, Instruction.CMP, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xCD, Instruction.CMP, AddressingMode.Absolute,    no,  no
+        @registerOperation 0xDD, Instruction.CMP, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0xD9, Instruction.CMP, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0xC1, Instruction.CMP, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0xD1, Instruction.CMP, AddressingMode.IndirectY,   no,  no
+
+        @registerOperation 0xE0, Instruction.CPX, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xE4, Instruction.CPX, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xEC, Instruction.CPX, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0xC0, Instruction.CPX, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xC4, Instruction.CPX, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xCC, Instruction.CPX, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0xC6, Instruction.DEC, AddressingMode.ZeroPage,    no,  yes
+        @registerOperation 0xD6, Instruction.DEC, AddressingMode.ZeroPageX,   no,  yes
+        @registerOperation 0xCE, Instruction.DEC, AddressingMode.Absolute,    no,  yes
+        @registerOperation 0xDE, Instruction.DEC, AddressingMode.AbsoluteX,   yes, yes
+
+        @registerOperation 0xCA, Instruction.DEX, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x88, Instruction.DEY, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x49, Instruction.EOR, AddressingMode.Immediate,   no,  no
+        @registerOperation 0x45, Instruction.EOR, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0x55, Instruction.EOR, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0x4D, Instruction.EOR, AddressingMode.Absolute,    no,  no
+        @registerOperation 0x5D, Instruction.EOR, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0x59, Instruction.EOR, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0x41, Instruction.EOR, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0x51, Instruction.EOR, AddressingMode.IndirectY,   no,  no
+
+        @registerOperation 0xC6, Instruction.INC, AddressingMode.ZeroPage,    no,  yes
+        @registerOperation 0xD6, Instruction.INC, AddressingMode.ZeroPageX,   no,  yes
+        @registerOperation 0xCE, Instruction.INC, AddressingMode.Absolute,    no,  yes
+        @registerOperation 0xDE, Instruction.INC, AddressingMode.AbsoluteX,   yes, yes
+
+        @registerOperation 0xCA, Instruction.INX, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x88, Instruction.INY, AddressingMode.Implied,     no,  no
+
+        @registerOperation 0x4C, Instruction.JMP, AddressingMode.Absolute,    no,  no
+        @registerOperation 0x6C, Instruction.JMP, AddressingMode.Indirect,    no,  no
+
+        @registerOperation 0x20, Instruction.JSR, AddressingMode.Absolute,    no,  no
+
+        @registerOperation 0xA9, Instruction.LDA, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xA5, Instruction.LDA, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xB5, Instruction.LDA, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xAD, Instruction.LDA, AddressingMode.Absolute,    no,  no
+        @registerOperation 0xBD, Instruction.LDA, AddressingMode.AbsoluteX,   no,  no
+        @registerOperation 0xB9, Instruction.LDA, AddressingMode.AbsoluteY,   no,  no
+        @registerOperation 0xA1, Instruction.LDA, AddressingMode.IndirectX,   no,  no
+        @registerOperation 0xB1, Instruction.LDA, AddressingMode.IndirectY,   no,  no
+
+        @registerOperation 0xA9, Instruction.LDX, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xA5, Instruction.LDX, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xB5, Instruction.LDX, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xAD, Instruction.LDX, AddressingMode.Absolute,    no,  no
+        @registerOperation 0xBD, Instruction.LDX, AddressingMode.AbsoluteX,   no,  no
+
+        @registerOperation 0xA9, Instruction.LDY, AddressingMode.Immediate,   no,  no
+        @registerOperation 0xA5, Instruction.LDY, AddressingMode.ZeroPage,    no,  no
+        @registerOperation 0xB5, Instruction.LDY, AddressingMode.ZeroPageX,   no,  no
+        @registerOperation 0xAD, Instruction.LDY, AddressingMode.Absolute,    no,  no
+        @registerOperation 0xBD, Instruction.LDY, AddressingMode.AbsoluteX,   no,  no

     registerOperation: (operationCode, instruction, addressingMode, emptyReadCycle, emptyWriteCycle) ->
         @operationsTable[operationCode] =


Ensure editors finish saving before running command

--- lib/controllers/process-controller.coffee
+++ lib/controllers/process-controller.coffee
@@ -78,10 +78,10 @@
   hasBeenRemoved: ->
     @configController.getMain().processControllerRemoved(@);

-  # Return false if the process didn't start.
   runProcessWithFile: (filePath) ->
+    # Return if there is already a process running.
     if @process?
-      return false;
+      return;

     @fields = {};
     @options = {};
@@ -142,21 +142,20 @@
       @fields.fileDirPath = "";
       @fields.fileProjectPath = "";

-    if !@saveDirtyFiles()
-      return false;
-
-    @takeUserInput(@config.inputDialogs);
+    @saveDirtyFiles();
+

   # Return true if the execution should continue. false if the user canceled.
   saveDirtyFiles: ->
     if @config.saveOption == 'none'
-      return true;
+      @runProcessAfterSave();
     else if @config.saveOption == 'all'
       return @saveEditors(@getAllDirtyEditors());
     else if @config.saveOption == 'referenced'
       return @saveEditors(@getReferencedDirtyEditors());
-
-    return true;
+    else
+      @runProcessAfterSave();
+

   getAllDirtyEditors: ->
     result = [];
@@ -202,25 +201,28 @@

     return null;

-  # return true if the editors were saved and the process can be executed.
-  # false if the user canceled saving and the process shouldn't execute.
   saveEditors: (editors) ->
     if editors.length == 0
-      return true;
+      @runProcessAfterSave();
+      return;

     option = 'yes';

     if @config.promptToSave
       option = @promptToSave(editors);

-      if option == 'cancel'
-        return false;
-
-    if option == 'yes'
-      for editor in editors
-        editor.save();
-
-    return true;
+    if option == 'cancel'
+      return;
+    else if option == 'no'
+      @runProcessAfterSave();
+      return;
+
+    promises = editors.map (e) ->
+      e.save()
+
+    Promise.all(promises).then (results) =>
+      @runProcessAfterSave();
+    .catch (error) -> console.error(error);

   # Prompt to ask if editors should be saved. Return 'yes', 'no' or 'cancel'
   promptToSave: (editors) ->
@@ -236,6 +238,9 @@

     choice = atom.confirm(options);
     return options.buttons[choice].toLowerCase();
+
+  runProcessAfterSave: ->
+    @takeUserInput(@config.inputDialogs);

   takeUserInput: (inputDialogs) ->
     if inputDialogs.length > 0


Better coords/speed display on reveal

Better coords/speed display on reveal

--- app/controllers/classify-steps/reveal.coffee
+++ app/controllers/classify-steps/reveal.coffee
@@ -143,16 +143,30 @@
         @chart.setSize @graphContainer.width(), @graphContainer.height()
         @chart.redraw()

-      coords = @classifier.classification.subject.coords
-      coords[1] += 360 if coords[1] < 0
-      @youAreHere.setLatLng coords
-      @youAreHere.bindPopup("""
-
#{coords}

+      [lat, lng] = @classifier.classification.subject.coords
+
+      displayLat = lat
+
+      lng += 360 if lng < 0
+
+      @youAreHere.setLatLng [lat, lng]
+
+      popupContent = """

-          #{translate 'span', 'classify.details.reveal.estimated'}

-          #{SPEED_ESTIMATES[@classifier.classification.get 'match'] || '?'}
+          #{Math.abs displayLat} #{if displayLat < 0 then 'S' else 'N'},
+          #{Math.abs lng} #{if lng < 0 then 'W' else 'E'}

-      """).openPopup()
+      """
+
+      if SPEED_ESTIMATES[@classifier.classification.get 'match']? then popupContent += """
+

+          #{translate 'span', 'classify.details.reveal.estimated'}
+          #{SPEED_ESTIMATES[@classifier.classification.get 'match']}
+

+      """
+
+      @youAreHere.bindPopup(popupContent).openPopup()
+
       @map.setView @classifier.classification.subject.coords, DEFAULT_ZOOM

   reset: ->


updating to use PixieCanvas

updating to use PixieCanvas

--- src/drawable.coffee
+++ src/drawable.coffee
@@ -53,8 +53,12 @@
       else
         warn?("Sprite has no draw method!")
     else
-      canvas.fillColor(I.color)
-      canvas.fillRect(0, 0, I.width, I.height)
+      canvas.drawRect
+        x: 0
+        y: 0
+        width: I.width
+        height: I.height
+        color: I.color

   ###*
   Draw does not actually do any drawing itself, instead it triggers all of the draw events.


Trim preview template so that createElement doesn't pick up an empty text node rather than the container.

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -277,7 +277,7 @@
     # Called when a file is added to the queue
     # Receives `file`
     addedfile: (file) ->
-      file.previewElement = Dropzone.createElement @options.previewTemplate
+      file.previewElement = Dropzone.createElement @options.previewTemplate.trim()
       file.previewTemplate = file.previewElement # Backwards compatibility

       @previewsContainer.appendChild file.previewElement


Initialize LabelManager only when necessary

--- app/assets/javascripts/dispatcher.js.coffee
+++ app/assets/javascripts/dispatcher.js.coffee
@@ -101,7 +101,7 @@
       when 'projects:labels:new', 'projects:labels:edit'
         new Labels()
       when 'projects:labels:index'
-        new LabelManager()
+        new LabelManager() if $('.prioritized-labels').length
       when 'projects:network:show'
         # Ensure we don't create a particular shortcut handler here. This is
         # already created, where the network graph is created.


we should not use `console.log` in production code...

--- app/assets/javascripts/dispatcher.js.coffee
+++ app/assets/javascripts/dispatcher.js.coffee
@@ -1,6 +1,6 @@
 $ ->
   new Dispatcher()
-
+
 class Dispatcher
   constructor: () ->
     @initSearch()
@@ -10,8 +10,6 @@
     page = $('body').attr('data-page')
     project_id = $('body').attr('data-project-id')

-    console.log(page)
-
     unless page
       return false


Fix editing types

Fix editing types

--- app/scripts/controllers/edittypemodal.coffee
+++ app/scripts/controllers/edittypemodal.coffee
@@ -24,6 +24,7 @@
         return (type in $scope.groups)

     $scope.save = () ->
+        $scope.type.type_id = $scope.selectedBase.id
         $http.put(
             SERVER_URL + "/task_types/" + $scope.type.id
             {"type": $scope.type}
@@ -32,7 +33,6 @@
                 $scope.$close()
             (error) ->
                 $scope.$close()
-                $scope.$parent.alert("The changes could not be saved", "danger")
         )

     return


Fix indexOf check to allow for an index of 0

--- src/scripts/models/content.coffee
+++ src/scripts/models/content.coffee
@@ -47,7 +47,7 @@
       super(arguments...)

       # Mark drafts as being in edit mode by default if user has edit permission
-      if @isDraft() and response.status isnt 'publishing' and _.indexOf(response.permissions, 'edit')
+      if @isDraft() and response.status isnt 'publishing' and _.indexOf(response.permissions, 'edit') >= 0
         response.editable = true

       return response


Clicking anywhere on the artist suggestion will trigger a follow

--- apps/personalize/client/views/artists.coffee
+++ apps/personalize/client/views/artists.coffee
@@ -13,13 +13,15 @@
 module.exports = class ArtistsView extends StepView
   _.extend @prototype, Followable
   _.extend @prototype, GeneArtists
+  _.extend @prototype, BookmarkedArtists

   analyticsUnfollowMessage : 'Unfollowed artist from personalize artist search'
   analyticsFollowMessage   : 'Followed artist from personalize artist search'

   events:
-    'click .personalize-skip' : 'advance'
-    'click .pfa-remove'       : 'unfollow'
+    'click .personalize-skip'       : 'advance'
+    'click .pfa-remove'             : 'unfollow'
+    'click .personalize-suggestion' : 'followSuggestion'

   initialize: (options) ->
     super
@@ -47,6 +49,10 @@
       model                    : model
       modelName                : 'artist'
       el                       : el
+
+  # Delegates to follow button
+  followSuggestion: (e) ->
+    $(e.currentTarget).find('.follow-button').click()

   createSuggestionSet: (artist) ->
     new Backbone.Model


refactored list controllers. Seems DRY now :)

--- static/coffee-script/app.coffee
+++ static/coffee-script/app.coffee
@@ -58,68 +58,57 @@
   ).observes('options', 'default')
 }

-Weathermaps.groups = ListController.create {
-  refresh: ( ->
-    @_super()
-    $.getJSON baseurl+"/groups", (data) =>
+createListController = (name, parentName, init) ->
+  if 'Object' is typeof parentName
+    init = parentName
+  init = init || {}
+
+  controller = if parentName
+    ListController.extend {
+      parentValueBinding: 'Weathermaps.'+parentName+'s.value'
+      parentUrlBinding: 'Weathermaps.'+parentName+'s.databaseurl'
+      databaseurl: (->
+        @get('parentUrl')+"/"+@get('parentValue')
+      ).property('parentValue', 'parentUrl')
+
+      refresh: ((cb)->
+        @_super()
+        url = @get('databaseurl')
+        parentValue = @get 'parentValue'
+        if parentValue
+          $.getJSON url+"/"+name+"s", cb
+      )
+    }
+  else
+    ListController.extend {
+      databaseurl: baseurl
+      refresh: ( ->
+        @_super()
+        $.getJSON @get('databaseurl')+"/"+name+"s", (data) =>
+          @set 'options', data
+      )
+    }
+  controller.create(init)
+
+dateTime =  {
+  default: 'first'
+  refresh: (->
+    @_super (data) =>
+      data.sort()
+      data.reverse()
       @set 'options', data
-  )
-}
-
-Weathermaps.maps = ListController.create {
-  groupBinding: 'Weathermaps.groups.value'
-
+  ).observes 'parentValue'
+}
+
+Weathermaps.groups = createListController "group"
+Weathermaps.maps = createListController "map", "group", {
   refresh: (->
-    @_super()
-    group = @get 'group'
-    if not group
-      @set 'options', []
-    else
-      $.getJSON baseurl+"/"+group+"/maps", (data) =>
-        @set 'options', data
-  ).observes("group")
-}
-
-Weathermaps.dates = ListController.create {
-  default: 'first'
-  default: 'first'
-  groupBinding: 'Weathermaps.groups.value'
-  mapBinding:   'Weathermaps.maps.value'
-
-  refresh: (->
-    @_super()
-    group = @get 'group'
-    map = @get 'map'
-    if not map
-      @set 'options', []
-    else
-      $.getJSON baseurl+"/"+group+"/"+map+"/dates", (data) =>
-        data.sort()
-        data.reverse()
-        @set 'options', data
-  ).observes("map")
-}
-
-Weathermaps.times = ListController.create {
-  default: 'first'
-  groupBinding: 'Weathermaps.groups.value'
-  mapBinding:   'Weathermaps.maps.value'
-  dateBinding:  'Weathermaps.dates.value'
-
-  refresh: (->
-    @_super()
-    group = @get 'group'
-    map = @get 'map'
-    date = @get 'date'
-    if not date
-      @set 'options', []
-    else
-      $.getJSON baseurl+"/"+group+"/"+map+"/"+date+"/times", (data) =>
-        data.sort()
-        data.reverse()
-        @set 'options', data
-  ).observes("date")
-}
+    @_super (data) =>
+       @set 'options', data
+  ).observes 'parentValue'
+}
+Weathermaps.dates = createListController "date", "map", dateTime
+Weathermaps.times = createListController "time", "date", dateTime

 Weathermaps.current = Ember.Object.create {
   groupBinding: "Weathermaps.groups.value"


update: 多重起動を防止

update: 多重起動を防止

--- src/bin/coffee/core.coffee
+++ src/bin/coffee/core.coffee
@@ -11,6 +11,16 @@

 # window
 player = null
+
+# make single instance --------------------------------------
+shouldQuit = app.makeSingleInstance ->
+  if player
+    if player.isMinimized()
+      player.restore()
+    player.focus()
+
+if shouldQuit
+  app.quit()

 # ready -----------------------------------------------------
 app.on 'ready', ->
@@ -31,4 +41,3 @@
 ipc.on 'close', ->
 	console.log 1
 	player.close()
-


Back to _.after; fix race condition

Back to _.after; fix race condition

--- apps/user_profile/client/user_profile.coffee
+++ apps/user_profile/client/user_profile.coffee
@@ -28,13 +28,11 @@
     @articles = new Articles
     @articles.url = "#{@articles.url}?author_id=#{@model.get('owner').id}&published=true"

-    $.when(
-      @model.fetchFavorites(success: ((@favorites) =>))
-      if @user?.hasLabFeature('Articles')
-        @articles.fetch()
-      else
-        @model.fetchPosts(success: ((@posts) =>))
-    ).always @render
+    @model.fetchFavorites(success: ((@favorites) =>), complete: => @render())
+    if @user?.hasLabFeature('Articles')
+      @articles.fetch(complete: => @render())
+    else
+      @model.fetchPosts(success: ((@posts) =>), complete: => @render())

   openWebsite: ->
     popup = window.open @model.get('website'), '_blank'
@@ -46,7 +44,7 @@
       model: @model
       collection: @following

-  render: =>
+  render: _.after 2, ->
     @setState()
     @renderPosts()
     @renderFavorites()


Removed 0.1.x example.

Removed 0.1.x example.

--- examples/websockets.coffee
+++ examples/websockets.coffee
@@ -1,44 +0,0 @@
-require('zappa') ->
-  get '/': -> render 'index'
-
-  get '/counter': -> "# of messages so far: #{app.counter}"
-
-  at connection: ->
-    app.counter ?= 0
-    console.log "Connected: #{id}"
-    broadcast 'connected', id: id
-
-  at disconnect: ->
-    console.log "Disconnected: #{id}"
-
-  at said: ->
-    console.log "#{id} said: #{@text}"
-    app.counter++
-    send 'said', id: id, text: @text
-    broadcast 'said', id: id, text: @text
-
-  client '/index.js': ->
-    $(document).ready ->
-      connect 'http://localhost'
-
-      at connection: -> $('#log').append '
Connected
'
-      at disconnect: -> $('#log').append '
Disconnected
'
-      at welcome: -> $('#log').append "
#{@id} Connected
"
-      at said: -> $('#log').append "
#{@id}: #{@text}
"
-
-      $('form').submit ->
-        emit said: {text: $('#box').val()}
-        $('#box').val('').focus()
-        false
-
-      $('#box').focus()
-
-  view index: ->
-    @title = 'Nano Chat'
-    @scripts = ['http://code.jquery.com/jquery-1.4.3.min', '/socket.io/socket.io', '/default']
-
-    h1 @title
-    div id: 'log'
-    form ->
-      input id: 'box'
-      button id: 'say', -> 'Say'


Fix level editor child window size

Fix level editor child window size

Makes it slightly larger at 1280x640, which is our target screen size
for slightly smaller than chromebook screens.

--- app/views/editor/level/LevelEditView.coffee
+++ app/views/editor/level/LevelEditView.coffee
@@ -165,7 +165,7 @@
       if me.get('name') is 'Nick'
         @childWindow = window.open("/play/level/#{scratchLevelID}", 'child_window', 'width=2560,height=1080,left=0,top=-1600,location=1,menubar=1,scrollbars=1,status=0,titlebar=1,toolbar=1', true)
       else
-        @childWindow = window.open("/play/level/#{scratchLevelID}", 'child_window', 'width=1024,height=560,left=10,top=10,location=0,menubar=0,scrollbars=0,status=0,titlebar=0,toolbar=0', true)
+        @childWindow = window.open("/play/level/#{scratchLevelID}", 'child_window', 'width=1280,height=640,left=10,top=10,location=0,menubar=0,scrollbars=0,status=0,titlebar=0,toolbar=0', true)
       @childWindow.onPlayLevelViewLoaded = (e) => sendLevel()  # still a hack
     @childWindow.focus()


No need to explicitly track subviews for determining what the current view is.

--- app/views/editor/level/LevelEditView.coffee
+++ app/views/editor/level/LevelEditView.coffee
@@ -33,7 +33,7 @@
     'click #undo-button': 'onUndo'
     'click #redo-button': 'onRedo'
     'click #patches-tab': -> @patchesView.load()
-    'click #components-tab': -> @componentsTab.refreshLevelThangsTreema @level.get('thangs')
+    'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs')
     'click #level-patch-button': 'startPatchingLevel'
     'click #level-watch-button': 'toggleWatchLevel'
     'click #pop-level-i18n-button': -> @level.populateI18N()
@@ -43,7 +43,6 @@
     super options
     @supermodel.shouldSaveBackups = (model) ->
       model.constructor.className in ['Level', 'LevelComponent', 'LevelSystem', 'ThangType']
-    @subViews = {}
     @levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, headless: true, editorMode: true
     @level = @levelLoader.level
     @files = new DocumentFiles(@levelLoader.level)
@@ -70,13 +69,13 @@
     return unless @supermodel.finished()
     @$el.find('a[data-toggle="tab"]').on 'shown.bs.tab', (e) =>
       Backbone.Mediator.publish 'level:view-switched', e
-    @subViews['thangsTab'] = @insertSubView new ThangsTabView world: @world, supermodel: @supermodel, level: @level
-    @subViews['settingsTab'] = @insertSubView new SettingsTabView supermodel: @supermodel
-    @subViews['scriptsTab'] = @insertSubView new ScriptsTabView world: @world, supermodel: @supermodel, files: @files
-    @subViews['componentsTab'] = @insertSubView new ComponentsTabView supermodel: @supermodel
-    @subViews['systemsTab'] = @insertSubView new SystemsTabView supermodel: @supermodel
-    @subViews['componentsDocsTab'] = @insertSubView new ComponentDocsView supermodel: @supermodel
-
+    @insertSubView new ThangsTabView world: @world, supermodel: @supermodel, level: @level
+    @insertSubView new SettingsTabView supermodel: @supermodel
+    @insertSubView new ScriptsTabView world: @world, supermodel: @supermodel, files: @files
+    @insertSubView new ComponentsTabView supermodel: @supermodel
+    @insertSubView new SystemsTabView supermodel: @supermodel
+    @insertSubView new ComponentDocsView supermodel: @supermodel
+
     Backbone.Mediator.publish 'level-loaded', level: @level
     @showReadOnly() if me.get('anonymous')
     @patchesView = @insertSubView(new PatchesView(@level), @$el.find('.patches-view'))
@@ -108,14 +107,16 @@
     @childWindow.focus()

   onUndo: ->
-    @getCurrentView()?.undo()
+    @getCurrentView()?.undo?()

   onRedo: ->
-    @getCurrentView()?.redo()
+    @getCurrentView()?.redo?()

   getCurrentView: ->
-    tabText = $('li.active')[0]?.textContent.toLowerCase()
-    currentView = @subViews[tabText + 'Tab' ]
+    tabText = _.string.underscored $('li.active')[0]?.textContent
+    currentView = @subviews["editor_level_#{tabText}_tab_view"]
+    if tabText is 'patches' then currentView = @patchesView
+    if tabText is 'documentation' then currentView = @subviews.docs_components_view
     currentView

   startPatchingLevel: (e) ->


removed unnecessary constant from script loader

--- src/script/script_loader.coffee
+++ src/script/script_loader.coffee
@@ -1,8 +1,6 @@
 exports = window.script ?= {}

 class ScriptLoader
-
-  @AUTOLOAD_PATH = 'scripts'

   constructor: ->
     @_scripts = {}


added metric system forecasts to wunderground script

--- src/scripts/wunderground.coffee
+++ src/scripts/wunderground.coffee
@@ -6,6 +6,7 @@
 #
 # Configuration:
 #   HUBOT_WUNDERGROUND_API_KEY Sign up at http://www.wunderground.com/weather/api/.
+#   HUBOT_WUNDERGROUND_USE_METRIC (heroku config:unset HUBOT_WUNDERGROUND_USE_METRIC to disable)
 #
 # Commands:
 #   hubot weather me <location> - short-term forecast
@@ -87,8 +88,10 @@

 send_forecast = (msg, location, data) ->
   report = data.forecast.txt_forecast.forecastday[0]
-  msg.send "#{report.title} in #{location}: #{report.fcttext} (#{formatted_ttl data})"
-
+  if not process.env.HUBOT_WUNDERGROUND_USE_METRIC?
+    msg.send "#{report.title} in #{location}: #{report.fcttext} (#{formatted_ttl data})"
+  else
+    msg.send "#{report.title} in #{location}: #{report.fcttext_metric} (#{formatted_ttl data})"
 send_radar = (msg, location, data) ->
   msg.send "#{data.radar.image_url}#.png"

@@ -121,4 +124,4 @@

 alternative_place = (item) ->
   return '' if item.country != 'US' || item.state == "" || item.city == ""
-  return "#{item.state}/#{item.city.replace(/\s/g, '_')}"
+  return "#{item.state}/#{item.city.replace(/\s/g, '_')}"


Extract build_expects_method.is_mock_method()

--- src/TinyMock.coffee
+++ src/TinyMock.coffee
@@ -97,7 +97,7 @@

   expects_method = (method_name) ->
     check_expects_usage(@, method_name, arguments.length)
-    if not mock_methods.contains(@[method_name])
+    if not is_mock_method(@[method_name])
       @[method_name] = build_mock_method(@, method_name)
       mock_methods.add(@[method_name])
     @[method_name].expectations.create_expectation()
@@ -109,6 +109,9 @@
     fail(messages.PreExistingProperty, method_name) if has_property(self, method_name)
     fail(messages.ReservedMethodName, method_name) if is_reserved_method_name(method_name)

+  is_mock_method = (method) ->
+    mock_methods.contains(method)
+
   is_reserved_method_name = (method_name) ->
     method_name == expects_method_name


Fixed bug showing spell palette for languages missing any code snippets.

--- app/views/play/level/tome/spell_view.coffee
+++ app/views/play/level/tome/spell_view.coffee
@@ -180,8 +180,8 @@
           return (owner is 'this' or owner is 'more') and (not doc.owner? or doc.owner is 'this')
         console.log 'could not find doc for', prop, 'from', e.allDocs['__' + prop], 'for', owner, 'of', e.propGroups unless doc
         doc ?= prop
-        if doc.snippets?
-          entry =
+        if doc.snippets?[@spell.language]
+          entry =
             content: doc.snippets[@spell.language].code
             name: doc.name
             tabTrigger: doc.snippets[@spell.language].tab


Add groups link to menu tray

Add groups link to menu tray

--- assets/js/intertwinkles/ui_components.coffee
+++ assets/js/intertwinkles/ui_components.coffee
@@ -155,8 +155,11 @@
               <form class='form-search'
                     action='<%- INTERTWINKLES_APPS.www.url %>/search/'
                     method='GET'>
+                <% if (window.location.pathname != "/") { %>
+                ← Groups
+                <% } %>

-                  <input class='input-medium search-query' type='text' name='q' />
+                  <input class='input-medium search-query' type='text' name='q' placeholder='Search'/>
                   <button class='btn' type='submit'>

                   </button>


break out schema upgrade

break out schema upgrade

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -308,7 +308,9 @@
   exists = (Zotero.DB.valueQuery("SELECT count(*) FROM #{table.schema}sqlite_master WHERE type='table' and name=?", [table.name]) != 0)
   return exists && (!mustHaveData || Zotero.DB.valueQuery("select count(*) from #{name}") != 0)

-Zotero.BetterBibTeX.attachDatabase = ->
+Zotero.BetterBibTeX.upgradeDatabase = ->
+  @flash('Better BibTeX: updating database', 'Updating database, this could take a while')
+
   for key in @pref.prefs.getChildList('')
     switch key
       when 'auto-abbrev.style' then @pref.set('autoAbbrevStyle', @pref.get(key))
@@ -334,6 +336,95 @@
     translatorSettings = JSON.parse(Zotero.Prefs.get('export.translatorSettings'))
     @pref.set('preserveBibTeXVariables', true) if translatorSettings['Preserve BibTeX variables']

+  # cleanup any junk
+  for table in Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name like '-%-'") || []
+    Zotero.DB.query("drop table if exists betterbibtex.\"#{table}\"")
+
+  tables = Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name <> 'schema' ORDER BY name") || []
+
+  if @tableExists('betterbibtex.autoexport') && @table_info('betterbibtex.autoexport').collection
+    Zotero.DB.query("update betterbibtex.autoexport set collection = (select 'library:' || libraryID from groups where 'group:' || groupID = collection) where collection like 'group:%'")
+    Zotero.DB.query("update betterbibtex.autoexport set collection = 'collection:' || collection where collection <> 'library' and collection not like '%:%'")
+
+  tip = Zotero.DB.transactionInProgress()
+  Zotero.DB.beginTransaction() unless tip
+
+  @pref.set('scanCitekeys', true)
+
+  for table in tables
+    @debug('initDatabase: backing up', table)
+    Zotero.DB.query("alter table betterbibtex.#{table} rename to \"-#{table}-\"")
+
+  ### clean slate ###
+
+  Zotero.DB.query('create table betterbibtex.keys (itemID primary key, citekey not null, citekeyFormat)')
+
+  Zotero.DB.query("
+    create table betterbibtex.cache (
+      itemID not null,
+
+      exportCharset not null,
+      exportNotes default 'false' CHECK(exportNotes in ('true', 'false')),
+      getCollections default 'false' CHECK(getCollections in ('true', 'false')),
+      translatorID not null,
+      useJournalAbbreviation default 'false' CHECK(useJournalAbbreviation in ('true', 'false')),
+
+      citekey not null,
+      bibtex not null,
+      lastaccess not null default CURRENT_TIMESTAMP,
+      PRIMARY KEY (itemID, exportCharset, exportNotes, getCollections, translatorID, useJournalAbbreviation)
+      )
+    ")
+
+  Zotero.DB.query("
+    create table betterbibtex.autoexport (
+      id INTEGER PRIMARY KEY NOT NULL DEFAULT NULL,
+
+      collection not null,
+      path not null,
+
+      exportCharset not null,
+      exportNotes default 'false' CHECK(exportNotes in ('true', 'false')),
+      translatorID not null,
+      useJournalAbbreviation default 'false' CHECK(useJournalAbbreviation in ('true', 'false')),
+
+      exportedRecursively CHECK(exportedRecursively in ('true', 'false')),
+      status,
+
+      UNIQUE (path)
+      )
+    ")
+
+  ### migrate data where needed ###
+
+  if @tableExists('betterbibtex."-keys-"', true)
+    @debug('initDatabase: migrating keys')
+    if @table_info('betterbibtex."-keys-"').pinned
+      @debug('initDatabase: migrating old-style keys')
+      Zotero.BetterBibTeX.debug('Upgrading betterbibtex.keys')
+      Zotero.DB.query('insert into betterbibtex.keys (itemID, citekey, citekeyFormat)
+                      select itemID, citekey, case when pinned = 1 then null else ? end from betterbibtex."-keys-"', [@citekeyFormat])
+    else
+      @debug('initDatabase: migrating keys')
+      @copyTable('betterbibtex."-keys-"', 'betterbibtex.keys')
+
+  if @tableExists('betterbibtex."-autoexport-"', true)
+    @debug('initDatabase: migrating autoexport')
+    if @table_info('betterbibtex."-autoexport-"').context
+      # sorry my dear colleague, but this was a mess
+    else
+      @copyTable('betterbibtex."-autoexport-"', 'betterbibtex.autoexport', 'id')
+
+  ### cleanup ###
+
+  for table in Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name like '-%-'") || []
+    @debug('initDatabase: deleting', table)
+    Zotero.DB.query("drop table if exists betterbibtex.\"#{table}\"")
+
+  Zotero.DB.commitTransaction() unless tip
+  @flash('Better BibTeX: database updated', 'Database update finished')
+
+Zotero.BetterBibTeX.initDatabase = ->
   db = Zotero.getZoteroDatabase('betterbibtex')
   Zotero.DB.query('ATTACH ? AS betterbibtex', [db.path])

@@ -341,20 +432,14 @@
   Zotero.DB.query("insert or ignore into betterbibtex.schema (lock, version) values ('schema', '')")
   Zotero.DB.query("update betterbibtex.schema set version = '' where not version like '%.%.%'")

-  installed = @version(Zotero.DB.valueQuery("select version from betterbibtex.schema"))
-  installing = @version(@release)
-
-  if @tableExists('betterbibtex.autoexport') && @table_info('betterbibtex.autoexport').collection
-    Zotero.DB.query("update betterbibtex.autoexport set collection = (select 'library:' || libraryID from groups where 'group:' || groupID = collection) where collection like 'group:%'")
-    Zotero.DB.query("update betterbibtex.autoexport set collection = 'collection:' || collection where collection <> 'library' and collection not like '%:%'")
-
-  upgrade = (installed != installing) || Services.vc.compare(@release, '1.2.1') <= 0
-
-  for table in Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name like '-%-'") || []
-    Zotero.DB.query("drop table if exists betterbibtex.\"#{table}\"")
-  tables = Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name <> 'schema' ORDER BY name") || []
-
-  upgrade ||= tables.join(' + ') != 'autoexport + cache + keys'
+  installed = Zotero.DB.valueQuery("select version from betterbibtex.schema")
+
+  # always upgrade on major or minor change
+  upgrade = Services.vc.compare(installed.split('.').slice(0, 2).join('.'), @release.split('.').slice(0, 2).join('.')) < 0
+
+  # upgrade anything before specific release
+  upgrade ||= Services.vc.compare(installed, '1.2.1') <= 0
+
   for check in [
     'SELECT itemID, citekey, citekeyFormat FROM betterbibtex.keys'
     'SELECT id, collection, path, exportCharset, exportNotes, translatorID, useJournalAbbreviation, exportedRecursively, status FROM betterbibtex.autoexport'
@@ -367,85 +452,7 @@
       @log('Unexpected schema:', check, e)
       upgrade = true

-  if upgrade
-    @flash('Better BibTeX: updating database', 'Updating database, this could take a while')
-
-    tip = Zotero.DB.transactionInProgress()
-    Zotero.DB.beginTransaction() unless tip
-
-    @pref.set('scanCitekeys', true)
-
-    for table in tables
-      @debug('attachdatabase: backing up', table)
-      Zotero.DB.query("alter table betterbibtex.#{table} rename to \"-#{table}-\"")
-
-    ### clean slate ###
-
-    Zotero.DB.query('create table betterbibtex.keys (itemID primary key, citekey not null, citekeyFormat)')
-
-    Zotero.DB.query("
-      create table betterbibtex.cache (
-        itemID not null,
-
-        exportCharset not null,
-        exportNotes default 'false' CHECK(exportNotes in ('true', 'false')),
-        getCollections default 'false' CHECK(getCollections in ('true', 'false')),
-        translatorID not null,
-        useJournalAbbreviation default 'false' CHECK(useJournalAbbreviation in ('true', 'false')),
-
-        citekey not null,
-        bibtex not null,
-        lastaccess not null default CURRENT_TIMESTAMP,
-        PRIMARY KEY (itemID, exportCharset, exportNotes, getCollections, translatorID, useJournalAbbreviation)
-        )
-      ")
-
-    Zotero.DB.query("
-      create table betterbibtex.autoexport (
-        id INTEGER PRIMARY KEY NOT NULL DEFAULT NULL,
-
-        collection not null,
-        path not null,
-
-        exportCharset not null,
-        exportNotes default 'false' CHECK(exportNotes in ('true', 'false')),
-        translatorID not null,
-        useJournalAbbreviation default 'false' CHECK(useJournalAbbreviation in ('true', 'false')),
-
-        exportedRecursively CHECK(exportedRecursively in ('true', 'false')),
-        status,
-
-        UNIQUE (path)
-        )
-      ")
-
-    ### migrate data where needed ###
-
-    if @tableExists('betterbibtex."-keys-"', true)
-      @debug('attachdatabase: migrating keys')
-      if @table_info('betterbibtex."-keys-"').pinned
-        @debug('attachdatabase: migrating old-style keys')
-        Zotero.BetterBibTeX.debug('Upgrading betterbibtex.keys')
-        Zotero.DB.query('insert into betterbibtex.keys (itemID, citekey, citekeyFormat)
-                        select itemID, citekey, case when pinned = 1 then null else ? end from betterbibtex."-keys-"', [@citekeyFormat])
-      else
-        @debug('attachdatabase: migrating keys')
-        @copyTable('betterbibtex."-keys-"', 'betterbibtex.keys')
-
-    if @tableExists('betterbibtex."-autoexport-"', true)
-      @debug('attachdatabase: migrating autoexport')
-      if @table_info('betterbibtex."-autoexport-"').context
-        # sorry my dear colleague, but this was a mess
-      else
-        @copyTable('betterbibtex."-autoexport-"', 'betterbibtex.autoexport', 'id')
-
-    ### cleanup ###
-
-    for table in Zotero.DB.columnQuery("SELECT name FROM betterbibtex.sqlite_master WHERE type='table' AND name like '-%-'") || []
-      @debug('attachdatabase: deleting', table)
-      Zotero.DB.query("drop table if exists betterbibtex.\"#{table}\"")
-
-    Zotero.DB.commitTransaction() unless tip
+  @upgradeDatabase() if upgrade

   if @pref.get('scanCitekeys')
     @flash('Citation key rescan', "Scanning 'extra' fields for fixed keys\nFor a large library, this might take a while")
@@ -486,7 +493,7 @@
   @threadManager = Components.classes['@mozilla.org/thread-manager;1'].getService()
   @windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator)

-  @attachDatabase()
+  @initDatabase()
   cfi = @pref.get('cacheFlushInterval')
   cfi = 1 if typeof cfi != 'number' || cfi < 1
   cfi = 5 if cfi > 5


Use closest instead of parent()

Use closest instead of parent()

Signed-off-by: Dmitriy Zaporozhets <be23d75b156792e5acab51b196a2deb155d35d6a@gmail.com>

--- app/assets/javascripts/activities.js.coffee
+++ app/assets/javascripts/activities.js.coffee
@@ -12,7 +12,7 @@

   toggleFilter: (sender) ->
-    sender.parent().toggleClass "active"
+    sender.closest('li').toggleClass "active"
     event_filters = $.cookie("event_filter")
     filter = sender.attr("id").split("_")[0]
     if event_filters


activity: invoke `super` in `CommentListViewController::loadView` method

--- client/Social/Activity/views/comments/listviewcontroller.coffee
+++ client/Social/Activity/views/comments/listviewcontroller.coffee
@@ -21,10 +21,7 @@

   loadView: (mainView) ->

-    {scrollView, noItemFoundWidget} = @getOptions()
-
-    @setScrollView mainView  if scrollView
-    @putNoItemView()  if noItemFoundWidget
+    super mainView

     {replies} = @getData()
     @instantiateListItems replies  if replies.length


minor fix

minor fix

--- client/account/lib/views/accountcredentiallistcontroller.coffee
+++ client/account/lib/views/accountcredentiallistcontroller.coffee
@@ -275,7 +275,7 @@

     { ui }    = kd.singletons.computeController

-    noCredFound = unless listView.items.length then yes else no
+    noCredFound = !listView.items.length

     view.form = ui.generateAddCredentialFormFor options, noCredFound
     view.form.setClass 'credentials-form-view'


app-updates: Refreshed app catalog when an app has been updated.

--- client/app/Applications/Apps.kdapplication/AppController.coffee
+++ client/app/Applications/Apps.kdapplication/AppController.coffee
@@ -15,6 +15,8 @@
     super options, data

     @appsController = @getSingleton "kodingAppsController"
+
+    @getSingleton("mainViewController").on "AnAppHasBeenUpdated", @bound "updateApps"

   loadView:(mainView)->


More pnotify configure

More pnotify configure

--- client/conf/pnotify.coffee
+++ client/conf/pnotify.coffee
@@ -1,3 +1,6 @@
 $.pnotify.defaults.styling = "bootstrap"
 $.pnotify.defaults.history = false
 $.pnotify.defaults.stack = {"dir1": "down", "dir2": "left", "firstpos1": 90, "firstpos2": 25}
+$.pnotify.defaults.nonblock = true
+$.pnotify.defaults.closer = false
+$.pnotify.defaults.sticker = false


remove spurious unused file

remove spurious unused file

--- bokehjs/src/coffee/common/collections.coffee
+++ bokehjs/src/coffee/common/collections.coffee
@@ -1,90 +0,0 @@
-define [
-  "underscore",
-  "require",
-  "common/plot",
-  "common/gmap_plot",
-  "common/grid_plot",
-  "common/plot_context",
-  # "pandas/ipython_remote_data",
-  # "pandas/pandas_pivot_table",
-  # "pandas/pandas_plot_source",
-  "range/range1d",
-  "range/data_range1d",
-  "range/factor_range",
-  "range/data_factor_range",
-  "renderer/glyph/glyph",
-  "renderer/guide/linear_axis",
-  "renderer/guide/datetime_axis",
-  "renderer/guide/grid",
-  "renderer/annotation/legend",
-  "renderer/overlay/box_selection",
-  "source/object_array_data_source",
-  "source/column_data_source",
-  "tool/pan_tool",
-  "tool/wheel_zoom_tool",
-  "tool/resize_tool",
-  "tool/box_select_tool",
-  "tool/data_range_box_select_tool",
-  "tool/preview_save_tool",
-  "tool/embed_tool",
-  "widget/data_slider",
-], (underscore, require) ->
-
-  locations =
-
-    Plot:                   'common/plot'
-    GMapPlot:               'common/gmap_plot'
-    GridPlot:               'common/grid_plot'
-    CDXPlotContext:         'common/plot_context'
-    PlotContext:            'common/plot_context'
-    PlotList:               'common/plot_context'
-
-    IPythonRemoteData:      'pandas/ipython_remote_data'
-    PandasPivotTable:       'pandas/pandas_pivot_table'
-    PandasPlotSource:       'pandas/pandas_plot_source'
-
-    Range1d:                'range/range1d'
-    DataRange1d:            'range/data_range1d'
-    FactorRange:            'range/factor_range'
-    DataFactorRange:        'range/data_factor_range'
-
-    Glyph:                  'renderer/glyph/glyph_factory'
-    LinearAxis:             'renderer/guide/linear_axis'
-    DatetimeAxis:           'renderer/guide/datetime_axis'
-    Grid:                   'renderer/guide/grid'
-    Legend:                 'renderer/annotation/legend'
-    BoxSelection:           'renderer/overlay/box_selection'
-
-    ObjectArrayDataSource:  'source/object_array_data_source'
-    ColumnDataSource:       'source/column_data_source'
-
-    PanTool:                'tool/pan_tool'
-    WheelZoomTool:          'tool/wheel_zoom_tool'
-    ResizeTool:             'tool/resize_tool'
-    BoxSelectTool:          'tool/box_select_tool'
-    DataRangeBoxSelectTool: 'tool/data_range_box_select_tool'
-    PreviewSaveTool:        'tool/preview_save_tool'
-    EmbedTool:              'tool/embed_tool'
-
-    DataSlider:             'widget/data_slider'
-
-  resolve_ref = (obj, ref) ->
-      # ### method: HasProperties::resolve_ref
-      #converts a reference into an object
-      #also works vectorized now
-      if _.isArray(ref)
-        return _.map(ref, (ref) -> resolve_ref(obj, ref))
-      if not ref
-        console.log('ERROR, null reference')
-      #this way we can reference ourselves
-      # even though we are not in any collection yet
-      if ref['type'] == obj.type and ref['id'] == obj.id
-        return obj
-      else
-        module_name = locations[ref['type']]
-        module = require(module_name)
-        return module.Collection.get(ref['id'])
-
-  return {
-    "resolve_ref": resolve_ref
-  }


Add Hamburg to the list for padding

Add Hamburg to the list for padding


fix top spacing

fix top spacing

--- src/smc-webapp/desktop_app.cjsx
+++ src/smc-webapp/desktop_app.cjsx
@@ -344,7 +344,7 @@
             borderRadius  : 0
             top           : 0

-        positionHackHeight = NAV_HEIGHT + 'px'
+        positionHackHeight = (NAV_HEIGHT - 26) + 'px'

         <div ref="page" style={style} onDragOver={(e) -> e.preventDefault()} onDrop={@drop}>
             {<FileUsePageWrapper /> if @props.show_file_use}


stacktrace related to mathjax and sagews -- fix https://github.com/sagemathinc/cocalc/issues/3620

--- src/smc-webapp/misc_page.coffee
+++ src/smc-webapp/misc_page.coffee
@@ -218,7 +218,12 @@

 mathjax_typeset = (el) ->
     # no MathJax.Hub, since there is no MathJax defined!
-    mathjax_enqueue(["Typeset", el])
+    try
+        mathjax_enqueue(["Typeset", el])
+    catch err
+        # This exception *does* happen sometimes -- see
+        #     https://github.com/sagemathinc/cocalc/issues/3620
+        # This is probably a bug in Mathjax, but whatever.

 $.fn.extend
     mathjax: (opts={}) ->


adopt old title when publishing

adopt old title when publishing


provide plot size info to julia

provide plot size info to julia


use selected information in datatable rendering

--- src/coffee/ui_view.coffee
+++ src/coffee/ui_view.coffee
@@ -43,7 +43,13 @@
       header.append($(html))
     table.append(header)
     rowCount = data_source.get('offset')
-    for rowdata in @mget_ref('data_source').get('data')
+    rawdata = @mget_ref('data_source').get('data')
+    if not data_source.get('selecting')
+      toiter = _.range(rawdata.length)
+    else
+      toiter = data_source.get('selected')
+    for idx in toiter
+      rowdata = rawdata[idx]
       row = $(row_template)
       datacell = $(_.template(datacell_template, {'data' : ++rowCount}))
       row.append(datacell)