update fonts listing

update fonts listing

--- src/options.coffee
+++ src/options.coffee
@@ -139,6 +139,24 @@
     # main: '\\mktsFontSunexta'
     home: '\\mktsPathsFontsHome'
     files: [
+
+      ### Conservative CJK Fonts: ###
+
+      { texname: 'mktsFontfileKangxizidianti',                 filename: 'TypeLand-com-KangXiZiDianTi-ShiYongBan-1.otf', }
+      { texname: 'mktsFontfileWenyuegudianmingchaoti',         filename: 'WenYue-GuDianMingChaoTi-NC-W5-1.otf', }
+      { texname: 'mktsFontfileHssfifteen',                     filename: 'H-SS15.ttf' }
+      { texname: 'mktsFontfileHssboldfifteen',                 filename: 'H-SS-Bold15.ttf' }
+      { texname: 'mktsFontfileRpmingliutw',                    filename: 'R-PMingLiU-TW.ttf' }
+      { texname: 'mktsFontfileImingvarcpsixten',                subfolder: 'I.Ming', filename: 'I.MingVarCP-6.10.ttf', }
+      { texname: 'mktsFontfileImingvarsixten',                  subfolder: 'I.Ming', filename: 'I.MingVar-6.10.ttf',   }
+      { texname: 'mktsFontfileImingsixten',                     subfolder: 'I.Ming', filename: 'I.Ming-6.10.ttf',      }
+      { texname: 'mktsFontfileImingcpsixten',                   subfolder: 'I.Ming', filename: 'I.MingCP-6.10.ttf',    }
+      { texname: 'mktsFontfileHkcstraditionalbmpcanonicalonly', subfolder: 'hkcs',   filename: 'hkcs-traditional-bmp-canonicalonly.otf', }
+
+      ### Unifonts: ###
+      { texname: 'mktsFontfileUnifont',       subfolder: 'unifont', filename: 'unifont-12.1.03.ttf',       }
+      { texname: 'mktsFontfileUnifontcsur',   subfolder: 'unifont', filename: 'unifont_csur-12.1.03.ttf',  }
+      { texname: 'mktsFontfileUnifontupper',  subfolder: 'unifont', filename: 'unifont_upper-12.1.03.ttf', }
       # { texname: 'mktsFontfileJizurathreeb',                   filename: 'jizura3b-from-svg-rewritten-by-fontforge.ttf',                        }
       { texname: 'mktsFontfileBabelstonehan',                  filename: 'BabelStoneHan.ttf',                   }
       { texname: 'mktsFontfileBiaukai',                        filename: 'BiauKai.ttf',                         }
@@ -218,7 +236,7 @@
       { texname: 'mktsFontfileIpam',                           filename: 'ipam.ttf',                            }
       { texname: 'mktsFontfileIpamp',                          filename: 'ipamp.ttf',                           }
       { texname: 'mktsFontfileJizurathreeb',                   filename: 'jizura3b.ttf',                        }
-      { texname: 'mktsFontfileJizurafourbmp',                  filename: 'jizura4bmp.ttf',                      }
+      # { texname: 'mktsFontfileJizurafourbmp',                  filename: 'jizura4bmp.ttf',                      }
       { texname: 'mktsFontfileKai',                            filename: 'Kai.ttf',                             }
       { texname: 'mktsFontfileMonoidbold',                     filename: 'Monoid-Bold.ttf',                     }
       { texname: 'mktsFontfileMonoiditalic',                   filename: 'Monoid-Italic.ttf',                   }
@@ -342,6 +360,9 @@
       { texname: 'mktsFontfileRobotothin',                      subfolder: 'Roboto',                  filename: 'Roboto-Thin.ttf'                     }
       { texname: 'mktsFontfileRobotothinitalic',                subfolder: 'Roboto',                  filename: 'Roboto-ThinItalic.ttf'               }
       { texname: 'mktsFontfileRobotocondensedbold',             subfolder: 'Roboto_Condensed',        filename: 'RobotoCondensed-Bold.ttf'            }
+
+      { texname: 'mktsFontfileRobotocondensedboldsmallcaps',    otf: 'Letters={SmallCaps}', subfolder: 'Roboto_Condensed',        filename: 'RobotoCondensed-Bold.ttf'            }
+
       { texname: 'mktsFontfileRobotocondensedbolditalic',       subfolder: 'Roboto_Condensed',        filename: 'RobotoCondensed-BoldItalic.ttf'      }
       { texname: 'mktsFontfileRobotocondenseditalic',           subfolder: 'Roboto_Condensed',        filename: 'RobotoCondensed-Italic.ttf'          }
       { texname: 'mktsFontfileRobotocondensedlight',            subfolder: 'Roboto_Condensed',        filename: 'RobotoCondensed-Light.ttf'           }


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -39,6 +39,6 @@
   rightEnemy.include(Follow)
   rightEnemy.follow(player)

-  ok rightEnemy.I.ve
+  ok rightEnemy.I.velocity.equ

 module()


remove some travis debug

remove some travis debug

--- test/helpers.coffee
+++ test/helpers.coffee
@@ -18,7 +18,6 @@
 # will create @app in context
 # usage : before helpers.init port
 _init = (ctx, port, done) ->
-    console.log process.env
     params = name: 'Cozy Home', port: port
     americano.start params, (app, server) =>
         app.server = server


Switch 'datetime' to normal string input, add support for 'timestamp' data type.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -32,7 +32,7 @@
   return select

 update_timestamp_inputs = ->
-  $('#Date').attr('value',(new Date).toISOString())
+  $('input[type=timestamp]').attr('value',(new Date).toISOString())

 build_input_for_property = (property) ->
   input = switch $(property).attr('type')
@@ -48,7 +48,7 @@
       pagedown_container.append $('<label>').append('Preview:')
       pagedown_container.append pagedown_preview
       pagedown_container
-    when 'string'
+    when 'string', 'datetime'
       if $(property).find('valueList').length > 0
         build_input_for_valuelist $(property).find('valueList')[0]
       else
@@ -59,8 +59,8 @@
         $('<input>').attr('style','width:100%').prop('disabled',true)
       else
         $('<input>').attr('style','width:100%')
-    when 'datetime'
-      $('<input>').attr('style','width:50%').attr('type','datetime').prop('disabled',true).attr('style','display:block')
+    when 'timestamp'
+      $('<input>').attr('style','width:50%').attr('type','timestamp').prop('disabled',true).attr('style','display:block')
     else
       console.log 'Error: unknown type'
       $('<input>')


Allow override of FUSION_TABLES_URI.

Allow override of FUSION_TABLES_URI.

--- src/js/collection-editor.coffee
+++ src/js/collection-editor.coffee
@@ -415,6 +415,9 @@
       $('#collection_select').change()

 merge_config_parameters = ->
+  if window.FUSION_TABLES_URI?
+    FUSION_TABLES_URI = window.FUSION_TABLES_URI
+
   cite_collection_editor_config = $.extend({}, default_cite_collection_editor_config, window.cite_collection_editor_config)
   google_oauth_parameters_for_fusion_tables['client_id'] = cite_collection_editor_config['google_client_id']
   return cite_collection_editor_config


Update en.coffee

Update en.coffee

one French word lost in translation in the en file

--- client/app/locales/en.coffee
+++ client/app/locales/en.coffee
@@ -32,7 +32,7 @@
       # Menu
       "menu compose"            : "Compose"
       "menu account new"        : "New account"
-      "menu settings"           : "Paramètres"
+      "menu settings"           : "Parameters"

       # List
       "list empty"              : "No email in this box."


removed object URLs and FireFox likes it

--- client/src/app/puppet.coffee
+++ client/src/app/puppet.coffee
@@ -128,19 +128,14 @@
 destroyAssets = (oldAssets) ->
   for name, dataUrl of oldAssets.urls
     splitUrl = RW.splitDataUrl(dataUrl)
-    if splitUrl.mimeType in ["application/javascript", "text/javascript"]
-      # Nothing to do, cannot unload JS!
-    else if splitUrl.mimeType == "text/css"
-      oldAssets.data[name].remove()
-    else if splitUrl.mimeType.indexOf("image/") == 0
-      URL.revokeObjectURL(oldAssets.objectUrls[name])
+    # Cannot unload JS or images
+    if splitUrl.mimeType == "text/css" then oldAssets.data[name].remove()

 # The evaluator is initialized with any JS assets.
-# Returns object containing three maps: { urls: , data: , objectUrls: }
+# Returns object containing two maps: { urls: , data: }
 # TODO: move asset handling into gamEvolve.coffee. CSS could be handled by the HTML io. JS is harder.
 createAssets = (inputAssets, evaluator) ->
   assetNamesToData = {}
-  assetNamesToObjectUrls = {}

   # Create new assets
   for name, dataUrl of inputAssets
@@ -153,20 +148,15 @@
         css = atob(splitUrl.data)
         assetNamesToData[name] = $('<style type="text/css"></style>').html(css).appendTo("head")
       else if splitUrl.mimeType.indexOf("image/") == 0
-        blob = RW.dataURLToBlob(dataUrl)
-        objectUrl = URL.createObjectURL(blob)
-
         image = new Image()
-        image.src = objectUrl
-        image.onload = -> console.log("Image '#{name}' loaded")
+        image.src = dataUrl
         image.onerror = -> console.error("Cannot load image '#{name}'")

-        assetNamesToObjectUrls[name] = objectUrl
         assetNamesToData[name] = image
       else
         assetNamesToData[name] = atob(splitUrl.data)

-  return { urls: inputAssets, data: assetNamesToData, objectUrls: assetNamesToObjectUrls }
+  return { urls: inputAssets, data: assetNamesToData }

 loadGameCode = (gameCode, logFunction) ->
   evaluator = eval


Open flextab when receiva call and hide video if url is undefined

--- client/views/app/room.coffee
+++ client/views/app/room.coffee
@@ -500,12 +500,22 @@

 	webrtc.to = this.data._id.replace(Meteor.userId(), '')
 	webrtc.onRemoteUrl = (url) ->
-		$('video.video-remote')[0].src = url
-		$('video.video-remote').css('display', 'block')
+		Session.set('flexOpened', true)
+		Meteor.defer ->
+			$('video.video-remote')[0].src = url
+			if url
+				$('video.video-remote').css('display', 'block')
+			else
+				$('video.video-remote').css('display', 'none')

 	webrtc.onSelfUrl = (url) ->
-		$('video.video-self')[0].src = url
-		$('video.video-self').css('display', 'block')
+		Session.set('flexOpened', true)
+		Meteor.defer ->
+			$('video.video-self')[0].src = url
+			if url
+				$('video.video-self').css('display', 'block')
+			else
+				$('video.video-self').css('display', 'none')

 renameRoom = (rid, name) ->
 	console.log 'room renameRoom' if window.rocketDebug


Send NOK.

Send NOK.

--- src/aftership.coffee
+++ src/aftership.coffee
@@ -42,7 +42,7 @@
     secret = query.secret
     return res.status(403).send("NOK") if secret != process.env.AFTERSHIP_SECRET
     room   = data.msg.custom_fields?.room
-    return res.status(400) if not room
+    return res.status(400).send("NOK") if not room

     checkpoints = data.msg.checkpoints
     msgs = checkpoints.map (checkpoint) ->


Handle webhook test.

Handle webhook test.

--- src/aftership.coffee
+++ src/aftership.coffee
@@ -41,6 +41,7 @@
     data   = if req.body.payload? then JSON.parse req.body.payload else req.body
     secret = query.secret
     return res.status(403).send("NOK") if secret != process.env.AFTERSHIP_SECRET
+    return res.status(200).send("OK") if data.msg.id == "000000000000000000000000"
     room   = data.msg.custom_fields?.room
     return res.status(400).send("NOK") if not room


Save parameters in SQLite on successful bootstrapping

--- src/bootstrap.coffee
+++ src/bootstrap.coffee
@@ -54,4 +54,19 @@
 			fs.writeFileAsync('/supervisor/data/client.crt', body.cert)
 			vpnConf
 		])
-	).then(knex('config').select('value').where(key: 'uuid')
+	).then(->
+		console.log('Finishing bootstrapping')
+		Promise.all([
+			knex('config').truncate().then(config).then((config) ->
+				knex('config').insert([
+					{key: 'uuid', value: uuid}
+					{key: 'apiKey', value: config.apiKey}
+					{key: 'username', value: config.username}
+					{key: 'userId', value: config.userId}
+				])
+			)
+			knex('app').truncate()
+		])
+	).catch((error) ->
+		console.log(error)
+	)


clampable

clampable

--- src/clampable.coffee
+++ src/clampable.coffee
@@ -1,7 +1,6 @@
 Clampable = (I={}, self) ->
   # Set some default properties
   Object.reverseMerge I,
-

   # Add events and methods here
   self.bind "afterUpdate", ->


Fix protractor tests

Fix protractor tests


72nd Commit

72nd Commit

--- protractor.conf.coffee
+++ protractor.conf.coffee
@@ -3,7 +3,7 @@

 class Config
   @directConnect: true
-  @specs: ['./src/**/*.e2e-spec.js']
+  @specs: ['./src/**/*.e2e-spec.ts']
   @exclude: []
   @capabilities:
     browserName: 'chrome'


Synchronize color inputs

Synchronize color inputs

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -24,11 +24,16 @@
     )

   synchronizeInputs: ->
-    $("[name='progress_bar[percentage]']").on
+    progress_bar = "[name='progress_bar[percentage]']"
+    process_background = "[name='legislation_process[background_color]']"
+    process_font = "[name='legislation_process[font_color]']"
+
+    inputs = $("#{progress_bar}, #{process_background}, #{process_font}")
+    inputs.on
       input: ->
         $("[name='#{this.name}']").val($(this).val())

-    $("[name='progress_bar[percentage]'][type='range']").trigger("input")
+    inputs.trigger("input")

   hideOrShowFieldsAfterSelection: ->
     $("[name='progress_bar[kind]']").on


removendo smooth scrolling de go to top

removendo smooth scrolling de go to top

--- app/assets/javascripts/index.js.coffee
+++ app/assets/javascripts/index.js.coffee
@@ -1,14 +1,6 @@
-scrollSpeed = 'fast'
-fadeSpeed = 'fast'
-minimumWindowScrollHeight = 200
-
 # go top button based on a tutplus tutorial
 # link: http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/quick-tip-implement-a-sticky-back-to-top-button/
-
-$('#go-top').click (e) ->
-  $('html, body').animate
-    scrollTop : 0
-  , scrollSpeed
+minimumWindowScrollHeight = 200

 goTop = $('#go-top')
 $(window).scroll ->


comment out debug output

comment out debug output

--- app/assets/javascripts/modal.js.coffee
+++ app/assets/javascripts/modal.js.coffee
@@ -87,9 +87,9 @@
     return false

   showError= (jqXHR, textStatus, errorThrown) ->
-    console.log("jqXHR",jqXHR)
-    console.log("textStatus",textStatus)
-    console.log("errorThrown",errorThrown)
+    # console.log("jqXHR",jqXHR)
+    # console.log("textStatus",textStatus)
+    # console.log("errorThrown",errorThrown)
     # console.log 'Loading error'
     InfoDialog.hideLoading()
     # restore url -> remove ?overflow=...


Fixed autoscroll not tunerd on in new rooms

--- app/assets/javascripts/rooms.js.coffee
+++ app/assets/javascripts/rooms.js.coffee
@@ -29,6 +29,7 @@
   constructor: (@room_id, @container) ->
     @_subscribe()
     @container.scroll(@_onChatScroll)
+    @_onChatScroll()
     @scrollToBottom()

   _subscribe: ->


cleanup routes indentation

cleanup routes indentation

--- app/express.coffee
+++ app/express.coffee
@@ -78,28 +78,29 @@
   configure()

 # Routes
-app.get  '/',                         routes.main.index
+app.get  '/',                                      routes.main.index

-app.get  '/users',                    routes.users.index
-app.post '/users.:format?',           routes.users.create
-app.get  '/users/:name.:format?',     routes.users.show
+app.get  '/users',                                 routes.users.index
+app.post '/users.:format?',                        routes.users.create
+app.get  '/users/:name.:format?',                  routes.users.show

-app.get  '/join',                     routes.join.index
+app.get  '/join',                                  routes.join.index

-app.get  '/login',                    routes.login.index
-app.post '/login.:format?',           routes.login.verify
-app.get  '/logout.:format?',          routes.logout.index
+app.get  '/login',                                 routes.login.index
+app.post '/login.:format?',                        routes.login.verify
+app.get  '/logout.:format?',                       routes.logout.index

-app.get  '/arguments/new',            routes.arguments.new
-app.post '/arguments.:format?',       routes.arguments.create
-app.get  '/arguments/:hash.:format?', routes.arguments.show
+app.get  '/arguments/new',                         routes.arguments.new
+app.post '/arguments.:format?',                    routes.arguments.create
+app.get  '/arguments/:hash.:format?',              routes.arguments.show
 app.get  '/arguments/:hash/propositions.:format?', routes.arguments.propositions

-app.get  '/tags/:hash.:format?',      routes.tags.show
-app.post '/tags.:format?',            routes.tags.create

-app.get  '/:name.:format?',           routes.users.public
-app.get  '/:name/:repo.:format?',     routes.repos.show
+app.get  '/tags/:hash.:format?',                   routes.tags.show
+app.post '/tags.:format?',                         routes.tags.create
+
+app.get  '/:name.:format?',                        routes.users.public
+app.get  '/:name/:repo.:format?',                  routes.repos.show

 # Http
 http.createServer(app).listen(3000)


Point "missing )/}/]" errors to the unclosed (/{/[

Previously such errors pointed at the end of the input, which wasn't very
helpful. This is also consistent with unclosed strings, where the errors point
at the opening quote.

Note that this includes unclosed #{ (interpolations).

--- lexer.coffee
+++ lexer.coffee
@@ -73,8 +73,7 @@
       return {@tokens, index: i} if opts.untilBalanced and @ends.length is 0

     @closeIndentation()
-    # TODO: Make this error point to the opener!
-    @error "missing #{tag}" if tag = @ends.pop()
+    throwSyntaxError "missing #{end.tag}", end.origin[2] if end = @ends.pop()
     return @tokens if opts.rewrite is off
     (new Rewriter).rewrite @tokens

@@ -319,7 +318,7 @@
       diff = size - @indent + @outdebt
       @token 'INDENT', diff, indent.length - size, size
       @indents.push diff
-      @ends.push 'OUTDENT'
+      @ends.push {tag: 'OUTDENT'}
       @outdebt = @indebt = 0
       @indent = size
     else if size < @baseIndent
@@ -419,10 +418,11 @@
         tag = 'INDEX_START'
         switch prev[0]
           when '?'  then prev[0] = 'INDEX_SOAK'
+    token = @makeToken tag, value
     switch value
-      when '(', '{', '[' then @ends.push INVERSES[value]
+      when '(', '{', '[' then @ends.push {tag: INVERSES[value], origin: token}
       when ')', '}', ']' then @pair value
-    @token tag, value
+    @tokens.push token
     value.length

   # Token Manipulators
@@ -572,7 +572,7 @@
   # Pairs up a closing token, ensuring that all listed pairs of tokens are
   # correctly balanced throughout the course of the token stream.
   pair: (tag) ->
-    unless tag is wanted = last @ends
+    unless tag is wanted = last(@ends)?.tag
       @error "unmatched #{tag}" unless 'OUTDENT' is wanted
       # Auto-close INDENT to support syntax like this:
       #


Selection may change before inspector is created

--- assets/demo.coffee
+++ assets/demo.coffee
@@ -21,7 +21,7 @@
       removeAttachment(attachment)

     didChangeSelection: ->
-      inspectorController.render()
+      inspectorController?.render()

     didRenderDocument: ->
       inspectorController?.render()


Add language-sca to atom package list

Add language-sca to atom package list

--- atom/packages.cson
+++ atom/packages.cson
@@ -20,6 +20,7 @@
   "language-patch"
   "language-pegjs"
   "language-prolog"
+  "language-sca"
   "language-svg"
   "language-tcl"
   "language-viml"


Add Atom package auto-update-packages

Add Atom package auto-update-packages

--- atom/packages.cson
+++ atom/packages.cson
@@ -3,6 +3,7 @@
   "atom-elixir"
   "atom-wrap-in-tag"
   "atomic-chrome"
+  "auto-update-packages"
   "close-tags"
   "date"
   "elmjutsu"


more TYPO3 snippets

more TYPO3 snippets

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -87,3 +87,15 @@
   'TYPO3 TypoScriptService':
    'prefix': 't3ts'
    'body': '\\\\TYPO3\\\\CMS\\\\Extbase\\\\Service\\\\TypoScriptService'
+
+  'TYPO3 Descending':
+   'prefix': 't3desc'
+   'body': '\\\\TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\QueryInterface::ORDER_DESCENDING'
+
+  'TYPO3 Ascending':
+   'prefix': 't3asc'
+   'body': '\\\\TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\QueryInterface::ORDER_ASCENDING'
+
+  'TYPO3 ImageService':
+    'prefix': 't3img'
+    'body': '\\\\TYPO3\\\\CMS\\\\Extbase\\\\Service\\\\ImageService'


Fix 'with open()' context manager snippet

--- atom/snippets.cson
+++ atom/snippets.cson
@@ -113,7 +113,7 @@

   'with open()':
     'prefix': 'witho'
-    'body': "with open(${1:path}, '${2:r}'):\n\t${3:pass}\n$4"
+    'body': "with open(${1:path}, '${2:r}') as ${3:file}:\n\t${4:pass}\n$5"

 # PHP blocks/statements (meant to be used within PHP source blocks)


HexBox: find 'corner fit' status

HexBox: find 'corner fit' status

--- views/puzzle.coffee
+++ views/puzzle.coffee
@@ -11,16 +11,16 @@
     @events = new EventHandler(this)

     @pattern = new PuzzlePattern(this)
-#    @mask = new MissingPiecesMask(this)
-#    @piece = new PuzzlePiece(this)
+    @mask = new MissingPiecesMask(this)
+    @piece = new PuzzlePiece(this)

 #    @pattern.draw_pattern()
-#    @piece.construct_piece("e")
-#    @piece.draw_piece(0,0)
+    @piece.construct_piece("h")
+    @piece.draw_piece(0,0)

 #    @piece.draw_piece_ab(@piece.box.anchor_hex[0],@piece.box.anchor_hex[1])

-    @hex_box.set_hex_box("f")
+    @hex_box.set_hex_box("h")

     @hex_draw.draw_all_hexes()

@@ -616,18 +616,28 @@
       aa = hx[0]
       b2 = 2*hx[1] + aa%2 - 1
       @test_left_right_top_bottom(aa,b2)
-    if @left%2 == 1
-      @first_column = "odd"
-    else
-      @first_column = "even"
+    @get_corner_fit()

     console.log("HexBox: left = "+@left)
     console.log("HexBox: right = "+@right)
     console.log("HexBox: top = "+@top)
     console.log("HexBox: bottom = "+@bottom)
 #    console.log("HexBox: ")
-    console.log("HexBox: first column = "+@first_column)
+    console.log("HexBox: corner fit = "+@corner_fit)
 #    console.log("HexBox: ")
+
+
+  get_corner_fit: () ->
+    if @left%2 == 1
+      if @top%2 == 1
+        @corner_fit = "low"
+      else
+        @corner_fit = "high"
+    else
+      if @top%2 == 1
+        @corner_fit = "high"
+      else
+        @corner_fit = "low"

   test_left_right_top_bottom: (aa,b2) ->
@@ -670,8 +680,6 @@
     for bb in [1..10]
       for aa in [1..24]
         hexes.push([aa,bb]) if @puzzle.pattern.grid[bb][aa] == piece_symbol
-    console.log("PIECE ID: "+piece_symbol)
-    console.log("    "+hx[0]+","+hx[1]) for hx in hexes
     return hexes


moving token replacer to utils

moving token replacer to utils

--- lib/Tokenized.coffee
+++ lib/Tokenized.coffee
@@ -2,7 +2,7 @@
 StringFile = require('./StringFile.coffee')
 _console = require('./Console.coffee')
 mime = require('mime')
-TokenReplacer = require('./TokenReplacer')
+TokenReplacer = require('./Utils').TokenReplacer

 module.exports = class Tokenized extends CompiledFile


Add settings for hostname and port

Add settings for hostname and port

--- lib/broadcast.coffee
+++ lib/broadcast.coffee
@@ -6,6 +6,10 @@
 sockets = []

 module.exports =
+  configDefaults:
+    hostname: 'localhost'
+    port: 8000
+
   activate: ->
     atom.workspaceView.command "broadcast:start", => @start()
     atom.workspaceView.command "broadcast:stop", => @stop()
@@ -29,6 +33,9 @@

     fileServer = new nodeStatic.Server filePath

+    hostname = atom.config.get('broadcast.hostname') or 'localhost'
+    port = atom.config.get('broadcast.port') or 8000
+
     http = require 'http'
     server = http.createServer (req, res) =>
       req.addListener 'end', =>
@@ -38,12 +45,12 @@
             res.writeHead 200, {'Content-Type': 'text/html'}
             res.end template
       .resume()
-    .listen 8000, '127.0.0.1'
+    .listen port, hostname

     server.addListener 'connection', (socket) =>
       sockets.push socket

-    console.log 'Broadcast started.'
+    console.log "Broadcast started at http://#{hostname}:#{port}"

   stop: ->
     if server is null


Fixed the "filename is not defined" message when using .styl files without having nib installed.

--- lib/compilers.coffee
+++ lib/compilers.coffee
@@ -32,7 +32,7 @@
 if stylus?
   compilers.styl =
     targetExt: "css"
-    render: (code, cb) ->
+    render: (filename, code, cb) ->
       stylus(code)
       .set('filename', filename)
       .render cb


fix order form bug with US country name

fix order form bug with US country name

--- assets/coffeescript/pupil_store.coffee
+++ assets/coffeescript/pupil_store.coffee
@@ -585,7 +585,7 @@

           country = countryList[_val]

-          console.log "country is: #{JSON.stringify(country)}"
+          # console.log "country is: #{JSON.stringify(country)}"
           # if country US or CA force state abbreviations
           if country.countryISO is 'US' or country.countryISO is 'CA'
             placeholderTxt = if country.countryISO is 'US' then "state abbreviation (e.g. CA for California)" else "province abbreviation (e.g. QC for Quebec)"
@@ -628,7 +628,7 @@
             validity = true
           if $("#country_s").val() == value && value in usa_alternates
             _val = 'United States Of America'
-            $("#country_b").val(_val)
+            $("#country_s").val(_val)
             update_country_dependents(_val)
             validity = true


remove some more code

remove some more code

--- lib/atom-lupa.coffee
+++ lib/atom-lupa.coffee
@@ -1,7 +1,6 @@
 AtomLupaView = require './atom-lupa-view'
 {CompositeDisposable, Range} = require 'atom'

-console.log("lupa module")
 module.exports = AtomLupa =
   atomLupaView: null
   modalPanel: null
@@ -19,61 +18,24 @@

-    #@atomLupaView.setContent '
kotek
 na plotek i mruga!'
+    # @subscriptions = new CompositeDisposable
+    #
+    # # Register command that toggles this view
+    # @subscriptions.add atom.commands.add 'atom-workspace', 'atom-lupa:toggle': => @toggle()
+    # sel = '.lupa-structure .lupa-entity'
+    # @subscriptions.add atom.commands.add sel, 'atom-lupa:lupa-structure-enter': => @enter()

-
-    # @modalPanel = atom.workspace.addModalPanel(item: @atomLupaView.getElement(), visible: false)
-
-    # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
-    @subscriptions = new CompositeDisposable
-
-    # Register command that toggles this view
-    @subscriptions.add atom.commands.add 'atom-workspace', 'atom-lupa:toggle': => @toggle()
-    sel = '.lupa-structure .lupa-entity'
-    @subscriptions.add atom.commands.add sel, 'atom-lupa:lupa-structure-enter': => @enter()
-
-    editor = atom.workspace.getActiveTextEditor()
-
-
-    plugin = require './init.coffee'
-
-    activePane = atom.workspace.paneContainer.activePane
-    @atomLupaView = new AtomLupaView()
-    activePane.addItem(@atomLupaView)
+    # editor = atom.workspace.getActiveTextEditor()
+    #
+    #
+    # plugin = require './init.coffee'
+    #
+    # activePane = atom.workspace.paneContainer.activePane
+    # @atomLupaView = new AtomLupaView()
+    # activePane.addItem(@atomLupaView)

   deactivate: ->
-    @modalPanel.destroy()
-    @subscriptions.dispose()
-    @atomLupaView.destroy()
-
-  serialize: ->
-    #atomLupaViewState: @atomLupaView.serialize()
-
-  enter: (e)->
-      entity = window.lupaActiveEntity
-      loc = entity.loc
-      if loc
-          pos = [loc.start.line - 1, loc.start.column]
-          if entity.file && entity.file.path
-              window.lupaEmit('goToFile', [entity.file.path, loc.start.line])
-          else
-              window.lupaGoToPos(pos)
-      else if entity.source
-          window.lupaEmit('goToFile', [entity.source])
-
-
-  toggle: ->
-    console.log 'AtomLupa was toggled!'
-    structure = document.getElementById('lupa-structure')
-    if structure
-        search = structure.querySelector('.lupa-search')
-        if search
-            search.focus()
-
-    # if @modalPanel.isVisible()
-    #   @modalPanel.hide()
-    # else
-    #   @modalPanel.show()
-  provide: ->
-    #require './autocomplete'
+    # @modalPanel.destroy()
+    # @subscriptions.dispose()
+    # @atomLupaView.destroy()


Handle storage:error actions

Handle storage:error actions


Activity: socialApiController api order fix

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -75,7 +75,6 @@

     return m

-  mapActivities: mapActivities
   mapActivities = (messages)->
     # if no result, no need to do something
     return messages unless messages
@@ -87,6 +86,8 @@
     revivedMessages = (mapActivity message for message in messages)
     return revivedMessages

+  mapActivities: mapActivities
+
   getCurrentGroup = (callback)->
     groupsController = KD.getSingleton "groupsController"
     groupsController.ready ->
@@ -184,7 +185,6 @@
       mappedAccounts.push {_id: account, constructorName : "JAccount"}
     return mappedAccounts

-  mapChannels: mapChannels
   mapChannels = (channels)->
     return channels unless channels
     revivedChannels = []
@@ -201,6 +201,8 @@
     # bind all events
     registerAndOpenChannels revivedChannels
     return revivedChannels
+  mapChannels: mapChannels
+

   forwardMessageEvents = (source, target,  events)->
     events.forEach (event) ->


activity: define channel name helper function

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -37,7 +37,11 @@
         channelName: slug
         isExclusive: yes

-      channelName   = "socialapi.#{slug}-group-#{slug}"
+      channelName    = generateChannelName
+        name         : slug
+        typeConstant : 'group'
+        groupName    : slug
+
       brokerChannel = KD.remote.subscribe channelName, subscriptionData
       @forwardMessageEvents brokerChannel, this, ["MessageAdded", "MessageRemoved"]
       @openedChannels[name] = brokerChannel
@@ -220,15 +224,14 @@

     getCurrentGroup (group)->
       for socialApiChannel in socialApiChannels
-        {typeConstant, name, groupName} = socialApiChannel
-        channelName = "socialapi.#{groupName}-#{typeConstant}-#{name}"
+        channelName = generateChannelName socialApiChannel
         continue  if socialapi.openedChannels[channelName]
         socialapi.openedChannels[channelName] = {} # placeholder to avoid duplicate registration

         subscriptionData =
           serviceType: 'socialapi'
           group      : group.slug
-          channelType: typeConstant
+          channelType: socialApiChannel.typeConstant
           channelName: channelName
           isExclusive: yes

@@ -241,6 +244,9 @@
           ]

           socialapi.emit "ChannelRegistered-#{name}", brokerChannel
+
+  generateChannelName = ({name, typeConstant, groupName}) ->
+    return "socialapi.#{groupName}-#{typeConstant}-#{name}"

   message:
     edit   :(args...)-> messageApiMessageResFunc 'edit', args...


Fix skip.

Fix skip.

--- app.coffee
+++ app.coffee
@@ -134,9 +134,31 @@

   skip: (k) ->
     console.log "skip #{k}"
+    console.log "startTs_1 = #{@startTs}"
+    @pause()
+
+    @startTs += k*parseFloat($('#skip-val').val())
+    console.log "startTs_2 = #{@startTs}"
+
+    dt = now() - @startTs
+    while (@pos > 0) and (@srt.events[@pos-1].ts > dt)
+      @pos -= 1
+
+    @play()
+
+    console.log "startTs_3 = #{@startTs}"

   speedup: ->
-    console.log "speedup"
+    @pause()
+
+    k = 1.0/parseFloat($('#speedup-val'))
+    console.log "speedup #{k}"
+
+    dt = now() - @startTs
+    @transform(k, 0.0)
+    @startTs = now() - k*dt
+
+    @play()

   showPicker: ->
     if $('#show-picker').prop('checked')


fixes?

fixes?

--- lib/flow-view.coffee
+++ lib/flow-view.coffee
@@ -1,4 +1,4 @@
-{$} = require "atom"
+{$, View} = require "atom"
 joint = require "./util/joint"
 {Graph, Link, Paper} = joint.dia
 _ = require "lodash"
@@ -200,7 +200,7 @@
       position:
         x: 470
         y: 160
-
+
       attrs:
         ".label":
           text: "accept"


heh, testing before deploying is often important

--- app.coffee
+++ app.coffee
@@ -37,7 +37,7 @@
   convert: (string) ->
     str = string
     stringArray = str.split('')
-    id = a.compute(stringArray)
+    id = @compute(stringArray)

 app.get '/avatar/:name', (req, res) ->


Reduce logging verbosity, add status JSON path.

Resolves #34

--- app.coffee
+++ app.coffee
@@ -1,6 +1,8 @@
 express = require 'express'
 app = module.exports = express.createServer()
 io = (require 'socket.io').listen(app)
+console.log io
+io.settings['log level'] = 1
 fs = require 'fs'
 eco = require 'eco'

@@ -34,6 +36,10 @@
   else if room.top? and room.top.socket?
     room.top.socket.emit event, data

+pruneRooms = ->
+  for i in [(rooms.length - 1)..0]
+    if rooms[i]? and not (rooms[i].top? or rooms[i].bottom?)
+      rooms.splice i, 1

 # Setup Template Engine
 app.set 'views', __dirname + '/views'
@@ -55,6 +61,17 @@
   response.render 'home',
     "config": config
     "home": true
+
+app.get '/status', (request, response) ->
+  pruneRooms()
+  payload = {}
+  payload.userCount = 0
+  payload.activeRooms = rooms.length
+  for room in rooms
+    payload.userCount += room.top? + room.bottom?
+  response.contentType 'application/json'
+  response.write JSON.stringify payload
+  response.end()

 app.get '/:token', (request, response) ->
   token = request.params.token
@@ -132,6 +149,7 @@
             room.bottom = null
           else
             rooms.splice i, 1
+      pruneRooms()

   console.log "Listening on #{config.host}:#{config.port}"
 fs.readFile __dirname + '/config.json', 'utf8', start


Removed FastCast tracker as it broke the client

--- app.coffee
+++ app.coffee
@@ -4,8 +4,7 @@
   [ 'udp://open.demonii.com:1337' ],
   [ 'udp://tracker.webtorrent.io:80' ],
   [ 'wss://tracker.webtorrent.io' ],
-  [ 'wss://tracker.btorrent.xyz' ],
-  [ 'ws://tracker.fastcast.nz' ]
+  [ 'wss://tracker.btorrent.xyz' ]
 ]

 opts = {announce: trackers}


Send 404 header

Send 404 header

--- app.coffee
+++ app.coffee
@@ -215,7 +215,7 @@
 bind api, "", app

 app.get "*", (req, res) ->
-  res.sendfile __dirname + '/pages/404.html', 404
+  res.status(404).sendfile(__dirname + '/pages/404.html')

 # Start Server
 server = app.listen(port)


Fix IsEmpty method

Fix IsEmpty method

--- client/app/stores/message_store.coffee
+++ client/app/stores/message_store.coffee
@@ -49,10 +49,8 @@
                 result[key] = value
         result

-    _isFilterEmpty = (str, filter) ->
-        if not filter or not _.isObject filter
-            filter = _getFilter() or {}
-        not (value = filter[str]) or value is '-'
+    _isFilter = (value) ->
+        value and value isnt '-'

     _getURISort = (filter) ->
         filter = _getFilter() unless filter
@@ -643,8 +641,9 @@
     #
     # return a {string}
     getQueryKey: (str = '') ->
+        filter = _getFilter()
         filterize = (key) ->
-            filter[key] unless _isFilterEmpty key
+            filter[key] if _isFilter filter[key]

         keys = _.compact ['before', 'after'].map filterize
         keys.unshift str unless _.isEmpty str
@@ -659,13 +658,13 @@
             encodeURIComponent _getURISort()

         url = "mailbox/#{mailboxID}/?sort=#{sort}"
-        if filter.type is 'flag' and _isFilterEmpty 'value', filter
+        if filter.type is 'flag' and _isFilter filter.value
             url += "&flag=#{filter.value}"

-        if _isFilterEmpty 'before', filter
+        if _isFilter filter.before
             url += "&before=#{encodeURIComponent filter.before}"

-        if _isFilterEmpty 'after', filter
+        if _isFilter filter.after
             url += "&after=#{encodeURIComponent filter.after}"
         return url


Fix automatic selection of default calendar

--- client/app/views/settings_modal.coffee
+++ client/app/views/settings_modal.coffee
@@ -44,7 +44,7 @@
         @defaultCalendar = new ComboBox
             el: @$('#default-calendar')
             source: app.calendars.toAutoCompleteSource()
-        @defaultCalendar.setValue window.app.settings.get('defaultCalendar')
+            current: app.settings.get('defaultCalendar')
         @defaultCalendar.on 'change', @defaultCalendarChange

         @$('#importviewplaceholder').append new ImportView().render().$el


Tests: Environments: VM: Fixed checkVMDiskUsage test.

--- client/test/lib/environments/vm.coffee
+++ client/test/lib/environments/vm.coffee
@@ -35,21 +35,18 @@

   checkVMDiskUsage: (browser) ->

-    diskUsageSelector     = modalSelector + ' .diskusage'
-    diskUsageBarSelector  = diskUsageSelector + ' .input-wrapper .progressbar-container.disk-usage'
+    diskUsageSelector  = modalSelector + ' .disk-usage'
+    circularBar        = '.disk-usage-info .circular-progress-bar'

     helpers.beginTest(browser)
     helpers.waitForVMRunning(browser)

-    environmentHelpers.openVmSettingsModal(browser)
+    environmentHelpers.openDiskUsageSettings(browser)

     browser
       .waitForElementVisible   diskUsageSelector, 20000
-      .waitForElementVisible   diskUsageSelector + ' label.disk-usage', 20000
-      .waitForElementVisible   diskUsageBarSelector, 20000
-      .waitForElementVisible   diskUsageBarSelector + ' .bar', 20000 # Assertion
-      .moveToElement           diskUsageBarSelector + ' .bar span.light-label', 70, 7 # Assertion
-      .pause  3000
-      .waitForElementVisible   '.kdtooltip', 20000 # Assertion
+      .click                   diskUsageSelector
+      .waitForElementVisible   circularBar, 20000
+      .assert.containsText     circularBar + ' span.percentage', '53%' # Assertion
       .end()


minor doc changes

minor doc changes

--- library/membrane/securing.coffee
+++ library/membrane/securing.coffee
@@ -41,7 +41,7 @@
 tools = require "../nucleus/toolkit"

 # This is an abstract base class API stub service. Its purpose is
-# providing the boilerplate for ensuring that the connection is
+# providing the boilerplate for ensuring that HTTP/S connection is
 # going through the master server! If a request is not going via
 # master server then deny current request with an error code. Be
 # aware that this compound will be effective for all HTTP methods.
@@ -75,7 +75,7 @@
         return response.end content

 # This is an abstract base class API stub service. Its purpose is
-# providing the boilerplate for ensuring that the connection is
+# providing the boilerplate for ensuring that HTTP/S connection is
 # going through the HTTPS channel. If a request is not going via
 # SSL transport then redirect the current request to such one. Be
 # aware that this compound will be effective for all HTTP methods.


now supports words like 'constructor'

now supports words like 'constructor'

--- lib/jsMegaHal.coffee
+++ lib/jsMegaHal.coffee
@@ -21,7 +21,7 @@
 	sentenceRegex: /[!?\.]/

 	#all of the current words jsMegaHal knows
-	words: {}
+	words: Object.create null

 	#all of the quads, mapped by quad.hash() -> quad
 	quads: {}


Remove unnecessary returns

Remove unnecessary returns

--- app/assets/javascripts/bookingsync_portal/admin/application.js.coffee
+++ app/assets/javascripts/bookingsync_portal/admin/application.js.coffee
@@ -42,8 +42,8 @@
       start: (e, ui) ->
         $(ui.helper).addClass "ui-draggable-helper"

-    return @each () ->
-          $(this).draggable(settings)
+    @each () ->
+      $(this).draggable(settings)

   droppableRemoteRental: ->
     settings =
@@ -80,5 +80,5 @@
             $(@).addClass('loading')
           success: ->
             $(@).removeClass('loading')
-    return @each () ->
-          $(this).droppable(settings)
+    @each () ->
+      $(this).droppable(settings)


dont refresh user after saving settings

dont refresh user after saving settings

--- components/settings/client/view.coffee
+++ components/settings/client/view.coffee
@@ -64,8 +64,7 @@
       @showError "Invalid, try again"
     else
       analytics.track.submit 'User settings changed'
-      $.ajax('/me/refresh').then -> window.location.reload()
-
+      window.location.reload()

   showError: (msg) =>
     @$('button').attr 'data-state', 'error'


fix #240 Should Ignore mouse event on insert-mode and cleanup

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -173,37 +173,39 @@
     } = {}
     @emitter.emit 'did-destroy'

-  checkSelection: (event) =>
-    return unless @editor?
-    return if @operationStack.isProcessing()
-
-    if haveSomeSelection(@editor)
-      submode = swrap.detectVisualModeSubmode(@editor)
-      if @isMode('visual', submode)
-        @updateCursorsVisibility()
-      else
-        @activate('visual', submode)
-    else
-      @activate('normal') if @isMode('visual')
-
   observeSelection: ->
-    @editorElement.addEventListener('mouseup', @checkSelection)
-    @subscriptions.add new Disposable =>
-      @editorElement.removeEventListener('mouseup', @checkSelection)
-
-    isInteresting = ({target, type}) =>
+    isInterestingEvent = ({target, type}) =>
       if @mode is 'insert'
         false
       else
-        target is @editorElement and not type.startsWith('vim-mode-plus:')
-
-    @subscriptions.add atom.commands.onWillDispatch (event) =>
-      if isInteresting(event)
-        for selection in @editor.getSelections()
-          swrap(selection).preserveCharacterwise()
-
-    @subscriptions.add atom.commands.onDidDispatch (event) =>
-      @checkSelection() if isInteresting(event)
+        @editor? and target is @editorElement and not type.startsWith('vim-mode-plus:')
+
+    onInterestingEvent = (fn) ->
+      (event) -> fn() if isInterestingEvent(event)
+
+    _checkSelection = =>
+      return if @operationStack.isProcessing()
+      if haveSomeSelection(@editor)
+        submode = swrap.detectVisualModeSubmode(@editor)
+        if @isMode('visual', submode)
+          @updateCursorsVisibility()
+        else
+          @activate('visual', submode)
+      else
+        @activate('normal') if @isMode('visual')
+
+    _preserveCharacterwise = =>
+      for selection in @editor.getSelections()
+        swrap(selection).preserveCharacterwise()
+
+    checkSelection = onInterestingEvent(_checkSelection)
+    preserveCharacterwise = onInterestingEvent(_preserveCharacterwise)
+
+    @editorElement.addEventListener('mouseup', checkSelection)
+    @subscriptions.add new Disposable =>
+      @editorElement.removeEventListener('mouseup', checkSelection)
+    @subscriptions.add atom.commands.onWillDispatch(preserveCharacterwise)
+    @subscriptions.add atom.commands.onDidDispatch(checkSelection)

   resetNormalMode: ->
     @editor.clearSelections()


Properly handle errors in spec tests with Promises

--- spec/beautify-languages-spec.coffee
+++ spec/beautify-languages-spec.coffee
@@ -146,7 +146,7 @@
                               oldHeader="beautified"
                               newHeader="expected"
                               diff = JsDiff.createPatch(fileName, oldStr, newStr, oldHeader, newHeader)
-                              throw new Error("Beautifier output does not match expected output:\n"+diff)
+                              expect(text).toEqual(expectedContents, "Beautifier output does not match expected output:\n"+diff)
                           # All done!
                           beautifyCompleted = true


Revert "Update deprecated require"

Revert "Update deprecated require"

This reverts commit 6b4de0a7ac1c0a8cb3039cc9154eda5b6a60a36b.

--- spec/default-file-icons-spec.coffee
+++ spec/default-file-icons-spec.coffee
@@ -1,6 +1,6 @@
 fs = require 'fs-plus'
 path = require 'path'
-{process} = require 'electron'
+process = require 'process'
 temp = require('temp').track()

 DefaultFileIcons = require '../lib/default-file-icons'


Re-add poll did not votes

Re-add poll did not votes

--- angular/core/models/poll_model.coffee
+++ angular/core/models/poll_model.coffee
@@ -42,6 +42,7 @@
       @belongsTo 'discussion'
       @hasMany   'pollOptions'
       @hasMany   'stances', sortBy: 'createdAt', sortDesc: true
+      @hasMany   'pollDidNotVotes'
       @hasMany   'communities'
       @hasMany   'visitors'


fix call faces

fix call faces

--- app/assets/javascripts/game.js.coffee
+++ app/assets/javascripts/game.js.coffee
@@ -104,7 +104,7 @@

   hair_options = ->
     res = ''
-    for k,v of game_faces.face_values['hair_color']
+    for k,v of game_faces().face_values['hair_color']
       res += "<option value=\"hair_color|#{k}\">#{v} colored hair</option>"
     res


always send message (blank if template)

always send message (blank if template)

--- app/assets/javascripts/text.js.coffee
+++ app/assets/javascripts/text.js.coffee
@@ -144,9 +144,6 @@

         if $('.message-custom-wrapper').hasClass('hidden')

-            # don't submit message data since we are using a template
-            $('#msg-text').remove()
-
             # add a DOM element to submit the templateData element
             $('<input form="send-message-form" name="templateData" id="template-data" type="hidden" value="1">').appendTo(".message-template-wrapper")


capture identify per animal not just final classification

--- app/controllers/animal_details.coffee
+++ app/controllers/animal_details.coffee
@@ -2,6 +2,7 @@
 template = require 'views/animal_details'
 PopupButton = require './popup_button'
 ImageChanger = require './image_changer'
+AnalyticsLogger = require 'lib/analytics'

 class AnimalDetails extends Controller
   animal: null
@@ -75,6 +76,7 @@
     @hide()

   onClickIdentify: ->
+    AnalyticsLogger.logEvent 'identify', @animal.id
     @classification.annotate
       species: @animal


update grammar

update grammar

--- grammars/marko.cson
+++ grammars/marko.cson
@@ -25,6 +25,9 @@
     include: "#comment-html"
   }
   {
+    include: "#comment-html-tag"
+  }
+  {
     include: "#html-block-concise"
   }
   {
@@ -55,7 +58,7 @@
 repository:
   "attr-assignment-concise":
     applyEndPatternLast: 1
-    begin: "(?:\\s+|[,])(?:(ref)|([a-zA-Z][a-zA-Z]*)|(data-[a-zA-Z-]*)|([_a-zA-Z0-9][a-zA-Z0-9:_.-]*))\\s*([=])\\s*"
+    begin: "(?:\\s+|[,])(?:(key)|([a-zA-Z][a-zA-Z]*)|([_a-zA-Z0-9][a-zA-Z0-9:_.-]*))\\s*([=])\\s*"
     beginCaptures:
       "1":
         name: "support.function.marko-attribute"
@@ -64,7 +67,79 @@
       "3":
         name: "entity.other.attribute-name.html"
       "4":
+        name: "punctuation.separator.key-value.html"
+    end: "(?=([,;]|\\s|$))"
+    patterns: [
+      {
+        include: "#expression"
+      }
+    ]
+  "attr-assignment-html":
+    applyEndPatternLast: 1
+    begin: "(?:\\s+|[,])(?:(key)|([a-zA-Z][a-zA-Z]*)|([_a-zA-Z0-9][a-zA-Z0-9:_.-]*))\\s*([=])\\s*"
+    beginCaptures:
+      "1":
         name: "support.function.marko-attribute"
+      "2":
+        name: "entity.other.attribute-name.html"
+      "3":
+        name: "entity.other.attribute-name.html"
+      "4":
+        name: "punctuation.separator.key-value.html"
+    end: "(?=[,;]|\\s|/>|>)"
+    patterns: [
+      {
+        include: "#expression-no-gt"
+      }
+    ]
+  "attr-js-block":
+    applyEndPatternLast: 1
+    begin: "\\s\\{"
+    beginCaptures:
+      "0":
+        name: "punctuation.section.scope.begin.js"
+    end: "\\}"
+    endCaptures:
+      "0":
+        name: "punctuation.section.scope.end.js"
+    patterns: [
+      {
+        include: "#js"
+      }
+    ]
+  "attr-no-value":
+    captures:
+      "1":
+        name: "entity.other.attribute-name.html"
+      "2":
+        name: "entity.other.attribute-name.html"
+      "3":
+        name: "entity.other.attribute-name.html"
+    match: "\\s+(?:([a-zA-Z][a-zA-Z]*)|(data-[a-zA-Z0-9-]*)|([a-zA-Z0-9][a-zA-Z0-9:_.-]+))(?=\\s|/>|>|$)"
+  "attr-semicolon":
+    applyEndPatternLast: 1
+    begin: "([;])"
+    beginCaptures:
+      "1":
+        name: "punctuation.terminator.statement.js"
+    end: "(?=\\n)"
+    patterns: [
+      {
+        include: "#comment-line"
+      }
+      {
+        include: "#comment-block"
+      }
+    ]
+  "attr-special":
+    captures:
+      "1":
+        name: "support.function.marko-attribute"
+    match: "\\s+(else)\\b"
+  "attr-spread-concise":
+    applyEndPatternLast: 1
+    begin: "(?:\\s+|[,])(\\.\\.\\.)\\s*"
+    beginCaptures:
       "5":
         name: "punctuation.separator.key-value.html"
     end: "(?=([,;]|\\s|$))"
@@ -73,19 +148,11 @@
         include: "#expression"
       }
     ]
-  "attr-assignment-html":
-    applyEndPatternLast: 1
-    begin: "(?:\\s+|[,])(?:(ref)|([a-zA-Z][a-zA-Z]*)|(data-[a-zA-Z0-9-]*)|([_a-zA-Z0-9][a-zA-Z0-9:_.-]*))\\s*([=])\\s*"
-    beginCaptures:
-      "1":
-        name: "entity.other.attribute-name.html"
-      "2":
-        name: "entity.other.attribute-name.html"
-      "3":
-        name: "entity.other.attribute-name.html"
-      "4":
-        name: "entity.other.attribute-name.html"
-      "5":
+  "attr-spread-html":
+    applyEndPatternLast: 1
+    begin: "(?:\\s+|[,])(\\.\\.\\.)\\s*"
+    beginCaptures:
+      "1":
         name: "punctuation.separator.key-value.html"
     end: "(?=[,;]|\\s|/>|>)"
     patterns: [
@@ -93,50 +160,6 @@
         include: "#expression-no-gt"
       }
     ]
-  "attr-js-block":
-    applyEndPatternLast: 1
-    begin: "\\s\\{"
-    beginCaptures:
-      "0":
-        name: "punctuation.section.scope.begin.js"
-    end: "\\}"
-    endCaptures:
-      "0":
-        name: "punctuation.section.scope.end.js"
-    patterns: [
-      {
-        include: "#js"
-      }
-    ]
-  "attr-no-value":
-    captures:
-      "1":
-        name: "entity.other.attribute-name.html"
-      "2":
-        name: "entity.other.attribute-name.html"
-      "3":
-        name: "entity.other.attribute-name.html"
-    match: "\\s+(?:([a-zA-Z][a-zA-Z]*)|(data-[a-zA-Z0-9-]*)|([a-zA-Z0-9][a-zA-Z0-9:_.-]+))(?=\\s|/>|>|$)"
-  "attr-semicolon":
-    applyEndPatternLast: 1
-    begin: "([;])"
-    beginCaptures:
-      "1":
-        name: "punctuation.terminator.statement.js"
-    end: "(?=\\n)"
-    patterns: [
-      {
-        include: "#comment-line"
-      }
-      {
-        include: "#comment-block"
-      }
-    ]
-  "attr-special":
-    captures:
-      "1":
-        name: "support.function.marko-attribute"
-    match: "\\s+(else)\\b"
   "attr-stuff-concise":
     patterns: [
       {
@@ -165,6 +188,9 @@
         include: "#attr-assignment-concise"
       }
       {
+        include: "#attr-spread-concise"
+      }
+      {
         include: "#attr-no-value"
       }
       {
@@ -193,6 +219,9 @@
       }
       {
         include: "#attr-assignment-html"
+      }
+      {
+        include: "#attr-spread-html"
       }
       {
         include: "#attr-no-value"
@@ -268,6 +297,13 @@
       "0":
         name: "punctuation.definition.comment.html"
     end: "-->"
+    name: "comment.block.html"
+  "comment-html-tag":
+    begin: "\\s*<html-comment>"
+    captures:
+      "0":
+        name: "punctuation.definition.comment.html"
+    end: "</html-comment>"
     name: "comment.block.html"
   "comment-line":
     begin: "(//)"
@@ -561,9 +597,6 @@
         match: "\\\\(x[0-9A-Fa-f]{2}|[0-2][0-7]{0,2}|3[0-6][0-7]|37[0-7]?|[4-7][0-7]?|.)"
         name: "constant.character.escape.js"
       }
-      {
-        include: "#placeholder-patterns"
-      }
     ]
   "expression-string-escapes":
     patterns: [
@@ -645,10 +678,13 @@
         include: "#cdata"
       }
       {
+        include: "#comment-html"
+      }
+      {
+        include: "#comment-html-tag"
+      }
+      {
         include: "#tag-html"
-      }
-      {
-        include: "#comment-html"
       }
       {
         include: "#js-code-line"
@@ -672,10 +708,13 @@
         include: "#cdata"
       }
       {
+        include: "#comment-html"
+      }
+      {
+        include: "#comment-html-tag"
+      }
+      {
         include: "#tag-html"
-      }
-      {
-        include: "#comment-html"
       }
       {
         include: "#placeholder-patterns"
@@ -692,10 +731,13 @@
         include: "#cdata"
       }
       {
+        include: "#comment-html"
+      }
+      {
+        include: "#comment-html-tag"
+      }
+      {
         include: "#tag-html"
-      }
-      {
-        include: "#comment-html"
       }
       {
         include: "#placeholder-patterns"
@@ -829,6 +871,9 @@
         include: "#comment-html"
       }
       {
+        include: "#comment-html-tag"
+      }
+      {
         include: "#scriptlet"
       }
       {
@@ -939,6 +984,9 @@
         include: "#tag-name-custom-concise"
       }
       {
+        include: "#tag-name-dynamic-concise"
+      }
+      {
         include: "#tag-name-concise"
       }
     ]
@@ -962,6 +1010,9 @@
       }
       {
         include: "#tag-name-custom-html"
+      }
+      {
+        include: "#tag-name-dynamic-html"
       }
       {
         include: "#tag-name-html"
@@ -990,7 +1041,7 @@
     ]
   "tag-name-custom-concise":
     applyEndPatternLast: 1
-    begin: "^\\s*(for|if|unless|else-if|else|var|assign|macro|invoke|include|app|await|[a-zA-Z0-9_]+([:-])[a-zA-Z0-9_:-]*|[@][a-zA-Z0-9_-]+)(?=(\\s+(?![=])|$|[(]))"
+    begin: "^\\s*(for|if|unless|while|else-if|else|var|assign|macro|invoke|include|await|[a-zA-Z0-9_]+([:])[a-zA-Z0-9_:-]*|[@][a-zA-Z0-9_-]+)(?=(\\s+(?![=])|$|[(]))"
     beginCaptures:
       "1":
         name: "support.function.marko-tag.concise"
@@ -1005,7 +1056,7 @@
       }
     ]
   "tag-name-custom-html":
-    begin: "(<)(for|if|unless|else-if|else|var|assign|macro|invoke|include|app|await|[a-zA-Z0-9_]+[-:][a-zA-Z0-9\\-_:]*|[@][a-zA-Z0-9_-]+)(?=(>|/>|\\s|\\())"
+    begin: "(<)(for|if|unless|while|else-if|else|var|assign|macro|invoke|include|await|[a-zA-Z0-9_]+[:][a-zA-Z0-9\\-_:]*|[@][a-zA-Z0-9_-]+)(?=(>|/>|\\s|\\())"
     beginCaptures:
       "1":
         name: "punctuation.definition.tag.begin.open.html"
@@ -1030,8 +1081,51 @@
         include: "#open-tag-end"
       }
     ]
+  "tag-name-dynamic-concise":
+    applyEndPatternLast: 1
+    begin: "^\\s*(?=\\$\\{.*\\})"
+    comment: "A concise custom tag name"
+    end: "(?=\\n)"
+    patterns: [
+      {
+        include: "#placeholder"
+      }
+      {
+        include: "#html-line-concise"
+      }
+      {
+        include: "#attr-stuff-concise"
+      }
+    ]
+  "tag-name-dynamic-html":
+    begin: "(<)(?=\\$\\{.*\\})"
+    beginCaptures:
+      "1":
+        name: "punctuation.definition.tag.begin.open.html"
+    comment: "The beginning of a regular HTML tag in non-concise mode"
+    end: "(</)(\\2)?(>)|(/>)"
+    endCaptures:
+      "1":
+        name: "punctuation.definition.tag.begin.close.html"
+      "2":
+        name: "support.function.marko-tag.close.html"
+      "3":
+        name: "punctuation.definition.tag.end.close.html"
+      "4":
+        name: "punctuation.definition.tag.end.self-close.html"
+    patterns: [
+      {
+        include: "#placeholder"
+      }
+      {
+        include: "#attr-stuff-html"
+      }
+      {
+        include: "#open-tag-end"
+      }
+    ]
   "tag-name-html":
-    begin: "(<)([a-zA-Z0-9]+)(?=(>|/>|\\s|\\())"
+    begin: "(<)([a-zA-Z0-9-]+)(?=(>|/>|\\s|\\())"
     beginCaptures:
       "1":
         name: "punctuation.definition.tag.begin.open.html"


home: removed log

home: removed log

--- client/app/Applications/Home.kdapplication/Views/homeslideshow.coffee
+++ client/app/Applications/Home.kdapplication/Views/homeslideshow.coffee
@@ -123,7 +123,7 @@
     then pos = 0
     else if pos > amount-1
     then pos = amount - 1
-    log "????", pos, @pos
+
     for slide, i in @slides when i isnt pos
       if pos < i
         slide.unsetClass 'to-left'


Take into account ctrl click and number of selected items for context menu changes

--- lib/tree-view.coffee
+++ lib/tree-view.coffee
@@ -40,16 +40,21 @@
       e.stopPropagation()
       currentTarget = $(e.currentTarget)
       # return early if we're opening a contextual menu (right click) during multi-select mode
-      return if @multiSelectEnabled() && e.button is 2 && currentTarget.hasClass('selected')
+      return if @multiSelectEnabled() && e.button is 2 && currentTarget.hasClass('selected') ||
+                # handle ctrl click as right click on darwin platforms
+                @multiSelectEnabled() && e.ctrlKey && currentTarget.hasClass('selected') && process.platform is 'darwin'

       entryToSelect = currentTarget.view()

       if e.shiftKey
         @selectContinuousEntries(entryToSelect)
         @showMultiSelectMenu()
-      else if e.metaKey || e.ctrlKey
+      # only allow ctrl click for multi selection on non darwin systems
+      else if e.metaKey || (e.ctrlKey && process.platform isnt 'darwin')
         @selectMultipleEntries(entryToSelect)
-        @showMultiSelectMenu()
+
+        if @selectedPaths().length > 1
+          @showMultiSelectMenu()
       else
         @selectEntry(entryToSelect)
         @showFullMenu()


more useless work

more useless work

--- lib/typeinfer.coffee
+++ lib/typeinfer.coffee
@@ -5,7 +5,7 @@

 { Stack } = require 'stack'
 { Set } = require 'set'
-{ NodeVisitor } = require 'nodevisitor'
+{ TreeVisitor } = require 'nodevisitor'

 echo_util = require 'echo-util'

@@ -32,6 +32,8 @@
                 return false if not @parameter_types[i].isCompatible param_tys.length[i] for i in [0..param_tys.length]
                 true

+        toString: -> "(#{t for t in (@parameter_types.join(', ').split(' '))}) -> #{@return_type}"
+

 class UndefinedType extends Type
         isCompatible: (ty) -> false
@@ -73,15 +75,14 @@

         toString: -> "Multi[#{t for t in @types}]"

-class InferVisitor extends NodeVisitor
+class InferVisitor extends TreeVisitor
         constructor: (@initial_env) ->
                 @stable = false
-                super true
+                @function_stack = new Stack

         isStable: -> @stable

         visit: (exp) ->
-                console.warn "visiting #{exp.type}"
                 super

         infer: (exp) ->
@@ -91,11 +92,10 @@
                 console.warn "types are stable, returning from inference pass"
                 exp

-        visitProgram: (program) ->
-                super
+        visitProgram: (program) -> super

         visitIdentifier: (exp) ->
-                if not InferVisitor.getType exp
+                if not @getType exp
                         # check if the identifier is defined in the env
                         if exp.name in @env
                                 @assignType exp, @env[exp.name]
@@ -103,44 +103,62 @@

         visitCallExpression: (exp) ->
                 super
-                # unify the callee type with argtypes
-                # this is the complicated bit, since information can travel in both directions:
-                #   1. if the function body (and therefore the function type) has an undefined/any type
+                # If we have a type for the callee in our environment,
+                # try to look up a matching signature for
+                # parameters/arguments.
+                #
+                # If we find one, then use the return type of that
+                # signature as exp's inferred type.
+                #
+                # If we don't find one, we need to re-traverse the
+                # function using the inferred argument types to see
+                # what the return type could be.
+                exp
+
+        visitFunctionBody: (exp) ->
+                @function_stack.push exp
+                exp.body = @visit exp.body
+                @function_stack.pop()
                 exp

         visitFunctionExpression: (exp) ->
-                exp
-
-        visitReturn: (n) ->
+                @visitFunctionBody exp
+
+        visitFunctionDeclaration: (exp) ->
+                @visitFunctionBody exp
+
+        visitReturn: (exp) ->
+                super
                 debug.log "visitReturn"
-                #if @iifeStack.top.iife_rv?
+                @assignType @function_stack.top, new FunctionType (@getType exp.argument), []
+                exp

         visitBinaryExpression: (exp) ->
                 super
                 switch exp.operator
                         when "+"
-                                if StringType.is(@getType exp.left) or StringType.is(@getType exp.right)
+                                if StringType.is @getType exp.left
                                         @assignType exp, new StringType
+                                        @setDesiredType exp.right, new StringType
+                                else if StringType.is @getType exp.right
+                                        @assignType exp, new StringType
+                                        @setDesiredType exp.left, new StringType
                                 else
                                         @assignType exp, new NumberType
+                                        @setDesiredType exp.left, new NumberType
+                                        @setDesiredType exp.right, new NumberType
                         when "-"
-                                # FIXME we need a way to push a the
-                                # NumberType down to left/right so
-                                # the compiler can possibly optimize
-                                # them.
                                 @assignType exp, new NumberType
+                                @setDesiredType exp.left, new NumberType
+                                @setDesiredType exp.right, new NumberType
                         when "*"
-                                # FIXME we need a way to push a the
-                                # NumberType down to left/right so
-                                # the compiler can possibly optimize
-                                # them.
                                 @assignType exp, new NumberType
+                                @setDesiredType exp.left, new NumberType
+                                @setDesiredType exp.right, new NumberType
                         when "/"
-                                # FIXME we need a way to push a the
-                                # NumberType down to left/right so
-                                # the compiler can possibly optimize
-                                # them.
                                 @assignType exp, new NumberType
+                                @setDesiredType exp.left, new NumberType
+                                @setDesiredType exp.right, new NumberType
                 exp
         visitVariableDeclarator: (exp) ->
                 super
@@ -150,15 +168,16 @@

         visitLiteral: (exp) ->
                 if typeof exp.value is 'number'
-                        if not NumberType.is InferVisitor.getType exp
-                                @assignType exp, new NumberType
+                        @assignType exp, new NumberType
+                else if typeof exp.value is 'string'
+                        @assignType exp, new StringType
                 exp

         @getType: (exp) -> return exp._ejs_inferredtype
         getType: (exp) -> InferVisitor.getType exp

         assignType: (ast, ty) ->
-                console.warn "assigning type of #{ty}"
+                #console.warn "assigning type of #{ty}"
                 if not ast._ejs_inferredtype
                         # The ast node didn't have a type specified before
                         Object.defineProperty ast, "_ejs_inferredtype",
@@ -166,7 +185,7 @@
                                 enumerable: true
                                 configurable: true
                                 writable: true
-                        console.warn "assigned type #{ast._ejs_inferredtype}"
+                        #console.warn "assigned type #{ast._ejs_inferredtype}"
                 else if ast._ejs_inferredtype instanceof MultiType
                         # The ast node is already polymorphic, add the new type to the bunch
                         ast._ejs_inferredtype.addType ty
@@ -179,6 +198,11 @@

                 @changed = true

+        setDesiredType: (ast, ty) ->
+                #console.warn "setting desired type of #{ty}"
+                ast._ejs_desiredtype = ty
+                @changed = true
+
 class TypeInfer
         constructor: ->
                 @defaultGlobalEnvironment = [
@@ -198,13 +222,10 @@

 # tests

-class DumpInferredTypesVisitor extends NodeVisitor
-        constructor: ->
-                super true
-
+class DumpInferredTypesVisitor extends TreeVisitor
         visit: (exp) ->
                 return null if not exp?
-                console.warn "dumping #{exp.type}"
+                #console.warn "dumping #{exp.type}"
                 ty = InferVisitor.getType exp
                 if ty
                         console.warn "inferred type of #{escodegen.generate exp} is '#{ty}'"
@@ -213,8 +234,8 @@
 dump = (tree)->
         dumpvisitor = new DumpInferredTypesVisitor
         dumpvisitor.visit tree
-###
+
 dump run esprima.parse "5;"
 dump run esprima.parse "4 + 5;"
-dump run esprima.parse "function () { return 5; }"
-###+dump run esprima.parse "function foo () { return 5; }"
+dump run esprima.parse "function foo () { return 5; return 'hi'; }"


cleanup

cleanup

--- lib/vim-state.coffee
+++ lib/vim-state.coffee
@@ -82,21 +82,6 @@
     @editorElement = @editor.element
     @emitter = new Emitter
     @subscriptions = new CompositeDisposable
-
-    # @modeManager = new ModeManager(this)
-    # @mark = new MarkManager(this)
-    # @register = new RegisterManager(this)
-    # @hover = new HoverManager(this)
-    # @hoverSearchCounter = new HoverManager(this)
-    # @searchHistory = new SearchHistoryManager(this)
-    # @highlightSearch = new HighlightSearchManager(this)
-    # @persistentSelection = new PersistentSelectionManager(this)
-    # @occurrenceManager = new OccurrenceManager(this)
-    # @mutationManager = new MutationManager(this)
-    # @flashManager = new FlashManager(this)
-    # @searchInput = new SearchInput(this)
-    # @operationStack = new OperationStack(this)
-    # @cursorStyleManager = new CursorStyleManager(this)
     @previousSelection = {}
     @observeSelections()

@@ -110,7 +95,7 @@
     else
       @activate('normal')

-    @subscriptions.add @editor.onDidDestroy(@destroy.bind(this))
+    @editor.onDidDestroy(@destroy)
     @constructor.vimStatesByEditor.set(@editor, this)

   getConfig: (param) ->
@@ -218,7 +203,7 @@
   isAlive: ->
     @constructor.has(@editor)

-  destroy: ->
+  destroy: =>
     return unless @isAlive()
     @constructor.delete(@editor)

@@ -312,12 +297,6 @@
     @operationStack.reset() if @__operationStack?
     @mutationManager.reset() if @__mutationManager?

-    # @register.reset()
-    # @searchHistory.reset()
-    # @hover.reset()
-    # @operationStack.reset()
-    # @mutationManager.reset()
-
   isVisible: ->
     @editor in getVisibleEditors()


Speed up altitide change

Speed up altitide change

--- app/scripts/lazerscripts/lunch.coffee
+++ app/scripts/lazerscripts/lunch.coffee
@@ -112,7 +112,7 @@
       @nextSlide()
       @updateTitle 'Vertical motion'
       @parallel(
-        @gainHeight 600, duration: 15000
+        @gainHeight 600, duration: 10000
         @placeSquad Game.Scripts.Sine,
           amount: 8
           delay: 1000
@@ -120,7 +120,7 @@
       @nextSlide()
       @updateTitle 'Bezier, powerups'
       @parallel(
-        @gainHeight -600, duration: 15000
+        @gainHeight -600, duration: 10000
         @nextSlide @sequence(
           @placeSquad Game.Scripts.PresentationSwirler,
             drop: 'xp'


improve logging for rsync

improve logging for rsync

--- app/templates/gulp/tasks/rsync.coffee
+++ app/templates/gulp/tasks/rsync.coffee
@@ -15,11 +15,12 @@
     args: ['--verbose']
   }, rsyncOpts

-  gutil.log "Rsyncing from #{rsyncOpts.src} to #{rsyncOpts.dest}"
+  gutil.log gutil.colors.green("Rsyncing from #{rsyncOpts.src} to #{rsyncOpts.dest}")

   rsync rsyncOpts, (error, stdout, stderr, cmd) ->
-    gutil.log error if error
-    gutil.log cmd, stderr, stdout
+    gutil.log gutil.colors.red(error) if error
+    gutil.log 'Command: \n', cmd
+    gutil.log stderr, stdout
     done()

 prepareRsync = (done, prop, isToRemote = true, rsyncOpts = {}) ->


Output a warning to console when using urls whit protocol scheme

--- lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee
+++ lib/assets/javascripts/websocket_rails/websocket_connection.js.coffee
@@ -5,7 +5,7 @@

   constructor: (@url,@dispatcher) ->
     if @url.match(/^ws?:\/\//) or @url.match(/^wss?:\/\//)
-        #URL scheme already defined, nothing to do
+        console.log "WARNING: Using connection urls with protocol specified is depricated"
     else if window.location.protocol == 'http:'
         @url             = "ws://#{@url}"
     else


Add method to set dashboard size and work out dimensions

--- assets/javascripts/application.coffee
+++ assets/javascripts/application.coffee
@@ -47,3 +47,10 @@
       str += prefix
     str += num
     str
+
+Dashing.setSize = (rows, cols) ->
+  Dashing.widget_margins = [5, 5]
+  Dashing.numColumns = cols
+  total_width = 1280 - (Dashing.widget_margins[0] * 2 * Dashing.numColumns)
+  total_height = 720.0 - 86.0 #header height
+  Dashing.widget_base_dimensions ||= [total_width/cols, total_height/rows]


Don't send autoUpdateManager to ApplicationMenu

--- src/browser/atom-application.coffee
+++ src/browser/atom-application.coffee
@@ -66,7 +66,7 @@
     @windows = []

     @autoUpdateManager = new AutoUpdateManager()
-    @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager)
+    @applicationMenu = new ApplicationMenu(@version)
     @atomProtocolHandler = new AtomProtocolHandler(@resourcePath)

     @listenForArgumentsFromNewProcess()


Give more feedback on external id sync.

Give more feedback on external id sync.

--- src/coffee/customerxmlimport.coffee
+++ src/coffee/customerxmlimport.coffee
@@ -140,7 +140,8 @@
       else
         deferred.resolve "Customer externalIds already in sync."
     else
-      deferred.reject "Customer numbers do not match for externalId sync."
+      console.log "Customer number has changed: '#{existingCustomer.customerNumber}' -> '#{newCustomer.customerNumber}'"
+      deferred.resolve "Customer numbers do not match for externalId sync."

     deferred.promise


Corrige erro de =>

Corrige erro de =>

--- src/coffee/portal-buy-button.coffee
+++ src/coffee/portal-buy-button.coffee
@@ -98,11 +98,11 @@

 		@triggerProductEvent 'vtex.modal.hide'
 		$.get(@getURL())
-		.done ->
+		.done =>
 				@triggerProductEvent 'vtex.cart.productAdded'
 				@triggerProductEvent 'productAddedToCart'
 				alert @options.addMessage if @options.addMessage
-		.fail ->
+		.fail =>
 				@redirect = true
 				window.location.href = @getURL()
 				alert @options.errMessage if @options.errMessage


commenting prototype, pulling out data methods

Former-commit-id: 0b68512df9bd99ef617e1d203690b142bd152cc9 [formerly b116483af61b8f9afdebb204a63c8b26ecb3578a] [formerly 3e17099dd150c4925fa671cdd128dc06ac3112d0 [formerly c7968da4621f3f7dc9b1799569b4c27c3679f8be]] [formerly 3f58f57cb2d4999279cfe657fb3513428b0f67be [formerly 2e9013bbe08ae38bbcfb74ebe6cff56408c7ea17] [formerly d419d2de49edd663eb4b874b155e8c94e62b51a6 [formerly d419d2de49edd663eb4b874b155e8c94e62b51a6 [formerly 5cc21365e22de136f1d9c436002da88284202c4f]]]]
Former-commit-id: 95ccac689b45cbdd1311a77a96f5236bab5ec999 [formerly c8a4298a1cde12bc35c4ba04d2862cd365a2255d] [formerly dd9d3a4d47ff5b3e3395a71035288b10e642ce11 [formerly 262984da9ce08c9239533d9c1f57c03de59341c1]]
Former-commit-id: 783cd8c03ba7e3d5d96ba7370591134393d8a4b3 [formerly 1af4f6fe527b8de7d0d2e620429e381e13f534df]
Former-commit-id: 7020252ab5ce4c9fab0003f4db46547bfc7dfa07
Former-commit-id: b97c57dbda863aee922a58c36a95c7c1cf22fa13

--- @client/components/franklin.coffee
+++ @client/components/franklin.coffee
@@ -1,9 +1,117 @@
+#////////////////////////////////////////////////////////////
+# Exploratory reimplemenntation of considerit client in React
+#////////////////////////////////////////////////////////////
+
+# Ugliness in this prototype:
+#   - ReactTransitionGroup based animations force defining custom components
+#     when I'd rather just keep them in the parent component.
+#   - State transition javascript animations sometimes require setting css (e.g. width or transform),
+#     which are duplicated in the CSS itself to accommodate navigating directly to that state.
+#   - Keeping javascript and CSS variables synchronized
+#      * transition_speed
+#      * slider width
+#   - Not clear where to handle splitting up opinions into segments
+#   - Sticky decision board requires hacks to CSS of community cons to get them to stay to the right
+#   - Haven't declared prop types for the components
+#   - Some animations are living in places that access DOM not managed by them
+
+# React aliases
 R = React.DOM
 ReactTransitionGroup = React.addons.TransitionGroup
-transition_speed = 700
-
-all_users = {}
-
+
+# Variables that need to be kept synchronized with CSS
+transition_speed = 700   # Speed of transition from results to crafting (and vice versa)
+histogram_width = 636    # Width of the slider / histogram base
+
+
+#####
+# Responsibilities that will later be managed by ActiveREST
+all_users = {}
+
+parseProposal = (data) ->
+  # Build hash of user information
+  data.users = $.parseJSON data.users
+  for user in data.users
+    if !user.avatar_file_name
+      user.avatar_file_name = {small: '/system/default_avatar/small_default-profile-pic.png', large: '/system/default_avatar/large_default-profile-pic.png'}
+    else
+      user.avatar_file_name = {small: "/system/avatars/#{user.id}/small/#{user.avatar_file_name}", large: "/system/avatars/#{user.id}/large/#{user.avatar_file_name}"}
+    all_users[user.id] = user
+
+  # separate pro and con points
+  data.pro_community_points = _.where data.points, {is_pro: true}
+  data.con_community_points = _.where data.points, {is_pro: false}
+
+  ##
+  # Split up opinions into segments. For now we'll keep three hashes:
+  #   - all opinions
+  #   - high level segments (the seven original segments, strong supporter, neutral, etc)
+  #   - small segments that represent individual columns in the histogram, now that
+  #     we do not have wide bars per se
+  num_opinions = data.opinions.length
+
+  # an initial function for sizing avatars
+  biggest_possible_avatar_size = 50
+  data.avatar_size = biggest_possible_avatar_size / Math.sqrt( (num_opinions + 1)/10  )
+
+  # Calculate how many segments columns to put on the histogram. Note that for the extremes and for neutral, we'll hack it
+  # to allow three columns for those segments.
+  data.num_small_segments = Math.floor(histogram_width/data.avatar_size) - 2 * 3 - 1 #for the additional cols for the extremes+neutral
+
+  seven_original_opinion_segments = {0:[],1:[],2:[],3:[],4:[],5:[],6:[]}
+
+  histogram_small_segments = {}
+  histogram_small_segments[i] = [] for i in [0..data.num_small_segments]
+
+  max_slider_variance = 2.0 # In old system, opinion stances varied from -1.0 to 1.0.
+
+  for opinion in data.opinions
+    seven_original_opinion_segments[opinion.stance_segment].push opinion
+    small_segment = Math.floor(data.num_small_segments * (opinion.stance + 1) / max_slider_variance)
+    histogram_small_segments[small_segment].push opinion
+
+  data.seven_original_opinion_segments = seven_original_opinion_segments
+  data.histogram_small_segments = histogram_small_segments
+
+  data
+
+fetch = (options, callback, error_callback) ->
+  error_callback ||= (xhr, status, err) -> console.error 'Could not fetch data', status, err.toString()
+
+  $.ajax
+    url: options.url
+    dataType: 'json'
+    success: (data) =>
+      if options.type == 'proposal'
+        data = parseProposal data
+      callback data
+
+    error: error_callback
+
+
+
+
+####################
+# REACT COMPONENTS #
+####################
+
+# These are the components and their relationships:
+#                       Proposal
+#                   /      |            \
+#    CommunityPoints   DecisionBoard      GiveOpinionButton
+#               |          |
+#               |      YourPoints
+#               |    /            \
+#              Point             NewPoint
+
+# DecisionBoard and GiveOpinionButton exist only for the sake of the particular way
+# React affords animations right now. See ReactTransitionGroup below.
+
+
+##
+# NewPoint
+# Handles adding a new point into the system. Only rendered when proposal is in Crafting state.
+# Manages whether the user has clicked "add a new point". If they have, show new point form.
 NewPoint = React.createClass
   displayName: 'NewPoint'

@@ -50,7 +158,9 @@
             R.input className:'button', action:'submit-point', type:'submit', value:'Done', onClick: @handleSubmitNewPoint

-
+##
+# Point
+# A single point in a list.
 Point = React.createClass
   displayName: 'Point'

@@ -74,6 +184,10 @@
                 @props.comment_count
                 ' comments'

+##
+# CommunityPoints
+# List of points contributed by others.
+# Shown in wing during crafting, in middle on results.
 CommunityPoints = React.createClass
   displayName: 'CommunityPoints'

@@ -98,11 +212,11 @@
                   comment_count: point.comment_count
                   author: point.user_id

-            # R.div className:'points_footer_region',
-            #   R.div className:'community_points_footer_view',
-            #     R.a className:'toggle_expand_points button', 'data-action':'expand-toggle',
-            #       "View all #{@props.points.length} Pros"
-
+##
+# YourPoints
+# List of important points for the active user.
+# Two instances used for Pro and Con columns. Shown as part of DecisionBoard.
+# Creates NewPoint instances.
 YourPoints = React.createClass
   displayName: 'YourPoints'

@@ -132,17 +246,26 @@

             NewPoint {valence: @props.valence}

+##
+# DecisionBoard
+# Handles the user's list of important points in crafting state.
+# Primary motivation for pulling this out separately is to
+# animate between results and crafting states using the
+# ReactTransitionGroup interface.
 DecisionBoard = React.createClass
   displayName: 'DecisionBoard'

   componentWillEnter: (callback) ->
+    # Don't display the pro/con columns immediately,
+    # otherwise they won't fit side by side as the
+    # area animates the width
     $(@getDOMNode()).hide()
     _.delay =>
       $(@getDOMNode()).show()
       callback()
     , transition_speed

-  componentWillLeave: (callback) ->
+  componentWillLeave: (callback) ->
     $(@getDOMNode()).hide()
     callback()
     # $(@getDOMNode()).slideUp transition_speed / 4, ->
@@ -164,15 +287,25 @@
         points: []
         valence: 'con'

+##
+# GiveOpinionButton
+# Displays the give opinion button which toggles to crafting state.
+# Primary motivation for pulling this out separately is to
+# animate between results and crafting states using the
+# ReactTransitionGroup interface.
 GiveOpinionButton = React.createClass
   displayName: 'GiveOpinionButton'

+  # Moves the GiveOpinionButton under the slider handle
   place: ->
     left = $('.ui-slider-handle').offset().left - $('.opinion_region').offset().left

     if @props.currentOpinion > 50 # position right justified when opposing
-      left -= 120 # width of give opinion button
-
+      # 120 is based on width of give opinion button, which can't be checked here b/c this is before animation is complete
+      left -= 120
+
+    # Ugly to manipulate the opinion region here
+    # Problem: This code will not run when results is directly accessed.
     $('.opinion_region').css
       transform: "translate(#{left}, -18px)"
       '-webkit-transform': "translate(#{left}px, -18px)"
@@ -180,6 +313,7 @@
   componentWillEnter: (callback) ->
     @place()

+    # wait a slight bit before showing "give your opinion" when moving from crafting to results
     $(@getDOMNode()).hide()
     _.delay =>
       $(@getDOMNode()).show()
@@ -190,42 +324,36 @@
     $('.opinion_region').css
       transform: "translate(0,0)"
       '-webkit-transform': "translate(0,0)"
-    # $('.decision_board_body').css
-    #   width: ''
     callback()
-

   render : ->
     R.a className:'give_opinion_button', onClick: @props.toggleState,
       'Give your Opinion'

+##
+# Proposal
+# The mega component for a proposal.
+# Has proposal description, feelings area (slider + histogram), and reasons area
 window.REACTProposal = React.createClass
   displayName: 'Proposal'
+
   componentWillMount : ->
-    $.ajax
-      url: Routes.proposal_path @props.long_id
-      dataType: 'json'
-      success: (data) =>
-        data = @processInitialData data
-        console.log data
-        @setState {data: data}
-      error: (xhr, status, err) =>
-        console.error 'Could not fetch data', status, err.toString()
+    fetch {type: 'proposal', url: Routes.proposal_path @props.long_id}, (proposal_data) =>
+      @setState {data: proposal_data}

   componentDidMount : ->
     $('.histogram_base').slider()

   componentDidUpdate : ->

+    # Sticky decision board
     $('.opinion_region').headroom
       offset: $('.opinion_region').offset().top #+ $('.opinion_region').height()
       classes:
-        initial : "headroom",
-        pinned : "headroom--pinned",
-        unpinned : "headroom--unpinned",
-        top : "normal_place",
+        top : "normal_place"
         notTop : "scrolling_with_user"
+
       onNotTop : ->
         $('.four_columns_of_points .community_cons_region').css
           transition: 'none'
@@ -246,9 +374,6 @@
             '-webkit-transition': ''
         , 100
         # $('.four_columns_of_points').removeClass('pinned')
-
-
-

   getInitialState : ->
     state : 'crafting',
@@ -257,7 +382,8 @@
       proposal : {}
       pro_community_points : []
       con_community_points : []
-      opinions : {0:[],1:[],2:[],3:[],4:[],5:[],6:[]}
+      seven_original_opinion_segments : {}
+      histogram_small_segments : {}
       users : {}

   toggleState : (ev) ->
@@ -265,54 +391,12 @@
       state : @state.priorstate,
       priorstate : @state.state

-  processInitialData : (data) ->
-    # This method will be mostly replaced by ActiveRest
-    data.users = $.parseJSON data.users
-
-    data.pro_community_points = _.where data.points, {is_pro: true}
-    data.con_community_points = _.where data.points, {is_pro: false}
-
-    num_opinions = data.opinions.length
-
-    biggest_avatar_size = 50
-    @avatar_size = biggest_avatar_size / Math.sqrt( (num_opinions + 1)/10  )
-
-    histogram_width = 636
-
-    @num_small_segments = Math.floor(histogram_width/@avatar_size) - 2 * 3 - 1 #for the additional cols for the extremes+neutral
-    max_slider_val = 2.0
-
-    opinions = {0:[],1:[],2:[],3:[],4:[],5:[],6:[]}
-    histogram_small_segments = {}
-
-    for i in [0..@num_small_segments]
-      histogram_small_segments[i] = []
-
-    for opinion in data.opinions
-      opinions[opinion.stance_segment].push opinion
-      small_segment = Math.floor(@num_small_segments * (opinion.stance + 1) / max_slider_val)
-      histogram_small_segments[small_segment].push opinion
-
-    data.opinions = opinions
-    data.histogram_small_segments = histogram_small_segments
-
-    users = {}
-    for user in data.users
-      if !user.avatar_file_name
-        user.avatar_file_name = {small: '/system/default_avatar/small_default-profile-pic.png', large: '/system/default_avatar/large_default-profile-pic.png'}
-      else
-        user.avatar_file_name = {small: "/system/avatars/#{user.id}/small/#{user.avatar_file_name}", large: "/system/avatars/#{user.id}/large/#{user.avatar_file_name}"}
-      users[user.id] = user
-
-    all_users = users
-
-    data
-
-  render : ->
+  render : ->
+    console.log @state.data.num_small_segments
     current_opinion = $('.histogram_base').slider('value')

     segment_is_extreme_or_neutral = (segment) =>
-      segment == 0 || segment == @num_small_segments || segment == Math.floor(@num_small_segments / 2)
+      segment == 0 || segment == @state.data.num_small_segments || segment == Math.floor(@state.data.num_small_segments / 2)

     R.div className:'proposal_layout', key:@props.long_id, 'data-role':'proposal', 'data-activity':'proposal-has-activity', 'data-status':'proposal-inactive', 'data-visibility':'published', 'data-state':@state.state, 'data-prior-state':@state.priorstate,

@@ -355,13 +439,11 @@
       R.div className:'proposal_histogram_region',
         R.div className:'histogram_layout', 'data-state':@state.state, 'data-prior-state':@state.priorstate,
           #for segment in [6..0]
-          for segment in [@num_small_segments..0]
-            R.div key:"#{segment}", className:"histogram_bar #{if segment_is_extreme_or_neutral(segment) then 'extreme_or_neutral' else '' }", id:"segment-#{segment}", 'data-segment':segment, style: {width: if segment_is_extreme_or_neutral(segment) then "#{3 * @avatar_size}px" else "#{@avatar_size}px"},
+          for segment in [@state.data.num_small_segments..0]
+            R.div key:"#{segment}", className:"histogram_bar #{if segment_is_extreme_or_neutral(segment) then 'extreme_or_neutral' else '' }", id:"segment-#{segment}", 'data-segment':segment, style: {width: if segment_is_extreme_or_neutral(segment) then "#{3 * @state.data.avatar_size}px" else "#{@state.data.avatar_size}px"},
               R.ul className:'histogram_bar_users',
                 for opinion in @state.data.histogram_small_segments[segment] #@state.data.opinions[segment]
-                  R.li key:"#{opinion.user_id}", id:"avatar-#{opinion.user_id}", className:"avatar segment-#{segment}", 'data-action':'user_opinion', 'data-id':opinion.user_id, style:{height:"#{@avatar_size}px", width:"#{@avatar_size}px"}, 'data-tooltip':'user_profile'
-
-
+                  R.li key:"#{opinion.user_id}", id:"avatar-#{opinion.user_id}", className:"avatar segment-#{segment}", 'data-action':'user_opinion', 'data-id':opinion.user_id, style:{height:"#{@state.data.avatar_size}px", width:"#{@state.data.avatar_size}px"}, 'data-tooltip':'user_profile'

           R.div className:'histogram_base',
             R.div className:'feeling_slider ui-slider-handle',


Support SVG in import

Support SVG in import

--- client/import.iced
+++ client/import.iced
@@ -12,6 +12,7 @@
   '.jpeg': 'image/jpeg'
   '.png': 'image/png'
   '.pdf': 'application/pdf'
+  '.svg': 'image/svg+xml'

 upfile_url = ///http://6...\.csail\.mit\.edu/[^/\s"=]*/upfiles/([^/\s"=]*)///g

@@ -246,7 +247,7 @@
           gr = /\\includegraphics\s*(\[[^\[\]]*\]\s*)?{((?:[^{}]|{[^{}]*})*)}/g
           while (match = gr.exec p2)?
             filename = match[2]
-            for extension in ['', '.png', '.jpg', '.pdf']
+            for extension in ['', '.svg', '.png', '.jpg', '.pdf']
               if filename + extension of zip.files
                 filename += extension
                 break


Bugfix.

Bugfix.

--- server/admin.coffee
+++ server/admin.coffee
@@ -533,7 +533,7 @@
     Publication.documents.find(source: 'FSM', cached: {$exists: false}).forEach (publication) ->
       try
         if not Storage.exists publication.cachedFilename()
-          Log.info "Caching file for #{ @_id }: #{ @foreignFilename() } -> #{ @cachedFilename() }"
+          Log.info "Caching file for #{ publication._id }: #{ publication.foreignFilename() } -> #{ publication.cachedFilename() }"

           tei = HTTP.get publication.foreignUrl,
             timeout: 10000 # ms


added log items to base.coffee

added log items to base.coffee

--- bokehjs/src/coffee/common/base.coffee
+++ bokehjs/src/coffee/common/base.coffee
@@ -11,6 +11,7 @@

   "mapper/1d/categorical_mapper",
   "mapper/1d/linear_mapper",
+  "mapper/1d/log_mapper",
   "mapper/2d/grid_mapper",
   "mapper/color/linear_color_mapper",

@@ -25,6 +26,7 @@
   "renderer/guide/datetime_axis",
   "renderer/guide/grid",
   "renderer/guide/linear_axis",
+  "renderer/guide/log_axis",
   "renderer/overlay/box_selection",

   "source/column_data_source",
@@ -34,6 +36,8 @@
   "ticking/adaptive_ticker",
   "ticking/basic_tick_formatter",
   "ticking/basic_ticker",
+  "ticking/log_ticker",
+  "ticking/log_tick_formatter",
   "ticking/categorical_tick_formatter",
   "ticking/categorical_ticker",
   "ticking/composite_ticker",
@@ -119,6 +123,7 @@

     Glyph:                    'renderer/glyph/glyph_factory'
     LinearAxis:               'renderer/guide/linear_axis'
+    LogAxis:                  'renderer/guide/log_axis'
     CategoricalAxis:          'renderer/guide/categorical_axis'
     DatetimeAxis:             'renderer/guide/datetime_axis'
     Grid:                     'renderer/guide/grid'
@@ -132,6 +137,8 @@
     AdaptiveTicker:           'ticking/adaptive_ticker'
     BasicTicker:              'ticking/basic_ticker'
     BasicTickFormatter:       'ticking/basic_tick_formatter'
+    LogTicker:                'ticking/log_ticker'
+    LogTickFormatter:         'ticking/log_tick_formatter'
     CategoricalTicker:        'ticking/categorical_ticker'
     CategoricalTickFormatter: 'ticking/categorical_tick_formatter'
     CompositeTicker:          'ticking/composite_ticker'


@jensechu will never use a bouncer

@jensechu will never use a bouncer

--- scripts/entrance.coffee
+++ scripts/entrance.coffee
@@ -12,6 +12,3 @@
       msg.send "wb #{username}"
     else if /kin/.test(username)
       msg.send "Hello #{username}! We missed you!"
-    else if /jense/i.test(username) or /prawn/i.test(username) or /sinjen/.test(username) or username == 'sen'
-      msg.send "Hello #{username}, have you heard of our lord & savior ZNC?"
-