Add structure for toggling tasks

Add structure for toggling tasks

--- lib/main.coffee
+++ lib/main.coffee
@@ -6,7 +6,7 @@
 module.exports =

   config:
-
+
     addListItems:
       title: 'Add new list-items'
       description: 'Automatically add a new list-item after the current (non-empty) one when pressing `ENTER`'
@@ -33,6 +33,9 @@
     # Add commands to overwrite the behavior of tab within list-item context
     @subscriptions.add atom.commands.add 'atom-workspace', 'markdown:indent-list-item': (event) => @indentListItem(event)
     @subscriptions.add atom.commands.add 'atom-workspace', 'markdown:outdent-list-item': (event) => @outdentListItem(event)
+
+    # Add command to toggle a task
+    @subscriptions.add atom.commands.add 'atom-workspace', 'markdown:toggle-task': (event) => @toggleTask(event)

     # Disable language-gfm as this package is intended as its replacement
     if atom.config.get('language-markdown.disableLanguageGfm')
@@ -153,8 +156,24 @@
       scopeDescriptor = editor.scopeDescriptorForBufferPosition(position)
       for scope in scopeDescriptor.scopes
         if scope.indexOf('list') isnt -1
-          return true
+          # NOTE
+          # return scope (which counts as true) which can be used to determine
+          # type of list-item
+          return scope
     return false
+
+  toggleTask: (event) ->
+    {editor, position} = @getEditorAndPosition(event)
+    listItem = @isListItem(editor, position)
+    if listItem and listItem.indexOf('task') isnt -1
+      if listItem.indexOf('completed') isnt -1
+        # TODO mark as incomplete
+        console.log "Mark as incomplete"
+      else
+        # TODO mark as complete
+        console.log "Mark as complete"
+    else
+      event.abortKeyBinding()

   # Loads the basic grammar structure,
   # which includes the grouped parts in the repository,


Update main.coffee

Update main.coffee

>If you were to pass it an invalid path or no path it'll throw an uncaught exception
This should be enough to check the user input before throwing it to checkInstallPath()
I have to admit that I'm new to io.js, so I'm not sure if there are better way to do this though.

--- lib/main.coffee
+++ lib/main.coffee
@@ -342,7 +342,17 @@
   openFolder()

 $(document).on 'input', '#install_path', ->
-  checkInstallPath($(this).val(), setInstallPath)
+  path = $(this).val()
+  fs.stat path, (err, stats)->
+    if err
+      $('#input_msg').attr('class', 'yellow')
+      $('#input_msg').text('No such file or directory')
+    else
+      if stats.isDirectory()
+        checkInstallPath(path, setInstallPath)
+      else
+        $('#input_msg').attr('class', 'yellow')
+        $('#input_msg').text('Not a directory. P')

 $('.github > a').click (e) ->
   e.preventDefault()


Add back the legacy service api deprecation

--- lib/main.coffee
+++ lib/main.coffee
@@ -98,5 +98,5 @@
   # Depreciated method associated with previous Services API
   # versioning that matched package version.
   legacyProvideStatusBar: ->
-     # Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
+     Grim.deprecate("Use versions ^1.0.0 of status-bar Service API.")
      @provideStatusBar()


Don't rely on screen rows and respect offsets

Screen buffer only counts unfolded lines so line numbers do not match
actual buffer lines.

--- lib/main.coffee
+++ lib/main.coffee
@@ -1,3 +1,5 @@
+tokenizedLineForRow = (textEditor, lineNumber) -> textEditor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(lineNumber)
+
 extractRange = ({code, message, lineNumber, colNumber, textEditor}) ->
   switch code
     when 'C901'
@@ -6,7 +8,7 @@
       symbol = /'(?:[^.]+\.)?([^']+)'/.exec(message)[1]
       while true
         offset = 0
-        tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+        tokenizedLine = tokenizedLineForRow(textEditor, lineNumber)
         if tokenizedLine is undefined
           break
         foundDecorator = false
@@ -25,7 +27,7 @@
       # E127 - continuation line over-indented for visual indent
       # E128 - continuation line under-indented for visual indent
       # E131 - continuation line unaligned for hanging indent
-      tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      tokenizedLine = tokenizedLineForRow(textEditor, lineNumber)
       if tokenizedLine is undefined
         break
       offset = 0
@@ -44,7 +46,7 @@
       symbol = /'([^']+)'/.exec(message)[1]
       while true
         offset = 0
-        tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+        tokenizedLine = tokenizedLineForRow(textEditor, lineNumber)
         if tokenizedLine is undefined
           break
         for token in tokenizedLine.tokens
@@ -56,12 +58,12 @@
       # F821 - undefined name 'SYMBOL'
       # F841 - local variable 'SYMBOL' is assigned but never used
       symbol = /'([^']+)'/.exec(message)[1]
-      tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      tokenizedLine = tokenizedLineForRow(textEditor, lineNumber)
       if tokenizedLine is undefined
         break
       offset = 0
       for token in tokenizedLine.tokens
-        if token.value is symbol
+        if token.value is symbol and offset >= colNumber - 1
           return [[lineNumber, offset], [lineNumber, offset + token.bufferDelta]]
         offset += token.bufferDelta
     when 'H101'
@@ -84,7 +86,7 @@
       return [[lineNumber, colNumber - 1], [lineNumber, colNumber + 4]]
     when 'H501'
       # H501 - do not use locals() for string formatting
-      tokenizedLine = textEditor.tokenizedLineForScreenRow(lineNumber)
+      tokenizedLine = tokenizedLineForRow(textEditor, lineNumber)
       if tokenizedLine is undefined
         break
       offset = 0
@@ -95,7 +97,7 @@
         offset += token.bufferDelta
     when 'W291'
       # W291 - trailing whitespace
-      screenLine = textEditor.lineTextForScreenRow(lineNumber)
+      screenLine = tokenizedLineForRow(textEditor, lineNumber)
       if screenLine is undefined
         break
       return [[lineNumber, colNumber - 1], [lineNumber, screenLine.length]]


add i18n language codes to config

add i18n language codes to config

--- lib/main.coffee
+++ lib/main.coffee
@@ -24,7 +24,10 @@
       enum: [2,3]
     defaultI18nLanguage:
       title: 'Default language of i18n'
-      description: 'Use this setting when generating i18n strings with hyperclick or autocomplete'
+      description: 'Use this setting when generating i18n strings with hyperclick and autocomplete'
       type: 'string'
       default: 'en'
-      enum: ['en','ko','jp','nl'] # TODO: add all languages
+      enum: ['af', 'sq', 'ar', 'eu', 'bg', 'be', 'ca', 'hr', 'cs', 'da', 'nl',
+        'en', 'eo', 'et', 'fo', 'fi', 'fr', 'gl', 'de', 'el', 'iw', 'hu', 'is',
+        'ga', 'it', 'ja', 'ko', 'lv', 'lt', 'mk', 'mt', 'no', 'pl', 'pt', 'ro',
+        'ru', 'gd', 'sr', 'sr', 'sk', 'sl', 'es', 'sv', 'tr', 'uk'] # https://www.w3.org/International/O-charset-lang.html


Escape title and alt text

Escape title and alt text

--- app/scripts/lib/formatters.coffee
+++ app/scripts/lib/formatters.coffee
@@ -90,6 +90,7 @@
   YT_PREVIEW_FORMATTER:
     [/([^<]+)<\/a>/g
     , (_m, link, id, alt) ->
+      alt = _.escape alt
       ""
     ]


(fix) Determine API path more robust.

(fix) Determine API path more robust.

--- app/scripts/api.coffee
+++ app/scripts/api.coffee
@@ -1,7 +1,7 @@

-host = window.location.host.toString().split(":");
-port = host[1].split("/")[0];
-address = 'http://'+host[0]+':'+port+'/api'
+host = window.location.host;
+proto = window.location.protocol;
+address = proto + '//'+host+'/api'

 ajax = (method, url, data) ->
   $.ajax


Fix slugify method so that proposals with all non-western character titles work

--- @client/shared.coffee
+++ @client/shared.coffee
@@ -761,12 +761,17 @@

 # from https://gist.github.com/mathewbyrne/1280286
 window.slugify = (text) ->
-  text.toString().toLowerCase()
+  slug = text.toString().toLowerCase()
     .replace(/\s+/g, '-')           # Replace spaces with -
     .replace(/[^\w\-]+/g, '')       # Remove all non-word chars
     .replace(/\-\-+/g, '-')         # Replace multiple - with single -
     .replace(/^-+/, '')             # Trim - from start of text
     .replace(/-+$/, '')             # Trim - from end of text
+
+  if text?.length > 0 && slug?.length == 0
+    slug = md5 slug
+
+  slug


Removes another redundant line

Removes another redundant line

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -109,11 +109,10 @@
     currentCat.set(@category)
     myCat = currentCat.get()
     objByCat = _.where(bootstrap_magic_variables, { category: myCat })
-    n = 0
     if (_.map objByCat, (obj) -> obj).length is 1
       currentSubCat.set(objByCat[0].keyName)
+      $('.sub-cat-message h4').remove()
       $('.sub-cat-item').show()
-      $('.sub-cat-message h4').remove()
     else
       $('.sub-cat-message').html("
Please select a submenu tab
")
       $('.sub-cat-item').hide()


remove or condition which is a mistake

remove or condition which is a mistake

--- bootstrap-magic.coffee
+++ bootstrap-magic.coffee
@@ -102,7 +102,7 @@
   "previewTmpl" : -> Template["bootstrap_magic_preview_#{camelToSnake @_id}"] || null
   "inputTmpl" : -> Template["bootstrap_magic_input_#{@type}"] || null
   "typeIs" : (type) -> @type is type
-  "searchInactive" : -> true if searchTerms.get() is "" || " "
+  "searchInactive" : -> true if searchTerms.get() is ""

 Template._bootstrap_magic.events
   'keyup .magic-search' : (e) ->


tamesi

tamesi

--- AppStoreClient.coffee
+++ AppStoreClient.coffee
@@ -191,7 +191,7 @@
     url = "https://itunes.apple.com/"+country.toLowerCase()+"/rss/topsongs/limit=#{limit}/explicit=true/xml"

     mojiretu = 'select * from xml where url="'+url+'"'
-    unless ENV_PRODUCTION then Ti.API.debug "url=#{url} moji=#{mojiretu}"
+    unless ENV_PRODUCTION then Ti.API.debug "url=#{url} moji=#{mojiretu} yql = #{Ti.Yahoo.yql}"
     Ti.Yahoo.yql mojiretu, (e) ->
       unless ENV_PRODUCTION then Ti.API.debug "じっこう"
       if e.success and e.data?


Clean up path logic

Clean up path logic

--- apps/feed/index.coffee
+++ apps/feed/index.coffee
@@ -9,10 +9,13 @@
 homePathMiddleware = (req, res, next) ->
   return next() unless req.user

-  path = req.user.get('home_path')
-  return res.redirect 302, path unless path is "/" or !path
+  path = req.user.get('home_path')

-  next()
+  # keep going in the middleware unless the path is "/" or the path is not present
+  return next() unless path or path is "/"
+
+  # otherwise redirect to the homepath
+  res.redirect 302, path

 app.get "/", homePathMiddleware, routes.index


sort input symbol suggestions

sort input symbol suggestions


Last id cleanup.

Last id cleanup.

--- lib/MemoryState.coffee
+++ lib/MemoryState.coffee
@@ -158,14 +158,13 @@

   # @private
   messagesGetLastId : () ->
-    id = @messagesIDs.peek() || 0
-    Promise.resolve id
+    Promise.resolve @lastMessageID

   # @private
   messagesGetAfterId : (id) ->
     nmessages = @messagesIDs.length
     maxlen = @historyMaxGetMessages
-    lastid = @messagesIDs.peek()
+    lastid = @lastMessageID
     id = _.min [ id, lastid ]
     end = lastid - id
     len = _.min [ maxlen, lastid - id ]


Fix query to resolve 'z' filter not working

--- repositories/gamesrepository.coffee
+++ repositories/gamesrepository.coffee
@@ -37,12 +37,11 @@
 				.where 'PartitionKey eq ?', 'zvgq-game'
 				.and 'title >= ? and title < ?', '0', getNextCharacter('9')
 		else
+			upperCaseFilter = filter.toUpperCase()
 			gameQuery = new azure.TableQuery()
-						.where 'PartitionKey eq ?', 'zvgq-game'
-						.and 'title ge ?', filter.toUpperCase()
-						.and 'title lt ?', getNextCharacter(filter).toUpperCase()
-						.or 'title ge ?', filter
-						.and 'title lt ?', getNextCharacter(filter)
+				.where 'PartitionKey eq ?', 'zvgq-game'
+				.and 'title >= ? and title < ?', filter, getNextCharacter(filter)
+				.or 'title >= ? and title < ?', upperCaseFilter, getNextCharacter(upperCaseFilter)

 		@tableService.queryEntities 'games', gameQuery, null, (gameQueryError, entities)=>
 			# Add games to Results


router: document readystatechange binding fix

--- lib/core/router.coffee
+++ lib/core/router.coffee
@@ -65,7 +65,9 @@

       # Safari fires extra popstate event right after window is loaded
       # this is to avoid this inconsistent initial firing
-      if document.readyState isnt 'complete' and not readyStateBinded
+      unless document.readyState is 'complete'
+        return  if readyStateBinded
+
         readyStateBinded = yes
         return document.addEventListener 'readystatechange', =>
           KD.utils.defer => @startListening()  if document.readyState is 'complete'


mobile drawer - ignore links

mobile drawer - ignore links

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -64,6 +64,8 @@

   mobileDrawerCategoryWatch: ->
     $(".category.main h2").click (e) ->
+      e.preventDefault()
+
       $category = $(this).parents(".category")
       if $category.hasClass("expanded")
         $category.removeClass("expanded")


updated links fixes #27

updated links fixes #27

--- app/lib/aboutContent.coffee
+++ app/lib/aboutContent.coffee
@@ -160,13 +160,13 @@
       imgAlt: 'Pan African Programme'
       listHeaderOne: "Collaborating Organizations",
       listOne: [
-        {title: "Agence Nationale des Parcs Nationaux, Gabon", link: null},
+        {title: "Agence Nationale des Parcs Nationaux, Gabon", link: "http://www.gabon-nature.com/"},
         {title: "Budongo Conservation Field Station, Uganda", link: "http://budongo.org/"},
         {title: "Campo Ma’an NP, Cameroon (WWF)", link: "http://wwf.panda.org/who_we_are/wwf_offices/cameroon/index.cfm?uProjectID=CM0858"},
         {title: "Conkouati-Douli NP, R-Congo (WCS)", link: "http://programs.wcs.org/congo/WildPlaces/ConkouatiDouliNP.aspx"},
-        {title: "Direction des Eaux, Forêts et Chasses, Senegal", link: null},
+        {title: "Direction des Eaux, Forêts et Chasses, Senegal", link: "http://www.servicepublic.gouv.sn/index.php/demarche_administrative/services/2/2"},
         {title: "Fongoli Savanna Chimpanzee Project, Senegal", link: "http://savannachimp.blogspot.de/"},
-        {title: "Forestry Development Authority, Liberia", link: null},
+        {title: "Forestry Development Authority, Liberia", link: "http://www.fda.gov.lr/"},
         {title: "Gashaka Primate Project, Nigeria", link: "http://www.ucl.ac.uk/gashaka/"},
         {title: "Gishwati Chimpanzee Project, Rwanda", link: null},
         {title: "Institut Congolais pour la Conservation de la Nature, DR-Congo", link: null},
@@ -174,27 +174,27 @@
         {title: "Loango Ape Project, Gabon", link: "http://www.eva.mpg.de/primat/research-groups/chimpanzees/field-sites/loango-ape-project.html"},
         {title: "Lukuru Wildlife Research Foundation, DR-Congo", link: "http://www.lukuru.org/"},
         {title: "Ministere de l'Agriculture de l'Elevage et des Eaux et Forets, Guinea", link: null},
-        {title: "Ministere de la Recherche Scientifique et de l'Innovation, Cameroon", link: null},
+        {title: "Ministere de la Recherche Scientifique et de l'Innovation, Cameroon", link: "http://www.campack-cm.com/minresi/"},
         {title: "Ministere de la Recherche Scientifique, DR-Congo", link: null},
-        {title: "Ministere de le Recherche Scientifique et Technologique, R-Congo", link: null},
-        {title: "Ministere de l’Economie Forestiere, R-Congo", link: null},
-        {title: "Ministere des Eaux et Forets, Cote d’Ivoire", link: null},
-        {title: "Ministere des Forets et de la Faune, Cameroon", link: null},
-        {title: "Ministry of Education, Rwanda", link: null},
+        {title: "Ministere de le Recherche Scientifique et Technologique, R-Congo", link: "http://www.mrsit-congo.net/"},
+        {title: "Ministere de l’Economie Forestiere, R-Congo", link: "http://www.mefdd.cg/accueil/"},
+        {title: "Ministere des Eaux et Forets, Cote d’Ivoire", link: "http://www.eauxetforets.gouv.ci/"},
+        {title: "Ministere des Forets et de la Faune, Cameroon", link: "http://www.minfof.cm/"},
+        {title: "Ministry of Education, Rwanda", link: "http://www.mineduc.gov.rw/home/"},
         {title: "Ngogo Chimpanzee Project, Uganda", link: "http://ngogochimpanzeeproject.org/"},
         {title: "Nigeria National Park Service, Nigeria", link: null},
         {title: "Nyungwe-Kibira Landscape, Rwanda-Burundi (WCS)", link: "http://rwanda.wcs.org/"},
         {title: "Projet Grands Singes, La Belgique, Cameroon (KMDA)", link: "http://webh01.ua.ac.be/crc/pgs/PGS_home.html"},
-        {title: "Station d’Etudes des Gorilles et Chimpanzees, Gabon", link: null},
+        {title: "Station d’Etudes des Gorilles et Chimpanzees, Gabon", link: "http://scienceparcsgabon.weebly.com/segc.html"},
         {title: "Tai Chimpanzee Project, Cote d’Ivoire", link: "http://www.eva.mpg.de/primat/research-groups/chimpanzees/field-sites/tai-chimpanzee-project.html"},
-        {title: "Tanzania Commission for Science and Technology, Tanzania", link: null},
-        {title: "Tanzania Wildlife Research Institute, Tanzania", link: null},
-        {title: "The Aspinall Foundation, Gabon", link: null},
+        {title: "Tanzania Commission for Science and Technology, Tanzania", link: "http://www.costech.or.tz/"},
+        {title: "Tanzania Wildlife Research Institute, Tanzania", link: "http://www.tawiri.or.tz/"},
+        {title: "The Aspinall Foundation, Gabon", link: "http://www.aspinallfoundation.org/backtothewild/back-wild-home"},
         {title: "Ugalla Primate Project, Tanzania", link: "http://ugallaprimateproject.com/"},
-        {title: "Ugandan Wildlife Authority, Uganda", link: null},
+        {title: "Ugandan Wildlife Authority, Uganda", link: "http://www.ugandawildlife.org/"},
         {title: "WCS Albertine Rift Programme, DRC", link: "http://www.albertinerift.org/"},
-        {title: "Wild Chimpanzee Foundation", link: null},
-        {title: "Wildlife Conservation Society (WCS) Nigeria, Nigeria", link: null},
+        {title: "Wild Chimpanzee Foundation", link: "http://www.wildchimps.org/"},
+        {title: "Wildlife Conservation Society (WCS) Nigeria, Nigeria", link: "http://www.wcsnigeria.org/"},
         {title: "WWF Congo Basin, DRC", link: "http://wwf-congobasin.org/where_we_work/democratic_republic_of_congo/lac_tumba/"}
       ],
       listHeaderTwo: "Funding organizations",


Fixed colour selection bug (wouldn't find results)

--- app/assets/javascripts/filters.js.coffee
+++ app/assets/javascripts/filters.js.coffee
@@ -31,10 +31,7 @@
       else
       #Added selected color
         whichThingSelected = t.attr("style").replace(/background-color: (\w+);?/i,'$1')
-        # Fix up the case issues for Internet Explorer (always pass in color value as "Red")
-        whichThingSelected = whichThingSelected.toLowerCase()
-        whichThingSelected = whichThingSelected.charAt(0).toUpperCase() + whichThingSelected.slice(1)
-        $('#categorical_color').val(whichThingSelected)
+        $('#categorical_color').val(whichThingSelected.toLowerCase())
       t.toggleClass('selected_swatch')
       optemo_module.submitAJAX()


time added instead of hours

time added instead of hours

--- app/assets/javascripts/motions.js.coffee
+++ app/assets/javascripts/motions.js.coffee
@@ -46,16 +46,20 @@
     $(".relative-time").each((index, element)->
       date = $(element).html()
       local_datetime = new Date()
+      hours = (parseInt(date.substring(11,13)) + offset).toString()
+      mins = date.substring(14,16)
       local_datetime.setYear(date.substring(0,4))
       local_datetime.setMonth((parseInt(date.substring(5,7)) - 1).toString())
       local_datetime.setDate(date.substring(8,10))
-      local_datetime.setHours((parseInt(date.substring(11,13)) + offset).toString())
+      local_datetime.setHours(hours)
+      local_datetime.setMinutes(mins)
       if local_datetime.getDate() == today.getDate()
-        hours_difference = today.getHours() - local_datetime.getHours()
-        if hours_difference == '1'
-          date_string = "#{hours_difference} hr ago"
+        if hours < 12
+          hours = 12 if hours == 0
+          date_string = "#{hours}:#{mins} AM"
         else
-          date_string = "#{hours_difference} hrs ago"
+          hours = 24 if hours == 12
+          date_string = "#{hours-12}:#{mins} PM"
       else
         date_string = "#{local_datetime.getDate()} #{month[local_datetime.getMonth()]}"
       $(element).html(String(date_string))


Fix colorwheel not rendering

Fix colorwheel not rendering

--- app/assets/javascripts/schemes.js.coffee
+++ app/assets/javascripts/schemes.js.coffee
@@ -23,10 +23,10 @@
         event.stopPropagation()
         $('#section_name').text(section)

-Raphael ->
+  Raphael ->

-  cp = $('#colorpicker')
-  offset = cp.offset()
+    cp = $('#colorpicker')
+    offset = cp.offset()

-  Raphael.colorwheel(offset.left, offset.top, 320, '#ccc', cp.get(0))
+    Raphael.colorwheel(offset.left, offset.top, 320, '#ccc', cp.get(0))


Simplify

Simplify

--- resources/assets/coffee/_classes/gallery.coffee
+++ resources/assets/coffee/_classes/gallery.coffee
@@ -44,24 +44,21 @@
       index: index
       history: false

-
     gallery.init()

   data: (galleryId) ->
-    $(".js-gallery[data-gallery-id='#{galleryId}']").map (_i, el) ->
-      src = el.getAttribute('data-src') || el.getAttribute('href')
-      {
-        msrc: src
-        src: src
-        w: parseInt el.getAttribute('data-width'), 10
-        h: parseInt el.getAttribute('data-height'), 10
-      }
-    .get()
+    for el in document.querySelectorAll(".js-gallery[data-gallery-id='#{galleryId}']")
+      src = el.getAttribute('data-src') ? el.getAttribute('href')
+
+      msrc: src
+      src: src
+      w: parseInt el.getAttribute('data-width'), 10
+      h: parseInt el.getAttribute('data-height'), 10

   thumbBoundsFn: (galleryId) =>
-    return (i) =>
+    (i) =>
       $thumb = $(".js-gallery[data-gallery-id='#{galleryId}'][data-index='#{i}']")
       thumbPos = $thumb.offset()

@@ -83,11 +80,9 @@
       scale = Math.max thumbDim[0] / imageDim[0], thumbDim[1] / imageDim[1]
       scaledImageDim = imageDim.map (s) -> s * scale

-      {
-        x: center[0] - scaledImageDim[0] / 2
-        y: center[1] - scaledImageDim[1] / 2
-        w: scaledImageDim[0]
-      }
+      x: center[0] - scaledImageDim[0] / 2
+      y: center[1] - scaledImageDim[1] / 2
+      w: scaledImageDim[0]

   switchPreview: (e) =>


Make snapping work.

Make snapping work.

--- app/assets/volume/slot.coffee
+++ app/assets/volume/slot.coffee
@@ -47,41 +47,54 @@
       fullRange.u = finite(slot.segment.u, u, 0)
       return

-    getDistance = (item) ->
-      # get screen width
-
-
-      # get width of the timeline.
-      # get percentage of offset.
-      # calculate pixels based on previous values.
-      # return out the distance in pixels.
-
-    snapping = (selectionEnd) ->
-     listOfAllPlacements = $scope.tracks.concat(records, $scope.consents)
-
-     # Make a list of all the stuff that is within four pixels
-     smallPlacements = listOfAllPlacements.filter (i) ->
-       isFinite(i.segment?.l) && isFinite(i.segment?.u) && getDistance(i) <= 4
-
-     # It's not inconcievable that we could have multiple items
-     # that are within four pixels.  Subsequently, let's sort so we
-     # can get a handle on the closest
-     smallPlacements.sort (a, b) -> getDistance(a) - getDistance(b)
-
-     # Now that everything is sorted, let's separate out the first element
-     # of the array, which should be the closest.
-     if smallPlacements.length
-       [closest] = smallPlacements
-
-       # "snap" the element by setting position to the closest
-       dragPosition = closest.position
-
-
+    tl = document.getElementById('slot-timeline')
+
+    # Takes a time position and convert it to an offset in pixels.
+    getOffsetPixels = (item) ->
+      #Grab the fraction offset of the item.
+      op = offsetPosition(item)
+
+      # Grab the surrounding rectangle.
+      tlr  = tl.getBoundingClientRect()
+
+      #Grab the width of the surrounding rectangle.
+      totalPixels = tlr.width
+
+      # Since we have the fraction and the total pixels, we can simply do
+      # some multiplication and grab the offset pixels.
+      offsetPixels = op * totalPixels
+
+      # Return back the offset pixels.
+      return offsetPixels
+
+
+
+    snapping = (selectionEnd, exclude) ->
+      listOfAllPlacements = []
+      selectionOffsetPixels = getOffsetPixels selectionEnd
+      $scope.tracks.concat(records, $scope.consents).forEach (i) ->
+        if i is exclude then return
+        if isFinite(i.segment?.l)
+          listOfAllPlacements.push i.segment.l
+        if isFinite(i.segment?.u)
+          listOfAllPlacements.push i.segment.u
+      if listOfAllPlacements.length == 0
+        return selectionEnd
+
+      min = _.min listOfAllPlacements, (i) ->
+        Math.abs(selectionOffsetPixels - getOffsetPixels(i))
+      if Math.abs(selectionOffsetPixels - getOffsetPixels(min)) <= 10
+        return min
+      else
+        return selectionEnd
+
+    # Takes a time position and turns it into a fraction.
     offsetPosition = (offset) ->
       return offset unless isFinite offset
       (offset - ruler.range.base) / (ruler.range.u - ruler.range.l)

-    tl = document.getElementById('slot-timeline')
+
+    # Takes a position in pixels returns a time position offset.
     positionOffset = (position) ->
       tlr = tl.getBoundingClientRect()
       p = (position - tlr.left) / tlr.width
@@ -365,7 +378,7 @@
         offset = down.offset ?= positionOffset(down.clientX) - @segment.l
         pos = positionOffset(up.clientX) - offset
         return unless isFinite(pos)
-        pos = snapping()
+        pos = snapping(pos, @)
         @setPosition(pos)
         if up.type != 'mousemove'
           $scope.updatePosition()
@@ -645,13 +658,14 @@

       dragLeft: (event) ->
         @segment.l = positionOffset(event.clientX)
-        snapping(@segment.l)
+        @segment.l = snapping(@segment.l, @)
         if event.type != 'mousemove'
           $scope.form.position.$setDirty()
         return

       dragRight: (event) ->
         @segment.u = positionOffset(event.clientX)
+        @segment.u = snapping(@segment.u, @)
         if event.type != 'mousemove'
           $scope.form.position.$setDirty()
         return


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/controllable.coffee
+++ src/controllable.coffee
@@ -25,7 +25,7 @@
   # facing in case you need that (eg. for attack direction)
   player.I.facing
   # => player.I.facing
-
+  # => Point()
 </pre></code>

 @name Controllable


That thing where you thought you tested this code, but didn't.

Actually pass in the db reference to the member votes implementation.

--- api/v1/members.coffee
+++ api/v1/members.coffee
@@ -3,7 +3,7 @@
 ObjectId = require('mongodb').ObjectID
 await = require('await')

-memberVoteImplementation = (req, res, queryTransformer) ->
+memberVoteImplementation = (db, req, res, queryTransformer) ->
   memberObjectId = req.member._id
   normalize = req.query.normalize || false
   queryTransformer = queryTransformer || (thing)->thing
@@ -67,10 +67,10 @@
   api.v1.getMemberById = (req, res) -> res.jsonp req.member

   api.v1.getMemberVotes = (req, res) ->
-    memberVoteImplementation(req, res)
+    memberVoteImplementation(db, req, res)

   api.v1.getMemberVotesBySession = (req, res) ->
-    memberVoteImplementation req, res, (baseQuery) ->
+    memberVoteImplementation db, req, res, (baseQuery) ->
       baseQuery.sessionId = req.legislativeSession

   api


Play sound on Buy Gems press

Play sound on Buy Gems press

Partial solution to #2916.

--- app/views/play/modal/BuyGemsModal.coffee
+++ app/views/play/modal/BuyGemsModal.coffee
@@ -45,6 +45,15 @@
     c.state = @state
     c.stateMessage = @stateMessage
     return c
+
+  afterRender: ->
+    super()
+    return unless @supermodel.finished()
+    @playSound 'game-menu-open'
+
+  onHidden: ->
+    super()
+    @playSound 'game-menu-close'

   onIPadProducts: (e) ->
     newProducts = []


fix exclusion w/o explicit inclusion

fix exclusion w/o explicit inclusion

--- app/assets/javascripts/helpers.js.coffee
+++ app/assets/javascripts/helpers.js.coffee
@@ -4,8 +4,9 @@

 String.prototype.matches  or= (exp) ->
   matchObj = patternsToRegex _.groupBy(exp.trim().split(' '), commandType)
-  take = matchObj.bare is [] || _(matchObj.bare).some(makeMatch(this))
+  take = _(matchObj.bare).some(makeMatch(this))
   drop = !_(matchObj.except).some(makeMatch(this))
+  console.log "bare: " + matchObj.bare
   take and drop

 commandType = (command) ->
@@ -17,7 +18,7 @@
       if obj.bare
         obj.bare.map toRegex
       else
-        []
+        [/.*/]
     except:
       if obj.except
         toRegex(pattern[1 ..]) for pattern in obj.except when pattern.length > 1


Made t() call in prediction.coffee conditional

--- app/assets/javascripts/prediction.coffee
+++ app/assets/javascripts/prediction.coffee
@@ -57,7 +57,7 @@
     @enable_forward_navigation()

   disable_forward_navigation: ->
-    $(document).trigger("prevent_forward_navigation",{source: @$form, message: t('PLEASE_SUBMIT')})
+    $(document).trigger("prevent_forward_navigation",{source: @$form, message: t?('PLEASE_SUBMIT')})

   enable_forward_navigation: ->
     $(document).trigger("enable_forward_navigation",{source: @$form})


Simplify toggle-nav-collapse JS

Simplify toggle-nav-collapse JS

--- app/assets/javascripts/sidebar.js.coffee
+++ app/assets/javascripts/sidebar.js.coffee
@@ -3,12 +3,7 @@
   collapsed = 'page-sidebar-collapsed'
   expanded = 'page-sidebar-expanded'

-  if $('.page-with-sidebar').hasClass(collapsed)
-    $('.page-with-sidebar').removeClass(collapsed).addClass(expanded)
-    $('.toggle-nav-collapse i').removeClass('fa-angle-right').addClass('fa-angle-left')
-    $.cookie("collapsed_nav", "false", { path: '/' })
-  else
-    $('.page-with-sidebar').removeClass(expanded).addClass(collapsed)
-    $('.toggle-nav-collapse i').removeClass('fa-angle-left').addClass('fa-angle-right')
-    $.cookie("collapsed_nav", "true", { path: '/' })
+  $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
+  $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
+  $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
 )


Fix: 起動時にエラーが出るのを修正

Fix: 起動時にエラーが出るのを修正

--- src/core/request.coffee
+++ src/core/request.coffee
@@ -49,10 +49,18 @@
  ###
 getLastestVersion = ->
   return new Promise( (resolve, reject) ->
-    request("https://api.github.com/repos/BlitzModder/BMPC/releases/latest", (err, res, body) ->
+    request(
+      url: "https://api.github.com/repos/BlitzModder/BMPC/releases/latest"
+      headers:
+        "User-Agent": "request"
+    , (err, res, body) ->
       if err? or (res? and res.statusCode is 404)
         reject(err)
-      resolve(JSON.parse(body).name)
+      try
+        response = JSON.parse(body)
+        resolve(response.name)
+      catch
+        reject("Failed to parse JSON")
     )
     return
   )


make course creator button render, at least

--- app/assets/javascripts/utils/router.cjsx
+++ app/assets/javascripts/utils/router.cjsx
@@ -1,11 +1,10 @@
 React               = require 'react'
 ReactDOM            = require 'react-dom'
+render              = ReactDOM.render
 ReactRouter         = require 'react-router'
 Router              = ReactRouter.Router
 Route               = ReactRouter.Route
 IndexRoute          = ReactRouter.IndexRoute
-DefaultRoute        = ReactRouter.DefaultRoute
-Redirect            = ReactRouter.Redirect

 App                 = require '../components/app'
 Course              = require '../components/course'
@@ -34,15 +33,14 @@
   <Route path='/' component={App}>
     <IndexRoute component={CourseCreatorButton} />
     <Route path='recent-activity' component={RecentActivityHandler}>
-      <Route path='did-you-know' component={DidYouKnowHandler} />
+      <IndexRoute component={DidYouKnowHandler} />
       <Route path='plagiarism' component={PlagiarismHandler} />
       <Route path='recent-edits' component={RecentEditsHandler} />
     </Route>
     <Route path='courses'>
-      <Route path='/' component={CourseCreatorButton} />
       <Route path=':course_school/:course_title' component={Course}>
-        <Route component={OverviewHandler} />
-        <Route path='timeline' component={TimelineHandler} >
+        <IndexRoute component={OverviewHandler} />
+        <Route path='timeline' component={TimelineHandler}>
           <Route path='wizard' component={Wizard} />
           <Route path='dates' component={Dates} />
         </Route>
@@ -60,7 +58,8 @@
   </Route>
 )

-$ ->
-  console.log routes
-  react_root =  document.getElementById('react_root')
-  ReactDOM.render((<Router>{routes}</Router>), react_root)
+render((
+  <Router>
+    {routes}
+  </Router>
+), document.getElementById('react_root'))


Fix forms.scrollToFirstError when there is no error to scroll to

--- app/core/forms.coffee
+++ app/core/forms.coffee
@@ -84,7 +84,8 @@

 module.exports.scrollToFirstError = ($el=$('body')) ->
   $first = $el.find('.has-error, .alert-danger, .error-help-block, .has-warning, .alert-warning, .warning-help-block').filter(':visible').first()
-  $('html, body').animate({ scrollTop: $first.offset().top - 20 }, 300)
+  if $first.length
+    $('html, body').animate({ scrollTop: $first.offset().top - 20 }, 300)

 module.exports.clearFormAlerts = (el) ->
   $('.has-error', el).removeClass('has-error')


fixed a few cases where duplicate plans would appear

it was a case of one event triggering multiple listeners. still pretty
shaky, though.

--- app/assets/transit/lib/views/plan.coffee
+++ app/assets/transit/lib/views/plan.coffee
@@ -17,7 +17,7 @@
     @map = Transit.map
     @map.on 'drag:end', @drag_plan

-    @model.on 'geocode geolocate fetch', @fetch_plan
+    @model.on 'geocode geolocate', @fetch_plan
     @model.on 'geocode:error', @geocode_error
     @model.on 'plan:timeout', @timeout_warn
     Transit.events.on 'geocode:fail', @geocode_fail
@@ -48,24 +48,28 @@
     # set the model from/to locations from the marker positions
     @model.set
       from: @map.get('from').position.toHash()
-      to: @map.get('to').position.toHash()
+      to: @map.get('to').position.toHash(),
+    { silent: true }
     # update the url and history with new locations
     from_url = @map.get('from').position.toUrlValue()
     to_url = @map.get('to').position.toUrlValue()
     # update the URL but don't trigger, purely cosmetic :)
     Transit.router.navigate "plan/#{from_url}/#{to_url}"
     # then load the new plan by hand
-    @model.trigger 'fetch'
+    @fetch_plan()

   # updates plan from geocode fail form
   update_plan: (evt) ->
     evt.preventDefault()
     # update endpoints using form or URL params (from router)
-    @model.set
-      from: Transit.escape $('#from_query').val() or @options.from
-      to: Transit.escape $('#to_query').val() or @options.to
+    from = $('#from_query').val() or Transit.unescape @options.from
+    to = $('#to_query').val() or Transit.unescape @options.to
     # navigate to new plan URL and trigger reload
-    Transit.router.navigate "plan/#{@model.get('from')}/#{@model.get('to')}", true
+    @remove_itineraries()
+    @remove()
+    Transit.router.navigate "plan/#{from}/#{to}", true
+
+    # @fetch_plan()

   fetch_plan: =>
     @render()
@@ -126,7 +130,7 @@

   reset: =>
     @$('.progress').hide()
-    @$('.itineraries').html('')
+    remove_itineraries()
     Transit.errorMessage(null)  # clear the error message

   add_itineraries: (plan) =>
@@ -147,8 +151,10 @@

   # remove all itineraries from the map
   remove_itineraries: =>
+    for view in @views if @views?
+      view?.clean_up(true)
+      view.remove()
     @$('#itineraries').html('')
-    view?.clean_up(true) for view in @views if @views?

   go_to_splash: (event) =>
     # TODO: Place this to the topbar.


cancel progress timer when leaving the page

workaround for a chrome bug that gets the page in a broken state:
https://code.google.com/p/chromium/issues/detail?id=263981

test plan:
1. basic regression of quiz stats csv generation UI
2. in chrome, confirm it's fixed in the new groups UI:
   1. cherry-pick g/22288
   2. enable the new groups UI
   3. don't start delayed jobs
   4. click to randomly assign students
   5. refresh the page several times
   6. you should not get the red ajax error box, and you should see the
      progress model continually polling in the network tab

Change-Id: I8e7cc6cf5ba01ca04c5fa528f2c4aabd627c4807
Reviewed-on: https://gerrit.instructure.com/22646
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Landon Wilkins <e219b0c23c4256074cdfc7a23138e4f17200b08f@instructure.com>
QA-Review: Marc LeGendre <d33c80bc45d65303e33ca83108a9952b745af9ef@instructure.com>
Product-Review: Marc LeGendre <d33c80bc45d65303e33ca83108a9952b745af9ef@instructure.com>

--- app/coffeescripts/models/Progress.coffee
+++ app/coffeescripts/models/Progress.coffee
@@ -1,4 +1,4 @@
-define ['Backbone'], ({Model}) ->
+define ['Backbone', 'jquery'], ({Model}, $) ->

   # Works with the progress API. Will poll its url until the `workflow_state`
   # is completed.
@@ -28,6 +28,9 @@
     initialize: ->
       @pollDfd = new $.Deferred
       @on 'change:url', => @poll() if @isPolling()
+      # don't try to do any ajax when we're leaving the page
+      # workaround for https://code.google.com/p/chromium/issues/detail?id=263981
+      $(window).on 'beforeunload', => clearTimeout(@timeout)

     url: ->
       @get 'url'
@@ -50,7 +53,7 @@

     onPoll: =>
       if @isPolling()
-        setTimeout @poll, @get('timeout')
+        @timeout = setTimeout(@poll, @get('timeout'))
       else
         @pollDfd.resolve()
         @trigger 'complete'


Remove dependency to the method "helpers.parse" + Getting ready for linter 2.0.0

--- lib/init.coffee
+++ lib/init.coffee
@@ -271,8 +271,8 @@
     ## For debug: ## alert(executablePath + " " + params.join(" "))
     return helpers.exec(executablePath, params, options).then ((file) ->
       # The "file" variable contains the raw mypy output.
-      # The goal of this method is to return a string where each line is a valid warning in the format: "FILEPATH:LINENO:COLNO:MESSAGE"
-      result = ""
+      # The goal of this method is to return an array of string where each string is a valid warning in the format: "FILEPATH:LINENO:COLNO:MESSAGE"
+      result = []

       # Each line of the mypy output may be a potential warning so we create an array of string containing each line of the output.
       # We split the text using the new line as a separator and handle any OS kind of new lines.
@@ -283,11 +283,11 @@
       for key, val of lines
         if 0 == val.indexOf(filePath + ":")
           # Warning was reported using an absolute path to the file.
-          result = result + val + os.EOL
+          result.push(val)
         else if 0 == val.indexOf(baseNamePath + ":")
           # Warning was reported using a relative path to the file.
           # Note: We use "path.join" even though "val" does not contain only the file name, but it creates the correct output nevertheless
-          result = result + path.join(rootPath, val) + os.EOL
+          result.push(path.join(rootPath, val))
         else
           #Ignore, we only want warnings within the file being linted.
           ## This filters out warnings about *.pyi files
@@ -444,22 +444,28 @@
     # - line = The line number where the warning is.
     # - col = The column where the warning is within the line.
     # - message = The warning text.
-    messages = helpers.parse(output[1],
-                             "^(?<file>([A-Z]:)?[^:]+)[:](?<line>\\d+):(?:(?<col>\\d+):)? error: (?<message>.+)",
-                             {flags: 'gm'})
+    regexLine = new NamedRegexp("^(?<file>([A-Z]:)?[^:]+)[:](?<line>\\d+):(?:(?<col>\\d+):)? error: (?<message>.+)")
+    regexHeuristic01 = new NamedRegexp("^Argument . to .(?<name>.+). has incompatible type ..+.; expected ..+.$")

     # Prepare an array of all the warnings to report.
     result = []
-    messages.forEach (msg) ->
-      @fastParser = output[0]
+    for msg, idx in output[1]
+      v_CurrMessageRaw = regexLine.execGroups(msg)
+      if v_CurrMessageRaw
+      else
+        continue
+
       # At this point every messages will be reported, we won't filter them
       # But we will improve them a little to make them more helpful.

       #HACK: Work around since mypy only provide the start column of the error, we have to use heuristics for the end column of the warning, without this workaround the warning would not be underline.
-      warnOrigStartLine = msg.range[0][0]
-      warnOrigStartCol = msg.range[0][1]
-      warnOrigEndLine = msg.range[1][0]
-      warnOrigEndCol = msg.range[1][1]
+      warnOrigStartLine = parseInt(v_CurrMessageRaw.line, 10) - 1
+      warnOrigStartCol = parseInt(v_CurrMessageRaw.col, 10) - 1
+      if warnOrigStartCol < 0
+        warnOrigStartCol = 0
+
+      warnOrigEndLine = warnOrigStartLine
+      warnOrigEndCol = warnOrigStartCol

       warnStartLine = warnOrigStartLine
       warnStartCol = warnOrigStartCol
@@ -475,26 +481,41 @@
           warnEndCol += 1

       #Rational: Since we know the method name we can underline its length
-      compiledRegexp = new NamedRegexp("^Argument . to .(?<name>.+). has incompatible type ..+.; expected ..+.$", "g")
-      rawMatch = compiledRegexp.exec(msg.text)
+      rawMatch = regexHeuristic01.execGroups(v_CurrMessageRaw.message)
       if rawMatch
-        if (@fastParser)
-          warnEndCol += rawMatch[1].length
+        if (output[0])
+          warnEndCol += rawMatch.name.length
         else
-          warnStartCol -= rawMatch[1].length
+          warnStartCol -= rawMatch.name.length
         if 0 < warnOrigStartCol
           warnEndCol -= 1

       #TODO: Put more heuristic

-      msg.range = [[warnStartLine, warnStartCol], [warnEndLine, warnEndCol]]
-
-      # The messages will be displayed as "Warning" to the user.
-      msg.type = "Warning"
-
       #Append the current warning to the final result.
-      result.push(msg)
-
+      if true
+        #Linter version 1.0.0
+        result.push(
+          {
+            type: 'Warning',
+            filePath: v_CurrMessageRaw.file,
+            range: [[warnStartLine, warnStartCol], [warnEndLine, warnEndCol]],
+            text: v_CurrMessageRaw.message,
+          }
+        )
+      else
+        #Linter version 2.0.0
+        result.push(
+          {
+            severity: 'Warning',
+            location: {
+              file: v_CurrMessageRaw.file,
+              position: [[warnStartLine, warnStartCol], [warnEndLine, warnEndCol]]
+            },
+            excerpt: v_CurrMessageRaw.message,
+            description: ""
+          }
+        )
     #The job is over, let's return the result so it can be displayed to the user.
     return result


Update error messages to mention help command.


Update main.coffee

Update main.coffee

Changed smart closing of ", (, { default value to false.

--- lib/main.coffee
+++ lib/main.coffee
@@ -5,13 +5,13 @@
   config:
     autocompleteBrackets:
       type: 'boolean'
-      default: true
+      default: false
     autocompleteSmartQuotes:
       type: 'boolean'
-      default: true
+      default: false
     wrapSelectionsInBrackets:
       type: 'boolean'
-      default: true
+      default: false

   activate: ->
     atom.workspace.observeTextEditors (editor) ->


Modify command name

Modify command name

--- lib/main.coffee
+++ lib/main.coffee
@@ -23,7 +23,7 @@
       'markdown-preview:toggle-break-on-single-newline': ->
         keyPath = 'markdown-preview.breakOnSingleNewline'
         atom.config.set(keyPath, not atom.config.get(keyPath))
-      'markdown-preview:use-github.com-style': ->
+      'markdown-preview:toggle-github-style': ->
         keyPath = 'markdown-preview.useGitHubStyle'
         atom.config.set(keyPath, not atom.config.get(keyPath))


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_mouse.coffee
+++ src/engine_mouse.coffee
@@ -1,5 +1,5 @@
 ###*
-This module sets up the keyboard inputs for each engine update.
+This module sets up the mouse inputs for each engine update.

 @name Mouse
 @fieldOf Engine


BUG-FIX Neck helper should provide template

--- src/helpers/neck.coffee
+++ src/helpers/neck.coffee
@@ -6,6 +6,6 @@
     Controller = injector.load(name, type: 'controller')

     el.removeAttr 'ui-neck'
-    controller = new Controller(el: el)
+    controller = new Controller(el: el, template: name)
     controller.injector = injector
-    controller.render()+    controller.render()


staticpage: comment link color

staticpage: comment link color

--- servers/lib/crawler/staticpages/activity.coffee
+++ servers/lib/crawler/staticpages/activity.coffee
@@ -42,7 +42,7 @@

           #{fullName}
-

+

#{message.body}

           #{createdAt}


Remove fold all command which interferes with typing } on Danish keyboards

--- services/web/public/coffee/editor/Editor.coffee
+++ services/web/public/coffee/editor/Editor.coffee
@@ -141,6 +141,7 @@
 				readOnly: true
 			aceEditor.commands.removeCommand "transposeletters"
 			aceEditor.commands.removeCommand "showSettingsMenu"
+			aceEditor.commands.removeCommand "foldall"

 			aceEditor.showCommandLine = (args...) =>
 				@trigger "showCommandLine", aceEditor, args...


Replace "vertex" with more descriptive "deepest" in edge clipping

--- app/js/physics.coffee
+++ app/js/physics.coffee
@@ -7,8 +7,8 @@
   controls = new KeyboardControls

   # dot = new Particle [100, ctx.height - 100]
-  window.rect = new Rectangle 200, 100, [120, 80], 0, "#F00"
-  rect2 = new Rectangle 200, 100, [0, 0], 0, "#00F"
+  window.rect = new Rectangle 120, 100, [110, 50], 0, "#F00"
+  rect2 = new Rectangle 120, 100, [0, 0], 0, "#00F"

   _.extend ctx,
     update: ->
@@ -36,7 +36,7 @@
   ctx.translate ctx.width/2, -ctx.height/2 # center the origin

 class Edge
-  constructor: (@vertex, @from, @to) ->
+  constructor: (@deepest, @from, @to) ->
     @vec = Vec.sub @to, @from
   dot: (other) ->
     Vec.dotProduct @vec, other
@@ -85,8 +85,8 @@
     @debug.reference = reference
     @debug.incident = incident

-    console.log "reference: #{reference.vertex} #{reference.from} -> #{reference.to} (vec #{reference.vec})"
-    console.log "incident: #{incident.vertex} #{incident.from} -> #{incident.to} (vec #{incident.vec})"
+    console.log "reference: #{reference.deepest} #{reference.from} -> #{reference.to} (vec #{reference.vec})"
+    console.log "incident: #{incident.deepest} #{incident.from} -> #{incident.to} (vec #{incident.vec})"
     console.log "flipped", flipped

     reference.normalize()
@@ -116,15 +116,14 @@
     refNorm = Vec.invert refNorm if flipped

     # get the largest depth
-    max = Vec.dotProduct refNorm, reference.vertex
-
+    max = Vec.dotProduct refNorm, reference.deepest
+
+    trimmed = []
     # make sure the final points are not past this maximum
-    if Vec.dotProduct(refNorm, clipped[0]) - max < 0
-      clipped[0] = null
-    if Vec.dotProduct(refNorm, clipped[1]) - max < 0
-      clipped[1] = null
-
-    trimmed = (entry for entry in clipped when entry)
+    unless Vec.dotProduct(refNorm, clipped[0]) - max < 0
+      trimmed.push clipped[0]
+    unless Vec.dotProduct(refNorm, clipped[1]) - max < 0
+      trimmed.push clipped[1]

     console.log "trimmed", trimmed[0], trimmed[1]
     @debug.trimmed = trimmed
@@ -152,30 +151,32 @@

     points

-  # Calculate the best edge (closest perpendicular edge given a separation axis)
+  # Calculate the best edge (deepest perpendicular edge given a separation axis)
   bestEdge: (minAxis) ->
     points = @points()

-    # Find farthest vertex in the polygon along separation axis
-    closestIndex = null
+    # Find deepest vertex in the polygon along separation axis
+    deepestIndex = null
     maxProjection = -Infinity
     for vertex, i in points
       projection = Vec.dotProduct minAxis, vertex
       if projection > maxProjection
         maxProjection = projection
-        closestIndex = i
+        deepestIndex = i

     # Find edge which is most perpendicular to separation axis
-    closest    = points[closestIndex]
-    prevVertex = points[ (closestIndex - 1 + points.length) % points.length ]
-    nextVertex = points[ (closestIndex + 1 + points.length) % points.length ]
-
-    left = Vec.sub closest, prevVertex
-    right = Vec.sub closest, nextVertex
+    deepest    = points[ deepestIndex ]
+    prevVertex = points[ (deepestIndex - 1 + points.length) % points.length ]
+    nextVertex = points[ (deepestIndex + 1 + points.length) % points.length ]
+
+    # vectors pointing at the deepest vertex
+    left  = Vec.sub deepest, prevVertex
+    right = Vec.sub deepest, nextVertex
+
     if Vec.dotProduct(right, minAxis) <= Vec.dotProduct(left, minAxis)
-      new Edge closest, closest, nextVertex # right edge is better
+      new Edge deepest, deepest, nextVertex # right edge is better
     else
-      new Edge closest, prevVertex, closest # left edge is better
+      new Edge deepest, prevVertex, deepest # left edge is better

   # Use Separating Axis Theorem to find minimum separation axis
   # from http://www.codezealot.org/archives/55 &c.


Refactor to "world" ur-model

Refactor to "world" ur-model

--- app/js/physics.coffee
+++ app/js/physics.coffee
@@ -1,63 +1,92 @@
 window.physics = ->
-  window.stats = Utils.drawStats()
-
-  window.ctx = Sketch.create
-    element: document.getElementById "physics"
-    retina: true
-
-  window.display = new Display ctx, [0, 0], 50
-
+
+  window.world = new World "physics"

   window.rect = new Rectangle 2, 2,
     position: [0, 0]
-    inverseMass: 1/1
+    inverseMass: 1/4
     color: "#F00"

   window.rect2 = new Rectangle 1, 1,
     position: [0, 5]
-    inverseMass: 1/5
-    velocity: [0, -2]
+    inverseMass: 1/1
+    velocity: [0, -1]
     color: "#00F"

-  paused = false
-
-  _.extend ctx,
-    update: ->
-      return if paused
-      dt = ctx.dt / 1000
-
-      rect.reset()
-      rect.resetDebug()
-      rect2.reset()
-      rect2.resetDebug()
-
-      rect.integrate dt
-      rect2.integrate dt
-      window.contacts = contacts = rect2.contactPoints(rect)
-
-      if contacts.length > 0
-        for c, i in contacts
-          console.log "contact #{i}", c.normal, c.depth, c.from.velocity
-
-        for n in [1..contacts.length*2]
-          worst = null
-          for contact in contacts
-            if not worst or contact.separatingVelocity() < worst.separatingVelocity()
-              worst = contact
-          break if worst.separatingVelocity() >= 0
-          worst.resolve(dt)
-
-        paused = true
-
-    draw: ->
-      rect.draw display, ctx
-      rect2.draw display, ctx
-      rect.drawDebug ctx
-      rect2.drawDebug ctx
-      stats.update()
-    keyup: (e) ->
-      if e.keyCode is 32 # space
-        paused = !paused
+  world.addObject rect
+  world.addObject rect2
+
+class World
+  constructor: (element) ->
+    @ctx = Sketch.create
+      element: document.getElementById element
+      retina: true
+    @stats = Utils.drawStats()
+    @display = new Display @ctx, [0, 0], 50
+
+    @objects = []
+    @paused = true
+
+    _.extend @ctx,
+      update: @update
+      draw: @draw
+      keyup: (e) =>
+        if e.keyCode is 32 # space
+          @paused = !@paused
+
+  addObject: (object) ->
+    @objects.push object
+
+  removeObject: (object) ->
+    @objects = _without(@objects, object)
+
+  update: =>
+    return if @paused
+
+    dt = @ctx.dt / 1000
+
+    for object in @objects
+      object.reset()
+      object.resetDebug()
+      object.integrate dt for object in @objects
+
+    @contacts = @narrowPhaseCollisions @broadPhaseCollisions()
+
+    if @contacts.length > 0
+      for c, i in @contacts
+        console.log "contact #{i}", c.normal, c.depth, c.from.velocity
+
+      for n in [1..@contacts.length*2] # loop contacts * 2 times
+        worst = null
+        for contact in @contacts
+          if not worst or contact.separatingVelocity() < worst.separatingVelocity()
+            worst = contact
+        break if worst.separatingVelocity() >= 0
+        worst.resolve(dt)
+
+      @paused = true
+
+  # Naive version: returns all unique pairs of objects, nothing more. TODO: use
+  # (memoized?) AABBs to build quadtree, then iterate and compare bounding boxes
+  # for overlap.
+  broadPhaseCollisions: ->
+    pairs = []
+    for i in [0..(@objects.length-2)]
+      for j in [(i+1)..(@objects.length-1)]
+        pairs.push [@objects[i], @objects[j]]
+    pairs
+
+  narrowPhaseCollisions: (pairs) ->
+    contacts = []
+    for [a, b] in pairs
+      contacts = contacts.concat(b.contactPoints a)
+    contacts
+
+  draw: =>
+    for object in @objects
+      object.draw @display, @ctx
+      object.drawDebug @display, @ctx # FIXME
+    @stats.update()

 class Display
   # ctx    - the canvas context
@@ -65,14 +94,18 @@
   # scale  - pixels per meter
   constructor: (@ctx, @center, @scale) ->

+  drawPolygon: (vertices, color) ->
+    points = @transform vertices
+    # @ctx.moveTo points
+
+  drawCircle: (point, radius, color) ->
+
+  drawLine: (from, to, color) ->
+
   transform: (points...) ->
-    dx = ctx.width/2
-    dy = ctx.height/2
+    dx = @ctx.width/2
+    dy = @ctx.height/2
     ([x * @scale + dx, -y * @scale + dy ] for [x, y] in points)
-
-  extent: ->
-    # ctx.width/2, ctx.height/2
-

 class Edge
   constructor: (@deepest, @from, @to) ->
@@ -147,7 +180,7 @@
     ctx.restore()

   resetDebug: -> @debug = {}
-  drawDebug: (ctx) ->
+  drawDebug: (display, ctx) ->
     # if ref = @debug.reference
     #   Utils.debugLine display, ctx, ref.from, ref.to, "#F66"
     # if inc = @debug.incident
@@ -211,11 +244,11 @@
     for point in clipped
       depth = Vec.dotProduct(refNorm, point) - maxDepth
       if depth >= 0
-        contacts.push new Contact(this, other, point, contactNormal, depth, 0.5)
+        contacts.push new Contact(this, other, point, contactNormal, depth, 0.3)

     # For simplicity sake, only return the "deepest" contact point. Eventually
-    # the physics engine will need to track more than one contact and update
-    # related contacts as each one is resolved.
+    # the physics engine will need to track more than one contact per pair of
+    # objects and update related contacts as each one is resolved.
     if contacts[1] && contacts[1].depth > contacts[0].depth
       contacts = [contacts[1]]
     else


Fix inspection failure for frame-summary.

--- src/ext/modules/routines.coffee
+++ src/ext/modules/routines.coffee
@@ -723,6 +723,19 @@
     inspect_ frame, inspections
     render_ frame, H2O.FrameOutput, frame

+  extendFrameSummary = (frameKey, frame) ->
+    inspections =
+      columns: inspectFrameColumns 'columns', frameKey, frame, frame.columns
+
+    enumColumns = (column for column in frame.columns when column.type is 'enum')
+    inspections.factors = inspectFrameColumns 'factors', frameKey, frame, enumColumns if enumColumns.length > 0
+
+    origin = "getFrameSummary #{stringify frameKey}"
+    inspections[frame.chunk_summary.name] = inspectTwoDimTable_ origin, frame.chunk_summary.name, frame.chunk_summary
+    inspections[frame.distribution_summary.name] = inspectTwoDimTable_ origin, frame.distribution_summary.name, frame.distribution_summary
+    inspect_ frame, inspections
+    render_ frame, H2O.FrameOutput, frame
+
   extendColumnSummary = (frameKey, frame, columnName) ->
     column = head frame.columns
     rowCount = frame.rows
@@ -887,14 +900,14 @@
       if error
         go error
       else
-        go null, extendFrame frameKey, frame
+        go null, extendFrameSummary frameKey, frame

   requestFrameSummary = (frameKey, go) ->
     _.requestFrameSummarySlice frameKey, undefined, 0, 20, (error, frame) ->
       if error
         go error
       else
-        go null, extendFrame frameKey, frame
+        go null, extendFrameSummary frameKey, frame

   requestColumnSummary = (frameKey, columnName, go) ->
     _.requestColumnSummary frameKey, columnName, (error, frame) ->


Throw Exception on impossible case in functionconstruction

--- src/functionconstruction.coffee
+++ src/functionconstruction.coffee
@@ -66,7 +66,7 @@
     (left, right) ->
       left[key](right)
   else
-    assert "not a function or a field key: " + f, false
+    throw new Exception("not a function or a field key: " + f)

 toFieldKey = (f) ->
   f.slice(1)


:fire: console log

:fire: console log

--- lib/mixins/axis.coffee
+++ lib/mixins/axis.coffee
@@ -166,5 +166,4 @@
     mixin = AxisMixin({axis, dimension, offset, singular, plural})
     mixin[key] = value for key,value of ext

-    console.log mixin
     mixin.includeInto(this)


Make sure we don't divide by zero when calculating justification word spacing

--- lib/mixins/text.coffee
+++ lib/mixins/text.coffee
@@ -167,7 +167,7 @@
                     # calculate the word spacing value
                     textWidth = @widthOfString(text.replace(/\s+/g, ''), options)
                     spaceWidth = @widthOfString(' ') + characterSpacing
-                    wordSpacing = (options.lineWidth - textWidth) / (options.wordCount - 1) - spaceWidth
+                    wordSpacing = (options.lineWidth - textWidth) / Math.max(1, options.wordCount - 1) - spaceWidth

         # flip coordinate system
         @save()


[fix] Button F forces FullScreen

[fix] Button F forces FullScreen

--- app/scripts/views/player_controls.coffee
+++ app/scripts/views/player_controls.coffee
@@ -48,7 +48,14 @@
       if @isValidToShow()
         @showView()
         @initPlayer()
+        @fixPressButtonF()
         @player.api.play()

     isValidToShow: ->
       _.any @model.get('media_urls')
+
+    fixPressButtonF: ->
+      @player.api.enterFullScreen = undefined
+      setTimeout ->
+        $('body').trigger('click')
+      , 5000


Better match allowed unicode characters in identifiers (probably isn't perfect)

--- grammars/rust.cson
+++ grammars/rust.cson
@@ -438,7 +438,7 @@
   'sigils': {
     'comment': 'Sigil'
     'name': 'keyword.operator.sigil.rust'
-    'match': '[&*](?=[a-zA-Z0-9_\\(\\[\\|\\"]+)'
+    'match': '[&*](?=[\\w\\(\\[\\|\\"]+)'
   }
   'self': {
     'comment': 'Self variable'
@@ -489,14 +489,14 @@
   'lifetime': {
     'comment': 'Named lifetime'
     'name': 'storage.modifier.lifetime.rust'
-    'match': '\'([a-zA-Z_][a-zA-Z0-9_]*)\\b'
+    'match': '\'([\\p{Alpha}_][\\w]*)\\b'
     'captures': {
       '1': { 'name': 'entity.name.lifetime.rust' }
     }
   }
   'ref_lifetime': {
     'comment': 'Reference with named lifetime'
-    'match': '&(\'([a-zA-Z_][a-zA-Z0-9_]*))\\b'
+    'match': '&(\'([\\p{Alpha}_][\\w]*))\\b'
     'captures': {
       '1': { 'name': 'storage.modifier.lifetime.rust' }
       '2': { 'name': 'entity.name.lifetime.rust' }
@@ -617,14 +617,14 @@
   # Function and macro calls
   {
     'comment': 'Invokation of a macro'
-    'match': '\\b([a-zA-Z_][a-zA-Z0-9_]*\\!)\\s*[({\\[]'
+    'match': '\\b([\\p{Alpha}_][\\w]*\\!)\\s*[({\\[]'
     'captures': {
       '1': { 'name': 'entity.name.function.macro.rust' }
     }
   }
   {
     'comment': 'Function call'
-    'match': '\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*(?=\\()'
+    'match': '\\b([\\p{Alpha}_][\\w]*)\\s*(?=\\()'
     'captures': {
       '1': { 'name': 'entity.name.function.rust' }
     }
@@ -633,7 +633,7 @@
   # Function definition
   {
     'comment': 'Function definition'
-    'begin': '\\b(fn)\\s+([a-zA-Z_][a-zA-Z0-9_]*)'
+    'begin': '\\b(fn)\\s+([\\p{Alpha}_][\\w]*)'
     'end': '(?=[\\{;])'
     'beginCaptures': {
       '1': { 'name': 'keyword.other.fn.rust' }
@@ -662,7 +662,7 @@
   # Type declaration
   {
     'comment': 'Type declaration'
-    'begin': '\\b(enum|struct|trait)\\s+([a-zA-Z_][a-zA-Z0-9_]*)'
+    'begin': '\\b(enum|struct|trait)\\s+([\\p{Alpha}_][\\w]*)'
     'end': '(?=[\\{\\(;])'
     'beginCaptures': {
       '1': { 'name': 'storage.type.rust' }
@@ -681,7 +681,7 @@
   # Type alias
   {
     'comment': 'Type alias'
-    'begin': '\\b(type)\\s+([a-zA-Z_][a-zA-Z0-9_]*)'
+    'begin': '\\b(type)\\s+([\\p{Alpha}_][\\w]*)'
     'end': '(?=;)'
     'beginCaptures': {
       '1': { 'name': 'storage.type.rust' }
@@ -711,7 +711,7 @@
       { 'include': '#comments' }
       { 'include': '#type_params' }
       {
-        'begin': '([\\p{Alpha}_][\\p{Alnum}_]*)'
+        'begin': '([\\p{Alpha}_][\\w]*)'
         'end': '(?=\\{)'
         'beginCaptures': {
           '1': { 'name': 'entity.name.type.rust' }
@@ -725,7 +725,7 @@
           { 'include': '#where' }
           {
             'name': ''
-            'match': '\\b(for)\\s+([\\p{Alpha}_][\\p{Alnum}_]*)'
+            'match': '\\b(for)\\s+([\\p{Alpha}_][\\w]*)'
             'captures': {
               '1': { 'name': 'storage.type.rust' }
               #'2': { 'name': 'entity.name.type.rust' }


Plugin fix.

Plugin fix.

--- grammars/sifu.cson
+++ grammars/sifu.cson
@@ -793,18 +793,36 @@
             'include': '#restSimple'
           }
           {
+            'include': '#modelSetting'
+          }
+          {
+            'include': '#stub'
+          }
+          {
+            'include': '#queryInlineCount'
+          }
+          {
             'include': '#endpointQueryCount'
           }
           {
-            'include': '#queryInlineCount'
-          }
-          {
             'include': '#requestInlineDto'
           }
           {
             'include': '#requestDto'
           }
           {
+            'include': '#endpointQueryInlineSelect'
+          }
+          {
+            'include': '#endpointQuerySelect'
+          }
+          {
+            'include': '#responseInlineDto'
+          }
+          {
+            'include': '#responseDto'
+          }
+          {
             'include': '#secured'
           }
           {
@@ -812,6 +830,9 @@
           }
           {
             'include': '#endpointErrors'
+          }
+          {
+            'include': '#version'
           }
           {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -849,27 +870,27 @@
             'include': '#modelSetting'
           }
           {
+            'include': '#stub'
+          }
+          {
+            'include': '#requestInlineDto'
+          }
+          {
+            'include': '#requestDto'
+          }
+          {
+            'include': '#endpointQueryInlineSelect'
+          }
+          {
             'include': '#endpointQuerySelect'
           }
           {
-            'include': '#requestInlineDto'
-          }
-          {
-            'include': '#requestDto'
-          }
-          {
             'include': '#responseInlineDto'
           }
           {
             'include': '#responseDto'
           }
           {
-            'include': '#responseMapped'
-          }
-          {
-            'include': '#stub'
-          }
-          {
             'include': '#secured'
           }
           {
@@ -887,58 +908,6 @@
           }
         ]
         'end': '\\]'
-      }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#crudPropertyInt'
-          }
-          {
-            'include': '#crudPropertyText'
-          }
-          {
-            'include': '#crudPropertyBoolean'
-          }
-          {
-            'include': '#crudPropertyDecimal'
-          }
-          {
-            'include': '#crudPropertyDateTime'
-          }
-          {
-            'include': '#crudPropertyDate'
-          }
-          {
-            'include': '#crudPropertyTime'
-          }
-          {
-            'include': '#crudPropertyUUID'
-          }
-          {
-            'include': '#crudPropertyBytes'
-          }
-          {
-            'include': '#crudPropertyInlineEnum'
-          }
-          {
-            'include': '#crudPropertyInlineDto'
-          }
-          {
-            'include': '#crudPropertyCopy'
-          }
-          {
-            'include': '#crudPropertyUnidentified'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
       }
       {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -985,751 +954,6 @@
         'end': '\\]'
       }
       {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#crudPropertyInt'
-          }
-          {
-            'include': '#crudPropertyText'
-          }
-          {
-            'include': '#crudPropertyBoolean'
-          }
-          {
-            'include': '#crudPropertyDecimal'
-          }
-          {
-            'include': '#crudPropertyDateTime'
-          }
-          {
-            'include': '#crudPropertyDate'
-          }
-          {
-            'include': '#crudPropertyTime'
-          }
-          {
-            'include': '#crudPropertyUUID'
-          }
-          {
-            'include': '#crudPropertyBytes'
-          }
-          {
-            'include': '#crudPropertyInlineEnum'
-          }
-          {
-            'include': '#crudPropertyInlineDto'
-          }
-          {
-            'include': '#crudPropertyCopy'
-          }
-          {
-            'include': '#crudPropertyUnidentified'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyBoolean':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+boolean)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyBoolean'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#defaultBoolean'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyBytes':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+bytes)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyBytes'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minNumber'
-          }
-          {
-            'include': '#maxNumber'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyCopy':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*(?:([ \\t]+mandatory))?[ \\t]+([a-zA-Z][_a-zA-Z0-9]*(\\.[a-zA-Z][_a-zA-Z0-9]*)*)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-      '5':
-        'name': 'support.type'
-      '6':
-        'name': 'support.type'
-    'name': 'crudPropertyCopy'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#minAny'
-          }
-          {
-            'include': '#maxAny'
-          }
-          {
-            'include': '#saveValue'
-          }
-          {
-            'include': '#updateValue'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'include': '#scale'
-          }
-          {
-            'include': '#precision'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyDate':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+date)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyDate'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minDate'
-          }
-          {
-            'include': '#maxDate'
-          }
-          {
-            'include': '#defaultDate'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyDateTime':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+datetime)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyDateTime'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minDateTime'
-          }
-          {
-            'include': '#maxDateTime'
-          }
-          {
-            'include': '#defaultDateTime'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyDecimal':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+decimal)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyDecimal'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minDecimal'
-          }
-          {
-            'include': '#maxDecimal'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#defaultDecimal'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'include': '#scale'
-          }
-          {
-            'include': '#precision'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyInlineDto':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+dto)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyInlineDto'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#dtoPropertyInt'
-          }
-          {
-            'include': '#dtoPropertyText'
-          }
-          {
-            'include': '#dtoPropertyBoolean'
-          }
-          {
-            'include': '#dtoPropertyDecimal'
-          }
-          {
-            'include': '#dtoPropertyDateTime'
-          }
-          {
-            'include': '#dtoPropertyDate'
-          }
-          {
-            'include': '#dtoPropertyTime'
-          }
-          {
-            'include': '#dtoPropertyUUID'
-          }
-          {
-            'include': '#dtoPropertyBytes'
-          }
-          {
-            'include': '#dtoPropertyInlineEnum'
-          }
-          {
-            'include': '#dtoPropertyDtoInline'
-          }
-          {
-            'include': '#dtoPropertyUnidentified'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyInlineEnum':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+enum)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyInlineEnum'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#defaultEnum'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#enumValue'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyInt':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+integer)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyInt'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minNumber'
-          }
-          {
-            'include': '#maxNumber'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#defaultNumber'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyText':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+text)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyText'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minNumber'
-          }
-          {
-            'include': '#maxNumber'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#defaultText'
-          }
-          {
-            'include': '#regex'
-          }
-          {
-            'include': '#email'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyTime':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+time)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyTime'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minTime'
-          }
-          {
-            'include': '#maxTime'
-          }
-          {
-            'include': '#defaultTime'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyUUID':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*([ \\t]+bytes)'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'keyword'
-    'name': 'crudPropertyUUID'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'crudPropertyUnidentified':
-    'begin': '([ \\t]*[a-zA-Z][_a-zA-Z0-9]*)(?:(?:([ \\t]+optional)|([ \\t]+list)))*(?:[ \\t]+([a-zA-Z][_a-zA-Z0-9]*(\\.[a-zA-Z][_a-zA-Z0-9]*)*)|[ \\t]+([a-zA-Z][_a-zA-Z0-9]*(\\.[a-zA-Z][_a-zA-Z0-9]*)*))'
-    'beginCaptures':
-      '1':
-        'name': 'punctuation.definition.identity'
-      '2':
-        'name': 'markup.inserted'
-      '3':
-        'name': 'markup.inserted'
-      '4':
-        'name': 'support.type'
-      '5':
-        'name': 'support.type'
-      '6':
-        'name': 'support.type'
-      '7':
-        'name': 'support.type'
-    'name': 'crudPropertyUnidentified'
-    'patterns':[
-      {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#minAny'
-          }
-          {
-            'include': '#maxAny'
-          }
-          {
-            'include': '#defaultAny'
-          }
-          {
-            'include': '#sensitive'
-          }
-          {
-            'include': '#regex'
-          }
-          {
-            'include': '#email'
-          }
-          {
-            'include': '#crudVisible'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
         'name': 'invalid.illegal'
       }
@@ -1756,36 +980,6 @@
     ]
     'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'

-  'crudVisible':
-    'begin': '([ \\t]*visible)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-    'name': 'crudVisible'
-    'patterns':[
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#crudTypeElement'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
   'currencies':
     'begin': '([ \\t]*currencies)'
     'beginCaptures':
@@ -1855,6 +1049,15 @@
             'include': '#comments'
           }
           {
+            'include': '#modelSetting'
+          }
+          {
+            'include': '#paged'
+          }
+          {
+            'include': '#stub'
+          }
+          {
             'include': '#requestInlineDto'
           }
           {
@@ -1880,9 +1083,6 @@
           }
           {
             'include': '#version'
-          }
-          {
-            'include': '#paged'
           }
           {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -2089,25 +1289,22 @@
             'include': '#restSimple'
           }
           {
+            'include': '#modelSetting'
+          }
+          {
+            'include': '#stub'
+          }
+          {
+            'include': '#endpointQueryInlineSelect'
+          }
+          {
             'include': '#endpointQuerySelect'
           }
           {
-            'include': '#endpointQueryInlineSelect'
-          }
-          {
             'include': '#requestInlineDto'
           }
           {
             'include': '#requestDto'
-          }
-          {
-            'include': '#responseInlineDto'
-          }
-          {
-            'include': '#responseDto'
-          }
-          {
-            'include': '#responseMapped'
           }
           {
             'include': '#secured'
@@ -2364,22 +1561,6 @@
     'name': 'dto'
     'patterns':[
       {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#modelSetting'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
         'begin': '\\('
         'patterns': [
           {
@@ -2678,9 +1859,6 @@
           }
           {
             'include': '#sensitive'
-          }
-          {
-            'include': '#modelSetting'
           }
           {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -3028,9 +2206,6 @@
             'include': '#sensitive'
           }
           {
-            'include': '#modelSetting'
-          }
-          {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
             'name': 'invalid.illegal'
           }
@@ -8770,12 +7945,18 @@
             'include': '#modelSetting'
           }
           {
+            'include': '#paged'
+          }
+          {
+            'include': '#stub'
+          }
+          {
+            'include': '#endpointQueryInlineSelect'
+          }
+          {
             'include': '#endpointQuerySelect'
           }
           {
-            'include': '#endpointQueryInlineSelect'
-          }
-          {
             'include': '#queryInlineCount'
           }
           {
@@ -8794,9 +7975,6 @@
             'include': '#responseDto'
           }
           {
-            'include': '#responseMapped'
-          }
-          {
             'include': '#secured'
           }
           {
@@ -8807,9 +7985,6 @@
           }
           {
             'include': '#version'
-          }
-          {
-            'include': '#paged'
           }
           {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -8991,22 +8166,6 @@
     'name': 'requestInlineDto'
     'patterns':[
       {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#modelSetting'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
         'begin': '\\('
         'patterns': [
           {
@@ -9098,22 +8257,6 @@
     'name': 'responseInlineDto'
     'patterns':[
       {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#modelSetting'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
         'begin': '\\('
         'patterns': [
           {
@@ -9162,55 +8305,6 @@
         ]
         'end': '\\)'
       }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'responseMapped':
-    'begin': '([ \\t]*response)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-    'name': 'responseMapped'
-    'patterns':[
-      {
-        'include': '#responseMappedProperty'
-      }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#responseMappedProperty'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'responseMappedProperty':
-    'begin': '[ \\t]*([a-zA-Z][_a-zA-Z0-9]*(\\.[a-zA-Z][_a-zA-Z0-9]*)*)'
-    'beginCaptures':
-      '1':
-        'name': 'support.type'
-      '2':
-        'name': 'support.type'
-    'name': 'responseMappedProperty'
-    'patterns':[
       {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
         'name': 'invalid.illegal'
@@ -9248,22 +8342,6 @@
       '2':
         'name': 'constant'
     'name': 'restSimple'
-    'patterns':[
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'saveValue':
-    'begin': '([ \\t]*save value)([ \\t]+[^(\\s\\{\\}\\[\\]\\(\\)#,)]+)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-      '2':
-        'name': 'constant'
-    'name': 'saveValue'
     'patterns':[
       {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -9487,10 +8565,13 @@
             'include': '#comments'
           }
           {
-            'include': '#securitySignInQuery'
-          }
-          {
-            'include': '#securitySignInResponse'
+            'include': '#endpointQuerySelect'
+          }
+          {
+            'include': '#responseInlineDto'
+          }
+          {
+            'include': '#responseDto'
           }
           {
             'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -9498,57 +8579,6 @@
           }
         ]
         'end': '\\]'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'securitySignInQuery':
-    'begin': '([ \\t]*query)[ \\t]+([a-zA-Z][_a-zA-Z0-9]*(\\.[a-zA-Z][_a-zA-Z0-9]*)*)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-      '2':
-        'name': 'support.type'
-      '3':
-        'name': 'support.type'
-    'name': 'securitySignInQuery'
-    'patterns':[
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'securitySignInResponse':
-    'begin': '([ \\t]*response)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-    'name': 'securitySignInResponse'
-    'patterns':[
-      {
-        'include': '#responseMappedProperty'
-      }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#responseMappedProperty'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
       }
       {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
@@ -11358,22 +10388,6 @@
     'name': 'unidentifiedApiElement'
     'patterns':[
       {
-        'begin': '\\['
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#modelSetting'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\]'
-      }
-      {
         'begin': '\\('
         'patterns': [
           {
@@ -11637,30 +10651,27 @@
             'include': '#modelSetting'
           }
           {
+            'include': '#stub'
+          }
+          {
+            'include': '#requestInlineDto'
+          }
+          {
+            'include': '#requestDto'
+          }
+          {
+            'include': '#endpointQueryInlineSelect'
+          }
+          {
             'include': '#endpointQuerySelect'
           }
           {
-            'include': '#endpointQueryInlineSelect'
-          }
-          {
-            'include': '#requestInlineDto'
-          }
-          {
-            'include': '#requestDto'
-          }
-          {
             'include': '#responseInlineDto'
           }
           {
             'include': '#responseDto'
           }
           {
-            'include': '#responseMapped'
-          }
-          {
-            'include': '#stub'
-          }
-          {
             'include': '#secured'
           }
           {
@@ -11679,74 +10690,6 @@
         ]
         'end': '\\]'
       }
-      {
-        'begin': '\\('
-        'patterns': [
-          {
-            'include': '#comments'
-          }
-          {
-            'include': '#crudPropertyInt'
-          }
-          {
-            'include': '#crudPropertyText'
-          }
-          {
-            'include': '#crudPropertyBoolean'
-          }
-          {
-            'include': '#crudPropertyDecimal'
-          }
-          {
-            'include': '#crudPropertyDateTime'
-          }
-          {
-            'include': '#crudPropertyDate'
-          }
-          {
-            'include': '#crudPropertyTime'
-          }
-          {
-            'include': '#crudPropertyUUID'
-          }
-          {
-            'include': '#crudPropertyBytes'
-          }
-          {
-            'include': '#crudPropertyInlineEnum'
-          }
-          {
-            'include': '#crudPropertyInlineDto'
-          }
-          {
-            'include': '#crudPropertyCopy'
-          }
-          {
-            'include': '#crudPropertyUnidentified'
-          }
-          {
-            'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-            'name': 'invalid.illegal'
-          }
-        ]
-        'end': '\\)'
-      }
-      {
-        'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
-        'name': 'invalid.illegal'
-      }
-    ]
-    'end': '(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n))'
-
-  'updateValue':
-    'begin': '([ \\t]*update value)([ \\t]+[^(\\s\\{\\}\\[\\]\\(\\)#,)]+)'
-    'beginCaptures':
-      '1':
-        'name': 'keyword'
-      '2':
-        'name': 'constant'
-    'name': 'updateValue'
-    'patterns':[
       {
         'match': '[^(\\s\\{\\}\\[\\]\\(\\)#,)]+'
         'name': 'invalid.illegal'
@@ -12044,4 +10987,3 @@
     ]
     'name': 'if'
     'end': '(\\G(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n)))|((?![ \\t]*\\()(?![ \\t]*and )(?![ \\t]*or )(?!(?:([ \\t]*optional))?(?:([ \\t]+mandatory))?([ \\t]+[^(\\s\\{\\}\\[\\]\\(\\)#,)]+)(?:([ \\t]+==)|([ \\t]+!=)|([ \\t]+>=)|([ \\t]+>)|([ \\t]+<=)|([ \\t]+<)|([ \\t]+like)|([ \\t]+in))([ \\t]+[^(\\s\\{\\}\\[\\]\\(\\)#,)]+))(?!(?:([ \\t]*optional))?(?:([ \\t]+mandatory))?([ \\t]+[^(\\s\\{\\}\\[\\]\\(\\)#,)]+)(?:([ \\t]+is null)|([ \\t]+is not null)))|(?=[ \\t]*(,|#|\\]|\\)|\\}|\\n)))'
-


added layer id for setting visibility after rendering

--- scripts/enableRasterLayers.coffee
+++ scripts/enableRasterLayers.coffee
@@ -16,6 +16,7 @@
     height = $toggler.data('height')
     extent = $toggler.data('extent').split(',')
     toggled = $toggler.data('toggled')
+    seasketch_layer_id = $toggler.data('seasketch-layer-id')
     if !url or !width or !height or !extent
       throw new Error("Raster links must include data-raster-url, data-width, data-height, and data-extent attributes")
     layer = new esri.layers.MapImageLayer({visible: toggled})
@@ -39,6 +40,7 @@
     $toggler.replaceWith(toc)
     $toggler = toc.find('.tableOfContentsItem');
     layer.addImage(mapImage);
+    layer.seasketch_layer_id = seasketch_layer_id
     rasterLayersList.push layer
     window.app.projecthomepage.map.addLayer(layer)
     $toggler.data('layer', layer)


case insensitive sort usernames

case insensitive sort usernames

--- resources/assets/coffee/react/beatmap-discussions/main.coffee
+++ resources/assets/coffee/react/beatmap-discussions/main.coffee
@@ -285,9 +285,8 @@
     _ @discussions()
       .map 'user_id'
       .uniq()
-      .map (user_id) =>
-        @users()[user_id]
-      .sortBy 'username'
+      .map (user_id) => @users()[user_id]
+      .orderBy (user) -> user.username.toLocaleLowerCase()
       .value()


Declare ui.router dependency in astrifex.maker module

--- src/modules/app/maker/index.coffee
+++ src/modules/app/maker/index.coffee
@@ -1,6 +1,6 @@
 'use strict'

-module.exports = angular.module('astrifex.maker', [])
+module.exports = angular.module('astrifex.maker', ['ui.router'])
     .config(($stateProvider) ->
       $stateProvider.state 'maker',
       url: ''


Prep Collection methods for extensibility


Add a test. (it passed already.)

Add a test. (it passed already.)

--- test/coffee/uglydb/writeSpec.coffee
+++ test/coffee/uglydb/writeSpec.coffee
@@ -205,3 +205,8 @@
         records = result[2]
         expect(records[17]).toEqual(1)
         expect(records[19]).toEqual(2)
+
+      it 'should translate values _after_ rounding them', ->
+        result = write(array, precision: 2)
+        objects = result[3]
+        expect(objects[0]).toEqual(3.14)