Add feature: interval label placement.

Add feature: interval label placement.

Timeline axis in CoffeeScript
=============================

Introduction and description
----------------------------

This demo is aimed at exploring possibilities of visualizing time axis.  This especially applies to situations where time axis is meant to be used in interactive plotting, being able to change from very short time intervals to very long time intervals.

Main points of usability of the timeline are:
 * Area that the timeline takes (ideally, as little as possible).
 * Clarity and readability (ideally text/info should be concise and distinctive).
 * How much information is showed (ideally, as much as current zoom level and resolution can afford).
 * Should be easily navigable (should work also for visually impaired).
 * Should be interactive (ideally react according to its representation).

Test cases
----------

### Visual test cases

 1. All time labels and ticks should correspond to their times
 2. Daylight-saving time should be displayed correctly
 3. Leap seconds should be displayed properly
 4. Every interval type should be displayed properly
   1. year
   2. month
   3. week
   4. day
   5. hour
   6. minute
   7. second
   8. millisecond
 5. Check that lines and text are sharp
 6. There should be no intersection of features

### Unit test cases

To be done:
 * Labels should not intersect with each other
 * There should be at least one label (or no?)
 * Label should be in range if its coordinates are in viewport
 * Check for daylight-saving time
 * Check for daylight-saving time table for several historical intervals
 * Check for leap seconds

Development concerns
--------------------

### Readability

The following things should be thought of before making a final version:
 * DPI should be taken into account.  It should work fine on any DPI display.
 * Every element should be separated from others.
    * Visual elements should not intersect with each other.
    * Visual elements such as labels and ticks should be at some good distance to each other.
 * It is a good idea to alternate colors of intervals.
    * Use two colors.
    * They should differ for visually impaired.
    * And should be suitable for grey-color display.
    * Thus, they should differ in brightness at least.
 * Continuos change (opposed to discrete) between scales (that is zoom levels).
    * Makes it possible to animate without jumps between representation levels, keeping viewer connection to details.
    * Adds more information to viewer.  A question is how to keep this simple enough and not bloated with information and graphics.

### Portability

To make it more flexible to render (to canvas, svg, vega, anything else), we should have an intermediate object as output of axis formatting, which could be rendered after withe a chosen rendeder.

Library functions
-----------------

### The `TimelineMaker` class

Since formatting and rendering of timeline involves a lot of calculations, it is divided in several functions, and they are combined in the `TimelineMaker` class.  The `options` parameter will be a dictionary with values that are needed for formatting the timeline.

    class TimelineMaker
      constructor: (@options) ->

The two functions that are intended to be called are `formatTimeAxis()`, which formats time axis into a dictionary that describes the look of axis, and one of the `renderTo...` functions that render that data dictionary to a desired context.

#### The `formatTimeAxis()` function

A function that formats time axis into an intermediate format.  It has two parameters, `interval` (a dictionary with `start` and `end` values, each of Date type) and corresponding to that interval `width` of a viewport.  It returns a formatted time axis object.  This object is a collection of features with their coordinates in a viewport (the top left point of the viewport is (0,0), the top-right is (0, width)).

      formatTimeAxis: (interval, width) ->
        {@start, end} = interval
        @intervalLength = end - @start
        @width = width

We can put on axis ticks and labels, and also colour areas between them.  They correspond to a set of time points.  Labels could correspond to time points or to time intervals between these points.

Ticks should correspond to _edge points_ of time, a point of time between two days (00-00), two years, months, weeks, or a sharp time point, having integer number of hours, or minutes, or seconds.  In general, while moving from bigger to smaller time interval types, every interval type has to be in integer amount, until  some point.  For example (10 years, 2 months, 3 days) from Epoch and remainder which is less than one day.  That means whe should have a parameter that corresponds to the smallest time interval that has to be integral.  We call this parameter _options.intervalType_.  We also need a number of this intervals between each tick.  This parameter will be `options.intervalMultiplier`.  There is one exception though:  in case of weeks there is no previous integral interval.  So there are two cases:  week and (year, month, day, hour, minute, second, millisecond).

So we need to build a list of time points that correspond to a given time interval.  We will put such code in a special function, `findPointList()`.

        pointList = @findPointList @start, end

Now, when we have found the list of time points, we need to construct a dictionary with graphical elements (features).  To transform time value into a coordinate we use the `timeToCoord()` function.  We start with ticks.  Each tick is a line.  The @options.tickLength parameter is a base length of a tick.  We assume that (y = 0) is a baseline and tick length is from baseline to `@options.tickLength` down and `@options.tickLength/5` up.

We store ticks and other lines in `lines` element of the dictionary.

        ticks = for timePoint in pointList
          {
            x1: @timeToCoord timePoint
            x2: @timeToCoord timePoint
            y1: -@options.tickLength / 5
            y2: @options.tickLength
          }

We also add axis (a horizontal line).

        axisLine = {
          x1: 0
          x2: @width
          y1: 0
          y2: 0
        }

Now we construct a list of text features, each has coordinates and string.

_Note_:  here we also need to improve formatting, now it's just a quick fix to display text.  Text should be formatted without problems on any display and resolution and shouldn't intersect ticks when it has reasonable font size.

Text labels can be basically displayed in two ways.  The first one is to put label right under the tick, so that lable corresponds to time or date at this point.  Another way is to put label between ticks so that it corresponds to time interval between these two ticks.  For me it seems quite rational to use the first way to show times, and the second way to show days, months and years;  this is in case of general timeline, that we can move and zoom, and without a need to highlight particular dates.  To clarify what I mean we can consider a typical time axis in pretty much any library these days.  When zoomed out a lot, it usually shows ticks at times like 00:00, at first day of month or a year.  This is pretty logical.  But then, usually a label is shown under such tick, for examle a label of a month that starts at this point of time.  Which doesn't make much sense, because a month is a period.  When we say 'in November', we usually mean 'some time between start and end of November' (unlike when we say 'at 3', which usually means 'at 3:00').  But if the label is under 00:00 of 1st of November, it looks for a viewer like november corresponds to some time from mid-October to mid-November.  Which is very confusing and misleading too.  On the other side, showing the 'November' label in the middle of November on an axis corrects this.  This is a motivation for making two types of label positioning in our timeline implementation:  _point_ and _interval_.  They are determined by `options.labelPlacement` ('point' or 'interval').

To do it we first make a list of text labels assuming point label placement.

        textLabels = for timePoint in pointList
          {
            x: @timeToCoord timePoint
            y: @options.tickLength * 1.2
            text: switch @options.intervalType
              when 'year' then timePoint.getFullYear().toString()
              when 'month' then timePoint.getMonth().toString()
              when 'week', 'day' then timePoint.getDate().toString()
              when 'hour' then timePoint.getHours().toString()
              when 'minute' then timePoint.getMinutes().toString()
              when 'second' then timePoint.getSeconds().toString()
              when 'millisecond' then timePoint.getMilliseconds().toString()
          }

Then, if `options.labelPlacement` is equal to 'interval', we remove the last item and adjust x coordinates of other items.

_Note_: one could thing of better implementation here.  I tried to make it shorter and came to the current solution.

        for textLabel, i in textLabels when i isnt (textLabels.length-1)
          textLabel.x = (textLabel.x + textLabels[i+1].x) / 2
        textLabels.pop()

Now we combine all elements into a one dictionary and return it.

        features =
          lines: ticks.concat axisLine
          textLabels: textLabels

#### The `findPointList()` function

This function returns a list of points that are needed to be calculated for a (`start`, `end`) interval.

      findPointList: (start, end) ->

Since we could need labels between time points, we need time points inside the interval (non-inclusive) and one point on left and right side.  We can build the point list by finding the leftmost _edge point_ which is strictly less than the `start` of the interval.  We move code that does that to the `findLeftTime()`.

        timePoint = @findLeftTime start

Then, we populate the list in by incrementing points until the we reach right end of the interval, using `findNextPoint()` function.

        pointList = [timePoint]
        until timePoint > end
          timePoint = @findNextPoint timePoint
          pointList.push timePoint
        pointList

#### The `findLeftTime()` function

The `findLeftTime()` function calculates the rightmost point of time for current `options.intervalType` such that it is not inside the given `interval` (excluding beginning).  The `options.intervalType` could be one of the following:
 * 'year'
 * 'month'
 * 'week'
 * 'day'
 * 'hour'
 * 'minute'
 * 'second'
 * 'millisecond'

Another option, `options.intervalMultiplier`, says how many of such intervals are between two time points.  It should be an integer value.  For years and months we truncate them to the desired value, while for days and weeks we will count from a fixed origin day near Epoch (Monday 5 january 1970), so that any day intervals are independent from underlying months and years.  That also means that in case of months the `options.intervalMultiplier` should be equal to 1, 2, 3, 4 or 6 to be displayed correctly.  To calculate number of days or weeks (reduced to 7 calculation for 7 days) from origin day we use binary subtracting, starting from huge multiplier equal to 1048576 (roughly 2800/11200 years), going down to base multiplier equal to 1.  For interval types smaller or equal than hours we use the `findNextPoint()` function by incrementing local origin (the interval type and everything smaller is reset to 0).  This is done to avoid problems with daylight-saving and similar things.  In `findNextPoint()` we make sure that edge points are consistent while using different values of `start`.

      findLeftTime: (start) ->
        leftTime = new Date start.getTime()
        switch @options.intervalType
          when 'year'
            newYear = start.getFullYear() - start.getFullYear() % @options.intervalMultiplier
            leftTime.setFullYear newYear, 0, 1
            leftTime.setHours 0, 0, 0, 0
          when 'month'
            newMonth = start.getMonth() - start.getMonth() % @options.intervalMultiplier
            leftTime.setMonth newMonth, 1
            leftTime.setHours 0, 0, 0, 0
          when 'week', 'day'
            leftTime.setFullYear 1970, 0, 5
            leftTime.setHours 0, 0, 0, 0
            delta = 1048576
            intervalMultiplier = @options.intervalMultiplier
            if @options.intervalType is 'week'
              intervalMultiplier *= 7
            while delta >= 1
              nextTime = new Date leftTime.getFullYear(),
                leftTime.getMonth()
                leftTime.getDate() + delta*intervalMultiplier
                leftTime.getHours()
                leftTime.getMinutes()
                leftTime.getSeconds()
                leftTime.getMilliseconds()
              delta /= 2
              leftTime = nextTime if nextTime <= start
          when 'hour'
            leftTime.setHours 0, 0, 0, 0
            while leftTime < start
              nextTime = @findNextPoint leftTime
              leftTime = nextTime if nextTime <= start
          when 'minute'
            newMinutes = start.getMinutes() - start.getMinutes() % @options.intervalMultiplier
            leftTime.setMinutes newMinutes, 0, 0
          when 'second'
            leftTime.setSeconds start.getSeconds(), 0
            while leftTime < start
              nextTime = @findNextPoint leftTime
              leftTime = nextTime if nextTime <= start
          when 'millisecond'
            newMilliseconds = start.getMilliseconds() - start.getMilliseconds() % @options.intervalMultiplier
            leftTime.setMilliseconds newMilliseconds
        leftTime

There is a code repetition in the switch statement above.  One can think of a good way to improve this part of code.

### The `findNextPoint()` function

This function calculates next edge time point for a current options assuming `timePoint` argument is an edge time point.  We take care of daylight-saving time and leap seconds here, assuming that at most one hour/minute/second/millisecond is added or subtracted.  Also we assume that subtraction is not happening at time with 0 value, that is something like 00 -> 23 is not happening.

In current implementation we cope with daylight-saving time by adding hours in UTC to date and then checking how the value changes in local time and fixing by one hour if needed.  The same for leap seconds.

      findNextPoint: (timePoint) ->
        nextTime = new Date timePoint.getTime()
        switch @options.intervalType
          when 'year'
            nextTime.setFullYear (timePoint.getFullYear() + @options.intervalMultiplier)
          when 'month'
            nextTime.setMonth (timePoint.getMonth() + @options.intervalMultiplier)
          when 'week'
            nextTime.setDate (timePoint.getDate() + 7*@options.intervalMultiplier)
          when 'day'
            nextTime.setDate (timePoint.getDate() + @options.intervalMultiplier)
          when 'hour'
            nextTime.setUTCHours (timePoint.getUTCHours() + @options.intervalMultiplier)
            hours = nextTime.getHours()
            if hours % @options.intervalMultiplier isnt 0
              if (hours+1) % @options.intervalMultiplier is 0
                nextTime.setUTCHours (nextTime.getUTCHours() + 1)
              else
                nextTime.setUTCHours (nextTime.getUTCHours() - 1)
          when 'minute'
            nextTime.setMinutes (timePoint.getMinutes() + @options.intervalMultiplier)
          when 'second'
            nextTime.setUTCSeconds (timePoint.getUTCSeconds() + @options.intervalMultiplier)
            seconds = nextTime.getSeconds()
            if seconds % @options.intervalMultiplier isnt 0
              if (seconds+1) % @options.intervalMultiplier is 0
                nextTime.setUTCSeconds (nextTime.getUTCSeconds() + 1)
              else
                nextTime.setUTCSeconds (nextTime.getUTCSeconds() - 1)
          when 'millisecond'
            nextTime.setUTCMilliseconds (timePoint.getUTCMilliseconds() + @options.intervalMultiplier)
        nextTime

#### The `timeToCoord()` function

To get coordinate of time point we use `@intervalLength` that we stored in `formatTimeAxis()`, which is equal to the number of milliseconds between `end` and `start` of the interval.  We use it to calculate pixe/time ratio, equal to `@width / @intervalLength`.

      timeToCoord: (time) ->
        timeFromStart = time - @start
        coordinate = timeFromStart * @width / @intervalLength

#### The `renderToCanvas()` function

This function renders formatted time axis to html canvas.
 * `axisData` is formatted data (that we get by calling any of the `format...()` functions)
 * `canvas` is a html canvas object on which axis is drawn
 * `left` and `top` are x- and y-coordinates of the canvas that correspond to the (0, 0) point of the axis viewport

      renderToCanvas: (axisData, canvas, left, top) ->

In the beginning we do just regular initialization of context and its properties.  We also wrap our drawing code into `save()` and `restore()` to not alter context properties globally.

        context = canvas.getContext '2d'
        context.save()
        context.strokeStyle = '#000000'
        context.fillStyle = '#000000'
        context.lineWidth = 1
        context.font = 'normal 12px sans-serif'
        context.textAlign = 'center'
        context.textBaseline = 'top'

To all drawing functions we pass coordinates altered by `roundForCanvas()` function, which rounds values in such a way that lines are more sharp.

        context.beginPath()
        for line in axisData.lines
          x1 = left + line.x1
          x2 = left + line.x2
          y1 = top + line.y1
          y2 = top + line.y2
          context.moveTo @roundForCanvas(x1), @roundForCanvas(y1)
          context.lineTo @roundForCanvas(x2), @roundForCanvas(y2)
        context.stroke()

        for textLabel in axisData.textLabels
          x = left + textLabel.x
          y = top + textLabel.y
          context.fillText textLabel.text, x, y

        context.restore()

#### The `roundForCanvas()` function

This function may be used to avoud aliasing of lines, especially on low-resolution displays.  It rounds coordinate to middle-pixel values, which are .5 in case of html canvas.

      roundForCanvas: (coord) ->
        0.5 + Math.round(coord-0.5)

Library tests
-------------

Examples
--------

Before we display anytihng, we define a function that colors background of canvas in some color, to erase before rendering axis, and to make canvas area easily visible.

    recleanCanvas = ->
      canvas = document.getElementById 'timeline'
      context = canvas.getContext '2d'
      context.fillStyle = '#77FFBB'
      context.fillRect(0, 0, canvas.clientWidth, canvas.clientHeight)

The `getMousePos` function gives mouse position relative to a `canvas`.

    getMousePos = (canvas, event) ->
      rect = canvas.getBoundingClientRect()
      {
        x: event.clientX - rect.left
        y: event.clientY - rect.top
      }

This simple code displays time axis when html page is loaded, in `timeline` canvas element.

    makeDemo = ->
      canvas = document.getElementById 'timeline'
      recleanCanvas()
      timelineMaker = new TimelineMaker
        tickLength: 25
        intervalType: 'day'
        labelPlacement: 'interval'
        intervalMultiplier: 1
      start = new Date('2015-06-15T00:00:00')
      end = new Date('2015-07-13T15:23:49')
      axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
      timelineMaker.renderToCanvas axisData, canvas, 0, 15

Wa also add mouse tracking functionality to test our timeline.  When mouse is pressed we can change time interval by dragging mouse.

      dragging = false
      oldX = 0
      oldY = 0
      document.getElementById('timeline').onmousedown = (event) ->
        dragging = true
        canvas = document.getElementById 'timeline'
        {x: oldX, y: oldY} = getMousePos(canvas, event)

      document.getElementById('timeline').onmouseup = (event) ->
        dragging = false

      document.getElementById('timeline').onmousemove = (event) ->
        if dragging
          canvas = document.getElementById 'timeline'
          {x: clientX, y: clientY} = getMousePos(canvas, event)
          deltaX = event.clientX - oldX
          deltaY = event.clientY - oldY
          oldX = event.clientX
          oldY = event.clientY

Now we calculate how much time we should move.

          timeInterval = end - start
          timeDelta = - deltaX * timeInterval / canvas.clientWidth
          start = new Date (start.getTime() + timeDelta)
          end = new Date (end.getTime() + timeDelta)

And render it again.

          recleanCanvas()
          axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
          timelineMaker.renderToCanvas axisData, canvas, 0, 15

The same for mouse wheel:  we change `intervalLength` when wheel is scrolled.  We keep the same time value under mouse pointer before and after zooming.  This is done by multiplying interval before point and after point by zooming multiplier.  So if mouse point has time P and our interval is _(P - A, P + B)_ it becomes after zoom _(P - A*m, P + B*m)_, where _m_ is the multiplier.

        base = 1.05
        document.getElementById('timeline').onwheel = (event) ->
          canvas = document.getElementById 'timeline'
          multiplier = Math.pow(base, event.deltaY)
          timeInterval = end-start
          {x: clientX, y: clientY} = getMousePos(canvas, event)

In the next line we subtract `0.5` to correct mouse x offset, though it is strange that we need to subtract it.  But it works better, so we keep it as a quick fix.

          leftInterval = (clientX-0.5) * timeInterval / canvas.clientWidth
          rightInterval = timeInterval - leftInterval
          mousePoint = start.getTime() + leftInterval
          leftInterval *= multiplier
          rightInterval *= multiplier
          start = new Date(mousePoint - leftInterval)
          end = new Date(mousePoint + rightInterval)

          recleanCanvas()
          axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
          timelineMaker.renderToCanvas axisData, canvas, 0, 15

We run the `makeDemo` function when page loads.

    window.onload = makeDemo

Timeline axis in CoffeeScript
=============================

Introduction and description
----------------------------

This demo is aimed at exploring possibilities of visualizing time axis.  This especially applies to situations where time axis is meant to be used in interactive plotting, being able to change from very short time intervals to very long time intervals.

Main points of usability of the timeline are:
 * Area that the timeline takes (ideally, as little as possible).
 * Clarity and readability (ideally text/info should be concise and distinctive).
 * How much information is showed (ideally, as much as current zoom level and resolution can afford).
 * Should be easily navigable (should work also for visually impaired).
 * Should be interactive (ideally react according to its representation).

Test cases
----------

### Visual test cases

 1. All time labels and ticks should correspond to their times
 2. Daylight-saving time should be displayed correctly
 3. Leap seconds should be displayed properly
 4. Every interval type should be displayed properly
   1. year
   2. month
   3. week
   4. day
   5. hour
   6. minute
   7. second
   8. millisecond
 5. Check that lines and text are sharp
 6. There should be no intersection of features

### Unit test cases

To be done:
 * Labels should not intersect with each other
 * There should be at least one label (or no?)
 * Label should be in range if its coordinates are in viewport
 * Check for daylight-saving time
 * Check for daylight-saving time table for several historical intervals
 * Check for leap seconds

Development concerns
--------------------

### Readability

The following things should be thought of before making a final version:
 * DPI should be taken into account.  It should work fine on any DPI display.
 * Every element should be separated from others.
    * Visual elements should not intersect with each other.
    * Visual elements such as labels and ticks should be at some good distance to each other.
 * It is a good idea to alternate colors of intervals.
    * Use two colors.
    * They should differ for visually impaired.
    * And should be suitable for grey-color display.
    * Thus, they should differ in brightness at least.
 * Continuos change (opposed to discrete) between scales (that is zoom levels).
    * Makes it possible to animate without jumps between representation levels, keeping viewer connection to details.
    * Adds more information to viewer.  A question is how to keep this simple enough and not bloated with information and graphics.

### Portability

To make it more flexible to render (to canvas, svg, vega, anything else), we should have an intermediate object as output of axis formatting, which could be rendered after withe a chosen rendeder.

Library functions
-----------------

### The `TimelineMaker` class

Since formatting and rendering of timeline involves a lot of calculations, it is divided in several functions, and they are combined in the `TimelineMaker` class.  The `options` parameter will be a dictionary with values that are needed for formatting the timeline.

    class TimelineMaker
      constructor: (@options) ->

The two functions that are intended to be called are `formatTimeAxis()`, which formats time axis into a dictionary that describes the look of axis, and one of the `renderTo...` functions that render that data dictionary to a desired context.

#### The `formatTimeAxis()` function

A function that formats time axis into an intermediate format.  It has two parameters, `interval` (a dictionary with `start` and `end` values, each of Date type) and corresponding to that interval `width` of a viewport.  It returns a formatted time axis object.  This object is a collection of features with their coordinates in a viewport (the top left point of the viewport is (0,0), the top-right is (0, width)).

      formatTimeAxis: (interval, width) ->
        {@start, end} = interval
        @intervalLength = end - @start
        @width = width

We can put on axis ticks and labels, and also colour areas between them.  They correspond to a set of time points.  Labels could correspond to time points or to time intervals between these points.

Ticks should correspond to _edge points_ of time, a point of time between two days (00-00), two years, months, weeks, or a sharp time point, having integer number of hours, or minutes, or seconds.  In general, while moving from bigger to smaller time interval types, every interval type has to be in integer amount, until  some point.  For example (10 years, 2 months, 3 days) from Epoch and remainder which is less than one day.  That means whe should have a parameter that corresponds to the smallest time interval that has to be integral.  We call this parameter _options.intervalType_.  We also need a number of this intervals between each tick.  This parameter will be `options.intervalMultiplier`.  There is one exception though:  in case of weeks there is no previous integral interval.  So there are two cases:  week and (year, month, day, hour, minute, second, millisecond).

So we need to build a list of time points that correspond to a given time interval.  We will put such code in a special function, `findPointList()`.

        pointList = @findPointList @start, end

Now, when we have found the list of time points, we need to construct a dictionary with graphical elements (features).  To transform time value into a coordinate we use the `timeToCoord()` function.  We start with ticks.  Each tick is a line.  The @options.tickLength parameter is a base length of a tick.  We assume that (y = 0) is a baseline and tick length is from baseline to `@options.tickLength` down and `@options.tickLength/5` up.

We store ticks and other lines in `lines` element of the dictionary.

        ticks = for timePoint in pointList
          {
            x1: @timeToCoord timePoint
            x2: @timeToCoord timePoint
            y1: -@options.tickLength / 5
            y2: @options.tickLength
          }

We also add axis (a horizontal line).

        axisLine = {
          x1: 0
          x2: @width
          y1: 0
          y2: 0
        }

Now we construct a list of text features, each has coordinates and string.

_Note_:  here we also need to improve formatting, now it's just a quick fix to display text.  Text should be formatted without problems on any display and resolution and shouldn't intersect ticks when it has reasonable font size.

        textLabels = for timePoint in pointList
          {
            x: @timeToCoord timePoint
            y: @options.tickLength * 1.2
            text: switch @options.intervalType
              when 'year' then timePoint.getFullYear().toString()
              when 'month' then timePoint.getMonth().toString()
              when 'week', 'day' then timePoint.getDate().toString()
              when 'hour' then timePoint.getHours().toString()
              when 'minute' then timePoint.getMinutes().toString()
              when 'second' then timePoint.getSeconds().toString()
              when 'millisecond' then timePoint.getMilliseconds().toString()
          }

Now we combine all elements into a one dictionary and return it.

        {
          lines: ticks.concat axisLine
          textLabels
        }

#### The `findPointList()` function

This function returns a list of points that are needed to be calculated for a (`start`, `end`) interval.

      findPointList: (start, end) ->

Since we could need labels between time points, we need time points inside the interval (non-inclusive) and one point on left and right side.  We can build the point list by finding the leftmost _edge point_ which is strictly less than the `start` of the interval.  We move code that does that to the `findLeftTime()`.

        timePoint = @findLeftTime start

Then, we populate the list in by incrementing points until the we reach right end of the interval, using `findNextPoint()` function.

        pointList = [timePoint]
        until timePoint > end
          timePoint = @findNextPoint timePoint
          pointList.push timePoint
        pointList

#### The `findLeftTime()` function

The `findLeftTime()` function calculates the rightmost point of time for current `options.intervalType` such that it is not inside the given `interval` (excluding beginning).  The `options.intervalType` could be one of the following:
 * 'year'
 * 'month'
 * 'week'
 * 'day'
 * 'hour'
 * 'minute'
 * 'second'
 * 'millisecond'

Another option, `options.intervalMultiplier`, says how many of such intervals are between two time points.  It should be an integer value.  For years and months we truncate them to the desired value, while for days and weeks we will count from a fixed origin day near Epoch (Monday 5 january 1970), so that any day intervals are independent from underlying months and years.  That also means that in case of months the `options.intervalMultiplier` should be equal to 1, 2, 3, 4 or 6 to be displayed correctly.  To calculate number of days or weeks (reduced to 7 calculation for 7 days) from origin day we use binary subtracting, starting from huge multiplier equal to 1048576 (roughly 2800/11200 years), going down to base multiplier equal to 1.  For interval types smaller or equal than hours we use the `findNextPoint()` function by incrementing local origin (the interval type and everything smaller is reset to 0).  This is done to avoid problems with daylight-saving and similar things.  In `findNextPoint()` we make sure that edge points are consistent while using different values of `start`.

      findLeftTime: (start) ->
        leftTime = new Date start.getTime()
        switch @options.intervalType
          when 'year'
            newYear = start.getFullYear() - start.getFullYear() % @options.intervalMultiplier
            leftTime.setFullYear newYear, 0, 1
            leftTime.setHours 0, 0, 0, 0
          when 'month'
            newMonth = start.getMonth() - start.getMonth() % @options.intervalMultiplier
            leftTime.setMonth newMonth, 1
            leftTime.setHours 0, 0, 0, 0
          when 'week', 'day'
            leftTime.setFullYear 1970, 0, 5
            leftTime.setHours 0, 0, 0, 0
            delta = 1048576
            intervalMultiplier = @options.intervalMultiplier
            if @options.intervalType is 'week'
              intervalMultiplier *= 7
            while delta >= 1
              nextTime = new Date leftTime.getFullYear(),
                leftTime.getMonth()
                leftTime.getDate() + delta*intervalMultiplier
                leftTime.getHours()
                leftTime.getMinutes()
                leftTime.getSeconds()
                leftTime.getMilliseconds()
              delta /= 2
              leftTime = nextTime if nextTime <= start
          when 'hour'
            leftTime.setHours 0, 0, 0, 0
            while leftTime < start
              nextTime = @findNextPoint leftTime
              leftTime = nextTime if nextTime <= start
          when 'minute'
            newMinutes = start.getMinutes() - start.getMinutes() % @options.intervalMultiplier
            leftTime.setMinutes newMinutes, 0, 0
          when 'second'
            leftTime.setSeconds start.getSeconds(), 0
            while leftTime < start
              nextTime = @findNextPoint leftTime
              leftTime = nextTime if nextTime <= start
          when 'millisecond'
            newMilliseconds = start.getMilliseconds() - start.getMilliseconds() % @options.intervalMultiplier
            leftTime.setMilliseconds newMilliseconds
        leftTime

There is a code repetition in the switch statement above.  One can think of a good way to improve this part of code.

### The `findNextPoint()` function

This function calculates next edge time point for a current options assuming `timePoint` argument is an edge time point.  We take care of daylight-saving time and leap seconds here, assuming that at most one hour/minute/second/millisecond is added or subtracted.  Also we assume that subtraction is not happening at time with 0 value, that is something like 00 -> 23 is not happening.

In current implementation we cope with daylight-saving time by adding hours in UTC to date and then checking how the value changes in local time and fixing by one hour if needed.  The same for leap seconds.

      findNextPoint: (timePoint) ->
        nextTime = new Date timePoint.getTime()
        switch @options.intervalType
          when 'year'
            nextTime.setFullYear (timePoint.getFullYear() + @options.intervalMultiplier)
          when 'month'
            nextTime.setMonth (timePoint.getMonth() + @options.intervalMultiplier)
          when 'week'
            nextTime.setDate (timePoint.getDate() + 7*@options.intervalMultiplier)
          when 'day'
            nextTime.setDate (timePoint.getDate() + @options.intervalMultiplier)
          when 'hour'
            nextTime.setUTCHours (timePoint.getUTCHours() + @options.intervalMultiplier)
            hours = nextTime.getHours()
            if hours % @options.intervalMultiplier isnt 0
              if (hours+1) % @options.intervalMultiplier is 0
                nextTime.setUTCHours (nextTime.getUTCHours() + 1)
              else
                nextTime.setUTCHours (nextTime.getUTCHours() - 1)
          when 'minute'
            nextTime.setMinutes (timePoint.getMinutes() + @options.intervalMultiplier)
          when 'second'
            nextTime.setUTCSeconds (timePoint.getUTCSeconds() + @options.intervalMultiplier)
            seconds = nextTime.getSeconds()
            if seconds % @options.intervalMultiplier isnt 0
              if (seconds+1) % @options.intervalMultiplier is 0
                nextTime.setUTCSeconds (nextTime.getUTCSeconds() + 1)
              else
                nextTime.setUTCSeconds (nextTime.getUTCSeconds() - 1)
          when 'millisecond'
            nextTime.setUTCMilliseconds (timePoint.getUTCMilliseconds() + @options.intervalMultiplier)
        nextTime

#### The `timeToCoord()` function

To get coordinate of time point we use `@intervalLength` that we stored in `formatTimeAxis()`, which is equal to the number of milliseconds between `end` and `start` of the interval.  We use it to calculate pixe/time ratio, equal to `@width / @intervalLength`.

      timeToCoord: (time) ->
        timeFromStart = time - @start
        coordinate = timeFromStart * @width / @intervalLength

#### The `renderToCanvas()` function

This function renders formatted time axis to html canvas.
 * `axisData` is formatted data (that we get by calling any of the `format...()` functions)
 * `canvas` is a html canvas object on which axis is drawn
 * `left` and `top` are x- and y-coordinates of the canvas that correspond to the (0, 0) point of the axis viewport

      renderToCanvas: (axisData, canvas, left, top) ->

In the beginning we do just regular initialization of context and its properties.  We also wrap our drawing code into `save()` and `restore()` to not alter context properties globally.

        context = canvas.getContext '2d'
        context.save()
        context.strokeStyle = '#000000'
        context.fillStyle = '#000000'
        context.lineWidth = 1
        context.font = 'normal 12px sans-serif'
        context.textAlign = 'center'
        context.textBaseline = 'top'

To all drawing functions we pass coordinates altered by `roundForCanvas()` function, which rounds values in such a way that lines are more sharp.

        context.beginPath()
        for line in axisData.lines
          x1 = left + line.x1
          x2 = left + line.x2
          y1 = top + line.y1
          y2 = top + line.y2
          context.moveTo @roundForCanvas(x1), @roundForCanvas(y1)
          context.lineTo @roundForCanvas(x2), @roundForCanvas(y2)
        context.stroke()

        for textLabel in axisData.textLabels
          x = left + textLabel.x
          y = top + textLabel.y
          context.fillText textLabel.text, x, y

        context.restore()

#### The `roundForCanvas()` function

This function may be used to avoud aliasing of lines, especially on low-resolution displays.  It rounds coordinate to middle-pixel values, which are .5 in case of html canvas.

      roundForCanvas: (coord) ->
        0.5 + Math.round(coord-0.5)

Library tests
-------------

Examples
--------

Before we display anytihng, we define a function that colors background of canvas in some color, to erase before rendering axis, and to make canvas area easily visible.

    recleanCanvas = ->
      canvas = document.getElementById 'timeline'
      context = canvas.getContext '2d'
      context.fillStyle = '#77FFBB'
      context.fillRect(0, 0, canvas.clientWidth, canvas.clientHeight)

The `getMousePos` function gives mouse position relative to a `canvas`.

    getMousePos = (canvas, event) ->
      rect = canvas.getBoundingClientRect()
      {
        x: event.clientX - rect.left
        y: event.clientY - rect.top
      }

This simple code displays time axis when html page is loaded, in `timeline` canvas element.

    makeDemo = ->
      canvas = document.getElementById 'timeline'
      recleanCanvas()
      timelineMaker = new TimelineMaker {tickLength: 25, intervalType: 'day', intervalMultiplier: 1}
      start = new Date('2015-06-15T00:00:00')
      end = new Date('2015-07-13T15:23:49')
      axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
      timelineMaker.renderToCanvas axisData, canvas, 0, 15

Wa also add mouse tracking functionality to test our timeline.  When mouse is pressed we can change time interval by dragging mouse.

      dragging = false
      oldX = 0
      oldY = 0
      document.getElementById('timeline').onmousedown = (event) ->
        dragging = true
        canvas = document.getElementById 'timeline'
        {x: oldX, y: oldY} = getMousePos(canvas, event)

      document.getElementById('timeline').onmouseup = (event) ->
        dragging = false

      document.getElementById('timeline').onmousemove = (event) ->
        if dragging
          canvas = document.getElementById 'timeline'
          {x: clientX, y: clientY} = getMousePos(canvas, event)
          deltaX = event.clientX - oldX
          deltaY = event.clientY - oldY
          oldX = event.clientX
          oldY = event.clientY

Now we calculate how much time we should move.

          timeInterval = end - start
          timeDelta = - deltaX * timeInterval / canvas.clientWidth
          start = new Date (start.getTime() + timeDelta)
          end = new Date (end.getTime() + timeDelta)

And render it again.

          recleanCanvas()
          axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
          timelineMaker.renderToCanvas axisData, canvas, 0, 15

The same for mouse wheel:  we change `intervalLength` when wheel is scrolled.  We keep the same time value under mouse pointer before and after zooming.  This is done by multiplying interval before point and after point by zooming multiplier.  So if mouse point has time P and our interval is _(P - A, P + B)_ it becomes after zoom _(P - A*m, P + B*m)_, where _m_ is the multiplier.

        base = 1.05
        document.getElementById('timeline').onwheel = (event) ->
          canvas = document.getElementById 'timeline'
          multiplier = Math.pow(base, event.deltaY)
          timeInterval = end-start
          {x: clientX, y: clientY} = getMousePos(canvas, event)

In the next line we subtract `0.5` to correct mouse x offset, though it is strange that we need to subtract it.  But it works better, so we keep it as a quick fix.

          leftInterval = (clientX-0.5) * timeInterval / canvas.clientWidth
          rightInterval = timeInterval - leftInterval
          mousePoint = start.getTime() + leftInterval
          leftInterval *= multiplier
          rightInterval *= multiplier
          start = new Date(mousePoint - leftInterval)
          end = new Date(mousePoint + rightInterval)

          recleanCanvas()
          axisData = timelineMaker.formatTimeAxis {start, end}, canvas.width
          timelineMaker.renderToCanvas axisData, canvas, 0, 15

We run the `makeDemo` function when page loads.

    window.onload = makeDemo


Add sample file for Literate CoffeeScript

error: pathspec 'samples/Literate' did not match any file(s) known to git
error: pathspec 'CoffeeScript/scope.litcoffee' did not match any file(s) known to git


document the jquery extra

document the jquery extra

# /api/Extras/Batman.jQuery

The `batman.jquery` extra is an implementation library for `Batman.DOM` and `Batman.Request` that depends on [jQuery](http://jquery.com/). It is packaged with batman.js builds on the [downloads page](/downloads.html).

Besides implementing `Batman.DOM` and `Batman.Request`, it adds a `"$node"` accessor to `Batman.View`, which returns the jQuery selection for the view's `node`. For example:

```coffeescript
class MyApp.CustomView extends Batman.View
  viewDidAppear: ->
    @get("$node") # => returns a jQuery selection
```

## Batman.DOM Implementation

`batman.jquery` implements the necessary functions of `Batman.DOM`: `querySelectorAll`, `querySelector`, `setInnerHTML`, `destroyNode`, `containsNode`, `textContent`, `addEventListener` and `removeEventListener`.

## Batman.Request Implementation

`batman.jquery` implements `Batman.Request::send` to use the `jquery.ajax` function.

It adds a `xhr` property to completed requests which contains the [`jqXHR` object](http://api.jquery.com/Types/#jqXHR).

error: pathspec 'docs/25_jquery.litcoffee' did not match any file(s) known to git


Create dice.litcoffee

Create dice.litcoffee

# Dice Game written by Heark
#
#'||  ||`                       '||
# ||  ||                         ||
# ||''||  .|''|,  '''|.  '||''|  || //`
# ||  ||  ||..|| .|''||   ||     ||<<
#.||  ||. `|...  `|..||. .||.   .|| \\.
#
#
#.|'''''|                                            .|'''',
#|| .                         ''                     ||
#|| |''||  '''|.  '||),,(|,   ||  `||''|,  .|''|,    ||      .|''|,
#||    || .|''||   || || ||   ||   ||  ||  ||  ||    ||      ||  ||
#`|....|' `|..||. .||    ||. .||. .||  ||. `|..||    `|....' `|..|' ..
#                                              ||
#
die = Math.floor(Math.random() * 6) + 1
die2 = Math.floor(Math.random() * 6) + 1
oppdie = Math.floor(Math.random() * 6) + 1
oppdie2 = Math.floor(Math.random() * 6) + 1
score = undefined
oppscore = undefined
if die is 1
  score = 0
else
  score = @die
if oppdie is 1
  oppscore = 0
else
  oppscore = @oppdie
console.log "You rolled a " + die + " for a score of " + score + ""
console.log "Your opponent rolled a " + oppdie + " for a score of " + oppscore + ""
if score > oppscore
  console.log "You win!"
else if score < oppscore
  console.log "You Lose!"
else console.log "Tie Game!"  if score = oppscore

error: pathspec 'dice.litcoffee' did not match any file(s) known to git


Began refactor; camera logic is now in its own module.

# thirdPerson

A camera instance is an object containing:

- transform: A transform matrix to apply this frame.
- transformPreviousDraw: A transform matrix to apply the previous frame.

    matrix = require "./../matrix.litcoffee"
    vector = require "./../vector.litcoffee"
    ik = require "./../ik.litcoffee"
    navmesh = require "./../navmesh.litcoffee"
    plane = require "./../plane.litcoffee"

## create

- A transform matrix to track.
- A number specifying how far the camera should be from the target.
- A number specifying how high above the target the camera should look.
- A number specifying how high above the target the camera should be.
- A number specifying how quickly the camera should move, where "0" is slowest
  and "1" is fastest.
- A number specifying how quickly the camera should track, where "0" is slowest
  and "1" is fastest.

Returns a new camera instance.

    create = (transform, distance, viewHeight, targetHeight, moveLag, trackLag) ->
        track: transform
        transform: []
        transformPreviousDraw: []
        location: []
        locationPreviousTick: []
        target: []
        targetPreviousTick: []
        side: []
        sidePreviousTick: []
        distance: distance
        viewHeight: viewHeight
        targetHeight: targetHeight
        moveLag: moveLag
        trackLag: trackLag
        firstTick: true
        firstDraw: true

## tick

- A camera instance.
- Optionally, a navmesh triangle to track from.  Used to prevent the camera
  passing through the floor when going up ramps.
- A number specifying how much gap to leave between the camera and the navmesh.

    offset = []
    upAxis = []
    forwardAxis = []

    tick = (cameraInstance, triangle, gap) ->
        if not cameraInstance.firstTick
            vector.copy cameraInstance.location, cameraInstance.locationPreviousTick
            vector.copy cameraInstance.side, cameraInstance.sidePreviousTick
            vector.copy cameraInstance.target, cameraInstance.targetPreviousTick

        matrix.getTranslation cameraInstance.track, cameraInstance.location
        matrix.getX cameraInstance.track, cameraInstance.side
        matrix.getY cameraInstance.track, upAxis
        vector.multiply.byScalar upAxis, cameraInstance.targetHeight, offset
        vector.add.vector cameraInstance.location, offset, cameraInstance.target

        vector.multiply.byScalar upAxis, cameraInstance.viewHeight, offset
        vector.add.vector cameraInstance.location, offset, cameraInstance.location

        matrix.getZ cameraInstance.track, forwardAxis
        vector.multiply.byScalar forwardAxis, cameraInstance.distance, offset
        vector.subtract.vector cameraInstance.location, offset, cameraInstance.location

        if triangle
            triangle = navmesh.constrain cameraInstance.location, triangle
            altitude = (plane.distance triangle.plane, cameraInstance.location) - gap
            if altitude < 0
                vector.multiply.byScalar triangle.plane.normal, altitude, offset
                vector.subtract.vector cameraInstance.location, offset, cameraInstance.location

        if cameraInstance.firstTick
            vector.copy cameraInstance.location, cameraInstance.locationPreviousTick
            vector.copy cameraInstance.side, cameraInstance.sidePreviousTick
            vector.copy cameraInstance.target, cameraInstance.targetPreviousTick
            cameraInstance.firstTick = false
        else
            vector.interpolate cameraInstance.locationPreviousTick, cameraInstance.location, cameraInstance.moveLag, cameraInstance.location
            vector.interpolate cameraInstance.sidePreviousTick, cameraInstance.side, cameraInstance.moveLag, cameraInstance.side
            vector.interpolate cameraInstance.targetPreviousTick, cameraInstance.target, cameraInstance.trackLag, cameraInstance.target

        return

## preDraw

    location = []
    target = []
    side = []

    preDraw = (cameraInstance, progress) ->
        if not cameraInstance.firstDraw
            matrix.copy cameraInstance.transform, cameraInstance.transformPreviousDraw

        vector.interpolate cameraInstance.locationPreviousTick, cameraInstance.location, progress, location
        vector.interpolate cameraInstance.targetPreviousTick, cameraInstance.target, progress, target
        vector.interpolate cameraInstance.sidePreviousTick, cameraInstance.side, progress, side

        ik.lookAt location, target, side, cameraInstance.transform
        matrix.invert cameraInstance.transform, cameraInstance.transform

        if cameraInstance.firstDraw
            matrix.copy cameraInstance.transform, cameraInstance.transformPreviousDraw
            cameraInstance.firstDraw = false
        return

    module.exports = { create, tick, preDraw }

error: pathspec 'src/cameras/thirdPerson.litcoffee' did not match any file(s) known to git


Initial commit

Initial commit

readme.litcoffee - a Literate CoffeeScript.

gQL®: An efficient Genome Query Language.
-----------------------------------------
[![Apache License](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/BioQL/BioQL/blob/master/LICENSE)

error: pathspec 'src/gql/readme.litcoffee' did not match any file(s) known to git


Nest angular properties

Nest angular properties

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Piot/boardgamedb.git/'


changed text in link to example gulpfile

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/curlee/Appnostic.git/'


Initial commit

pQL®: An efficient Proteome Query Language.
-------------------------------------------
[![Apache License](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/BioQL/BioQL/blob/master/LICENSE)

error: pathspec 'src/pql/readme.litcoffee' did not match any file(s) known to git


Update game.litcoffee

Update game.litcoffee

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/hansoksendahl/twitterpated.git/'


Taking things literally with lists

Taking things literally with lists

# can.List

	steal "can/util", "can/map", (can, Map) ->

can.List takes care of creating observable lists.

### Misc utilities

We'll use these later in the implementation of `can.List#splice` to
iron out cross-browser issues, hence why this is up here.

	splice = [].splice

We test whether the browser's native `splice` method removes properties, or
merely changes `.length`. Our `can.List#splice` method will be adapted
accordingly such that it *does* remove them. IE doesn't remove properties,
specifically, so we test for that.

	spliceRemovesProps = ->
		obj =
			0: "a"
			length: 1
		splice.call obj, 0, 1
		not obj[0]

## Main implementation

Here we subclass `can.Map` to create our `List`. We will treat the `Map` as
an array-like.

	class list extends Map

`Map` can be changes statically for new `can.List` classes
to any subclass of `can.Map`. This controls what constructor
to use when turning objects into `Map`s in the various
relevant `can.List` methods.

	@Map: Map

And now we set up our constructor...

	constructor: (instances = [], options) ->

By managing `.length`, we make this object an array-like.

		@length = 0;

The `cid` is used to namespace event handlers.

		can.cid this, ".map"

`@_init` this instance as currently initializing.

		@_init = 1

Now we check if `instances` is a promise because we have special behavior
in that case...

		if can.isDeferred instances

The array passed into `new can.List()` can be a promise, in which case we
pass it straight to `can.List#replace()`. This will let us have a working
list, and automatically replace its contents when the promise resolves.

			@replace instances
		else

If our instances argument isn't a promise, we assume it's an array.

The `teardownMapping` is an implementation detail of `can.Map` used for
memory safety. Refer to can/map for more details on how it works.

			teardownMapping = instances.length and can.Map.helpers.addToMap instances, this

Then we `push` each individual element into our new `List`.

			@push.apply @, can.makeArray instances

And here we use `teardownMapping` if the `can.Map` helper actually returned
one.

		teardownMapping() if teardownMapping

Whenever a change event happens, we also take note of it in the
`_changes()` method.

		@bind 'change', can.proxy @_changes, @
		can.simpleExtend @, options

No longer initializing, soooo...

		delete @_init

### _triggerChange

	_triggerChange: (attr, how, newVal, oldVal) ->

On top of triggering the change event, we manually trigger `add`, `length`,
and `remove` events manually, depending on `how`.

		Map::_triggerChange.apply @, arguments

We definitely do not trigger any `add`/`remove` events ourselves that
involve dots, so we skip this section in those cases (all of the triggers
for `can.List` itself are done on indices)

		if not ~attr.indexOf '.'

If a new item is added...

			if how is 'add'

...trigger the two events, with newVal and the ndex as the values.

				can.batch.trigger @, how, [newVal, +attr]
				can.batch.trigger @, 'length', [this.length]

otherwise if an item is removed...

			else if how is 'remove'

Then do the same, but using the old value (the item removed) in the
`remove` event.

				can.batch.trigger @, how, [oldVal, +attr]
				can.batch.trigger @, 'length', [this.length]

And just trigger anything else, whatever it is, because why not? As long as
it has no dot, of course.

			else
				can.batch.trigger @, how, [newVal, +attr)

### __get

This overrides `can.Map`'s version of this method, which is meant to be used to implement the reading version of `attr()`, **after** dotted keys (`"foo.bar.baz"`) have already been split up.

	__get: (attr) -> if attr then this[attr] else @

### ___set

This overrides `can.Map`'s version of this method, which does the final
data assignment to out `List` object.

	___set: (attr, val) ->
		@[attr] = val;

nd we use it this way mainly to update the length of
our array-like.

		@length = (+attr + 1) if (+attr >= @.length)

### _each

The regular `can.Map` method provides a `for..in`-style
loop, but this object is supposed to act like an array -- so
we treat it like one.

	_each: (callback) ->
		data = @__get();

Note how this method, unlike the native `Array#forEach`,
calls its callback on `undefined` for undefined array
indices, which is more in like with `jQuery`'s `$.each`
utility.

		callback datum, i for datum, i in data

### _bindsetup

Used by `can/util/bind` to set up live-binding.

	_bindsetup: Map.helpers.makeBindSetup("*"),

### serialize

Returns the serialized form of this list (meaning, a regular `Array`, not
JSON).

	serialize: -> Map.helpers.serialize @, 'serialize', []

### splice

We write our own splice implementation to make sure arguments are properly
converted to observable objects and events are fired as planned.

	splice: (index, howMany = @length - index) ->
		args = can.makeArray arguments

Arguments after the first two (`index`, `howMany`) are converted to `Map`s
(or whatever the static `can.List.Map` configuration is) when
appropriate. These new observables will also bubble their own events up to
the List, because of `hookupBubble`.

		for val, i in args
			if i >= 2 and Map.helpers.canMakeObserve val
				args[i] = Map.helpers.hookupBubble val, "*", @, @.constructor.Map, @.constructor);

We call the native `splice` function here with our constructed
arguments. We will use `removed` to trigger appropriate removal events.

		removed = splice.apply @, args

Some browsers' splice methods do not actually delete properties, so we do
 it manually here. The test for whether this is necessary is at the top of
 the file.

		if not spliceRemovesProps
			for thing, i in removed
				delete @[i]

We batch the process of triggering `remove` and `add` events. On removal,
we also stop removed items from bubbling their events up to the `List`,
with `Map.helpers.unhookup`. Added items have already been given the
`hookupBubble` treatment above, so we don't need to do it in here.

		can.batch.start()

		if howMany > 0
			@_triggerChange "" + index, "remove", undefined, removed
			Map.helpers.unhookup removed, @
		if args.length > 2
			@_triggerChange "" + index, "add", args.slice(2), removed

		can.batch.stop();

To reflect the API of the native `splice` method, we return a plain array
of objects that were removed.

		removed;

### _attrs

`_attrs()` implements part of the functionality of
`can.List#attr()`. It is invoked in two situations:

1. When `attr()` is called without any arguments

2. When `attr()` is called with an array or array-like of values.

	_attrs: (items, remove) ->

When invoked like `.attr()`, with no arguments, we simply return an `Array`
version of the list, recursively converted to regular JS objects.

		if items is undefined
			Map.helpers.serialize(this, 'attr', []);

Otherwise, `items` is expected to be either an `Array` or an
`Array`-like. From here, we will force `items` to be a proper `Array` copy
of the original argument and call `_updateAttrs()` inside a batch to
prevent event spam.

		else
			items = can.makeArray items
			can.batch.start()
			@_updateAttrs items, remove
			can.batch.stop()

### _updateAttrs

TODO

	_updateAttrs: function (items, remove) {
		var len = Math.min(items.length, this.length);

		for (var prop = 0; prop < len; prop++) {
			var curVal = this[prop],
				newVal = items[prop];

			if (Map.helpers.canMakeObserve(curVal) && Map.helpers.canMakeObserve(newVal)) {
				curVal.attr(newVal, remove);
				//changed from a coercion to an explicit
			} else if (curVal !== newVal) {
				this._set(prop, newVal);
			} else {

			}
		}
		if (items.length > this.length) {
			// Add in the remaining props.
			this.push.apply(this, items.slice(this.length));
		} else if (items.length < this.length && remove) {
			this.splice(items.length);
		}
	}

### indexOf

	indexOf: (item, fromIndex) ->

We force computes and helpers to watch out for length changes on this
`can.List` if the `indexOf` method is used.

		@attr('length');

But otherwise, `can.List#indefOf()` works just like native, piggybacking on
the `can.inArray` utility from `can.util`.

		can.inArray item, @, fromIndex

### join

`can.List#join()` is just like the native `Array#join()`, except we
 serialize to a regular array before calling the native method.`

	join: -> [].join.apply @attr(), arguments

### reverse

The native `Array#reverse` method already does in-place reversing for us,
so we just use that. Note that this method will not fire `set` events, as
doing the same thing manually with `attr()` usually would.

	reverse: [].reverse,

### slice

	slice: ->

Our `slice()` is just a call to `Array#slice`...

		temp = Array.prototype.slice.apply @, arguments

... that returns an `List` of the same type as `this`.

		new @constructor temp

### concat

	concat: ->
		args = []

We want to turn any `can.List` arguments into plain old `Array`s,

		for arg, i in can.makeArray arguments
			args[i] = if arg instanceof can.List then arg.serialize() else arg

so we can use the native `Array#concat` method for
concatenation, before turning the results into a new
`can.List` with the same prototype as `this`.

		new @constructor (Array.prototype.concat.apply @serialize(), args)

### forEach

We delegate `List#forEach` to the cross-browser generic implementation in
`can.util`.

	forEach: (cb, thisarg = @) -> can.each @, cb, thisarg

### replace

Our reimplementation of `Array#replace()` adds support for replacing with
the results of promiñses, but otherwise works just like `List#splice()`.

	replace: (newList) ->

If `newList` is a promise, we queue a replacement with the promise's
result. Note that this method returns the current `List`, so if you want to
take any other action after the promise resolves, you'll need to do
`.then()` on the promise externally, instead of using the returned value
from this.

		if (can.isDeferred(newList)) {
			newList.then(can.proxy(this.replace, this));
		else

This regular `List#splice()` call will replace all
elements in the `List` and run all the usual event
hookups that that method does.

			this.splice.apply(this, [0, this.length].concat(can.makeArray(newList || [])));

We return the current `List`, even if a promise was passed in.

		this;

### getArgs

Converts to an `array` of arguments.

	getArgs = (args) ->
		if args[0] and can.isArray args[0]
			args[0]
		else
			can.makeArray arg)

### push, pop, shift, and unshift

Create `push`, `pop`, `shift`, and `unshift`

	obj =
		push: "length"
		unshift: 0

Adds a method `name` - The method name.	 `where` - Where items in the
`array` should be added.

	for where, name in obj
		orig = [][name];
		list::[name] = ->

Get the items being added.

			args = []

Where we are going to add items.

			len = if where then @length else 0
			i = arguments.length

Go through and convert anything to an `map` that needs to be converted.

			while (i--)
				val = arguments[i]
				args[i] = if Map.helpers.canMakeObserve val
					Map.helpers.hookupBubble val, "*", @, @constructor.Map, @constructor
				else
					val

Call the original method.

			res = orig.apply(this, args);

			if not this.comparator or args.length
				@_triggerChange "" + len, "add", args, undefined

And then return res

			res

We do the same thing for pop/length:

	obj =
		pop: "length"
		shift: 0

Creates a `remove` type method

	for where, name in obj
		list::[name] = ->
			args = getArgs arguments
			len = if where and this.length then this.length - 1 else 0

			res = [][name].apply @, args

Create a change where the args are
* `len` - Where these items were removed.
* `remove` - Items removed.
* `undefined` - The new values (there are none).
* `res` - The old, removed values (should these be unbound).

			@_triggerChange "" + len, "remove", undefined, [res]

			if res and res.unbind
				can.stopListening.call @, res, "change"

and just return `res`

		res

## The End

	can.List = Map.List = list;

error: pathspec 'list/list.litcoffee' did not match any file(s) known to git


initial commit

initial commit

Do nothing without the presence of Underscore.

    return unless _?

This regular expression includes words we won't capitalize.

    noncapsWords = /^(a|an|and|as|be|of|the|to)$/

...and this helps to split "cased" words into their constituent parts...

    caseSplitter = /[-_\s]|(!?[A-Z][a-z]*)/g

...so we can join them together with some glue.

    cleanJoin    = (arr, glue) -> _.compact(arr).join(glue)

Assuming we are using the English language, we'll declare some numbers

    numbers =
      "EN": ["one", "two", "three", "four", "five", "six", "seven","eight", "nine", "ten", "eleven", "twelve"]
      "FR": [{m: "un", f: "une"}, "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze"]
      "DE": ["eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun", "zehn", "elf", "zwölf"]
      "IT": [{m: "uno", f: "una"}, "due", "tre", "quattro", "cinque", "sei", "sette", "otto", "nove", "dieci", "undici", "dodic"]

Declare the mixins we're adding...

    mixins =

Capitalize the first letter of a string.

      capitalize: (str) ->
        str.charAt(0).toUpperCase() + str.slice(1)

Capitalize all words in a string apart from some common lower case words.

```coffee
_.capitalizeAll("this and that") # "This and That"
```

      capitalizeAll: (str, regEx = noncapsWords) ->
        _.map(str.split(" "), (s, i) ->
          if i > 0 and regEx.test(s) then s else _.capitalize(s)
        ).join(" ")

Transform strings between common code cases:

```coffee
_.camelCase("some-thing")  # "someThing"
_.hyphenCase("some_thing") # "some-thing"
_.snakeCase("someThing")   # "some_thing"
_.humanCase("fromA_to-Z")  # "from A to Z"
```

      camelCase: (str) ->
        str.replace(/[\s_-](\w)/g, (a, w) -> w.toUpperCase())

      hyphenCase: (str) ->
        cleanJoin(str.split(caseSplitter), "-").toLowerCase()

      snakeCase: (str) ->
        cleanJoin(str.split(caseSplitter), "_").toLowerCase()

      humanCase: (str) ->
        cleanJoin(str.split(caseSplitter), " ")

Enclose a string inside an HTML tag.

```coffee
_.wrapInTag("Hello world!", "strong") # "<strong>Hello world!</strong>"
```

      wrapInTag: (str, tag = "span") ->
        "<#{tag}>#{str}</#{tag}>"

Group strings into a grammatically correct list with an arbitrary limit.

```coffee
_.prettyList(["Ben", "Bob"])                    # "Ben and Bob"
_.prettyList(["Ben", "Bob", "Bill"])            # "Ben, Bob and Bill"
_.prettyList(["Ben", "Bob", "Bill", "Max"], 2)  # "Ben, Bob and 2 more"
_.prettyList(["Ben", "Bob"], 1, "other")        # "Ben and 1 other"
```

      prettyList: (arr, max, opts = {}) ->
        more   = opts.more or "more"
        amp    = opts.amp or "and"

        if opts.wrap
          arr = _.map(arr, (s) -> _.wrapInTag(s, opts.wrap))

        if max < (len = arr.length)
          diff = len - max
          more = _.quantify(more, diff, false) if opts.quantify
          diff = _.writtenNumber(diff, opts.numbers) if opts.written
          arr  = arr.slice(0, max)
          arr  = arr.concat("#{diff} #{more}")

        arr
          .slice(0, -1)
          .join(", ")
          .concat((if arr.length is 1 then "" else " #{amp} "), arr.slice(-1))

Add soft hyphens every `n` characters so that the CSS attribute
`hyphens: manual` will allow for nice breaks in long strings of text.

```coffee
_.hyphenate("antidisestablishmentarianism") # "antidisest%C2%ADablishment%C2%ADarianism"
```

      hyphenate: (str = "", n = 10, softHyphen = "\u00AD") ->
        str.replace new RegExp("\\w{#{n}}", "g"), (w) -> w + softHyphen

Add an "s" to a string when an amount is non-singular, disregarding the
order of the arguments passsed.

```coffee
_.prettyList("monkey", 1)                   # "1 monkey"
_.prettyList(1, "monkey")                   # "1 monkey"
_.prettyList("monkey", 9, written: true)    # "nine monkeys"
```

      quantify: (str, n, {numberless, written, lang}) ->
        { n, str } = str: n, n: str unless _.isString(str)

        _n = _.writtenNumber(n, lang) if written?

        (if numberless? then "" else _n + " ") +
        str +
        (if n is 1 then "" else "s")

Convert numbers between one and twelve into their written counter-parts.

```coffee
_.writtenNumber(1)                          # "one"
_.writtenNumber(2, ["ein", "zwei", "drei"]) # "zwei"
```

      writtenNumber: (n, lang = "EN", gender ?= "m") ->
        if n < 13
          num = numbers[lang][n - 1]
          num[gender] and num[gender] or num
        else
          n

      setNumbers: (arr, lang) ->
        numbers[lang] = arr

Add the mixin object to Underscore.

    _.mixin(mixins)


Add docs for View lifecycle events

Add docs for View lifecycle events

# /api/App Components/Batman.View/Batman.View Lifecycle

When [`Batman.View`](/docs/api/batman.view.html) is rendered, it goes through many steps. Lifecycle callbacks allow you to hook into those steps and control or respond to the rendering process.

### Listening for Lifecycle Events

To set up handlers for a `View`'s lifecycle, You can either call [`on`](/docs/api/batman.eventemitter.html#prototype_function_on) on the `View`'s  prototype:

```coffeescript
class MyApp.FadingView extends Batman.View
  @::on 'viewWillAppear', -> $(@get('node')).hide()

  @::on 'viewDidAppear', -> $(@get('node')).fadeIn('fast')

  @::on 'viewWillDisappear', -> $(@get('node')).fadeOut('fast')
```

or define functions with the same name as the events:

```coffeescript
class MyApp.FadingView extends Batman.View
  viewWillAppear: -> $(@get('node')).hide()

  viewDidAppear: -> $(@get('node')).fadeIn('fast')

  viewWillDisappear: -> $(@get('node')).fadeOut('fast')
```

### Lifecycle Events and Subviews

A `View` propagates its lifecycle events to its [`subviews`](/docs/api/batman.view.html#prototype_property_subviews), so it's likely that a lifecycle event will be called more than once. `ready` is an exception -- it's a one-shot event.

## viewWillAppear

The view is about to be attached to the DOM. It has a [`superview`](/docs/api/batman.view.html#prototype_property_superview).

## viewDidAppear

The view has just been attached to the DOM. Its [`node`](/docs/api/batman.view.html#prototype_accessor_node) is on the page and can be selected with `document.querySelector`.

## viewDidLoad

The view's [`node`](/docs/api/batman.view.html#prototype_accessor_node) has been loaded from its [`html`](/docs/api/batman.view.html#prototype_accessor_html)). It may not be in the DOM yet.

## ready

The view's bindings have been initialized. The view may or may not be in the DOM. `ready` is a one-shot event.

## viewWillDisappear

The view is about to be detached from the DOM. It still has a [`superview`](/docs/api/batman.view.html#prototype_property_superview) and its [`node`](/docs/api/batman.view.html#prototype_accessor_node) is still selectable.

## viewDidDisappear

The view has been detached from the DOM. It still has a [`superview`](/docs/api/batman.view.html#prototype_property_superview) but its [`node`](/docs/api/batman.view.html#prototype_accessor_node) is not selectable.

## destroy

[`die`](/docs/api/batman.view.html#prototype_function_die) was called on this view. It will be removed from its superview, removed from the DOM, and have all of its properties set to `null`.

## viewDidMoveToSuperview

The view has been attached to its [`superview`](/docs/api/batman.view.html#prototype_property_superview), but it is not yet in the DOM.

## viewWillRemoveFromSuperview

The view is about removed from its [`superview`](/docs/api/batman.view.html#prototype_property_superview), then it will be detached from the DOM.

error: pathspec 'docs/13a_view_lifecycle.litcoffee' did not match any file(s) known to git


Add partials task

Add partials task


Added a new png renderer, PngRoof, to render block roof segments

    PngRenderer = require './png_renderer'

    class PngRoof extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        segment_width   = 1 / ground_segments
        ground_width    = cell_width * segment_width

        fill_segment = (block, segment, height, color) =>
          right  = segment * (cell_width * segment_width)
          left   = (cell_width * block.x) + right
          top    = (cell_height * block.y)

          ctx.fillStyle = color

          ctx.beginPath()
          ctx.rect left, top, ground_width, height
          ctx.fill()

        for step in path
          if step.roof?
            for f, i in step.roof
              fill_segment step, i, f.thickness, 'red' if f?

    module.exports = PngRoof

error: pathspec 'lib/render-pipelines/png/png_roof.litcoffee' did not match any file(s) known to git


Published with https://stackedit.io/

Published with https://stackedit.io/

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/candril/tavola.git/'


Add png_platforms renderer

Add png_platforms renderer

    PngRenderer = require './png_renderer'

    class PngPlatforms extends PngRenderer

      do_render: (ctx, path) =>
        cell_width  = @config.width
        cell_height = @config.height

        ground_segments = @config.ground.segments
        wall_segments   = @config.wall.segments

        segment_width  = 1 / ground_segments
        segment_height = 1 / wall_segments

        ground_width = cell_width  * segment_width
        wall_height  = cell_height * segment_height

        fill_segment = (block, platform, color) =>
          for p in platform
            height =  p.thickness * wall_height
            left   = (cell_width  * block.x) + (p.x * ground_width)
            top    = (cell_height * block.y) + (p.y * wall_height) - height

            ctx.fillStyle = color

            ctx.beginPath()
            ctx.rect left, top, ground_width, height
            ctx.fill()

        for block, i in path
          for p in block.platforms
            fill_segment block, p, 'blue'

    module.exports = PngPlatforms

error: pathspec 'lib/render-pipelines/png/png_platforms.litcoffee' did not match any file(s) known to git


new file:   client/models/settings.litcoffee

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/ai10/radioscope-lnr.git/'


Recompile when elements src is changed

Recompile when elements src is changed


Add new delegates file

Add new delegates file

    randomIn = (r) => Math.random() * (r.max - r.min) + r.min

    roundedRandomIn = (r) => Math.round randomIn r

    render_cube = ({ x, y, width, height }) ->
      left   = [ ]
      top    = [ ]
      right  = [ ]
      bottom = [ ]
      front  = [ ]
      back   = [ ]

      left.push [ x, y,          0 ]
      left.push [ x, y + height, 0 ]
      left.push [ x, y + height, 1 ]
      left.push [ x, y + height, 1 ]
      left.push [ x, y,          1 ]
      left.push [ x, y,          0 ]

      top.push [ x,         y + height, 0 ]
      top.push [ x + width, y + height, 0 ]
      top.push [ x + width, y + height, 1 ]
      top.push [ x + width, y + height, 1 ]
      top.push [ x,         y + height, 1 ]
      top.push [ x,         y + height, 0 ]

      right.push [ x + width, y + height, 0 ]
      right.push [ x + width, y,          0 ]
      right.push [ x + width, y,          1 ]
      right.push [ x + width, y,          1 ]
      right.push [ x + width, y + height, 1 ]
      right.push [ x + width, y + height, 0 ]

      bottom.push [ x + width, y, 0 ]
      bottom.push [ x,         y, 0 ]
      bottom.push [ x,         y, 1 ]
      bottom.push [ x,         y, 1 ]
      bottom.push [ x + width, y, 1 ]
      bottom.push [ x + width, y, 0 ]

      front.push [ x,         y,          1 ]
      front.push [ x,         y + height, 1 ]
      front.push [ x + width, y + height, 1 ]
      front.push [ x + width, y + height, 1 ]
      front.push [ x + width, y,          1 ]
      front.push [ x,         y,          1 ]

      back.push [ x,         y,          0 ]
      back.push [ x + width, y,          0 ]
      back.push [ x + width, y + height, 0 ]
      back.push [ x + width, y + height, 0 ]
      back.push [ x,         y + height, 0 ]
      back.push [ x,         y,          0 ]

      { left, top, right, bottom, front, back }

    render_pass = (i, x, y, wdth, step, faces) ->
      args =
        x:      x + ((i + 1) * wdth)
        y:      y
        width:  wdth
        height: step * (i + 1)

      cube = render_cube args
      keys = Object.keys faces

      for k in keys
        for f in cube[k]
          faces[k].push f

    delegates =
      ground:    ({ x, y, width, height }) -> render_cube { x, y, width, height }
      stairs_up: ({ x, y, width, height }) ->
        faces =
          left:   [ ]
          top:    [ ]
          right:  [ ]
          bottom: [ ]
          front:  [ ]
          back:   [ ]

        max = Math.ceil width / height
        min = Math.ceil max   / 2

        wdth      = Math.ceil width / max
        step      = roundedRandomIn { min, max }
        remainder = width % wdth

        iterations = Math.floor width / wdth
        i          = 0

        while i < iterations
          render_pass i, x, y, wdth, step, faces
          ++i

        if remainder > 0
          render_pass i, x, y, remainder, step, faces

        faces

    module.exports = delegates

error: pathspec 'lib/delegates.litcoffee' did not match any file(s) known to git


fix typo

fix typo


Add literation

Add literation


Remove redundant code

Remove redundant code


Add guard

Add guard


Published with https://stackedit.io/

Published with https://stackedit.io/


[Character; minor] Fixed a GFM table (hopefully)

error: pathspec 'source/engine/07' did not match any file(s) known to git
error: pathspec 'Character.litcoffee' did not match any file(s) known to git


Listen for change event and delegate to controller


Add simple style task

Add simple style task


removed gulp minify css

removed gulp minify css


Initial commit

Initial commit

readme.litcoffee, a Literate CoffeeScript.

BioQL<sup>®</sup> [![Apache License](https://img.shields.io/badge/license-Apache-blue.svg)](https://github.com/BioQL/BioQL/blob/master/LICENSE)
------
> An efficient Query Language for interpreting genome and proteome.

error: pathspec 'src/readme.litcoffee' did not match any file(s) known to git


See #18. Add example Literate CoffeeScript file.

The **Scope** class regulates lexical scoping within CoffeeScript. As you
generate code, you create a tree of scopes in the same shape as the nested
function bodies. Each scope knows about the variables declared within it,
and has a reference to its parent enclosing scope. In this way, we know which
variables are new and need to be declared with `var`, and which are shared
with external scopes.

Import the helpers we plan to use.

    {extend, last} = require './helpers'

    exports.Scope = class Scope

The `root` is the top-level **Scope** object for a given file.

      @root: null

Initialize a scope with its parent, for lookups up the chain,
as well as a reference to the **Block** node it belongs to, which is
where it should declare its variables, and a reference to the function that
it belongs to.

      constructor: (@parent, @expressions, @method) ->
        @variables = [{name: 'arguments', type: 'arguments'}]
        @positions = {}
        Scope.root = this unless @parent

Adds a new variable or overrides an existing one.

      add: (name, type, immediate) ->
        return @parent.add name, type, immediate if @shared and not immediate
        if Object::hasOwnProperty.call @positions, name
          @variables[@positions[name]].type = type
        else
          @positions[name] = @variables.push({name, type}) - 1

When `super` is called, we need to find the name of the current method we're
in, so that we know how to invoke the same method of the parent class. This
can get complicated if super is being called from an inner function.
`namedMethod` will walk up the scope tree until it either finds the first
function object that has a name filled in, or bottoms out.

      namedMethod: ->
        return @method if @method?.name or !@parent
        @parent.namedMethod()

Look up a variable name in lexical scope, and declare it if it does not
already exist.

      find: (name) ->
        return yes if @check name
        @add name, 'var'
        no

Reserve a variable name as originating from a function parameter for this
scope. No `var` required for internal references.

      parameter: (name) ->
        return if @shared and @parent.check name, yes
        @add name, 'param'

Just check to see if a variable has already been declared, without reserving,
walks up to the root scope.

      check: (name) ->
        !!(@type(name) or @parent?.check(name))

Generate a temporary variable name at the given index.

      temporary: (name, index) ->
        if name.length > 1
          '_' + name + if index > 1 then index - 1 else ''
        else
          '_' + (index + parseInt name, 36).toString(36).replace /\d/g, 'a'

Gets the type of a variable.

      type: (name) ->
        return v.type for v in @variables when v.name is name
        null

If we need to store an intermediate result, find an available name for a
compiler-generated variable. `_var`, `_var2`, and so on...

      freeVariable: (name, reserve=true) ->
        index = 0
        index++ while @check((temp = @temporary name, index))
        @add temp, 'var', yes if reserve
        temp

Ensure that an assignment is made at the top of this scope
(or at the top-level scope, if requested).

      assign: (name, value) ->
        @add name, {value, assigned: yes}, yes
        @hasAssignments = yes

Does this scope have any declared variables?

      hasDeclarations: ->
        !!@declaredVariables().length

Return the list of variables first declared in this scope.

      declaredVariables: ->
        realVars = []
        tempVars = []
        for v in @variables when v.type is 'var'
          (if v.name.charAt(0) is '_' then tempVars else realVars).push v.name
        realVars.sort().concat tempVars.sort()

Return the list of assignments that are supposed to be made at the top
of this scope.

      assignedVariables: ->
        "#{v.name} = #{v.type.value}" for v in @variables when v.type.assigned

error: pathspec 'spec/samples/scope.litcoffee' did not match any file(s) known to git


Watch css files

Watch css files


minor documentation update on restjson

minor documentation update on restjson

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/LiuJinliang/yangforge.git/'


Literate CofeeScript underway

Literate CofeeScript underway

((root) ->
  root = this
  _ = root._ || require 'underscore'
  F = {}

  # Non-determinism
  # ---------------

  F.succeed = (result) -> [result]
  F.fail = _.always []

  disjunction = (l, r) ->
    (x) -> _.cat(l(x), r(x))
  conjunction = (l, r) ->
    (x) -> _.mapcat(l(x), r)

  F.disj = () ->
    return F.fail if _.isEmpty(arguments)
    disjunction(_.first(arguments), F.disj.apply(this, _.rest(arguments)))

  F.conj = () ->
    clauses = _.toArray(arguments)
    return F.succeed if _.isEmpty(clauses)
    return _.first(clauses) if _.size(clauses) is 1
    conjunction(_.first(clauses),
                (s) -> F.conj.apply(null, _.rest(clauses))(s))

  # Knowledge representation
  # ------------------------

  class LVar
    constructor: (@name) ->

  F.lvar = (name) -> new LVar(name)
  F.isLVar = (v) -> (v instanceof LVar)

  find = (v, bindings) ->
    lvar = bindings.lookup(v)
    return lvar if F.isLVar(v)
    if _.isArray(lvar)
      if _.isEmpty(lvar)
        return lvar
      else
        return _.cons(find(_.first(lvar), bindings), find(_.rest(lvar), bindings))
    lvar

  class Bindings
    constructor: (seed = {}) ->
      @binds = _.merge({}, seed)
    extend: (lvar, value) ->
      o = {}
      o[lvar.name] = value
      new Bindings(_.merge(@binds, o))
    has: (lvar) ->
      @binds.hasOwnProperty(lvar.name)
    lookup: (lvar) ->
      return lvar if !F.isLVar(lvar)
      return this.lookup(@binds[lvar.name]) if this.has(lvar)
      lvar

  F.ignorance = new Bindings()

  # Unification
  # -----------

  F.unify = (l, r, bindings) ->
    t1 = bindings.lookup(l)
    t2 = bindings.lookup(r)

    if _.isEqual(t1, t2)
      return s
    if F.isLVar(t1)
      return bindings.extend(t1, t2)
    if F.isLVar(t2)
      return bindings.extend(t2, t1)
    if _.isArray(t1) && _.isArray(t2)
      s = F.unify(_.first(t1), _.first(t2), bindings)
      s = if (s isnt null) then F.unify(_.rest(t1), _.rest(t2), bindings) else s
      return s
    return null

  # Operational logic
  # -----------------

  F.goal = (l, r) ->
    (bindings) ->
      result = F.unify(l, r, bindings)
      return F.succeed(result) if result isnt null
      return F.fail(bindings)

  F.run = (goal) ->
    goal(F.ignorance)

  # Logico
  # ------

  F.choice = ($v, list) ->
    return F.fail if _.isEmpty(list)

    F.disj(F.goal($v, _.first(list)),
                  F.choice($v, _.rest(list)))

  # Exports and sundries
  # --------------------

  if module?
    module.exports = F
  else
    root.F = F

)(this)

error: pathspec 'lib/friebyrd.litcoffee' did not match any file(s) known to git


Update game.litcoffee

Update game.litcoffee


Fixes tabbed code test in literate

Fixes tabbed code test in literate

Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code must be separated from text by a blank line.

    test "code blocks must be preceded by a blank line", ->

The next line is part of the text and will not be executed.
      fail()

      ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List

Tabs work too:

				test "tabbed code", ->
					ok yes


Created a new "file" module as part of #13.

# file

This is a module which abstracts reading files.  It should be used when they are
impractical to embed into the JavaScript.

While at present this performs AJAX, this module could be replaced to access
local files for non-web distribution.

    handleError = require "./handleError.litcoffee"

## arrayBuffer

- filename: The "real" location of the binary file to load.  (i.e. already
            "require"-d)
- callback: A callback to execute with the ArrayBuffer loaded from the file on
            success.

    arrayBuffer = (path, callback) ->
        request = new XMLHttpRequest()
        request.open "GET", path, true
        request.responseType = "arraybuffer"
        request.onreadystatechange = ->
            if request.readyState is 4
                if request.status is 200
                    callback request.response
                else
                    handleError "Failed to load binary file " + path
            return
        request.send null
        return

    module.exports = { arrayBuffer }

error: pathspec 'src/file.litcoffee' did not match any file(s) known to git


Dont concat for now, also remove lint

Dont concat for now, also remove lint

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Piot/boardgamedb.git/'


Test case for literal mode: Code blocks must be preceded by blank line.

Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code must be separated from text by a blank line.

    test "code blocks must be preceded by a blank line", ->

The next line is part of the text and will not be executed.
      fail()

      ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List

Tabs work too:

	test "tabbed code", ->
		ok yes

Literate CoffeeScript Test
--------------------------

comment comment

    test "basic literate CoffeeScript parsing", ->
      ok yes

now with a...

    test "broken up indentation", ->

... broken up ...

      do ->

... nested block.

        ok yes

Code in `backticks is not parsed` and...

    test "comments in indented blocks work", ->
      do ->
        do ->
          # Regular comment.

          ###
            Block comment.
          ###

          ok yes

Regular [Markdown](http://example.com/markdown) features, like links
and unordered lists, are fine:

  * I

  * Am

  * A

  * List

Tabs work too:

	test "tabbed code", ->
		ok yes


Remove unused deps

Remove unused deps


moved tasks around and added a heading

moved tasks around and added a heading

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/curlee/Appnostic.git/'


Separation of command concerns

Separation of command concerns

`test` command handler
===

	fs = require 'fs-sync'
	path = require 'path'
	exec = require('child_process').exec
	spawn = require('child_process').spawn
	pkg = require(path.join(__dirname, '../package.json'))

	module.exports = (end) ->

		if not fs.exists path.join(__dirname, '../factory/node_modules')
			factory = path.join(__dirname, '../factory')
			console.log "Installing NPM Modules for /factory..."
			exec("cd #{factory} && npm install")
			console.log "Successfully installed NPM Modules in /factory/node_modules".green

		testTarget = path.join(__dirname, '../test/**/*litcoffee')
		test = spawn("mocha", ["--compilers", "coffee:coffee-script/register", testTarget])
		test.stdout.pipe(process.stdout)
		test.stderr.pipe(process.stderr)

error: pathspec 'bin/test.litcoffee' did not match any file(s) known to git


Published with https://stackedit.io/

Published with https://stackedit.io/


document singular associations

document singular associations

# /api/App Internals/Batman.Association/Batman.SingularAssociation

`Batman.SingularAssociation` extends `Batman.Association`.

## ::constructor(@model, @label, options={}) : SingularAssociation

Adds default options:

- `namespace`: `Batman.currentApp`
- `name`: camelized `@label`

## ::getAccessor(association, model, label)

Checks for the associated record in this record's attributes. Otherwise returns a proxy. Loads the associated record if `options.autoload`.

## ::setIndex() : UniqueAssociationSetIndex

Returns a `Batman.UniqueAssociationSetIndex` based on `indexRelatedModelOn` which is suitable for finding the associated record in its loaded set.

## ::associationProxy(association)

Returns a suitable proxy object for `association`.

## ::.indexRelatedModelOn

Implemented by subclasses to be used in `setIndex`.

## ::.isSingular[=true]

Identifies the `SingularAssociation`.

# /api/App Internals/Batman.Association/Batman.BelongsToAssociation

`Batman.BelongsToAssociation` extends `Batman.SingularAssociation` and provides backing to associations declared with `@belongsTo` in model definitions.

## ::constructor(model, label, options)

Returns a `Batman.PolymorphicBelongsToAssociation` if `options.polymorphic` is true.

## ::encoder() : Function

Returns a function that serializes a record by calling `toJSON` on it.

## ::decoder() : Function

Returns a function that loads a record by:

- using `createFromJSON` to instantiate the record
- if the inverse is a `Batman.HasManyAssociation`, setting the foreignKey from the parent record
- if the inverse is something else (eg, `Batman.HasOneAssociation`), setting the parent's `inverse.label` to this record.
- setting the record's `association.label` to the parent record

## ::apply(record)

Update's `record`'s foreignKey to the associated record's primaryKey.

## ::.defaultOptions

- `saveInline`: false
- `autoload`: true
- `encodeForeignKey`: true

## ::.proxyClass[=Batman.BelongsToProxy]

Returned by a `belongsTo` get accessor.

## ::.associationType[='belongsTo']

Used by `Batman.AssociationCurator` to store this association.

## ::.indexRelatedModelOn[='primaryKey']

Used to create a `Batman.UniqueAssociationSetIndex` to locate the associated record.

# /api/App Internals/Batman.Association/Batman.HasOneAssociation

`Batman.HasOneAssociation` extends `Batman.SingularAssociation` and backs associations declared with `@hasOne` in model definitions.

## ::constructor(model, label, options) : HasOneAssociation

Provides default options:

- `primaryKey`: "id"
- `foreignKey`: underscored `@model.resourceName` + `_id`

## ::apply(baseSaveError, base)

Called after `base` is saved. If there's no `baseSaveError`, updates the related record's foreign key to `base`'s primary key.

## ::encoder() : Function

Returns a function that serializes the record if `options.saveInline` is true.

## ::decoder() : Function

Returns a function that creates a related record from JSON and sets its `inverseOf`.

## ::.associationType[='hasOne']

Used by `Batman.AssociationCurator` to store the association.

## ::.proxyClass[=Batman.HasOneProxy]

Sometimes returned by a `hasOne` get accessor.

## ::.indexRelatedModelOn[='foreignKey']

Used to create a `Batman.UniqueAssociationSetIndex` to locate the associated record.

# /api/App Internals/Batman.Association/Batman.PolymorphicBelongsToAssociation

`Batman.PolymorphicBelongsToAssociation` extends `Batman.BelongsToAssociation` and backs associations declared with `@belongsTo "...", polymorphic: true`.

## ::constructor(model, label, options) : PolymorphicBelongsToAssociation

Calls `super`, then encodes the `foreignTypeKey` if option `encodeForeignTypeKey` was true.

## ::apply(base)

Extends `Batman.BelongsToAssociation::apply` by also updating `base`'s property for `foreignTypeKey`.

## ::getAccessor(association, model, label)

Returns either a record from attributes or a proxy. Loads the proxy if the autoload option was true.

## ::url(recordOptions) : String

Returns a suitable URL for the association.

## ::getRelatedModelForType(type)

Tries to find a model in the association's scope with `type` or camelized `type`.

## ::setIndexForType(type) : PolymorphicUniqueAssociationSetIndex

Returns a set index to find the associated record for `type`.

## ::.typeIndices : Object

A cache of `Batman.PolymorphicUniqueAssociationSetIndex`es for this association.

## ::inverseForType(type) : Association

Looks up the inverse association for `type` by using `@options.inverseOf` or checking all associations on the return value of`getRelatedModelForType` for `type`.

## ::decoder() : Function

Returns a function that loads a record from JSON, taking into account the `foreignTypeKey`.

## ::.isPolymorphic[=true]

Labels this association as polymorphic.

## ::.proxyClass[=Batman.PolymorphicBelongsToProxy]

Returned when the associated record isn't found in attributes.

## ::.defaultOptions

Extends `Batman.BelongsToAssociation`'s  default options and adds:

- `encodeForeignTypeKey`: true

## ::.getRelatedModel[=false]
## ::.setIndex[=false]
## ::.inverse[=false]

error: pathspec 'docs/03_app_internals/24b_singular_associations.litcoffee' did not match any file(s) known to git