Add embedded instructions to command editor.

--- lib/views/edit/command-edit-view.coffee
+++ lib/views/edit/command-edit-view.coffee
@@ -16,7 +16,7 @@
         @table =>
           @tbody =>
             @tr =>
-              @td =>
+              @td {colspan: 2}, =>
                 @h2 'General', {class: 'text-highlight'}
             @tr =>
               @td 'Namespace:', {class: 'text-highlight first-column'}
@@ -27,24 +27,43 @@
               @td =>
                 @subview 'actionEditor', new TextEditorView(mini: true)
             @tr =>
+              @td ' '
+              @td =>
+                @span 'The namespace and action name together forms the identifier in the command palette.' , {class: 'text-smaller text-subtle'}
+            @tr =>
               @td 'Shell Command:', {class: 'text-highlight first-column'}
               @td =>
                 @subview 'commandEditor', new TextEditorView(mini: true)
             @tr =>
+              @td ' '
+              @td =>
+                @span 'Command with arguments to run. Any of the input variables can be used.', {class: 'text-smaller text-subtle'}
+            @tr =>
               @td 'Working Directory:', {class: 'text-highlight first-column'}
               @td =>
                 @subview 'cwdEditor', new TextEditorView(mini: true)
             @tr =>
+              @td ' '
+              @td =>
+                @span 'Working directory when running command. Any of the input variables can be used.', {class: 'text-smaller text-subtle'}
+            @tr =>
               @td 'Keystroke:', {class: 'text-highlight first-column'}
               @td =>
                 @subview 'keystrokeEditor', new TextEditorView(mini: true)
             @tr =>
-              @td =>
+              @td ' '
+              @td =>
+                @span 'Shortcut key. Combine shift, ctrl, cmd and alt with other keys, like ctrl-alt-r', {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td {colspan: 2}, =>
                 @h2 'Output', {class: 'text-highlight'}
             @tr =>
               @td {class: 'first-column', colspan: 2}, =>
                 @input {type: 'checkbox', outlet: 'streamCheck'}
                 @span 'Stream output to target', {class: 'check-label'}
+            @tr =>
+              @td {class: 'first-column', colspan: 2}, =>
+                @span 'Select to stream output to target as it is produced. The target format is not applied when streaming.' , {class: 'text-smaller text-subtle'}
             @tr =>
               @td 'Target:', {class: 'text-highlight first-column'}
               @td =>
@@ -52,7 +71,7 @@
                   @option 'Panel', {value: 'panel'}
                   @option 'Clipboard', {value: 'clipboard'}
                   @option 'Developer console', {value: 'console'}
-                  @option 'Editor', {value: 'editor'}
+                  @option 'Active editor', {value: 'editor'}
                   @option 'New file', {value: 'file'}
                   @option 'Void', {value: 'void'}
             @tr =>
@@ -60,9 +79,16 @@
               @td =>
                 @subview 'bufferSizeEditor', new TextEditorView(mini: true)
             @tr =>
-              @td =>
+              @td ' '
+              @td =>
+                @span 'The maximum number of characters to keep in memory. Leave unspecified to disable the limit.' , {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td {colspan: 2}, =>
                 @h3 'Target Format', {class: 'text-highlight'}
             @tr =>
+              @td {colspan: 2}, =>
+                @span 'Format of output when written to the target. Any variable can be used here.' , {class: 'text-smaller text-subtle'}
+            @tr =>
               @td 'Success:', {class: 'text-highlight top-label first-column'}
               @td =>
                 @subview 'successOutputEditor', new TextEditorView()
@@ -71,9 +97,12 @@
               @td =>
                 @subview 'errorOutputEditor', new TextEditorView()
             @tr =>
-              @td =>
+              @td {colspan: 2}, =>
                 @h3 'Notification Format', {class: 'text-highlight'}
             @tr =>
+              @td {colspan: 2}, =>
+                @span 'Format of output when creating a notification. Any variable can be used here. Leave empty to disable notification.' , {class: 'text-smaller text-subtle'}
+            @tr =>
               @td 'Success:', {class: 'text-highlight top-label first-column'}
               @td =>
                 @subview 'successMessageEditor', new TextEditorView()
@@ -82,7 +111,7 @@
               @td =>
                 @subview 'errorMessageEditor', new TextEditorView()
             @tr =>
-              @td =>
+              @td {colspan: 2}, =>
                 @h2 'Panel', {class: 'text-highlight'}
             @tr =>
               @td {class: 'first-column', colspan: 2}, =>
@@ -101,25 +130,104 @@
               @td =>
                 @subview 'maxCompletedEditor', new TextEditorView(mini: true)
             @tr =>
-              @td =>
+              @td ' '
+              @td =>
+                @span 'The maximum number of panels of completed processes to keep. Leave unspecified to disable the limit.' , {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td {colspan: 2}, =>
                 @h2 'Patterns', {class: 'text-highlight'}
             @tr =>
               @td {class: 'first-column', colspan: 2}, =>
+                @span 'Choose which patterns should be applied when detecting file paths. Patterns are applied from top to bottom.' , {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td {class: 'first-column', colspan: 2}, =>
                 @subview 'patternChooseView', new PatternChooseView()
             @tr =>
-              @td =>
+              @td {colspan: 2}, =>
                 @h2 'Environment Variables', {class: 'text-highlight'}
+            @tr =>
+              @td {colspan: 2}, =>
+                @span 'Add additional environment variables. Any of the input variables can be used in the value.' , {class: 'text-smaller text-subtle'}
             @tr =>
               @td {class: 'first-column', colspan: 2}, =>
                 @div {class: 'bordered'}, =>
                   @subview 'envVarsView', new TableEditView(['Name', 'Value'])
             @tr =>
-              @td =>
+              @td {colspan: 2}, =>
                 @h2 'Input Dialogs', {class: 'text-highlight'}
+            @tr =>
+              @td {colspan: 2}, =>
+                @span 'Configure dialogs to prompt for values of user defined input variables.' , {class: 'text-smaller text-subtle'}
             @tr =>
               @td {class: 'first-column', colspan: 2}, =>
                 @div {class: 'bordered'}, =>
                   @subview 'inputDialogsView', new TableEditView(['Name', 'Message', 'Default value'])
+            @tr =>
+              @td {colspan: 2}, =>
+                @h2 'Variables', {class: 'text-highlight'}
+            @tr =>
+              @td {colspan: 2}, =>
+                @span 'Variables can be used in many of the fields. These should be surrounded with curly brackets when referenced.' , {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td {colspan: 2}, =>
+                @h3 'Input Variables', {class: 'text-highlight'}
+            @tr =>
+              @td 'clipboard', {class: 'text-highlight first-column'}
+              @td 'Text currently on clipboard.'
+            @tr =>
+              @td 'fullCommand', {class: 'text-highlight first-column'}
+              @td 'The full command along with its arguments. Both the command and arguments will have their variables resolved.'
+            @tr =>
+              @td 'configDirAbsPath', {class: 'text-highlight first-column'}
+              @td 'Absolute path of folder where the process-palette.json configuration file is that defines this command.'
+            @tr =>
+              @td 'projectPath', {class: 'text-highlight first-column'}
+              @td 'If projects are open then the first project\'s folder will be used. If there aren\'t any projects open then the path of the folder containing the process-palette.json file is used.'
+            @tr =>
+              @td {colspan: 2}, =>
+                @h3 'Input Variables From Editor', {class: 'text-highlight'}
+            @tr =>
+              @td 'fileExt', {class: 'text-highlight first-column'}
+              @td 'Extension of file.'
+            @tr =>
+              @td 'fileName', {class: 'text-highlight first-column'}
+              @td 'Name of file without extension.'
+            @tr =>
+              @td 'fileNameExt', {class: 'text-highlight first-column'}
+              @td 'Name of file with extension.'
+            @tr =>
+              @td 'filePath', {class: 'text-highlight first-column'}
+              @td 'Path of file relative to project.'
+            @tr =>
+              @td 'fileDirPath', {class: 'text-highlight first-column'}
+              @td 'Path of file\'s directory relative to project.'
+            @tr =>
+              @td 'fileAbsPath', {class: 'text-highlight first-column'}
+              @td 'Absolute path of file.'
+            @tr =>
+              @td 'fileDirAbsPath', {class: 'text-highlight first-column'}
+              @td 'Absolute path of file\'s directory.'
+            @tr =>
+              @td 'selection', {class: 'text-highlight first-column'}
+              @td 'Currently selected text.'
+            @tr =>
+              @td 'fileProjectPath', {class: 'text-highlight first-column'}
+              @td 'Absolute path of file\'s project folder.'
+            @tr =>
+              @td {colspan: 2}, =>
+                @h3 'Output Variables', {class: 'text-highlight'}
+            @tr =>
+              @td {colspan: 2}, =>
+                @span 'Output variables are only available after the command has run and can therefore only be used in the target and notification formats.' , {class: 'text-smaller text-subtle'}
+            @tr =>
+              @td 'stdout', {class: 'text-highlight first-column'}
+              @td 'Standard output produced by the process.'
+            @tr =>
+              @td 'stderr', {class: 'text-highlight first-column'}
+              @td 'Standard error output produced by the process.'
+            @tr =>
+              @td 'exitStatus', {class: 'text-highlight first-column'}
+              @td 'Exit status code returned by the process.'

   initialize: ->
     @command = @commandItemView.getCommand();


code cleaning: remove console.log

code cleaning: remove console.log

--- lib/permissions.coffee
+++ lib/permissions.coffee
@@ -12,7 +12,6 @@
         # Download file with application's permissions
         path = (app.git).substring(19, (app.git.length - 4))
         path = "https://raw.github.com/" + path + '/master/package.json'
-        console.log path
         options =
             host: url.parse(path).host
             path: url.parse(path).path
@@ -20,7 +19,7 @@
         request = https.get options, (response) =>
             if response.headers.status isnt "404 Not Found"
                 response.on('data', (data) =>
-                    #config = JSON.parse(data)
+                    config = JSON.parse(data)
                 ).on 'end', () =>
                     # Read application's permissions
                     if config.permissions?


add methods to find ideographic advance factor, execute FontForge scripts

--- src/main.coffee
+++ src/main.coffee
@@ -58,6 +58,7 @@
   R.descender           = -R.em_size / 5
   # R.global_glyph_scale  = 50 / 48.5 ### TAINT value must come from configuration ###
   R.global_glyph_scale  = 256 / 248 ### TAINT value must come from configuration ###
+  # R.global_glyph_scale  = 1 ### TAINT value must come from configuration ###
   return R

 #-----------------------------------------------------------------------------------------------------------
@@ -71,11 +72,42 @@
     descender:    me.descender,
     glyphs:       glyphs }

+#-----------------------------------------------------------------------------------------------------------
+@_find_ideographic_advance_factor = ( otjsfont ) ->
+  ### In some fonts, the advance width of CJK ideographs differs from the font design size; this is
+  especially true for fonts from the `cwTeXQ` series. This routine probes the font with a number of CJK
+  codepoints and returns the ratio of the font design size and the advance width of the first CJK glyph.
+  The function always returns 1 for fonts that do not contain CJK characters. ###
+  probe = Array.from '一丁乘㐀㑔㙜𠀀𠀁𠀈𪜵𪝘𪜲𫝀𫝄𫠢𫡄𫡦𬺰𬻂'
+  for chr in probe
+    cid = chr.codePointAt 0
+    continue unless ( glyph = @glyph_from_cid otjsfont, cid )?
+    return otjsfont.unitsPerEm / glyph.advanceWidth
+  return 1

 #===========================================================================================================
 # FONTFORGE
 #-----------------------------------------------------------------------------------------------------------
+@exec_fontforge_script = ( script, parameters = null ) ->
+  command     = 'fontforge'
+  parameters  = [ '--lang=ff', '-c', script, ( parameters ? [] )..., ]
+  settings    =
+    cwd:        process.cwd()
+    timeout:    3 * 60 * 1000 ### TAINT make timeout configurable ###
+    encoding:   'utf-8'
+    shell:      false
+  #.........................................................................................................
+  { status
+    stderr }  = spawn_sync command, parameters, settings
+  #.........................................................................................................
+  unless status is 0
+    throw new Error """^svgttf#3309 when trying to execute `#{command} #{jr parameters}`, an error occurred:
+      #{stderr}"""
+  return null
+
+#-----------------------------------------------------------------------------------------------------------
 @rewrite_with_fontforge = ( path ) ->
+  ### TAINT rewrite using `exec_fontforge_script()` ###
   help "^svgttf#0091 size before normalisation: #{CND.format_number @_size_from_path path} B"
   #.........................................................................................................
   command     = 'fontforge'
@@ -112,8 +144,22 @@
 #-----------------------------------------------------------------------------------------------------------
 @save_otjsfont = ( path, otjsfont ) ->
   # FS.writeFileSync path, buffer = otjsfont.toBuffer() # deprecated
-  FS.writeFileSync path, buffer = Buffer.from otjsfont.toArrayBuffer()
+  # buffer = Buffer.from otjsfont.toArrayBuffer()
+  buffer = Buffer.from @_otjsfont_toArrayBuffer otjsfont
+  FS.writeFileSync path, buffer
   return buffer.length
+
+@_otjsfont_toArrayBuffer = ( otjsfont ) ->
+  sfntTable = otjsfont.toTables();
+  bytes     = sfntTable.encode();
+  buffer    = new ArrayBuffer(bytes.length);
+  intArray  = new Uint8Array(buffer);
+  ```
+  for (let i = 0; i < bytes.length; i++) {
+      intArray[i] = bytes[i];
+  }
+  ```
+  return buffer;

 #-----------------------------------------------------------------------------------------------------------
 @list_glyphs_in_otjsfont = ( otjsfont ) ->
@@ -141,17 +187,22 @@
   return "<!-- #{cid_hex} #{glyph} --><path d='#{pathdata}'/>"

 #-----------------------------------------------------------------------------------------------------------
-@glyph_and_pathdata_from_cid = ( otjsfont, cid ) ->
-  XXXXX_metrics       = @new_metrics()
+@glyph_from_cid = ( otjsfont, cid ) ->
   validate.positive_integer cid
-  fglyph              = otjsfont.charToGlyph String.fromCodePoint cid
-  return null unless fglyph.unicode?
-  path_obj            = fglyph.getPath 0, 0, XXXXX_metrics.font_size
+  R = otjsfont.charToGlyph String.fromCodePoint cid
+  return if R.unicode? then R else null
+
+#-----------------------------------------------------------------------------------------------------------
+@glyph_and_pathdata_from_cid = ( me, otjsfont, cid ) ->
+  validate.positive_integer cid
+  fglyph              = @glyph_from_cid otjsfont, cid
+  return null unless fglyph?
+  path_obj            = fglyph.getPath 0, 0, me.font_size
   pathdata            = path_obj.toPathData path_precision
   return null if pathdata.length is 0
   svg_path            = new SvgPath pathdata
-  global_glyph_scale  = XXXXX_metrics.global_glyph_scale ? 1
-  scale_factor        = XXXXX_metrics.scale_factor * global_glyph_scale
+  global_glyph_scale  = me.global_glyph_scale ? 1
+  scale_factor        = me.scale_factor * global_glyph_scale
   svg_path            = svg_path.scale scale_factor, -scale_factor
   return { glyph: fglyph, pathdata: svg_path.toString(), }

@@ -182,6 +233,7 @@
 #-----------------------------------------------------------------------------------------------------------
 @get_fallback_glyph = ( me, shape = 'square' ) ->
   # validate.svgttf_metrics me
+  '❶❷❸❹❺❻❼❽❾❿'
   validate.nonempty_text shape
   #.........................................................................................................
   width         = 3 * me.em_size // 4


simplify `$async` implementation

simplify `$async` implementation


Fix up example app slightly

Fix up example app slightly

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -11,7 +11,7 @@
     color = "red"

     return {
-      contrast: 5.5
+      contrast: 5
       foregroundMin: 0.02
       foregroundMax: 0.98
       backgroundMin: 0.15
@@ -21,11 +21,11 @@

   render: ->
     {bg, fg} = colorPairsPicker(@state.color, {
-      contrast: @state.contrast
-      foregroundMin: @state.foregroundMin
-      foregroundMax: @state.foregroundMax
-      backgroundMax: @state.backgroundMax
-      backgroundMin: @state.backgroundMin
+      contrast: parseFloat(@state.contrast)
+      foregroundMin: parseFloat(@state.foregroundMin)
+      foregroundMax: parseFloat(@state.foregroundMax)
+      backgroundMax: parseFloat(@state.backgroundMax)
+      backgroundMin: parseFloat(@state.backgroundMin)
     })

     <div style={width:'500px', margin:'0 auto'}>


allow numbers in first char of domain

allow numbers in first char of domain

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -199,7 +199,7 @@
       notifyAdmins "#{logmsg}\nReason: #{usermsg}"
       return

-  else if extra = l_val.match /^(?:http:\/\/|)([a-zA-Z][-a-zA-Z0-9\.]+)$/
+  else if extra = l_val.match /^(?:http:\/\/|)([a-zA-Z0-9][-a-zA-Z0-9\.]+)$/
     entry.type = 'domain'
     entry.val = extra[1]


add comments about AutoLaunch path

add comments about AutoLaunch path

--- src/ui/util.coffee
+++ src/ui/util.coffee
@@ -192,6 +192,7 @@
         range.text = text
         range.select()

+# AutoLaunch requires a path unless you are running in electron/nw
 vesrions = process?.versions
 if versions? and (versions.nw? or versions['node-webkit']? or versions.electron?)
     autoLaunchPath = undefined


more tweaks

more tweaks

--- @client/collapsed_proposal.coffee
+++ @client/collapsed_proposal.coffee
@@ -171,7 +171,7 @@
           DIV
             style:
               fontSize: 12
-              color: 'black'
+              color: 'black' #'#999'
               marginTop: 4
               #fontStyle: 'italic'

@@ -186,7 +186,6 @@
                 SPAN
                   style:
                     padding: '0 8px'
-                    color: 'black'
                   '|'

                 if !icons && (editor = proposal_editor(proposal)) && editor == proposal.user
@@ -199,7 +198,6 @@
                     SPAN
                       style:
                         padding: '0 8px'
-                        color: 'black'
                       '|'
                   ]

@@ -377,8 +375,8 @@

         score = pad score.toFixed(1),2

-        val = "#{opinions.length} opinion#{if opinions.length != 1 then 's' else ''}"
-        score_w = widthWhenRendered val, {fontSize: 12}
+        val = "0000 opinion#{if opinions.length != 1 then 's' else ''}"
+        score_w = widthWhenRendered(' opinion' + (if opinions.length != 1 then 's' else ''), {fontSize: 12}) + widthWhenRendered("0000", {fontSize: 24})

         show_tooltip = =>
           if opinions.length > 0
@@ -397,8 +395,9 @@
           ref: 'score'
           style:
             position: 'absolute'
-            right: -10 - score_w
-            top: 40 - 12
+            right: -18 - score_w
+            top: 23 #40 - 12
+            textAlign: 'left'

           onFocus: show_tooltip
           onMouseEnter: show_tooltip
@@ -408,10 +407,20 @@
           SPAN
             style:
               color: '#999'
-              fontSize: 12
+              fontSize: 20
+              #fontWeight: 600
               cursor: 'default'
-
-            val
+              lineHeight: 1
+            opinions.length
+
+            SPAN
+              style:
+                color: '#999'
+                fontSize: 12
+                cursor: 'default'
+                verticalAlign: 'baseline'
+
+              ' opinion' + (if opinions.length != 1 then 's' else '')

   componentDidUpdate: ->
     if @local.keep_in_view


Clip Path should derive from css-clip-path feature (#793)

I think clip-path should derive from `caniuse-db/features-json/css-clip-path.json` instead of `caniuse-db/features-json/css-masks.json`.
This was a real quick patch. Please check for correctness.

--- data/prefixes.coffee
+++ data/prefixes.coffee
@@ -353,11 +353,17 @@
          'mask-border-source',
           browsers: browsers
           feature: 'css-masks'
-  prefix 'clip-path', 'mask', 'mask-position', 'mask-size',
+  prefix 'mask', 'mask-position', 'mask-size',
          'mask-border', 'mask-border-outset', 'mask-border-width',
          'mask-border-slice',
           browsers: browsers
           feature: 'css-masks'
+
+# CSS clip-path property
+feature require('caniuse-db/features-json/css-clip-path.json'), (browsers) ->
+  prefix 'clip-path',
+          browsers: browsers
+          feature: 'css-clip-path'

 # Fragmented Borders and Backgrounds
 boxdecorbreak = require('caniuse-db/features-json/css-boxdecorationbreak.json')


Update regex for stripping unwanted ep. title text

--- node_modules/application/helpers.coffee
+++ node_modules/application/helpers.coffee
@@ -28,7 +28,7 @@

     episodeTitle: (text, showTitle) ->
       #strip off spaces and "nutv -" from the start
-      text = text.replace(/^(?:\s)*(?:nutv)?(?:\s)*(?:-)*(?:\s)*/i, '')
+      text = text.replace(/^.*[-:]\s?/i, '')
       if showTitle?
         regexStr = '(?:\s)*(?:-)*(?:\s)*' + showTitle + '(?:\s)*(?:-)*(?:\s)*'
         regex = new RegExp('(?:\s)*(?:-)*(?:\s)*' + showTitle + '(?:\s)*(?:-)*(?:\s)*', 'i')


fixup d659a5a0b539d4c5ec577109265501fa54ecb542

--- client/config.coffee
+++ client/config.coffee
@@ -14,7 +14,7 @@
   my_context.validate(doc)
   invalid_keys = my_context.invalidKeys()
   if invalid_keys.length > 0
-    conslog.log "for doc",doc
+    console.log "for doc",doc
     console.warn "invalid keys",invalid_keys
   return


Be sexy, edgy and cool

Be sexy, edgy and cool

--- client/header.coffee
+++ client/header.coffee
@@ -82,7 +82,7 @@
   if Session.get 'currentSearchQuery'
     Session.get 'currentSearchQuery'
   else
-    "Search publications and people"
+    "Search knowledge and people"

 Deps.autorun ->
   $('.search-input').val(Session.get 'currentSearchQuery')


cleaning up console.logs

cleaning up console.logs

--- client/jotgit.coffee
+++ client/jotgit.coffee
@@ -36,11 +36,9 @@

 Template.fileEdit.events(
   'click div.btn-group.btn-toggle': ->
-    console.log 'toggle-toggle'
     $('.btn-toggle').children('.btn').toggleClass "active"
                                       .toggleClass "btn-primary"
     if $('#timer-on-button').hasClass("active")
-      console.log "on"
       $('#timer-settings').css('display': 'inline-block')
       timeInMillisecs = getTimerMillis()
       if timeInMillisecs
@@ -49,7 +47,6 @@
         setTimer(5)
         autoSaveTimer = Meteor.setInterval(commit, 300000)
     else
-      console.log "off"
       $('#timer-settings').hide()
       Meteor.clearInterval autoSaveTimer
     false


Wait for the admin_users collection to be loaded before redirecting

--- client/router.coffee
+++ client/router.coffee
@@ -5,7 +5,6 @@
 Houston._subscribe = (name) -> Meteor.subscribe Houston._houstonize name

 Houston._subscribe 'collections'
-Houston._subscribe 'admin_user'

 setup_collection = (collection_name, document_id) ->
   Houston._page_length = 20
@@ -37,6 +36,9 @@
     # Append _houston_ to template and route names to avoid clobbering parent route namespace
     options.template = Houston._houstonize(options.template)
     options.layoutTemplate = null
+    options.waitOn = ->
+      ready: -> !Meteor.loggingIn() and Houston._subscribe('admin_user').ready()
+    options.action = -> if @ready() then @render()
     @route Houston._houstonize_route(route_name), options

   houston_route 'home',
@@ -81,10 +83,9 @@
 # filters
 # ########
 mustBeAdmin = ->
-  unless Meteor.loggingIn() # don't check for admin user until ready
-    unless Houston._user_is_admin Meteor.userId()
-      @stop()
-      Houston._go 'login'
+  if @ready() and not Houston._user_is_admin Meteor.userId()
+    @stop()
+    Houston._go 'login'

 # If the host app doesn't have a router, their html may show up
 hide_non_admin_stuff = ->
@@ -106,7 +107,7 @@

 Router.onBeforeAction mustBeAdmin,
   only: (Houston._houstonize_route(name) for name in ['home', 'collection', 'document', 'change_password'])
-Router.onAfterAction hide_non_admin_stuff,
+Router.onBeforeAction hide_non_admin_stuff,
   only: (Houston._houstonize_route(name) for name in ['home', 'collection', 'document', 'login', 'custom_template'])
 Router.onBeforeAction remove_host_css,
   only: (Houston._houstonize_route(name) for name in ['home', 'collection', 'document', 'login'])


Somehow working preloading system for frontend.

--- frontend/script.coffee
+++ frontend/script.coffee
@@ -1,6 +1,12 @@
+PRELOAD_COUNT = 20
+# Even though all songs can not be converted in 10 seconds, this at least
+# ensures that a long running song will not completely destroy the preload
+# performance.
+PRELOAD_WAIT_TIMEOUT = 10 * 1000
+
 RESIZE_UPDATE_DELAY = 500
 SEARCH_UPDATE_PRELOAD_DELAY = 2000
-CONVERSION_WAIT_TIMEOUT = 180 * 60 * 1000
+CONVERSION_WAIT_TIMEOUT = 180 * 1000

 MINIMUM_PROGRESS_FILES = 10000
 FILE_PROGRESS_UPDATE_INTERVAL = 2000
@@ -61,6 +67,49 @@
                 shuffled[insert_position + swap_position] = shuffled[insert_position]
                 shuffled[insert_position] = swappable
         return shuffled
+
+
+class Preloader
+        constructor: (@queue, @playbackType, @preloadCount) ->
+                @preloaded = {}
+                @nextPreloads = []
+                @ongoingPreloads = {}
+
+        _preloadSong: =>
+                if @nextPreloads.length == 0
+                        return
+                if _.keys(@ongoingPreloads).length != 0
+                        return
+                song = @nextPreloads.shift()
+                encodedPath = encodeURIComponent song.filename
+                preloadSource = "preload/#{encodedPath}?type=#{@playbackType.request}"
+                @ongoingPreloads[song.filename] = new Date()
+                request = $.ajax preloadSource,
+                        type: "GET"
+                        timeout: PRELOAD_WAIT_TIMEOUT
+                preloadCallback = =>
+                        @preloaded[song.filename] = new Date()
+                        delete @ongoingPreloads[song.filename]
+                        if @nextPreloads.length == 0
+                                return
+                        nextSong = @nextPreloads.shift()
+                        @_preloadSong nextSong
+                request.error preloadCallback
+                request.success preloadCallback
+
+        updatePreload: =>
+                allSongs = @queue.playbackQueue()
+                preloadSongs = allSongs[0..].reverse()
+                @nextPreloads = []
+                totalPreloads = 0
+                for song in preloadSongs
+                        if totalPreloads == @preloadCount
+                                break
+                        if song.filename in @preloaded
+                                continue
+                        totalPreloads++
+                        @nextPreloads.push song
+                @_preloadSong()

 class SongQueue
@@ -86,6 +135,7 @@

         updateAll: (@allSongs) =>
                 @trigger "updateAll", @allSongs
+                @trigger "queueChange"

         updateVisible: (newVisibleSongs) ->
                 if @visibleSongs == newVisibleSongs
@@ -94,6 +144,7 @@
                 @playbackFiles = []
                 @_removeRandom()
                 @trigger "updateVisible", @visibleSongs
+                @trigger "queueChange"

         length: =>
                 return @fullQueue().length
@@ -103,6 +154,7 @@
                 @clearNext()
                 if next != null
                         @trigger "next", next
+                        @trigger "queueChange"
                 return next

         clearNext: =>
@@ -144,6 +196,7 @@
                 @storage.queue = JSON.stringify @fullQueue()
                 @_removeRandom()
                 @trigger "add", song
+                @trigger "queueChange"
                 return @queuedSongs.length

         remove: (index) =>
@@ -153,10 +206,14 @@
                 @storage.queue = JSON.stringify queue
                 @queuedSongs = queue
                 @trigger "remove", removed, index
+                @trigger "queueChange"
                 return removed

         show: =>
                 return @queuedSongs
+
+        playbackQueue: =>
+                return @playbackFiles

 class PlaybackType
@@ -802,6 +859,8 @@
         timeouter = new RetryTimeouter()
         playerInstance = new Player playerElement, preloadElement, localStorage, timeouter, playbackType
         songQueue = new SongQueue localStorage
+        preloader = new Preloader songQueue, playbackType, PRELOAD_COUNT
+        songQueue.on "queueChange", preloader.updatePreload

         PlayerView playerContainer, playerInstance, songQueue


Inserting a string as the template parameter returns the string itself

--- src/stix.coffee
+++ src/stix.coffee
@@ -1,18 +1,21 @@
 do (root = module?.exports ? this) ->

-  root.sticks = (template, options = {compress: false}) ->
+  root.stix = (template, options = {compress: false}) ->
     content = ''

-    for key, value of template
-      content += "<#{key}>"
-
-      if typeof value is 'function'
-        unless options.compress then content += '\n\t'
-        content += sticks value()
-      else
-        content += value
+    if typeof template is 'string'
+      return template
+    else
+      for key, value of template
+        content += "<#{key}>"

-      content += "</#{key}>"
-      unless options.compress then content += '\n'
+        if typeof value is 'function'
+          unless options.compress then content += '\n\t'
+          content += stix value()
+        else
+          content += value
+
+        content += "</#{key}>"
+        unless options.compress then content += '\n'

     content


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -30,6 +30,7 @@

   ok fastEnemy.I.velocity.equal(Point(0, -5)), 'fastEnemy should head toward player with velocity Point(0, 5)'

-  rightEnemy =
+  rightEnemy = GameObject
+

 module()


prevNode fix

prevNode fix

--- coffee/parser.coffee
+++ coffee/parser.coffee
@@ -145,7 +145,7 @@
      @node.add node
      if node.type is TYPES.block
       @blocks.push node
-     @node = node
+     @prevNode = @node = node

     getOffsetTop: (elem, parent) ->
      top = 0


fix: wrong api invoke

fix: wrong api invoke

--- src/yaku.coffee
+++ src/yaku.coffee
@@ -612,7 +612,9 @@

 		while i < len
 			p1[i] = p2[i]
-			addHandler p1, p2[i++]
+			addHandler p2[i++], p1
+
+		return

 	###*
 	 * Try to get a promise's then method.


GameObject: Fix duplicate children in clones.

--- engine/gameobject.coffee
+++ engine/gameobject.coffee
@@ -342,8 +342,6 @@
             child_options = Object.assign {}, options, {new_parent: n}
             for child in @children
                 child = child.clone child_options
-                child.parent = n
-                children.push child
         n.body.instance()
         for child in n.children
             child.body.instance()


Fix render unsatisfiable goals. Related to #1330

--- admin/static/coffee/resolve_issues.coffee
+++ admin/static/coffee/resolve_issues.coffee
@@ -403,19 +403,23 @@
                                 num_machines: 0
                     else
                         # We won't add universe here because if universe has too many replicas, it's an issue in which there might two solutions. We will deal with this case later
-                        if datacenter_id isnt universe_datacenter.get('id') and number_replicas > @model.get('actual_machines_in_datacenters')[datacenter_id]
+                        if @model.get('actual_machines_in_datacenters')[datacenter_id]?
+                            number_machines_in_datacenter = @model.get('actual_machines_in_datacenters')[datacenter_id]
+                        else
+                            number_machines_in_datacenter = 0
+                        if datacenter_id isnt universe_datacenter.get('id') and number_replicas > number_machines_in_datacenter
                             datacenter_name = datacenters.get(datacenter_id).get('name') # That's safe, datacenters.get(datacenter_id) is defined

                             json.datacenters_with_issues.push
                                 datacenter_id: datacenter_id
                                 datacenter_name: datacenter_name
                                 num_replicas: number_replicas
-                                num_machines: @model.get('actual_machines_in_datacenters')[datacenter_id]
-                                change_ack: namespace.get('ack_expectations')[datacenter_id] > @model.get('actual_machines_in_datacenters')[datacenter_id]
+                                num_machines: number_machines_in_datacenter
+                                change_ack: namespace.get('ack_expectations')[datacenter_id] > number_machines_in_datacenter

                         # We substract the number of machines used by the datacenter if we solve the issue
                         if datacenter_id isnt universe_datacenter.get('id')
-                            number_machines_universe_can_use_if_no_known_issues -= Math.min number_replicas, @model.get('actual_machines_in_datacenters')[datacenter_id]
+                            number_machines_universe_can_use_if_no_known_issues -= Math.min number_replicas, number_machines_in_datacenter

                 # The primary might not have a value in @model.get('replica_affinities'), in which case we just missed it, so let's check
                 if not @model.get('replica_affinities')[@model.get('primary_datacenter')]? and @model.get('primary_datacenter') isnt universe_datacenter.get('id')
@@ -428,12 +432,18 @@
                     if found_machine is false
                         # We didn't find any machine in the datacenter
                         datacenter_id = @model.get('primary_datacenter')
+
+                        if @model.get('actual_machines_in_datacenters')[datacenter_id]?
+                            number_machines_in_datacenter = @model.get('actual_machines_in_datacenters')[datacenter_id]
+                        else
+                            number_machines_in_datacenter = 0
+
                         json.datacenters_with_issues.push
                             datacenter_id: datacenter_id
                             datacenter_name: datacenters.get(datacenter_id).get('name') # Safe since it cannot be universe
                             num_replicas: number_replicas
                             num_machines: 0
-                            change_ack: namespace.get('ack_expectations')[datacenter_id] > @model.get('actual_machines_in_datacenters')[datacenter_id]
+                            change_ack: namespace.get('ack_expectations')[datacenter_id] > number_machines_in_datacenter

                 number_machines_requested_by_universe = @model.get('replica_affinities')[universe_datacenter.get('id')]
@@ -441,6 +451,10 @@
                     number_machines_requested_by_universe++
                 # If universe has some responsabilities and that the user is asking for too many replicas across the whole cluster
                 if @model.get('replica_affinities')[universe_datacenter.get('id')]? and number_machines_universe_can_use_if_no_known_issues < number_machines_requested_by_universe
+                    number_replicas = @model.get('replica_affinities')[universe_datacenter.get('id')]
+                    if datacenter_id is @model.get('primary_datacenter')
+                        number_replicas++
+
                     extra_replicas_accross_cluster = @model.get('replica_affinities')[universe_datacenter.get('id')] - number_machines_universe_can_use_if_no_known_issues

                     # We might still be able to solve the issue if the table is just using universe. Let's check that


Fix Snapshot Favorite Bug

Fix Snapshot Favorite Bug

--- server/app/assets/javascript/fav.coffee
+++ server/app/assets/javascript/fav.coffee
@@ -1,12 +1,12 @@
 $(document).ready ->
   loadFavCounter()

-@loadFavCounter = (path) ->
-  $('.favorite-group').each ->
-    path = $(this).attr('data-path')
-    path = if path then path else (location.pathname + location.search + location.hash)
-    btn = $(this).find('.btn-add-favorite')
-    counter = $(this).find('.fav-counter')
+@loadFavCounter = () ->
+  $('.favorite-group').each (i, elem) ->
+    path = $(elem).attr('data-path')
+    path ?= location.pathname + location.search + location.hash
+    btn = $(elem).find('.btn-add-favorite')
+    counter = $(elem).find('.fav-counter')
     btn.click -> addFavorite(btn, counter, path)
     btn.each -> checkButton(btn, path)
     counter.each -> favCounter(counter, path)


Commented unreachable code in JS to suppress js console warning.

--- app/assets/javascripts/core_ext.js.coffee
+++ app/assets/javascripts/core_ext.js.coffee
@@ -29,8 +29,8 @@
 Math.humanize = (value, power = 0) ->
   return Math.round(value)
   # return Math.round(value / Math.pow(10, power)) + "e#{power}"
-  size = Math.round(power / 3)
-  return Math.round(value / Math.pow(10, 3 * size)) + "pnµm KMGTPE"[size + 4]
+  # size = Math.round(power / 3)
+  # return Math.round(value / Math.pow(10, 3 * size)) + "pnµm KMGTPE"[size + 4]

 Math.ceil2 = (number, round = 1) ->
   return round * Math.ceil(number / round)


fixes #264

fixes #264

--- app/assets/javascripts/main/router.coffee
+++ app/assets/javascripts/main/router.coffee
@@ -26,7 +26,7 @@
     # handle all links and manage page changes (rather the reloading the whole site)
     $(document).on "click", "a", (evt) =>

-      url = $(evt.currentTarget).attr("href")
+      url = $(evt.currentTarget).attr("href") || ""
       urlWithoutSlash = url.slice(1)

       if @routes[urlWithoutSlash]


BF-124 adjusted branching, now compatible with brainflight DB

--- app/assets/javascripts/model/route.coffee
+++ app/assets/javascripts/model/route.coffee
@@ -84,7 +84,8 @@
     @initialize().done =>

       @addToBuffer(position)
-      @branchStack.push(position)
+      # push TransformationMatrix for compatibility reasons
+      @branchStack.push([0,0,0,0,0,0,0,0,0,0,0,0,position[0],position[1],position[2],0])

     return


Fix: Adjust in events animation at newsfeed#index

--- app/assets/javascripts/newsfeed.js.coffee
+++ app/assets/javascripts/newsfeed.js.coffee
@@ -32,7 +32,7 @@
     eventEvents: ->

       # event mouseenter
-      $(".event .panel").mouseenter ->
+      $(".Event--newsFeed .panel").mouseenter ->
         # limpa o timeout
         i = 0
         while i < timeouts.length
@@ -74,7 +74,7 @@

       # event mouseleave
-      $(".event .panel").mouseleave ->
+      $(".Event--newsFeed .panel").mouseleave ->
         $(".infobox").hide().text("")


Add equalHeights to Startup cards

Add equalHeights to Startup cards

--- app/assets/javascripts/startups.js.coffee
+++ app/assets/javascripts/startups.js.coffee
@@ -1,17 +1,17 @@
-initialize = () ->
-    $markets = $("#startup_market_list")
-
-    $markets.tokenInput "http://api.angel.co/1/search?type=MarketTag",
-      crossDomain: true,
-      queryParam: "query",
-      prePopulate: $markets.data('pre'),
-      theme: "facebook",
-      tokenLimit: 3,
-      tokenValue: "name"
-
 $ ->
     $body = $('body')
+    bodyClass = $body.attr 'class'
     routes = ['startups-new', 'startups-edit']

-    if _.some(routes, (route) -> $body.hasClass route)
-        initialize()+    if bodyClass in ['startups-new', 'startups-edit']
+        $markets = $("#startup_market_list")
+
+        $markets.tokenInput "http://api.angel.co/1/search?type=MarketTag",
+          crossDomain: true,
+          queryParam: "query",
+          prePopulate: $markets.data('pre'),
+          theme: "facebook",
+          tokenLimit: 3,
+          tokenValue: "name"
+    else if bodyClass is 'startups-index'
+        $(".startupcard").equalHeights()


Bugfix

Bugfix

--- svgtiler.coffee
+++ svgtiler.coffee
@@ -207,7 +207,7 @@
       if symbol.xml.documentElement.tagName in ['svg', 'symbol']
         ## Remove a layer of indirection for <svg> and <symbol>
         for attribute in symbol.xml.documentElement.attributes
-          unless attribute in ['version'] or attribute[...5] == 'xmlns'
+          unless attribute.name in ['version'] or attribute.name[...5] == 'xmlns'
             node.setAttribute attribute.name, attribute.value
         for child in symbol.xml.documentElement.childNodes
           node.appendChild child.cloneNode true


fix: remove selected preference

fix: remove selected preference

--- app/components/project-preferences.coffee
+++ app/components/project-preferences.coffee
@@ -9,7 +9,6 @@
   store: Ember.inject.service()
   selectedDeviceType: ENUMS.DEVICE_TYPE.NO_PREFERENCE
   deviceTypes: ENUMS.DEVICE_TYPE.CHOICES[1...-1]
-  selectedPreference: false

   devices: (->
     store = @get "store"
@@ -61,7 +60,6 @@
       @get("ajax").post devicePreferences, data: data
       .then (data) ->
         that.get("notify").success "You have sucessfully selected the device"
-        that.set "selectedPreference", true
       .catch (error) ->
         that.get("notify").error "failed"
         if Ember.isEmpty error?.errors


Finish support for downloading as csv.

Finish support for downloading as csv.

--- app/controllers/interactive/graphs.coffee
+++ app/controllers/interactive/graphs.coffee
@@ -128,12 +128,12 @@
     window.open img

   generateCSV: (e) =>
-    headerString = @createCSVHeader(@graph.filteredData[0]) + '\n'
+    headerString = (@createCSVHeader(@graph.filteredData[0]) + '\n').slice(1)
     console.log headerString
     bodyString = @createCSVBody @graph.filteredData
     console.log bodyString
     csv = headerString + bodyString
-    window.location.href = "data:application/csv;charset=UTF-8," + encodeURIComponent(csv)
+    window.location.href = "data:text/csv;charset=UTF-8," + encodeURIComponent(csv)

   createCSVHeader: (datum, prefix='') =>
     header = new String
@@ -146,7 +146,7 @@

   createCSVBody: (data) =>
     body = new Array
-    body.push @createCSVLine datum for datum in data
+    body.push (@createCSVLine(datum)).slice(1) for datum in data
     return body.join '\n'

   createCSVLine: (datum) =>


Bug: fixes #218 - node colour fill on clustering

--- app/scripts/alchemy/core/drawnodes.coffee
+++ app/scripts/alchemy/core/drawnodes.coffee
@@ -72,7 +72,7 @@
         .attr('target-id', (d) -> d.id)
         .attr('style', (d) ->
            radius = d3.select(this).attr('r')
-           "fill:#{nodeColours(d)}; stroke-width: #{ radius / 3 }")
+           "#{nodeColours(d)}; stroke-width: #{ radius / 3 }")

     #append caption to the node
     nodeEnter


Revert "intel-corei7-64.coffee: Define attribute for the yocto flasher image"

This reverts commit 9b4a662b00551bd6dfc243bf1f8663aa2d90cfc6.

We revert this because the UI part is not ready yet.

--- intel-corei7-64.coffee
+++ intel-corei7-64.coffee
@@ -91,8 +91,7 @@
 		image: 'resin-image-flasher'
 		fstype: 'resinos-img'
 		version: 'yocto-morty'
-		deployArtifact: 'resin-image-intel-corei7-64.resinos-img'
-		deployFlasherArtifact: 'resin-image-flasher-intel-corei7-64.resinos-img'
+		deployArtifact: 'resin-image-flasher-intel-corei7-64.resinos-img'
 		compressed: true

 	configuration:


Mechanics: Correct several move effects.

--- data/bw/moves.coffee
+++ data/bw/moves.coffee
@@ -483,7 +483,7 @@
 extendWithDrain 'Absorb'
 extendWithSecondaryBoost 'Acid', 'target', .1, specialDefense: -1
 makeBoostMove 'Acid Armor', 'self', defense: 2
-makeBoostMove 'Acid Spray', 'target', specialDefense: -2
+extendWithSecondaryBoost 'Acid Spray', 'target', 1, specialDefense: -2
 makeBoostMove 'Agility', 'self', speed: 2
 makeBoostMove 'Amnesia', 'self', specialDefense: 2
 extendWithSecondaryBoost 'AncientPower', 'self', .1, {
@@ -541,7 +541,7 @@
 extendWithSecondaryBoost 'BubbleBeam', 'target', .1, speed: -1
 extendWithSecondaryBoost 'Bug Buzz', 'target', .1, specialDefense: -1
 makeBoostMove 'Bulk Up', 'self', attack: 1, defense: 1
-extendWithBoost 'Bulldoze', 'target', speed: -1
+extendWithSecondaryBoost 'Bulldoze', 'target', 1, speed: -1
 makeBoostMove 'Calm Mind', 'self', specialAttack: 1, specialDefense: 1

 extendMove 'Camouflage', ->
@@ -569,7 +569,7 @@
     oldUse.call(this, battle, user, target)

 makeBoostMove 'Charm', 'target', attack: -2
-extendWithSecondaryEffect 'Chatter', 1, Attachment.Confusion
+extendWithSecondaryEffect 'Chatter', .1, Attachment.Confusion
 extendWithSecondaryBoost 'Charge Beam', 'self', .7, specialAttack: 1

 extendMove 'Chip Away', ->
@@ -649,7 +649,7 @@
 makeRandomSwitchMove "Dragon Tail"
 extendWithSecondaryEffect 'DynamicPunch', 1, Attachment.Confusion
 extendWithSecondaryBoost 'Earth Power', 'target', .1, specialDefense: -1
-extendWithBoost 'Electroweb', 'target', speed: -1
+extendWithSecondaryBoost 'Electroweb', 'target', 1, speed: -1
 extendWithSecondaryBoost 'Energy Ball', 'target', .1, specialDefense: -1

 extendMove 'Embargo', ->
@@ -688,7 +688,7 @@
 makeTrappingMove "Fire Spin"
 makeOneHitKOMove 'Fissure'
 makeReversalMove 'Flail'
-extendWithBoost 'Flame Charge', 'self', speed: 1
+extendWithSecondaryBoost 'Flame Charge', 'self', 1, speed: 1
 extendMove 'Flame Wheel', -> @thawsUser = true
 extendWithSecondaryStatus 'Flame Wheel', .1, Status.Burn
 extendWithSecondaryStatus 'Flamethrower', .1, Status.Burn
@@ -718,7 +718,7 @@
 extendMove 'Fusion Flare', -> @thawsUser = true
 extendWithDrain 'Giga Drain'
 makeRechargeMove 'Giga Impact'
-extendWithBoost 'Glaciate', 'target', speed: -1
+extendWithSecondaryBoost 'Glaciate', 'target', 1, speed: -1
 makeWeightBased 'Grass Knot'
 extendWithPrimaryStatus 'GrassWhistle', Status.Sleep
 makeBoostMove 'Growl', 'target', attack: -1
@@ -750,7 +750,7 @@
 makeRechargeMove 'Hyper Beam'
 extendWithPrimaryStatus 'Hypnosis', Status.Sleep
 makeMomentumMove 'Ice Ball'
-extendWithBoost 'Icy Wind', 'target', speed: -1
+extendWithSecondaryBoost 'Icy Wind', 'target', 1, speed: -1
 makeBoostMove 'Iron Defense', 'self', defense: 2
 extendWithSecondaryBoost 'Iron Tail', 'target', .1, defense: -1
 makeWeatherMove 'Hail', Weather.HAIL
@@ -772,7 +772,7 @@
 makeJumpKick 'Jump Kick'
 extendWithSecondaryStatus 'Lava Plume', .3, Status.Burn
 extendWithBoost 'Leaf Storm', 'self', specialAttack: -2
-extendWithSecondaryBoost 'Leaf Tornado', 'target', .3, accuracy: -1
+extendWithSecondaryBoost 'Leaf Tornado', 'target', .5, accuracy: -1
 makeBoostMove 'Leer', 'target', defense: -1
 extendWithDrain 'Leech Life'

@@ -795,7 +795,7 @@
 extendWithSecondaryStatus 'Lick', .3, Status.Paralyze
 makeLockOnMove 'Lock-On'
 makeWeightBased 'Low Kick'
-extendWithBoost 'Low Sweep', 'target', speed: -1
+extendWithSecondaryBoost 'Low Sweep', 'target', 1, speed: -1
 extendWithPrimaryStatus 'Lovely Kiss', Status.Sleep

 extendMove 'Lucky Chant', ->
@@ -837,8 +837,8 @@
 makeWeatherRecoveryMove 'Moonlight'
 makeWeatherRecoveryMove 'Morning Sun'
 extendWithSecondaryBoost 'Mud Bomb', 'target', .3, accuracy: -1
-extendWithBoost 'Mud Shot', 'target', speed: -1
-extendWithBoost 'Mud-Slap', 'target', accuracy: -1
+extendWithSecondaryBoost 'Mud Shot', 'target', 1, speed: -1
+extendWithSecondaryBoost 'Mud-Slap', 'target', 1, accuracy: -1
 extendWithSecondaryBoost 'Muddy Water', 'target', .3, accuracy: -1
 makeBoostMove 'Nasty Plot', 'self', specialAttack: 2
 extendWithSecondaryEffect 'Needle Arm', .3, Attachment.Flinch
@@ -876,7 +876,7 @@
 extendWithSecondaryEffect 'Rock Climb', .2, Attachment.Confusion
 makeBoostMove 'Rock Polish', 'self', speed: 2
 extendWithSecondaryBoost 'Rock Smash', 'target', .5, defense: -1
-extendWithBoost 'Rock Tomb', 'target', speed: -1
+extendWithSecondaryBoost 'Rock Tomb', 'target', 1, speed: -1
 extendWithSecondaryEffect 'Rock Slide', .3, Attachment.Flinch
 makeRechargeMove 'Rock Wrecker'
 extendWithSecondaryEffect 'Rolling Kick', .3, Attachment.Flinch
@@ -886,7 +886,7 @@
   @afterSuccessfulHit = (battle, user, target, damage) ->
     user.attach(Attachment.Roost)

-extendWithBoost 'Sand-Attack', 'target', accuracy: -1
+makeBoostMove 'Sand-Attack', 'target', accuracy: -1
 extendMove 'Sacred Fire', -> @thawsUser = true
 extendWithSecondaryStatus 'Sacred Fire', .5, Status.Burn
 makeWeatherMove 'Sandstorm', Weather.SAND
@@ -921,7 +921,7 @@
 extendWithSecondaryStatus 'Sludge Wave', .1, Status.Poison
 extendWithSecondaryStatus 'Smog', .4, Status.Poison
 makeBoostMove 'SmokeScreen', 'target', accuracy: -1
-extendWithBoost 'Snarl', 'target', specialAttack: -1
+extendWithSecondaryBoost 'Snarl', 'target', 1, specialAttack: -1
 extendWithSecondaryEffect 'Snore', .3, Attachment.Flinch
 makeRecoveryMove 'Softboiled'
 extendWithSecondaryStatus 'Spark', .3, Status.Paralyze
@@ -965,7 +965,7 @@
 makeStompMove 'Stomp'
 makeBasePowerBoostMove 'Stored Power', 20, 860, 'user'
 makeBoostMove 'String Shot', 'target', speed: -1
-extendWithBoost 'Struggle Bug', 'target', specialAttack: -1
+extendWithSecondaryBoost 'Struggle Bug', 'target', 1, specialAttack: -1
 extendWithPrimaryStatus 'Stun Spore', Status.Paralyze
 makeWeatherMove 'Sunny Day', Weather.SUN
 extendWithBoost 'Superpower', 'self', attack: -1, defense: -1
@@ -1004,7 +1004,7 @@
     halfHP = Math.floor(target.currentHP / 2)
     Math.max(1, halfHP)
 makeWeatherRecoveryMove 'Synthesis'
-makeBoostMove 'Tail Glow', 'self', attack: 3
+makeBoostMove 'Tail Glow', 'self', specialAttack: 3
 makeBoostMove 'Tail Whip', 'target', defense: -1
 extendWithPrimaryEffect 'Teeter Dance', Attachment.Confusion
 extendMove 'Teleport', (battle) ->


drop cache flags

drop cache flags

--- apps/fair/lib/fair_data_middleware.coffee
+++ apps/fair/lib/fair_data_middleware.coffee
@@ -12,13 +12,11 @@
   # TODO: Replace this profile fetch for a more generic solution:
   # https://www.pivotaltracker.com/story/show/67567624
   new Profile(id: req.params.id).fetch
-    cache: true
     error: res.backboneError
     success: (profile) ->
       return next() unless profile.isFairOranizer()
       fair = new Fair id: profile.get('owner').default_fair_id
       fair.fetchPrimarySets
-        cache: true
         error: res.backboneError
         success: (primarySets) =>
           res.locals.primarySets = primarySets


Explicitly use "full" projection for Google Books (fixes #15)

--- js/book-aligner.coffee
+++ js/book-aligner.coffee
@@ -229,7 +229,7 @@

 gb_biblio_query = (gb_id, score = 0) ->
   if $("##{html_id(gb_id)}").length == 0
-    $.ajax "#{GOOGLE_BOOKS_URI}/#{gb_id}?key=#{GOOGLE_BOOKS_API_KEY}",
+    $.ajax "#{GOOGLE_BOOKS_URI}/#{gb_id}?projection=full&key=#{GOOGLE_BOOKS_API_KEY}",
       type: 'GET'
       cache: true
       dataType: 'json'


Use Urls.githubCommit for githubify

Use Urls.githubCommit for githubify

--- assets/scripts/app/helpers/helpers.coffee
+++ assets/scripts/app/helpers/helpers.coffee
@@ -98,7 +98,8 @@
   _githubCommitReferenceLink: (reference, current, matched) ->
     owner = matched.owner || current.owner
     repo = matched.repo || current.repo
-    "#{reference}"
+    url = "#{Travis.Urls.githubCommit("#{owner}/#{repo}", matched.sha)}"
+    "#{reference}"

   _normalizeDateString: (string) ->
     if window.JHW


:lipstick:

:lipstick:

--- build/tasks/output-long-paths-task.coffee
+++ build/tasks/output-long-paths-task.coffee
@@ -7,7 +7,7 @@
     longPaths = []
     grunt.file.recurse shellAppDir, (absolutePath, rootPath, relativePath, fileName) ->
       if relativePath
-        fullPath =  path.join(relativePath, fileName)
+        fullPath = path.join(relativePath, fileName)
       else
         fullPath = fileName
       longPaths.push(fullPath) if fullPath.length >= 200


Refactoring.

Refactoring.

--- views/index.coffee
+++ views/index.coffee
@@ -18,33 +18,25 @@
 { html, head, meta, title, link, body, header, div, script } = Elements

 googleFontsUrl = 'http://fonts.googleapis.com/css?family='
+stylesheets = [
+  googleFontsUrl + 'Open+Sans:400,400italic,700,700italic,800italic'
+  googleFontsUrl + 'Rokkitt:400,700'
+  server + assetPaths.styles.css
+]

 module.exports = React.createClass
   render: ->
+    metadata = [
+      meta charSet: 'utf-8'
+      meta name: 'viewport', content: 'width=device-width, initial-scale=1'
+      title(@props.title)
+    ].concat stylesheets.map (href) ->
+        link rel: 'stylesheet', href: href
     html(
       { lang: @props.lang }
-      head(
-        meta(charSet: "utf-8")
-        meta(
-          name: "viewport"
-          content: "width=device-width, initial-scale=1"
-        )
-        title(@props.title)
-        link(
-          rel: "stylesheet"
-          href: googleFontsUrl + "Open+Sans:400,400italic,700,700italic,800italic"
-        )
-        link(
-          rel: "stylesheet"
-          href: googleFontsUrl + "Rokkitt:400,700"
-        )
-        link(
-          rel: "stylesheet"
-          href: server + assetPaths.styles.css
-        )
-      )
+      head metadata
       body(
-        div(id: "app", innerHtml: @props.app)
+        div id: 'app', innerHtml: @props.app
         script
           id: 'state', type: 'application/json'
           JSON.stringify @props.state


fixed nested object

fixed nested object

--- lib/Bumped.util.coffee
+++ lib/Bumped.util.coffee
@@ -16,15 +16,15 @@
     firstChar = opts.value.charAt(0);
     lastChar = opts.value.charAt(opts.value.length - 1);
     isArray = (firstChar is '[') and (lastChar is ']')
-    isDotProp = opts.property.split('.') > 1
+    isDotProp = opts.property.split('.').length > 1

-    if isDotProp
-      dotProp.set(file, opts.property, opts.value);
-    else if isArray
+    if isArray
       items = opts.value.substring(1, opts.value.length - 1)
       items = items.split(',')
       items = items.map (item) -> item.trim()
       file[opts.property] = items
+    else if isDotProp
+      dotProp.set(file, opts.property, opts.value);
     else
       file[opts.property] = opts.value


now support import other type files for code block

--- lib/doc-imports.coffee
+++ lib/doc-imports.coffee
@@ -65,7 +65,16 @@
       catch e
         output = "
#{e.toString()}
"
     else # codeblock
-      output = ''
+      try
+        fileContent = fs.readFileSync(absoluteFilePath, {encoding: 'utf-8'})
+        output = """
+```#{extname.slice(1, extname.length)}
+#{fileContent}
+```
+"""
+        filesCache?[absoluteFilePath] = output
+      catch e
+        output = "
#{e.toString()}
"

     return output


save the number of points per second so that I know if the quad is broken or not

--- src/analytics-router/persistence.coffee
+++ src/analytics-router/persistence.coffee
@@ -16,15 +16,32 @@

   startTime.clone().add(timeInSeconds, 'seconds').toDate()

+pointsPerSecond = 0
+
 save = (name, timeInSeconds, value) ->
   point =
     time: calculateTime(timeInSeconds)
     value: value

   console.log name, value, calculateTime(timeInSeconds)
+  pointsPerSecond++

   influxdb.writePoint name, point, {}, (error) ->
     if error
       console.log "ah oh, error writing point to series #{name}:", error

+
+savePointsPerSecond = ->
+  point =
+    time: moment().toDate()
+    value: pointsPerSecond
+
+  influxdb.writePoint 'pps-.metr', point, {}, (error) ->
+    if error
+      console.log "ah oh, error writing point to series pps-.metr:", error
+
+  pointsPerSecond = 0
+
+setInterval(savePointsPerSecond, 1000)
+
 module.exports = save: save


Fix the merge.

Fix the merge.

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -235,7 +235,7 @@

   # Internal
   remoteBranchName: ->
-    @repo.getConfigValue("atom.open-on-github.branch", @filePath).then(configBranch) =>
+    @repo.getConfigValue("atom.open-on-github.branch", @filePath).then (configBranch) =>
       return configBranch if configBranch?

       @remoteName().then (remoteName) =>


Order values by label rather than index

Order values by label rather than index

This fixes an issue when comparing the working and upstream condition
values. The difference in order of the arrays caused the condition to
be considered 'dirty'. Sorting against the label ensures the comparison
and sorting is not index-based (i.e. arbitrary).

--- src/coffee/cilantro/models/value.coffee
+++ src/coffee/cilantro/models/value.coffee
@@ -34,7 +34,7 @@
     class Values extends c.Backbone.Collection
         model: Value

-        comparator: 'index'
+        comparator: 'label'

         initialize: ->
             super


rest.all 方法增加输出总条目数的头信息

rest.all 方法增加输出总条目数的头信息

--- lib/helper/rest.coffee
+++ lib/helper/rest.coffee
@@ -75,13 +75,14 @@
   # hook 默认为空，如果指定了hook，则数据不直接输出而是先挂在 hook上
   all: (Model, opt = null, allowAttrs, hook = null) ->
     (req, res, next) ->
-      options = (opt and req.hooks[opt]) or
-        Model.findAllOpts(req.params, yes)
+      options = (opt and req.hooks[opt]) or Model.findAllOpts(req.params, yes)
+      ignoreTotal = req.params._ignoreTotal is 'yes'
       utils.callback(Model.findAll(options), (error, ls) ->
         return next(error) if error
         ls = listAttrFilter(ls, allowAttrs)
         unless hook
           ls = listAttrFilter(ls, req.params.attrs.split(',')) if req.params.attrs
+        res.header("X-Content-Record-Total", ls.length) unless ignoreTotal
         hook and (req.hooks[hook] = ls) or res.send(200, ls)
         next()
       )


:art: Use Config.onDidChange instead of Config.observe

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -32,14 +32,12 @@
     @subscriptions.add atom.workspace.onDidChangeActivePaneItem =>
       @commands.lint()

-    ignoreTabUpdates = true
-    @subscriptions.add atom.config.observe 'linter.showErrorTabLine', =>
-      @views.updateTabs() unless ignoreTabUpdates
-    @subscriptions.add atom.config.observe 'linter.showErrorTabFile', =>
-      @views.updateTabs() unless ignoreTabUpdates
-    @subscriptions.add atom.config.observe 'linter.showErrorTabProject', =>
-      @views.updateTabs() unless ignoreTabUpdates
-    ignoreTabUpdates = false
+    @subscriptions.add atom.config.onDidChange 'linter.showErrorTabLine', =>
+      @views.updateTabs()
+    @subscriptions.add atom.config.onDidChange 'linter.showErrorTabFile', =>
+      @views.updateTabs()
+    @subscriptions.add atom.config.onDidChange 'linter.showErrorTabProject', =>
+      @views.updateTabs()

     @subscriptions.add atom.workspace.observeTextEditors (editor) =>
       currentEditorLinter = new EditorLinter @, editor


:art: Consume EditorRegistry in main instead of ES6 Map

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -17,8 +17,8 @@
     # Private Stuff
     @subscriptions = new CompositeDisposable
     @emitter = new Emitter
-    @editorLinters = new Map
     @linters = new (require('./linter-registry'))()
+    @editors = new (require('./editor-registry'))()
     @messages = new (require('./messages'))()
     @views = new LinterViews(this)
     @commands = new Commands(this)
@@ -78,21 +78,20 @@
     @deleteMessages(linter)

   getActiveEditorLinter: ->
-    @getEditorLinter atom.workspace.getActiveTextEditor()
+    return @editors.ofActiveTextEditor()

   getEditorLinter: (editor) ->
-    @editorLinters.get editor
+    return @editors.ofTextEditor(editor)

   eachEditorLinter: (callback) ->
-    @editorLinters.forEach(callback)
+    @editors.editorLinters.forEach(callback)

   observeEditorLinters: (callback) ->
     @eachEditorLinter callback
     @emitter.on 'observe-editor-linters', callback

   createEditorLinter: (editor) ->
-    editorLinter = new EditorLinter(editor)
-    @editorLinters.set editor, editorLinter
+    editorLinter = @editors.create(editor)
     @emitter.emit 'observe-editor-linters', editorLinter
     editorLinter.onShouldUpdateBubble =>
       @views.renderBubble()
@@ -102,14 +101,12 @@
       @linters.lint({onChange, editorLinter})
     editorLinter.onDidDestroy =>
       editorLinter.deactivate()
-      @editorLinters.delete(editor)
       @messages.deleteEditorMessages(editor)

   deactivate: ->
     @subscriptions.dispose()
-    @eachEditorLinter (linter) ->
-      linter.deactivate()
     @views.destroy()
+    @editors.deactivate()
     @linters.deactivate()
     @commands.destroy()
     @messages.deactivate()


Remove unnecessary wrapper

Remove unnecessary wrapper

--- lib/mocha-given.coffee
+++ lib/mocha-given.coffee
@@ -79,6 +79,8 @@
 			suites[0].afterAll(fn)
 			return

+		context.Given =
+		context.When =
 		context.beforeEach = (fn) ->
 			suites[0].beforeEach(fn)
 			return
@@ -87,7 +89,8 @@
 			suites[0].afterEach(fn)
 			return

-		context.describe = context.context = (title, fn) ->
+		context.describe =
+		context.context = (title, fn) ->
 			suite = Suite.create(suites[0], title)
 			suites.unshift(suite)
 			fn.call(suite)
@@ -109,7 +112,8 @@
 			mocha.grep(suite.fullTitle())
 			return suite

-		context.it = context.specify = (title, fn) ->
+		context.it =
+		context.specify = (title, fn) ->
 			suite = suites[0]
 			fn = null if suite.pending
 			test = new Test(title, fn)
@@ -128,12 +132,6 @@
 			context.it(title)
 			return

-		context.Given = (fn) ->
-			context.beforeEach(fn)
-
-		context.When = (fn) ->
-			context.beforeEach(fn)
-
 		context.Then = ->
 			declareSpec arguments, context.it


Fix name collision

Fix name collision

--- lib/path-loader.coffee
+++ lib/path-loader.coffee
@@ -3,7 +3,7 @@

 module.exports =
   startTask: (callback) ->
-    projectPaths = []
+    results = []
     taskPath = require.resolve('./load-paths-handler')
     followSymlinks = atom.config.get 'core.followSymlinks'
     ignoredNames = atom.config.get('fuzzy-finder.ignoredNames') ? []
@@ -17,10 +17,10 @@
       followSymlinks,
       ignoreVcsIgnores,
       ignoredNames, ->
-        callback(projectPaths)
+        callback(results)
     )

     task.on 'load-paths:paths-found', (paths) ->
-      projectPaths.push(paths...)
+      results.push(paths...)

     task


Adapt to latest robe

Adapt to latest robe

--- lib/robe-client.coffee
+++ lib/robe-client.coffee
@@ -55,4 +55,4 @@
           console.error errorThrown
           reject(textStatus)
         success: (data, textStatus, jqXHR) ->
-          resolve(JSON.parse(data))
+          resolve(data)


Add wrapper for api endpoints recipe app needs access to (to avoid CORS trouble)

--- src/handlers/beers/RecipeHandler.coffee
+++ src/handlers/beers/RecipeHandler.coffee
@@ -45,6 +45,59 @@
 				handler: (req, reply) =>
 					@onRequest(req, reply,  "Fork #{req.params.id}", 'beers')
 			}
+
+			# The Recipe app will want some data
+			# We proxy the API to avoid CORS
+			{
+				path: '/api/fermentables'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.fermentables.get { size: 1000, callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/hops'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.hops.get { size: 1000, callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/hops/forms'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.hops.forms { callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/yeasts'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.yeasts.get { size: 1000, callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/others'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.others.get { size: 1000, callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/beerstyles'
+				method: 'GET'
+				handler: (req, reply) =>
+					@api.beerStyles.get { size: 1000, callback: req.query.callback }, (err, res, body) ->
+						reply body
+			}
+			{
+				path: '/api/beers/{id}'
+				method: 'GET'
+				handler: (req, reply) =>
+					console.log req.params.id
+					@api.beers.get { id: req.params.id }, (err, res, body) ->
+						reply body
+			}
 		]

 	# Show the recipe app


ui: handle touch events

ui: handle touch events

--- ui/index.coffee
+++ ui/index.coffee
@@ -51,7 +51,7 @@

     this.delegateDOMEvents "focus:active #{focusItems}", (e) ->
       $nodes = getFocusables()
-      $nodes.not(e.target).attr('tabindex', -1)
+      $nodes.not(e.target).attr('tabindex', -1).blur()
       $(e.target).attr('tabindex', 0)

 AppEvents = extend {}, Backbone.Events,
@@ -200,7 +200,7 @@
     item = this.props.item
     mainLink = if this.props.externalLink then item.uri else item.screenURL()
     cls = "ItemView #{this.props.className or ''}"
-    `

+    `

         {this.renderIcon()}
         {item.title &&
{item.title}
}
@@ -457,6 +457,7 @@

`

 window.onload = ->
+  React.initializeTouchEvents(true)
   Wall = window.Wall = App(title: "wall")
   Wall.settings = __data
   React.renderComponent Wall, document.body


switch atom config

switch atom config

--- link/.atom/config.cson
+++ link/.atom/config.cson
@@ -49,9 +49,9 @@
   linter: {}
   "vim-mode":
     useClipboardAsDefaultRegister: true
+".source.ssh-config":
+  editor:
+    preferredLineLength: 120
 ".ini.source":
   editor:
     preferredLineLength: 120
-".source.ssh-config":
-  editor:
-    preferredLineLength: 120


added submenu to context-menu

added submenu to context-menu

--- menus/atom-ternjs.cson
+++ menus/atom-ternjs.cson
@@ -2,8 +2,10 @@
 'context-menu':
   'atom-text-editor': [
     {
-      label: 'Find definition',
-      command: 'tern:definition'
+      label: 'Atom Ternjs'
+      submenu: [
+        {label: 'Find definition', command: 'tern:definition'}
+      ]
     }
   ]
 'menu': [
@@ -13,7 +15,7 @@
       {
         'label': 'Tern'
         'submenu': [
-
+
         ]
       }
     ]


Sinan has fixed an UI issue.

Sinan has fixed an UI issue.

--- client/Framework/components/inputs/KDInputRadioGroup.coffee
+++ client/Framework/components/inputs/KDInputRadioGroup.coffee
@@ -52,7 +52,7 @@
     @$("input[value='#{value}']").attr "checked", "checked"
     @$("input[value='#{value}']").prop "checked", yes
     @$(".kd-radio-holder").removeClass 'active'
-    @$(".kd-radio-holder.#{value}").addClass 'active'  if value?
+    @$(".kd-radio-holder.#{value}").addClass 'active'  if value? and value isnt ""

   getInputElements:->
     @getDomElement().find('input')


activity: unneeded variable declaration

activity: unneeded variable declaration

--- client/Social/Activity/views/comments/newcommentform.coffee
+++ client/Social/Activity/views/comments/newcommentform.coffee
@@ -24,7 +24,7 @@
   viewAppended:->
     {editable} = @getOptions()
     unless editable
-      @addSubView commenterAvatar = new AvatarStaticView
+      @addSubView new AvatarStaticView
         size    :
           width : 42
           height: 42


sidebar/owned: use showError util

sidebar/owned: use showError util

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/sidebar/components/ownedresourceslist.coffee
+++ client/app/lib/sidebar/components/ownedresourceslist.coffee
@@ -9,6 +9,7 @@
 isAdmin = require 'app/util/isAdmin'
 canCreateStacks = require 'app/util/canCreateStacks'
 isDefaultTeamStack = require 'app/util/isdefaultteamstack'
+showError = require 'app/util/showError'

 List = require 'app/components/list'
 Link = require 'app/components/common/link'
@@ -29,9 +30,11 @@
     @headers = []

-  onMenuItemClickError: (name) ->
-
-    new kd.NotificationView { title: "Error occured while #{name} template" }
+  onMenuItemClickError: (action, templateId) ->
+
+    debug "error while #{action}", { templateId }
+
+    showError "Error occured while #{action} the template"

   getSectionCount: -> @props.resources.length


event refac

event refac

--- components/contextmenu/contextmenutreeviewcontroller.coffee
+++ components/contextmenu/contextmenutreeviewcontroller.coffee
@@ -127,7 +127,7 @@
     contextMenu = @getDelegate()
     if nodeData.callback and "function" is typeof nodeData.callback
       nodeData.callback.call contextMenu, nodeView, event
-    contextMenu.propagateEvent KDEventType : "ContextMenuItemReceivedClick", nodeView
+    contextMenu.emit "ContextMenuItemReceivedClick", nodeView
     event.stopPropagation()
     no

@@ -176,6 +176,6 @@

     @getSingleton("windowController").revertKeyView()
     contextMenu = @getDelegate()
-    contextMenu.propagateEvent KDEventType : "ContextMenuItemReceivedClick", nodeView
+    contextMenu.emit "ContextMenuItemReceivedClick", nodeView
     contextMenu.destroy()
     return no


better way to disable this button.

better way to disable this button.

--- public/views/cart_line_item_view.coffee
+++ public/views/cart_line_item_view.coffee
@@ -40,10 +40,6 @@

   removeLineItem: (event) ->
     event.preventDefault()
+    event.target.disabled = true # disable button

-    @_disableRemoveButton()
     App.cart.removeLineItem(@model)
-
-  _disableRemoveButton: ->
-    @$(".epages-cart-overlay-line-item-remove")
-      .prop("disabled", true)


Fix handling of symbol without viewBox and manual width/height

(as in tilt example)

--- src/svgtiler.coffee
+++ src/svgtiler.coffee
@@ -398,9 +398,9 @@
           @viewBox[2] = zeroSizeReplacement
         if @height == 0
           @viewBox[3] = zeroSizeReplacement
-    ## Reset viewBox attribute in case either absent (and computed via svgBBox)
-    ## or changed to avoid zeroes.
-    @xml.documentElement.setAttribute 'viewBox', @viewBox.join ' '
+      ## Reset viewBox attribute in case either absent (and computed via
+      ## svgBBox) or changed to avoid zeroes.
+      @xml.documentElement.setAttribute 'viewBox', @viewBox.join ' '
     @overflowBox = extractOverflowBox @xml
     if Symbol.forceWidth?
       @width = Symbol.forceWidth


export aliases

export aliases

--- src/template.coffee
+++ src/template.coffee
@@ -114,5 +114,6 @@

 Template.schema = schema
 Template.doctype = doctype
+Template.self_closing = self_closing
+Template.aliases = aliases
 module.exports = Template
-Template.self_closing = self_closing


this should work, but doesnt :(

this should work, but doesnt :(

--- src/test/xml.coffee
+++ src/test/xml.coffee
@@ -125,6 +125,7 @@

         xml.tag('test').end()
         xml.$tag('p', "content")
+        xml.tag('p', "end").end()
         do æ.done


References #67, Lichen: add javascript toggle functions

--- app/assets/javascripts/biodiversity_reports.js.coffee
+++ app/assets/javascripts/biodiversity_reports.js.coffee
@@ -25,3 +25,11 @@
     $(@).find(':input:not([type=hidden])').val('')
     $(@).find('#biodiversity_report_fungi_sample_attributes__destroy').val('1')
     $('#lnk-toggle-fungi-fields').text('Add fungi sample')
+
+  $('#lichen_fields').on 'shown.bs.collapse', (event) ->
+    $('#lnk-toggle-lichen-fields').text('Omit lichen sample')
+
+  $('#lichen_fields').on 'hidden.bs.collapse', (event) ->
+    $(@).find(':input:not([type=hidden])').val('')
+    $(@).find('#biodiversity_report_lichen_sample_attributes__destroy').val('1')
+    $('#lnk-toggle-lichen-fields').text('Add lichen sample')


add underscordependecy in swipe component

--- app/assets/javascripts/lib/components/swipe.js.coffee
+++ app/assets/javascripts/lib/components/swipe.js.coffee
@@ -50,7 +50,7 @@

-define ['jquery','handlebars'], ($) ->
+define ['jquery','handlebars','underscore'], ($) ->

   class Swipe


fix url file uploading error handling

fix url file uploading error handling

--- app/assets/javascripts/uploadcare/files/url.js.coffee
+++ app/assets/javascripts/uploadcare/files/url.js.coffee
@@ -116,7 +116,7 @@

       __error: ->
         @stopWatching()
-        @uploader.__state 'error', data
+        @uploader.__state 'error'

       __checkStatus: (callback) ->
         fail = =>


fix bug

fix bug

--- lib/carte/client/views/content.cjsx
+++ lib/carte/client/views/content.cjsx
@@ -25,7 +25,7 @@
       when "list"
         console.log 'list', @props.router.query
         cards = new CardCollection()
-        cards.query = $.extend config.default_query, @props.router.query
+        cards.query = $.extend {}, config.default_query, @props.router.query
         cards.fetching = true
         cards.fetch success: ()-> cards.fetching = false
         title = []


Fix visualization-edit height

Fix visualization-edit height

--- app/frontend/javascripts/visualization-edit.js.coffee
+++ app/frontend/javascripts/visualization-edit.js.coffee
@@ -6,7 +6,7 @@

 class VisualizationEdit extends VisualizationBase

-  mainHeaderHeight:             86
+  mainHeaderHeight:             82
   visualizationHeaderHeight:    null
   tableHeaderHeight:            40

@@ -89,19 +89,20 @@
         $('.visualization-graph-component .visualization-empty-msg').fadeOut()

   resize: =>
-    # setup container height
-    #h = if $('body').hasClass('fullscreen') then @$window.height() else @$window.height() - 178 # -50-64-64
     #console.log 'resize'
     windowHeight = $(window).height()
-    @visualizationHeaderHeight = $('.visualization-graph .visualization-header').outerHeight()
-    graphHeight = windowHeight - @mainHeaderHeight - @visualizationHeaderHeight - @tableHeaderHeight
-    tableHeight = (windowHeight*0.5) + @tableHeaderHeight
-    @$table.css 'top', graphHeight + @visualizationHeaderHeight
-    @$table.height tableHeight
-    @tableNodes.setSize tableHeight, @$table.offset().top
-    @tableRelations.setSize tableHeight, @$table.offset().top
-    @visualizationCanvas.$el.height graphHeight
-    #$('.footer').css 'top', graphHeight + @visualizationHeaderHeight
+    if !$('body').hasClass('fullscreen')
+      @visualizationHeaderHeight = $('.visualization-graph .visualization-header').outerHeight()
+      graphHeight = windowHeight - @mainHeaderHeight - @visualizationHeaderHeight - @tableHeaderHeight
+      tableHeight = (windowHeight*0.5) + @tableHeaderHeight
+      @$table.css 'top', graphHeight + @visualizationHeaderHeight
+      @$table.height tableHeight
+      @tableNodes.setSize tableHeight, @$table.offset().top
+      @tableRelations.setSize tableHeight, @$table.offset().top
+      @visualizationCanvas.$el.height graphHeight
+    else
+      graphHeight = windowHeight
+      @visualizationCanvas.$el.height graphHeight
     super()

   render: ->


Use new status bar tile API

Use new status bar tile API

--- lib/image-editor-status-view.coffee
+++ lib/image-editor-status-view.coffee
@@ -15,7 +15,7 @@
     @disposables.add atom.workspace.onDidChangeActivePaneItem => @updateImageSize()

   attach: ->
-    @statusBar.appendLeft this
+    @statusBar.addLeftTile(item: this)

   attached: ->
     @updateImageSize()


Add plugin origin on created decorations

--- lib/minimap-git-diff-binding.coffee
+++ lib/minimap-git-diff-binding.coffee
@@ -60,7 +60,7 @@
   markRange: (startRow, endRow, scope) ->
     return if @editor.displayBuffer.isDestroyed()
     marker = @editor.markBufferRange([[startRow, 0], [endRow, Infinity]], invalidate: 'never')
-    @minimap.decorateMarker(marker, type: 'line', scope: scope)
+    @minimap.decorateMarker(marker, type: 'line', scope: scope, plugin: 'git-diff')
     @markers ?= []
     @markers.push(marker)


Require Trackers to opt-in to `_`-prefixed Metrics.

This allows for Trackers to avoid high frequency internal Metrics without needing to throttle, filter, or one-by-one subscribe.

--- client/Metric.coffee
+++ client/Metric.coffee
@@ -44,7 +44,8 @@
             event_data._ms_since_last = null

         @_fire(@_name, event_data)
-        @_fire('*', event_data) unless @_name is '*'
+        # Trackers must opt-in specifically to Metrics prefixed with `_`.
+        @_fire('*', event_data) unless @_name is '*' or @_name[0] is '_'

     _fire: (metric_name, event_data) ->
         _subscriptions[metric_name]?.forEach (tracker) =>


added tooltip "Lines on scroll bar show match locations"

--- client/editor.coffee
+++ client/editor.coffee
@@ -212,7 +212,9 @@
         cm.setCursor line: l + 1, ch: 0

   lastQuery = lastIC = overlay = annotation = null
-  clearSearch = -> cm.removeOverlay overlay; overlay = null; annotation?.clear(); annotation = null; return
+  clearSearch = ->
+    $('.CodeMirror-vscrollbar', $e).prop 'title', ''
+    cm.removeOverlay overlay; overlay = null; annotation?.clear(); annotation = null; return
   highlightSearch = ->
     ic = !$('.tb-case:visible', $tb).hasClass 'pressed' # ic: ignore case (like in vim)
     q = $('.tb-search:visible', $tb).val(); if ic then q = q.toLowerCase() # q: the query string
@@ -226,6 +228,7 @@
           if !i then         stream.pos += q.length; 'searching'
           else if i > 0 then stream.pos += i;        return
           else               stream.skipToEnd();     return
+        $('.CodeMirror-vscrollbar', $e).prop 'title', 'Lines on scroll bar show match locations'
     [q, ic]
   search = (backwards) ->
     [q, ic] = highlightSearch()


We disabled all dragging in import code. Viewport should be an exception.

--- client/import.coffee
+++ client/import.coffee
@@ -149,7 +149,11 @@

 Meteor.startup ->
   $(document).on 'dragstart', (e) ->
-    e.preventDefault()
+    # We want to prevent dragging of everything except the viewport
+    return if $(e.target).is('.viewport')
+
+    e.preventDefault()
+
     return # Make sure CoffeeScript does not return anything

   $(document).on 'dragenter', (e) ->


Parse accents in BibTeX import

Parse accents in BibTeX import

--- client/import.coffee
+++ client/import.coffee
@@ -210,8 +210,18 @@

 importOSQA.readAs = 'ArrayBuffer'

+accentMap =
+  "'": '\u0301'
+  '"': '\u0308'
+  '`': '\u0300'
+latexAccents = (x) ->
+  x.replace /\\(['"`])([^{]|{[^{}]})/g, (match, accent, char) ->
+    char = char[1] if char.length == 3
+    (char + accentMap[accent]).normalize()
+
 lastName = (x) ->
-  x = x.replace /[{}]/g, ''
+  x = latexAccents x
+  .replace /[{}]/g, ''
   if ' ' in x
     x[x.lastIndexOf(' ')+1..]
   else
@@ -286,7 +296,7 @@
           cite = cite.trim()
           bib = bibs[cite]
           if bib?
-            "\\pdftooltip{\\href{#{bib.url}}{#{bib.abbrev}}}{``#{cleanBibTeX bib.title}'' by #{cleanBibTeX bib.author.join '; '}}"
+            "\\pdftooltip{\\href{#{bib.url}}{#{bib.abbrev}}}{\u201c#{cleanBibTeX bib.title}\u201d by #{cleanBibTeX latexAccents bib.author.join '; '}}"
           else
             console.warn "Missing bib url for '#{cite}'" unless bib?
             cite


Deleted empty line in import.coffee.

Deleted empty line in import.coffee.

--- client/import.coffee
+++ client/import.coffee
@@ -153,7 +153,6 @@
       errored: true
     ,
       canceled: true
-
     ]
   ).count()


Cleanup.

Cleanup.

--- client/import.coffee
+++ client/import.coffee
@@ -15,7 +15,6 @@
     collection: null

 UPLOAD_CHUNK_SIZE = 128 * 1024 # bytes
-
 DRAGGING_OVER_DOM = false

 verifyFile = (file, fileContent, publicationId, samples) ->
@@ -334,6 +333,10 @@
 Template.importOverlay.destroyed = ->
   $(document).off '.importOverlay'

+Template.signInOverlay.rendered = Template.importOverlay.rendered
+
+Template.signInOverlay.destroyed = Template.importOverlay.destroyed
+
 Template.importOverlay.importOverlayActive = ->
   Session.get 'importOverlayActive'


Clarify wording in comment about routing deprecated event URLs

--- client/router.coffee
+++ client/router.coffee
@@ -15,7 +15,7 @@
   '/login': 'login'
   '/event/:title_id': (title_id) ->
     event_id = Events.findOne(title_id: encodeURIComponent(title_id))
-    # Backwards compatible: if title_id != exist, assume the url is an event_id
+    # Backwards compatible: if title_id does not exist, assume the url is an event_id
     if not event_id
       event_id = title_id
     Session.set("event_id", event_id)


Upgrade to iron-router 1.0+

Upgrade to iron-router 1.0+

--- client/router.coffee
+++ client/router.coffee
@@ -15,6 +15,7 @@
     path: '/'
     onBeforeAction: ->
       Session.set('params', parse_params @params)
+      this.next()
   @route 'new_event', path: '/new'
   @route 'login'
   @route 'event_info',
@@ -26,19 +27,23 @@
       if not event_id
         event_id = title_id
       Session.set("event_id", event_id)
+      this.next()
   @route 'show_user',
     path: '/user/:user_id'
     onBeforeAction: ->
       Session.set('user_id', @params.user_id)
+      this.next()
   @route 'edit_user', path: '/settings'
   @route 'search',
     onBeforeAction: ->
       Session.set('params', parse_params @params)
+      this.next()

 requireLogin = ->
   if not Meteor.user()
     @render 'login'
-    pause()
+  else
+    this.next()

 Router.onBeforeAction requireLogin, only: [
   'new_event',


convert some Session variables to ReactiveVar

--- client/search.coffee
+++ client/search.coffee
@@ -1,19 +1,19 @@
-Session.setDefault "comments_search_text", null
-Session.setDefault "flycircuit_search_text", null
-Session.setDefault "active_tags", {}
+comments_search_text = new ReactiveVar(null)
+flycircuit_search_text = new ReactiveVar(null)
+active_tags = new ReactiveVar({})

 update_url_bar = ->
   q = {}

-  cst = Session.get("comments_search_text")
+  cst = comments_search_text.get()
   if cst?
     q['text'] = cst

-  fst = Session.get("flycircuit_search_text")
+  fst = flycircuit_search_text.get()
   if fst?
     q['idid'] = fst

-  taglist = Object.keys(Session.get("active_tags"))
+  taglist = Object.keys(active_tags.get())
   if taglist.length
     q['tags'] = JSON.stringify(taglist)

@@ -21,24 +21,24 @@

 Template.Search.events window.okCancelEvents("#comments-search-input",
   ok: (value,evt) ->
-    Session.set "comments_search_text", value
+    comments_search_text.set(value)
     update_url_bar()
     return

   cancel: (evt) ->
-    Session.set "comments_search_text", null
+    comments_search_text.set(null)
     update_url_bar()
     return
 )

 Template.Search.events window.okCancelEvents("#flycircuit-search-input",
   ok: (value,evt) ->
-    Session.set "flycircuit_search_text", value
+    flycircuit_search_text.set(value)
     update_url_bar()
     return

   cancel: (evt) ->
-    Session.set "flycircuit_search_text", null
+    flycircuit_search_text.set(null)
     update_url_bar()
     return
 )
@@ -46,14 +46,14 @@
 Template.Search.events
   "click .tag-name": (event, template) ->
     event.preventDefault()
-    obj = Session.get("active_tags")
+    obj = active_tags.get()
     myname = this.name
     if myname of obj
       delete obj[myname]
     else
       # add item
       obj[myname] = true
-    Session.set("active_tags",obj)
+    active_tags.set(obj)
     update_url_bar()

 del = (obj, key) ->
@@ -110,17 +110,17 @@
   if !@find("#comments-search-input")?
     return
   if @data.text?
-    Session.set("comments_search_text",@data.text)
+    comments_search_text.set(@data.text)
   if @data.idid?
-    Session.set("flycircuit_search_text",@data.idid)
+    flycircuit_search_text.set(@data.idid)
   if @data.tags?
     atl = JSON.parse(@data.tags)
     taglist = {}
     for tag in atl
       taglist[tag] = true
-    Session.set("active_tags",taglist)
-  this.find("#comments-search-input").value = Session.get("comments_search_text")
-  this.find("#flycircuit-search-input").value = Session.get("flycircuit_search_text")
+    active_tags.set(taglist)
+  this.find("#comments-search-input").value = comments_search_text.get()
+  this.find("#flycircuit-search-input").value = flycircuit_search_text.get()

 Template.Search.helpers
   current_search: ->
@@ -151,10 +151,10 @@
       result[tag]=true for tag in doc.tags
     Object.keys(result)
     lods = [] # list of dicts
-    active_tags = Session.get("active_tags")
+    my_active_tags = active_tags.get()
     for tag_name in Object.keys(result)
       tmp = {name:tag_name}
-      if tag_name of active_tags
+      if tag_name of my_active_tags
         tmp.is_active_css_class='active'
       else
         tmp.is_active_css_class=''


Kill the window pinning

Kill the window pinning

--- coffee/select.coffee
+++ coffee/select.coffee
@@ -258,7 +258,6 @@
       classPrefix: 'select'
       constraints: [
         to: 'window'
-        pin: true
         attachment: 'together'
       ]


Add updated main server file

Add updated main server file

--- coffee/server.coffee
+++ coffee/server.coffee
@@ -0,0 +1,21 @@
+"use strict"
+
+pg = require 'pg'
+helpers = require './query-helpers'
+config = require './config'
+ObjectID = require('mongodb').ObjectID
+
+pg.connect config.conString, (err, client) ->
+  return console.err err if err
+
+  #client.on 'notification', (msg) ->
+  #console.log(msg)
+
+  #query = client.query("LISTEN watchers")
+
+
+  helpers.makeOidForTables client, config.from, config.oid, (err, data) ->
+    console.log err, data
+    client.end ->
+      console.log 'client.end'
+


fix IE9 compatibility

fix IE9 compatibility

--- src/javascripts/embed.coffee
+++ src/javascripts/embed.coffee
@@ -3,7 +3,7 @@
 class Iframe
   constructor: (@elem)->
     @id = Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1)
-    config = @elem.dataset.configuration
+    config = @elem.getAttribute('data-configuration')
     @configuration = if typeof config == 'string'
       window[config] || {json_config: config}
     else


Toggling new / old simulation updates enables / disables the run button.

--- src/code/stores/simulation-store.coffee
+++ src/code/stores/simulation-store.coffee
@@ -63,12 +63,7 @@

   onGraphChanged: (data)->
     @nodes = data.nodes
-
-    simulator = new Simulation
-      nodes: @nodes
-      newIntegration: @settings.newIntegration
-
-    @graphIsValid = simulator.graphIsValid()
+    @_updateGraphValid()
     @notifyChange()

   onSetDuration: (n) ->
@@ -96,8 +91,9 @@

   onSetNewIntegration: (newInt) ->
     @settings.newIntegration = newInt
+    @_updateGraphValid()
     @notifyChange()
-
+
   onRunSimulation: ->
     if @settings.modelIsRunnable and @settings.modelReadyToRun
       @currentSimulation = new Simulation
@@ -137,6 +133,12 @@
       @currentSimulation.stop()
     @settings.modelReadyToRun = true
     @notifyChange()
+
+  _updateGraphValid: ->
+    simulator = new Simulation
+      nodes: @nodes
+      newIntegration: @settings.newIntegration
+    @graphIsValid = simulator.graphIsValid()

   _checkModelIsRunnable: ->
     @settings.modelIsRunnable = @graphIsValid and @settings.duration > 0


use different html completion

use different html completion

--- home/.atom/packages.cson
+++ home/.atom/packages.cson
@@ -4,7 +4,7 @@
   'aligner-scss',
   'atom-beautify'
   'atom-handlebars'
-  'autocomplete-html-entities'
+  'autoclose-html'
   'autocomplete-ruby'
   'blame'
   'change-case'


account: redirect to pricing section of subscription when plan change is requested

--- client/Account/views/subscriptions.coffee
+++ client/Account/views/subscriptions.coffee
@@ -23,15 +23,12 @@
           @confirm 'resume', subscription

         .on 'PlanChangeRequested', ->
-          payment = KD.getSingleton 'paymentController'
-
-          workflow = payment.createUpgradeWorkflow tag: 'vm'
-
-          modal = new KDModalView
-            view    : workflow
-            overlay : yes
-
-          workflow.on 'Finished', modal.bound 'destroy'
+          route =
+            if "vm" in subscription.tags
+            then "/Pricing/Developer"
+            else if "custom-plan" in subscription.tags
+            then "/Pricing/Team"
+          KD.singleton("router").handleRoute route

   getConfirmationText = (action, subscription) -> switch action
     when 'cancel'


trigger change event when a link is modified or inserted

--- plugins/linkimg.coffee
+++ plugins/linkimg.coffee
@@ -39,8 +39,10 @@
                     else
                         widget.lastSelection.startContainer.parentNode.href = link
                 widget.options.editable.removeAllSelections()
+                widget.options.editable.element.trigger('change')
                 dialog.dialog('close')
                 return false
+
             dialog.find("form").submit dialogSubmitCb

             buttonset = jQuery ""


full success and failure messages to all drawer tests

--- testApp/app/controllers/drawers.coffee
+++ testApp/app/controllers/drawers.coffee
@@ -66,7 +66,7 @@
       id: "center2"

   @testAddRightButton: ->
-    rightDrawerBtn = new steroids.buttons.NavigationBarButton
+    rightDrawerBtn = new steroids.buttons.NavigationBarButtons
     rightDrawerBtn.title = "==>"
     rightDrawerBtn.onTap = =>
       steroids.drawers.show
@@ -76,32 +76,46 @@
     steroids.navigationBar.update
       buttons:
         right: [rightDrawerBtn]
+    ,
+      onSuccess: ->
+        steroids.logger.log "SUCCESS in adding button to navigation bar that opens right drawer"
+      onFailure: ->
+        steroids.logger.log "FAILURE in testAddRightButton"

   @testShow: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in showing default drawer"
+    failure = ->
+      steroids.logger.log "FAILURE in testShow"

     steroids.drawers.show {}, {
       onSuccess: success
+      onFailure: failure
     }

   @testShowLeft: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in showing left drawer"
+    failure = ->
+      steroids.logger.log "FAILURE in testShowLeft"

     steroids.drawers.show
       edge: steroids.screen.edges.LEFT
     ,
       onSuccess: success
+      onFailure: failure

   @testShowRight: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in showing right drawer"
+    failure = ->
+      steroids.logger.log "FAILURE in testShowRight"

     steroids.drawers.show
       edge: steroids.screen.edges.RIGHT
     ,
       onSuccess: success
+      onFailure: failure

@@ -123,9 +137,9 @@

   @testShowNonPreloadedDrawerWithIdRight: ->
     success = ->
-      alert "TEST FAIL managed to set non-preloaded view as drawer"
-    failure = ->
-      alert "TEST SUCCESS because failed to set non-preloaded view as drawer"
+      steroids.logger.log "TEST SUCCESS because failed to set non-preloaded view as drawer"
+    failure = ->
+      steroids.logger.log "TEST FAIL managed to set non-preloaded view as drawer"

     newDrawer = new steroids.views.WebView
       location: "views/drawers/extraDrawer.html"
@@ -134,16 +148,17 @@
     steroids.drawers.update
       right: newDrawer
     ,
-      onSuccess: success
-      onFailure: failure
+      # test should not pass
+      onSuccess: failure
+      onFailure: success

   @testShowPreloadedDrawerRight: ->
     success = ->
-      alert "SUCCESS, check right drawer"
-    failure = ->
-      alert "FAILED"
+      steroids.logger.log "SUCCESS in updating preloaded view as right drawer"
+    failure = ->
+      steroids.logger.log "FAILED in testShowPreloadedDrawerRight"

     newDrawer = new steroids.views.WebView("views/drawers/extraDrawer.html")

@@ -273,27 +288,33 @@

   @testLegacyDisableGesture: ->
     success = ->
-      steroids.logger.log "SUCCESS"
-    failure = ->
-      steroids.logger.log "FAILURE"
+      steroids.logger.log "SUCCESS in disabling gestures the legacy way"
+    failure = ->
+      steroids.logger.log "FAILURE in testLegacyDisableGesture"

     steroids.drawers.disableGesture
       onSuccess: success
+      onFailure: failure

   @testUpdateWithWidthOfLayerInPixels: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in updating the width of the center layer"
+    failure = ->
+      steroids.logger.log "FAILURE in testUpdateWithWidthOfLayerInPixels"

     steroids.drawers.update {
       options:
         widthOfLayerInPixels: 200
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testDiffSizes: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in testing different sizes for drawers"
+    failure = ->
+      steroids.logger.log "FAILURE in testDiffSizes"

     steroids.drawers.update {
       options:
@@ -305,11 +326,14 @@
         widthOfDrawerInPixels: 75
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testDefaultSizes: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in returning to original sizes for drawers and center layer"
+    failure = ->
+      steroids.logger.log "FAILURE in testDefaultSizes"

     steroids.drawers.update {
       options:
@@ -321,33 +345,42 @@
         widthOfDrawerInPixels: 280
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testHideShadow: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in hiding drawer shadow"
+    failure = ->
+      steroids.logger.log "FAILURE in testHideShadow"

     steroids.drawers.update {
       options:
         showShadow: false
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testShowShadow: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in showing drawer shadow"
+    failure = ->
+      steroids.logger.log "FAILURE in testShowShadow"

     steroids.drawers.update {
       options:
         showShadow: true
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testStretchDrawer: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in enabling stretch effect for drawer"
+    failure = ->
+      steroids.logger.log "FAILURE in testStretchDrawer"

     steroids.drawers.update {
       options: {
@@ -360,11 +393,14 @@
         widthOfDrawerInPixels: 150
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testUpdateWithParallax: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in changing drawer animation to parallax"
+    failure = ->
+      steroids.logger.log "FAILURE in testUpdateWithParallax"

     steroids.drawers.update
       options:
@@ -374,10 +410,13 @@
           parallaxFactor: 2.1
     ,
       onSuccess: success
+      onFailure: failure

   @testUpdateWithSlideAndScale: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in changing drawer animation to SlideAndScale"
+    failure = ->
+      steroids.logger.log "FAILURE in testUpdateWithSlideAndScale"

     steroids.drawers.update {
       options:
@@ -386,11 +425,14 @@
           duration: 0.5
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testUpdateWithSwingingDoor: ->
     success = ->
-      steroids.logger.log "SUCCESS"
+      steroids.logger.log "SUCCESS in changing drawer animation to SwingingDoor"
+    failure = ->
+      steroids.logger.log "FAILURE in testUpdateWithSwingingDoor"

     steroids.drawers.update {
       options:
@@ -399,6 +441,7 @@
           duration: 0.5
     }, {
       onSuccess: success
+      onFailure: failure
     }

   @testReplaceLayers: ->
@@ -408,9 +451,9 @@
       }
       {
         onSuccess: ->
-          steroids.logger.log "Replaced!"
+          steroids.logger.log "SUCCESS in replacing center layer to center 2"
         onFailure: ->
-          steroids.logger.log "Could not replace."
+          steroids.logger.log "FAILURE in testReplaceLayers - could not replace center layer"
       }
     )

@@ -422,9 +465,9 @@
       }
       {
         onSuccess: ->
-          steroids.logger.log "Modal shown!"
+          steroids.logger.log "SUCCESS in showing modal"
         onFailure: ->
-          steroids.logger.log "Could not show modal!"
+          steroids.logger.log "FAILURE in testShowModal - could not show modal"
       }
     )

@@ -434,50 +477,51 @@
     eventHandler = steroids.drawers.on 'willshow', (event) ->
       alert "willshow event -> eventName: #{event.name} drawer.edge: #{event.drawer.edge}"

-    alert "event listener added"
+    steroids.logger.log "Event listener drawer WILL SHOW added"

   @testDidShowChangeEvent: ->
     eventHandler = steroids.drawers.on 'didshow', (event) ->
       alert "didshow event -> eventName: #{event.name} drawer.edge: #{event.drawer.edge}"

-    alert "event listener added"
+    steroids.logger.log "Event listener drawer DID SHOW added"

   @testWillCloseChangeEvent: ->
     eventHandler = steroids.drawers.on 'willclose', (event) ->
       alert "willclose event -> eventName: #{event.name} drawer.edge: #{event.drawer.edge}"

-    alert "event listener added"
+    steroids.logger.log "Event listener drawer WILL CLOSE added"

   @testDidCloseChangeEvent: ->
     eventHandler = steroids.drawers.on 'didclose', (event) ->
       alert "didclose event -> eventName: #{event.name} drawer.edge: #{event.drawer.edge}"

-    alert "event listener added"
+    steroids.logger.log "Event listener drawer DID CLOSE added"

   @testRemoveShowEvents: ->
     steroids.drawers.off 'willshow'
     steroids.drawers.off 'didshow'

-    alert "show events handlers removed"
+    steroids.logger.log "Drawer show event handlers removed"

   @testRemoveCloseEvents: ->
     steroids.drawers.off 'willclose'
     steroids.drawers.off 'didclose'

-    alert "close events handlers removed"
+    steroids.logger.log "Drawer close event handlers removed"

   @testTryToReusePreloadedAsModal: ->
     success = ->
-      alert "should not have sucessed :-("
-
-    fail = ->
-      alert "could not open the modal !"
+      steroids.logger.log "TEST SUCCESS was not able to open modal"
+
+    failure = ->
+      steroids.logger.log "TEST FAILURE in testTryToReusePreloadedAsModal - should not have been able to"

     steroids.modal.show
       view: DrawersController.leftDrawer
     ,
-      onSuccess: success
-      onFailure: fail
+      # modal.show should fail
+      onSuccess: failure
+      onFailure: success

   @testPushLayer: ->
     popView = new steroids.views.WebView("/views/layers/pop.html")
@@ -487,8 +531,8 @@
         view: popView
       }
       {
-        onSuccess: -> alert "Successfully pushed!"
-        onFailure: -> alert "Could not push."
+        onSuccess: -> alert "SUCCESS pushed layer"
+        onFailure: -> alert "FAILURE in testPushLayer"
       }
     )