create default render overridable class

create default render overridable class

--- src/render/Render.coffee
+++ src/render/Render.coffee
@@ -1,2 +1,56 @@
-class Rotten.Render
-
+class Rotten.Engine
+
+    @CANVAS = 0
+    @WEBGL = 1
+
+    constructor: (view) ->
+        if not view
+            @view = document.createElement "canvas"
+            @view.style.position = "absolute"
+            @view.style.top = "0px"
+            @view.style.left = "0px"
+            @view.width = FF.Util.screenWidth()
+            @view.height = FF.Util.screenHeight()
+            document.body.appendChild @view
+        else
+            @view = view
+
+        @width = @view.width
+        @height = @view.height
+
+
+    getContext: ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    getWidth: ->
+        @width
+
+    getHeight: ->
+        @height
+
+    setBackgroundColor: (color) ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    clearRect: ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    save: ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    restore: ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    translate: (x,y) ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    rotate: (angle) ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    setAlpha: (alpha) ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    drawImage: (image, x, y, width, height)->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"
+
+    drawSubImage: (image, sub_x, sub_y, sub_width, sub_height, x, y, width, height) ->
+        throw new Error "[Rotten.Engine] abstract method should be implemented"


Use __dirname for script directory

Use __dirname for script directory

--- src/server/server.coffee
+++ src/server/server.coffee
@@ -4,7 +4,7 @@

 app = express()

-app.use express.static 'app/public'
+app.use express.static __dirname + '/public'
 app.use session secret: 'secret-TODO-change-me'
 app.use body_parser.json()
 app.use body_parser.urlencoded extended: true


[FIX] Remove password and email from the mandatory fields.

--- src/services/user.coffee
+++ src/services/user.coffee
@@ -64,7 +64,7 @@
     @get        : () ->
         if do @_instance.get == undefined
             definition = Database.createTable do @_name.get, do @_schema.get
-            definition.validatesPresenceOf 'name', 'mail', 'password', 'id_acl', 'id_role'
+            definition.validatesPresenceOf 'name', 'id_acl', 'id_role'
             definition.validatesUniquenessOf 'mail',
                 message : 'This mail address is already used.'
             definition.validatesUniquenessOf 'name',


3 more https links

3 more https links

--- src/smc-hub/email.coffee
+++ src/smc-hub/email.coffee
@@ -264,15 +264,15 @@
 These are personal workspaces which contain your files, computational worksheets, and data.
 You can run your computations through the web interface, via interactive worksheets and notebooks, or by executing a program in a terminal.
 #{SITE_NAME} supports online editing of
-    Jupyter Notebooks,
-    Sage Worksheets,
+    Jupyter Notebooks,
+    Sage Worksheets,
     Latex files, etc.

Software:

Mathematical calculation:
-    SageMath,
+    SageMath,
     SymPy, etc.

Statistics and Data Science:


Change line toggling

Change line toggling

--- src/ubret/spectra.coffee
+++ src/ubret/spectra.coffee
@@ -4,9 +4,6 @@
   constructor: (selector) ->
     super selector
     @cache = {}
-    @opts.fluxLine = true
-    @opts.bestFitLine = true
-    @opts.emissionLines = true
     @on 'next', @next
     @on 'prev', @prev

@@ -61,14 +58,12 @@
       .range([@height, 0])
       .domain(d3.extent(@subject.flux))

-
     @svg = @opts.selector.append('svg')
         .attr('width', @opts.width - 10)
         .attr('height', @opts.height - 10)
       .append('g')
         .attr('transform', "translate(#{margin.left}, #{margin.top})")
         .call(d3.behavior.zoom().x(@x).y(@y).scaleExtent([1, 8]).on("zoom", @zoom))
-

     @xAxis = d3.svg.axis()
       .scale(@x)
@@ -100,11 +95,11 @@
         .style("text-anchor", "end")
         .text("Flux (1E-17 erg/cm^2/s/Ang)")

-    @fluxLine() if @opts.fluxLine
-    @bestFitLine() if @opts.bestFitLine
-    @emissionLines() if @opts.emissionLines
+    @fluxLinedDraw() if @opts.fluxLine is 'show'
+    @bestFitLinedDraw() if @opts.bestFitLine is 'show'
+    @emissionLinesdDraw() if @opts.emissionLines is 'show'

-  fluxLine: =>
+  fluxLinedDraw: =>
     fluxLine = d3.svg.line()
       .x((d, i) => @x(@subject.wavelengths[i]))
       .y((d, i) => @y(d))
@@ -116,7 +111,7 @@
         .attr('fill', 'none')
         .attr('stroke', 'black')

-  bestFitLine: =>
+  bestFitLinedDraw: =>
     bestFitLine = d3.svg.line()
       .x((d, i) => @x(@subject.wavelengths[i]))
       .y((d, i) => @y(d))
@@ -128,7 +123,7 @@
         .attr('stroke', "blue")
         .attr('fill', 'none')

-  emissionLines: (redshiftCorrected=true) =>
+  emissionLinesdDraw: (redshiftCorrected=true) =>
     for name, line of @spectralLines
       multiplier = if redshiftCorrected then (1 + line.redshift) else 1
       @svg.append("line")


client: home: fixed a build breaker dep

client: home: fixed a build breaker dep

--- client/home/lib/index.coffee
+++ client/home/lib/index.coffee
@@ -3,7 +3,6 @@
 HomeAppView         = require './homeappview'
 HomeAccount         = require './account'
 HomeUtilities       = require './utilities'
-HomeVirtualMachines = require './virtualmachines'
 HomeMyTeam          = require './myteam'
 HomeTeamBilling     = require './billing'
 HomeWelcome         = require './welcome'
@@ -20,7 +19,6 @@
   TABS = [
     { title : 'Welcome',          viewClass : HomeWelcome,         role: 'member' }
     { title : 'Stacks',           viewClass : HomeStacks,          role: 'member' }
-    # { title : 'Virtual Machines', viewClass : HomeVirtualMachines, role: 'member' }
     { title : 'My Team',          viewClass : HomeMyTeam,          role: 'member' }
     { title : 'Team Billing',     viewClass : HomeTeamBilling                     }
     { title : 'Koding Utilities', viewClass : HomeUtilities,       role: 'member' }


force reload on logout

force reload on logout

--- client/app/MainApp/maincontroller/maincontroller.coffee
+++ client/app/MainApp/maincontroller/maincontroller.coffee
@@ -112,6 +112,9 @@
             cssClass  : "login"
             title     : "Come back soon!"
             duration  : 2000
+          # fixme: get rid of reload, clean up ui on account change
+          # tightly related to application manager refactoring
+          @utils.wait 2000, -> location.reload yes
       else
         @goToPage pageInfo


Routes: Change machine route for permanent shared vms.

--- client/ide/lib/routes.coffee
+++ client/ide/lib/routes.coffee
@@ -117,7 +117,8 @@
     if machineLabel is machine.label
       workspaceSlug = latestWorkspace.workspaceSlug

-  kd.getSingleton('router').handleRoute "/IDE/#{machine.slug}/#{workspaceSlug}"
+  identifier = if machine.isPermanent() then machine.uid else machine.slug
+  kd.getSingleton('router').handleRoute "/IDE/#{identifier}/#{workspaceSlug}"

 routeToLatestWorkspace = ->


Dont launch an airbrake for client-side posts, which were working

--- js/outer.coffee
+++ js/outer.coffee
@@ -151,6 +151,8 @@
   @get("/gh/.*", (cx) =>
     @unload()
     window.location = cx.path)
+  @post "/notify", -> true # allow to propagate
+  @post "/about/contact", -> true # allow to propagate

 # Global polyfills
 if $.browser.msie and $.browser.version > 6 and $.browser.version < 9


spec: Increate timeout for webview tests

--- spec/webview-spec.coffee
+++ spec/webview-spec.coffee
@@ -3,13 +3,13 @@
 http   = require 'http'

 describe '<webview> tag', ->
+  @timeout 10000
+
   fixtures = path.join __dirname, 'fixtures'

   webview = null
-
   beforeEach ->
     webview = new WebView
-
   afterEach ->
     document.body.removeChild webview


Added more search engines

Added more search engines

--- src/hosts.coffee
+++ src/hosts.coffee
@@ -6,9 +6,38 @@
   "facebook.com":       facebook
   "g.doubleclick.net":  doubleclick

+  "www.yahoo.com":      [search, "p"]
+  "www.msn.com":        [search, "q"]
+  "www.bing.com":       [search, "q"]
+  "www.baidu.com":      [search, "wd"]
+
+  # http://code.google.com/apis/analytics/docs/tracking/gaTrackingTraffic.html#searchEngine
   "www.daum.net":       [search, "q"]
   "www.eniro.se":       [search, "search_word"]
   "www.naver.com":      [search, "query"]
-  "www.yahoo.com":      [search, "p"]
-  "www.msn.com":        [search, "q"]
-  "www.bing.com":       [search, "q"]
+  "www.lycos.com":      [search, "query"]
+  "www.ask.com":        [search, "q"]
+  "www.altavista.com":  [search, "q"]
+  "search.netscape.com":[search, "query"]
+  "www.about.com":      [search, "terms"]
+  "www.mamma.com":      [search, "query"]
+  "www.alltheweb.com":  [search, "q"]
+  "www.voila.fr":       [search, "rdata"]
+  "search.virgilio.it": [search, "qs"]
+  "www.alice.com":      [search, "qs"]
+  "www.yandex.com":     [search, "text"]
+  "www.najdi.org.mk":   [search, "q"]
+  "www.aol.com":        [search, "q"]
+  "www.seznam.cz":      [search, "q"]
+  "www.search.com":     [search, "q"]
+  "www.wp.pl":          [search, "szukaj"]
+  "www.szukacz.pl":     [search, "q"]
+  "www.yam.com":        [search, "k"]
+  "www.pchome.com":     [search, "q"]
+  "www.kvasir.no":      [search, "q"]
+  "sesam.no":           [search, "q"]
+  "www.ozu.es":         [search, "q"]
+  "www.terra.com":      [search, "query"]
+  "www.mynet.com":      [search, "q"]
+  "www.ekolay.net":     [search, "q"]
+  "www.rambler.ru":     [search, "words"]


Save template description on modal close

--- services/web/public/coffee/ide/templates/controllers/TemplatesController.coffee
+++ services/web/public/coffee/ide/templates/controllers/TemplatesController.coffee
@@ -47,6 +47,9 @@
 				description: $scope.templateDetails.description
 				_csrf: window.csrfToken
 			})
+
+		# Save the description on modal close
+		$modalInstance.result.finally () -> updateProjectDescription()

 		$scope.publishTemplate = ->
 			$scope.state.publishInflight = true


Fix delete.

Fix delete.

--- src/scripts/goodbad.coffee
+++ src/scripts/goodbad.coffee
@@ -39,11 +39,11 @@
     @robot.brain.data.bad = @badcache
     badthing
   gooddel: ->
-    good = @goodcache = []
-    good
+    @goodcache = []
+    @robot.brain.data.good = @goodcache
   baddel: ->
-    bad = @badcache = []
-    bad
+    @badcache = []
+    @robot.brain.data.bad = @badcache

 module.exports = (robot) ->
   goodbad = new GoodBad robot


Missing controller import in routes template.

--- model/templates/public/_routes.coffee
+++ model/templates/public/_routes.coffee
@@ -1,4 +1,4 @@
-define ["../routing"], (routing) ->
+define ["../routing", "./Controller"], (routing, Controller) ->

 	new routing.RouteCollection
 		appRoutes:
@@ -8,4 +8,4 @@
 			"<%= collectionName %>-edit": url: "<%= collectionName %>/edit/:id", action: "edit"
 			"<%= collectionName %>-show": url: "<%= collectionName %>/show/:id", action: "show"
 			"<%= collectionName %>-remove": url: "<%= collectionName %>/remove/:id", action: "remove"
-		controller: new <%= collectionClassName %>Controller
+		controller: new Controller


Fix doc emission for files that start with code

--- packages/shrub-grunt/dox/dynamic.coffee
+++ packages/shrub-grunt/dox/dynamic.coffee
@@ -106,7 +106,7 @@
     else

       @hanging = [] if @commenting
-      @push "```#{@highlight}\n" if @commenting
+      @push "```#{@highlight}\n" if @commenting or not @hasWrittenCode

       if line.length is 0
         @hanging.push '' unless @commenting


Refactor

Refactor

--- packages/shrub-user/client/index.coffee
+++ packages/shrub-user/client/index.coffee
@@ -46,7 +46,7 @@
 			# ## user.isLoggedIn
 			#
 			# *Whether the current application user is logged in.*
-			service.isLoggedIn = -> service.instance().id?
+			service.isLoggedIn = -> _instance.id?

 			# ## user.login
 			#


Add keybindings for "zoom to fit"

Add keybindings for "zoom to fit"

--- keymaps/image-view.cson
+++ keymaps/image-view.cson
@@ -3,6 +3,7 @@
   'cmd-=': 'image-view:zoom-in'
   'cmd--': 'image-view:zoom-out'
   'cmd-_': 'image-view:zoom-out'
+  'cmd-9': 'image-view:zoom-to-fit'
   'cmd-0': 'image-view:reset-zoom'

 '.platform-win32 .image-view':
@@ -10,6 +11,7 @@
   'ctrl-=': 'image-view:zoom-in'
   'ctrl--': 'image-view:zoom-out'
   'ctrl-_': 'image-view:zoom-out'
+  'ctrl-9': 'image-view:zoom-to-fit'
   'ctrl-0': 'image-view:reset-zoom'

 '.platform-linux .image-view':
@@ -17,4 +19,5 @@
   'ctrl-=': 'image-view:zoom-in'
   'ctrl--': 'image-view:zoom-out'
   'ctrl-_': 'image-view:zoom-out'
+  'ctrl-9': 'image-view:zoom-to-fit'
   'ctrl-0': 'image-view:reset-zoom'


remove tours component (inadvertent check-in)

--- tutor/src/components/tour/index.cjsx
+++ tutor/src/components/tour/index.cjsx
@@ -1,22 +0,0 @@
-React = require 'react'
-Joyride = require('react-joyride').default
-
-
-Tour = React.createClass
-
-  contextTypes:
-    router: React.PropTypes.object
-
-  propTypes:
-    identifier: React.PropTypes.string.isRequired
-
-
-  render: ->
-
-    <Joyride ref="joyride"
-      debug={true}
-      steps={STEPS} type='continuous'
-      showStepsProgress={true} />
-
-
-module.export = Tour


fix: indicate first node not as decision so chain indicators "chain" each other seamlessly #347

--- app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
+++ app/assets/javascripts/oxalis/view/skeletontracing/abstract_tree_renderer.coffee
@@ -230,7 +230,13 @@
     # Decision points will definitely be drawn.

     chainCount = 0
-    while !@nodeHasComment(tree.id) and tree.children.length == 1
+
+    # skip comment check on first node
+    if tree.children.length == 1
+        tree = tree.children[0]
+        chainCount++
+
+    while tree.children.length == 1 and !@nodeHasComment(tree.id)
       tree = tree.children[0]
       chainCount++


change mouseenter and mouse leave to 'hover' for touch events on iPad

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -162,19 +162,6 @@
     items = _.map flattenMagic, mapVariableOverrides
     return _.filter items, (obj) => obj.value?.indexOf(@._id) >- 1

-
-# Template.bootstrap_magic_input.events
-#   'click .popover-pin' : ->
-#     console.log ("I'm pinned")
-#     console.log "my parent",
-#     $(@).parent('.popover-active').addClass 'pinned'
-#     # $('.popover-active').addClass ->
-#     #   'pinned'
-#     console.log "after pinned event: ", $(@).attr('class')
-#     console.log "after pinned parent: ", $(@).parent().attr('class')
-
-      #toggleCLass
-
 Template.bootstrap_magic_input.onRendered ->
   @$('.popover-label').each ->
     $elem = $(@)
@@ -191,21 +178,18 @@

         """
-  @$('.popover-label').on {
-    'mouseenter': ->
-      $(@).popover 'show'
-      $(@).addClass('popover-active')
-      console.log "started", $(@).attr('class')
-
-      $('.popover-pin').click ->
-        $(@).parents('.popover-active').toggleClass 'pinned'
-
-    'mouseleave': ->
-      if !$(@).hasClass('pinned')
-        $(@).removeClass('popover-active')
-        $(@).popover 'hide'
-  }
-
+  @$('.popover-label').hover (->
+    $(@).popover 'show'
+    $(@).addClass 'popover-active'
+    console.log "started", $(@).attr('class')
+
+    $('.popover-pin').click ->
+      $(@).parents('.popover-active').toggleClass 'pinned'
+
+  ), ->
+    if !$(@).hasClass('pinned')
+      $(@).removeClass('popover-active')
+      $(@).popover 'hide'

   # $elem = @$('.magic-child-list')


Made shrink button disappear properly

Made shrink button disappear properly

--- SingularityUI/app/views/fileBrowserSubview.coffee
+++ SingularityUI/app/views/fileBrowserSubview.coffee
@@ -8,7 +8,7 @@

     events: ->
         'click [data-directory-path]':  'navigate'
-        'click [data-action="shrink"]': 'shrink'
+        'click [data-action="shrink"]': 'startShrink'

     initialize: ({ @scrollWhenReady }) ->
         @listenTo @collection, 'sync',  @render
@@ -38,9 +38,9 @@

     scrollToTop: ->
         return if @suppressExpansion
-
+
         @$el.addClass 'expanded'
-        utils.animatedExpansion @$el
+        utils.animatedExpansion @$el, @shrink

     navigate: (event) ->
         event.preventDefault()
@@ -67,9 +67,12 @@
         if tableHeight?
             $loaderContainer.css 'height', "#{ tableHeight }px"

-    shrink: ->
+    startShrink: ->
+        @$el.trigger 'shrink'
+        @shrink()
+
+    shrink: =>
         @$el.removeClass 'expanded'
-        @$el.trigger 'shrink'
         @suppressExpansion = true

 module.exports = FileBrowserSubview


View: defaults is a function

View: defaults is a function

--- coffee/src/views/view.coffee
+++ coffee/src/views/view.coffee
@@ -26,7 +26,7 @@
     super

     # Extend options with defaults.
-    _.defaults @options, @defaults
+    _.defaults @options, @defaults()

     # If no eventhub is provided, this object is used as the eventhub.
     @eventhub = @options.eventhub || @


made editor buttons more flexible

made editor buttons more flexible

--- modules/gui/lib/editor/Editor.coffee
+++ modules/gui/lib/editor/Editor.coffee
@@ -46,30 +46,31 @@
                     view:
                         children: [ editor ]

-            for action, handler of buttons
-                do(action, handler)=>
-                    if typeof handler is 'string'
-                        handler =
-                            type: handler
-
-                    if typeof handler is 'object'
-                        editor = handler
+            for action, conf of buttons
+                do(action, conf)=>
+
+                    if typeof conf is 'function'
+                        conf =
+                            handler: conf
+
+                    @_createButton action, conf, (err, button)=>
+                        if conf.text
+                            button.text conf.text

-                        handler = (event, open)=>
-                            open editor
+                        if conf.title
+                            button.attr 'title', conf.title

-                    @_createButton action, (err, button)=>
                         @_append button.click (event)=>

-                            handler.apply @, [event, _popup]
+                            conf.handler.apply @, [event, _popup]

                             return false

         ##
         ##
         ##
-        _createButton: (action, fn)->
-            fn null, $('')
+        _createButton: (action, conf, fn)->
+            fn null, $('<button/>').addClass action


Use correct data

Use correct data

--- client/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
+++ client/app/Applications/Members.kdapplication/ContentDisplays/profileview.coffee
@@ -75,9 +75,9 @@
         cssClass     : "troll-switch"
         click        :() =>
           if KD.checkFlag('exempt', memberData)
-            @getSingleton('mainController').unmarkUserAsTroll @getData()
+            @getSingleton('mainController').unmarkUserAsTroll memberData
           else
-            @getSingleton('mainController').markUserAsTroll @getData()
+            @getSingleton('mainController').markUserAsTroll memberData

     else
       @trollSwitch = new KDCustomHTMLView


scroll window when paginating results

scroll window when paginating results

--- app/assets/javascripts/application/search.coffee
+++ app/assets/javascripts/application/search.coffee
@@ -33,6 +33,9 @@
       filter.slideDown()
       link.addClass("shown")

+  this.find("nav.pagination").click ->
+    $("html, body").animate({ scrollTop: 0 }, 600);
+
   this.on 'ajax:beforeSend', 'form[data-remote], a[data-remote]', (e, data, status, xhr) ->
     new Spinner(search_spinner_opts).spin(document.getElementById('spin'))
     $('form section.results .loading').show()


BugFix: Auf der Form-Seite konnte die waterline nicht gefunden werden, und deswegen wurden das javascript nicht weiter geladen.

--- app/assets/javascripts/backlog_entries.js.coffee
+++ app/assets/javascripts/backlog_entries.js.coffee
@@ -73,7 +73,8 @@

       this.setSumAbove(sum)

-  the_waterline = new Waterline()
+  if $('#waterline').length
+    the_waterline = new Waterline()

   $('#capacity').change ->
     if $.isNumeric($(this).val())


from now on, answers will also be delimited by semicolons

--- app/classifier/tasks/flexible-survey/editor.cjsx
+++ app/classifier/tasks/flexible-survey/editor.cjsx
@@ -341,7 +341,7 @@
       answersOrder: []
       answers: {}

-    for answer in answers.split(/\s*,\s*/).concat(__parsedExtra ? []).filter Boolean
+    for answer in answers.split(/;/).concat(__parsedExtra ? []).filter Boolean
       answerID = @makeID answer
       @props.task.questions[questionID].answersOrder.push answerID
       @props.task.questions[questionID].answers[answerID] =


wrap roots template name with apostrophe

--- lib/api/template.coffee
+++ lib/api/template.coffee
@@ -18,7 +18,7 @@
 exports.add = (args) ->
   __track('api', { name: 'template-add', template: args.name })
   sprout.add(args.name, args.uri, _.omit(args, 'name', 'uri'))
-    .then -> "template #{args.name} added"
+    .then -> "template '#{args.name}' added"

 ###*
  * Removes a template from sprout. Delegates directly to sprout's API.
@@ -30,7 +30,7 @@
 exports.remove = (args) ->
   __track('api', { name: 'template-remove', template: args.name })
   sprout.remove(args.name)
-    .then -> "template #{args.name} removed"
+    .then -> "template '#{args.name}' removed"

 ###*
  * List all templates. Delegates directly to sprout's API.
@@ -61,7 +61,7 @@
   config = global_config()
   config.set('default_template', args.name)

-  W.resolve("default template set to #{args.name}")
+  W.resolve("default template set to '#{args.name}'")

 ###*
  * Resets the global config file and removes all installed sprout templates.


updated config descriptions

updated config descriptions

--- lib/block-cursor.coffee
+++ lib/block-cursor.coffee
@@ -18,43 +18,43 @@
       enum: (key for own key of cursorTypeMap)
       order: 0
     primaryColor:
-      description: 'Primary color of the cursor'
+      description: 'Primary color of the cursor.'
       type: 'color'
       default: '#393939'
       order: 1
     primaryColorAlpha:
-      description: 'Alpha of the primary color in percentage, because the Linux color picker has no opacity'
+      description: 'Alpha of the primary color. 0 means invisible, 1 means fully opaque.'
       type: 'number'
       default: 1
       minimum: 0
       maximum: 1
       order: 2
     secondaryColor:
-      description: 'Secondary color of the cursor'
+      description: 'Secondary color of the cursor.'
       type: 'color'
       default: 'transparent'
       order: 3
     secondaryColorAlpha:
-      description: 'Alpha of the secondary color in percentage, because the Linux color picker has no opacity'
+      description: 'Alpha of the secondary color. 0 means invisible, 1 means fully opaque.'
       type: 'number'
       default: 0
       minimum: 0
       maximum: 1
       order: 4
     blinkInterval:
-      description: 'Cursor blinking interval in milliseconds. Set to 0 to disable blinking.'
+      description: 'Cursor blinking interval in milliseconds. Set to 0 to disable cursor blinking.'
       type: 'integer'
       default: 800
       minimum: 0
       order: 5
     pulseDuration:
-      description: 'Duration of the pulse transition in milliseconds, set to 0 to disable pulse'
+      description: 'Duration of the pulse transition in millisecond. Set to 0 to disable.'
       type: 'integer'
       default: 0
       minimum: 0
       order: 6
     cursorThickness:
-      description: 'Thickness of the cursor in pixels. Doesn\'t apply to "block" cursor type'
+      description: 'Thickness of the cursor in pixels. Doesn\'t apply to "block" cursor type.'
       type: 'integer'
       default: 1
       minimum: 1
@@ -65,7 +65,7 @@
       default: ''
       order: 8
     cursorLineFix:
-      description: 'Fix to render the cursor above the text when .cursor-line has a background-color. See readme for more info'
+      description: 'Fix to render the cursor above the text when the cursor line has a background-color. See readme for more info.'
       type: 'boolean'
       default: false
       order: 9
@@ -116,11 +116,13 @@
     color = @getColor 'primary'
     @updateStylesheet primarySelector, 'background-color', color
     @updateStylesheet primarySelector, 'border-color', color
+    # also apply to blink-off state if cursor blink is disabled
     if 0 is @getConfig 'blinkInterval'
       @updateStylesheet secondarySelector, 'background-color', color
       @updateStylesheet secondarySelector, 'border-color', color

   applySecondaryColor: =>
+    # use primaryColor if cursor blink is disabled
     color = @getColor (if 0 is @getConfig 'blinkInterval' then 'primary' else 'secondary')
     @updateStylesheet secondarySelector, 'background-color', color
     @updateStylesheet secondarySelector, 'border-color', color
@@ -128,6 +130,7 @@
   applyBlinkInterval: do ->
     sub = null
     (interval) ->
+      # max value for setInterval
       if interval is 0 then interval = -1 + Math.pow 2, 31
       @applySecondaryColor()
       sub?.dispose()


Update status finder keybinding to meta-B

--- keymaps/fuzzy-finder.cson
+++ keymaps/fuzzy-finder.cson
@@ -2,4 +2,4 @@
   'meta-t': 'fuzzy-finder:toggle-file-finder'
   'meta-b': 'fuzzy-finder:toggle-buffer-finder'
   'ctrl-.': 'fuzzy-finder:find-under-cursor'
-  'meta-T': 'fuzzy-finder:toggle-git-status-finder'
+  'meta-B': 'fuzzy-finder:toggle-git-status-finder'


update loading

update loading

--- lib/console/view.coffee
+++ lib/console/view.coffee
@@ -38,6 +38,7 @@
     d = document.createElement 'div'
     d.classList.add 'divider'
     if input then @addBeforeInput d else @addItem @fadeIn d
+    @updateLoading()

   cellView: (v, {icon, gutterText}={}) ->
     cell = document.createElement 'div'


Fix wrong char

Fix wrong char

--- src/browser/application.coffee
+++ src/browser/application.coffee
@@ -25,7 +25,7 @@
     @resourcePath = path.dirname path.dirname(__dirname)

     @applicationMenu = new ApplicationMenu()
-    _.defer(-> @applicationExecutor = new ApplicationExecutor())
+    _.defer(=> @applicationExecutor = new ApplicationExecutor())

     @handleEvents()


Log Linux distro name in issue body

Log Linux distro name in issue body

--- lib/user-utilities.coffee
+++ lib/user-utilities.coffee
@@ -25,6 +25,7 @@
       switch @getPlatform()
         when 'darwin' then resolve(@macVersionText())
         when 'win32' then resolve(@winVersionText())
+        when 'linux' then resolve(@linuxVersionText())
         else resolve("#{os.platform()} #{os.release()}")

   macVersionText: ->
@@ -50,6 +51,28 @@
           resolve({ProductVersion, ProductName})

       plistBuddy.onWillThrowError ({handle}) ->
+        handle()
+        resolve({})
+
+  linuxVersionText: ->
+    new Promise (resolve, reject) ->
+      @linuxVersionInfo.then (info) ->
+        return "#{os.platform()} #{os.release()}" unless info.DistroName and info.DistroVersion
+        "#{info.DistroName} #{info.DistroVersion}"
+
+  linuxVersionInfo: ->
+    new Promise (resolve, reject) ->
+      stdout = ''
+
+      lsbRelease = new BufferedProcess
+        command: 'lsb_release'
+        args: ['-ds']
+        stdout: (output) -> stdout += output
+        exit: (exitCode) ->
+          [DistroName, DistroVersion] = stdout.trim().split(' ')
+          resolve({DistroName, DistroVersion})
+
+      lsbRelease.onWillThrowError ({handle}) ->
         handle()
         resolve({})


Finished function section

Finished function section

Signed-off-by: ysgard <125fa00567d70184e4d0dc6a16b66d7bf20d7d44@gmail.com>

--- grammars/d.cson
+++ grammars/d.cson
@@ -712,22 +712,32 @@
     }
     'function-attributes': {
       'patterns': [
-
+        { 'include': '#function-attribute' },
       ]
     },
     'function-attribute': {
       'patterns': [
-
+        {
+          'match': 'nothrow|pure',
+          'name': 'storage.type.modifier.function-attribute.d'
+        },
+        {
+          'include': '#property'
+        }
       ]
     },
     'member-function-attributes': {
       'patterns': [
-
+        { 'include': '#member-function-attribute' }
       ]
     },
     'member-function-attribute': {
       'patterns': [
-
+        {
+          'match': 'const|immutable|inout|shared',
+          'name': 'storage.type.modifier.member-function-attribute'
+        },
+        { 'include': '#function-attribute' }
       ]
     },
     'default-initializer-expression': {
@@ -738,32 +748,72 @@
     },
     'function-body': {
       'patterns': [
-
+        { 'include': '#block-statement' },
+        { 'include': '#in-statement' },
+        { 'include': '#out-statement' },
+        { 'include': '#body-statement' },
       ]
     },
     'in-statement': {
       'patterns': [
-
+        {
+          'match': 'in',
+          'name': '#keyword.control.in.d'
+        },
+        { 'include': '#block-statement' }
       ]
     },
     'out-statement': {
       'patterns': [
-
+        {
+          'begin': 'out\\s*\\(',
+          'beginCaptures': { '0': { 'name': 'keyword.control.out.begin.d' } },
+          'end': '\\)',
+          'endCaptures': { '0': { 'name': 'keyword.control.out.end.d' } },
+          'patterns': [
+            { 'include': '#identifier' }
+          ]
+        },
+        {
+          'match': 'out',
+          'name': 'keyword.control.out.d'
+        },
+        { 'include': '#block-statement' }
       ]
     },
     'body-statement': {
       'patterns': [
-
+        {
+          'match': 'body',
+          'name': 'keyword.control.body.d'
+        },
+        { 'include': '#block-statement' }
       ]
     },
     'constructor': {
       'patterns': [
-
+        {
+          'match': 'this',
+          'name': 'entity.name.function.constructor.d'
+        },
+        { 'include': '#parameters' },
+        { 'include': '#member-function-attributes' },
+        { 'include': '#semi-colon' },
+        { 'include': '#function-body' },
+        { 'include': '#constructor-template' }
       ]
     },
     'constructor-template': {
       'patterns': [
-
+        {
+          'match': 'this',
+          'name': 'entity.name.function.constructor-template.d'
+        },
+        { 'include': '#template-parameters' },
+        { 'include': '#parameters' },
+        { 'include': '#member-function-attributes'},
+        { 'include': '#constraint' },
+        { 'include': '#function-body' }
       ]
     },
     'destructor': {
@@ -821,6 +871,35 @@
           'match': 'unittest',
           'name': 'entity.name.function.unit-test.d'
         },
+        { 'include': '#function-body' }
+      ]
+    },
+    'static-constructor': {
+      'patterns': [
+        { 'include': '#shared-static-constructor' }
+      ]
+    },
+    'static-destructor': {
+      'patterns': [
+        { 'include': '#shared-static-destructor' }
+      ]
+    },
+    'shared-static-constructor': {
+      'patterns': [
+        {
+          'match': '(shared\\s+)?static\\s+this\\s*\\(\\s*\\)',
+          'name': 'entity.name.class.constructor.shared-static.d'
+        },
+        { 'include': '#function-body' }
+      ]
+    },
+    'shared-static-destructor': {
+      'patterns': [
+        {
+          'match': '(shared\\s+)?static\\s+~\\s*this\\s*\\(\\s*\\)',
+          'name': 'entity.name.class.destructor.static.d'
+        },
+        { 'include': '#member-function-attributes' },
         { 'include': '#function-body' }
       ]
     },


Protected existing custom wsgi file from beeing overridden by gulp.

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -28,7 +28,73 @@

 # Gulp default task

-gulp.task 'default', ['css', 'js'], ->
+gulp.task 'default', ['django', 'css', 'js'], ->
+
+
+# Django settings and wsgi file and media directory
+
+gulp.task 'django', [
+    'createmanage'
+    'createsettings'
+    'createwsgifile'
+    'createmediadirectory'
+], ->
+
+gulp.task 'createmanage', ->
+    gulp.src path.join __dirname, projectName, 'default_manage.py'
+    .pipe template
+        outputDirectoryBaseName: path.basename outputDirectory
+    .pipe rename 'manage.py'
+    .pipe gulp.dest __dirname
+
+gulp.task 'createsettings', (callback) ->
+    settingsPath = path.join outputDirectory, 'settings.py'
+    fs.access settingsPath, (error) ->
+        if error? and error.code is 'ENOENT'
+            secretKey = ''
+            chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
+            for i in [1..50]
+                randomnumber = Math.floor Math.random() * chars.length
+                secretKey += chars[randomnumber]
+            gulp.src path.join __dirname, projectName, 'default_settings.py'
+            .pipe template
+                outputDirectoryBaseName: path.basename outputDirectory
+                projectName: projectName
+                secretKey: secretKey
+            .pipe rename 'settings.py'
+            .pipe gulp.dest outputDirectory
+            .on 'end', callback
+        else
+            gutil.log 'Task createsettings: Settings file does already exist. Skip task.'
+            callback error
+        return
+    return
+
+gulp.task 'createwsgifile', (callback) ->
+    wsgiPath = path.join outputDirectory, 'wsgi.py'
+    fs.access wsgiPath, (error) ->
+        if error? and error.code is 'ENOENT'
+            gulp.src path.join __dirname, projectName, 'default_wsgi.py'
+            .pipe template
+                outputDirectory: outputDirectory
+                outputDirectoryBaseName: path.basename outputDirectory
+            .pipe rename 'wsgi.py'
+            .pipe gulp.dest outputDirectory
+            .on 'end', callback
+        else
+            gutil.log 'Task createwsgifile: Wsgi file does already exist. Skip task.'
+            callback error
+        return
+    return
+
+gulp.task 'createmediadirectory', ['createsettings'], (callback) ->
+    fs.mkdir mediaDirectory, (error) ->
+        if error? and error.code is 'EEXIST'
+            callback()
+        else
+            callback error
+        return
+    return

 # CSS and font files
@@ -73,60 +139,3 @@
     .pipe gulpif isntSpecialFile, concat "#{projectName}-libs.js"
     .pipe gulpif productionMode, uglify()
     .pipe gulp.dest path.join staticDirectory, 'js'
-
-
-# Django settings and wsgi file and media directory
-
-gulp.task 'django', [
-    'createmanage'
-    'createsettings'
-    'createwsgifile'
-    'createmediadirectory'
-], ->
-
-gulp.task 'createmanage', ->
-    gulp.src path.join __dirname, projectName, 'default_manage.py'
-    .pipe template
-        outputDirectoryBaseName: path.basename outputDirectory
-    .pipe rename 'manage.py'
-    .pipe gulp.dest __dirname
-
-gulp.task 'createsettings', (callback) ->
-    settingsPath = path.join outputDirectory, 'settings.py'
-    fs.access settingsPath, (error) ->
-        if error? and error.code is 'ENOENT'
-            secretKey = ''
-            chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
-            for i in [1..50]
-                randomnumber = Math.floor Math.random() * chars.length
-                secretKey += chars[randomnumber]
-            gulp.src path.join __dirname, projectName, 'default_settings.py'
-            .pipe template
-                outputDirectoryBaseName: path.basename outputDirectory
-                projectName: projectName
-                secretKey: secretKey
-            .pipe rename 'settings.py'
-            .pipe gulp.dest outputDirectory
-            .on 'end', callback
-        else
-            gutil.log 'Task createsettings: Settings file does already exist. Skip task.'
-            callback error
-        return
-    return
-
-gulp.task 'createwsgifile', ->
-    gulp.src path.join __dirname, projectName, 'default_wsgi.py'
-    .pipe template
-        outputDirectory: outputDirectory
-        outputDirectoryBaseName: path.basename outputDirectory
-    .pipe rename 'wsgi.py'
-    .pipe gulp.dest outputDirectory
-
-gulp.task 'createmediadirectory', ['createsettings'], (callback) ->
-    fs.mkdir mediaDirectory, (error) ->
-        if error? and error.code is 'EEXIST'
-            callback()
-        else
-            callback error
-        return
-    return


Rename sub task : typescript-build -> ts-build

--- gulpfile.coffee
+++ gulpfile.coffee
@@ -4,7 +4,7 @@
 # Main task
 gulp.task 'default', ['build']

-gulp.task 'build', ['copy', 'typescript-build']
+gulp.task 'build', ['copy', 'ts-build']

 gulp.task 'copy', ()->
     gulp.src 'src/html/**'
@@ -12,7 +12,7 @@

 gulp.task 'watch', ['build'], ()->
     gulp.watch 'src/**/*.html', ['copy']
-    gulp.watch 'src/**/*.ts', ['typescript-build']
+    gulp.watch 'src/**/*.ts', ['ts-build']

 # Sub task
@@ -22,7 +22,7 @@
     noImplicitAny: true,
 }

-gulp.task 'typescript-build', ()->
+gulp.task 'ts-build', ()->
     gulp.src 'src/ts/**/*.ts'
     .pipe tsc tscBuildOptions
     .pipe gulp.dest 'dest/js'


update deprecation text

update deprecation text

--- lib/main.coffee
+++ lib/main.coffee
@@ -104,14 +104,14 @@
     sb = @provideStatusBar()

     addLeftTile: (args...) ->
-      Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
+      Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.")
       sb.addLeftTile(args...)
     addRightTile: (args...) ->
-      Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
+      Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.")
       sb.addRightTile(args...)
     getLeftTiles: () ->
-      Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
+      Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.")
       sb.getLeftTiles()
     getRightTiles: () ->
-      Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
+      Grim.deprecate("Use version ^1.0.0 of the status-bar Service API.")
       sb.getRightTiles()


When subscribing to a feed, while the list of entries loads the start page will be hidden. If there is a problem subscribing to the feed, the start page will be shown (regardless where the user was when he clicked on the "Add subscription" button).

--- app/assets/javascripts/feeds.js.coffee
+++ app/assets/javascripts/feeds.js.coffee
@@ -223,9 +223,11 @@
       $("#feed-entries").empty().addClass "hidden"
       $("#feed-title a").text ""
       $("#feed-title").addClass "hidden"
+      $("#start-info").addClass "hidden"
       $.post(form_url, post_data, subscription_result)
       .fail ->
         $("#loading").addClass "hidden"
+        $("#start-page").click()
         alertTimedShowHide $("#problem-subscribing")

     # Clean textfield and close modal


Redraw av booking forms vid val av klass.

--- app/assets/javascripts/forms.js.coffee
+++ app/assets/javascripts/forms.js.coffee
@@ -47,6 +47,7 @@
             $.get form.data("form-path"), params, (data) ->
                 form.html(data)
                 form.find(":input.seats:first").trigger("change")
+                location.reload()

         groupSelection.on "school-select", ->
             form.html("")


Corrige último commit: agora sim mostramos pago + rp-pago

--- app/assets/javascripts/graph.js.coffee
+++ app/assets/javascripts/graph.js.coffee
@@ -62,7 +62,7 @@

     pagamentos = pago[0].drilldown.map (element, i) ->
       rppago_amount = rppago[0].drilldown[i]["rp-pago"]
-      element.amount = element.pago + element["rp-pago"]
+      element.amount = element.pago + rppago_amount
       element

     [


enable tagging form on items#new

enable tagging form on items#new

--- app/assets/javascripts/items.js.coffee
+++ app/assets/javascripts/items.js.coffee
@@ -35,6 +35,7 @@
       false

   $(".edit_item").submit(add_tag_or_submit)
+  $("#new_item").submit(add_tag_or_submit)
   $("#tag-comp-btn").click(add_tag_or_submit)

   $(".controls").find("input[type=checkbox]").each (i, dom) ->


Add Session#sendIQ method to interface.

Add Session#sendIQ method to interface.

--- web/lib/coffeescripts/session.coffee
+++ web/lib/coffeescripts/session.coffee
@@ -44,6 +44,8 @@

   bareJid: -> @xmpp.jid.split('/')[0]

+  uniqueId: -> @xmpp.getUniqueId()
+
   avatar: (jid) ->
     card = this.loadCard(jid)
     if card && card.photo
@@ -89,7 +91,7 @@
     iq = $iq(type: 'get', to: jid, id: @xmpp.getUniqueId())
       .c('vCard', xmlns: 'vcard-temp').up()

-    @xmpp.sendIQ iq, handler, handler, 5000
+    this.sendIQ iq, handler

   parseRoster: (node) ->
     $('item', node).map(-> new Contact this ).get()
@@ -103,7 +105,7 @@
     iq = $iq(type: 'get', id: @xmpp.getUniqueId())
       .c('query', xmlns: 'jabber:iq:roster').up()

-    @xmpp.sendIQ iq, handler, handler, 5000
+    this.sendIQ iq, handler

   sendMessage: (jid, message) ->
     stanza = $msg(to: jid, from: @xmpp.jid, type: 'chat')
@@ -118,6 +120,9 @@
     else
       stanza.c('status').t status if status != 'Available'
     @xmpp.send stanza.tree()
+
+  sendIQ: (node, callback) ->
+    @xmpp.sendIQ node, callback, callback, 5000

   updateContact: (contact, add) ->
     iq = $iq(type: 'set', id: @xmpp.getUniqueId())


Add divisionAdvancedLoadMembers to members

--- src/collections/members.coffee
+++ src/collections/members.coffee
@@ -104,6 +104,13 @@

   @collections.members.queryItems('divisionSearch', params, callback)

+
+exports.divisionAdvancedLoadMembers = (params, callback) ->
+  unless params.divisionId
+    throw new TSArgsError 'teamsnap.divisionAdvancedLoadMembers',
+      "`divisionId` must be provided"
+
+  @collections.members.queryItems('advancedDivisionSearch', params, callback)

 # Helper to output a member's name, forward or reverse (reverse will use comma)
 exports.memberName = (member, reverse, forSort) ->


Catching an error when appending the rickshaw HoverDetail on iOS

--- app/assets/javascripts/power.js.coffee
+++ app/assets/javascripts/power.js.coffee
@@ -86,10 +86,13 @@

     # xAxis.render();

-    hoverDetail = new Rickshaw.Graph.HoverDetail
-      graph: graph
-      xFormatter: (x)-> moment.unix(x).format('LLL')
-      yFormatter: (y)-> "#{y} offline"
+    try
+      hoverDetail = new Rickshaw.Graph.HoverDetail
+        graph: graph
+        xFormatter: (x)-> moment.unix(x).format('LLL')
+        yFormatter: (y)-> "#{y} offline"
+    catch error
+      console.log "error: #{error}"

   # Map


Fix mysterious teaspoon bug through try/catch.

--- app/assets/javascripts/utils.js.coffee
+++ app/assets/javascripts/utils.js.coffee
@@ -19,7 +19,13 @@
     heights = $headers.find('h2').map ->
       $(this).outerHeight()

-    max = Math.max.apply(Math, heights)
+    max = null
+    try
+      max = Math.max.apply(Math, heights)
+    catch error
+      console.log "Math error, setting height to 20"
+      console.log error
+      max = 20

     $headers.css('height', max)
     $('.column-content').css('top', max)


NavigationLink: Remove drag events from link

--- client/Main/navigation/navigationlink.coffee
+++ client/Main/navigation/navigationlink.coffee
@@ -32,10 +32,6 @@

     @on "DragStarted", @bound 'dragStarted'

-    # @on "DragInAction", @bound 'dragInAction'
-
-    @on "DragFinished", @bound 'dragFinished'
-
   setState:(state = 'initial')->

     states = 'running failed loading'
@@ -48,7 +44,7 @@

     # This check is for custom items which isn't connected to an app
     # or if the item is a separator
-    return if not path or @positionChanged()
+    return false  if not path or @positionChanged()

     mc = KD.getSingleton 'mainController'
     mc.emit "NavigationLinkTitleClick",
@@ -69,18 +65,10 @@
       <cite>#{@name}</cite>
     """

-  dragInAction: (x, y)->
-    # log x, y
-
   dragStarted: (event, dragState)->

     @keepCurrentPosition()
     @setClass 'no-anim on-top'
-
-  dragFinished: (event, dragState)->
-
-    # @restoreLastPosition()
-    @unsetClass 'no-anim on-top'

   keepCurrentPosition:->


flux: user store minor change

flux: user store minor change

--- client/app/lib/flux/stores/usersstore.coffee
+++ client/app/lib/flux/stores/usersstore.coffee
@@ -56,7 +56,7 @@
   ###
   handleUpdateTeamMember: (users, { account }) ->

-    return users.set account.get('id'), account
+    return users.set account.get('_id'), account

   ###*


use existential operator

use existential operator

One of the entries is undefined in the `gos` array on my machine.

--- lib/goexecutable.coffee
+++ lib/goexecutable.coffee
@@ -220,5 +220,5 @@
   current: =>
     return @gos[0] if _.size(@gos) is 1
     for go in @gos
-      return go if go.executable is @currentgo
+      return go if go?.executable is @currentgo
     return @gos[0]


test/layouthelpers: update css paths and add callback

--- client/test/lib/helpers/layouthelpers.coffee
+++ client/test/lib/helpers/layouthelpers.coffee
@@ -23,7 +23,7 @@
     browser.pause 7500 # find a better way

-  undoSplit: (browser, shouldAssert = yes) ->
+  undoSplit: (browser, shouldAssert = yes, callback = ->) ->

     @waitForSnapshotRestore browser

@@ -39,9 +39,10 @@
         if shouldAssert
           browser.elements 'css selector', newPaneSelector, (result) ->
             assert.equal result.value.length, oldLength - 1
+            browser.pause 10, -> callback()

-  split: (browser, direction) ->
+  split: (browser, direction, callback = ->) ->

     if direction is 'vertical'
       splitButtonSelector = 'li.split-vertically'
@@ -58,11 +59,11 @@

         if length >= 1
           console.log(' ✔ Views already splitted. Ending test...')
-          browser.end()
+          callback null
         else
           @openMenuAndClick(browser, splitButtonSelector)
           browser.pause 2000

           browser.elements 'css selector', splitViewSelector, (result) ->
             assert.equal result.value.length, length + 1
-            browser.pause 2000 # wait for snapshot write
+            browser.pause 2000, -> callback null # wait for snapshot write


refactor IM

refactor IM

--- lib/input-method.coffee
+++ lib/input-method.coffee
@@ -8,7 +8,7 @@
 class InputMethod extends EventEmitter

     activated: false
-    muteEvent: false
+    mute: false

     # raw characters
     inputBuffer: ''
@@ -16,7 +16,6 @@
     outputBuffer: ''
     # mark the starting position of the input buffer when activated
     startPosition: null
-

     @trie: require './input-method/keymap.js'

@@ -51,16 +50,17 @@
         if not @activated

+            # initializations
+            log 'IM', 'activated'
             @startPosition = @core.editor.getCursorBufferPosition()
-            log 'IM', 'activated'
             @inputBuffer = ''
             @outputBuffer = ''
+            @decorator.show()
             @activated = true
-            @decorator.show()

             # monitors raw text buffer and figures out what happend
             @textBufferMarker = @core.editor.markBufferRange(new Range @startPosition, @startPosition)
-            @textBufferMarker.on 'changed', @analysisEvent
+            @textBufferMarker.on 'changed', @dispatchEvent

             # insert '\' at the cursor
             @updateOutputBuffer '\\'
@@ -74,7 +74,25 @@
             # we shall leave 1 backslash in the buffer, then deactivate
             @deactivate()

-    analysisEvent: (ev) =>
+    deactivate: ->
+
+        if @activated
+            log 'IM', 'deactivated'
+            @core.panel.deactivateIM()
+            @decorator.hide()
+            @textBufferMarker.destroy()
+            @activated = false
+
+    ##################
+    ###   Events   ###
+    ##################
+
+    muteEvent: (callback) ->
+        @mute = true
+        callback()
+        @mute = false
+
+    dispatchEvent: (ev) =>

         # see if the text buffer's resized
         range = new Range ev.newTailBufferPosition, ev.newHeadBufferPosition
@@ -82,7 +100,7 @@
         if @getRange().compare range isnt 0
             @emit 'resize', range

-        unless @muteEvent
+        unless @mute
             if textBuffer.length is 0
                 # got wiped out
                 @emit 'deactivate', range
@@ -97,15 +115,9 @@
                     @emit 'delete', range, textBuffer

-
-    deactivate: ->
-
-        if @activated
-            log 'IM', 'deactivated'
-            @core.panel.deactivateIM()
-            @decorator.hide()
-            @textBufferMarker.destroy()
-            @activated = false
+    #######################
+    ###   Text Buffer   ###
+    #######################

     getRange: ->
         new Range(@startPosition, @startPosition.translate(new Point 0, @outputBuffer.length))
@@ -113,10 +125,9 @@
     updateOutputBuffer: (text) ->

         # update text buffer
-        @muteEvent = true
-        @core.editor.getBuffer().delete @getRange()
-        @core.editor.getBuffer().insert @startPosition, text
-        @muteEvent = false
+        @muteEvent =>
+            @core.editor.getBuffer().delete @getRange()
+            @core.editor.getBuffer().insert @startPosition, text

         # update output buffer
         @outputBuffer = text


fix IM quit

fix IM quit

--- lib/input-method.coffee
+++ lib/input-method.coffee
@@ -42,9 +42,8 @@
                 # range & decorator
                 range = new Range ev.newTailBufferPosition, ev.newHeadBufferPosition

-                # console.log "@input #{@input}"
-                # console.log "#{@core.editor.getBuffer().getTextInRange(range)}"
-                # console.log @core.editor.getBuffer().getTextInRange(range) is ''
+                # log 'IM', "input buffer: #{@input}"
+                # log 'IM', "text buffer: #{@core.editor.getBuffer().getTextInRange(range)}"

                 # update input content incrementally,
                 # append only with the last character,
@@ -70,7 +69,7 @@
                         @deactivate()
                         symbol = result['>>'][0]
                         @core.editor.getBuffer().setTextInRange range, symbol
-
+                        @core.panel.deactivateIM()
                     else
                         # further key combinations are possible
                         if result['>>'].length > 0
@@ -80,7 +79,7 @@
                         else
                             @decorator.resize range

-                    @core.panel.activateIM @input, candidateKeys, candidateSymbols
+                        @core.panel.activateIM @input, candidateKeys, candidateSymbols

                 else
                     # key combination out of keymap


Fix FF WebGL issue

Fix FF WebGL issue

--- app/controllers/quick_dashboard.coffee
+++ app/controllers/quick_dashboard.coffee
@@ -53,8 +53,12 @@

     # Determine if WebGL is supported, otherwise fall back to canvas
     canvas  = document.createElement('canvas')
-    context = canvas.getContext('webgl')
-    context = canvas.getContext('experimental-webgl') unless context?
+    for name in ['webgl', 'experimental-webgl']
+      try
+        context = canvas.getContext(name)
+      catch error
+        continue
+      break if context?

     lib = if context? then 'gl' else 'canvas'


linebreaks in the rewriter ... cosmetic.

--- rewriter.coffee
+++ rewriter.coffee
@@ -67,7 +67,8 @@
         else
           @tokens.splice i, 0, ['TERMINATOR', "\n", prev[2]]
         return 2
-      else if before and before[0] is 'OUTDENT' and prev and prev[0] is 'TERMINATOR' and post and post[0] is 'TERMINATOR' and after and after[0] is 'ELSE'
+      else if before and before[0] is 'OUTDENT' and prev and prev[0] is 'TERMINATOR' and
+              post and post[0] is 'TERMINATOR' and after and after[0] is 'ELSE'
           @tokens.splice i + 1, 0, @tokens.splice(i - 2, 1)[0]
       return 1


Limit lint to the current file

Limit lint to the current file

--- lib/linter-clang.coffee
+++ lib/linter-clang.coffee
@@ -1,3 +1,4 @@
+{Range, Point, BufferedProcess, BufferedNodeProcess} = require 'atom'
 linterPath = atom.packages.getLoadedPackage("linter").path
 Linter = require "#{linterPath}/lib/linter"
 path = require 'path'
@@ -9,7 +10,7 @@

   # A string, list, tuple or callable that returns a string, list or tuple,
   # containing the command line (with arguments) used to lint.
-  cmd: 'clang++ -cc1 -fsyntax-only -fno-caret-diagnostics -fcxx-exceptions -Wall -I' + path.dirname()
+  cmd: 'clang++ -cc1 -fsyntax-only -fno-caret-diagnostics -fcxx-exceptions -Wall -I.'

   executablePath: null

@@ -17,13 +18,54 @@

   errorStream: 'stderr'

+  fileName: ''
+
   # A regex pattern used to extract information from the executable's output.
   regex: '.+:(?<line>\\d+):.+: .*((?<error>error)|(?<warning>warning)): (?<message>.*)'
+
+  lintFile: (filePath, callback) ->
+    # build the command with arguments to lint the file
+    {command, args} = @getCmdAndArgs(filePath)
+
+    file = path.basename(args[args.length - 1])
+    if file[file.length - 1] == @grammar
+      file = file.replace(".", "\\.")
+      file = file.replace("++", "\\+\\+")
+      @regex = file + ':(?<line>\\d+):.+: .*((?<error>error)|(?<warning>warning)): (?<message>.*)'
+
+    if atom.inDevMode()
+      console.log 'is node executable: ' + @isNodeExecutable
+
+    # use BufferedNodeProcess if the linter is node executable
+    if @isNodeExecutable
+      Process = BufferedNodeProcess
+    else
+      Process = BufferedProcess
+
+    # options for BufferedProcess, same syntax with child_process.spawn
+    options = {cwd: @cwd}
+
+    stdout = (output) =>
+      if atom.inDevMode()
+        console.log 'stdout', output
+      if @errorStream == 'stdout'
+        @processMessage(output, callback)
+
+    stderr = (output) =>
+      if atom.inDevMode()
+        console.warn 'stderr', output
+      if @errorStream == 'stderr'
+        @processMessage(output, callback)
+
+    new Process({command, args, options, stdout, stderr})

   constructor: (editor) ->
     super(editor)
     if editor.getGrammar().name == 'C++'
       @cmd += ' -x c++';
+      @grammar = '+'
+    if editor.getGrammar().name == 'C'
+      @grammar = 'c'

     atom.config.observe 'linter-clang.clangExecutablePath', =>
       @executablePath = atom.config.get 'linter-clang.clangExecutablePath'


:arrow_down: Revert linter-views changes for now

--- lib/linter-views.coffee
+++ lib/linter-views.coffee
@@ -62,20 +62,21 @@
       @panel.refresh(@state.scope)

   classifyMessages: (messages) ->
-    activeEditorLinter = @linter.getActiveEditorLinter()
-    @count.File = if activeEditorLinter then activeEditorLinter.getMessages().size else 0
-    @count.Project = @messages.length
+    filePath = atom.workspace.getActiveTextEditor()?.getPath()
+    @count.File = 0
+    @count.Project = 0
+    for key, message of messages
+      if message.currentFile = (filePath and message.filePath is filePath)
+        @count.File++
+      @count.Project++
     return @classifyMessagesByLine(messages)

   classifyMessagesByLine: (messages) ->
-    activeEditorLinter = @linter.getActiveEditorLinter()
-    return unless activeEditorLinter
-    row = atom.workspace.getActiveTextEditor().getCursorBufferPosition().row
+    row = atom.workspace.getActiveTextEditor()?.getCursorBufferPosition().row
     @count.Line = 0
-    activeEditorLinter.getMessages().forEach((message) =>
-      if message.currentLine = (message.range and message.range.intersectsRow(row))
+    for key, message of messages
+      if message.currentLine = (message.currentFile and message.range and message.range.intersectsRow(row))
         @count.Line++
-    )
     return messages

   renderBubble: ->


bug fix. bust now replaces file name occurences globally. it was only replacing the first instance before

--- tasks/bust.coffee
+++ tasks/bust.coffee
@@ -24,7 +24,8 @@
 					replaceFiles = grunt.file.expand file
 					if replaceFiles.length > 0
 						contents = grunt.file.read replaceFiles[0]
-						contents = contents.replace "#{base}#{ext}", newFileName
+						exp = new RegExp "#{base}#{ext}", "g"
+						contents = contents.replace exp, newFileName
 						grunt.file.write replaceFiles[0], contents

 				fs.renameSync filePath, newFilePath


- [grunt] better configuration for grunt-uncss

--- tasks/grunt-uncss.coffee
+++ tasks/grunt-uncss.coffee
@@ -7,6 +7,6 @@
   @config 'uncss',
     build:
       options:
-        stylesheets: ['../<%= file.build.style.prefixed %>']
+        htmlroot: '<%= path.build.root %>'
       files:
-        '<%= file.build.style.tidy %>': ['<%= path.build.root %>/*.html']+        '<%= file.build.style.tidy %>': ['<%= path.build.root %>/{,**/}*.html']


remove empty line

remove empty line

--- lib/minimap-view.coffee
+++ lib/minimap-view.coffee
@@ -37,7 +37,6 @@
     @isPressed = false

   initialize: ->
-
     @on 'mousewheel', @onMouseWheel
     @on 'mousedown', @onMouseDown


Rujen interlinks with referTo

Rujen interlinks with referTo

--- rujen-json-to-rdf.coffee
+++ rujen-json-to-rdf.coffee
@@ -93,6 +93,8 @@
 	addTriple(uri, a, "skos:Concept")
 	addTriple(uri, "jl:describedAt", record.uri)
 	addTriple(uri, "skos:prefLabel", literal(record.title))
+	identifier = decodeURI(record.uri.replace("http://rujen.ru/index.php/", ""))
+	addTriple(uri, "dcterms:identifier", literal(identifier))
 	for l in record.links ? []
 		addTriple(uri, "skos:related", local(l.href))
 		if l.text.length>0 then addTriple(local(l.href), "skos:altLabel", literal(l.text))
@@ -105,6 +107,20 @@
 			console.log("Unknown category: " + cat)
 	if record.abstract
 		addTriple(uri, "jl:hasAbstract", literal(record.abstract, "ru"))
+		referTo = /^([\S]+), см\. ([\S]+)\.$/.exec(record.abstract)
+		if (referTo)
+			source = referTo[1]
+			target = referTo[2]
+			targetCheck = false
+			for link in record.links ? []
+				if link.href.indexOf("/#{encodeURI(target.toLowerCase())}")>0
+					targetCheck = link.href
+			sourceCheck = identifier==source.toLowerCase()
+			if !sourceCheck || !targetCheck
+				console.log("#{source} -> #{target} (#{identifier}<>#{source.toLowerCase()}) (#{targetCheck})")
+			else
+				addTriple(uri, "jl:referTo", local(targetCheck))
+
 	else if record.empty
 		addTriple(uri, "skos:scopeNote", literal("The article describing this concept does not (yet) exist in the encyclopedia.", "en"))
 	addTriple(provURI(uri), "dcterms:created", literal(record.created, expand("xsd:dateTime")))


si un job ya esta stoppeado, que no siga preguntando

--- src/processors/job/notification.api.coffee
+++ src/processors/job/notification.api.coffee
@@ -3,8 +3,14 @@
 retry = require("bluebird-retry")
 Promise = require("bluebird")
 NodeCache = require("node-cache");
-NOTIFICATIONS_API_JOB_CACHE_TTL = parseInt(process.env.NOTIFICATIONS_API_JOB_CACHE_TTL) or 5
-jobCache = new NodeCache({ stdTTL: NOTIFICATIONS_API_JOB_CACHE_TTL });
+NOTIFICATIONS_API_JOBS_CACHE_TTL = parseInt(process.env.NOTIFICATIONS_API_JOBS_CACHE_TTL) or 5
+NOTIFICATIONS_API_STOPPED_JOB_CACHE_TTL = parseInt(process.env.NOTIFICATIONS_API_STOPPED_JOB_CACHE_TTL) or 2
+HOUR = 60 * 60
+
+#Para minimizar las requests a notifications-api, cachea unos segundos el estado del job
+jobsCache = new NodeCache({ stdTTL: NOTIFICATIONS_API_JOBS_CACHE_TTL })
+#A nivel dominio, podria ser cache sin TTL porque un job stoppeado queda asi para siempre. Pero se pone TTL de 2h para que luego libere la memoria
+stoppedJobsCache = new NodeCache({ stdTTL: NOTIFICATIONS_API_STOPPED_JOB_CACHE_TTL * HOUR });

 class NotificationsApi
   constructor: ({ @notificationApiUrl, @token, @jobId }) ->
@@ -19,21 +25,29 @@
   ), { max_tries: 3 }).catchReturn()

   jobIsStopped: () =>
+    cachedStoppedJob = stoppedJobsCache.get @jobId
+    return Promise.resolve cachedStoppedJob if cachedStoppedJob?
+    @_jobIsStopped()
+    .tap (jobIsStopped) => stoppedJobsCache.set(@jobId, job) if jobIsStopped
+
+  _jobIsStopped: () =>
     @_fetchJob()
     .then (job) => job.stopped

   _fetchJob: () =>
-    cachedJob = jobCache.get @jobId
+    cachedJob = jobsCache.get @jobId
     return Promise.resolve cachedJob if cachedJob?
     @_doFetchJob()
-    .tap (job) => jobCache.set @jobId, job
+    .tap (job) => jobsCache.set @jobId, job

   _doFetchJob: () =>
-    retry => request {
+    __fetchJob = () => request({
       url: "#{ @notificationApiUrl }/jobs/#{ @jobId }"
       method: "GET"
       headers: { authorization: @token }
-    }
+    }).promise()
+
+    retry __fetchJob, throw_original: true

   _makeRequest: (body) =>
     request {


Another return added to clean up the compiled js

--- coffee/codesearch.coffee
+++ coffee/codesearch.coffee
@@ -26,6 +26,7 @@
             else
                 resultsService.setResults []
             return
+        return

 controller.factory "resultsService", ->


fix the fix

fix the fix

--- src/scripts/modules/media/body/body.coffee
+++ src/scripts/modules/media/body/body.coffee
@@ -1,3 +1,4 @@
+
 define (require) ->
   $ = require('jquery')
   embeddablesConfig = require('cs!configs/embeddables')
@@ -177,7 +178,7 @@

           # Convert links to maintain context in a book, if appropriate
           if @model.isBook()
-            $temp.find('a:not([data-type=footnote-number]):not(href^="#")').each (i, el) =>
+            $temp.find('a:not([data-type=footnote-number]):not([href^="#"])').each (i, el) =>
               $el = $(el)
               href = $el.attr('href')


slight hear value change

slight hear value change

--- scripts/TM-Github.coffee
+++ scripts/TM-Github.coffee
@@ -42,7 +42,7 @@
       res.reply 'Request sent, take a look at https://ci.appveyor.com/project/DinisCruz/tm-4-0-build where a new build should have just started'
       res.reply 'In a bit, the compiled node_modules will be at the https://github.com/tm-build/TM_4_0_Windows repo'

-  robot.hear /show(.*)tm graphs/i,(res) ->
+  robot.hear /show(.*)tm graph/i,(res) ->
     res.reply 'http://4.bp.blogspot.com/-7g82Ltxt7wI/VLqbd1CLOyI/AAAAAAAAKcc/kisGogPKcmo/s1600/Screen%2BShot%2B2014-11-02%2Bat%2B16.49.16.png'
     res.reply 'http://2.bp.blogspot.com/-PUi2erHHCKA/VLqbmSw69pI/AAAAAAAAKeY/fWA8nqSam8k/s1600/Screen%2BShot%2B2014-11-18%2Bat%2B10.57.13.png'
     res.reply 'http://3.bp.blogspot.com/-WVlMYyq0B0g/VLqbshL9y1I/AAAAAAAAKfw/x8fqum-5gnc/s1600/Screen%2BShot%2B2014-11-18%2Bat%2B14.31.59.png'


remove excess line

remove excess line

--- lib/tab-bar-view.coffee
+++ lib/tab-bar-view.coffee
@@ -178,7 +178,6 @@
     @closeTab(tab)
     pathsToOpen = [atom.project.getPaths(), itemURI].reduce ((a, b) -> a.concat(b)), []
     atom.open({pathsToOpen: pathsToOpen, newWindow: true, devMode: atom.devMode, safeMode: atom.safeMode})
-    # tab.style.maxWidth = '' for tab in @getTabs()

   splitTab: (fn) ->
     if item = @querySelector('.right-clicked')?.item


:lipstick:

:lipstick:

--- lib/text-objects.coffee
+++ lib/text-objects.coffee
@@ -25,7 +25,10 @@
       while pos.column >= 0
         if line[pos.column] == @char
           if pos.column == 0 or line[pos.column - 1] != '\\'
-            return if @isStartQuote(pos) then pos else @lookForwardOnLine(start)
+            if @isStartQuote(pos)
+              return pos
+            else
+              return @lookForwardOnLine(start)
         -- pos.column
       pos.column = -1
       -- pos.row
@@ -33,16 +36,15 @@

   isStartQuote: (end) ->
     line = @editor.lineForBufferRow(end.row)
-    numQuotes = line.substring(0,end.column + 1).replace('\''+@char,'').split(@char).length-1
-    return numQuotes % 2
-
+    numQuotes = line.substring(0, end.column + 1).replace( "'#{@char}", '').split(@char).length - 1
+    numQuotes % 2

   lookForwardOnLine: (pos) ->
     line = @editor.lineForBufferRow(pos.row)

-    idx = line.substring(pos.column).indexOf @char
-    if idx >= 0
-      pos.column += idx
+    index = line.substring(pos.column).indexOf(@char)
+    if index >= 0
+      pos.column += index
       return pos
     null


Update subscription help in index.coffee

Change URL and contact details.

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -886,7 +886,7 @@
       if err?
         return resp.send 404, error: "Couldn't find your subscription"
       if not currentSubscription
-        return resp.send 404, error: "You do not have a recurly subscription. Please get one at https://scraperwiki.com/pricing"
+        return resp.send 404, error: "You do not have a recurly subscription. Please get one at https://app.quickcode.io/pricing"

       currentSubscription.upgrade req.params.plan, (err, recurlyResp) ->
         if err?
@@ -912,7 +912,7 @@
         return resp.send 404, error: err.error

       if not recurlyAdminUrl
-        return resp.send 404, error: "You do not have a recurly hosted_login_token. Contact hello@scraperwiki.com for help."
+        return resp.send 404, error: "You do not have a recurly hosted_login_token. Contact hello@quickcode.io for help."
       resp.writeHead 302,
         location: recurlyAdminUrl
       resp.end()


Skip ident check if using local database

This is done so that the ident check doesn't prevent local testing
of the status API endpoint. It's fail safe because it can only ever
touch the database named localhost/cu-test, which is never true for
production.

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -652,6 +652,15 @@
 # can post to anyone's status).
 # throttleRoute = throttle.throttle (req) -> req.ident

+if process.env.CU_DB == "mongodb://localhost/cu-test"
+  console.warn "Skipping ident because local mongo database is being used"
+  localhostSkipIdent = (req, res, next) ->
+    if req.query.ident
+      req.ident = req.query.ident
+    return next()
+
+  checkIdent = localhostSkipIdent
+
 app.post '/api/status/?', checkIdent, postStatus
 app.delete '/api/status/?', checkIdent, deleteStatus


- remove unessary codes

- remove unessary codes

--- src/index.coffee
+++ src/index.coffee
@@ -28,15 +28,9 @@

   constructor: ->return super

-  getEngine: ->
-    result = @_engine
-    unless result
-      result = templateEngines.get()
-      @_engine = result
-    result
-
   _templateFilecallback: (aFile, done)->
     (err, contents)->
+      ### !pragma coverage-skip-next ###
       return done err if err
       vEngine = extend {}, aFile.engine
       vEngine.template = contents


fix: writeFile callback

fix: writeFile callback

--- src/index.coffee
+++ src/index.coffee
@@ -103,7 +103,9 @@
   # operations file
   #
   opline_log: (opline) ->
-    fs.writeFile(@_fileDescriptorOP, opline, @_fileEncoding)
+    fs.writeFile(@_fileDescriptorOP, opline, @_fileEncoding, (err, result) ->
+      if err then console.error(err)
+    )

   ##
   # Split an opline to the following format: [op, key, value]


Support table align

Support table align

--- src/index.coffee
+++ src/index.coffee
@@ -1,12 +1,13 @@
 mdast = require 'mdast'

 $ = React.createElement
-toChildren = (node, parentKey) ->
+toChildren = (node, parentKey, tableAlign = []) ->
   return (for child, i in node.children
-    compile(child, parentKey+'_'+i))
+    align = tableAlign[i]
+    compile(child, parentKey+'_'+i, align))

 sanitize = null
-compile = (node, parentKey='_start') ->
+compile = (node, parentKey='_start', tableAlign = null) ->
   key = parentKey+'_'+node.type

   switch node.type
@@ -40,12 +41,12 @@
     when 'blockquote' then $ 'blockquote', {key}, toChildren(node, key)

     # Table
-    when 'table'       then $ 'table', {key}, toChildren(node, key)
+    when 'table'       then $ 'table', {key}, toChildren(node, key, node.align)
     when 'tableHeader'
       $ 'thead', {key}, [
         $ 'tr', {key: key+'-_inner-tr'}, node.children.map (cell, i) ->
           k = key+'-th'+i
-          $ 'th', {key: k}, toChildren(cell, k)
+          $ 'th', {key: k, style: {textAlign: tableAlign ? 'left'}}, toChildren(cell, k)
       ]

     when 'tableRow'
@@ -53,7 +54,7 @@
       $ 'tbody', {key}, [
         $ 'tr', {key: key+'-_inner-td'}, node.children.map (cell, i) ->
           k = key+'-td'+i
-          $ 'td', {key: k}, toChildren(cell, k)
+          $ 'td', {key: k, style: {textAlign: tableAlign ? 'left'}}, toChildren(cell, k)
       ]
     when 'tableCell'   then $ 'span', {key}, toChildren(node, key)


use jitsu.com

use jitsu.com

--- models/deploy.coffee
+++ models/deploy.coffee
@@ -22,6 +22,7 @@
   @platform =
     if inNetwork v, '127.0.0.1/24'
       'localdomain'
+      ###
     else if inNetwork v, '72.2.126.0/23'
       'joyent'
     else if inNetwork v, """
@@ -39,7 +40,8 @@
                          178.79.128.0/18
                          """
       'linode'
-    else if @hostname?.match(/\.nodejitsu\.com$/)
+      ###
+    else if @hostname?.match(/\.jitsu\.com$/)
       'nodejitsu'
   @platform?
 , 'not production'


move min/max value update to processLightcurve method


Remove debugging code

Remove debugging code

--- components/Render.coffee
+++ components/Render.coffee
@@ -39,8 +39,6 @@
       else
         bindings = data

-      console.log "*** AAA: #{enclosing} | #{@enclosing} | #{@enclosingAll}"
-      console.log bindings
       # Apply template
       output = transparency.render(root, bindings).outerHTML


Shave 12 bytes off the CoffeeScript quine

--- CoffeeScript/quine.coffee
+++ CoffeeScript/quine.coffee
@@ -1,4 +1,4 @@
-n = String.fromCharCode(10)
-q = String.fromCharCode(39)
-x = 'n = String.fromCharCode(10)%sq = String.fromCharCode(39)%sx = %s%s%s%sconsole.log x, n, n, q, x, q, n'
+n=String.fromCharCode(10)
+q=String.fromCharCode(39)
+x='n=String.fromCharCode(10)%sq=String.fromCharCode(39)%sx=%s%s%s%sconsole.log x, n, n, q, x, q, n'
 console.log x, n, n, q, x, q, n


[client][framework][core] added pixel size support to lazy load threshold, now if threshold is greater than 1 it will be treated as pixel value

--- client/Framework/core/KDView.coffee
+++ client/Framework/core/KDView.coffee
@@ -495,6 +495,7 @@
     if @getSubViews()
       (subView.parentDidResize(parent,event) for subView in @getSubViews())

+  # if threshold is greater than 1 it is treated as pixel value
   setLazyLoader:(threshold=.75)->
     @getOptions().bind += ' scroll' unless /\bscroll\b/.test @getOptions().bind
     view = @
@@ -502,9 +503,17 @@
       lastRatio = 0
       (event)->
         el = view.$()[0]
-        ratio = (el.scrollTop + view.getHeight()) / el.scrollHeight
-        if ratio > lastRatio and ratio > threshold
+        {scrollHeight, scrollTop} = el
+
+        dynamicThreshold = if threshold > 1
+        then (scrollHeight - threshold) / scrollHeight
+        else threshold
+
+        ratio = (scrollTop + view.getHeight()) / scrollHeight
+
+        if dynamicThreshold > ratio > lastRatio
           @emit 'LazyLoadThresholdReached', {ratio}
+
         lastRatio = ratio

   bindEvents:($elm)->


KDView: code style

KDView: code style

--- client/Framework/core/KDView.coffee
+++ client/Framework/core/KDView.coffee
@@ -379,7 +379,7 @@
 # ADD/DESTROY VIEW INSTANCES
 # #

-  destroy:->
+  destroy: ->
     # instance destroys own subviews
     @destroySubViews()  if @getSubViews().length > 0

@@ -398,7 +398,7 @@
     # and delete instance from KD.instances registry
     super

-  destroySubViews:()->
+  destroySubViews: ->
     # (subView.destroy() for subView in @getSubViews())

     for subView in @getSubViews().slice()


fire an action when url /send-invites

fire an action when url /send-invites

--- client/home/lib/myteam/index.coffee
+++ client/home/lib/myteam/index.coffee
@@ -37,6 +37,11 @@
     super options, data

     kd.singletons.groupsController.ready @bound 'putViews'
+
+
+  handleAction: (action) ->
+
+    TeamFlux.actions.focusSendInvites yes  if action is 'send-invites'

   putViews: ->


Added missing .end()

Added missing .end()

--- client/test/lib/ide/terminal.coffee
+++ client/test/lib/ide/terminal.coffee
@@ -157,6 +157,8 @@
       .setValue                 editSelector, [name,browser.Keys.RETURN]
       .waitForElementNotPresent editSelector, 20000
       .assert.containsText      tabNameSelector, name #Assertion
+      .end()
+

   # this test is deprecated with persistent storage
   # openOldTerminalSession: (browser) ->


fix files being shown only because of caching

--- client/views/pages/wiki/wiki.coffee
+++ client/views/pages/wiki/wiki.coffee
@@ -8,7 +8,9 @@
 		project = Projects.findOne FlowRouter.getParam('projectId')
 		project?.wiki?.tabs

-	files: -> Files.find()
+	files: ->
+		projectId = FlowRouter.getParam('projectId')
+		Files.find projectId: projectId

 	projectId: -> FlowRouter.getParam('projectId')


Main: minor whitespace cleanup

Main: minor whitespace cleanup

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -34,7 +34,6 @@

     KD.registerSingleton "mainController",            this

-    # if KD.useNewKites
     KD.registerSingleton "kontrol",                   new KodingKontrol

     KD.registerSingleton 'appManager',   appManager = new ApplicationManager
@@ -68,7 +67,6 @@
       KD.registerSingleton 'widgetController',        new WidgetController
       KD.registerSingleton 'onboardingController',    new OnboardingController
       KD.registerSingleton "socialapi",               new SocialApiController
-      # KD.registerSingleton "kontrol",                 new Kontrol

       @emit 'AppIsReady'

@@ -248,8 +246,8 @@
     idleDetector = new IdleUserDetector { threshold }
     @forwardEvents idleDetector, ['userIdle', 'userBack']

+
   startCachingAssets:->
-

     KD.utils.defer ->


Admin: Stacks: Added createOnboardingView method with custom scroll view support.

--- client/admin/lib/views/groupstacksettings.coffee
+++ client/admin/lib/views/groupstacksettings.coffee
@@ -3,6 +3,7 @@

 InitialView     = require './stacks/initialview'
 DefineStackView = require './stacks/definestackview'
+GetStartedView  = require './stacks/onboarding/getstartedview'

 module.exports = class GroupStackSettings extends kd.View
@@ -12,6 +13,14 @@
     curryIn options, cssClass: 'stacks stacks-v2'

     super options, data
+
+
+  createOnboardingView: ->
+
+    @scrollView = new kd.CustomScrollView
+    @scrollView.wrapper.addSubView new GetStartedView
+
+    @addSubView @scrollView

   viewAppended: ->


Move checkpoint more forward

Move checkpoint more forward

--- app/scripts/lazerscripts/stage1.coffee
+++ app/scripts/lazerscripts/stage1.coffee
@@ -66,6 +66,7 @@
         @placeSquad Game.Scripts.Stage1BossPopup
       )
       @setSpeed 100
+      @checkpoint @checkpointMidStage('Skyline', 450000)

       @setPowerupPool 'damageb', 'damage', 'aimb', 'rapidb', 'damage', 'damageb'
       @cloneEncounter()
@@ -83,8 +84,6 @@
       )

       @gainHeight(300, duration: 4000)
-
-      @checkpoint @checkpointMidStage('Skyline', 450000)

       # TODO: Mix this up with helicopters and jet fighters when
       # graphics are in


Fix unit test

Fix unit test

--- services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee
+++ services/web/test/UnitTests/coffee/Collaborators/CollaboratorsHandlerTests.coffee
@@ -30,7 +30,10 @@
 		describe "with project", ->
 			beforeEach ->
 				@Project.findOne = sinon.stub()
-				@Project.findOne.withArgs({_id: @project_id}, {owner_ref: 1, collaberator_refs: 1, readOnly_refs: 1}).yields(null, @project = {
+				@Project.findOne.withArgs(
+					{_id: @project_id},
+					{owner_ref: 1, collaberator_refs: 1, readOnly_refs: 1, tokenAccessReadOnly_refs: 1, tokenAccessReadAndWrite_refs: 1}
+				).yields(null, @project = {
 					owner_ref: [ "owner-ref" ]
 					readOnly_refs: [ "read-only-ref-1", "read-only-ref-2" ]
 					collaberator_refs: [ "read-write-ref-1", "read-write-ref-2" ]


Handle non-printable characters

Handle non-printable characters

--- demos/visualizer-lazy/Grid.coffee
+++ demos/visualizer-lazy/Grid.coffee
@@ -9,13 +9,16 @@
 		PATHINDICATOR: 'rgb(24, 155, 230)'
 		FONT: '#EDF'

+	@FONTS =
+		NORMAL: '20px Consolas, monospace'
+		SMALL: '14px Consolas, monospace'
+
 	@canvas.width = @playfield.width * @cellSize
 	@canvas.height = @playfield.height * @cellSize

 	@con2d = @canvas.getContext '2d'
 	@con2d.textAlign = 'center'
 	@con2d.textBaseline = 'middle'
-	@con2d.font = '20px Consolas, monospace'
 	@con2d.strokeStyle = '#FFF'
 	@con2d.lineWidth = 0.8

@@ -146,11 +149,22 @@
 	@con2d.fillStyle = @COLORS.FONT
 	for i in [0...@playfield.width]
 		for j in [0...@playfield.height]
-			@con2d.fillText(
-				(@playfield.getAt i, j),
-				i * @cellSize + @cellSize / 2,
-				j * @cellSize + @cellSize / 2
-			)
+			charRaw = @playfield.getAt i, j
+			charCode = charRaw.charCodeAt 0
+
+			if charCode > 0
+				charPretty = if 32 <= charCode <= 126
+					@con2d.font = @FONTS.NORMAL
+					charRaw
+				else
+					@con2d.font = @FONTS.SMALL
+					"##{charCode}"
+
+				@con2d.fillText(
+					charPretty
+					i * @cellSize + @cellSize / 2
+					j * @cellSize + @cellSize / 2
+				)

 	#path indicators
 		for hitRegion in @hitRegions


Fix timing issues in logging test even more.

--- test/logging_test.coffee
+++ test/logging_test.coffee
@@ -23,26 +23,20 @@
         assert.equal(messages[0].verb, 'create')
         assert.equal(messages[0].message, 'tmp/js/logging.js')

-    assertBundle = (index) ->
-        assert.equal(messages[index].verb, 'bundle')
-        assert.equal(messages[index].message, 'public/js/logging.bundle.js')
+    assertMessage = (verb, message) ->
+        found = false
+        for pair in messages
+            found = true if pair.verb == verb && pair.message == message
+        assert found, "Did not find log message: #{verb}: #{message}"

     it 'Logs the main source bundling', ->
-        # Order may flip around
-        assertBundle(if messages[1].verb == 'bundle' then 1 else 2)
-
-    assertMinify = (index) ->
-        assert.equal(messages[index].verb, 'minify')
-        assert.equal(messages[index].message, 'tmp/js/someLibrary.min.js')
+        assertMessage('bundle', 'public/js/logging.bundle.js')

     it 'Logs the library minification', ->
-        # Order may flip around
-        assertMinify(if messages[1].verb == 'bundle' then 2 else 1)
+        assertMessage('minify', 'tmp/js/someLibrary.min.js')

     it 'Logs the CoffeeScript minification', ->
-        assert.equal(messages[3].verb, 'minify')
-        assert.equal(messages[3].message, 'tmp/js/logging.min.js')
+        assertMessage('minify', 'tmp/js/logging.min.js')

     it 'Logs the minified source bundling', ->
-        assert.equal(messages[4].verb, 'bundle')
-        assert.equal(messages[4].message, 'public/js/logging.bundle.min.js')
+        assertMessage('bundle', 'public/js/logging.bundle.min.js')


more coffee style

more coffee style

--- app/assets/javascripts/dash.js.coffee
+++ app/assets/javascripts/dash.js.coffee
@@ -5,19 +5,17 @@
 @slider_setup = ->
   @exchanges = []
   $('ul.exchanges li').each (idx,el)->
-      exg = spans_to_json(el)
-      console.log(exg)
-      dust.render('exchange-button', exg, (err,out)->
-        if err
-          console.log(err)
-        else
-          $(el).replaceWith(out)
-      )
-      exchanges.push(exg)
+    exg = spans_to_json(el)
+    dust.render 'exchange-button', exg, (err,out)->
+      if err
+        console.log(err)
+      else
+        $(el).replaceWith(out)
+    exchanges.push(exg)

 spans_to_json = (el)->
   record = {}
   $("span", el).each (idx,el)->
-                       j = $(el)
-                       record[j.attr('key')] = j.html()
+    j = $(el)
+    record[j.attr('key')] = j.html()
   record


js local fix

js local fix

--- app/assets/javascripts/i18n.js.coffee
+++ app/assets/javascripts/i18n.js.coffee
@@ -18,3 +18,12 @@
     checked:
       pre: 'Je hebt '
       post: ' entries geselecteerd'
+  de:
+    date:
+      today: 'today'
+      yesterday: 'yesterday'
+      future: 'days in the future'
+      dateAgo: 'days ago'
+    checked:
+      pre: 'You\'ve marked '
+      post: ' entries to be billed'


FIX: Browser back button breaks "Loading..." visual

From: https://github.com/rails/turbolinks/
page:change the page has been parsed and changed to the new version and on DOMContentLoaded

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -56,7 +56,7 @@

 document.addEventListener("page:fetch", startSpinner)
 document.addEventListener("page:fetch", unbindEvents)
-document.addEventListener("page:receive", stopSpinner)
+document.addEventListener("page:change", stopSpinner)

 $ ->
   # Click a .one_click_select field, select the contents


get hovered bit, first attempt

get hovered bit, first attempt

--- app/assets/javascripts/root.js.coffee
+++ app/assets/javascripts/root.js.coffee
@@ -1,59 +1,66 @@

-	root = global ? window
+root = global ? window

-	# ############## SETUP  ***********************
-	# soundManager.setup
-	#   url: "/swf/"
-	#   flashVersion: 8 # optional: shiny features (default = 8)
-	#   useFlashBlock: false
+# ############## SETUP  ***********************
+# soundManager.setup
+#   url: "/swf/"
+#   flashVersion: 8 # optional: shiny features (default = 8)
+#   useFlashBlock: false

-	# # UI config for sound bit
-	# threeSixtyPlayer.config =
-	#   playNext: false # stop after one sound, or play through list until end
-	#   autoPlay: false # start playing the first sound right away
-	#   allowMultiple: true # let many sounds play at once (false = one at a time)
-	#   loadRingColor: "#ccc" # amount of sound which has loaded
-	#   playRingColor: "#000" # amount of sound which has played
-	#   showHMSTime: true
-	#   useEQData: true
-	#   useWaveformData: true
-	#   backgroundRingColor: "#eee" # "default" color shown underneath everything else
-	#   animDuration: 500
-	#   animTransition: Animator.tx.bouncy # http://www.berniecode.com/writing/animator.html
+# # UI config for sound bit
+# threeSixtyPlayer.config =
+#   playNext: false # stop after one sound, or play through list until end
+#   autoPlay: false # start playing the first sound right away
+#   allowMultiple: true # let many sounds play at once (false = one at a time)
+#   loadRingColor: "#ccc" # amount of sound which has loaded
+#   playRingColor: "#000" # amount of sound which has played
+#   showHMSTime: true
+#   useEQData: true
+#   useWaveformData: true
+#   backgroundRingColor: "#eee" # "default" color shown underneath everything else
+#   animDuration: 500
+#   animTransition: Animator.tx.bouncy # http://www.berniecode.com/writing/animator.html
+
+
+
+root.httpCodes =
+	movedPermanently: 301
+	pageNotFound: 404
+	serverError: 500

-	root.httpCodes =
-		movedPermanently: 301
-		pageNotFound: 404
-		serverError: 500
+root.showMenu = ->
+	console.log "showMenu call"

-
-	root.showMenu = ->
-		alert
+# TODO: better way to get the hovered element?

+root.showNotification = (message, type, id) ->

-	root.showNotification = (message, type, id) ->
-
-		$.pnotify
-		  text: message
-		  shadow: false
-		  animation: 'fade'
-		  type: 'info'
+	$.pnotify
+	  text: message
+	  shadow: false
+	  animation: 'fade'
+	  type: 'info'

-	Mousetrap.bind ["command"], (e, combo) ->
-	  m = showNotification "pressed : " + combo + " : modifier ... ", "warning", e.id
-	  e.preventDefault
+# TODO: refactor into function that handles shortcuts
+Mousetrap.bind ["command"], (e, combo) ->
+	m = showNotification "pressed : " + combo + " : modifier ... ", "warning", e.id
+	# console.log root.hoveredElement
+	$('body').on "mousemove", (event) ->
+		root.hoveredElement = $(event.target).closest('.bit')
+		console.log "#{ root.hoveredElement.attr('id') } : x: #{ event.pageX } y: #{ event.pageY }"
+		$('body').off "mousemove"

-	Mousetrap.bind ["command+n"], (e, combo) ->
-	  m = showNotification "pressed : " + combo + " : New bit", "warning", e.id
-	  e.preventDefault
+Mousetrap.bind ["n b"], (e, combo) ->
+	m = showNotification "pressed : " + combo + " : New bit", "warning", e.id
+	e.preventDefault


remove old init commented code

remove old init commented code

--- app/assets/javascripts/tahi.js.coffee
+++ app/assets/javascripts/tahi.js.coffee
@@ -1,10 +1,6 @@
 window.Tahi ||= {}

 Tahi.init = ->
-  # Tahi.papers.init()
-  # Tahi.overlay.init()
-  # Tahi.flowManager.init()
-  # Tahi.manuscriptManager.init(location.href)
   Tahi.overlays.newCard.init()
   for i of Tahi
     (Tahi[i].init||->).call(Tahi[i])


Use predefined value

Use predefined value

--- app/assets/javascripts/vote.js.coffee
+++ app/assets/javascripts/vote.js.coffee
@@ -15,6 +15,7 @@
             "Great",
             "Favorite"
         ]
+        @votes = {}

     registerVotes: (votes) ->
         @posts = votes
@@ -50,7 +51,7 @@
                 star.addClass 'star-set-after'
             i++
         jQuery('#post-score-'+@current_post.id).html score
-        jQuery('#favorited-by').html Favorite.link_to_users @votes["3"]
+        jQuery('#favorited-by').html Favorite.link_to_users @votes[@v.fav]
         false

@@ -82,7 +83,7 @@

     $(".vote-up").on 'click', ->
         current_score = vote.getVote()
-        return false if current_score == 3
+        return false if current_score == vote.v.fav
         vote.set current_score + 1
         false


Improve source-level documentation along with some restructuring of the code We rewrite the comments so that we will get a better reading experience after they are parsed by docco, the documentation tool. Also, some bits of the code have been shuffled around so that the flow of comments will read better.

--- src/eg/fieldmapper.coffee
+++ src/eg/fieldmapper.coffee
@@ -1,35 +1,52 @@
-# fieldmapper.coffee
+# We define a module to provide services to resolve
+# inconsistencies between the server and client
+# in how they represent values of a gvein data type
+# and how they structure certain data objects.
 #
-# Field mapping functions:
+# For example, the non-Javascript server will represent a number as 1 or '1'
+# whereas the Javascript client will represent it as the *Number* object.
 #
-# These turn the raw data returned by the server into objects which are easily
-# manipulated, and back again.
+# Moreover, the server will represent a data object
+# as values in an array, with the position of a value in the array corresponding to the field,
+# whereas the client will need to represent the values in an object with the field names.
+# When we convert an array into an object, it is known as 'fieldmapping'
+# and will be accomplished by the *fieldmap* function.
+# The converse operation is accomplished by the *mapfield* function.

-# Synchronously load dependents that are not jModules.
+# The module is dependent on Javascript files that are not packaged as modules.
+# Here, we synchronously load them into the module for use.
+# *fmall* will supply a global object *fmclasses* that defines
+# a mapping between position indices and field names.
+# *fm_datatypes* will supply another global object that maps between fields and data types.
 jMod.include 'dojo.fieldmapper.fmall', 'eg.fm_datatypes'

 module 'eg.fieldmapper', ->
-	that = @

-	identity = (x) -> x
-
-	# Sometimes a value in a fieldmapped object is null. If so, we don't
-	# want to cast it, because we don't want a value of, e.g., "null"
+	# ***
+	# Define a helper function to guard a given function
+	# from being applied against a null argument.
 	guard_null = (fn) -> (x) -> if x? then fn.apply @, arguments else x

-	# We don't want to try to cast an error object
-	guard_ret = (fn) ->
-
+	# Define a helper function to guard a given function
+	# from being applied against an argument that is an *ilsevent* object.
+	guard_ilsevent = (fn) ->
 		guard_null (x) ->
 			if typeof x is 'object' and (x.ilsevent isnt undefined or (x[0] and x[0].ilsevent isnt undefined))
 				return x
 			else
 				return fn.apply @, arguments

-	@ret_types = {
-		'number': guard_ret Number
-		'string': guard_ret String
-		'search': guard_ret (x) ->
+	# Define a public object to cast data types.
+	@ret_types =
+
+		# We type cast a given input as a number or string
+		# except if it is a null value.
+		'number': guard_ilsevent Number
+		'string': guard_ilsevent String
+
+		# We type cast the number values in a given *search* object
+		# except if it is an *ilsevent* object.
+		'search': guard_ilsevent (x) ->
 			(x[f] = Number x[f]) for f in [
 				'count'
 				'superpage_size'
@@ -45,23 +62,49 @@
 					'total'
 				] when x.superpage_summary[f] isnt undefined
 			return x
-		'prefs': guard_ret (x) ->
+
+		# We type cast the number values in a given *prefs* object
+		# except if it is an *ilsevent* object.
+		'prefs': guard_ilsevent (x) ->
 			(x[p] = Number x[p]) for p in [
 				'opac.hits_per_page'
 				'opac.default_search_location'
 				'opac.default_search_depth'
 			] when x[p]
 			return x
-	}
+
+	# ***
+	# Define a public function to 'flatten' a given tree of objects.
+	# *opac_visible* objects are returned in a list.
+	# > FIXME: this could be integrated into *ret_types()* as another type cast operation,
+	@flatten_tree = (o) ->
+
+		_flatten_tree = (os) ->
+			a = []
+			$.each os, (n, o) ->
+				return [] unless o.opac_visible
+				a.push o
+				(a.push v) for k, v of _flatten_tree o.children if o.children
+				delete o.children
+			return a
+
+		# We need to deep-copy the object,
+		# otherwise the version in the cache will be modified.
+		_flatten_tree $.extend true, {}, [o]

-	typemap = {
-		'':        identity
+
+	# ***
+	# We define a helper object for use by the public *fieldmap* function
+	# to cast data types for client use.
+	# We have to guard against casting only null values.
+	typemap =
+		'':        (x) -> x
 		'fm':      guard_null (x) => if typeof x is 'object' then @fieldmap x else x
 		'number':  guard_null Number
 		'string':  guard_null String

-		# Use Date objects to represent dates in the app.
+		# > We will use Date objects to represent dates in the client.
 		# For the most part though,
 		# dates are displayed to the user as strings,
 		# and possibly modified by the user as strings
@@ -76,32 +119,37 @@
 				when 't', '1' then true
 				when 'f', '0' then false
 				else !!x
-	}

-	# Map an array of values to an object using a map between position indices and field names.
-	# If we find an empty array or object, we will return it.
-	# The mapping is shown by the following ASCII diagram.
-	#	x                 =  { '__c': mapname, '__p': [    1,     2,     3 ] }
-	#	fmclasses         =  {        mapname:        ['a',   'b',   'c'   ] }
-	#	y = fieldmap(x)   #  {                         'a':1, 'b':2, 'c':3   }
+	# ***
+	# Define a function to convert a class hint
+	# and an array of property values into an object.
+	# The function uses *fmclasses*,
+	# a mapping between position indices and field names.
+	# If we find an empty array or object, we will simply return it.
+	_fieldmap = (c, p) ->
+		o = {}
+		if (ts = fm_datatypes[c])?
+			for name, n in fmclasses[c]
+				o[name] = if t = ts[name] then typemap[t] p[n] else p[n]
+		else
+			for name, n in fmclasses[c]
+				o[name] = p[n]
+		return o
+
+	# Define the public version of *_fieldmap()*.
+	# It will handle the case in which the input array could be inside an array,
+	# The conversion is shown by the following ASCII diagrams.
 	#
-	# The input could be an array, in which the diagram would be modified as follows.
-	#	xs                = [{ '__c': mapname, '__p': [    1,     2,     3 ] }]
-	#	fmclasses         =  {        mapname:        ['a',   'b',   'c'   ] }
-	#	ys = fieldmap(x)  # [{                         'a':1, 'b':2, 'c':3   }]
+	# 	x                 =  { '__c': classhint, '__p': [    1,     2,     3 ] }
+	# 	fmclasses         =  {        classhint:        ['a',   'b',   'c'   ] }
+	# 	y = fieldmap(x)  //  {                           'a':1, 'b':2, 'c':3   }
+	#
+	# 	xs                = [{ '__c': classhint, '__p': [    1,     2,     3 ] }]
+	# 	fmclasses         =  {        classhint:        ['a',   'b',   'c'   ] }
+	# 	ys = fieldmap(x) // [{                           'a':1, 'b':2, 'c':3   }]
+	#
 	@fieldmap = (x) ->
-
-		_fieldmap = (m, a) ->
-			o = {}
-			if (ts = fm_datatypes[m])?
-				for name, n in fmclasses[m]
-					o[name] = if t = ts[name] then typemap[t] a[n] else a[n]
-			else
-				for name, n in fmclasses[m]
-					o[name] = a[n]
-			return o
-
 		if $.isArray x
 			return x unless x.length
 			_fieldmap(a.__c, a.__p) for a in x when a.__c
@@ -109,52 +157,39 @@
 			if x.__c then _fieldmap(x.__c, x.__p) else {}

-
-	maptype = {
-		'':        identity
+	# ***
+	# We define the converse of *typemap*,
+	# a helper object for use by *mapfield()*
+	# to cast data types for server use.
+	# We have to guard against only null types.
+	maptype =
+		'':        (x) -> x
 		'fm':      guard_null (x, cls) => if typeof x is 'object' then @mapfield { cls:x } else x
 		'number':  guard_null Number
 		'string':  guard_null String
-		'date':    identity
+		'date':    (x) -> x
 		'boolean': (x) -> if x then 't' else 'f'
-	}

-	# The reverse operation of fieldmap(),
-	# Map to an array of values from an object using a map between position indices and field names.
-	#	xs                 = [{        mapname:        {'a':1, 'b':2, 'c':3 } }]
-	#	fmclasses          =  {        mapname:        ['a',   'b',   'c'   ] }
-	#	ys = mapfield(xs)  # [{ '__c': mapname, '__p': [    1,     2,     3 ] }]
+	# The reverse operation of *fieldmap()*,
+	# ie, convert a given object to an array of values.
+	# The function uses *fmclasses*, a mapping between position indices and field names.
+	# The conversion is shown by the following ASCII diagram.
+	#
+	# 	xs                 = [{        classhint:        {'a':1, 'b':2, 'c':3 } }]
+	# 	fmclasses          =  {        classhint:        ['a',   'b',   'c'   ] }
+	# 	ys = mapfield(xs) // [{ '__c': classhint, '__p': [    1,     2,     3 ] }]
+	#
 	@mapfield = (xs) ->
-
-		a = []
-		for m, o of xs
-			map = m
-			if (ts = fm_datatypes[m])?
-				for name, n in fmclasses[m]
-					a[n] = if t = ts[name] then maptype[t] o[name], m else o[name]
+		p = []
+		for c, o of xs
+			class_hint = c
+			if (ts = fm_datatypes[c])?
+				for name, n in fmclasses[c]
+					p[n] = if t = ts[name] then maptype[t] o[name], c else o[name]
 			else
-				for name, n in fmclasses[m]
-					a[n] = o[name]
-
-		{ '__c': map, '__p': a }
-
-
-	# Input fm.flatten_tree( array, [flag] )
-	#	fm.flatten_tree( array ) : return the visible children in the tree
-	#	fm.flatten_tree( array, flag ): return the complete tree
-	@flatten_tree = (o) ->
-
-		_flatten_tree = (os) ->
-			a = []
-			$.each os, (n, o) ->
-				return [] unless o.opac_visible
-				a.push o
-				(a.push v) for k, v of _flatten_tree o.children if o.children
-				delete o.children
-			return a
-
-		# We need to deep copy the object
-		# otherwise the version in the cache will be modified.
-		_flatten_tree $.extend true, {}, [o]
+				for name, n in fmclasses[c]
+					p[n] = o[name]
+		__c: class_hint, __p: p

 	return @
+


Updated generic-parser (take)

Updated generic-parser (take)

--- src/generic-parser.coffee
+++ src/generic-parser.coffee
@@ -172,6 +172,11 @@
     @collectUntilFalse => @takeIfInStringArray stringArray
     return

+  take: (stringArray)->
+    if typeof stringArray is 'string'
+      stringArray = [stringArray]
+    return @takeIfInStringArray stringArray
+
   takeAll: (stringArray)->
     if typeof stringArray is 'string'
       stringArray = [stringArray]