Traverse shadow DOM to find links

Traverse shadow DOM to find links

--- content_scripts/link_hints.coffee
+++ content_scripts/link_hints.coffee
@@ -745,7 +745,14 @@
   getLocalHints: (requireHref) ->
     # We need documentElement to be ready in order to find links.
     return [] unless document.documentElement
-    elements = document.documentElement.getElementsByTagName "*"
+    # Find all elements, recursing into shadow DOM if present.
+    getAllElements = (root, elements = []) ->
+      for element in root.querySelectorAll "*"
+        elements.push element
+        if element.shadowRoot
+          getAllElements(element.shadowRoot, elements)
+      elements
+    elements = getAllElements document.documentElement
     visibleElements = []

     # The order of elements here is important; they should appear in the order they are in the DOM, so that


Update ru/about.cson (#245)

Update ru/about.cson (#245)

--- def/ru/about.cson
+++ def/ru/about.cson
@@ -1,8 +1,8 @@
 About:
-  tabTitle: "О Atom"
+  tabTitle: "Об Atom"
   releaseNotes: "Список изменений"
   newUpdate: "Новое обновление"
-  upToDate: "Atom обновлен!"
+  upToDate: "Atom обновлён!"
   checkingForUpdates: "Проверка обновлений..."
   checkUpdatesNow: "Проверить сейчас"
   automaticDownloadUpdates: "Автоматически загружать обновления"


Support both array of flows and flows as separate args for session.stream().

--- src/flowdock.coffee
+++ src/flowdock.coffee
@@ -43,7 +43,8 @@
         callback(flows)
     request.end()

-  stream: (flows) ->
+  stream: (flows...) ->
+    flows = flows[0] if flows[0] instanceof Array && flows.length == 1
     return new Stream(@auth, flows)

 class Stream extends process.EventEmitter


Write error to console when PDF does not exist

This is one of the things pointed out in #28: when the PDF file doesn't exist, we still call the viewer resulting in a (possible bizarre) error message. This pre-empts this by only calling the viewer if the PDF file exists.

--- lib/viewer.coffee
+++ lib/viewer.coffee
@@ -1,4 +1,4 @@
-{LTool,get_tex_root} = require './ltutils'
+{LTool, get_tex_root, is_file} = require './ltutils'
 {exec, execFile} = require 'child_process'
 path = require 'path'

@@ -108,5 +108,19 @@
     tex_exts = atom.config.get("latextools.texFileExtensions")
     if parsed_master.ext in tex_exts && parsed_current.ext in tex_exts
       master_path_no_ext = path.join(parsed_master.dir, parsed_master.name)
+
+      @ltConsole.show()  # ensure console is visible
       @ltConsole.addContent("Jump to #{row},#{col}")
-      @_jumpToPdf(current_file,master_path_no_ext + ".pdf",row,col)
+
+      pdf_file = master_path_no_ext + '.pdf'
+      if not is_file(pdf_file)
+        log_file = master_path_no_ext + '.log'
+        @ltConsole.addContent(
+          "Could not find PDF file #{pdf_file}. If no errors appeared from " +
+          "the build, please check your log file, #{log_file}",
+          file: log_file,
+          level: 'error'
+        )
+        return
+
+      @_jumpToPdf(current_file, pdf_file, row, col)


Disable webFrameZoom for other windows

Disable webFrameZoom for other windows

--- lib/window.coffee
+++ lib/window.coffee
@@ -28,6 +28,11 @@
         current.restore()
       else
         show.bind(current)()
+    # Disable OSX zoom
+    current.webContents.on 'dom-ready', ->
+      current.webContents.executeJavaScript '''
+        require('web-frame').setZoomLevelLimits(1, 1);
+      '''
     # Close window really
     if options.realClose
       current.on 'closed', (e) ->


Remove workaround for coffee-script bug fixed in 1.2.0

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -14,8 +14,7 @@
   #
   #     prefix:arg1:arg2:...
   constructor: (@key, @queue, @sticky) ->
-    [@prefix, args...] = @key.split consts.arg_sep
-    @args = args # weird bug in coffeescript: wanted @args... in line above
+    [@prefix, @args...] = @key.split consts.arg_sep
     @db = @queue.worker_db
     @req_channel = _('requests').namespace @queue.options.db_index
     @resp_channel = _('responses').namespace @queue.options.db_index


Fix bug; respect null values inside @each

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -468,7 +468,7 @@
     @require @needed, (err, values) =>
       return @resume err if err
       for key, index in @needed
-        if value = values[index]
+        if (value = values[index]) != undefined
           @pending.push [key, value]
         else
           @missing.push key


Call @got callback

Call @got callback

--- lib/worker.coffee
+++ lib/worker.coffee
@@ -153,6 +153,7 @@
   notify_dep: (key) ->
     msg = ['!dep', @key, key].join consts.key_sep
     @_pubsub.publish @req_channel, msg
+    @got(key) if @got # can be mixed in

   # Search for the given keys in the database, then remember them.
   keys: (str) ->


Fix playback for mobile safari

Fix playback for mobile safari

--- resources/assets/coffee/react/_components/tracklist.coffee
+++ resources/assets/coffee/react/_components/tracklist.coffee
@@ -87,7 +87,7 @@
       td className: 'tracklisting__cover', style: { backgroundImage: "url('#{@props.track.cover_url}')" },
         a className: 'tracklisting__preview', href: '#', onClick: @playPreview,
           i className: "fa fa-fw #{if @props.playing then 'fa-pause' else 'fa-play'}"
-          audio id: "track-#{@props.track.id}-audio", src: (if @props.playing then @props.track.preview else ''), preload: 'none', onEnded: @playDone
+        audio id: "track-#{@props.track.id}-audio", src: (if @props.playing then @props.track.preview else ''), preload: 'none', onEnded: @playDone
       td className:'tracklisting__title',
         "#{@props.track.title} "
         span className: 'tracklisting__version', @props.track.version
@@ -125,6 +125,7 @@

   playTrack: (id) ->
     ele = $("#track-#{id}-audio")[0]
+    ele.load()
     ele.currentTime = 0
     ele.play()


Activity: show more member link click handler disabled

--- client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
+++ client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
@@ -26,6 +26,9 @@
     @isLoading            = no

     @group.on "moreLinkClicked", =>
+
+      return warn "not yet implemented, @facet fix pls!!!"
+
       return if @isLoading or @getData().count <= @lastFetchedItemCount and @showMore
       @isLoading = yes
       @groupLoader = new KDLoaderView


Static MainPage : Remove hack comment

Static MainPage : Remove hack comment

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -168,7 +168,6 @@
     @on "FeaturedActivityCommentRequested", @bound "featuredActivityCommentRequested"
     @on "FeaturedActivityRequested", @bound "featuredActivityRequested"

-    # this is a hack, find a better way to hide inner navigation
     @getView().innerNav.hide()
     eventName = "activity_fetch"
     @once "#{eventName}_failed", ()->


[client][apps][activity] removed fetching from cache and isExempt method

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -195,13 +195,13 @@
     currentGroup     = groupsController.getCurrentGroup()

     fetch = (slug)=>
-      if slug isnt 'koding' and not KD.config.useNeo4j
-        @fetchActivitiesDirectly options, callback
-      else
-        @isExempt (exempt)=>
-          if exempt or @getFilter() isnt activityTypes
-          then @fetchActivitiesDirectly options, callback
-          else @fetchActivitiesFromCache options, callback
+
+      # since it is not working, disabled it,
+      # to-do add isExempt control.
+      # @isExempt (exempt)=>
+      #   if exempt or @getFilter() isnt activityTypes
+
+      @fetchActivitiesDirectly options, callback

     unless isReady
     then groupsController.once 'groupChanged', fetch


crude fix for intermittent jasmine test failures

--- common/static/coffee/spec/discussion/view/response_comment_view_spec.coffee
+++ common/static/coffee/spec/discussion/view/response_comment_view_spec.coffee
@@ -141,8 +141,6 @@
                 spyOn(@view, 'cancelEdit')
                 spyOn($, "ajax").andCallFake(
                     (params) =>
-                        expect(params.url._parts.path).toEqual("/courses/edX/999/test/discussion/comments/01234567/update")
-                        expect(params.data.body).toEqual(@updatedBody)
                         if @ajaxSucceed
                             params.success()
                         else
@@ -154,6 +152,8 @@
                 @ajaxSucceed = true
                 @view.update(makeEventSpy())
                 expect($.ajax).toHaveBeenCalled()
+                expect($.ajax.mostRecentCall.args[0].url._parts.path).toEqual('/courses/edX/999/test/discussion/comments/01234567/update')
+                expect($.ajax.mostRecentCall.args[0].data.body).toEqual(@updatedBody)
                 expect(@view.model.get("body")).toEqual(@updatedBody)
                 expect(@view.cancelEdit).toHaveBeenCalled()

@@ -162,6 +162,8 @@
                 @ajaxSucceed = false
                 @view.update(makeEventSpy())
                 expect($.ajax).toHaveBeenCalled()
+                expect($.ajax.mostRecentCall.args[0].url._parts.path).toEqual('/courses/edX/999/test/discussion/comments/01234567/update')
+                expect($.ajax.mostRecentCall.args[0].data.body).toEqual(@updatedBody)
                 expect(@view.model.get("body")).toEqual(originalBody)
                 expect(@view.cancelEdit).not.toHaveBeenCalled()
                 expect(@view.$(".edit-comment-form-errors *").length).toEqual(1)


Oops, syntax error

Oops, syntax error

--- src/iptables.coffee
+++ src/iptables.coffee
@@ -8,7 +8,7 @@
 	exec("iptables -t #{rule.table} -A #{rule.rule}", cb)

 iptables.delete = (rule, cb) ->
-	exec ("iptables -t #{rule.table} -D #{rule.rule}", cb)
+	exec("iptables -t #{rule.table} -D #{rule.rule}", cb)

 iptables.appendMany = (rules, cb) ->
 	async.eachSeries rules, iptables.append, cb


More readable error message

More readable error message

--- test/assertions/contain_consecutive_elements_assertion.coffee
+++ test/assertions/contain_consecutive_elements_assertion.coffee
@@ -15,8 +15,8 @@
     array = @_obj
     @assert ContainConsecutiveElementsAssertion.has_consecutive_values(array,
                                                                        consecutive_values),
-            "expected #{array} to contain #{consecutive_values} consecutively",
-            "expected #{array} to not contain #{consecutive_values} consecutively"
+            "expected #{array.join ' '} to contain #{consecutive_values} consecutively",
+            "expected #{array.join ' '} to not contain #{consecutive_values} consecutively"

   # Returns whether the given array contains the given values consecutively.


Blender Internal: Fix WebGL2 support.

Blender Internal: Fix WebGL2 support.

--- engine/material_shaders/blender_internal.coffee
+++ engine/material_shaders/blender_internal.coffee
@@ -62,7 +62,7 @@

 class BlenderInternalMaterial
     constructor: (@material) ->
-        {data, _input_list, inputs, _texture_list, context} = @material
+        {data, _input_list, inputs, _texture_list, @context} = @material
         for u in data.uniforms
             switch u.type
                 when -1
@@ -71,7 +71,7 @@
                     _input_list.push inputs[path] = {value, type: u.count, path}
                 when 13, GPU_DYNAMIC_SAMPLER_2DIMAGE, GPU_DYNAMIC_SAMPLER_2DBUFFER, \
                     14, GPU_DYNAMIC_SAMPLER_2DSHADOW
-                        _texture_list.push {value: context.render_manager.blank_texture}
+                        _texture_list.push {value: @context.render_manager.blank_texture}
         return

     assign_textures: ->
@@ -100,9 +100,13 @@
         return "projection_matrix"

     get_code: ->
-        fragment = @material.data.fragment
-        fragment = @material.context.SHADER_LIB + fragment
-        return {fragment}
+        glsl_version = 100
+        fragment = @material.context.SHADER_LIB + @material.data.fragment
+        if @context.is_webgl2
+            {glsl100to300} = require '../material'
+            fragment = glsl100to300 fragment
+            glsl_version = 300
+        return {fragment, glsl_version}

     get_uniform_assign: (gl, program) ->
         # TODO: reassign lamps when cloning etc


Added custom layout template support

Added custom layout template support

--- router.coffee
+++ router.coffee
@@ -11,6 +11,12 @@
         'blogNotFound'
     Iron.Router.hooks.dataNotFound.call @
   , only: ['blogShow']
+
+  Router.onBeforeAction ->
+    if Blog.settings.layoutTemplate
+      @layout Blog.settings.layoutTemplate
+    @next()
+  , only: ['blogIndex', 'blogShow', 'blogTagged', 'blogAdmin', 'blogAdminEdit']

 # RSS


Extend routes to catch show action

Extend routes to catch show action

--- routes.coffee
+++ routes.coffee
@@ -1,7 +1,10 @@
 module.exports = (app) ->

   app.get '/', (req, res) ->
-    res.render 'index', title: 'Express'
+    res.render 'index'

   app.get '/sensors', (req, res) ->
-    res.render 'index', title: 'Express'
+    res.render 'index'
+
+  app.get '/sensors/:id', (req, res) ->
+    res.render 'index'


skip character if matrix not found and few more special chars added

--- isoblocks.coffee
+++ isoblocks.coffee
@@ -134,8 +134,12 @@
 		else if ch is '-' then ch = 'dash'
 		else if ch is ';' then ch = 'semicolon'
 		else if ch is ':' then ch = 'colon'
+		else if ch is '/' then ch = 'f_slash'
+		else if ch is '[' then ch = 'l_square_bracket'
+		else if ch is ']' then ch = 'r_square_bracket'

-		current_ch = Characters[ch]
+		# Skip if character matrix not found
+		if not current_ch = Characters[ch] then return 0
 		for arr, i in current_ch
 			for val, j in arr
 				if val


Don't store incompatible packages in custom object

The info is on the Package object itself to the loaded/activated
packages can just be iterated over.

--- src/package-manager.coffee
+++ src/package-manager.coffee
@@ -36,7 +36,6 @@

     @loadedPackages = {}
     @activePackages = {}
-    @incompatiblePackages = {}
     @packageStates = {}

     @packageActivators = []
@@ -160,7 +159,6 @@
           pack = new Package(packagePath, metadata)
         pack.load()
         @loadedPackages[pack.name] = pack
-        @incompatiblePackages[pack.name] = pack unless pack.isCompatible()
         pack
       catch error
         console.warn "Failed to load package.json '#{path.basename(packagePath)}'", error.stack ? error


Fixing indentation.

Fixing indentation.

--- src/polvo/core/file.coffee
+++ src/polvo/core/file.coffee
@@ -107,20 +107,20 @@

     require_reg = /([^\s]+)?(?:\s*=\s*)?(?:require\s*\()(?:'|")(.+)(?:'|")/g

-    while matched = require_reg.exec js_code
+    while (matched = require_reg.exec js_code)

-        # computes dep name and path
-        dep =
-          name: matched[1]
-          id: matched[2]
-          vendor: matched[1] is undefined or (matched[2] of @config.vendors.javascript)
-          incompatible: matched[2] in @config.vendors.javascript.incompatible
+      # computes dep name and path
+      dep =
+        name: matched[1]
+        id: matched[2]
+        vendor: matched[1] is undefined or (matched[2] of @config.vendors.javascript)
+        incompatible: matched[2] in @config.vendors.javascript.incompatible

-        # and add it to the dependencies array
-        if dep.is_vendor is true or dep.name is undefined
-          @dependencies.push dep
-        else
-          @dependencies.splice @dependencies_diff_head++, 0, dep
+      # and add it to the dependencies array
+      if dep.is_vendor is true or dep.name is undefined
+        @dependencies.push dep
+      else
+        @dependencies.splice @dependencies_diff_head++, 0, dep

   inject_dependencies:( js_code, exclude_anonymous_requires = false )->


Hide corners on horizontal

Hide corners on horizontal

--- js/script.coffee
+++ js/script.coffee
@@ -1,5 +1,11 @@
 ---
 ---
+
+if window.innerWidth > window.innerHeight
+	divs = document.querySelectorAll('#top, #bottom')
+	for e in divs
+		e.style.display = 'none'
+
 screens = {{ site.data.screens | jsonify }}
 timedRefresh = (timeoutPeriod) ->
 	id = Math.floor Math.random() * screens.length


Add "plus" in countdown

Add "plus" in countdown

--- js/script.coffee
+++ js/script.coffee
@@ -98,7 +98,7 @@
 		body += "#{p.launch_site.site_name.replace /(?=\D)\s(?=\d)/g, '-'}##{p.flight_number}	"
 		# :T_MESSAGE
 		# :LAUNCH_TIME
-		body += "T#{t_message}#{t_units.toUpperCase()}	"
+		body += "T#{if t_message > 0 then "+#{t_message}" else t_message}#{t_units.toUpperCase()}	"
 		body += "#{launch_time}#{tz}#{launch_time_local}#{offset}	"
 		if p.reuse.core then core_reflown.push p.core_serial
 		if p.reuse.capsule then cap_reflown.push p.cap_serial


Update interval timewriter

Update interval timewriter

--- js/script.coffee
+++ js/script.coffee
@@ -11,7 +11,7 @@
           clearInterval(timer)
           $el.text(str)
         progress++
-      , 130
+      , 100
     this

   textfill: (maxFontSize, maxWords) ->


Move more things into transformer.

Move more things into transformer.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -101,6 +101,10 @@
     @removeBreaksFromConsequents node
   CallExpression: (node) ->
     @parenthesizeCallee node
+  MemberExpression: (node) ->
+    @parenthesizeObjectIfFunction node
+  Identifier: (node) ->
+    @escapeUndefined node

   pushStack: (node) ->
     @scopes.push node
@@ -123,6 +127,16 @@
       description: description
     , @options.source, @options.filename)
     throw err
+
+  ###
+  # Turn 'undefined' into '`undefined`'
+  ###
+
+  escapeUndefined: (node) ->
+    if node.name is 'undefined'
+      @replace node, type: 'CoffeeEscapedExpression', value: 'undefined'
+    else
+      node

   ###
   # Consolidates empty cases into the next case.
@@ -145,6 +159,16 @@
     node

   ###
+  # Parenthesize function expressions if they're in the left-hand side of a
+  # member expression (eg, `(-> x).toString()`).
+  ###
+
+  parenthesizeObjectIfFunction: (node) ->
+    if node.object.type is 'FunctionExpression'
+      node.object._parenthesized = true
+    node
+
+  ###
   # Removes `break` statements from consequents in a switch case.
   # (eg, `case x: a(); break;` gets break; removed)
   ###
@@ -306,10 +330,7 @@
     space [ @walk(node.left), node.operator, @walk(node.right) ]

   Identifier: (node) ->
-    if node.name is 'undefined'
-      [ '`undefined`' ]
-    else
-      [ node.name ]
+    [ node.name ]

   UnaryExpression: (node) ->
     if node.operator is 'void'
@@ -333,8 +354,6 @@
     [ node.raw ]

   MemberExpression: (node) ->
-    @parenthesizeObjectIfFunction(node)
-
     isThis = (node.object.type is 'ThisExpression')

     left = if isThis
@@ -704,15 +723,6 @@
           arg._parenthesized = true

   ###
-  # Parenthesize function expressions if they're in the left-hand side of a
-  # member expression (eg, `(-> x).toString()`).
-  ###
-
-  parenthesizeObjectIfFunction: (node) ->
-    if node.object.type is 'FunctionExpression'
-      node.object._parenthesized = true
-
-  ###
   # Injects a ForStatement's update (eg, `i++`) into the body.
   ###


Show an explanation error message on too many track changes error

--- services/web/public/coffee/ide/editor/EditorManager.coffee
+++ services/web/public/coffee/ide/editor/EditorManager.coffee
@@ -121,10 +121,21 @@

 		_bindToDocumentEvents: (doc, sharejs_doc) ->
 			sharejs_doc.on "error", (error, meta) =>
-				if error?.message?.match "maxDocLength"
+				if error?.message?
+					message = error.message
+				else if typeof error == "string"
+					message = error
+				else
+					message = ""
+				if message.match "maxDocLength"
 					@ide.showGenericMessageModal(
 						"Document Too Long"
 						"Sorry, this file is too long to be edited manually. Please upload it directly."
+					)
+				else if message.match "too many comments or tracked changes"
+					@ide.showGenericMessageModal(
+						"Too many comments or tracked changes"
+						"Sorry, this file has too many comments or tracked changes. Please try accepting or rejecting some existing changes, or resolving and deleting some comments."
 					)
 				else
 					@ide.socket.disconnect()


Improve error handling and freeze several objects

@throw is implemented, so -throw- is only used for actual errors in code (such as invalid arguments), and @throw is used for problems in what is being parsed. Only parsingErrors from @throw are caught by @optional.
The prototype of parsing instances (with @require, @optional, @throw, etc.), makeParser, and each individual parser, are now all Object.freeze()ed.

--- makeParser.coffee
+++ makeParser.coffee
@@ -1,30 +1,16 @@
 # (c) Wilson Woodcock Berkow. MIT License.

-# (This is parser0.5.coffee in my local folder, with some comment alterations)
-
-# ############################################################################ #
-#  TODO: I really have to separate parsing-errors thrown intentionally in a    #
-#   parser from code errors, so that if there is a type- or reference-error,   #
-#   it does not appear to be a parsing error and it does not take the notFound #
-#   branch. This tripped me up when I wrote "char()" instead of "@char()".     #
-# ADDITIONALLY: Should makeParser record a stack so that when this error       #
-#  function is called, you can expand the error and observe which parsers      #
-#  were called, without having to look at the actual stack and just observing  #
-#  a bunch of calls to anonymous function or to `parser` or `requireFor` and   #
-#  shit?                                                                       #
-# ############################################################################ #
-
-# Note: Consider extending @require and @optional to operate not just on a string at @str, but also potentially on an ast at @ast
+# parser0.6.coffee

 "use strict"
-util = Object.freeze # This is frozen because it is added publicly to makeParser.
+util = Object.freeze # This is frozen as it is publicly added to makeParser.
     "y": (le) -> # Y-Combinator
         ((f) ->
             f(f)
         ) (f) ->
             le (args...) ->
                 f(f).call(this, args...)
-    "loop": (f) -> util.y(f)() # For functional/expressional loops. e.g. util.loop (repeat) => => ...code...
+    "loop": (f) -> util.y(f)() # For functional/expressional loops. Interface: util.loop (repeat) => => ...
     "makeObject": (proto, ownProps) ->
         result = Object.create proto
         for prop, val of ownProps
@@ -42,15 +28,20 @@
 last = (xs) -> xs[xs.length - 1]
 empty = (l) -> l.length is 0
 concat = (xs, ys) -> xs.concat(ys)
-makeParsingInstance = do ->
+
+parsingError = (msg) -> {"name": "Parsing Error", "msg": msg}
+isParsingError = (err) -> err?.name is "Parsing Error"
+makeParsingInstance = do -> # The parsing instance contains methods for parsing, and the information for parsing (e.g. source being parsed and index/position in it).
     isParser = (x) -> util.typeOf(x) is "function" and x.isParser
-    makeSureParser = (x) -> if isParser(x) then x else makeParser(x)
-    parserUser = (which) -> # This is the abstraction for @optional and @require
+    makeSureParser = (x) -> if isParser(x) then x else makeParser(x) # This must be used here to prevent infinite recursion between @require and makeParser, but does not need to be used anywhere else.
+    parserUsage = (which) -> # This is the abstraction for @optional and @require
         (a, b, c) ->
             # Handle all arrangements of arguments:
-            #  (The arrangements are (lookFor, found, notFound); (lookFor, {found, notFound, dontAdvance, args}); ({lookFor, found, notFound, dontAdvance, args}))
-            if util.typeOf(a) is "null" or util.typeOf(a) is "undefined" or util.typeOf(a) is "number"
-                throw new Error "Invalid argument to @" + which + "."
+            #  (The arrangements are (lookFor, found, notFound)
+            #                        (lookFor, {found, notFound, dontAdvance, args})
+            #                        ({lookFor, found, notFound, dontAdvance, args}))
+            if util.typeOf(a) in ["number", "null", "undefined"]
+                @throw "Invalid argument to @#{which}."
             if util.typeOf(a) is "object"
                 prsr = makeSureParser a.lookFor
                 other = a
@@ -64,10 +55,10 @@
                     notFound: c
                     args: []
                 }
-            if which is "optional" and other?
+            if which is "optional"
                 other.notFound ?= -> # This makes nothing happen if the thing isnt found.
             prsr.requireFor(@, other)
-    proto =
+    proto = Object.freeze
         # FIRST ARE THE UTIL-LIKE FUNCTIONS:
         "loop": util.loop
         "beginsWith": (s) -> # TODO: test @test so that it can be used instead.
@@ -81,10 +72,10 @@
                         repeat(i + 1)
         "char": (x = 0) -> @str[@index + x] # Gets the current char, or one before/after it depending on -x-.
         "soFar": -> @str[@startIndex...@index]
-        "until": (x) -> if util.typeOf(x) is "string" then @str[@index...@str.indexOf(x, @index)] else @str[@index...x]
+        "until": (x) -> if util.typeOf(x) is "string" then @str[@index...@str.indexOf(x, @index)] else @str[@index...(@index + x)]
         # NOW THE PARSING FUNCTIONS:
         "test": (prsr) ->
-            !!(try prsr(@str, startAt: @index))
+            !!(prsr.opt()(@str, startAt: @index))
         "advance": (x) ->
             @index += switch util.typeOf x
                 when "string" then x.length
@@ -94,30 +85,33 @@
             while not @beginsWith(s)
                 @advance()
             @advance(s)
+        "throw": (msg) ->
+            throw parsingError msg
         "reqBool": (bool, err) ->
             if !bool
-                throw (err ? new Error "Test to @reqBool failed.")
+                throw parsingError(err ? "Test to @reqBool failed.")
         "caseParse": (o, onNoMatch) -> # Each key of -o- is a string to optionally parse.
             @loop (repeat) => (keys = Object.keys o) =>
-                if empty(keys) and onNoMatch then onNoMatch()
-                else if empty(keys) then throw new Error("Expected one of the following: " + JSON.stringify(Object.keys(o), null, 4))
+                if empty(keys)
+                    if onNoMatch then onNoMatch()
+                    else @throw("Expected one of the following: " + JSON.stringify(Object.keys(o), null, 4))
                 else
                     fn = o[head(keys)]
                     @optional head(keys),
                         found:    => if fn.isParser then @require fn else fn() # TODO: Should it always do @require(fn), never just fn()?
                         notFound: => repeat tail keys
         "white": (req) ->
-            if req is "required"
-                @require getWhite
-            else
-                @optional getWhite
-        "optional": parserUser("optional")
-        "require": parserUser("require")
+            if req is "req"
+                @require makeParser.getWhite
+            else
+                @optional makeParser.getWhite
+        "optional": parserUsage("optional")
+        "require": parserUsage("require")
     (str, i = 0) ->
         util.makeObject(proto, {
-            index: i
-            str: str
-            startIndex: i
+            "index": i
+            "str": str
+            "startIndex": i
         })
 parserListToString = (arr) -> # For stringifying an array of parser options as to display the name of each parser.
     util.loop (repeat) => (options = arr, str = "[") =>
@@ -162,12 +156,12 @@
                 @advance x.length # (Can't do @require(x) here because x is a string and this is the def of @require(x) for strings).
                 @soFar()
             else
-                throw new Error("""Expected "#{x}" at index #{@index} and instead found #{@char()} in string:\n#{JSON.stringify(@str)}""")
+                @throw """Expected "#{x}" at index #{@index} and instead found #{@char()} in string:\n#{JSON.stringify(@str)}"""
     else if util.typeOf(x) is "array" # Each element of the array is an OPTION, and this requires one to match. RETURNS whatever the matched option returns.
         makeParser (name ? "array-form-parser: #{parserListToString x}"), (args) ->
             @loop (repeat) => (i = 0) =>
                 if not x[i]?
-                    throw new Error("(From #{name}) Expected one of the following: #{parserListToString(x)} in string:\n#{@str}\nat index #{@index}")
+                    @throw "(From #{name}) Expected one of the following: #{parserListToString(x)} in string:\n#{@str}\nat index #{@index}"
                 @optional x[i],
                     args: args
                     notFound: ->
@@ -176,15 +170,15 @@
         makeParser (name ? "regexp-form-parser: #{x}"), ->
             val = x.exec @str[@index...]
             if val is null
-                throw new Error("Expected the regexp pattern " + x.toString() + " in string ``#{@str}'' at index #{@index}")
+                @throw "Expected the regexp pattern " + x.toString() + " in string ``#{@str}'' at index #{@index}"
             else
                 @require val[0]
-    else if util.typeOf(x) is "function" # This is what every other overload is defined in terms of.
+    else if util.typeOf(x) is "function" # This is the primary form, which every other overload is defined in terms of.
         if x.isParser
-            makeParser name, ->
+            makeParser (name ? "copy-of-parser: #{name}"), ->
                 @require x
-        else # (If x is not already a parser, use it to construct one.)
-            ### The parser (`parser`), can have the following arrangments of arguments:
+        else # This is the usual case. -x- is a function intended to be made into a parser.
+            ### The parser (in the variable -parser-), can have the following arrangments of arguments:
                     1. "string"
                     2. ({string, startAt, args, found, notFound})
                     3. (string, {startAt, args, found, notFound})
@@ -219,22 +213,25 @@
                 parsingInstance = makeParsingInstance(str, startAt)
                 result = parser.requireFor(parsingInstance, other)
                 if parsingInstance.index isnt parsingInstance.str.length
-                    throw new Error("Expected end of string index #{parsingInstance.index}.")
+                    throw parsingError("Expected end of string index #{parsingInstance.index}.")
                 else
                     result
             parser.requireFor = (origInstance, other = {}) ->
                 if util.typeOf(other.notFound) is "function"
-                    # This branch is effectively the @optional capability.
+                    # This branch is effectively the @optional function.
                     instance = makeParsingInstance(origInstance.str, origInstance.index)
                     try
                         val = x.call(instance, other.args)
                     catch e
-                        err = e
+                        if isParsingError e
+                            err = e
+                        else
+                            throw e
                     if err
                         other.notFound(err)
                     else
-                        if !other.dontAdvance
-                            origInstance.index = instance.index # This is what synchronizes the referenced instance with the one it's used in, so that @require()ing another function also advances the current instance.
+                        if not other.dontAdvance
+                            origInstance.index = instance.index # This is what synchronizes the referenced instance with the one it's used in, so that @require()ing another function also advances @index in the current instance.
                         if other.found
                             other.found(val)
                         else
@@ -242,7 +239,7 @@
                 else
                     instance = makeParsingInstance(origInstance.str, origInstance.index)
                     val = x.call(instance, other.args)
-                    if !other.dontAdvance
+                    if not other.dontAdvance
                         origInstance.index = instance.index
                     if other.found
                         other.found(val)
@@ -254,16 +251,16 @@
             parser.return = (fn) ->
                 makeParser parser.parserName, (args) ->
                     @require parser, args: args, found: fn
-            parser.then = (x) -> # Note: This returns the result of the LAST parser. Thus, it is best for places where the result doesn't matter, like in the "sep" option of makeParser.many
+            parser.then = (x) -> # Note: This returns the result of the LAST parser (e.g. in getFoo.then(getBar).then(getBaz)), while makeParser.seq returns a list of the results.
                 makeParser ->
                     @require parser
                     @require x
-            parser.opt = (x) ->
-                makeParser ->
+            parser.opt = ->
+                makeParser (parser.parserName + "--opt"), ->
                     @optional parser
             parser.isParser = true
             parser.parserName = name
-            parser
+            Object.freeze parser
     else
         throw new Error("The -makeParser- function requires argument(s).")
 makeParser.util = util
@@ -277,10 +274,10 @@
             else
                 findings.src = @soFar()
                 findings
-makeParser.many = (x, other) -> # TODO: test other.amt, other.start and other.end. TODO: CHANGE THE ARGS PROPERTY: make it take an ARRAY of args, not an object of args.
+makeParser.many = (x, other) -> # TODO: test other.amt. TODO: CHANGE THE ARGS PROPERTY: make it take an ARRAY of args, not an object of args.
     if not x?
         throw new Error "Invalid first argument to makeParser.many: #{x}"
-    if util.typeOf(other.amt) is "number"
+    if util.typeOf(other.amt) is "number" # TODO: Consider putting this case into another function, like -makeParser.several-, so it is very clear which is being used.
         parseInner = makeParser (args = {}) ->
             args.amtLeft ?= other?.amt
             if args.amtLeft <= 0
@@ -297,9 +294,8 @@
                         rest = @require parseInner,
                             args:
                                 amtLeft: args.amtLeft - 1
-                cons first, rest
+                    cons first, rest
         makeParser ->
-            findings = []
             if other?.start
                 @require other.start
             findings = @require parseInner
@@ -307,7 +303,7 @@
                 @require other.end
             findings
     else
-        parseInner = makeParser -> # This parses it WITHOUT the start-sequence and end-sequence so that it can have a simple recursive structure.
+        parseInner = makeParser -> # This parses it without the start- and end-sequence so that it can have a simple recursive structure.
             first = @require x
             rest = []
             @optional ->
@@ -322,7 +318,6 @@
             if other?.end
                 @require other.end
             findings ? [] # When elements are optional and not there, findings will be `undefined` as it is returned from the @optional(parserInner) invoc above.
-makeParser.rep = (x, n) -> makeParser.many(x, amt: n)[0] # This is intended for when x is a string and each finding in `findings` in the "many" method will the the same. This saves you the trouble of picking one out of the array.
 makeParser.cases = (a, b) ->
     if util.typeOf(a) is "string"
         name = a
@@ -332,21 +327,20 @@
     makeParser name, ->
         @caseParse(o)
 makeParser.getWhite = makeParser "getWhite", /^[\s]+/
-makeParser.jsonParsers = do -> # TODO: REALLY TEST ALL OF THESE INDIVIDUALLY AND CAREFULLY
+makeParser.jsonParsers = do -> # TODO: REALLY TEST ALL OF THESE INDIVIDUALLY AND CAREFULLY (I just (6/20/14) fixed a stupid, glaring would-be reference error). Also, CHANGE THE NAME. Its too long. make it makeParser.json
     getString = makeParser "getString", (args) ->
         @require '"'
         @loop (repeat) => =>
             if @char() is undefined
-                throw new Error("Unterminated JSON string literal -- the `string` function")
+                @throw "Unterminated JSON string literal"
             if @char() is '"' and (@char(-1) isnt "\\" or @char(-2) is "\\") # This means that the string has been terminated.
                 @advance()
                 JSON.parse(@soFar())
             else
                 @advance()
                 repeat()
-    getDigits = makeParser "getDigits", -> @require(/^[\d]+/)[0] # This returns the string of digits.
+    getDigits = makeParser "getDigits", /^[\d]+/ # This returns the string of digits.
     getNumber = makeParser "getNumber", ->
-        data = {"type": "number"}
         @optional ["+", "-"]
         @require getDigits
         @optional ->
@@ -361,19 +355,17 @@
         @caseParse
             "true": -> true
             "false": -> false
-    getNull = makeParser "getNull", ->
-        @require "null"
-        null
+    getNull = makeParser("getNull", "null").return(-> null)
     getArray = do ->
         arrayBody = makeParser ->
             # This parses everything betweten the [ and ] in an array, and requires there to be at least one item.
-            fst = @require json
+            fst = @require getJson
             @white()
             @optional ",",
                 found: => # Handles the rest of the elements.
                     @white()
                     rest = @require arrayBody
-                    [fst].concat(rest)
+                    cons fst, rest
                 notFound: => [fst] # This means -fst- is the only element.
         makeParser "getArray", ->
             @require "["
@@ -387,18 +379,18 @@
             prop = @require string
             @require ":"
             @white()
-            val = @require json # TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! MAKE THE JSON FUNCTIONS HAVE AN OPTION (in -args-) for if it should return the value or an object describing it.
+            val = @require getJson
             {"prop": prop, "val": val, "src": @soFar()}
         getObjectBody = makeParser ->
             @optional getObjectPair,
                 found: (fst) =>
-                    rest = [] # This is the default if there is nothing else
+                    rest = [] # This is the default if there is nothing else in the object
                     @optional ->
                         @white()
                         @require ","
                         @white()
                         rest = @require getObjectBody
-                    [fst].concat(rest)
+                    cons fst, rest
                 notFound: => [] # No pairs in the list corresponds to an empty object.
         makeParser "getObject", ->
             # Validate structure and get data:
@@ -415,7 +407,7 @@
                     repeat(i + 1)
             obj
     getJson = makeParser "getJson", [getNumber, getNull, getBoolean, getArray, getString, getObject]
-    {
+    Object.freeze {
         "getJson":    getJson
         "getString":  getString
         "getNumber":  getNumber
@@ -424,6 +416,7 @@
         "getArray":   getArray
         "getObject":  getObject
     }
+Object.freeze makeParser

 ### TODO: Things to THINK ABOUT for the future:
  -Allowing some easier way of creating parsers like ones from makeParser
@@ -433,4 +426,11 @@
     "atStart": Just run the parser (equivalent to what a parser is now)
     "wholeStr": Runs the parser and makes sure that the content occupies the ENTIRE string (and there is no shit at the end of the string).
    or at least having the latter feature.
+ -Make the "args" property of the options object in @require and @optional calls be an array of args
+    rather than an object to be passed as the first arg.
+ -ADDITIONALLY: Should makeParser record a stack so that when this error
+    function is called, you can expand the error and observe which parsers
+    were called, without having to look at the actual stack and just observing
+    a bunch of calls to anonymous function or to `parser` or `requireFor` and
+    shit?
 ###


remove unrelated file option check

remove unrelated file option check

--- client/ace/lib/aceview.coffee
+++ client/ace/lib/aceview.coffee
@@ -120,8 +120,6 @@

     @ace.on 'ace.requests.save', (contents) =>
       file = @getData()
-
-      return  if file.dismissSaveDialogModal

       if /localfile:/.test file.path
         @openSaveDialog()


Admin: add Logs tab for admins

Admin: add Logs tab for admins

--- client/admin/lib/index.coffee
+++ client/admin/lib/index.coffee
@@ -1,6 +1,7 @@
 kd                         = require 'kd'
 AppController              = require 'app/appcontroller'

+LogsView                   = require './views/logs'
 AdminAPIView               = require './views/api/adminapiview'
 AdminAppView               = require './views/customviews/adminappview'
 TeamInviteView             = require './views/koding-admin/teaminviteview'
@@ -47,6 +48,7 @@
         }
         { slug : 'Stacks',         title : 'Compute Stacks',    viewClass : GroupStackSettings,       role: 'member' }
         { slug : 'APIAccess',      title : 'API Access',        viewClass : AdminAPIView             }
+        { slug : 'Logs',           title : 'Team Logs',         viewClass : LogsView                 }
       ]
     koding     :
       title    : 'Koding Administration'


Include 01.02. as an active registration date

--- assets/javascripts/app.coffee
+++ assets/javascripts/app.coffee
@@ -69,7 +69,7 @@
 # Render countdown ring
 start = new Date("November 29, 2013 14:40:00")
 now = new Date().getTime()
-end = new Date("February 01, 2014 00:00:00")
+end = new Date("February 02, 2014 00:00:00")
 ratio = (now - start) / (end - start)

 days = [100 * ratio, 100 - 100 * ratio]


create_record(): Prefer schema to schema name.

--- client/views-list-edit.coffee
+++ client/views-list-edit.coffee
@@ -12,10 +12,10 @@

 create_record = (parent, schema, schema_name) ->
   doc = xml_document parent
-  parent.prepend if schema_name?
+  parent.prepend if schema?
+                   generate_xml_from_field schema, doc
+                 else
                    generate_xml_from_schema_name schema_name, doc
-                 else
-                   (generate_xml_from_field schema, doc).children 0

 add_button = () ->
   do (parent, schema) ->


Styles: add Autoprefixer and CSSO.

Styles: add Autoprefixer and CSSO.

--- src/styles/index.coffee
+++ src/styles/index.coffee
@@ -18,26 +18,37 @@
 	gf = new Gruntfile()
 	return  if gf.hasSection 'stylus'

+	buildCss = if @isWordpressTheme() then 'style.css' else "#{@htdocs_prefix}build/styles.css"
+
+	# Stylus
 	config =
+		options:
+			'include css': true
+			'urlfunc': 'embedurl'
+			'define':
+				DEBUG: gf.JS 'debug'
+			'paths': ['tamia']
+			'use': [gf.JS "() -> (require 'autoprefixer-stylus')('last 2 versions', 'ie 8')"]
 		compile:
-			options:
-				'include css': true
-				'define':
-					DEBUG: gf.JS 'debug'
-				'paths': ['tamia']
 			files: {}
-	config.compile.files[if @isWordpressTheme() then 'style.css' else "#{@htdocs_prefix}build/styles.css"] = 'styles/index.styl'
+	config.compile.files[buildCss] = 'styles/index.styl'
 	gf.addSection 'stylus', config
+
+	# CSSO
+	# TODO: Temporary solution because of
+	config =
+		files: {}
+	config.files[buildCss] = buildCss
+	gf.addSection 'csso', config

 	gf.addWatcher 'stylus',
 		files: 'styles/**'
 		tasks: 'stylus'

-	gf.addTask 'default', 'stylus'
-	gf.addTask 'deploy', 'stylus'
+	gf.addTask 'default', ['stylus', 'csso']
+	gf.addTask 'deploy', ['stylus', 'csso']

 	gf.save()

 Generator::dependencies = ->
-	@installFromNpm ['grunt', 'matchdep', 'grunt-contrib-stylus', 'grunt-contrib-watch']
-	# @hookFor 'tamia:framework', @args
+	@installFromNpm ['grunt', 'matchdep', 'grunt-contrib-stylus', 'grunt-contrib-watch', 'grunt-csso', 'autoprefixer-stylus']


SocialWorker: move permissions to socialapi/message

--- workers/social/lib/social/models/socialapi/message.coffee
+++ workers/social/lib/social/models/socialapi/message.coffee
@@ -11,6 +11,42 @@
   @share()

   @set
+    permissions :
+      # JPost related permissions
+      'read posts'              : ['member', 'moderator']
+      'create posts'            : ['member', 'moderator']
+      'edit posts'              : ['moderator']
+      'delete posts'            : ['moderator']
+      'edit own posts'          : ['member', 'moderator']
+      'delete own posts'        : ['member', 'moderator']
+      'reply to posts'          : ['member', 'moderator']
+      'like posts'              : ['member', 'moderator']
+      'pin posts'               : ['member', 'moderator']
+      'send private message'    : ['member', 'moderator']
+      'list private messages'   : ['member', 'moderator']
+
+      # JComment related permissions
+      'edit comments'           : ['moderator']
+      'edit own comments'       : ['moderator']
+
+      # JTag related permissions
+      'read tags'               :
+        public                  : ['guest', 'member', 'moderator']
+        private                 : ['member', 'moderator']
+      'create tags'             : ['member', 'moderator']
+      'freetag content'         : ['member', 'moderator']
+      'browse content by tag'   : ['member', 'moderator']
+      'edit tags'               : ['moderator']
+      'delete tags'             : ['moderator']
+      'edit own tags'           : ['moderator']
+      'delete own tags'         : ['moderator']
+      'assign system tag'       : ['moderator']
+      'fetch system tag'        : ['moderator']
+      'create system tag'       : ['moderator']
+      'remove system tag'       : ['moderator']
+      'create synonym tags'     : ['moderator']
+      # 'delete system tag'     : ['moderator']
+
     # move permission from jpost to here
     # permissions :
     #   'send private message' : ['member', 'moderator']


payment: remove unused signature

payment: remove unused signature

--- workers/social/lib/social/models/socialapi/payment.coffee
+++ workers/social/lib/social/models/socialapi/payment.coffee
@@ -11,8 +11,6 @@
     sharedMethods     :
       static          :
         subscribe         :
-          (signature Object, Function)
-        unsubscribe       :
           (signature Object, Function)
         subscriptions     :
           (signature Object, Function)


redirect after remove template

redirect after remove template

--- BackofficeBundle/Resources/public/coffee/treeAjaxDelete.coffee
+++ BackofficeBundle/Resources/public/coffee/treeAjaxDelete.coffee
@@ -18,8 +18,9 @@
           if redirectUrl != undefined
             displayMenu(redirectUrl)
           else
-            Backbone.history.history.back()
-            displayMenu(Backbone.history.fragment)
+            redirectUrl = appRouter.generateUrl 'showHome'
+            Backbone.history.navigate(redirectUrl, {trigger:true})
+            displayMenu(redirectUrl)
           return
         error: (response) ->
           $('.modal-footer', this.el).html response.responseJSON.error.message


Be case insensitive in arrivals help command

--- scripts/arrivals.coffee
+++ scripts/arrivals.coffee
@@ -65,7 +65,7 @@

 module.exports = (robot) ->
   # arrival help text
-  robot.respond /add/, (res) ->
+  robot.respond /add/i, (res) ->
     res.reply "To add a greyhound, post a picture to #arrivals with the greyhound's name in the title and a comment in the format below:\n" +
       "\tsex = female|male, dob = 2017-01-21, color = white and black, cats = yes|no\n" +
       "Notice the equals sign between each attribute and its value, and the commas separating each pair of attribute and value.\n" +


update german C-ROC

update german C-ROC

--- coffeescripts/cards-de.coffee
+++ coffeescripts/cards-de.coffee
@@ -86,6 +86,7 @@
         "Quadjumper Expansion Pack": "Quadjumper Erweiterung"
         "Sabine's TIE Fighter Expansion Pack": "Sabines TIE-Jäger Erweiterung"
         "Upsilon-class Shuttle Expansion Pack": "Shuttle der Ypsilon-Klasse Erweiterung"
+        "C-ROC Cruiser Expansion Pack": "C-ROC Kreuzer Erweiterung"
     ui:
         shipSelectorPlaceholder: "Wähle ein Schiff"
         pilotSelectorPlaceholder: "Wähle einen Piloten"
@@ -222,7 +223,8 @@
     exportObj.renameShip 'TIE Striker', 'TIE-Stürmer'
     # Move Upsilon-class Shuttle to Shuttle der Ypsilon-Klasse
     exportObj.renameShip 'Upsilon-class Shuttle', 'Shuttle der Ypsilon-Klasse'
-
+    # Move C-ROC Cruiser to C-ROC Kreuzer
+    exportObj.renameShip 'C-ROC Cruiser', 'C-ROC Kreuzer'
     pilot_translations =
         "Wedge Antilles":
             text: """Wenn du angreifst, sinkt der Wendigkeitswert des Verteidigers um 1 (Minimum 0)."""
@@ -905,6 +907,10 @@
         '''Black Squadron Scout''':
             ship: "TIE-Stürmer"
             name: "Scout der Schwarzen Staffel"
+        # C-ROC
+        'C-ROC Cruiser':
+            ship: "C-ROC Kreuzer"
+            name: "C-ROC Kreuzer"
         'Genesis Red':
             ship: "M3-A Abfangjäger"
             text: '''After you acquire a target lock, assign focus and evade tokens to your ship until you have the same number of each token as the locked ship.'''
@@ -1575,17 +1581,21 @@
             text: '''Sobald ein Schiff, das du berührst, aktiviert wird, darfst du dir sein gewähltes Manöver ansehen. Falls du das tust, muss sein Besitzer das Rad auf ein nebenstehendes Manöver drehen. Das Schiff kann dieses Manöver aufdecken und ausführen, selbst wenn es gestresst ist.'''
         # C-ROC
         'Heavy Laser Turret':
-            text: '''C-ROC Cruiser only.%LINEBREAK%Attack (energy): Spend 2 energy from this card to perform this attack against 1 ship (even a ship outside of your firing arc).'''
+            name: "Schweres Lasergeschütz"
+            text: '''Nur für C-ROC Kreuzer.%LINEBREAK%Angriff (Energie): Gib 2 Energie von dieser Karte aus, um diesen Angriff auf 1 Schiff durchzuführen (auch außerhalb deines Feuerwinkels).'''
         'Cikatro Vizago':
-            text: '''%SCUMONLY%%LINEBREAK%At the start of the End phase, you may discard this card to replace a faceup %ILLICIT% or %CARGO% Upgrade card you have equipped with another Upgrade card of the same type of equal or fewer squad points.'''
+            text: '''%SCUMONLY%%LINEBREAK%Zu Beginn der Endphase darfst du diese Karte ablegen, um eine von dir ausgerüstete offene %ILLICIT%- oder %CARGO%-Aufwertung durch eine andere Aufwertung desselben Typs zu ersetzen. Die osten der neuen Aufwertung müssen gleich oder niedriger sein.'''
         'Azmorigan':
-            text: '''%HUGESHIPONLY% %SCUMONLY%%LINEBREAK%At the start of the End phase, you may spend 1 energy to replace a faceup %CREW% or %TEAM% Upgrade card you have equipped with another Upgrade card of the same type of equal or fewer squad points.'''
+            text: '''%HUGESHIPONLY% %SCUMONLY%%LINEBREAK%Zu Beginn der Endphase darfst du 1 Energie ausgeben, um eine von dir ausgerüstete offene %CREW%- oder %TEAM%-Aufwertung durch eine andere Aufwertung desselben Typs zu ersetzen. Die Kosten der neuen Aufwertung müssen gleich oder niedriger sein.'''
         'Quick-release Cargo Locks':
-            text: '''%LINEBREAK%At the end of the Activation phase, you may discard this card to place 1 container token.'''
+            name: "Frachtcontainer mit Schnellverschluss"
+            text: '''%LINEBREAK%Am Ende der Aktivierungsphase darfst du diese Karte ablegen, um 1 Containermarker zu platzieren.'''
         'Supercharged Power Cells':
-            text: '''When attacking, you may discard this card to roll 2 additional attack dice.'''
+            name: "Hochaufgeladene Energiezellen"
+            text: '''Sobald du angreifst, darfst du diese Karte ablegen, um 2 zusätzliche Angriffswürfel zu werfen.'''
         'ARC Caster':
-            text: '''Rebel and Scum only.%DUALCARD%%LINEBREAK%Side A:%LINEBREAK%Attack: Attack 1 ship.  If this attack hits, you must choose 1 other ship at Range 1 of the defender to suffer 1 damage.%LINEBREAK%Then flip this card.%LINEBREAK%Side B:%LINEBREAK%(Recharging) At the start of the Combat phase, you may receive a weapons disabled token to flip this card.'''
+            name: "ARC-Werfer"
+            text: '''Nur für Rebellen und Abschaum & Kriminelle.%DUALCARD%%LINEBREAK%Seite A:%LINEBREAK%Angriff: Greife 1 Schiff an. Falls der Angriff trifft, musst du 1 anderes Schiff in Reichweite 1 des Verteidigers wählen, das 1 Schaden nimmt.%LINEBREAK%Dann wird diese Karte umgedreht.%LINEBREAK%Seite B:%LINEBREAK%(wird aufgeladen) Zu Beginn der Kampfphase darfst du einen Waffen-deaktiviert-Marker erhalten, um diese Karte umzudrehen.'''
         'Wookiee Commandos':
             text: '''When attacking, you may reroll your %FOCUS% results.'''
         'Synced Turret':
@@ -1595,9 +1605,11 @@
         'Intensity':
             text: '''%SMALLSHIPONLY% %DUALCARD%%LINEBREAK%Side A: After you perform a boost or barrel roll action, you may assign 1 focus or evade token to your ship. If you do, flip this card.%LINEBREAK%Side B: (Exhausted) At the end of the Combat phase, you may spend 1 focus or evade token to flip this card.'''
         'Jabba the Hutt':
-            text: '''%SCUMONLY%%LINEBREAK%When you equip this card, place 1 illicit token on each %ILLICIT% Upgrade card in your squad.  When you are instructed to discard an Upgrade card, you may discard 1 illicit token on that card instead.'''
+            name: "Jabba der Hutt"
+            text: '''%SCUMONLY%%LINEBREAK%Sobald du diese Karten ausrüstest, lege 1 Schmugglermarker auf jede %ILLICIT%-Aufwertung deiner Staffel. Sobald du angewiesen wirst eine Aufwertung abzulegen, darfst du stattdessen 1 Schmugglermarker von ihr ablegen.'''
         'IG-RM Thug Droids':
-            text: '''When attacking, you may change 1 of your %HIT% results to a %CRIT% result.'''
+            name: "IG-RM-Schlägerdroiden"
+            text: '''Sobald du angreifst, darfst du 1 deiner %HIT% in ein %CRIT% ändern.'''
         'Selflessness':
             text: '''%SMALLSHIPONLY% %REBELONLY%%LINEBREAK%When a friendly ship at Range 1 is hit by an attack, you may discard this card to suffer all uncanceled %HIT% results instead of the target ship.'''
         'Breach Specialist':
@@ -1715,7 +1727,8 @@
             name: "Leichtgewichtrahmen"
             text: '''Nur für TIE.%LINEBREAK%Sobald du verteidigst, falls es nach dem Würfeln der Verteidigungswürfel, mehr Angriffswürfel als Verteidigungswürfel gibt, wirf 1 zusätzlichen Verteidigungswürfel.%LINEBREAK%Du kannst diese Karte nicht ausrüsten, falls dein Wendigkeitswert 3 oder höher ist.'''
         'Pulsed Ray Shield':
-            text: '''Rebel and Scum only.%LINEBREAK%During the End phase, you may receive 1 ion token to recover 1 shield (up to your shield value). You can equip this card only if your shield value is "1."'''
+            name: "Pulsstrahlenschild"
+            text: '''Nur für Rebellen und Abschaum & Kriminelle.%LINEBREAK%Während der Endphase darfst du 1 Ionenmarker erhalten, um 1 Schild wiederaufzuladen (bis maximal zum Schildwert). Du kannst diese Karte nur dann ausrüsten, wenn dein Schildwert 1 ist.'''

     title_translations =
         "Slave I":
@@ -1877,14 +1890,20 @@
             text: '''Nur für TIE-Stürmer.%LINEBREAK%Unmittelbar bevor du dein Rad aufdeckst, musst du ein weißes (%BANKLEFT% 1)-, (%STRAIGHT% 1)- oder (%BANKRIGHT% 1)-Manöver ausführen, falls du nicht gestresst bist.'''
         # C-ROC
         '''Merchant One''':
-            text: '''C-ROC Cruiser only.%LINEBREAK%Your upgrade bar 1 additional %CREW% upgrade icon and 1 additional %TEAM% upgrade icon and loses 1 %CARGO% upgrade icon.'''
+            name: "Händler Eins"
+            ship: "C-ROC Kreuzer"
+            text: '''Nur für C-ROC Kreuzer.%LINEBREAK%Füge deiner Aufwertungsleiste 1 zusätzliches %CREW%-Symbol und 1 zusätzliches %TEAM%-Symbol hinzu und entferne 1 %CARGO%-Symbol aus deiner Aufwertungsleiste.'''
         '''"Light Scyk" Interceptor''':
+            name: '''"Leichter Scyk"-Abfangjäger'''
             ship: "M3-A Abfangjäger"
-            text: '''M3-A Interceptor only.%LINEBREAK%All Damage cards dealt to you are dealt faceup.  You may treat all bank maneuvers (%BANKLEFT% or %BANKRIGHT%) as green maneuvers.  You cannot equip Modification upgrades.'''
+            text: '''Nur für M3-A-Abfangjäger.%LINEBREAK%Alle dir zugeteilten Schadenskarten werden offen zugeteilt. Du darfst alle Drehmanöver (%BANKLEFT% oder %BANKRIGHT%) wie grüne Manöver behandeln. Du kannst keine Modifikationen ausrüsten.'''
         '''Insatiable Worrt''':
-            text: '''After you perform the recover action, gain 3 energy.'''
+            name: "Gefrässiger Worrt"
+            ship: "C-ROC Kreuzer"
+            text: '''Nachdem du die Aktion Aufladen durchgeführt hast, erhälst du 3 Energie.'''
         '''Broken Horn''':
-            text: '''When defending, if you have a reinforce token, you may add 1 additional %EVADE% result.  If you do, after defending, discard your reinforce token.'''
+            ship: "C-ROC Kreuzer"
+            text: '''Sobald du verteidigst, darfst du, falls du einen Verstärkungsmarker hast, 1 zusätzliches %EVADE% hinzufügen. Falls du das tust, musst du nach dem Verteidigen deinen Verstärkungsmarker ablegen.'''
         'Havoc':
             text: '''Scurrg H-6 Bomber only.%LINEBREAK%Your upgrade bar gains the %SYSTEM% and %SALVAGEDASTROMECH% icons and loses the %CREW% upgrade icon.%LINEBREAK%You cannot equip non-unique %SALVAGEDASTROMECH% Upgrade cards.'''
         'Vaksai':


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/timed_events.coffee
+++ src/timed_events.coffee
@@ -21,7 +21,7 @@
 ###
 TimedEvents = (I={}) ->
   ###*
-  Execute <
+  Execute fn

   player = GameObject


new task

new task

--- coffee/lib.coffee
+++ coffee/lib.coffee
@@ -95,11 +95,15 @@
 #* 66. Оптимизировать send и добавить очередь слушателей, которые должны иметь метод emit - вместо слушателей - замыкания с _belong указывающим на поместившего замыкание в очередь объект
 #* 67. CListen заменить на слушатели виджетов $(window) и $(document)
 #* 68. Добавить потоки
-# 69. Переделать animate
+# 69. Переделать animate - через css. @keyframes. В old - старую или переделать на классах или на потоках
 #* 70. В модель добавить функции для сравнения данных
 #- 71. Переопределить __bind на нормальное наследование метаклассов - невозможно
 # 72. Протестировать удаление элементов из памяти, так как все они имеют перекрёстные ссылки
 # 73. Отложенное выполнение в модели. Как сработал send - пока из него не выйдет - другие обновления этого ключа не выполняются. А добавляются в очередь и начинают отправляться после. Добавить значение для ключа - нет - очередь будет отправлять для всех значений последовательно, 0 - очередь игнорируется, 1 - выполнится последнее значение из очереди
+# 74. update: keyup - для модели, style {...} anim?, разработать свой мини-язык
+# 75. Списки для модели
+# 76. Избавиться от методов onKey на модели (?)
+# 77.

 # Ссылки:
 # http://docs.ractivejs.org/latest/observers - фреймворк с моделью данных и темплейтами


promisify Model#save

promisify Model#save

--- lib/Model.coffee
+++ lib/Model.coffee
@@ -199,17 +199,16 @@

     beforeSave: (cb) -> cb()

-    save: (cb = ->) ->
-      # TODO: defaults, required
-      @beforeSave (err) =>
-        return cb err if err?
+    save: (cb) ->
+      beforeSave = Promise.promisify @beforeSave, @
+
+      promise = beforeSave().then =>
+        # TODO: defaults, required
         c = @constructor
         newObj = {}
         # newObj[key] = @[key] for key, value of @

-        @validateTypes (err) =>
-          return cb err if err?
-
+        @validateTypes().then =>
           if c.timestamp and c.schema.modifiedAt and !@isNewRecord
             @modifiedAt = new Date

@@ -222,12 +221,12 @@
             # console.log key, typeOf(newObj[key]) if newObj[key]?

             if typeOf(opts) is 'object' and Object.keys(opts).length
-              return cb "Type not specified for `#{key}`" unless opts.type?
+              return reject new ValidationError "Type not specified for `#{key}`" unless opts.type?

             # if typeOf(opts) is 'object' (typeOf(opts) is 'object' and opts.type?)

               if opts.required and not newObj[key]?
-                return cb "#{key} field is required"
+                return reject new ValidationError "#{key} field is required"

               switch opts.type
                 when String
@@ -240,20 +239,23 @@

           if @isNewRecord
             newObj.id = @getId() if @getId()?
-            db.run c.r().insert(newObj), (err, reply) =>
-              return cb err if err?
-
+            db.run(c.r().insert(newObj)).then (reply) =>
               unless @id?
                 @id = reply?.generated_keys?[0]

               @isNewRecord = no

-              @saveRelated (err) => cb err, reply
+              @saveRelated().then => @

           else
-            @saveRelated (err) =>
-              return cb "undefined id" unless @getId()?
-              db.run c.r().get(@getId()).update(newObj), cb
+            @saveRelated().then =>
+              if @getId()?
+                db.run(c.r().get(@getId()).update(newObj)).then => @
+              else
+                reject new ModelError "undefined id"
+
+      promise.nodeify cb if cb?
+      promise

     saveRelated: (cb) ->
       c = @constructor


Fixed wrong parameter name

Fixed wrong parameter name

--- src/updown.class.coffee
+++ src/updown.class.coffee
@@ -167,7 +167,7 @@
 			# Assemble params
 			params =
 				url: url
-				interval: interval
+				period: interval
 			params.name = name if name?

 			# Create request


Rename function to slideFromBottom

Rename function to slideFromBottom


fullpieagent testing

fullpieagent testing

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -218,16 +218,16 @@

         $(@node).children("svg").remove();

-        svg = d3.select(@node).append("svg:svg")
+        #svg = d3.select(@node).append("svg:svg")
+        #    .attr("width", width)
+        #    .attr("height", height)
+        #    .append("svg:g")
+        #    .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
+        svg = d3.select(@node).append("svg")
             .attr("width", width)
             .attr("height", height)
-            .append("svg:g")
+            .append("g")
             .attr("transform", "translate(" + width/2 + "," + height/2 + ")")
-        #svg = d3.select(@node).append("svg")
-        #    .attr("width", width)
-        #    .attr("height", height)
-        #    .append("g")
-        #    .attr("transform", "translate(" + width/2 + "," + height/2 + ")")

         pie = d3.layout.pie()
             .sort(null)


newpie experiments

newpie experiments

--- widgets/fullpie_agent/fullpie_agent.coffee
+++ widgets/fullpie_agent/fullpie_agent.coffee
@@ -13,8 +13,10 @@
         if !dataSet
             return

+        container = $(@node)
+
         # Remove any previous svg
-        $(@node).children("svg").remove();
+        container.children("svg").remove();

         # Config values here
         width = 580                             # Width of the SVG area


fix js error for account groups page

fix js error for account groups page

fixes CNVS-13600

test plan:
- visit the account groups page
- it should not throw an error and should work

Change-Id: I5a9a6ce10240b5e1650b49a4cf7566a339174229
Reviewed-on: https://gerrit.instructure.com/36392
Reviewed-by: Braden Anderson <5d57265d16cfe2e0f2e6eb7f13e876bb378ef0f1@instructure.com>
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
QA-Review: Steven Shepherd <e794458448443812fb2685675aab3bbd1a3891bc@instructure.com>
Product-Review: Braden Anderson <5d57265d16cfe2e0f2e6eb7f13e876bb378ef0f1@instructure.com>

--- app/coffeescripts/views/groups/manage/GroupCategoryView.coffee
+++ app/coffeescripts/views/groups/manage/GroupCategoryView.coffee
@@ -84,7 +84,7 @@

     setUnassignedHeading: ->
       count = @model.unassignedUsersCount() ? 0
-      @unassignedUsersView.render()
+      @unassignedUsersView.render() if @unassignedUsersView
       @$unassignedUsersHeading.text(
         if @model.get('allows_multiple_memberships')
           I18n.t('everyone', "Everyone (%{count})", {count})


VM: Change username for profile nickname

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -497,9 +497,10 @@
     addVm = ({ account, target, user, sudo, groups, groupSlug
                type, planCode, planOwner, webHome })->

+      nickname = account.profile.nickname or user.username
       uid = 0
       hostnameAliases = JVM.createAliases {
-        nickname : user.username
+        nickname
         type, uid, groupSlug
       }

@@ -527,7 +528,7 @@
                                {users, groups, hostnameAlias}

         JVM.ensureDomainSettings \
-          {account, vm, type, nickname:user.username, groupSlug}
+          {account, vm, type, nickname, groupSlug}
         # JVM.createDomains account, hostnameAliases, hostnameAlias
         target.addVm vm, handleError


Groups: instead of using @one for Vm retrieval fecthVmByHostname is called

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -141,33 +141,29 @@
   @setAlwaysOn = secure (client, options, callback)->
     {connection: delegate: account} = client
     {vmName, status} = options
-    JDomain = require './domain'
-    JDomain.isDomainEligible {domain: vmName, newDomain: no}, (err, webHome) =>
-      return callback err  if err
-
-      @one hostnameAlias: vmName, (err, vm) ->
-        return callback err if err
-        return callback new KodingError "VM Not FOUND" unless vm
-
-        [{ id: groupId }] = vm.groups
-        JGroup = require './group'
-        JGroup.one { _id: groupId }, (err, group)->
+    fetchVmByHostname account, vmName, (err, vm) ->
+      return callback err if err
+      return callback new KodingError "VM Not FOUND" unless vm
+
+      [{ id: groupId }] = vm.groups
+      JGroup = require './group'
+      JGroup.one { _id: groupId }, (err, group)->
+        return callback err  if err
+        return callback new KodingError "Group not found"  unless group
+        object = if group.slug is "koding" then account else group
+
+        object.fetchSubscription (err, subscription) ->
           return callback err  if err
-          return callback new KodingError "Group not found"  unless group
-          object = if group.slug is "koding" then account else group
-
-          object.fetchSubscription (err, subscription) ->
+          return callback new KodingError("Subscription not found","NOTSUBSCRIBED")  unless subscription
+
+          JPaymentPack.one tags: "alwayson", (err, pack) ->
             return callback err  if err
-            return callback new KodingError("Subscription not found","NOTSUBSCRIBED")  unless subscription
-
-            JPaymentPack.one tags: "alwayson", (err, pack) ->
+            return callback new KodingError "Always On pack not found"  unless pack
+            {debit, credit} = subscription
+            fn = if status then debit else credit
+            fn.call subscription, {pack}, (err) ->
               return callback err  if err
-              return callback new KodingError "Always On pack not found"  unless pack
-              {debit, credit} = subscription
-              fn = if status then debit else credit
-              fn.call subscription, {pack}, (err) ->
-                return callback err  if err
-                vm.update $set: alwaysOn: status, callback
+              vm.update $set: alwaysOn: status, callback

   @fetchDefaultVm_ = (client, callback)->
     {delegate} = client.connection


vm: added a hostname to the model

vm: added a hostname to the model

--- workers/social/lib/social/models/vm.coffee
+++ workers/social/lib/social/models/vm.coffee
@@ -34,6 +34,7 @@
         type            : String
         default         : -> null
       name              : String
+      hostname          : String
       users             : Array
       groups            : Array
       usage             : # TODO: usage seems like the wrong term for this.


Check is transiting in displayPage.

Check is transiting in displayPage.

--- js/flight.coffee
+++ js/flight.coffee
@@ -120,6 +120,7 @@

    # displays pages when transiton is false
    displayPage: =>
+    if @isTransitioning is true then return else @isTransitioning = true
     @targetPanel.style.display = "block"
     @currentPanel.style.display = "block"
     @targetPanel.style.left = "0%"
@@ -127,6 +128,7 @@

     @removeClass(@currentPanel, 'visible')
     @addClass(@targetPanel, 'visible')
+    @isTransitioning = false

   fitHeightToContent: ->
     flightViewport = document.getElementById 'flight'


empty page fix

empty page fix

--- js/render.coffee
+++ js/render.coffee
@@ -22,6 +22,10 @@
    PAGE_MARGIN = '1000px'
    START = 1

+   EMPTY_PAGE_COST = (filled, height) ->
+    p = filled / height
+    return 1000 / p - 1000
+

    class Render extends Base
     @initialize (options) ->
@@ -106,7 +110,7 @@
             (@getOffsetTop elem, @elems.main)
       break if pos > H

-      c = @broken[i] + @breakCost[i] + PAGE_COST
+      c = @broken[i] + @breakCost[i] + PAGE_COST + EMPTY_PAGE_COST pos, H
       if @broken[n] >= c
        @broken[n] = c
        @nextBreak[n] = i


forgot to remove old requires

forgot to remove old requires

--- server/src/index.coffee
+++ server/src/index.coffee
@@ -1,8 +1,6 @@
 exec = require("child_process").exec
 spawn = require("child_process").spawn
 Hapi = require("hapi")
-req = require("request")
-Storage = require("node-storage")

 IS_DEV = process.argv[2]


Move LabeledStatement and WithStatement traversers.

--- js2coffee.coffee
+++ js2coffee.coffee
@@ -68,7 +68,7 @@
   run: ->
     self = this
     @estraverse().replace @ast,
-      enter: (node, parent) =>
+      enter: (node, parent) ->
         fn = self["#{node.type}"]
         if fn
           fn.apply self, [ node, parent, (=> @skip()), (=> @break()) ]
@@ -112,6 +112,33 @@
     @updateBinaryExpression node
   UnaryExpression: (node) ->
     @updateVoidToUndefined node
+  LabeledStatement: (node, parent) ->
+    @warnAboutLabeledStatements node, parent
+  WithStatement: (node) ->
+    @syntaxError node, "'with' is not supported in CoffeeScript"
+  VariableDeclarator: (node, parent, skip) ->
+    @addExplicitUndefinedInitializer node, parent, skip
+
+  ###
+  # For VariableDeclarator with no initializers (`var a`), add `undefined` as the initializer.
+  ###
+
+  addExplicitUndefinedInitializer: (node, parent, skip) ->
+    unless node.init?
+      node.init = { type: 'Identifier', name: 'undefined' }
+      skip()
+    node
+
+  ###
+  # Produce warnings when using labels. It may be a JSON string being pasted,
+  # so produce a more helpful warning for that case.
+  ###
+
+  warnAboutLabeledStatements: (node, parent) ->
+    if parent.type is 'BlockStatement' and parent is @block
+      @syntaxError node, "Labeled statements are not supported (wrap your JSON in parentheses)"
+    else
+      @syntaxError node, "Labeled statements are not supported in CoffeeScirpt"

   ###
   # Updates `void 0` UnaryExpressions to `undefined` identifiers
@@ -556,16 +583,9 @@
     delimit(declarators, @indent())

   VariableDeclarator: (node) ->
-    init = if node.init?
-      @walk(node.init)
-    else
-      "undefined"
-
-    [ @walk(node.id), ' = ', init, "\n" ]
+    [ @walk(node.id), ' = ', @walk(node.init), "\n" ]

   FunctionExpression: (node, ctx) ->
-    # if node.id
-    #   @syntaxError node, "Named function expressions are not supported yet"
     params = @makeParams(node.params)

     expr = @indent (i) =>
@@ -639,15 +659,6 @@

   ThrowStatement: (node) ->
     [ "throw ", @walk(node.argument), "\n" ]
-
-  WithStatement: (node) ->
-    @syntaxError node, "'with' is not supported in CoffeeScript"
-
-  LabeledStatement: (node, ctx) ->
-    if ctx.parent?.type is 'BlockStatement'
-      @syntaxError node, "Labeled statements are not supported (wrap your JSON in parentheses)"
-    else
-      @syntaxError node, "Labeled statements are not supported in CoffeeScirpt"

   # Ternary operator (`a ? b : c`)
   ConditionalExpression: (node) ->


Don't show topic popover

Don't show topic popover

--- core/app/backbone/views/activities/activity_item_view.coffee
+++ core/app/backbone/views/activities/activity_item_view.coffee
@@ -59,14 +59,14 @@
     topic: @topic().toJSON()

   onRender: ->
-    Backbone.Factlink.makeTooltipForView @,
-      positioning:
-        align: 'left'
-        side: 'bottom'
-      selector: '.js-link'
-      $offsetParent: @options.$offsetParent
-      tooltipViewFactory: =>
-        new TopicPopoverContentView model: @topic()
+    # Backbone.Factlink.makeTooltipForView @,
+    #   positioning:
+    #     align: 'left'
+    #     side: 'bottom'
+    #   selector: '.js-link'
+    #   $offsetParent: @options.$offsetParent
+    #   tooltipViewFactory: =>
+    #     new TopicPopoverContentView model: @topic()

   topic: -> @_topic ?= new Topic(@model.get('activity').topic)


Highlight "autocmd" as a support-type function

--- grammars/viml.cson
+++ grammars/viml.cson
@@ -295,7 +295,7 @@
 		}, {
 			# Extra functions not picked up by keyword parser
 			name: "support.function.${1:/downcase}.viml"
-			match: "\\b(execute|Plugin)\\b"
+			match: "\\b(execute|Plugin|autocmd)\\b"
 		}]

 	attribNames:


Don't do this after all, always prompts for authorization...

--- core/app/backbone/views/popovers/react_signin_popover.coffee
+++ core/app/backbone/views/popovers/react_signin_popover.coffee
@@ -40,8 +40,7 @@
           _span ["signin-popover"],
             'Sign in with: ',
             _a ["button-twitter small-connect-button js-accounts-popup-link",
-              # The "?use_authorize=true&x_auth_access_type=write" part is for some weird twitter bug
-              href: "/auth/twitter?use_authorize=true&x_auth_access_type=write"
+              href: "/auth/twitter"
             ],
               _i ["icon-twitter"]
             ' ',


maybe better solution...

maybe better solution...

--- api/apps/articles/model.coffee
+++ api/apps/articles/model.coffee
@@ -19,7 +19,7 @@

 schema = (->
   author_id: @objectId().required()
-  slug: @string()
+  slug: @string().allow(null)
   thumbnail_title: @string().allow('', null)
   thumbnail_teaser: @string().allow('', null)
   thumbnail_image: @string().allow('', null)
@@ -105,8 +105,9 @@
     return callback err if err
     article.updated_at = moment().format()
     article.author_id = ObjectId article.author_id
-    getSlug _.omit(article, 'slug'), (err, slug) ->
+    getSlug article, (err, slug) ->
       return callback err if err
+      article.slug = undefined
       article.slugs ?= []
       article.slugs.push slug unless slug in article.slugs
       db.articles.findAndModify {


remove unnecessary code

remove unnecessary code

--- app/coffee/modules/auth.coffee
+++ app/coffee/modules/auth.coffee
@@ -609,7 +609,6 @@
         form = $el.find("form").checksley()

         onSuccessSubmit = (response) ->
-            console.log(response)
             if $auth.isAuthenticated()
                 $repo.queryOne("users", $auth.getUser().id).then (data) =>
                     $auth.setUser(data)


Use response meta field over query param for collection pagination

--- app/collections/show-list.cjsx
+++ app/collections/show-list.cjsx
@@ -97,13 +97,15 @@
           <Translate component="p" content="collectionSubjectListPage.noSubjects" />}

         {if subjects.length > 0
+          meta = subjects[0].getMeta()
+

{subjects.map(subjectNode)}

             <Paginator
-              page={+@props.query.page}
+              page={meta.page}
               onPageChange={@onPageChange}
-              pageCount={subjects[0].getMeta().page_count} />
+              pageCount={meta.page_count} />

}


fix for left sidebar current items highlighting (issue #441)

--- app/js/controllers/root.coffee
+++ app/js/controllers/root.coffee
@@ -1,10 +1,10 @@
-angular.module("app").controller "RootController", ($scope, $location, $modal, $q, $http, $rootScope, Wallet, Client, $idle, Shared, Info) ->
+angular.module("app").controller "RootController", ($scope, $location, $modal, $q, $http, $rootScope, $state, $stateParams, Wallet, Client, $idle, Shared, Info) ->
     $scope.unlockwallet = false
     $scope.bodyclass = "cover"
     $scope.currentPath = $location.path()

     $scope.current_path_includes = (str)->
-        $scope.currentPath.indexOf(str) >= 0
+        $state.current.name.indexOf(str) >= 0

     Wallet.check_wallet_status().then ->
         Info.watch_for_updates()


Allow updating the request when triaging

* Only update the waterpoint if any request attributes are applied
* Update the request if the waterpoint update was successful or there
  was no data to update

--- app/scripts/controllers.coffee
+++ app/scripts/controllers.coffee
@@ -71,20 +71,40 @@
         $scope.waterpoint = waterpoint._items[0]
     $scope.triage = {}
     $scope.doTriage = () ->
-      $http
-        method: 'PATCH'
-        url: '/api/waterpoints/'+$scope.waterpoint._id
-        data: $scope.triage
-        headers: {'If-Match': $scope.waterpoint._etag}
-      .success (data, status, headers, config) ->
-        console.log data, status, headers, config
-        if status == 200 and data._status == 'OK'
-          flash.success = 'Waterpoint successfully updated!'
-          for k, v of $scope.triage
-            $scope.waterpoint[k] = v
-        if status == 200 and data._status == 'ERR'
-          for field, message of data._issues
-            flash.error = "#{field}: #{message}"
+      if Object.keys($scope.triage).length
+        $http
+          method: 'PATCH'
+          url: '/api/waterpoints/'+$scope.waterpoint._id
+          data: $scope.triage
+          headers: {'If-Match': $scope.waterpoint._etag}
+        .success (data, status, headers, config) ->
+          console.log data, status, headers, config
+          if status == 200 and data._status == 'OK'
+            flash.success = 'Waterpoint successfully updated!'
+            for k, v of $scope.triage
+              $scope.waterpoint[k] = v
+            saveRequest()
+          if status == 200 and data._status == 'ERR'
+            for field, message of data._issues
+              flash.error = "#{field}: #{message}"
+      else
+        saveRequest()
+    saveRequest = () ->
+      etag = $scope.request._etag
+      # We need to remove these special attributes since they are not defined
+      # in the schema and the data will not validate and the update be rejected
+      for attr in ['_created', '_etag', '_id', '_links', '_updated']
+        $scope.request[attr] = undefined
+      $http.put('/api/requests/'+$routeParams.id,
+                $scope.request,
+                headers: {'If-Match': etag})
+        .success (data, status, headers, config) ->
+          console.log data, status, headers, config
+          if status == 200 and data._status == 'OK'
+            flash.success = 'Request successfully updated!'
+          if status == 200 and data._status == 'ERR'
+            for field, message of data._issues
+              flash.error = "#{field}: #{message}"

   .controller 'DashboardCtrl', ($scope, $http) ->
     $scope.plots = [


storage: fix a typo

storage: fix a typo

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -914,7 +914,7 @@
             dbg("mounting snapshots on all compute vm's")
             errors = {}
             f = (host, cb) ->
-                exports.mount_snapshots_on_cofmpute_vm
+                exports.mount_snapshots_on_compute_vm
                     host : host
                     cb   : (err) ->
                         if err


storage: implement more optimized (low level) project move, which directly rsync's from one machine to another.

--- src/smc-hub/storage.coffee
+++ src/smc-hub/storage.coffee
@@ -53,7 +53,6 @@
         command     : 'rsync'
         args        : args
         timeout     : 3600*2  # up to 2 hours...
-        verbose     : true
         err_on_exit : true
         cb          : (err, output) ->
             if err and output?.exit_code == 24 or output?.exit_code == 23
@@ -329,6 +328,83 @@
                 host       : opts.host
                 cb         : cb
     ], opts.cb)
+
+exports.move_project = move_project = (opts) ->
+    opts = defaults opts,
+        database   : required
+        project_id : required
+        target     : required
+        cb         : required
+    dbg = (m) -> winston.debug("move_project(project_id='#{opts.project_id}'): #{m}")
+    source = undefined
+    async.series([
+        (cb) ->
+            dbg('determine current location of project')
+            opts.database.get_project_host
+                project_id : opts.project_id
+                cb         : (err, x) ->
+                    if err
+                        cb(err)
+                    else
+                        source = x?.host
+                        if not source
+                            cb("project not opened, so can't move")
+                        else if source == opts.target
+                            cb("project is already on '#{opts.target}'")
+                        else
+                            cb()
+        (cb) ->
+            dbg("copy the project")
+            copy_project_from_one_compute_server_to_another
+                project_id : opts.project_id
+                source     : source
+                target     : opts.target
+                cb         : cb
+        (cb) ->
+            dbg("successfully copied the project, now setting host in database")
+            opts.database.set_project_host
+                project_id : opts.project_id
+                host       : opts.target
+                cb         : cb
+        (cb) ->
+            dbg("also, delete from the source to save space")
+            delete_project_from_compute_server
+                project_id : opts.project_id
+                host       : source
+                cb         : cb
+    ], opts.cb)
+
+# Low level function that copies a project from one compute server to another.
+# We assume the target is empty (so no need for dangerous --delete).
+copy_project_from_one_compute_server_to_another = (opts) ->
+    opts = defaults opts,
+        project_id : required
+        source     : required
+        target     : required
+        cb         : required
+    winston.debug("copy the project from '#{opts.source}' to '#{opts.target}'")
+    # Do a check on the input, given how dangerous this command is!
+    if not misc.is_valid_uuid_string(opts.project_id)
+        opts.cb("project_id='#{opts.project_id}' is not a valid uuid")
+        return
+    for host in [opts.source, opts.target]
+        if not misc.startswith(host, 'compute')
+            opts.cb("host='#{host}' must start with 'compute'")
+            return
+    source = "/projects/#{opts.project_id}/"
+    target = "#{opts.target}:/projects/#{opts.project_id}/"
+    excludes = exclude().join(' ')
+
+    misc_node.execute_code
+        command : 'ssh'
+        args    : ["root@#{opts.source}", "rsync -axH -e 'ssh -T -c arcfour -o Compression=no -x  -o StrictHostKeyChecking=no' #{excludes} #{source} #{target}"]
+        timeout : 3600*2  # up to 2 hours...
+        cb      : (err, output) ->
+            if err and output?.exit_code == 24 or output?.exit_code == 23 # see copy_project_from_compute_to_storage
+                err = undefined
+            opts.cb(err)
+
+

 ###


Check extra option name validity

Check extra option name validity

--- lib/debug.coffee
+++ lib/debug.coffee
@@ -70,22 +70,25 @@
   isEnabled: ->
     enabled

+  validateTagName: (tag) ->
+    valid = tag? and typeof tag is 'string' and tag.length > 0
+    @assert valid, 'You must pass a non-empty string!'
+    valid
   enableExtra: (tag) ->
-    console.assert tag, 'Are you kidding me? What do you want to enable?'
-    return (Debug.wrap 'Nothing happened') if !tag
-    @extra tag
+    return (Debug.wrap 'Invalid extra option name') if !@extra tag
     extraOpts.add tag.toString()
     Debug.wrap {enabledExtra: tag}
   disableExtra: (tag) ->
+    return (Debug.wrap 'Invalid extra option name') if !@validateTagName tag
     extraOpts.delete tag.toString()
     Debug.wrap {disabledExtra: tag}
   isExtraEnabled: (tag) ->
-    return false if !tag
+    return false if !@validateTagName tag
     extraOpts.has tag
   getAllExtraOptionsAsArray: ->
     Array.from extraOpts
   extra: (tag) ->
-    if !@ex[tag]?
+    if @validateTagName tag and !@ex[tag]?
       Object.defineProperty @ex, tag,
         value: new ExOptHandler
         enumerable: true


sidebar: progressbar class setting taken outside

--- client/app/lib/components/sidebarmachineslistitem/index.coffee
+++ client/app/lib/components/sidebarmachineslistitem/index.coffee
@@ -45,8 +45,9 @@
     status = @machine ['status', 'state']
     percentage = @machine('percentage') or 0
     percentage = 100 - percentage  if status is Machine.State.Stopping
+    fullClass  = if percentage is 100 then ' full' else ''

-

+


Use a fat arrow for onSuccess and onError

--- lib/assets/javascripts/turbograft/remote.coffee
+++ lib/assets/javascripts/turbograft/remote.coffee
@@ -116,7 +116,7 @@
       enabledInputs.push(input)
     enabledInputs

-  onSuccess: (ev) ->
+  onSuccess: (ev) =>
     @opts.success?()

     xhr = ev.target
@@ -145,7 +145,7 @@
         Page.refresh
           response: xhr

-  onError: (ev) ->
+  onError: (ev) =>
     @opts.fail?()

     xhr = ev.target


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/active_bounds.coffee
+++ test/active_bounds.coffee
@@ -2,10 +2,10 @@

 App = {}

+App.width = 640
+App.height = 480
+
 test "should not be destroyed when inside activeBounds", ->
-  App.width = 640
-  App.height = 480
-
   obj = GameObject
     x: 50
     y: 50


feature: log module require

feature: log module require

--- lib/index.coffee
+++ lib/index.coffee
@@ -16,7 +16,8 @@
     super()

   require: (name, module = name) ->
-    @unless name, ->
+    @unless name, (logger) ->
+      logger.debug "require module", name: module
       require module

   injectAll: (plugins) ->


fix help and changelog links

fix help and changelog links

fixes #121

--- lib/index.coffee
+++ lib/index.coffee
@@ -209,13 +209,13 @@
 $(document).on 'click', '#browse', ->
   openFolder()

-$('.github > a').click (e) ->
+$(document).on 'click', '.github > a', (e) ->
   e.preventDefault()
   open('https://github.com/dustinblackman/Championify#faq')

-$('.championify_version > span').click (e) ->
-  e.preventDefault()
-  open('https://github.com/dustinblackman/Championify/releases/latest')
+$(document).on 'click', '.championify_version > span', (e) ->
+  e.preventDefault()
+  open('https://github.com/dustinblackman/Championify/blob/master/CHANGELOG.md')

 log_uploaded = false
 $(document).on 'click', '#upload_log', (e) ->


Added `filename` option

Added `filename` option

--- lib/index.coffee
+++ lib/index.coffee
@@ -8,7 +8,8 @@
   overwrite: true

 module.exports = (bumped, plugin, cb) ->
-  outStream = fs.createWriteStream 'CHANGELOG.md'
+  filename = plugin.options.filename or 'CHANGELOG.md'
+  outStream = fs.createWriteStream filename
   changelogStream = changelog options
   changelogStream.pipe outStream
   changelogStream.on 'error', cb


changing lantern status user count lookup

--- server/api/service/lantern/lantern.coffee
+++ server/api/service/lantern/lantern.coffee
@@ -9,7 +9,7 @@
       total: job_job.count('service:lantern')
       done: job_job.count('service:lantern AND done:true')
     results: job_result.count('service:lantern')
-    users: API.accounts.count({"roles.lantern":"*"})
+    users: Users.count({"roles.lantern":"*"})

 API.service.lantern.job = (job) ->
   for i of job.processes


Commentate some

Commentate some

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,6 +1,7 @@
 find = require 'core-js/library/fn/array/find'
 findIndex = require 'core-js/library/fn/array/find-index'

+# Default options
 defaults =
   commentList: false
   commentTime: 4
@@ -21,8 +22,6 @@
   'black'
 ]

-HTML5Niconicoplayer = undefined
-
 ###*
 # Initialize the plugin.
 # @param options (optional) {object} configuration for the plugin
@@ -35,6 +34,7 @@

   commentLoaded = no

+  # Get comment list element
   if settings.commentList
     commentListEl = document.querySelector settings.commentList


Add Node 0.8 compatibility

Add Node 0.8 compatibility

--- lib/index.coffee
+++ lib/index.coffee
@@ -1,5 +1,7 @@

-path = require 'path'
+fs = require 'path'
+fs = require 'path'
+existsSync = fs.existsSync or path.existsSync

 ###

@@ -49,14 +51,14 @@
             # Path is relative to workspace
             search = path.resolve settings.workspace, search
             # If it exists, determin the executable
-            if path.existsSync "#{search}"
+            if existsSync "#{search}"
                 if search.substr(-4) is '.coffee'
                 then return ['coffee', "#{search}"]
                 else return ['node', "#{search}"]
             # Otherwise, try to see if filename is without an extension
-            if path.existsSync "#{search}.js"
+            if existsSync "#{search}.js"
                 return ['node', "#{search}.js"]
-            else if path.existsSync "#{search}.coffee"
+            else if existsSync "#{search}.coffee"
                 return ['coffee', "#{search}.coffee"]
         throw new Error 'Failed to discover a "server.js" or "app.js" file'
     route = (req, res, next) ->


a couple small edits, untested

a couple small edits, untested

--- lib/index.coffee
+++ lib/index.coffee
@@ -5,9 +5,12 @@
 pluralize  = require 'pluralize'

 errors =
-  no_token:     'Missing required options for roots-contentful. Please ensure `access_token` and `space_id` are present.'
-  no_type_id:   'One or more of your content types is missing an `id` value'
-  sys_conflict: 'One of your content types has `sys` as a field. This is reserved for storing Contentful system metadata, please rename this field to a different value.'
+  no_token: 'Missing required options for roots-contentful. Please ensure
+  `access_token` and `space_id` are present.'
+  no_type_id: 'One or more of your content types is missing an `id` value'
+  sys_conflict:'One of your content types has `sys` as a field. This is reserved
+   for storing Contentful system metadata, please rename this field to a
+   different value.'

 module.exports = (opts) ->
   # throw error if missing required config
@@ -28,10 +31,11 @@

   configure_content = (types) ->
     W.map types, (t) ->
-      if not t.id then throw new Error errors.no_type_id
+      if not t.id then W.reject(errors.no_type_id)
+      if t.name then return W.resolve()
+
       t.filters ?= {}
-      if t.name then return W.resolve()
-      return client.contentType(t.id).then (res) ->
+      W client.contentType(t.id).then (res) ->
         t.name = pluralize(S(res.name).toLowerCase().underscore().s)
         return t

@@ -43,12 +47,12 @@
   ###

   get_all_content = (types) ->
-    locals = {}
-    W.map(types, (t) ->
+    W.reduce types, (m, t) ->
       fetch_content(t)
         .then(format_content)
-        .then (c) -> locals[t.name] = c
-    ).yield(locals)
+        .then((c) -> m[t.name] = c)
+        .yield(m)
+    , {}

   ###*
    * Fetch entries for a single content type object
@@ -57,7 +61,7 @@
   ###

   fetch_content = (type) ->
-    client.entries(_.merge(type.filters, content_type: type.id))
+    W client.entries(_.merge(type.filters, content_type: type.id))

   ###*
    * Formats raw response from Contentful
@@ -75,7 +79,7 @@
   ###

   format_entry = (e) ->
-    if _.has(e.fields, 'sys') then throw new Error errors.sys_conflict
+    if _.has(e.fields, 'sys') then W.reject(errors.sys_conflict)
     _.assign(_.omit(e, 'fields'), e.fields)

   # load content
@@ -89,5 +93,5 @@
       before_pass: (ctx) =>
         # once content is loaded, pass contentful data into locals
         promise.then (locals) =>
-          return if @roots.config.locals.contentful
+          if @roots.config.locals.contentful then return
           @roots.config.locals.contentful = locals


fix infinite blackbox loop on windows

fix infinite blackbox loop on windows

fixes #142

--- lib/index.coffee
+++ lib/index.coffee
@@ -26,6 +26,10 @@
 Translate = require './js/translate'
 updateManager = require './js/update_manager'
 viewManager = require './js/view_manager'
+
+# Windows Specific Dependencies
+if process.platform == 'win32'
+  runas = require 'runas'

 # Initialize Globals
 window.devEnabled = fs.existsSync('./dev_enabled') or fs.existsSync(path.join(__dirname, '..', 'dev_enabled'))
@@ -262,7 +266,12 @@
 ###
 viewManager.init ->
   updateManager.check (version, major) ->
-    if version and major
+    if version and optionsParser.update()
+      if process.platform == 'win32' and !optionsParser.runnedAsAdmin()
+        return runas(process.execPath, ['--startAsAdmin'], {hide: false, admin: true})
+      else
+        return EndSession(new cErrors.UpdateError('Can\'t auto update, please redownload'))
+    else if version and major
       updateManager.majorUpdate(version)
     else if version
       updateManager.minorUpdate(version)


#1 fix

#1 fix

--- lib/input.coffee
+++ lib/input.coffee
@@ -11,8 +11,6 @@
   attachedCallback: ->
     @editorElement = document.getElementById("choose-pane-editor")
     @editor = @editorElement.getModel()
-    @editor.onDidChange =>
-      @confirm() unless @finished
     this

   destroy: ->
@@ -33,6 +31,9 @@
     @panel.show()
     @editorElement.focus()
     @commandSubscription = @handleEvents()
+    disposable = @editor.onDidChange =>
+      disposable.dispose()
+      @confirm() unless @finished

     new Promise (@resolve, @reject) =>


:fire: garbage

:fire: garbage

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -2,7 +2,6 @@
 semver = require 'semver'
 {Range, Point, Disposable} = require 'atom'
 {inspect} = require 'util'
-settings = require '../lib/settings'
 globalState = require '../lib/global-state'

 KeymapManager = atom.keymaps.constructor
@@ -466,7 +465,6 @@
         switch
           when k.input?
             # TODO no longer need to use [input: 'char'] style.
-            # if settings.
             rawKeystroke(_key, target) for _key in k.input.split('')
           when k.search?
             @vimState.searchInput.editor.insertText(k.search) if k.search


Route checksum fixture in update spec

Route checksum fixture in update spec

--- spec/update-spec.coffee
+++ spec/update-spec.coffee
@@ -20,6 +20,8 @@
       response.sendfile path.join(__dirname, 'fixtures', 'node.lib')
     app.get '/node/v0.10.3/x64/node.lib', (request, response) ->
       response.sendfile path.join(__dirname, 'fixtures', 'node_x64.lib')
+    app.get '/node/v0.10.3/SHASUMS256.txt', (request, response) ->
+      response.sendfile path.join(__dirname, 'fixtures', 'SHASUMS256.txt')
     app.get '/tarball/test-module-1.0.0.tgz', (request, response) ->
       response.sendfile path.join(__dirname, 'fixtures', 'test-module-1.0.0.tgz')
     server =  http.createServer(app)
@@ -48,6 +50,7 @@
       callback.callCount > 0

     runs ->
+      expect(callback.mostRecentCall.args[0]).toBeUndefined()
       expect(fs.existsSync(removedPath)).toBeFalsy()
       expect(fs.existsSync(path.join(moduleDirectory, 'node_modules', 'test-module', 'index.js'))).toBeTruthy()
       expect(fs.existsSync(path.join(moduleDirectory, 'node_modules', 'test-module', 'package.json'))).toBeTruthy()