Fixing date validation

Fixing date validation

--- app/assets/javascripts/frontend/form-validation.js.coffee
+++ app/assets/javascripts/frontend/form-validation.js.coffee
@@ -69,7 +69,7 @@
     question.find("input[type='checkbox']").length

   toDate: (str) ->
-    moment(str, "DD/MM/YYYY")
+    moment(str, ["DD/MM/YYYY", "D/M/YYYY", "D/MM/YYYY", "DD/M/YYYY"], true)

   compareDateInDays: (date1, date2) ->
     date = @toDate(date1)
@@ -138,9 +138,9 @@
   validateMaxDate: (question) ->
     val = question.find("input[type='number']").val()

-    questionYear = parseInt(question.find(".js-date-input-year").val())
-    questionMonth = parseInt(question.find(".js-date-input-month").val())
-    questionDay = parseInt(question.find(".js-date-input-day").val())
+    questionYear = question.find(".js-date-input-year").val()
+    questionMonth = question.find(".js-date-input-month").val()
+    questionDay = question.find(".js-date-input-day").val()
     questionDate = "#{questionDay}/#{questionMonth}/#{questionYear}"

     if not val
@@ -161,9 +161,9 @@
   validateDynamicMaxDate: (question) ->
     val = question.find("input[type='text']").val()

-    questionYear = parseInt(question.find(".js-date-input-year").val())
-    questionMonth = parseInt(question.find(".js-date-input-month").val())
-    questionDay = parseInt(question.find(".js-date-input-day").val())
+    questionYear = question.find(".js-date-input-year").val()
+    questionMonth = question.find(".js-date-input-month").val()
+    questionDay = question.find(".js-date-input-day").val()
     questionDate = "#{questionDay}/#{questionMonth}/#{questionYear}"

     if not val
@@ -195,9 +195,9 @@
   validateMinDate: (question) ->
     val = question.find("input[type='text']").val()

-    questionYear = parseInt(question.find(".js-date-input-year").val())
-    questionMonth = parseInt(question.find(".js-date-input-month").val())
-    questionDay = parseInt(question.find(".js-date-input-day").val())
+    questionYear = question.find(".js-date-input-year").val()
+    questionMonth = question.find(".js-date-input-month").val()
+    questionDay = question.find(".js-date-input-day").val()
     questionDate = "#{questionDay}/#{questionMonth}/#{questionYear}"

     if not val
@@ -218,9 +218,9 @@
   validateBetweenDate: (question) ->
     val = question.find("input[type='text']").val()

-    questionYear = parseInt(question.find(".js-date-input-year").val())
-    questionMonth = parseInt(question.find(".js-date-input-month").val())
-    questionDay = parseInt(question.find(".js-date-input-day").val())
+    questionYear = question.find(".js-date-input-year").val()
+    questionMonth = question.find(".js-date-input-month").val()
+    questionDay = question.find(".js-date-input-day").val()
     questionDate = "#{questionDay}/#{questionMonth}/#{questionYear}"

     if not val


[Frontend validations] commented 'console.logs'

--- app/assets/javascripts/frontend/form-validation.js.coffee
+++ app/assets/javascripts/frontend/form-validation.js.coffee
@@ -89,7 +89,6 @@

   validateMaxDate: (question) ->
     val = question.find("input[type='text']").val()
-    console.log("[ERROR - MaxDate] val:" + val)

     if not val
       return
@@ -99,12 +98,10 @@

     if not @toDate(val).isValid()
       @addErrorMessage(question, "Not a valid date")
-      console.log("[ERROR - MaxDate] " + question.find("h2").text())
       return

     if diff > 0
       @addErrorMessage(question, "Date cannot be after #{expDate}")
-      console.log("[ERROR - MaxDate] " + question.find("h2").text())

   validateMinDate: (question) ->
     val = question.find("input[type='text']").val()


Fix middleware order

Fix middleware order

Closes #29

--- src/exprestive.coffee
+++ src/exprestive.coffee
@@ -38,13 +38,13 @@
     # Save reverse routes
     @reverseRoutes = @options.routes

+    # Set res.locals.routes if the routes option was not set
+    @middlewareRouter.use @setReverseRoutesOnReqLocals if @optionIsDefault 'routes'
+
     # Initialize controllers and routes
     @controllers = new ControllerInitializer controllersDirPath, @options
     routes = new RoutesInitializer routesFilePath, @reverseRoutes
     @addRoute route for route in routes.toArray()
-
-    # Set res.locals.routes if the routes option was not set
-    @middlewareRouter.use @setReverseRoutesOnReqLocals if @optionIsDefault 'routes'

   # Registers a route on @middlewareRouter


Fix the handler for set!

Fix the handler for set!

--- src/extras/ast.coffee
+++ src/extras/ast.coffee
@@ -59,7 +59,7 @@

 register 'set!', (name, expression) ->
 	nodes.makeAssignment(
-		name
+		name.token.value
 		buildAst expression
 	)


Small fix to error message

Small fix to error message

--- SingularityUI/app/application.coffee
+++ SingularityUI/app/application.coffee
@@ -107,7 +107,7 @@
                     message:   "
A #{ jqxhr.statusText } error occurred while accessing:

#{ url }
"
                     hideAFter: 20
             else
-                serverMessage = jqxhr.responseJSON.message or jqxhr.responseText
+                serverMessage = JSON.parse(jqxhr.responseText)?.message or jqxhr.responseText
                 Messenger().error
                     message:   "
An uncaught error occurred with your request. The server said:

#{ serverMessage }

The error has been saved to your JS console.
"
                     hideAfter: 20


rename dumb aliases to encode, decode

rename dumb aliases to encode, decode

--- src/googleplus.coffee
+++ src/googleplus.coffee
@@ -76,24 +76,24 @@
 class QueryParameters
     constructor: -> @data = {}

+    decode = window.decodeURIComponent
+    encode = window.encodeURIComponent
+
     read: (str) ->
-        _ = decodeURIComponent
-
         if str.charAt 0 is '?'
             str = str.slice 1

         for pair in str.split '&'
             [key, value] = pair.split '='
-            @data[_(key)] = _(value)
+            @data[decode(key)] = decode(value)

         this

     write: ->
-        _ = encodeURIComponent
         pairs = []

         for key, value of @data
-            pairs.push( _(key) + '=' + _(value) )
+            pairs.push(encode(key) + '=' + encode(value))

         if pairs.length is 0
             ''


Do we really need attachToDOM?

Do we really need attachToDOM?

--- spec/about-spec.coffee
+++ spec/about-spec.coffee
@@ -51,7 +51,6 @@
     spyOn(atom, 'isReleasedVersion').andReturn(true)

     workspaceElement = atom.views.getView(atom.workspace)
-    jasmine.attachToDOM(workspaceElement)

     waitsForPromise ->
       atom.packages.activatePackage('status-bar')


added faling test to see if Travis CI correctly picks up the failure

--- spec/admin.spec.coffee
+++ spec/admin.spec.coffee
@@ -26,6 +26,9 @@
          end : jasmine.createSpy()

    describe 'urlValid', ->
+      it 'should fail', ->
+         expect(true).toBe false
+
       it 'should accept the root url', ->
          url = '/'


Tests: IDE: Helpers: Fix closeAllTabs helper.

--- client/test/lib/helpers/idehelpers.coffee
+++ client/test/lib/helpers/idehelpers.coffee
@@ -6,6 +6,7 @@
 filesTabSelector    = '.ide-files-tab .file-container'

 module.exports =
+

   openNewFile: (browser) ->

@@ -27,12 +28,19 @@
     browser.pause 5000 # wait for snapshot restore

     handleSelector = panelSelector + ' .kdtabhandle.kddraggable'
+    modalSelector  = '.autoremovepane-confirm'
+

     doClose = ->
       browser
         .moveToElement handleSelector, 5, 5
         .click         handleSelector + ' .close-tab'
-        .pause         300
+        .pause         500 # wait for modal
+        .element       'css selector', modalSelector, (result) ->
+          if result.status is 0
+            browser
+              .click   "#{modalSelector} .kdbutton.red"
+              .pause   500

     close = ->
@@ -41,6 +49,7 @@

         if result.value.length isnt 0 then doClose()
         if length - 1 > 0 then close()
+

     close()


correct units for font size

correct units for font size

--- demo/coffee/map.coffee
+++ demo/coffee/map.coffee
@@ -66,7 +66,7 @@
   type: 'text',
   text_align: 'center'
   text_baseline: 'middle'
-  text_font_size: 6
+  text_font_size: '8pt'
 }

 test('map', make_glyph_test('map', source, {}, [area, text], xdr, ydr, true, [1200, 600], false))


test(lxd): obtain image from configuration in lxd.cluster

--- packages/lxd/test/cluster/index.coffee
+++ packages/lxd/test/cluster/index.coffee
@@ -1,6 +1,6 @@

 nikita = require '@nikitajs/core/lib'
-{tags, config} = require '../test'
+{config, images, tags} = require '../test'
 they = require('mocha-they')(config)
 path = require('path')

@@ -59,7 +59,7 @@
           network: 'nktlxdpub'
         await @lxc.network.delete
           network: 'nktlxdprv'
-      @clean()
+      await @clean()
       await @lxc.cluster
         networks:
           nktlxdpub:
@@ -74,7 +74,7 @@
             'dns.domain': 'nikita.local'
         containers:
           'nikita-cluster-1':
-            image: 'images:alpine/3.11'
+            image: "images:#{images.alpine}"
             disk:
               nikitadir:
                 source: process.env['NIKITA_HOME'] or path.join(__dirname, '../../../../')
@@ -121,7 +121,7 @@
               'dns.domain': 'nikita.local'
           containers:
             'nikita-cluster-2':
-              image: 'images:alpine/3.11'
+              image: "images:#{images.alpine}"
               nic:
                 eth0: # Overwrite the default DHCP Nat enabled interface
                   name: 'eth0', nictype: 'bridged', parent: 'nktlxdprv'


editSummaryView: select editImageView link on show

handle all popover toggling manually in order to receive custom show and
hide events

--- coffee/src/views/edit_summary_view.coffee
+++ coffee/src/views/edit_summary_view.coffee
@@ -24,6 +24,7 @@
   events: => _.extend super,
     'blur input': @saveData
     'blur textarea': @saveData
+    'click .thumbnail-view': => @popoverView.toggle()

   initialize: =>
@@ -38,6 +39,10 @@
     @popoverView = new athena.lib.PopoverView
       eventhub: @eventhub
       content: @editImageView
+      trigger: 'manual'
+
+    @listenTo @popoverView, 'PopoverView:PopoverDidShow', =>
+      @editImageView.$('#link').select()

     @listenTo @editImageView, 'EditImage:Cancel', =>
       @editImageView.model.link @model.thumbnail()


#1 adds in configuration documentation to script

--- src/domain-me.coffee
+++ src/domain-me.coffee
@@ -5,7 +5,7 @@
 #   None
 #
 # Configuration:
-#   None
+#   HUBOT_DOMAIN_ME_SFW - set to TRUE if you want only safe for work words used.
 #
 # Commands:
 #   hubot domain me - returns a random domain name


add what's for lunch

add what's for lunch

--- scripts/memes.coffee
+++ scripts/memes.coffee
@@ -67,6 +67,9 @@
   robot.hear /^(what do we want|when do we want it)/i, (msg) ->
     msg.send 'BRAINS'

+  robot.respond /what's for lunch/i, (msg) ->
+    msg.send 'https://drive.google.com/open?id=0Ahnoxd3OzgVsdEJpUEhNTFpMQWFaU1p5RTZPZFE1eEE'
+
 # Stolen from google-images.coffee
 imageMe = (msg, query, animated, faces, cb) ->
   cb = animated if typeof animated == 'function'


Fixed bugs with deferred queues.

Fixed bugs with deferred queues.

--- src/EntityUtils.coffee
+++ src/EntityUtils.coffee
@@ -122,6 +122,7 @@
     df = Q.defer()
     @renderQueue.add id, =>
       @renderingEnabledDf.promise.then Meteor.bindEnvironment => df.resolve @_render(id, args)
+      df.promise
     df.promise

   _render: (id, args) ->
@@ -397,9 +398,8 @@
   unrender: (id) ->
     df = Q.defer()
     @renderQueue.add id, =>
-      @renderingEnabledDf.promise.then Meteor.bindEnvironment =>
-        AtlasManager.unrenderEntity(id)
-        df.resolve(id)
+      AtlasManager.unrenderEntity(id)
+      df.resolve(id)
     df.promise

   show: (id) ->


update slugs script

update slugs script

--- scripts/slugs.coffee
+++ scripts/slugs.coffee
@@ -41,7 +41,7 @@
       "bam-green-eggs-and-h": "green-eggs-and-ham"
       "node-group-tbd": "xyzzy"
       "tmp": "walbril"
-      "software-niagara-tea": "software-niagara"
+      "software-niagara-tea": "software-niagarateam"
       "swhite": "pimps-love"
       "front-end-developers": "fed-brazil"
       "cold-brew-rocket-fue": "brew-rocket-fuel"
@@ -54,7 +54,7 @@
       "team-awesome-1": "team-fried-chicken"
       "little-bobby-drop-ta": "bobby-drop-tables"
       "red-hot-pink-daisy-s": "red-hot-pink-daisy"
-
+


Added ability to fetch player info in matches by custom URL

--- scripts/steam.coffee
+++ scripts/steam.coffee
@@ -106,7 +106,7 @@
                 params.account_id = id
                 history params, 'profile URL'

-    robot.respond /dota match (\d+)( \d*)?/i, (msg) ->
+    robot.respond /dota match (\d+)\s*(.*)?/i, (msg) ->
         steam_request msg, "/IDOTA2Match_570/GetMatchDetails", match_id: msg.match[1], (object) ->
             match = object.result
             start = moment.unix(match.start_time).fromNow()
@@ -122,12 +122,19 @@

             communityID = if msg.match[2]? then getCommunityID(msg.match[2]) else false

-            if communityID
+            playerInfo = (communityID) ->
                 for player in match.players
                     if player.account_id is communityID
                         msg.reply "#{getHero(player.hero_id).localized_name} (Lvl #{player.level}) | KDA: #{player.kills}/#{player.deaths}/#{player.assists} | LH: #{player.last_hits} | GPM: #{player.gold_per_min} | XPM: #{player.xp_per_min}"
                         return
                 msg.reply "The Steam ID you have entered (\"#{msg.match[1]}\") was not found in Match ID #{match.match_id}."
+
+            if msg.match[2]?
+                if msg.match[2].match /\d{17}/
+                    playerInfo getCommunityID(msg.match[2])
+                else
+                    getSteamID msg, msg.match[2], (id) ->
+                        playerInfo getCommunityID(id)

 getSteamID = (msg, customURL, handler) ->
     steam_request msg, "/ISteamUser/ResolveVanityURL", vanityurl: customURL, (object) ->


fix(karma): use exec instead of spawn

fix(karma): use exec instead of spawn

Some user's machines are erroring out with spawn

--- generators/app/templates/gulpfile.coffee
+++ generators/app/templates/gulpfile.coffee
@@ -1004,7 +1004,9 @@
 gulp.task 'test', ['build'], ->
 	# launch karma in a new process to avoid blocking gulp
 	command = windowsify '.\\node_modules\\.bin\\gulp.cmd', 'gulp'
-	spawn   = childProcess.spawn command, ['karma'], {stdio: 'inherit'}
+	# spawn   = childProcess.spawn command, ['karma'], {stdio: 'inherit'}
+	exec = childProcess.exec "#{command} karma", (error, stdout, stderr) ->
+		gutil.log stdout

 # Compile TypeScript
 gulp.task 'typeScript', ['prepare'], ->


Init filesByUrl correctly with configredo

--- src/lib/docpad.coffee
+++ src/lib/docpad.coffee
@@ -923,6 +923,7 @@
 		@exchange = {}
 		@pluginsTemplateData = {}
 		@instanceConfig = {}
+		@filesByUrl = {}
 		@locales = balUtil.dereference(@locales)
 		@userConfig = balUtil.dereference(@userConfig)
 		@initialConfig = balUtil.dereference(@initialConfig)


Fixed test breakage

Fixed test breakage

--- packages/shrub-ui/notifications.coffee
+++ packages/shrub-ui/notifications.coffee
@@ -171,7 +171,7 @@
 	registrar.registerHook 'config', (req) ->

 		# Make sure ORM is up (it won't be when grunt is running).
-		return unless Notification = orm.collection 'shrub-ui-notification'
+		return unless Notification = orm?.collection 'shrub-ui-notification'

 		# Get all the notification queues.
 		promiseKeys = []


Linear interpolation of colors

Linear interpolation of colors

--- src/mandelbrot.coffee
+++ src/mandelbrot.coffee
@@ -1,15 +1,16 @@
 canvas = document.getElementById 'canvas'
 ctx = canvas.getContext '2d'
+image = ctx.createImageData canvas.width, canvas.height

-image = ctx.createImageData canvas.width, canvas.height
 w = image.width
 h = image.height
-
+num_pixels = w * h
 max_itr = 1000

+ms = new Array num_pixels
+hgram = (0 for i in [0...max_itr])
+
 palette = chroma.scale('RdYlBu')
-  .domain([0, max_itr], max_itr + 1)
-  .mode('lab')

 mandelbrot = (px, py) ->
   x0 = px / w * 3.5 - 2.5
@@ -28,17 +29,29 @@
     itr = itr + 1 - nu
   itr - 1

+render = (idx) ->
+  level = Math.floor ms[idx] + 1
+  phue = hue = 0
+  for i in [0..level]
+    phue = hue
+    hue += hgram[i]
+  c1 = palette(phue / num_pixels)
+  c2 = palette(hue / num_pixels)
+  c = chroma(chroma.interpolate c1, c2, ms[idx] % 1)
+  [r, g, b] = c.rgb()
+  image.data[idx * 4 + 0] = r
+  image.data[idx * 4 + 1] = g
+  image.data[idx * 4 + 2] = b
+  image.data[idx * 4 + 3] = 255
+
 for y in [0...h]
   for x in [0...w]
     m = mandelbrot x, y
-    c1 = palette Math.floor m
-    c2 = palette Math.floor m + 1
-    c = chroma(chroma.interpolate c1, c2, m % 1, 'lab')
-    [r, g, b] = c.rgb()
-    idx = (y * w + x) * 4
-    image.data[idx + 0] = r
-    image.data[idx + 1] = g
-    image.data[idx + 2] = b
-    image.data[idx + 3] = 255
+    ms[y * w + x] = m
+    hgram[Math.round m]++
+
+for i in [0...ms.length]
+  render i
+

 ctx.putImageData image, 0, 0


WIP: get package index from cran.

WIP: get package index from cran.

--- indexer.coffee
+++ indexer.coffee
@@ -114,17 +114,36 @@
 					url: null
 					command: "sudo apt-get install #{name}"
 				}
-		return _.without(packages, undefined)
+		result = {}
+		for p in _.without(packages, undefined)
+			result[p.name] = p

 	getRemoteCranPackages: (callback) ->
 		command_out = child_process.execSync """
-				echo 'write.table(available.packages(), sep=",")' | R --no-save --slave
+				echo 'write.table(available.packages(fields=c("Description", "Title")), sep=",")' | R --no-save --slave
 		"""
 		csv_data = command_out.toString()
-		CsvParse csv_data, {delimiter: ',', columns: true}, (err, table) ->
+		CsvParse csv_data, {delimiter: ',', columns: true}, (err, parsed) ->
+			console.log parsed[0]
 			if err
-				throw err
-			console.log table.slice(0, 4)
+				callback err, null
+			packages = parsed.map (row) ->
+				name: row.Package
+				summary: row.Title
+				description: row.Description
+				url: "https://cran.rstudio.com/web/packages/#{row.Package}/index.html"
+				command: """
+					sudo Rscript -e '
+						installPackages(#{row.Package});
+						suppressMessages(suppressWarnings(if(!require('#{row.Package}')) {
+							stop('Could not load package', call.=FALSE)
+						}))
+				"""
+			result = {}
+			for p in packages
+				result[p.name] = p
+
+			callback null, result

 	build: (callback) ->


refactor: enhance how to open a link in a new tab, as introduced in 4c200f8

--- app/assets/javascripts/init.coffee
+++ app/assets/javascripts/init.coffee
@@ -102,7 +102,15 @@
       else if e.metaKey # SOURCE: http://stackoverflow.com/a/12852471
         # Open in new tab
         # SOURCE: http://stackoverflow.com/a/28374344
-        $("<a target='_blank'>").attr("href", $(this).data('href'))[0].click()
+        # NOTE: From a comment in http://stackoverflow.com/a/28374344:
+        #       "Any action like this - if not triggered by user action
+        #       like mouse click will turn popup-blocker. Imagine that
+        #       you can just open any url with Javascript without user
+        #       action - that would be quite dangerous. If you put
+        #       this code inside event like click function - it will
+        #       work fine - it's the same with all proposed solutions
+        #       here"
+        window.open($(this).data('href'), '_blank');
       else
         document.location = $(this).data('href')


Store classifier scale in state, update on load and resize

--- app/classifier/subject-viewer.cjsx
+++ app/classifier/subject-viewer.cjsx
@@ -20,21 +20,23 @@
   getInitialState: ->
     naturalWidth: 0
     naturalHeight: 0
+    scale:
+      horizontal: 1
+      vertical: 1
     showWarning: false
     frame: 0
     selectedMark: null
     detailsTooltipOffset: ''

-  getScale: ->
-    ALMOST_ZERO = 0.01 # Prevent divide-by-zero errors when there is no image.
-    rect = @refs.sizeRect?.getDOMNode().getBoundingClientRect()
-    horizontal = (rect?.width || ALMOST_ZERO) / (@state.naturalWidth || ALMOST_ZERO)
-    vertical = (rect?.height || ALMOST_ZERO) / (@state.naturalHeight || ALMOST_ZERO)
-    {horizontal, vertical}
+  componentDidMount: ->
+    addEventListener 'resize', @handleResize
+
+  componentWillUnmount: ->
+    removeEventListener 'resize', @handleResize

   getEventOffset: (e) ->
     rect = @refs.sizeRect.getDOMNode().getBoundingClientRect()
-    scale = @getScale()
+    scale = @state.scale
     x = (e.pageX - pageXOffset - rect.left) / scale.horizontal
     y = (e.pageY - pageYOffset - rect.top) / scale.vertical
     {x, y}
@@ -47,7 +49,7 @@
     setTimeout (=> @refs.detailsTooltip?.forceUpdate()), 100

   render: ->
-    scale = @getScale()
+    scale = @state.scale

     <div className="subject-area">
       <SubjectViewer subject={@props.subject} frame={@state.frame} onLoad={@handleSubjectFrameLoad} onFrameChange={@handleFrameChange}>
@@ -149,8 +151,10 @@
   handleSubjectFrameLoad: (e) ->
     if e.target.tagName.toUpperCase() is 'IMG'
       {naturalWidth, naturalHeight} = e.target
-      unless @state.naturalWidth is naturalWidth and @state.naturalHeight is naturalHeight
-        @setState {naturalWidth, naturalHeight}
+      if @state.naturalWidth is naturalWidth and @state.naturalHeight is naturalHeight
+        @handleResize()
+      else
+        @setState {naturalWidth, naturalHeight}, @handleResize
       @props.onLoad? arguments...

   handleFrameChange: (frame) ->
@@ -162,6 +166,16 @@
   updateAnnotations: ->
     @props.classification.update
       annotations: @props.classification.annotations
+
+  handleResize: ->
+    ALMOST_ZERO = 0.01 # Prevent divide-by-zero errors when there is no image.
+    rect = @refs.sizeRect?.getDOMNode().getBoundingClientRect()
+    scale =
+      horizontal: (rect?.width || ALMOST_ZERO) / (@state.naturalWidth || ALMOST_ZERO)
+      vertical: (rect?.height || ALMOST_ZERO) / (@state.naturalHeight || ALMOST_ZERO)
+
+    unless scale.horizontal is @state.scale.horizontal and scale.vertical is @state.scale.vertical
+      @setState {scale}

   handleInitStart: (e) ->
     taskDescription = @props.workflow.tasks[@props.annotation.task]


Credentials: showDetails use <pre> instead <code>

--- client/Account/views/credentials.coffee
+++ client/Account/views/credentials.coffee
@@ -187,6 +187,7 @@
     credential = item.getData()
     @emit "ShowShareCredentialFormFor", credential

+
   showItemContent: (item)->

     credential = item.getData()
@@ -204,7 +205,8 @@
             title: "An error occured"

         new KDModalView
-          content : "#{cred}"
+          content : "
#{cred}
"
+

 class AccountCredentialListItem extends KDListItemView


make photo processing lighter for cpu

make photo processing lighter for cpu

--- client/app/models/photoprocessor.coffee
+++ client/app/models/photoprocessor.coffee
@@ -53,6 +53,7 @@
 # create photo.thumb_du : a DataURL encoded thumbnail of photo.img
 makeThumbDataURI = (photo, next) ->
     photo.thumb_du = resize photo, 300, 300, true
+    photo.trigger 'thumbed'
     next()

 # create photo.screen_du : a DataURL encoded thumbnail of photo.img
@@ -107,19 +108,25 @@
         (cb) -> makeThumbDataURI photo, cb
         (cb) ->
             delete photo.img
-            cb()
+            setTimeout cb, 200
     ], (err) ->
         if err
             photo.trigger 'upError', err
         else
             photo.trigger 'thumbed'

-        done(err)
+        if err
+            done err
+        else
+            console.log photo
+            uploadWorker photo, done
+        #done(err)

 # make screen sized version and upload
 uploadWorker = (photo, done) ->
     async.waterfall [
         (cb) -> readFile          photo, cb
+        (cb) -> makeThumbDataURI photo, cb
         (cb) -> makeScreenDataURI photo, cb
         (cb) -> makeScreenBlob    photo, cb
         (cb) -> makeThumbBlob     photo, cb
@@ -133,7 +140,7 @@
             delete photo.thumb_du
             delete photo.scren
             delete photo.screen_du
-            cb()
+            setTimeout cb, 200
     ], (err) ->
         if err
             photo.trigger 'upError', err
@@ -146,15 +153,15 @@
 class PhotoProcessor

     # create thumbs 3 by 3
-    thumbsQueue: async.queue makeThumbWorker, 3
+    #thumbsQueue: async.queue makeThumbWorker, 2

     # upload 2 by 2
     uploadQueue: async.queue uploadWorker, 2

     process: (photo) ->
-        @thumbsQueue.push photo, (err) =>
-            return console.log err if err
-            @uploadQueue.push photo, (err) =>
-                return console.log err if err
+        #@thumbsQueue.push photo, (err) =>
+        #    return console.log err if err
+        @uploadQueue.push photo, (err) =>
+             return console.log err if err

 module.exports = new PhotoProcessor()


Fixed: search input auto-size was not working properly on Firefox

--- client/app/views/breadcrumb_view.coffee
+++ client/app/views/breadcrumb_view.coffee
@@ -146,7 +146,7 @@

     adjustInputSize: (evt) =>
         inputEl = $ evt.currentTarget
-        key = evt.keyCode
+        key = evt.keyCode or evt.charCode
         char = String.fromCharCode key
         inputVal = inputEl.val()


fix route when clickAdd

fix route when clickAdd

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -6,7 +6,7 @@
     @options = options
     @options.order = [ 0, 'asc' ]
     @options.order = options.order if options.order != undefined
-    @options.addUrl = appRouter.generateUrl('addEntity', entityType: @options.entityType)
+    @addUrl = appRouter.generateUrl('addEntity', entityType: @options.entityType)
     key = 'click a.ajax-add-' + @cid
     @events[key] = 'clickAdd'
     _.bindAll this, "render"


fix log ordering

fix log ordering

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -9,8 +9,7 @@

   initialize: (options) ->
     @options = options
-    @options.order = [ 0, 'asc' ]
-    @options.order = options.order if options.order != undefined
+    @options.order = [ 0, 'asc' ] if @options.order == undefined
     @addUrl = appRouter.generateUrl('addEntity', entityType: @options.entityType)
     key = 'click a.ajax-add-' + @cid
     @events[key] = 'clickAdd'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/animation.coffee
+++ test/animation.coffee
@@ -12,7 +12,7 @@
 asyncTest "Animation should increment frame on update", ->
   animation = Animation.fromPixieId 45

-  milliseconds = 300
+  milliseconds = 30

   setTimeout ->
     animation.update()


[pro-merge] Add application configuration hooks for pro

--- assets/scripts/app/app.coffee
+++ assets/scripts/app/app.coffee
@@ -145,3 +145,45 @@
     currentDate: ->
       new Date()
   )
+
+  onUserUpdate: (user) ->
+    if Travis.config.pro
+      @identifyCustomer(user)
+      @subscribePusher(user)
+      @setupCharm(user)
+
+  subscribePusher: (user) ->
+    channels = user.channels
+    channels = channels.map (channel) ->
+      if channel.match /^private-/
+        channel
+      else
+        "private-#{channel}"
+    Travis.pusher.subscribeAll(channels)
+
+  setupCharm: (user) ->
+    $.extend window.__CHARM,
+      customer: user.login,
+      customer_id: user.id,
+      email: user.email
+
+  displayCharm: ->
+    __CHARM.show()
+
+  identifyCustomer: (user) ->
+    if _cio && _cio.identify
+      _cio.identify
+        id: user.id
+        email: user.email
+        name: user.name
+        created_at: (Date.parse(user.created_at) / 1000) || null
+        login: user.login
+
+    @on 'user:signed_in', (user) ->
+      Travis.onUserUpdate(user)
+
+    @on 'user:synced', (user) ->
+      Travis.onUserUpdate(user)
+
+    @_super.apply this, arguments
+


Update project

Update project

--- src/app/project.coffee
+++ src/app/project.coffee
@@ -24,7 +24,21 @@
   editSessions: null
   ignoredPathRegexes: null

-  # Public: Establishes a new project at a given path.
+  ### Internal ###
+
+  serialize: ->
+    deserializer: 'Project'
+    path: @getPath()
+
+  @deserialize: (state) ->
+    new Project(state.path)
+
+  destroy: ->
+    editSession.destroy() for editSession in @getEditSessions()
+
+  ### Public ###
+
+  # Establishes a new project at a given path.
   #
   # path - The {String} name of the path
   constructor: (path) ->
@@ -32,27 +46,13 @@
     @editSessions = []
     @buffers = []

-  ### Internal ###
-
-  serialize: ->
-    deserializer: 'Project'
-    path: @getPath()
-
-  @deserialize: (state) ->
-    new Project(state.path)
-
-  destroy: ->
-    editSession.destroy() for editSession in @getEditSessions()
-
-  ### Public ###
-
-  # Public: Retrieves the project path.
+  # Retrieves the project path.
   #
   # Returns a {String}.
   getPath: ->
     @rootDirectory?.path

-  # Public: Sets the project path.
+  # Sets the project path.
   #
   # path - A {String} representing the new path
   setPath: (path) ->
@@ -66,13 +66,13 @@

     @trigger "path-changed"

-  # Public: Retrieves the name of the root directory.
+  # Retrieves the name of the root directory.
   #
   # Returns a {String}.
   getRootDirectory: ->
     @rootDirectory

-  # Public: Retrieves the names of every file (that's not `git ignore`d) in the project.
+  # Retrieves the names of every file (that's not `git ignore`d) in the project.
   #
   # Returns an {Array} of {String}s.
   getFilePaths: ->
@@ -84,7 +84,7 @@
     deferred.resolve(paths)
     deferred.promise()

-  # Public: Identifies if a path is ignored.
+  # Identifies if a path is ignored.
   #
   # path - The {String} name of the path to check
   #
@@ -96,7 +96,7 @@

     @ignoreRepositoryPath(path)

-  # Public: Identifies if a path is ignored.
+  # Identifies if a path is ignored.
   #
   # path - The {String} name of the path to check
   #
@@ -104,7 +104,7 @@
   ignoreRepositoryPath: (path) ->
     config.get("core.hideGitIgnoredFiles") and git?.isPathIgnored(fsUtils.join(@getPath(), path))

-  # Public: Given a path, this resolves it relative to the project directory.
+  # Given a path, this resolves it relative to the project directory.
   #
   # filePath - The {String} name of the path to convert
   #
@@ -113,7 +113,7 @@
     filePath = fsUtils.join(@getPath(), filePath) unless filePath[0] == '/'
     fsUtils.absolute filePath

-  # Public: Given a path, this makes it relative to the project directory.
+  # Given a path, this makes it relative to the project directory.
   #
   # filePath - The {String} name of the path to convert
   #
@@ -122,27 +122,27 @@
     return fullPath unless fullPath.lastIndexOf(@getPath()) is 0
     fullPath.replace(@getPath(), "").replace(/^\//, '')

-  # Public: Identifies if the project is using soft tabs.
+  # Identifies if the project is using soft tabs.
   #
   # Returns a {Boolean}.
   getSoftTabs: -> @softTabs

-  # Public: Sets the project to use soft tabs.
+  # Sets the project to use soft tabs.
   #
   # softTabs - A {Boolean} which, if `true`, sets soft tabs
   setSoftTabs: (@softTabs) ->

-  # Public: Identifies if the project is using soft wrapping.
+  # Identifies if the project is using soft wrapping.
   #
   # Returns a {Boolean}.
   getSoftWrap: -> @softWrap

-  # Public: Sets the project to use soft wrapping.
+  # Sets the project to use soft wrapping.
   #
   # softTabs - A {Boolean} which, if `true`, sets soft wrapping
   setSoftWrap: (@softWrap) ->

-  # Public: Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
+  # Given a path to a file, this constructs and associates a new `EditSession`, showing the file.
   #
   # filePath - The {String} path of the file to associate with
   # editSessionOptions - Options that you can pass to the `EditSession` constructor
@@ -154,51 +154,21 @@
     else
       @buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)

-  # Public: Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
+  # Retrieves all the {EditSession}s in the project; that is, the `EditSession`s for all open files.
   #
   # Returns an {Array} of {EditSession}s.
   getEditSessions: ->
     new Array(@editSessions...)

-  ### Internal ###
-
-  buildEditSessionForBuffer: (buffer, editSessionOptions) ->
-    options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
-    options.project = this
-    options.buffer = buffer
-    editSession = new EditSession(options)
-    @editSessions.push editSession
-    @trigger 'edit-session-created', editSession
-    editSession
-
-  defaultEditSessionOptions: ->
-    tabLength: @tabLength
-    softTabs: @getSoftTabs()
-    softWrap: @getSoftWrap()
-
-  eachEditSession: (callback) ->
-    callback(editSession) for editSession in @getEditSessions()
-    @on 'edit-session-created', (editSession) -> callback(editSession)
-
-  eachBuffer: (args...) ->
-    subscriber = args.shift() if args.length > 1
-    callback = args.shift()
-
-    callback(buffer) for buffer in @getBuffers()
-    if subscriber
-      subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer)
-    else
-      @on 'buffer-created', (buffer) -> callback(buffer)
-
   ### Public ###

-  # Public: Removes an {EditSession} association from the project.
+  # Removes an {EditSession} association from the project.
   #
   # Returns the removed {EditSession}.
   removeEditSession: (editSession) ->
     _.remove(@editSessions, editSession)
-
-  # Public: Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
+
+  # Retrieves all the {Buffer}s in the project; that is, the buffers for all open files.
   #
   # Returns an {Array} of {Buffer}s.
   getBuffers: ->
@@ -207,7 +177,7 @@
       buffers.push editSession.buffer
     buffers

-  # Public: Given a file path, this retrieves or creates a new {Buffer}.
+  # Given a file path, this retrieves or creates a new {Buffer}.
   #
   # If the `filePath` already has a `buffer`, that value is used instead. Otherwise,
   # `text` is used as the contents of the new buffer.
@@ -225,7 +195,7 @@
     else
       @buildBuffer(null, text)

-  # Public: Given a file path, this sets its {Buffer}.
+  # Given a file path, this sets its {Buffer}.
   #
   # filePath - A {String} representing a path
   # text - The {String} text to use as a buffer
@@ -237,13 +207,13 @@
     @trigger 'buffer-created', buffer
     buffer

-  # Public: Removes a {Buffer} association from the project.
+  # Removes a {Buffer} association from the project.
   #
   # Returns the removed {Buffer}.
   removeBuffer: (buffer) ->
     _.remove(@buffers, buffer)

-  # Public: Performs a search across all the files in the project.
+  # Performs a search across all the files in the project.
   #
   # regex - A {RegExp} to search with
   # iterator - A {Function} callback on each file found
@@ -299,4 +269,34 @@
     new BufferedProcess({command, args, stdout, exit})
     deferred

+  ### Internal ###
+
+  buildEditSessionForBuffer: (buffer, editSessionOptions) ->
+    options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
+    options.project = this
+    options.buffer = buffer
+    editSession = new EditSession(options)
+    @editSessions.push editSession
+    @trigger 'edit-session-created', editSession
+    editSession
+
+  defaultEditSessionOptions: ->
+    tabLength: @tabLength
+    softTabs: @getSoftTabs()
+    softWrap: @getSoftWrap()
+
+  eachEditSession: (callback) ->
+    callback(editSession) for editSession in @getEditSessions()
+    @on 'edit-session-created', (editSession) -> callback(editSession)
+
+  eachBuffer: (args...) ->
+    subscriber = args.shift() if args.length > 1
+    callback = args.shift()
+
+    callback(buffer) for buffer in @getBuffers()
+    if subscriber
+      subscriber.subscribe this, 'buffer-created', (buffer) -> callback(buffer)
+    else
+      @on 'buffer-created', (buffer) -> callback(buffer)
+
 _.extend Project.prototype, EventEmitter


take the default key from the user's home directory

--- src/kite-server/kite-server.coffee
+++ src/kite-server/kite-server.coffee
@@ -127,7 +127,7 @@
       @kontrol.register(url: kiteURL).then =>
         @emit 'info', "Registered to Kontrol with URL: #{ kiteURL }"

-  normalizeKiteKey: Promise.method (src, enc = "utf-8") -> switch
+  normalizeKiteKey: Promise.method (src = @defaultKiteKey(), enc = "utf-8") -> switch
     when 'string' is typeof src
       fs.readFileAsync(src, enc)
         .catch (KiteError.codeIs 'ENOENT'), (err) -> src
@@ -136,6 +136,9 @@
     else throw new Error """
       Don't know how to normalize the kite key: #{ src }
       """
+
+  defaultKiteKey: ->
+    joinPath process.env.HOME, '.kite/kite.key'

   onConnection: (ws) ->
     proto = dnodeProtocol @api


Removed position from TileView

Removed position from TileView

--- app/scripts/views/tile_view.coffee
+++ app/scripts/views/tile_view.coffee
@@ -4,7 +4,7 @@
   HEIGHT : 75

   el: no
-  template: _.template("<div class=\"tile tile_<%= type %>\"><%= position %>
")
+  template: _.template("<div class=\"tile tile_<%= type %>\">
")
   events:
     'touchstart' : 'dragTileStart'
     'touchmove'  : 'dragTileMove'


HistoryManager: Fixed typo.

HistoryManager: Fixed typo.

Consistent typo detected!

--- src/lib/core/HistoryManager.coffee
+++ src/lib/core/HistoryManager.coffee
@@ -1,5 +1,5 @@
 goog.provide 'spark.core.HistoryManager'
-goog.provide 'spark.core.HistorManager.TokenTransformer'
+goog.provide 'spark.core.HistoryManager.TokenTransformer'

 goog.require 'spark.core.Object'
 goog.require 'goog.History'
@@ -36,7 +36,7 @@
     {useHtml5History} = options

     if isHtml5HistorySupported and useHtml5History
-      tokenTransformer = new spark.core.HistorManager.TokenTransformer
+      tokenTransformer = new spark.core.HistoryManager.TokenTransformer
       @history = new goog.history.Html5History null, tokenTransformer
       @history.setPathPrefix  options.pathPrefix
       @history.setUseFragment no
@@ -71,7 +71,7 @@
   @constructor
   @implements {goog.history.Html5History.TokenTransformer}
 ###
-class spark.core.HistorManager.TokenTransformer
+class spark.core.HistoryManager.TokenTransformer

   ###*
     @override


Fix checking for function-ness in Reqwest adapter

--- src/batman.solo.coffee
+++ src/batman.solo.coffee
@@ -296,7 +296,7 @@
 param = (a) ->
   s = []
   add = (key, value) ->
-    value = (if Batman.typeOf(value) is 'function' then value() else value)
+    value = value() if typeof value is 'function'
     s[s.length] = encodeURIComponent(key) + "=" + encodeURIComponent(value)

   if Batman.typeOf(a) is 'Array'


Hide the fixed-header whenever we switch to a  new tab

--- frontend/coffeescript/app/viz/raw_view.coffee
+++ frontend/coffeescript/app/viz/raw_view.coffee
@@ -78,6 +78,8 @@
             @

         switch_view: ( view ) ->
+            $( '#fixed-header' ).hide()
+
             # Hide the currently selected view
             @currentView.$el.hide()


setCur成功后应该将停止当前播放

setCur成功后应该将停止当前播放

--- src/js/player.coffee
+++ src/js/player.coffee
@@ -216,10 +216,12 @@
         ###
         setCur: (sid) ->
             pl = @playlist
-            if sid
+            if not sid and @getSongsNum()
+                sid = pl.list[0]
+            if sid and @_sid isnt sid
                 pl.setCur(sid)
-            else if @getSongsNum()
-                pl.setCur(pl.list[0])
+                @_sid = sid
+                @stop()
             @

         ###*


Add ability to remove terms from list

Add ability to remove terms from list

--- src/businesscat.coffee
+++ src/businesscat.coffee
@@ -19,10 +19,18 @@
 images = require './data/images.json'
 jargon = require './data/triggers.json'

+removeTerm = (term, arrayToDeleteFrom) ->
+  index = arrayToDeleteFrom.indexOf term
+  if index > -1
+    arrayToDeleteFrom.splice index, 1
+    return arrayToDeleteFrom
+
 if process.env.HUBOT_BUSINESS_CAT_JARGON?
   additionalJargon = (process.env.HUBOT_BUSINESS_CAT_JARGON).split(',')
+  omittedJargon = (process.env.HUBOT_BUSINESS_CAT_OMITTED_JARGON).split(',')
   jargon = jargon.concat(additionalJargon)
-
+  jargon = removeTerm(term, jargon) for term in omittedJargon
+
 regex = new RegExp jargon.join('|'), 'gi'

 module.exports = (robot) ->


more nuanced handling of medium display

more nuanced handling of medium display

--- components/filter2/headline/view.coffee
+++ components/filter2/headline/view.coffee
@@ -15,7 +15,7 @@
     @listenTo @params, "change:for_sale", @setHeadline, @

   setHeadline: ->
-    if @anyFacetsSelected()
+    if @anyFacetsSelected() || @stuckFacet
       @$el.text(@paramsToHeading()).show()
     else
       @$el.text("").hide()
@@ -32,11 +32,18 @@
   anyFacetsSelected: ->
     @params.get('for_sale') is 'true' || _.any @facets, (facet) => @params.has facet

+  humanizeMedium: ->
+    # replace the 'slash' in 'film-slash-video'
+    _s.humanize(@params.get('medium')).replace('slash', '/')
+
   displayMedium: ->
     if @stuckFacet
-      @stuckFacet.get('name')
+      if @params.has('medium')
+        @humanizeMedium()
+      else
+        @stuckFacet.get('name')
     else
-      @facetName('medium') || 'Artworks'
+      @humanizeMedium() || 'Artworks'

   displayForSale: ->
     "For Sale" if @params.get('for_sale') is 'true'


quickfix for keyboard

quickfix for keyboard

--- app/assets/javascripts/keyboard.coffee
+++ app/assets/javascripts/keyboard.coffee
@@ -1,3 +1,5 @@
+controllKeys = [37, 38, 39, 40, 65, 68, 83, 87, 89, 81, 70, 67]
+
 KEY_LEFT = 37
 KEY_UP = 38
 KEY_RIGHT = 39
@@ -43,7 +45,8 @@
     if @onChange isnt null
       countKeysDown = 0
       for i in [0..127]
-        countKeysDown++ if keysDown[i] is true
+        if keysDown[i] is true
+          countKeysDown++  if _.indexOf(controllKeys, i) isnt -1

       @onChange countKeysDown


Labeling operators including delims in blue

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -152,6 +152,9 @@
       ['brace', token in ['{', '}']]
       ['bare', true]

+isDelim = (token) ->
+  /[\(\)\[\]\{\}]/.test token.token
+
 labelMatches = (ast) ->
   macro 'match', ast, (node, args) ->
     [keyword, over, patterns...] = args
@@ -160,12 +163,12 @@
     node

 isReference = (token) ->
-  token.label in ['bare', 'operator', 'param', 'recurse']
+  token.label in ['bare', 'operator', 'param', 'recurse'] and not isDelim token

 fnDefinition = (node) ->
   words = inside node
   [keyword, paramList, defs...] = words
-  params = if Array.isArray(paramList) then inside paramList else undefined
+  params = if Array.isArray(paramList) then paramList else undefined
   defs ?= []
   for def, i in defs
     if matchNode '::', def
@@ -224,6 +227,7 @@
 labelNames = (pattern) ->
   if Array.isArray pattern
     if isList(pattern) or isTuple pattern
+      pattern.type = 'pattern'
       (inside pattern).map labelNames
     else
       [op, args...] = inside pattern
@@ -240,7 +244,8 @@
   return

 labelParams = (ast, params) ->
-  paramNames = (token for {token} in params)
+  params.type = 'pattern'
+  paramNames = (token for {token} in inside params)
   mapTokens paramNames, ast, (word) ->
     word.label = 'param' if isReference word

@@ -280,9 +285,14 @@

 labelOperators = (ast) ->
   walk ast, (node) ->
-    [paren, operator] = node
-    if paren.token == '(' and not operator.type and operator.label is 'bare'
-      operator.label = 'operator'
+    [openDelim, operator] = node
+    if openDelim.token is '('
+      if not operator.type and operator.label is 'bare'
+        operator.label = 'operator'
+    else if node.type isnt 'pattern'
+      [openDelim, ..., closeDelim] = node
+      openDelim.label = 'operator'
+      closeDelim.label = 'operator'
     node

 apply = (onto, fns...) ->
@@ -567,7 +577,7 @@
   {params, body, wheres} = fnDefinition node
   if !params?
     throw new Error 'missing parameter list'
-  paramNames = params.map(getToken).map(validIdentifier)
+  paramNames = (inside params).map(getToken).map(validIdentifier)
   node.scope = toMap paramNames
   curry paramNames, """
     (function (#{paramNames.join ', '}) {


Fix the last lifting change

Fix the last lifting change

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2336,7 +2336,7 @@

 addSiblingDefines = (ctx, nameSet) ->
   auxiliaries = ctx.auxiliaries()
-  arrayToSet concat (for name of values auxiliaries
+  arrayToSet concat (for name of values nameSet
     (lookupInMap auxiliaries, name)?.defines)

 findDeps = (ctx) -> (names) ->


JS format date

JS format date

--- app/assets/javascripts/views.js.coffee
+++ app/assets/javascripts/views.js.coffee
@@ -28,6 +28,19 @@

     get_token: ->
       $('meta[name="csrf-token"]').attr('content')
+
+    leading_zero: (integer)->
+      if integer < 10 then "0" + integer else integer
+
+    format_date: (date)->
+      d = new Date(date)
+      day = @leading_zero(d.getDay() + 1)
+      month = @leading_zero(d.getMonth() + 1)
+      year = d.getFullYear()
+      hours = @leading_zero(d.getHours() + 1)
+      minutes = @leading_zero(d.getMinutes() + 1)
+      secondes = @leading_zero(d.getSeconds() + 1)
+      "#{day}/#{month}/#{year}, #{hours}:#{minutes}:#{secondes}"

     member: (members)->
       member = (member for member in members when member.nickname is Blabbr.current_user.nickname)
@@ -176,7 +189,7 @@
       @post.current = @post.creator_n is Blabbr.current_user.nickname
       @post.published = @post.state is 'published'
       @post.deleted = @post.state is 'deleted'
-      @post.created_at = new Date(@post.created_at).toDateString()
+      @post.created_at = @format_date(@post.created_at)
       super

     template: (post)->
@@ -270,6 +283,7 @@

     template_item: (topic)->
       topic.member = @member(topic.members)
+      topic.posted_at = @format_date(topic.posted_at)
       ich.topic_item topic

     paginate: (selector)->


Yet again fixed compiling expression by adding semi colon and dont try to cache it

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -2235,9 +2235,9 @@
 iife = (body) ->
   # """(function(){
   #     #{body}}())"""
-  (jsCall (jsFunction
-    params: []
-    body: body), [])
+  (jsWrap (jsCall (jsFunction
+      params: []
+      body: body), []))

 varList = (varNames) ->
   # "var #{listOf varNames};"
@@ -3188,6 +3188,13 @@
 jsVarDeclarationsTranslate = ({names}) ->
   "var #{listOf names};"

+
+jsWrap = (value) ->
+  {js: jsWrapTranslate, value}
+
+jsWrapTranslate = ({value}) ->
+  "(#{value})"
+
 blockOfLines = (lines) ->
   if lines.length is 0
     ''
@@ -4767,7 +4774,7 @@
     [expression] = _terms ast
     {js} = compileCtxAstToJs topLevelExpression, ctx, expression
     (finalizeTypes ctx, expression)
-    js: library + immutable + (listOfLines map lookupJs, (reverse modules)) + '\n' + js
+    js: library + immutable + (listOfLines map lookupJs, (reverse modules)) + '\n;' + js
     ast: ast
     errors: checkTypes ctx

@@ -4921,7 +4928,7 @@
   types: ctx._scope()
   subs: ctx.substitution.fails
   ast: ast
-  compiled: library + immutable + compiledDefinitions.js + '\n' + compiledExpression.js
+  compiled: library + immutable + compiledDefinitions.js + '\n;' + compiledExpression.js

 typeEnumaration = (ctx) ->
   values mapMap _type, ctx._scope()


drawing root autoFocus syntax fix

drawing root autoFocus syntax fix

--- app/classifier/drawing-tools/root.cjsx
+++ app/classifier/drawing-tools/root.cjsx
@@ -66,7 +66,7 @@
           {for detailTask, i in toolProps.details
             detailTask._key ?= Math.random()
             TaskComponent = tasks[detailTask.type]
-            <TaskComponent autoFocus={true if i is 0} key={detailTask._key} task={detailTask} annotation={toolProps.mark.details[i]} onChange={@handleDetailsChange.bind this, i} />}
+            <TaskComponent autoFocus={i is 0} key={detailTask._key} task={detailTask} annotation={toolProps.mark.details[i]} onChange={@handleDetailsChange.bind this, i} />}

             <button type="submit" className="standard-button" disabled={not detailsAreComplete}>OK</button>


ゲームオーバー判定を修正

ゲームオーバー判定を修正

--- src/coffee/kana.coffee
+++ src/coffee/kana.coffee
@@ -94,6 +94,20 @@
 		cell_a.completed = cell_b.completed
 		cell_b.completed = tmp

+	# 手詰まりか
+	# いずれの条件も満さない場合に真
+	#  (1) 配列内に空きがある
+	#  (2) 言葉になる組合せがある
+	isInDeadlock: ->
+		for i in [0...@cells.length - 1]
+			cell = @cells[i]
+			if (cell.isEmpty())
+				return false
+			cell_b = @cells[i+1]
+			if @comparator.compare(cell.kana, cell_b.kana)
+				return false
+		return true
+
 class KanaRow extends KanaGroup
 	constructor: (@size, @comparator) ->
 		super @size
@@ -258,14 +272,11 @@
 		# (2) 空セルがある
 		# (3) 単語になる組み合わせがある
 		for row in @rows
-			for i in [0...row.cells.length]
-				cell = row.cells[i]
-				if cell.isEmpty()
-					return true
-				else if i < row.cells.length - 1
-					cell_b = row.cells[i+1]
-					if @comparator.compare(cell.kana, cell_b.kana)
-						return true
+			if ! row.isInDeadlock()
+				return true
+		for col in @cols
+			if ! col.isInDeadlock()
+				return true
 		return false

 	updateState: (moved) ->


Barras com percentual do total agora começam do zero

Fixes #26

--- app/assets/javascripts/angular_app/directives/percentual_graph.js.coffee
+++ app/assets/javascripts/angular_app/directives/percentual_graph.js.coffee
@@ -9,7 +9,7 @@
       percentual: calculatePercentual(year.amount, total)
     percentuals = years.map (year) -> year.percentual
     scale = d3.scale.linear()
-              .domain([min(percentuals), max(percentuals)])
+              .domain([0, max(percentuals)])
               .range([1, 100])
     years.map (year) ->
       year.height = scale(year.percentual)


Improve error handling (if object is not already on client).

--- app/assets/javascripts/app/controllers/_application_controller.js.coffee
+++ app/assets/javascripts/app/controllers/_application_controller.js.coffee
@@ -413,7 +413,7 @@
       item.object = item.object.replace("::", '')

       # lookup real data
-      if App[item.object]
+      if App[item.object] && App[item.object].exists( item.o_id )
         object            = App[item.object].find( item.o_id )
         item.link         = object.uiUrl()
         item.title        = object.displayName()


Fixed issue #3198 - Unable to open ticket/content not loading.

--- app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee
@@ -22,7 +22,7 @@
        @permissionCheck('admin') || ticket.currentView() is 'agent'
       new App.ControllerForm(
         elReplace:      @el
-        model:          { configure_attributes: @formMeta.configure_attributes }
+        model:          { configure_attributes: @formMeta.configure_attributes || App.Ticket.configure_attributes }
         screen:         'edit'
         handlersConfig: handlers
         filter:         @formMeta.filter
@@ -35,7 +35,7 @@
     else
       new App.ControllerForm(
         elReplace:      @el
-        model:          { configure_attributes: @formMeta.configure_attributes }
+        model:          { configure_attributes: @formMeta.configure_attributes || App.Ticket.configure_attributes }
         screen:         'edit'
         handlersConfig: handlers
         filter:         @formMeta.filter


default option is has account

default option is has account

Former-commit-id: 1009e9abfdc7f539c2d9a6ea9afb26ebf6ec8297 [formerly f91b214fbd588116c8b8a0e09effe3cadd6abe10] [formerly 50dd4756542d8bd7cc93974cf6a3b8700fe54e2f [formerly 50dd4756542d8bd7cc93974cf6a3b8700fe54e2f [formerly 3169a58f5ecc4b57106ae31fc537eb9bb5d06bf7]]]
Former-commit-id: e6995843d873c51b0ebef071347add0565d8fc0f [formerly 6198e1b6a04f80bd85347522db5c966eb64ddc60]
Former-commit-id: 7aa02664c96429f6ed8e3fdd458f299a15045861
Former-commit-id: 549e28511d3d3b06df374e7fc68a64ffa895b80c

--- app/assets/javascripts/backbone/apps/auth/signin/auth_signin_view.coffee
+++ app/assets/javascripts/backbone/apps/auth/signin/auth_signin_view.coffee
@@ -52,12 +52,14 @@
       if !Modernizr.input.placeholder
         @$el.find('[placeholder]').simplePlaceholder()

+
+      selector = if @options.selected == 'has_pass' then '#password_none' else '#password_has'
+      $password_input = @$el.find selector
+      $password_input.trigger 'click'
+
       if !@options.fixed
         @$el.find('#user_email').focus()

-      selector = if @options.selected == 'no_pass' then '#password_has' else '#password_none'
-      $password_input = @$el.find selector
-      $password_input.trigger 'click'

     respondToPasswordReminderRequest : (success) ->
       if success


Tweak dropdown persist behaviour.  Read pages from passed in pages

--- app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee
+++ app/assets/javascripts/ckeditor/plugins/effective_menus/plugin.js.coffee
@@ -330,13 +330,15 @@
                 id: 'source',
                 type: 'radio',
                 label: 'Link Type',
-                items: [['Page', 'Page'], ['URL', 'URL'], ['Route', 'Route'], ['Divider', 'Divider']]
+                items: [['Page', 'Page'], ['URL', 'URL'], ['Route', 'Route'], ['Divider', 'Divider'], ['Dropdown', 'Dropdown']]
                 setup: (element) ->
                   menuable_id = element.children('.menu-item').children("input[name$='[menuable_id]']").val() || ''
                   special = element.children('.menu-item').children("input[name$='[special]']").val() || ''
                   url = element.children('.menu-item').children("input[name$='[url]']").val() || ''

-                  if menuable_id.length > 0
+                  if this.getDialog().effective_menu_item.hasClass('dropdown')
+                    this.setValue('Dropdown')
+                  else if menuable_id.length > 0
                     this.setValue('Page')
                   else if special == 'divider'
                     this.setValue('Divider')
@@ -348,12 +350,18 @@
                     this.setValue('Page')

                 onChange: (event) ->
+                  if this.getValue() != 'Dropdown'
+                    radios = $('#' + this.getDialog().getContentElement('item', 'source').getElement().getId())
+                    radios.find('input').prop('disabled', false)
+                    radios.find("input[value='Dropdown']").prop('disabled', true)
+
                   if this.getValue() == 'Page'
                     this.getDialog().getContentElement('item', 'title').getElement().show()
                     this.getDialog().getContentElement('item', 'menuable_id').getElement().show()

                     this.getDialog().getContentElement('item', 'url').getElement().hide()
                     this.getDialog().getContentElement('item', 'special').getElement().hide()
+                    this.getDialog().getContentElement('item', 'dropdown').getElement().hide()

                   if this.getValue() == 'URL'
                     this.getDialog().getContentElement('item', 'title').getElement().show()
@@ -361,12 +369,23 @@

                     this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
                     this.getDialog().getContentElement('item', 'special').getElement().hide()
+                    this.getDialog().getContentElement('item', 'dropdown').getElement().hide()

                   if this.getValue() == 'Divider'
                     this.getDialog().getContentElement('item', 'url').getElement().hide()
                     this.getDialog().getContentElement('item', 'title').getElement().hide()
                     this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
                     this.getDialog().getContentElement('item', 'special').getElement().hide()
+                    this.getDialog().getContentElement('item', 'dropdown').getElement().hide()
+
+                  if this.getValue() == 'Dropdown'
+                    this.getDialog().getContentElement('item', 'title').getElement().show()
+                    this.getDialog().getContentElement('item', 'dropdown').getElement().show()
+                    $('#' + this.getDialog().getContentElement('item', 'source').getElement().getId()).find('input').prop('disabled', true)
+
+                    this.getDialog().getContentElement('item', 'special').getElement().hide()
+                    this.getDialog().getContentElement('item', 'url').getElement().hide()
+                    this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()

                   if this.getValue() == 'Route'
                     this.getDialog().getContentElement('item', 'title').getElement().show()
@@ -374,20 +393,13 @@

                     this.getDialog().getContentElement('item', 'menuable_id').getElement().hide()
                     this.getDialog().getContentElement('item', 'url').getElement().hide()
+                    this.getDialog().getContentElement('item', 'dropdown').getElement().hide()
               },
               {
                 id: 'menuable_id',
                 type: 'select',
                 label: 'Page',
-                items: (
-                  pages = []
-                  $.ajax
-                    url: '/admin/pages'
-                    dataType: 'json'
-                    async: false
-                    complete: (data) -> pages = data.responseJSON
-                  pages
-                ),
+                items: ((CKEDITOR.config['effective_regions'] || {})['pages'] || [['', '']]),
                 setup: (element) ->
                   this.setValue(element.children('.menu-item').children("input[name$='[menuable_id]']").val())
                 commit: (element) ->
@@ -411,8 +423,9 @@
                 commit: (element) ->
                   if this.getDialog().getValueOf('item', 'source') == 'URL' then value = this.getValue() else value = ''

-                  element.children('.menu-item').children("input[name$='[url]']").val(value)
-                  element.children('a').attr('href', value || '#')
+                  if this.getDialog().getValueOf('item', 'source') != 'Dropdown'
+                    element.children('.menu-item').children("input[name$='[url]']").val(value)
+                    element.children('a').attr('href', value || '#')
                 validate: ->
                   if this.getDialog().getValueOf('item', 'source') == 'URL' && (this.getValue() || '').length == 0
                     CKEDITOR.dialog.validate.notEmpty('please enter a URL').apply(this)
@@ -429,6 +442,8 @@
                     element.children('a').html('')
                   else if this.getDialog().getValueOf('item', 'source') == 'Route'
                     element.children('.menu-item').children("input[name$='[special]']").val(this.getValue())
+                  else if this.getDialog().getValueOf('item', 'source') == 'Dropdown'
+                    # Nothing
                   else
                     element.children('.menu-item').children("input[name$='[special]']").val('')
                   # There is more stuff in the classes commit
@@ -439,6 +454,11 @@
                   else if this.getDialog().getValueOf('item', 'source') == 'Route'
                     if (this.getValue() || '').length == 0
                       CKEDITOR.dialog.validate.notEmpty('please enter a route').apply(this)
+              },
+              {
+                id: 'dropdown',
+                type: 'html',
+                html: 'Dropdown menu items cannot also be links.

To change this dropdown back to a regular menu item,
please click Cancel and then use the drag & drop
functionality to remove all its child menu items.'
               }
             ] # /tab1 elements
           },


Add [].join snippet

Add [].join snippet

--- snippets/arrays.cson
+++ snippets/arrays.cson
@@ -130,3 +130,9 @@
     body: """
     .concat(${1:array});
     """
+  "join":
+    prefix: "join"
+    body: "${1:array}.join(${2:''})$0"
+  "join chain":
+    prefix: ".join"
+    body: ".join(${1:''})$0"


add src to test image

add src to test image

--- test/unit/leaf.coffee
+++ test/unit/leaf.coffee
@@ -6,7 +6,7 @@
   describe('constructor', ->
     tests =
       'image':
-        html: ''
+        html: ''
         text: Quill.DOM.EMBED_TEXT
       'break':
         html: '
'
@@ -39,7 +39,7 @@
         html: '
'
         expected: true
       'image':
-        html: ''
+        html: ''
         expected: true
       'element with element child':
         html: ''


Update gruntfile

Update gruntfile

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -8,7 +8,7 @@
             pre:
                 src: ['dist/assets/images/sprites']
             post:
-                src: ['.tmp/*js', '.tmp/assets']
+                src: ['.tmp/*']

         sprite:
             options:
@@ -33,7 +33,7 @@
                 files: [
                     expand: true,
                     cwd: '.tmp/assets/images/sprites/',
-                    src: ['**/*.png'],
+                    src: ['*.png'],
                     dest: 'dist/assets/images/sprites/'
                     ]
             others:
@@ -67,7 +67,7 @@
                 files: [
                     expand: true,
                     cwd: '.tmp/',
-                    src: '*.js',
+                    src: ['**/*.js', '!assets/**/*.js'],
                     dest: 'dist/'
                     ]


[grunt] remove no longer used files' paths

--- gruntfile.coffee
+++ gruntfile.coffee
@@ -58,17 +58,12 @@
     file:
       source:
         script: '<%= path.source.scripts %>/main'
-        style: '<%= path.source.styles %>/style.scss'

       build:
         script:
           compiled: '<%= path.build.scripts %>/main.js'
-          min: '<%= path.build.scripts %>/main.min.js'
         style:
-          compiled: '<%= path.build.styles %>/style.compiled.css'
-          prefixed: '<%= path.build.styles %>/style.prefixed.css'
           tidy: '<%= path.build.styles %>/style.tidy.css'
-          min: '<%= path.build.styles %>/style.min.css'
         sprite:
           compiled: '<%= path.build.sprites %>/sprite.png'
           hash: '<%= path.build.sprites %>/hash.json'


Added Smooth Zooming

Added Smooth Zooming

--- js/main.coffee
+++ js/main.coffee
@@ -16,7 +16,7 @@
   MAX_MASS = 10
   PAN_SPEED = 10
   ROTATE_SPEED = 0.001
-  SCALE_SPEED = 1.1
+  SCALE_SPEED = 0.001
   TIME_SCALE_SPEED = 1.1

   width = window.innerWidth
@@ -49,10 +49,6 @@
       timeScale *= TIME_SCALE_SPEED
     if e.keyCode == 189
       timeScale /= TIME_SCALE_SPEED
-    if e.keyCode == 88
-      scale *= SCALE_SPEED
-    if e.keyCode == 90
-      scale /= SCALE_SPEED
     if e.keyCode == 37 || e.keyCode == 65
       offset = offset.add new Vector(PAN_SPEED, 0)
     if e.keyCode == 38 || e.keyCode == 87
@@ -78,6 +74,8 @@

     rotation += ROTATE_SPEED * deltaTime if Keyboard.isDown 69
     rotation -= ROTATE_SPEED * deltaTime if Keyboard.isDown 81
+    scale *= 1 + SCALE_SPEED * deltaTime if Keyboard.isDown 88
+    scale /= 1 + SCALE_SPEED * deltaTime if Keyboard.isDown 90

     ctx.clearRect 0, 0, width, height
     ctx.translate width/2, height/2


Make tests for optimization run

Make tests for optimization run

--- test_optimized.coffee
+++ test_optimized.coffee
@@ -1,4 +1,6 @@
 #!/usr/bin/env coffee
+
+ck = require "./src/coffeecup"

 tests =
   'Literal text':
@@ -53,7 +55,7 @@

   'CoffeeScript helper (string)':
     template: "coffeescript \"alert 'hi'\""
-    expected: "<script>alert('hi');</script>"
+    expected: "<script>\nalert('hi');\n</script>"

   'Context vars':
     template: "h1 @foo"


......oops

......oops

I do this for a day job, if you can believe it.

--- tests/ayp_test.coffee
+++ tests/ayp_test.coffee
@@ -1,7 +1,7 @@
 assert = require 'assert'

 Helper = require('hubot-test-helper')
-helper = new Helper('../scripts/dice.coffee')
+helper = new Helper('../scripts/ayp.coffee')

 describe 'AYP', ->


:boar: Update amd_tamer task

:boar: Update amd_tamer task

--- grunt/amd_tamer.coffee
+++ grunt/amd_tamer.coffee
@@ -1,7 +1,7 @@
 module.exports =
   options:
     namespace: '<%= package.name %>'
-    base: 'dist/'
+    base: 'tmp/'
   all:
     options:
       footer: '\n//# sourceMappingURL=<%= package.name %>.js.map'
@@ -11,4 +11,4 @@
         'cancelanimationframe': '[\'animframe/cancelanimationframe\'], function(caf) { return caf; }'
         'performance': '[\'animframe/performance\'], function(perf) { return perf; }'
     files:
-      'dist/<%= package.name %>.js': 'dist/**/*.js'+      'dist/amd/<%= package.name %>.js': 'tmp/**/*.js'


Teamwork: Override session related methods for teamwork workspace.

--- client/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
+++ client/app/Applications/Teamwork.kdapplication/Views/teamworkworkspace.coffee
@@ -6,3 +6,21 @@
       tagName    : "img"
       attributes :
         src      : "#{KD.apiUri}/images/teamwork/loading.gif"
+
+  startNewSession: ->
+    @destroySubViews()
+    options  = @getOptions()
+    delete options.sessionKey
+    teamwork = new TeamworkWorkspace options
+    @getDelegate().teamwork = teamwork
+    @addSubView teamwork
+
+  joinSession: (sessionKey) ->
+    options                = @getOptions()
+    options.sessionKey     = sessionKey.trim()
+    options.joinedASession = yes
+    @destroySubViews()
+
+    @forceDisconnect()
+
+    @addSubView new TeamworkWorkspace options


add surround keys

add surround keys

--- home/.atom/keymap.cson
+++ home/.atom/keymap.cson
@@ -86,6 +86,11 @@
   # Enter Insert Mode
   'ctrl-i': 'vim-mode-plus:activate-insert-mode'

+###################################################
+################ Pending Operation ################
+'atom-text-editor.vim-mode-plus.operator-pending-mode.surround-pending':
+  's': 'vim-mode-plus:inner-current-line'
+
 ##############################################
 ################ NORMAL MODE #################
 'atom-text-editor.vim-mode-plus.normal-mode':
@@ -105,6 +110,14 @@
   '\' shift-c': 'window:move-active-item-to-pane-above'
   '\' shift-t': 'window:move-active-item-to-pane-below'

+  # Surround
+  'y s': 'vim-mode-plus:surround'
+
+  'd s': 'vim-mode-plus:delete-surround-any-pair'
+  'd S': 'vim-mode-plus:delete-surround'
+
+  'c s': 'vim-mode-plus:change-surround-any-pair'
+  'c S': 'vim-mode-plus:change-surround'

 ##############################################
 ################ INSERT MODE #################
@@ -118,6 +131,9 @@
 ##############################################
 ################ VISUAL MODE #################
 'atom-text-editor.vim-mode-plus.visual-mode':
+  # Surround
+  'S': 'vim-mode-plus:surround'
+
   # Exit Visual Mode - Escape is too far away.
   'ctrl-u': 'vim-mode-plus:activate-normal-mode'
   # Enter Insert Mode


Dont allow TextEditor export in tasks

Dont allow TextEditor export in tasks

--- exports/atom.coffee
+++ exports/atom.coffee
@@ -6,7 +6,6 @@
   BufferedNodeProcess: require '../src/buffered-node-process'
   BufferedProcess: require '../src/buffered-process'
   GitRepository: require '../src/git-repository'
-  TextEditor: require '../src/text-editor'
   Point: Point
   Range: Range
   Emitter: Emitter
@@ -17,6 +16,8 @@
 # only be exported when not running as a child node process
 unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE
   module.exports.Task = require '../src/task'
+  module.exports.TextEditor = require '../src/text-editor'
+
   {$, $$, $$$, View} = require '../src/space-pen-extensions'

   Object.defineProperty module.exports, 'Workspace', get: ->


prevent Cannot read property 'top' of undefined errors from when hash changed query is not found

--- src/scripts/modules/media/media.coffee
+++ src/scripts/modules/media/media.coffee
@@ -107,8 +107,8 @@
         handleHeaderViewPinning()
         if isPinned
           obscured = $pinnable.height()
-          top = $(window.location.hash).position().top
-          $(window).scrollTop(top - obscured)
+          top = $(window.location.hash)?.position()?.top
+          $(window).scrollTop(top - obscured) if top

       Backbone.on('window:hashChange', _.debounce(adjustHashTop, 150))


Don't overwrite url when navigating away from editor

--- src/scripts/modules/media/media.coffee
+++ src/scripts/modules/media/media.coffee
@@ -79,6 +79,7 @@

     toggleEditor: () -> if @editing then @closeEditor() else @loadEditor()

+    # FIX: How much of loadEditor and closeEditor can be merged into the editbar?
     loadEditor: () ->
       @editing = true

@@ -95,4 +96,8 @@
       $('body').css('padding-top', '0') # Remove added padding
       window.scrollBy(0, -height) # Prevent viewport from jumping

-    onBeforeClose: () -> @model.set('editable', false) if @model.get('editable')
+    onBeforeClose: () ->
+      if @model.get('editable')
+        @model.set('editable', false, {silent: true})
+        @closeEditor()
+


jupyter project: just a note about how to interactively debug it

--- src/smc-project/jupyter/jupyter.coffee
+++ src/smc-project/jupyter/jupyter.coffee
@@ -3,7 +3,11 @@

 For interactive testing:

-coffee> k = require('./jupyter').kernel(name:'sagemath', path:'a.ipynb'); k.execute_code(all:true, cb:console.log, code:'2+3')
+$ source smc-env
+$ coffee
+coffee> j = require('./smc-project/jupyter/jupyter')
+coffee> k = j.kernel(name:'python3', path:'x.ipynb')
+coffee> k.execute_code(all:true, cb:((x) -> console.log(JSON.stringify(x))), code:'2+3')
 ###

 {EventEmitter} = require('events')


".console .validate-token" seems no logner exists

--- app/assets/javascripts/garage/docs/console.js.coffee
+++ app/assets/javascripts/garage/docs/console.js.coffee
@@ -8,11 +8,6 @@
   $(".modal-close").click (ev) ->
     $.colorbox.close()
     ev.preventDefault()
-
-  $(".console .validate-token").click (ev) ->
-    $("#method").val('GET')
-    $("#location").val("/oauth/token/info")
-    $('.send-request').click()

   buildAuthorizedUrl = (base, location, token) ->
     url = base + location


Add try/catch to check localStorage support

--- app/assets/javascripts/lesson_header_onScroll.coffee
+++ app/assets/javascripts/lesson_header_onScroll.coffee
@@ -4,17 +4,29 @@
     @prevST = 0
     @scrollTimer = null

-    if localStorage.getItem('scrollAlertShown')
-        $('.scroll-effect-alert').alert('close')
-    else
-        $('.scroll-effect-alert .close').click(-> localStorage.setItem('scrollAlertShown', 'true'))
+    @localStorageSupport = ->
+        test = ->
+            support = 'support'
+            localStorage.setItem(support, support)
+            localStorage.removeItem(support)
+            return true
+
+        try test(); catch e then return false
+
+    @notification = ->
+        if localStorage.getItem('scrollAlertShown')
+            $('.scroll-effect-alert').alert('close')
+        else
+            $('.scroll-effect-alert .close').click(
+                -> localStorage.setItem('scrollAlertShown', 'true'))
+
+    @notification() if @localStorageSupport()

     @onHover = ->
         elem.addClass('mouse--enter') for elem in classList
         $('.mouse--enter').hover(
             => @showElems()
-            => @hideElems()
-        )
+            => @hideElems())

     @onHover() if options.showOnHover


Added a great variety of easing functions to math package.

--- src/math/math.coffee
+++ src/math/math.coffee
@@ -7,7 +7,141 @@
   fit01: (value, min, max) -> value * (max - min) + min

   clamp: (value, min, max) -> Math.max(min, Math.min(max, value))
+  clamp01: (value) -> Math.max(0, Math.min(1, value))

   randF: (min, max) -> random.float(min, max)
   randI: (min, max) -> random.int(min, max)
   randB: (chance) -> random.bool(chance)
+
+
+  ###
+    Easing Functions - inspired by http://easings.net/
+    only considering the t value for the range [0, 1] => [0, 1]
+  ###
+  ease:
+    linear: (t) -> t
+
+    # accelerating from zero velocity
+    inQuad: (t) -> Math.pow t, 2
+
+    # decelerating to zero velocity
+    outQuad: (t) -> -(Math.pow((t - 1), 2) - 1)
+
+    # acceleration until halfway, then deceleration
+    inOutQuad: (t) ->
+      return 0.5 * Math.pow(t, 2)  if (t /= 0.5) < 1
+      -0.5 * ((t -= 2) * t - 2)
+
+    inCubic: (t) -> Math.pow t, 3
+
+    outCubic: (t) -> Math.pow((t - 1), 3) + 1
+
+    inOutCubic: (t) ->
+      return 0.5 * Math.pow(t, 3)  if (t /= 0.5) < 1
+      0.5 * (Math.pow((t - 2), 3) + 2)
+
+    inQuart: (t) -> Math.pow t, 4
+
+    outQuart: (t) -> -(Math.pow((t - 1), 4) - 1)
+
+    inOutQuart: (t) ->
+      return 0.5 * Math.pow(t, 4)  if (t /= 0.5) < 1
+      -0.5 * ((t -= 2) * Math.pow(t, 3) - 2)
+
+    inQuint: (t) -> Math.pow t, 5
+
+    outQuint: (t) -> Math.pow((t - 1), 5) + 1
+
+    inOutQuint: (t) ->
+      return 0.5 * Math.pow(t, 5)  if (t /= 0.5) < 1
+      0.5 * (Math.pow((t - 2), 5) + 2)
+
+    inSine: (t) -> -Math.cos(t * (Math.PI / 2)) + 1
+
+    outSine: (t) -> Math.sin t * (Math.PI / 2)
+
+    inOutSine: (t) -> -0.5 * (Math.cos(Math.PI * t) - 1)
+
+    inExpo: (t) -> (if (t is 0) then 0 else Math.pow(2, 10 * (t - 1)))
+
+    outExpo: (t) -> (if (t is 1) then 1 else -Math.pow(2, -10 * t) + 1)
+
+    inOutExpo: (t) ->
+      return 0 if t == 0
+      return 1 if t == 1
+      return 0.5 * Math.pow(2, 10 * (t - 1))  if (t /= 0.5) < 1
+      0.5 * (-Math.pow(2, -10 * --t) + 2)
+
+    inCirc: (t) -> -(Math.sqrt(1 - (t * t)) - 1)
+
+    outCirc: (t) -> Math.sqrt 1 - Math.pow((t - 1), 2)
+
+    inOutCirc: (t) ->
+      return -0.5 * (Math.sqrt(1 - t * t) - 1)  if (t /= 0.5) < 1
+      0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1)
+
+    outBounce: (t) ->
+      if (t) < (1 / 2.75)
+        7.5625 * t * t
+      else if t < (2 / 2.75)
+        7.5625 * (t -= (1.5 / 2.75)) * t + 0.75
+      else if t < (2.5 / 2.75)
+        7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375
+      else
+        7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375
+
+    inBack: (t) ->
+      s = 1.70158
+      (t) * t * ((s + 1) * t - s)
+
+    outBack: (t) ->
+      s = 1.70158
+      (t = t - 1) * t * ((s + 1) * t + s) + 1
+
+    inOutBack: (t) ->
+      s = 1.70158
+      return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s))  if (t /= 0.5) < 1
+      0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2)
+
+    elastic: (t) ->
+      -1 * Math.pow(4, -8 * t) * Math.sin((t * 6 - 1) * (2 * Math.PI) / 2) + 1
+
+    swingFromTo: (t) ->
+      s = 1.70158
+      (if ((t /= 0.5) < 1) then 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)) else 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2))
+
+    swingFrom: (t) ->
+      s = 1.70158
+      t * t * ((s + 1) * t - s)
+
+    swingTo: (t) ->
+      s = 1.70158
+      (t -= 1) * t * ((s + 1) * t + s) + 1
+
+    bounce: (t) ->
+      if t < (1 / 2.75)
+        7.5625 * t * t
+      else if t < (2 / 2.75)
+        7.5625 * (t -= (1.5 / 2.75)) * t + 0.75
+      else if t < (2.5 / 2.75)
+        7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375
+      else
+        7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375
+
+    bouncePast: (t) ->
+      if t < (1 / 2.75)
+        7.5625 * t * t
+      else if t < (2 / 2.75)
+        2 - (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75)
+      else if t < (2.5 / 2.75)
+        2 - (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375)
+      else
+        2 - (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375)
+
+    fromTo: (t) ->
+      return 0.5 * Math.pow(t, 4)  if (t /= 0.5) < 1
+      -0.5 * ((t -= 2) * Math.pow(t, 3) - 2)
+
+    from: (t) -> Math.pow t, 4
+
+    to: (t) -> Math.pow t, 0.25


Create default table page number

Create default table page number

--- app/models/settings.coffee
+++ app/models/settings.coffee
@@ -1,3 +1,5 @@
 class Settings extends Backbone.Model
+  defaults:
+    "currentPage": 0

 module.exports = Settings


updated parity color

updated parity color

--- docs/coffee/vis.coffee
+++ docs/coffee/vis.coffee
@@ -1,10 +1,10 @@
 root = exports ? this

-majorityColor = "#1f77b4"
-minorityColor = "#d62728"
-
-# majorityColor = "#3182bd"
-# minorityColor = "#fa9fb5"
+# majorityColor = "#1f77b4"
+# minorityColor = "#d62728"
+
+majorityColor = "#ffffe0"
+minorityColor = "#8b0000"

 Network = () ->
   # width and height of visualization (should be based off css spacing on page)


fix this ref

fix this ref

--- src/pouchmodel.coffee
+++ src/pouchmodel.coffee
@@ -27,10 +27,8 @@
                 callback err
             else if not doc?
                 callback null, null
-            else if doc.docType.toLowerCase() isnt @getDocType().toLowerCase()
-                callback null, null
-            else
-                callback null, new @ doc
+            else
+                callback null, doc

     create: (attributes, callback) ->
         func = 'post'
@@ -72,8 +70,26 @@
                         callback()

     updateAttributes: (id, attributes, callback) ->
-        # @TODO, this should actually merge
-        @save id, attributes, callback
+        docType = @getDocType()
+        PouchdbBackedModel.db.get id, (err, doc) =>
+            if err
+                callback err
+            else if not doc?
+                callback new Error 'document does not exist'
+            else if doc.docType.toLowerCase() isnt docType.toLowerCase()
+                callback new Error 'document does not exist'
+            else
+                for own key, value of attributes
+                    doc[key] = value
+                PouchdbBackedModel.db.put doc, (err, response) ->
+                    if err
+                        callback err
+                    if not response.ok
+                        callback new Error """
+                            An error occured while updating document.'
+                        """
+                    else
+                        callback()

     destroy: (id, callback) ->
         PouchdbBackedModel.db.get id, (err, doc) =>


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -163,7 +163,7 @@
               I.includedModules.push moduleName
               self.extend Module(I, self)
           else
-            warn "Unable to discover name for module: ", Module, "\nSerialization issues may occur"
+            warn "Unable to discover name for module: ", Module, "\nSerialization issues may occur."
             self.extend Module(I, self)

         return self
@@ -172,5 +172,4 @@
         self[name](args...)

     return self
-
 )()


Core object docs

Core object docs

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -9,15 +9,17 @@
 @param {Object} I Instance variables
 ###

-###*
-@name I
-@memberOf Core#
-###
-
 Core = (I) ->
   I ||= {}

   self =
+    ###*
+    External access to instance variables. Use of this property should be avoided
+    in general, but can come in handy from time to time.
+
+    @name I
+    @fieldOf Core#
+    ###
     I: I

     ###*


Move emit to correct place to handle all events both keyboard and mice

--- client/Framework/components/autocomplete/KDAutoCompleteController.coffee
+++ client/Framework/components/autocomplete/KDAutoCompleteController.coffee
@@ -57,7 +57,6 @@
       when 13, 9 #enter, tab
         unless autoCompleteView.getValue() is ""
           @submitAutoComplete autoCompleteView.getValue()
-          @emit 'ItemListChanged'
         else
           return yes
       when 27 #escape
@@ -191,6 +190,7 @@
         @appendAutoCompletedItem()
       @addItemToSubmitQueue activeItem.item
       @rearrangeInputWidth()
+      @emit 'ItemListChanged'
     else
       inputView.setValue ''
       @getSingleton("windowController").setKeyView null


referral: track user invitation interaction

--- client/app/Applications/Account.kdapplication/views/referrermodal.coffee
+++ client/app/Applications/Account.kdapplication/views/referrermodal.coffee
@@ -4,7 +4,7 @@
     options.width          = 570
     options.overlay        = yes
     options.title          = "Get free space up to 16GB"
-    options.url          or= "#{location.origin}/?r=#{KD.nick}"
+    options.url          or= "#{location.origin}/?r=#{KD.nick()}"
     options.tabs           =
       navigable            : no
       goToNextFormOnSubmit : no
@@ -119,13 +119,18 @@

     @invite.addSubView new KDButtonView
       title   : "Send invitation(s)"
-      callback: ->
-        listController.getItemsOrdered().forEach (view) ->
+      callback: =>
+        recipients = listController.getItemsOrdered().filter (view) =>
+          return  view.isSelected and not contact.invited
+
+        recipients.forEach (view) ->
           contact = view.getData()
           return if not view.isSelected or contact.invited
           view.getData().invite (err) =>
             return log "invite", err  if err
             view.emit "InvitationSent"
+
+        @track recipients.length

     @invite.addSubView new KDButtonView
       title: "Send invitations to all"
@@ -143,12 +148,14 @@
                     loader    :
                       color   : "#444"
                       diameter: 12
-                    callback  : ->
-                      listController.getItemsOrdered().forEach (view) ->
+                    callback  : =>
+                      recipients = listController.getItemsOrdered()
+                      recipients.forEach (view) ->
                         view.getData().invite (err) =>
                           return log err  if err
                           view.emit "InvitationSent"
                       modal.destroy()
+                      @track recipients.length
                   Cancel      :
                     style     : "modal-clean-red"
                     callback  : -> modal.destroy()
@@ -169,6 +176,9 @@

     @setPositions()

+  track: (count) ->
+    KD.kdMixpanel.track "User Sent Invitation", $user: KD.nick(), count: count
+
   dontShowAgain: ->
     storage = KD.getSingleton("appStorageController").storage "MainApp"
     storage.setValue "dontDisplayReferrerModalAgain", yes


default to first group

default to first group

--- src/node_modules/app/routes.coffee
+++ src/node_modules/app/routes.coffee
@@ -1,5 +1,5 @@
 module.exports = ($stateProvider, $urlRouterProvider) ->
-  $urlRouterProvider.otherwise("/")
+  $urlRouterProvider.otherwise("/groups/1")
   $stateProvider.state 'layout', require('layout')
   $stateProvider.state 'layout.group', require('group-page')
   $stateProvider.state 'layout.group.rounds', require('group-rounds')


web: tweets/*: javascript: fix checking existence of element

--- app/assets/javascripts/tweets.coffee
+++ app/assets/javascripts/tweets.coffee
@@ -5,7 +5,7 @@
       Application.Helpers.rescue_profile_image(d)
     formatTweet $(".statuses")

-    if $("link[rel=next]") isnt null
+    if $("link[rel=next]").length
       $.autopager
         content: $(".statuses")
         link: $("link[rel=next]")


Go to newly created subject set or workflow

--- app/pages/lab/project.cjsx
+++ app/pages/lab/project.cjsx
@@ -132,12 +132,15 @@
       workflowCreationInProgress: true

     workflow.save()
+      .then =>
+        @transitionTo 'edit-project-workflow', projectID: @props.project.id, workflowID: workflow.id
       .catch (error) =>
         @setState workflowCreationError: error
       .then =>
         @props.project.uncacheLink 'workflows'
         @props.project.uncacheLink 'subject_sets' # An "expert" subject set is automatically created with each workflow.
-        @setState workflowCreationInProgress: false
+        if @isMounted()
+          @setState workflowCreationInProgress: false

   createNewSubjectSet: ->
     subjectSet = apiClient.type('subject_sets').create
@@ -150,11 +153,14 @@
       subjectSetCreationInProgress: true

     subjectSet.save()
+      .then =>
+        @transitionTo 'edit-project-subject-set', projectID: @props.project.id, subjectSetID: subjectSet.id
       .catch (error) =>
         @setState subjectSetCreationError: error
       .then =>
         @props.project.uncacheLink 'subject_sets'
-        @setState subjectSetCreationInProgress: false
+        if @isMounted()
+          @setState subjectSetCreationInProgress: false

   deleteProject: ->
     @setState deletionError: null


use more specific user_roles route

use more specific user_roles route

When testing if the current user has access to the project, only request the current user’s roles, not all user roles.

--- app/pages/lab/project.cjsx
+++ app/pages/lab/project.cjsx
@@ -256,7 +256,7 @@
       getProject = apiClient.type('projects').get @props.params.projectID

       getOwners = getProject.then (project) =>
-        project.get('project_roles', page_size: 100).then (projectRoles) =>
+        project.get('project_roles', user_id: @props.user.id).then (projectRoles) =>
           owners = for projectRole in projectRoles when 'owner' in projectRole.roles or 'collaborator' in projectRole.roles
             projectRole.get 'owner'
           Promise.all owners


Pass vars into course for registration

Pass vars into course for registration

--- src/course/status.cjsx
+++ src/course/status.cjsx
@@ -17,8 +17,8 @@

   startRegistration: ->
     course = @getCourse()
-    course.set(enrollment_code: @refs.input.value, book_cnx_id: @props.collectionUUID)
-    course.register()
+    course.set(ecosystem_book_uuid: @props.collectionUUID)
+    course.register(React.findDOMNode(@refs.input).value)

   onCourseChange: -> @forceUpdate()
   componentWillUnmount: -> @course.channel.off('change', @onCourseChange) if @course


report unsubscribe success on when url params exists

Panoptes token unsubscribe via get will redirect to front end with ?processed=true param. This will notify users that their request was received and processed. Without the query param it will display a form for email unsubscribe. This setup will allow new unsubscribe functionality and preserve existing behaviour with links in the wild.

--- app/pages/unsubscribe.cjsx
+++ app/pages/unsubscribe.cjsx
@@ -43,27 +43,33 @@

   render: ->

-      <form onSubmit={@handleEmailSubmit}>
-
Unsubscribe from all Zooniverse emails.

-
We get it, no one like keep getting email they don't want.

-
Just enter your email address here and we'll unsubscribe you from all our email lists.

-

-          <input ref="email" type="email" required onChange={@handleEmailChange} className="standard-input" defaultValue={@props.query?.email} size="50" />
-

-

-          <button type="submit" className="standard-button" disabled={!@state.emailIsValid || @state.emailSuccess}>Submit</button>
+      { if @props.query?.processed
+

+
Your unsubscribe request was successfully processed.

+
If you change your mind, just visit your account settings page to update your email preferences.

+

+      else
+        <form onSubmit={@handleEmailSubmit}>
+
Unsubscribe from all Zooniverse emails.

+
We get it, no one like keep getting email they don't want.

+
Just enter your email address here and we'll unsubscribe you from all our email lists.

+

+            <input ref="email" type="email" required onChange={@handleEmailChange} className="standard-input" defaultValue={@props.query?.email} size="50" />
+

+

+            <button type="submit" className="standard-button" disabled={!@state.emailIsValid || @state.emailSuccess}>Submit</button>

-          {' '}
+            {' '}

-          {if @state.inProgress
-
-          else if @state.emailSuccess
-            }
-

+            {if @state.inProgress
+
+            else if @state.emailSuccess
+              }
+

-        {if @state.emailSuccess
-
You've been unsubscribed!

-        else if @state.emailError
-          There was an error unsubscribing your email.}
-      </form>
+          {if @state.emailSuccess
+
You've been unsubscribed!

+          else if @state.emailError
+            There was an error unsubscribing your email.}
+        </form>}


Fixed empty lines

Fixed empty lines

--- app/templates/mvc-coffee/app/controllers/home.coffee
+++ app/templates/mvc-coffee/app/controllers/home.coffee
@@ -10,12 +10,10 @@
 module.exports = (app) ->
   app.use '/', router
 <% if(options.viewEngine == 'marko'){ %>
-indexTemplate = marko.load require.resolve '../views/index.marko'<% } %>
-<% if(options.database == 'mongodb'){ %>
+indexTemplate = marko.load require.resolve '../views/index.marko'<% } if(options.database == 'mongodb'){ %>
 router.get '/', (req, res, next) ->
   Article.find (err, articles) ->
-    return next(err) if err<% } %>
-<% if(options.database == 'mysql' || options.database == 'postgresql' || options.database == 'sqlite'){ %>
+    return next(err) if err<% } if(options.database == 'mysql' || options.database == 'postgresql' || options.database == 'sqlite'){ %>
 router.get '/', (req, res) ->
   db.Article.findAll().then (articles) -><% } %><% if(options.database == 'rethinkdb'){ %>
   Article.run().then (articles) -><% } %><% if(options.viewEngine == 'marko'){ %>


Set model on accounts controller for backwards compat

--- app/routes/accounts.coffee
+++ app/routes/accounts.coffee
@@ -11,6 +11,8 @@
     controller.set('user', user)
     controller.set('organizations', orgs)

+    controller.set('model', model)
+
   renderTemplate: ->
     @_super.apply(this, arguments)
     @render 'profile_accounts', outlet: 'left', into: 'profile'


Level autocompleteReplacement can work as additions (#6561)

Co-authored-by: Valentin Briukhanov <1c97702c869e5316729111bfe70e91c2a0718c7e@Valentins-MacBook-Air.local>

--- app/views/play/level/tome/editor/autocomplete.coffee
+++ app/views/play/level/tome/editor/autocomplete.coffee
@@ -258,6 +258,7 @@
     haveFindNearestEnemy = false
     haveFindNearest = false
     autocompleteReplacement = level.get("autocompleteReplacement") ? []
+    usedAutocompleteReplacement = []
     for group, props of e.propGroups
       for prop in props
         if _.isString prop  # organizePalette
@@ -277,6 +278,8 @@
         if doc?.snippets?[e.language]
           name = doc.name
           replacement = _.find(autocompleteReplacement, (el) -> el.name is name)
+          if replacement
+            usedAutocompleteReplacement.push(replacement.name)
           content = replacement?.snippets?[e.language]?.code or doc.snippets[e.language].code
           if /loop/.test(content) and level.get 'moveRightLoopSnippet'
             # Replace default loop snippet with an embedded moveRight()
@@ -353,6 +356,17 @@
     if haveFindNearest and not haveFindNearestEnemy
       spellView.translateFindNearest()

+    for replacement in autocompleteReplacement
+      continue if replacement.name in usedAutocompleteReplacement
+      continue if not replacement.snippets
+      entry =
+        content: replacement?.snippets?[e.language]?.code
+        meta: $.i18n.t('keyboard_shortcuts.press_enter', defaultValue: 'press enter')
+        name: replacement.name
+        tabTrigger: replacement.snippets?[e.language]?.tab
+        importance: replacement.autoCompletePriority ? 1.0
+      snippetEntries.push entry
+
     # window.AutocompleteInstance = @Autocomplete  # For debugging. Make sure to not leave active when committing.
     # window.snippetEntries = snippetEntries
     lang = aceUtils.aceEditModes[e.language].substr 'ace/mode/'.length


Fix permalinks for comments

Fix permalinks for comments

--- app/talk/comment-link.cjsx
+++ app/talk/comment-link.cjsx
@@ -1,5 +1,6 @@
 React = require 'react'
 talkConfig = require './config'
+{Link} = require 'react-router'

 PAGE_SIZE = talkConfig.discussionPageSize

@@ -18,22 +19,17 @@

   projectCommentUrl: ->
     {comment} = @props
-    [ownerName, projectName] = comment.project_slug.split('/')
-    window.location.origin + "/projects/#{ownerName}/#{projectName}/talk/#{comment.board_id}/#{comment.discussion_id}?comment=#{comment.id}"
+    "/projects/#{comment.project_slug}/talk/#{comment.board_id}/#{comment.discussion_id}?comment=#{comment.id}"

   mainTalkCommentUrl: ->
     {comment} = @props
-    window.location.origin + "/talk/#{comment.board_id}/#{comment.discussion_id}?comment=#{comment.id}"
+    "/talk/#{comment.board_id}/#{comment.discussion_id}?comment=#{comment.id}"

   render: ->
+    href = if @projectComment() then @projectCommentUrl() else @mainTalkCommentUrl()
+

-      {if @projectComment()
-
-          {@props.children ? @projectCommentUrl()}
-
-
-      else
-
-          {@props.children ? @mainTalkCommentUrl()}
-        }
+      <Link to={href}>
+        {@props.children ? window.location.origin + href}
+      </Link>


Prevent undefined ref from disrupting navigation.

Closes #2830

--- app/talk/search-input.cjsx
+++ app/talk/search-input.cjsx
@@ -21,7 +21,7 @@
   onSearchSubmit: (e) ->
     e.preventDefault()
     {owner, name} = @props.params
-    inputValue = @searchInput().value
+    inputValue = @refs.talkSearchInput?.value
     @logSearch inputValue

     if owner and name
@@ -32,14 +32,12 @@
     else
       @history.pushState(null, "/talk/search", {query: inputValue})

-  searchInput: ->
-    @refs.talkSearchInput
-
   componentWillReceiveProps: (nextProps) ->
+    return unless @refs.talkSearchInput
     if nextProps.location.query?.query
-      @searchInput().value = nextProps.location.query.query
+      @refs.talkSearchInput.value = nextProps.location.query.query
     else
-      @searchInput().value = ''
+      @refs.talkSearchInput.value = ''

   render: ->
     <form className="talk-search-form" onSubmit={ @onSearchSubmit }>


Update Atom panel height

Update Atom panel height


Fix broken css on some input text

Fix broken css on some input text

--- BackofficeBundle/Resources/public/coffee/formBehavior/ValidateHidden.coffee
+++ BackofficeBundle/Resources/public/coffee/formBehavior/ValidateHidden.coffee
@@ -20,4 +20,4 @@

 jQuery ->
   OpenOrchestra.FormBehavior.formBehaviorLibrary.add(new OpenOrchestra.FormBehavior.ValidateHidden("input[type='hidden'][required='required']"))
-  OpenOrchestra.FormBehavior.formBehaviorLibrary.add(new OpenOrchestra.FormBehavior.ValidateHidden("input:hidden[type='text'][required='required']"))
+  OpenOrchestra.FormBehavior.formBehaviorLibrary.add(new OpenOrchestra.FormBehavior.ValidateHidden("input.select-boolean[type='text'][required='required']"))


Add the House to the list of chambers we retrieve data for.

--- jobs/import-votes.coffee
+++ jobs/import-votes.coffee
@@ -76,7 +76,7 @@
         persistVote(job.data.session, result.GetVoteResult, db, callback)

   jobs.process 'import all votes for session', 5, (job, callback) ->
-    for branch in ["Senate"]
+    for branch in ["House", "Senate"]
       getVotesArgs =
         Branch: branch
         SessionId: job.data.session.assemblyId


Added fi and en languages fot BROWSER_TYPE

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -25,6 +25,8 @@
   ipad: false
   android: false
   mobile: false
+  lang_en: false
+  lang_fi: false

 ###
 The DOM Element abstraction engine
@@ -882,6 +884,8 @@
   _warmup: ->
     _ua = navigator.userAgent
     _av = navigator.appVersion
+    _lang = navigator.language || navigator.userLanguage
+
     _browserType = BROWSER_TYPE
     _browserType.opera    = !!~_ua.indexOf('Opera')
     _browserType.safari   = !!~_ua.indexOf('KHTML')
@@ -910,6 +914,11 @@
     _browserType.ipad = _iPad
     _browserType.android = !!~_ua.indexOf('Android')
     _browserType.mobile = !!~_av.indexOf('Mobile')
+    if !!~_lang.indexOf('fi')
+      _browserType.lang_fi = true
+    else
+      _browserType.lang_en = true
+
     @_domWaiter()
     null


Allow "hash" style attrs setting in ELEM#make

--- js/core/elem/elem.coffee
+++ js/core/elem/elem.coffee
@@ -666,9 +666,13 @@
     _id = @_add( _elem )
     @_initCache( _id )
     if _options?
-      if _options.attrs
-        for _attr in _options.attrs
-          @setAttr( _id, _attr[0], _attr[1], true )
+      if _options.attrs?
+        if _options.attrs instanceof Array
+          for _attr in _options.attrs
+            @setAttr( _id, _attr[0], _attr[1], true )
+        else
+          for _attrName, _attrValue of _options.attrs
+            @setAttr( _id, _attrName, _attrValue, true )
       if _options.styles
         @setStyles( _id, _options.styles )
     @_elements[_targetId].appendChild(_elem)


:lipstick: Use string interpolation

:lipstick: Use string interpolation

--- build/tasks/build-task.coffee
+++ build/tasks/build-task.coffee
@@ -62,8 +62,8 @@
     ignoredPaths = ignoredPaths.map (ignoredPath) -> _.escapeRegExp(ignoredPath)

     # Add .* to avoid matching hunspell_dictionaries.
-    ignoredPaths.push _.escapeRegExp(path.join('spellchecker', 'vendor', 'hunspell') + path.sep) + ".*"
-    ignoredPaths.push _.escapeRegExp(path.join('build', 'Release') + path.sep) + '.*\\.pdb'
+    ignoredPaths.push "#{_.escapeRegExp(path.join('spellchecker', 'vendor', 'hunspell') + path.sep)}.*"
+    ignoredPaths.push "#{_.escapeRegExp(path.join('build', 'Release') + path.sep)}.*\\.pdb"

     # Hunspell dictionaries are only not needed on OS X.
     if process.platform is 'darwin'


fix regex on birthday field

fix regex on birthday field

--- apps/artist_2/test/view_helpers.coffee
+++ apps/artist_2/test/view_helpers.coffee
@@ -100,6 +100,8 @@
     it 'birth and death', ->
       helpers.formatBirthDeath( birthday: "1920", deathday: "1990" ).should.eql '1920–1990'

+    it 'birth and death with non-birth prefix', ->
+      helpers.formatBirthDeath( birthday: "Est 1920",  deathday: "Died 1990").should.eql '1920–1990'

     it 'cleans stray characters', ->
       helpers.formatBirthDeath( birthday: "b. 1920", deathday: "Died 1990" ).should.eql '1920–1990'


slight code tweak

slight code tweak

--- app/components/eui-popup.coffee
+++ app/components/eui-popup.coffee
@@ -45,8 +45,7 @@

       labelPathDidChange: Ember.observer ->
         labelPath = @get 'labelPath'
-        path = "content.#{labelPath}"
-        Ember.defineProperty(this, 'label', Ember.computed.alias(path))
+        Ember.defineProperty(this, 'label', Ember.computed.alias("content.#{labelPath}"))
         @notifyPropertyChange 'label'
       , 'content', 'labelPath'