New React-centric approach to image reference counting (still overreactive)

--- client/message.coffee
+++ client/message.coffee
@@ -342,8 +342,8 @@
 threadAuthors = {}
 threadMentions = {}

-images = {}
-imagesInternal = {}
+imageRefCount = new ReactiveDict
+imageInternalRefCount = new ReactiveDict
 id2dom = {}
 scrollToLater = null
 fileQuery = null
@@ -374,49 +374,6 @@
           initImageInternal images[id].file, id
           checkImageInternal images[id].file
 , 250
-
-initImage = (id) ->
-  if id not of images
-    images[id] =
-      count: 0
-      file: null
-    updateFileQuery()
-
-initImageInternal = (id, image = null) ->
-  #console.log "initImageInternal #{id}, #{image}"
-  if id not of imagesInternal
-    imagesInternal[id] =
-      count: 0
-      image: image
-  else if image?
-    imagesInternal[id].image = image
-
-checkImage = (id) ->
-  return unless id of images
-  ## Image gets unnaturally folded if it's referenced at least once
-  ## and doesn't have any children (don't want to hide children, and this
-  ## can also lead to infinite loop if children has the image reference)
-  ## and doesn't refer to any other images (which can also lead to bad loops).
-  newDefault = images[id].naturallyFolded or
-    (not images[id].children and
-     not images[id].images and
-     (images[id].count > 0 or
-      (imagesInternal[images[id].file]? and
-       imagesInternal[images[id].file].count > 0)))
-  console.log id, defaultFolded.get(id), newDefault
-  if newDefault != defaultFolded.get id
-    defaultFolded.set id, newDefault
-    messageFolded.set id, newDefault
-  ## No longer care about this image if it's not referenced and doesn't have
-  ## a rendered template.
-  if images[id].count == 0 and id not of id2dom
-    delete images[id]
-    updateFileQuery()
-
-checkImageInternal = (id) ->
-  image = imagesInternal[id].image
-  #console.log "#{id} corresponds to #{image}"
-  checkImage image if image?

 messageOnDragStart = (message) -> (e) ->
   #url = "coauthor:#{message._id}"
@@ -1825,15 +1782,10 @@
     if scrollToLater == message._id
       scrollToLater = null
       scrollToMessage message._id
-    ## Maintain id2dom mapping, and seed images map
+    ## Maintain id2dom mapping
     id2dom[message._id] = ref.current
-    initImage message._id
-    ## initImage calls updateFileQuery which will do this:
-    #images[message._id].file = message.file
-    #initImageInternal message.file if message.file?
     ->
       delete id2dom[message._id]
-      checkImage message._id
   , [message._id]

   ## Fold naturally folded (minimized and deleted) messages by default on
@@ -1857,52 +1809,66 @@
   , [message._id]
   ###

-  ## Fold referenced attached files by default on initial load.
+  ## Count images referenced by this message, and
+  ## update default folded status of this message.
   messageBodyRef = useRef()
+  ## Depend on references to this message (if it's a file).
+  useTracker ->
+    [
+      imageRefCount.equals 0, message._id
+      imageInternalRefCount.equals 0, message.file if message.file?
+    ]
+  , [message._id, message.file]
   useEffect ->
-    initImage message._id
-    images[message._id].naturallyFolded = naturallyFolded message
-    images[message._id].children = message.children?.length
-    images[message._id].images = 0
+    refCount = 0
     ## If message is naturally folded, don't count images it references.
-    if images[message._id].naturallyFolded or not messageBodyRef.current?
-      return checkImage message._id
-    messageImages = {}
-    messageImagesInternal = {}
-    for elt in messageBodyRef.current.querySelectorAll """
-      img[src^="#{fileUrlPrefix}"],
-      img[src^="#{fileAbsoluteUrlPrefix}"],
-      img[src^="#{internalFileUrlPrefix}"],
-      img[src^="#{internalFileAbsoluteUrlPrefix}"],
-      video source[src^="#{fileUrlPrefix}"],
-      video source[src^="#{fileAbsoluteUrlPrefix}"],
-      video source[src^="#{internalFileUrlPrefix}"],
-      video source[src^="#{internalFileAbsoluteUrlPrefix}"]
-    """
-      images[message._id].links += 1
-      src = elt.getAttribute 'src'
-      if 0 <= src.indexOf 'gridfs'
-        messageImagesInternal[url2internalFile src] = true
-      else
-        messageImages[url2file src] = true
-    for id of messageImages
-      #console.log 'source', id
-      initImage id
-      images[id].count += 1
-      checkImage id
-    for id of messageImagesInternal
-      #console.log 'source', id
-      initImageInternal id
-      imagesInternal[id].count += 1
-      checkImageInternal id
-    checkImage message._id
-    ->
+    natural = naturallyFolded message
+    if not natural and messageBodyRef.current?
+      messageImages = {}
+      messageImagesInternal = {}
+      for elt in messageBodyRef.current.querySelectorAll """
+        img[src^="#{fileUrlPrefix}"],
+        img[src^="#{fileAbsoluteUrlPrefix}"],
+        img[src^="#{internalFileUrlPrefix}"],
+        img[src^="#{internalFileAbsoluteUrlPrefix}"],
+        video source[src^="#{fileUrlPrefix}"],
+        video source[src^="#{fileAbsoluteUrlPrefix}"],
+        video source[src^="#{internalFileUrlPrefix}"],
+        video source[src^="#{internalFileAbsoluteUrlPrefix}"]
+      """
+        refCount += 1
+        src = elt.getAttribute 'src'
+        if 0 <= src.indexOf 'gridfs'
+          messageImagesInternal[url2internalFile src] = true
+        else
+          messageImages[url2file src] = true
       for id of messageImages
-        images[id].count -= 1
-        checkImage id
+        imageRefCount.set id, (imageRefCount.get(id) ? 0) + 1
       for id of messageImagesInternal
-        imagesInternal[id].count -= 1
-        checkImageInternal id
+        imageInternalRefCount.set id, (imageInternalRefCount.get(id) ? 0) + 1
+      uncount = ->
+        for id of messageImages
+          imageRefCount.set id, (imageRefCount.get(id) ? 0) - 1
+        for id of messageImagesInternal
+          imageInternalRefCount.set id, (imageInternalRefCount.get(id) ? 0) - 1
+    ## Image gets unnaturally folded if it's referenced at least once
+    ## and doesn't have any children (don't want to hide children, and this
+    ## can also lead to infinite loop if children has the image reference)
+    ## and doesn't refer to any other images (which can also lead to bad loops).
+    newDefault = natural or
+      (not message.children?.length and
+       not refCount and
+       (imageRefCount.get(message._id) or
+        imageInternalRefCount.get(message.file)))
+    console.log message._id, defaultFolded.get(message._id), newDefault, natural,
+      not message.children?.length,
+      not refCount,
+      imageRefCount.get(message._id),
+      imageInternalRefCount.get(message.file)
+    if newDefault != defaultFolded.get message._id
+      defaultFolded.set message._id, newDefault
+      messageFolded.set message._id, newDefault
+    uncount
   # too many dependencies to list

   ## Transform images


keymaps for different OS

keymaps for different OS

--- keymaps/md.cson
+++ keymaps/md.cson
@@ -1,11 +1,38 @@
-".editor":
-  "cmd-k": "markdown-writer:insert-link"
-  "cmd-i": "markdown-writer:toggle-italic-text"
-  "cmd-b": "markdown-writer:toggle-bold-text"
-  "cmd-'": "markdown-writer:toggle-code-text"
-  "cmd-h": "markdown-writer:toggle-strikethrough-text"
-  "alt-1": "markdown-writer:toggle-h1"
-  "alt-2": "markdown-writer:toggle-h2"
-  "alt-3": "markdown-writer:toggle-h3"
-  "alt-4": "markdown-writer:toggle-h4"
-  "alt-5": "markdown-writer:toggle-h5"
+".platform-darwin":
+  ".editor":
+    "cmd-k": "markdown-writer:insert-link"
+    "cmd-i": "markdown-writer:toggle-italic-text"
+    "cmd-b": "markdown-writer:toggle-bold-text"
+    "cmd-'": "markdown-writer:toggle-code-text"
+    "cmd-h": "markdown-writer:toggle-strikethrough-text"
+    "alt-1": "markdown-writer:toggle-h1"
+    "alt-2": "markdown-writer:toggle-h2"
+    "alt-3": "markdown-writer:toggle-h3"
+    "alt-4": "markdown-writer:toggle-h4"
+    "alt-5": "markdown-writer:toggle-h5"
+
+".platform-win32":
+  ".editor":
+    "ctrl-k": "markdown-writer:insert-link"
+    "ctrl-i": "markdown-writer:toggle-italic-text"
+    "ctrl-b": "markdown-writer:toggle-bold-text"
+    "ctrl-'": "markdown-writer:toggle-code-text"
+    "ctrl-h": "markdown-writer:toggle-strikethrough-text"
+    "ctrl-1": "markdown-writer:toggle-h1"
+    "ctrl-2": "markdown-writer:toggle-h2"
+    "ctrl-3": "markdown-writer:toggle-h3"
+    "ctrl-4": "markdown-writer:toggle-h4"
+    "ctrl-5": "markdown-writer:toggle-h5"
+
+".platform-linux":
+  ".editor":
+    "ctrl-k": "markdown-writer:insert-link"
+    "ctrl-i": "markdown-writer:toggle-italic-text"
+    "ctrl-b": "markdown-writer:toggle-bold-text"
+    "ctrl-'": "markdown-writer:toggle-code-text"
+    "ctrl-h": "markdown-writer:toggle-strikethrough-text"
+    "ctrl-1": "markdown-writer:toggle-h1"
+    "ctrl-2": "markdown-writer:toggle-h2"
+    "ctrl-3": "markdown-writer:toggle-h3"
+    "ctrl-4": "markdown-writer:toggle-h4"
+    "ctrl-5": "markdown-writer:toggle-h5"


Updates atom config.

Updates atom config.

--- env/atom.config.cson
+++ env/atom.config.cson
@@ -105,7 +105,6 @@
 			120
 		]
 		locked: true
-	"opened-files": {}
 	"python-yapf":
 		yapfStyle: "{based_on_style: pep8, column_limit: 120}"
 	"spell-check":
@@ -142,15 +141,10 @@
 ".coffee.source":
 	editor:
 		tabLength: 2
-".css.less.source":
+".html.mustache.text":
 	editor:
-		softTabs: true
-		tabLength: 4
-".gfm.source":
-	editor:
-		softTabs: true
-		tabLength: 4
-".html.mustache.text":
+		tabLength: 2
+".json.source":
 	editor:
 		tabLength: 2
 ".source.yaml":


Need to cleanup layout directory

Need to cleanup layout directory

--- src/layout.coffee
+++ src/layout.coffee
@@ -1,3 +1,5 @@
+# TODO: Move views into a different folder than layout tools
+
 {my} = require './my'
 {render} = require './layout/render'
 {header} = require './layout/header'


remove JS errors on home page

remove JS errors on home page

--- client/views/home.coffee
+++ client/views/home.coffee
@@ -3,8 +3,17 @@

 Template.home.helpers
   project_name: ->
-    NeuronCatalogConfig.findOne({}).project_name
+    doc = NeuronCatalogConfig.findOne({})
+    if !doc?
+      return
+    doc.project_name
   data_authors: ->
-    html_sanitize(NeuronCatalogConfig.findOne({}).data_authors, url_converter )
+    doc = NeuronCatalogConfig.findOne({})
+    if !doc? or !doc.data_authors?
+      return
+    html_sanitize(doc.data_authors, url_converter )
   blurb: ->
-    html_sanitize(NeuronCatalogConfig.findOne({}).blurb, url_converter )
+    doc = NeuronCatalogConfig.findOne({})
+    if !doc? or !doc.blurb?
+      return
+    return html_sanitize(doc.blurb, url_converter )


feature(jqLite): Extended jqLite with new functions. Includes height(), width(), outerHeight(), outerWidth(), offset() and scrollTop()

--- src/main.coffee
+++ src/main.coffee
@@ -1 +1,157 @@
 angular.module "Mac", ["Mac.Util"]
+
+###
+@chalk overview
+@name angular.element
+
+@description
+Angular comes with jqLite, a tiny, API-compatible subset of jQuery.
+However, its functionality is very limited and MacGyver extends
+jqLite to make sure MacGyver components work properly.
+
+Real jQuery will continue to take precedence over jqLite and all
+functions MacGyver extends.
+
+MacGyver adds the following methods:
+- [height()](http://api.jquery.com/height/) - Does not support set
+- [width()](http://api.jquery.com/width/) - Does not support set
+- [outerHeight()](http://api.jquery.com/outerHeight/) - Does not support set
+- [outerWidth()](http://api.jquery.com/outerWidth/) - Does not support set
+- [offset()](http://api.jquery.com/offset/)
+- [scrollTop()](http://api.jquery.com/scrollTop/)
+###
+
+cssExpand = [ "Top", "Right", "Bottom", "Left" ]
+core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source
+rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" )
+
+getStyles = (element) ->
+  window.getComputedStyle element, null
+
+isWindow = (obj) ->
+  return obj and obj.document and obj.location and obj.alert and obj.setInterval
+
+getWindow = (element) ->
+  if isWindow(element) then element else element.nodeType is 9 and element.defaultView
+
+augmentWidthOrHeight = (element, name, extra, isBorderBox, styles) ->
+  return 0 if extra is (if isBorderBox then "border" else "content")
+
+  start = if name is "Width" then 1 else 0
+
+  for i in [start..3] by 2
+    if extra is "margin"
+      val += parseFloat styles["#{extra}#{cssExpand[i]}"]
+
+    if isBorderBox
+      if extra is "content"
+        val -= parseFloat styles["padding#{cssExpand[i]}"]
+
+      if extra isnt "margin"
+        val -= parseFloat styles["border#{cssExpand[i]}"]
+    else
+      val += parseFloat styles["padding#{cssExpand[i]}"]
+
+      if extra isnt "padding"
+        val += parseFloat styles["border#{cssExpand}Width"]
+
+  return val
+
+getWidthOrHeight = (type, prefix, element) ->
+
+  return (margin) ->
+    defaultExtra =
+      switch prefix
+        when "inner" then "padding"
+        when "outer" then ""
+        else              "content"
+    extra = defaultExtra or (if margin is true then "margin" else "border")
+
+    if isWindow element
+      return element.document.documentElement["client#{type}"]
+
+    if element.nodeType is 9
+      doc = element.documentElement
+
+      return Math.max(
+        element.body["scroll#{type}"], doc["scroll#{type}"],
+        element.body["offset#{type}"], doc["offset#{type}"],
+        doc["client#{type}"]
+      )
+
+    valueIsBorderBox = true
+    styles           = getStyles element
+    name             = type.toLowerCase()
+    value            =
+      if type is "Height" then element.offsetHeight else element.offsetWidth
+    isBorderBox = element.style.boxSizing is "border-box"
+
+    if value <= 0 or value is null
+      value = styles[name]
+      if value < 0 or value is null
+        value = element.style[name]
+
+      return value if rnumnonpx.test value
+
+      valueIsBorderBox = isBorderBox
+
+      value = parseFloat(value) or 0
+
+    value + augmentWidthOrHeight(
+      element
+      type
+      extra or (if isBorderBox then "border" else "content")
+      valueIsBorderBox
+      styles
+    )
+
+jqLiteExtend =
+  height: (element) ->
+    getWidthOrHeight("Height", "", element)()
+
+  width: (element) ->
+    getWidthOrHeight("Width", "", element)()
+
+  outerHeight: (element, margin) ->
+    getWidthOrHeight("Height", "outer", element) margin
+
+  outerWidth: (element, margin) ->
+    getWidthOrHeight("Width", "outer", element) margin
+
+  offset: (element) ->
+    box = {top: 0, left: 0}
+    doc = element && element.ownerDocument
+
+    return unless doc
+
+    docElem = doc.documentElement
+    if element.getBoundingClientRect?
+      box = element.getBoundingClientRect()
+
+    win = getWindow doc
+    return {
+      top: box.top + win.pageYOffset - docElem.clientTop,
+      left: box.left + win.pageXOffset - docElem.clientLeft
+    }
+
+  scrollTop: (element, value) ->
+    win = getWindow element
+
+    unless value?
+      return if win then win["pageYOffset"] else element["scrollTop"]
+
+    if win
+      win.scrollTo(window.pageXOffset, value)
+    else
+      element["scrollTop"] = value
+
+extendjQuery = ->
+  return if window.jQuery?
+
+  jqLite = angular.element
+
+  angular.forEach jqLiteExtend, (fn, name) ->
+    jqLite.prototype[name] = (arg1, arg2) ->
+      return fn(this[0], arg1, arg2) if this.length
+
+extendjQuery()


remove coffee (no need for both)

remove coffee (no need for both)

--- capn-crunch.coffee
+++ capn-crunch.coffee
@@ -1,53 +0,0 @@
-glob = require("glob")
-path = require("path")
-fs = require("fs")
-uglify = require("uglify-js")
-CleanCSS = require("clean-css")
-root = process.argv[2]
-rootStat = undefined
-
-unless root
-  console.log "Usage: coffee capn-crunch folder"
-  process.exit -1
-
-rootStat = fs.statSync(root)
-unless rootStat and rootStat.isDirectory()
-  console.log root + " is not a directory"
-  process.exit -1
-
-sourceExp = /\.(js|css)$/
-minnedExp = /\.min\.(js|css)$/
-getMinName = (fname) -> fname.replace sourceExp, ".min.$1"
-isNotMinnedFile = (fname) -> not minnedExp.test(fname)
-relPath = (fname) -> fname.replace root.replace(/\\/g, "/"), ""
-getExt = (fname) -> sourceExp.exec(fname)[1]
-fullPath = (fname) -> path.join root, fname
-
-cleaner = new CleanCSS()
-minifiers =
-  js: (fname) -> uglify.minify(fname).code
-  css: (fname) -> cleaner.minify fs.readFileSync fname, encoding: "utf-8"
-
-needsMin = (fname) ->
-  min = getMinName(fname)
-  try
-    fs.statSync(fname).mtime > fs.statSync(min).mtime
-  catch e
-    true # statSync threw on min file name
-
-writeMinned = (fname) ->
-  try
-    minifier = minifiers[getExt(fname)]
-    minFile = getMinName(fname)
-    fs.writeFile minFile, minifier(fname), encoding: "utf-8" , (err) ->
-      throw err  if err
-      console.log "Wrote " + relPath(minFile)
-  catch e
-    console.log "Error minifying #{fname}: #{e}"
-
-glob "**/*.{js,css}", cwd: root , (err, files) ->
-  throw err  if err
-  files = files.map(fullPath)
-  files = files.filter(isNotMinnedFile)
-  files = files.filter(needsMin)
-  files.forEach writeMinned


force buckets to show on load of group page


Fixed spelling error. Closes #1.

Fixed spelling error. Closes #1.

--- src/lib/strata_.coffee
+++ src/lib/strata_.coffee
@@ -10,7 +10,7 @@
 #
 # Strata is a [b-tree](http://en.wikipedia.org/wiki/B-tree) implementation for
 # [Node.js](http://nodejs.org/) that is **evented**, **concurrent**,
-# **persistant** and **durable**.
+# **persistent** and **durable**.
 #
 # Strata is **evented**. It uses asynchronous I/O to read and write b-tree
 # pages, allowing your CPU to continue to do work while Strata waits on I/O.
@@ -19,7 +19,7 @@
 # cache when it can, so requests can be satisifed, even when there are evented
 # I/O requests outstanding.
 #
-# Strata is **persistant**. It stores your tree in page files. The page files
+# Strata is **persistent**. It stores your tree in page files. The page files
 # are plain old JSON, text files that are easy to manage. You can open them up
 # in a `vim` or EMACS, back them up using `tar`, check them into `git`, and
 # munge them with JavaScript.


bring callback in right call order for new tags

--- src/linker.coffee
+++ src/linker.coffee
@@ -59,10 +59,9 @@
 # create a new tag (and children) from data structure
 new_tag = (parent, el, callback) ->
     attrs = deep_merge el.attrs # copy data
-    parent.tag el.name, attrs, ->
-        @once 'end', ->
-            callback?()
-        @end()
+    tag = parent.tag(el.name, attrs).end()
+    callback()
+    return tag

 ##
 # apply possible additions from the data structure on the tag
@@ -115,7 +114,7 @@
                 tag.text?(el, append:on)
                 do repeat

-            else if el?# create new tag
+            else if el? # create new tag
                 # create and insert the new tag from el and delay work
                 new_tag(tag, el, repeat)


mobile sidebar - scroll to top on switch

--- _assets/javascripts/shared/shared.coffee
+++ _assets/javascripts/shared/shared.coffee
@@ -44,6 +44,7 @@
     $("header.top .drawer_toggle a").click (e) ->
       e.preventDefault()
       $('body').toggleClass('drawer_open')
+      $('body').scrollTop 0

   mobileWatchSearchToggle: ->
     $(".search_holder .trigger .search").click (e) ->


Fix typo in listener.

Fix typo in listener.


Update autocomplete.cson - Fix deprecation warning

Fix atom deprecation warning about the use of .editor class and editor-mini

--- keymaps/autocomplete.cson
+++ keymaps/autocomplete.cson
@@ -1,6 +1,6 @@
-'.editor':
+'atom-text-editor':
   'ctrl-alt-space': 'autocomplete-jedi:toggle'

-'.autocomplete-jedi .mini.editor input':
+'.autocomplete-jedi atom-text-editor[mini] input':
   'enter': 'core:confirm'
   'tab': 'core:confirm'


:penguin: Add linux keymaps.

:penguin: Add linux keymaps.

--- keymaps/fuzzy-finder.cson
+++ keymaps/fuzzy-finder.cson
@@ -9,3 +9,9 @@
   'ctrl-p': 'fuzzy-finder:toggle-file-finder'
   'ctrl-b': 'fuzzy-finder:toggle-buffer-finder'
   'ctrl-B': 'fuzzy-finder:toggle-git-status-finder'
+
+'.platform-linux':
+  'ctrl-t': 'fuzzy-finder:toggle-file-finder'
+  'ctrl-p': 'fuzzy-finder:toggle-file-finder'
+  'ctrl-b': 'fuzzy-finder:toggle-buffer-finder'
+  'ctrl-B': 'fuzzy-finder:toggle-git-status-finder'


Understand onDidChangeEmbeddedTextEditor() to track embedded TextEditor changes

--- lib/find.coffee
+++ lib/find.coffee
@@ -21,6 +21,7 @@
       new ResultsPaneView() if filePath is ResultsPaneView.URI

     @subscriptions = new CompositeDisposable
+    @currentItemSub = new Disposable
     @findHistory = new History(findHistory)
     @replaceHistory = new History(replaceHistory)
     @pathsHistory = new History(pathsHistory)
@@ -30,8 +31,16 @@
     @resultsModel = new ResultsModel(@findOptions)

     @subscriptions.add atom.workspace.getCenter().observeActivePaneItem (paneItem) =>
+      @subscriptions.delete @currentItemSub
+      @currentItemSub.dispose()
+
       if atom.workspace.isTextEditor(paneItem)
         @findModel.setEditor(paneItem)
+      else if paneItem?.onDidChangeEmbeddedTextEditor?
+        @currentItemSub = paneItem.onDidChangeEmbeddedTextEditor (editor) =>
+          if atom.workspace.getCenter().getActivePaneItem() is paneItem
+            @findModel.setEditor(editor)
+        @subscriptions.add @currentItemSub
       else if paneItem?.getEmbeddedTextEditor?
         @findModel.setEditor(paneItem.getEmbeddedTextEditor())
       else


errors: removed old ParameterError

errors: removed old ParameterError

--- coffee/src/errors.coffee
+++ coffee/src/errors.coffee
@@ -5,9 +5,6 @@

 NotSupportedError = (method, extra) ->
   throw new Error "#{method} not supported. #{extra ? ''}"
-
-ParameterError = (param) ->
-  throw new Error "Parameter error: #{param} must be specified."

 UrlError = ->
   ParameterError '"url" property or function'
@@ -26,7 +23,6 @@

 acorn.errors.UrlError = UrlError
 acorn.errors.APIError = APIError
-acorn.errors.ParameterError = ParameterError
 acorn.errors.NotSupportedError = NotSupportedError
 acorn.errors.UndefinedShellError = UndefinedShellError
 acorn.errors.NotImplementedError = NotImplementedError


:lipstick:

:lipstick:

--- keymaps/default.cson
+++ keymaps/default.cson
@@ -14,6 +14,8 @@
   'ctrl-N': 'core:select-down'
   'ctrl-F': 'core:select-right'
   'ctrl-B': 'core:select-left'
+  'ctrl-h': 'core:backspace'
+  'ctrl-d': 'core:delete'

   # Atom Specific
   'meta-O': 'application:open-dev'
@@ -29,6 +31,7 @@
   'meta-alt-ctrl-p': 'window:run-package-specs'

   # Sublime Parity
+  'meta-,': 'application:show-settings'
   'meta-N': 'application:new-window'
   'meta-W': 'window:close'
   'meta-o': 'application:open'
@@ -62,6 +65,27 @@
   'meta-}': 'pane:show-next-item'
   'meta-alt-left': 'pane:show-previous-item'
   'meta-alt-right': 'pane:show-next-item'
+  'meta-=': 'window:increase-font-size'
+  'meta-+': 'window:increase-font-size'
+  'meta--': 'window:decrease-font-size'
+
+  'meta-k up': 'pane:split-up' # Atom Specific
+  'meta-k down': 'pane:split-down' # Atom Specific
+  'meta-k left': 'pane:split-left' # Atom Specific
+  'meta-k right': 'pane:split-right' # Atom Specific
+  'meta-k meta-w': 'pane:close' # Atom Specific
+  'meta-k alt-meta-w': 'pane:close-other-items' # Atom Specific
+  'meta-k meta-left': 'window:focus-previous-pane'
+  'meta-k meta-right': 'window:focus-next-pane'
+  'meta-1': 'pane:show-item-1'
+  'meta-2': 'pane:show-item-2'
+  'meta-3': 'pane:show-item-3'
+  'meta-4': 'pane:show-item-4'
+  'meta-5': 'pane:show-item-5'
+  'meta-6': 'pane:show-item-6'
+  'meta-7': 'pane:show-item-7'
+  'meta-8': 'pane:show-item-8'
+  'meta-9': 'pane:show-item-9'

 '.editor':
   # Apple Specific
@@ -83,27 +107,33 @@
   'shift-end': 'editor:select-to-end-of-line'
   'alt-backspace': 'editor:backspace-to-beginning-of-word'
   'alt-delete': 'editor:delete-to-end-of-word'
-
   'ctrl-a': 'editor:move-to-first-character-of-line'
   'ctrl-e': 'editor:move-to-end-of-line'
   'ctrl-k': 'editor:cut-to-end-of-line'
-  'ctrl-h': 'core:backspace'
-  'ctrl-d': 'core:delete'
-
+
+  # Atom Specific
+  'ctrl-W': 'editor:select-word'
+
+  # Sublime Parity
+  'meta-a': 'core:select-all'
+  'meta-alt-p': 'editor:log-cursor-scope'
+  'ctrl-t': 'editor:transpose'
+  'meta-k meta-u': 'editor:upper-case'
+  'meta-k meta-l': 'editor:lower-case'
+
+'.editor:not(.mini)':
   # Atom Specific
   'alt-meta-z': 'editor:checkout-head-revision'
   'meta-<': 'editor:scroll-to-cursor'
   'ctrl-C': 'editor:copy-path'
   'alt-meta-ctrl-f': 'editor:fold-selection'
   'meta-=': 'editor:auto-indent'
-  'ctrl-W': 'editor:select-word'

   # Sublime Parity
   'tab': 'editor:indent'
   'enter': 'editor:newline'
   'meta-enter': 'editor:newline-below'
   'meta-shift-enter': 'editor:newline-above'
-  'meta-a': 'core:select-all'
   'meta-]': 'editor:indent-selected-rows'
   'meta-[': 'editor:outdent-selected-rows'
   'shift-tab': 'editor:outdent-selected-rows'
@@ -112,14 +142,6 @@
   'meta-/': 'editor:toggle-line-comments'
   'meta-j': 'editor:join-line'
   'meta-D': 'editor:duplicate-line'
-  'meta-alt-p': 'editor:log-cursor-scope'
-  'ctrl-t': 'editor:transpose'
-  'meta-,': 'application:show-settings'
-  'meta-k meta-u': 'editor:upper-case'
-  'meta-k meta-l': 'editor:lower-case'
-  'meta-=': 'window:increase-font-size'
-  'meta-+': 'window:increase-font-size'
-  'meta--': 'window:decrease-font-size'
   'ctrl-K': 'editor:delete-line'
   'ctrl-shift-up': 'editor:add-selection-above'
   'ctrl-shift-down': 'editor:add-selection-below'
@@ -139,32 +161,9 @@
   'meta-k meta-8': 'editor:fold-at-indent-level-8'
   'meta-k meta-9': 'editor:fold-at-indent-level-9'

-  'meta-k up': 'pane:split-up' # Atom Specific
-  'meta-k down': 'pane:split-down' # Atom Specific
-  'meta-k left': 'pane:split-left' # Atom Specific
-  'meta-k right': 'pane:split-right' # Atom Specific
-  'meta-k meta-w': 'pane:close' # Atom Specific
-  'meta-k alt-meta-w': 'pane:close-other-items' # Atom Specific
-  'meta-k meta-left': 'window:focus-previous-pane'
-  'meta-k meta-right': 'window:focus-next-pane'
-  'meta-1': 'pane:show-item-1'
-  'meta-2': 'pane:show-item-2'
-  'meta-3': 'pane:show-item-3'
-  'meta-4': 'pane:show-item-4'
-  'meta-5': 'pane:show-item-5'
-  'meta-6': 'pane:show-item-6'
-  'meta-7': 'pane:show-item-7'
-  'meta-8': 'pane:show-item-8'
-  'meta-9': 'pane:show-item-9'
-
 '.tool-panel':
-  'meta-escape': 'tool-panel:unfocus'
   'escape': 'core:close'

-'.mini.editor':
-  'enter': 'core:confirm'
-  'escape': 'core:cancel'
-  'meta-w': 'core:cancel'

 '.editor !important, .editor.mini !important':
   'escape': 'editor:consolidate-selections'
@@ -231,9 +230,6 @@
 #   'meta-k meta-c': 'show_at_center'
 #   'ctrl-shift-w': 'insert_snippet', 'args': { 'name': 'Packages/XML/long-tag.sublime-snippet' }
 #
-#   # Tree View
-#   'meta-k meta-b': 'toggle_side_bar'
-#
 #   # Fuzzy Finder
 #   'meta-t': 'show_overlay', 'args': {'overlay': 'goto', 'show_files': true}
 #   'meta-p': 'show_overlay', 'args': {'overlay': 'goto', 'show_files': true}


init sshcmd on element instantiation

init sshcmd on element instantiation

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -377,7 +377,7 @@
         sys: "root",
         model: "",
         args: "",
-        init: ""
+        init: "",
       }
       @id = {
         name: "model0",
@@ -462,7 +462,8 @@
         design: dsg,
         sense: "",
         actuate: ""
-        interfaces: {}
+        interfaces: {},
+        sshcmd: ""
       }
       @id = {
         name: "sax0",
@@ -494,7 +495,8 @@
         sys: "root",
         capacity: 100,
         latency: 0,
-        interfaces: {}
+        interfaces: {},
+        sshcmd: ""
       }
       @id = {
         name: "router0"
@@ -746,13 +748,15 @@
   addElement: (ef, x, y) ->
     e = null
     if ef instanceof Phyo and ef.tex?
-      #ef.shp.obj3d.userData = null
       e = new ef.constructor(@baseRect, x, y, 50, ef.tex)
     else
       e = new ef.constructor(@baseRect, x, y, 50)
     e.props.name = @ve.namemanager.getName(e.constructor.name.toLowerCase())
     e.id.name = e.props.name
     e.props.design = dsg
+    if e.props.sshcmd?
+      BaseElements.setSshCmd(e)
+
     @elements.push(e)
     @ve.render()
     e


Clear require cache to ensure changes are used when watching.

--- compiler/index.coffee
+++ compiler/index.coffee
@@ -46,6 +46,12 @@
         ['CONTENT_API_TOKEN', 'CONTENT_API_ROOT', 'HOST'].forEach (prop) ->
             unless project_config[prop]
                 throw new SDKError('configuration', "Project missing `package.marquee.#{ prop }`.")
+
+        # Clear the require cache first to ensure it loads the most recent
+        # version when watching.
+        Object.keys(require.cache).forEach (key) ->
+            if key.indexOf(project_directory) > -1
+                delete require.cache[key]

         # Load the project compiler entrypoint.
         buildFn = require(project_main)


[grunt] use `pathBreadcrumb` to generate `data.page.breadcrumb`

--- tasks/grunt-nunjucks-2-html.coffee
+++ tasks/grunt-nunjucks-2-html.coffee
@@ -547,7 +547,7 @@
           data.page.region     = getRegioncode(currentLocale)
           data.page.rtl        = localeProps.rtl
           data.page.url        = if pagedir == '.' then '' else pagedir
-          data.page.breadcrumb = if pagedir == '.' then [pagebasename] else pagedir.split('/')
+          data.page.breadcrumb = pathBreadcrumb(pagepath)
           data.page.basename   = pagebasename
           data.page.dirname    = pagedirname


Refactor font loading code and add support for ArrayBuffers

--- lib/font.coffee
+++ lib/font.coffee
@@ -15,32 +15,32 @@
         @isAFM = true
         @font = new AFMFont STANDARD_FONTS[src]()
         @registerAFM src
+        return

       else if /\.(ttf|ttc)$/i.test src
         @font = TTFFont.open src, family
-        @subset = new Subset @font
-        @registerTTF()

       else if /\.dfont$/i.test src
         @font = TTFFont.fromDFont src, family
-        @subset = new Subset @font
-        @registerTTF()

       else
         throw new Error 'Not a supported font format or standard PDF font.'

     else if Buffer.isBuffer(src)
       @font = TTFFont.fromBuffer src, family
-      @subset = new Subset @font
-      @registerTTF()

     else if src instanceof Uint8Array
-      @font = TTFFont.fromBuffer (new Buffer src), family
-      @subset = new Subset @font
-      @registerTTF()
+      @font = TTFFont.fromBuffer new Buffer(src), family
+
+    else if src instanceof ArrayBuffer
+      @font = TTFFont.fromBuffer new Buffer(new Uint8Array(src)), family

     else
       throw new Error 'Not a supported font format or standard PDF font.'
+
+    # create a subset for the font and register
+    @subset = new Subset @font
+    @registerTTF()

   # This insanity is so browserify can inline the font files
   STANDARD_FONTS =


Fix typo in function call

Fix typo in function call

--- src/pane.coffee
+++ src/pane.coffee
@@ -713,7 +713,7 @@
     if @parent.orientation is 'vertical'
       bottommostSibling = last(@parent.children)
       if bottommostSibling instanceof PaneAxis
-        @splitRight()
+        @splitDown()
       else
         bottommostSibling
     else


More todos.

More todos.

--- test/OVirtResponseHydrator.test.coffee
+++ test/OVirtResponseHydrator.test.coffee
@@ -224,6 +224,10 @@
         expect(spy).to.be.called hash.api.link.length

+  describe.skip "#getHydratedProperties", ->
+    # @todo Add tests for .getHydratedProperties()
+
+
   describe "#isLink", ->
     hydrator = do getHydrator


add remote_filter;

add remote_filter;

--- coffee/jquery.atwho.coffee
+++ coffee/jquery.atwho.coffee
@@ -68,8 +68,6 @@
       ENTER: 13

     DEFAULT_CALLBACKS =
-      data_source: ->
-        []

       matcher: (flag, subtext) ->
         regexp = new RegExp flag+'([A-Za-z0-9_\+\-]*)$|'+flag+'([^\\x00-\\xff]*)$','gi'
@@ -79,44 +77,53 @@
           matched = if match[2] then match[2] else match[1]
         matched

-      filter: (key) ->
-        data = this.data_source()
+      filter: (query, data, search_query) ->
         if $.isArray(data) and data.length != 0
           items = $.map data, (item,i) =>
             try
-              name = if $.isPlainObject item then item[this.data_value()] else item
-              regexp = new RegExp(key.text.replace("+","\\+"),'i')
+              name = if $.isPlainObject item then item[search_query] else item
+              regexp = new RegExp(query.replace("+","\\+"),'i')
               match = name.match(regexp)
             catch e
               return null
             return if match then item else null
         items

-      tpl_eval = (tpl, map) ->
+      remote_filter: (params, url, render_view) ->
+        $.ajax url, params, (data) ->
+          names = $.parseJSON(data)
+          render_view(names)
+
+      tpl_eval: (tpl, map) ->
         try
           el = tpl.replace /\$\{([^\}]*)\}/g, (tag,key,pos) ->
             map[key]
         catch error
           ""

-      highlighter = (li, query) ->
+      highlighter: (li, query) ->
         return li if not query
         li.replace new RegExp(">\\s*(\\w*)(" + query.replace("+","\\+") + ")(\\w*)\\s*<", 'ig'), (str,$1, $2, $3) ->
             '> '+$1+'' + $2 + ''+$3+' <'

-      sorter = (query, items) ->
-        data_value = this.data_value()
+      sorter: (query, items, search_key) ->
         results = []

         for item in items
-            text = item[data_value]
-            continue if text.toLowerCase().indexOf(query) is -1
-            item.order = text.toLowerCase().indexOf query
-            results.push(item)
+          text = item[search_key]
+          continue if text.toLowerCase().indexOf(query) is -1
+          item.order = text.toLowerCase().indexOf query
+          results.push(item)

         results.sort (a,b) ->
-            a.order - b.order
+          a.order - b.order
         return results
+
+      data_refactor: (data) ->
+        $.map data, (item, k) ->
+          if not $.isPlainObject item
+            item = {id:k, name:item}
+          return item

     class At
@@ -159,12 +166,10 @@

       get_opt: (key, default) ->
         try
-          @settings[@current_flag][key]
+          opt = @settings[@current_flag][key]
+          default if !opt
         catch e
           default || null
-
-      data_value: ->
-        @data_value || @data_value = $(this.get_opt('tpl'))

       rect: ->
         $inputor = @$inputor
@@ -272,23 +277,29 @@
           $.noop()
         e.stopPropagation()

-      render_view: (datas) ->
-        datas = datas.splice(0, this.get_opt('limit'))
-        datas = _unique(datas, this.data_value())
-        datas = _objectify(datas)
-        datas = @callbacks["sorter"].call(this, @query.text, datas)
-
-        @view.render datas
+      render_view: (data) ->
+        data = data.splice(0, this.get_opt('limit'))
+        data = @callbacks["data_refactor"].call(this, data)
+        search_key = this.get_opt("search_key")
+        data = @callbacks["sorter"].call(this, @query.text, data, search_key)
+
+        @view.render data

       look_up: ->
-        key = this.catch_query()
-        return no if not key
-        log "At.look_up.key", key
-
-        if (datas = @methods['filter'].call(this, key))
-            this.render_view datas
-        # else if $.isFunction(callback = @.get_opt 'callback')
-        #     callback key.text, $.proxy(this.render_view,@)
+        query = this.catch_query()
+        return no if not query
+        log "At.look_up.query", query
+
+        origin_data = this.get_opt("data")
+        search_query = this.get_opt("search_query")
+        if typeof data is "string"
+          params =
+            q: query.text
+            limit: this.get_opt("limit")
+          callback = $.proxy(this.render_view, this
+          @callbacks['remote_filter'].call(this, params, callback))
+        else if (data = @callbacks['filter'].call(this, query.text, origin_data, search_query)
+            this.render_view data
         else
             @view.hide()
         $.noop()
@@ -388,27 +399,7 @@
         $ul.find("li:eq(0)").addClass "cur"

-    _objectify = (list) ->
-        $.map list, (item,k) ->
-            if not $.isPlainObject item
-                item = {id:k, name:item}
-            return item
-
-    ###
-      maybe we can use $._unique.
-      But i don't know it will delete li element frequently or not.
-      I think we should not change DOM element frequently.
-      more, It seems batter not to call evalTpl function too much times.
-    ###
-    _unique = (list,query) ->
-        record = []
-        $.map list, (v, id) ->
-            value = if $.isPlainObject(v) then v[query] else v
-            if $.inArray(value,record) < 0
-                record.push value
-                return v
-
-    _DEFAULT_TPL = "
${name}
"
+    _DEFAULT_TPL = "
${name}
"

     log = () ->
         #console.log(arguments)
@@ -426,7 +417,9 @@
         # Parameter: choose
         ## specify the attribute on customer tpl,
         ## so that we could append different value to the input other than the value we searched in
-        choose: "data-value"
+        data: null
+        search_key: "name"
+        choose_key: "name"
         callbacks: {}
         cache: yes
         limit: 5


Remove jquery dependency

Remove jquery dependency

--- src/scripts/modules/media/title/title.coffee
+++ src/scripts/modules/media/title/title.coffee
@@ -1,5 +1,4 @@
 define (require) ->
-  $ = require('jquery')
   _ = require('underscore')
   Backbone = require('backbone')
   BaseView = require('cs!helpers/backbone/views/base')


Add links to pylint/pycodestyle error codes

--- lib/init.coffee
+++ lib/init.coffee
@@ -37,7 +37,8 @@
       type: 'string'
       default: 'D203,D212,D213,D404'
       description: 'Comma-separated list of errors and warnings.
-      Example: ED203,D212,D213,D404,111,E114,D101,D102,DW0311'
+      Example: ED203,D212,D213,D404,111,E114,D101,D102,DW0311.
+      See more: https://goo.gl/jeYN96, https://goo.gl/O8xhLM'
       order: 5
     skipFiles:
       type: 'string'


fix lint error

fix lint error

--- lib/deployers/gh-pages/index.coffee
+++ lib/deployers/gh-pages/index.coffee
@@ -74,8 +74,10 @@
   .then (res) -> res[0].sha
   .catch (err) =>
     msg = JSON.parse(err.message).message
-    if msg == 'Git Repository is empty.' then return create_initial_commit.call(@)
-    if msg == 'Not Found' then return create_branch.call(@)
+    if msg == 'Git Repository is empty.'
+      return create_initial_commit.call(@)
+    if msg == 'Not Found'
+      return create_branch.call(@)
     throw err

 ###*


Don't destroy editors when deactivating package

Previously, upon package deactivation, we were destroying all the
editors where spell-check was enabled. This commit changes it so that we
destroy only the view which, in turn, will clear all the created markers
and decorations.

--- lib/main.coffee
+++ lib/main.coffee
@@ -75,8 +75,8 @@

     # Clear out the known views.
     for editorId of spellCheckViews
-      view = spellCheckViews[editorId]
-      view['editor'].destroy()
+      {view} = spellCheckViews[editorId]
+      view.destroy()
     spellCheckViews = {}

     # While we have WeakMap.clear, it isn't a function available in ES6. So, we


:fire: Cleanup main

:fire: Cleanup main

--- lib/main.coffee
+++ lib/main.coffee
@@ -3,7 +3,7 @@
 module.exports =
   config:
     jshintExecutablePath:
-      default: path.join __dirname, '..', 'node_modules', 'jshint', 'bin'
+      default: ''
       title: 'JSHint Executable Path'
       type: 'string'

@@ -12,8 +12,6 @@
       title: 'Disable when no .jshintrc file found in path'
       type: 'boolean'

-  activate: ->
-    console.log 'activate linter-jshint'
   provideLinter: ->
     provider =
       grammarScopes: ['source.js']


Updates panel

Updates panel

--- lib/main.coffee
+++ lib/main.coffee
@@ -83,8 +83,14 @@
     for d in window.JapaneseMenu.defS.Settings.settings
       applyTextContentBySettingsId(d)

-    # Every panel
-    #TODO
+    # Updates panel
+    sv = document.querySelector('.settings-view')
+    h1 = sv.querySelector('h1.section-heading.icon-cloud-download')
+    h1.childNodes[2].textContent = "利用可能なアップデート"
+    applyTextWithOrg(h1.querySelector('.update-all-button.btn-primary'), "すべてアップデート")
+    applyTextWithOrg(h1.querySelector('.update-all-button:not(.btn-primary)'), "アップデートをチェック")
+    applyTextWithOrg(sv.querySelector('.alert.icon-hourglass'), "アップデートを確認中...")
+    applyTextWithOrg(sv.querySelector('.alert.icon-heart'), "インストール済みのパッケージはすべて最新です！")

   applyTextContentBySettingsId = (data) ->
     el = document.querySelector("[id='#{data.id}']")


Deconstruct helper functions

Deconstruct helper functions

--- lib/main.coffee
+++ lib/main.coffee
@@ -75,7 +75,7 @@
   provideLinter: ->
     {find} = require 'atom-linter'
     globule = require 'globule'
-    helpers = require './helpers'
+    {getRuleURI} = require './helpers'

     provider =
       name: 'sass-lint'
@@ -169,7 +169,7 @@
           line = if msg.line then msg.line - 1 else 0
           col = if msg.column then msg.column - 1 else 0
           text = if msg.message then ' ' + msg.message else 'Unknown Error'
-          ruleHref = helpers.getRuleURI(msg.ruleId)
+          ruleHref = getRuleURI(msg.ruleId)
           html = '' + msg.ruleId + '' + text

           result = {


yoyoyo

yoyoyo

--- lib/markem.iced
+++ lib/markem.iced
@@ -17,60 +17,71 @@
     console.log "cloning into markem.out"
     branch='gh-pages'
     await this._git "clone #{fetch} markem.out",null,defer err,out
-    await this._git "branch #{branch}",'markem.out',defer err,out
-    # console.log out
-    # if err?
-    #   console.log "branch #{branch} does not exists, creating..."
-    #   await this._git "branch #{branch}",'markem.out',defer err,out
-    #   await this._git "checkout #{branch}",'markem.out',defer err,out
-    # await this._git "status",'markem.out',defer err,out
-    # if branch!=out.match(/on\s*branch\s*([^\s]*)/i)[1]
-    #   console.log "branch #{branch} does not exists, creating..."
-    #   await this._git "branch #{branch}",'markem.out',defer err,out
-    #   console.log out
-      # await this._git "checkout #{branch}",'markem.out',defer err out
-      # console.log out
-    # await this._generate defer()
-
+    await this._git "branch",'markem.out',defer err,out
+    if !out.match branch
+      console.log "branch #{branch} does not exists, creating..."
+      await this._git "branch #{branch}",'markem.out',defer err
+    await this._git "checkout #{branch}",'markem.out',defer err
+    console.log "generating content..."
+    await this._generate defer()
+    await this._git "add --all",'markem.out',defer err
+    await this._git "commit -m 'compiled by markem'",'markem.out',defer err
+    console.log "pushing back into origin..."
+    await this._git "push origin #{branch}",'markem.out',defer err
+    await utils.rmdirDeep 'markem.out',defer err
+    console.log "done."
   _git:(command,workTree,cb)->
     if workTree?
       command="git --work-tree #{workTree} --git-dir #{path.join workTree,'.git'} #{command}"
     else
       command="git #{command}"
-    await childprocess.exec command,defer err,out
+    await childprocess.exec command,defer err,stdout,stderr
     if this.options.verbose
       console.log "> #{command}"
+      if stderr?&&stderr.trim().length
+        console.error stderr
+      if stdout?&&stdout.trim().length
+        console.log stdout
       if err?
-        console.log err
-      if out?
-        console.log out
-    cb(err,out)
+        console.error err
+    cb(err,stdout,stderr)
   _generate: (cb)->
     await fs.readFile path.join('markem.conf','layout.jade'),'utf8',defer err,layout
     layout=jade.compile layout,
       filename:path.join('markem.conf','layout.jade')
     utils.scandir
-      path: '.'
-      readFiles: false
+      path: 'markem.out',
+      readFiles:false
       ignoreHiddenFiles: true
-      next: (err,list) ->
+      recurse:false
+      next:(err,list)->
         for file,type of list
-          if type=='file'&&!file.match /node_modules/
-            target=null
-            if file.match /\.markdown$/
-              target=path.join 'markem.out',file.replace /\.markdown$/,'.html'
-            if file.match /\.md$/
-              target=path.join 'markem.out',file.replace /\.md$/,'.html'
-            if file in ['READMD','README.md','README.markdown','Readme.md']
-              target=path.join 'markem.out',file.replace /[^#{path.sep}]*$/,'index.html'
-            if target?
-              await fs.stat file,defer err,fileStat
-              await fs.stat target,defer err,targetStat
-              if err? || Number(fileStat.mtime)-Number(targetStat.mtime)<300
-                console.log "rendering #{target}"
-                await mkdirp path.dirname(target),defer err
-                await fs.readFile file,'utf8',defer err,file
-                file=layout
-                  content: marked file
-                await fs.writeFile target,file,'utf8',defer err
-        cb()+          if type=='dir'
+            await utils.rmdirDeep file,defer err
+          else
+            await utils.unlink file,defer err
+        utils.scandir
+          path: '.'
+          readFiles: false
+          ignoreHiddenFiles: true
+          next: (err,list) ->
+            for file,type of list
+              if type=='file'&&!file.match /node_modules/
+                target=null
+                if file.match /\.markdown$/
+                  target=path.join 'markem.out',file.replace /\.markdown$/,'.html'
+                if file.match /\.md$/
+                  target=path.join 'markem.out',file.replace /\.md$/,'.html'
+                if file in ['READMD','README.md','README.markdown','Readme.md']
+                  target=path.join 'markem.out',file.replace /[^#{path.sep}]*$/,'index.html'
+                if target?
+                  await fs.stat file,defer err,fileStat
+                  await fs.stat target,defer err,targetStat
+                  if err? || Number(fileStat.mtime)-Number(targetStat.mtime)<300
+                    console.log "rendering #{target}"
+                    await mkdirp path.dirname(target),defer err
+                    await fs.readFile file,'utf8',defer err,file
+                    file=layout
+                      content: marked file
+                    await fs.writeFile target,file,'utf8',defer err
+            cb()


fixes to pointer lock

fixes to pointer lock

--- app/assets/javascripts/libs/mouse.coffee
+++ app/assets/javascripts/libs/mouse.coffee
@@ -65,22 +65,22 @@
 					y : evt.pageY
 			else
 				if @buttonDown
-					distX = evt.webkitMovementX
+					distX = -evt.webkitMovementX
 					distY = evt.webkitMovementY
 					changedCallback.x distX/SLOWDOWN_FACTOR if distX isnt 0
 					changedCallback.y distY/SLOWDOWN_FACTOR if distY isnt 0

 		mouseDown : =>
-			$(@target).css("cursor", "none")
+			#$(@target).css("cursor", "none")
 			@buttonDown = true

 		mouseUp : =>
 			@buttonDown = false
-			$(@target).css("cursor", "auto")
+			#$(@target).css("cursor", "auto")

 		lockMouse : =>
 			@locked = true
-			navigator.webkitpointer.lock @target, -> console.log "Success Mosue Lock", -> console.log "Error: Mouse Lock"
+			navigator.webkitPointer.lock @target, -> console.log "Success Mosue Lock", -> console.log "Error: Mouse Lock"

 		unlockMouse : =>
 			@locked = false


OX-72 fixed minor issues according to review, renamed callbacks to support readability #time 15m

--- app/assets/javascripts/libs/mouse.coffee
+++ app/assets/javascripts/libs/mouse.coffee
@@ -24,8 +24,8 @@
   # stores the callbacks for mouse movement in each dimension
   changedCallbackX : $.noop()
   changedCallbackY : $.noop()
-  changedCallbackR : $.noop()
-  changedCallbackMW : $.noop()
+  changedCallbackRightclick : $.noop()
+  changedCallbackMouseWheel : $.noop()
   activeCallback : $.noop()

@@ -34,7 +34,6 @@
   # HTML object where the mouse attaches the events
   ###
   constructor : (target, activeCallback) ->
-    # @User = User
     @target = target

     @activeCallback = activeCallback
@@ -45,7 +44,6 @@
       "mouseup"   : @mouseUp

     $(@target).on
-      "mouseup"   : @mouseUp
       "mousedown" : @mouseDown
       "mouseenter" : @mouseEnter
       "mousewheel" : @mouseWheel
@@ -80,10 +78,16 @@
   # gets the relative mouse position as parameter
   ###
   bindR : (callback) ->
-    @changedCallbackR = callback
+    @changedCallbackRightclick = callback
+
+  ###
+  #Binds a function as callback when mousewheel was changed
+  #@param {Function} callback :
+  # gets the delta as parameter
+  ###

   bindW : (callback) ->
-    @changedCallbackMW = callback
+    @changedCallbackMouseWheel = callback

   unbind : ->
     $(@target).off
@@ -124,19 +128,20 @@
       @changedCallbackY distY * User.Configuration.mouseRotateValue if distY isnt 0

   mouseEnter : (evt) =>
-    if !(evt.which == 1) and @activeCallback?
+    # don't invoke activeCallback, when leftclicking while entering
+    if evt.which != 1 and @activeCallback?
       @activeCallback()

   mouseWheel : (evt, delta) =>
-    if @changedCallbackMW?
-      @changedCallbackMW(delta)
+    if @changedCallbackMouseWheel?
+      @changedCallbackMouseWheel(delta)
       return false      # prevent scrolling the web page

   mouseDown : (evt) =>
     # check whether the mouseDown event is a rightclick
     if evt.which == 3
       # on rightclick, return mouse position relative to the canvas
-      @changedCallbackR [evt.pageX - $(@target).offset().left, evt.pageY - $(@target).offset().top], 0
+      @changedCallbackRightclick [evt.pageX - $(@target).offset().left, evt.pageY - $(@target).offset().top], 0
     else
       if @activeCallback?
         @activeCallback()


Fix mask height on basic overlay

Fix mask height on basic overlay

--- app/assets/javascripts/overlay.js.coffee
+++ app/assets/javascripts/overlay.js.coffee
@@ -48,8 +48,8 @@
     @dom.el.css('left', @dimensions.margin())
     @dom.el.css('top', @dimensions.top())
     @dom.el.width(@dimensions.width())
-    @dom.mask.height('100%')
     @dom.closeButton.css('left', @dimensions.closeButtonLeftPosition())
+    @setMaskHeight()

   show: ->
     @overlayContent.show()


Hide questions when done

Hide questions when done

--- app/assets/javascripts/components/feed_builder.coffee
+++ app/assets/javascripts/components/feed_builder.coffee
@@ -8,13 +8,14 @@
     { questions, loading, done } = @response
     elements = []

-    for question in questions
-      elements.push React.createElement Question,
-        question: question
-        key: elements.length
-        editable: !done
-        onChange: @onAnswerChange
-        onAnswerChange: @onAnswerChange
+    unless done
+      for question in questions
+        elements.push React.createElement Question,
+          question: question
+          key: elements.length
+          editable: !done
+          onChange: @onAnswerChange
+          onAnswerChange: @onAnswerChange

     if loading
       elements.push React.createElement LoadingIndicator,


the one true tab size

the one true tab size

--- app/assets/javascripts/scripts.js.coffee
+++ app/assets/javascripts/scripts.js.coffee
@@ -33,6 +33,7 @@
   editor.setTheme("ace/theme/clouds");
   editor.getSession().setMode("ace/mode/javascript");
   editor.getSession().setUseSoftTabs(true);
+  editor.getSession().setTabSize(2);
   editor.getSession().setValue(body);
   $('form').submit((evt) ->
     $('textarea').val(editor.getSession().getValue())


fix loading indicator

fix loading indicator

--- app/assets/javascripts/components/subject-viewer.cjsx
+++ app/assets/javascripts/components/subject-viewer.cjsx
@@ -14,16 +14,6 @@

 WORKFLOW_ID = '54b82b4745626f20c9020000' # marking workflow
 endpoint = "/workflows/#{WORKFLOW_ID}/subjects.json?limit=5"
-
-
-# ImageSubjectViewer_mark = React.createClass # rename to Classifier
-#   displayName: 'ImageSubjectViewer_mark'
-
-#   render: ->
-#     endpoint = "/workflows/#{WORKFLOW_ID}/subjects.json?limit=5"
-#

-#       <SubjectViewer endpoint=endpoint task={@props.task} />
-#

 SubjectViewer = React.createClass
   displayName: 'SubjectViewer'
@@ -46,14 +36,6 @@
     classification: null
     selectedMark: null # TODO: currently not in use
     showTranscribeTool: false
-
-  getFakeSubject: (group) ->
-    if group is "transcribe"
-      transcriptionSubject = "THIS IS A TRANSCRIPTION SUBJECT"
-      return transcriptionSubject
-    if group is "mark"
-      markingSubject = "THIS IS A MARKING SUBJECT"
-      return markingSubject

   componentDidMount: ->
     @setView 0, 0, @state.imageWidth, @state.imageHeight
@@ -110,72 +92,6 @@
             # console.log @state.loading
             # console.log "Finished Loading."

-  prepareClassification: ->
-    console.log 'prepareClassification()'
-    for mark in [ @state.marks... ]
-      @state.classification.annotate
-        timestamp: mark.timestamp
-        key: mark.key
-        y_upper: mark.yUpper
-        y_lower: mark.yLower
-        x: mark.x
-        y: mark.y
-
-    # DEBUG CODE
-    # console.log @state.classification.annotations
-
-  submitMark: (key) ->
-    # prepare classification
-    mark = @state.marks[key]
-
-    classification = new Classification @state.subject
-    classification.annotate mark
-
-    @disableMarkButton(key)
-
-    # TODO: replace with this
-    # @state.classification.send()
-
-    # send classification
-    $.post('/classifications', {
-        workflow_id: WORKFLOW_ID
-        subject_id:  @state.subject.id
-        location:    @state.subject.location
-        annotations: classification.annotations
-        started_at:  classification.started_at
-        finished_at: classification.finished_at
-        subject:     classification.subject
-        user_agent:  classification.user_agent
-      }, )
-      .done (response) =>
-        console.log "Success" #, response._id.$oid
-        @setTranscribeSubject(key, response._id.$oid)
-        @enableMarkButton(key)
-        return
-      .fail =>
-        console.log "Failure"
-        return
-      # .always ->
-      #   console.log "Always"
-      #   return
-
-  setTranscribeSubject: (key, transcribe_id) ->
-    marks = @state.marks
-    marks[key].transcribe_id = transcribe_id
-    @setState marks: marks
-
-  disableMarkButton: (key) ->
-    marks = @state.marks
-    marks[key].buttonDisabled = true
-    @setState marks: marks
-    @forceUpdate()
-
-  enableMarkButton: (key) ->
-    marks = @state.marks
-    marks[key].buttonDisabled = false
-    @setState marks: marks
-    @forceUpdate()
-
   nextSubject: () ->

     @prepareClassification()
@@ -200,140 +116,16 @@
     @state.classification = new Classification @state.subject

   handleInitStart: (e) ->
-    # console.log 'handleInitStart()'
-
-    {horizontal, vertical} = @getScale()
-    rect = @refs.sizeRect?.getDOMNode().getBoundingClientRect()
-    timestamp = (new Date).toUTCString()
-    key = @state.marks.length
-    {x, y} = @getEventOffset e
-    yUpper = Math.round( y - 50/2 )
-    yLower = Math.round( y + 50/2 )
-    buttonDisabled = false
-
-    marks = @state.marks
-    marks.push {yUpper, yLower, x, y, key, timestamp, buttonDisabled}
-
-    @setState
-      marks:        marks
-      offset:       $(e.nativeEvent.target).offset()
-      selectedMark: @state.marks[@state.marks.length-1]
-
+    console.log 'handleInitStart()'
+
   handleInitDrag: (e) ->
-    # console.log 'handleInitDrag()'
-
-    {x,y} = @getEventOffset e
-
-    dist = Math.abs( @state.selectedMark.y - y )
-
-    if dist > 50/2
-      currentMark = @state.selectedMark
-      currentMark.yUpper = currentMark.y - dist
-      currentMark.yLower = currentMark.y + dist
-      currentMark.markHeight = currentMark.yLower - currentMark.yUpper
-
-      @setState
-        selectedMark: currentMark
+    console.log 'handleInitDrag()'

   handleInitRelease: (e) ->
-    # console.log 'handleInitRelease()'
+    console.log 'handleInitRelease()'

   handleToolMouseDown: ->
-    # console.log 'handleToolMouseDown()'
-
-  handleMarkClick: (mark, e) ->
-    {x,y} = @getEventOffset e
-
-    @setState
-      selectedMark: mark
-      markOffset: {
-        x: mark.x - x,
-        y: mark.y - y
-      }, =>
-        @forceUpdate()
-
-  handleDragMark: (e) ->
-    # console.log 'handleDragMark()'
-    # return unless @state.workflow is "mark"
-
-    {x,y} = @getEventOffset e
-
-    currentMark = @state.selectedMark
-    currentMark.x = Math.round x + @state.markOffset.x
-    currentMark.y = Math.round y + @state.markOffset.y
-    markHeight = currentMark.yLower - currentMark.yUpper
-    currentMark.yUpper = Math.round currentMark.y - markHeight/2
-    currentMark.yLower = Math.round currentMark.y + markHeight/2
-
-    # prevent dragging mark beyond image bounds
-    offset = @state.markOffset.y
-    return if ( y + offset - markHeight/2 ) < 0
-    return if ( y + offset + markHeight/2 ) > @state.imageHeight
-
-    @setState
-      selectedMark: currentMark
-
-  handleUpperResize: (e) ->
-    # console.log 'handleUpperResize()'
-    {x,y} = @getEventOffset e
-
-    x = Math.round x
-    y = Math.round y
-
-    currentMark = @state.selectedMark
-
-    # enforce bounds
-    if y < 0
-      y = 0
-      return
-
-    if currentMark.yLower - y < 50
-      currentMark.yUpper = Math.round( -50 + currentMark.yLower )
-      @setState selectedMark: currentMark
-      return
-
-    dy = currentMark.yUpper - y
-    yUpper_p = y
-    markHeight_p = currentMark.yLower - currentMark.yUpper + dy
-    y_p = yUpper_p + markHeight_p/2
-
-    currentMark.yUpper = yUpper_p
-    currentMark.markHeight = markHeight_p
-    currentMark.y = y_p
-
-    @setState
-      selectedMark: currentMark
-
-  handleLowerResize: (e) ->
-    # console.log 'handleLowerResize()'
-    {x,y} = @getEventOffset e
-
-    x = Math.round x
-    y = Math.round y
-
-    currentMark = @state.selectedMark
-
-    # enforce bounds
-    if y > @state.imageHeight
-      y = @state.imageHeight
-      return
-
-    if y - currentMark.yUpper < 50
-      currentMark.yLower = Math.round( 50 + currentMark.yUpper )
-      @setState selectedMark: currentMark
-      return
-
-    dy = y - currentMark.yLower
-    yLower_p = y
-    markHeight_p = currentMark.yLower - currentMark.yUpper + dy
-    y_p = yLower_p - markHeight_p/2
-
-    currentMark.yLower = yLower_p
-    currentMark.markHeight = markHeight_p
-    currentMark.y = y_p
-
-    @setState
-      selectedMark: currentMark
+    console.log 'handleToolMouseDown()'

   setView: (viewX, viewY, viewWidth, viewHeight) ->
     @setState {viewX, viewY, viewWidth, viewHeight}
@@ -348,100 +140,40 @@
   getEventOffset: (e) ->
     rect = @refs.sizeRect.getDOMNode().getBoundingClientRect()

-    # console.log 'RECT: ', rect
     {horizontal, vertical} = @getScale()
     x: ((e.pageX - pageXOffset - rect.left) / horizontal) + @state.viewX
     y: ((e.pageY - pageYOffset - rect.top) / vertical) + @state.viewY

-    # x: ((e.pageX - pageXOffset - rect.left)) + @state.viewX
-    # y: ((e.pageY - pageYOffset - rect.top)) + @state.viewY
-
-  onClickDelete: (key) ->
-    marks = @state.marks
-    marks.splice(key,1) # delete marks[key]
-    @setState
-      marks: marks
-      selectedMark: null, =>
-        @forceUpdate() # make sure keys are up-to-date before re-render
-
-  beginTextEntry: ->
-    # console.log 'beginTextEntry()'
-    @nextSubject()
-    return # just load next subject for now
-    return unless @state.marks.length > 0
-    @setState
-      selectedMark: @state.marks[0], =>
-        {horizontal, vertical} = @getScale()
-        $('html, body').animate scrollTop: vertical*@state.selectedMark.y-window.innerHeight/2+80, 500
-
-  nextTextEntry: ->
-    key = @state.selectedMark.key
-    if key+1 > @state.marks.length-1
-      # console.log "That's all the marks for now!"
-      return
-
-    @setState selectedMark: @state.marks[key+1], =>
-      {horizontal, vertical} = @getScale()
-      $('html, body').animate scrollTop: vertical*@state.selectedMark.y-window.innerHeight/2+80, 500
-
-  onClickTranscribe: (key) ->
-    console.log 'onClickTranscribe() '
-
-    console.log location.host + "/?subject_id=#{@state.marks[key].transcribe_id}#/transcribe"
-    location.replace 'http://' + location.host + "/?subject_id=#{@state.marks[key].transcribe_id}&scrollOffset=#{$(window).scrollTop()}#/transcribe"
-    # @setState showTranscribeTool: true
-
-  # dummy placeholder
-  recordTranscription: ->
-    console.log 'recordTranscription()'
-
-  # "https://zooniverse-static.s3.amazonaws.com/scribe_subjects/logbookofalfredg1851unse_0083.jpg"
-
   render: ->
-    # don't render if ya ain't got subjects (yet)
-    # console.log 'showTranscribeTool is ', @state.showTranscribeTool
     return null if @state.subjects is null or @state.subjects.length is 0
-
     viewBox = [0, 0, @state.imageWidth, @state.imageHeight]
-
-    # LOADING
     if @state.loading
-

-

-          <LoadingIndicator/>
-

-
{@state.subjects.location}

-

-          <ActionButton loading={@state.loading} />
-

-

-
-    else
-
-      if @state.marks.length is 0
-        action_button =  <ActionButton label={"NEXT PAGE"} onActionSubmit={@nextSubject} />
-      else if @state.workflow is "finished"
-        action_button =  <ActionButton label={"FINISH"} onActionSubmit={@nextSubject} />
-
-      else if @state.marks.length > 0
-        action_button =  <ActionButton label={"FINISHED MARKING"} onActionSubmit={@beginTextEntry} />
-

-
+            <LoadingIndicator/>
+

+
{@state.subjects.location}

+

+            <ActionButton loading={@state.loading} />
+

+

+

+    else
+      action_button =  <ActionButton label={"NEXT PAGE"} onActionSubmit={@nextSubject} />
+

+

+

             <svg
               className = "subject-viewer-svg"
               width = {@state.imageWidth}
               height = {@state.imageHeight}
               viewBox = {viewBox}
               data-tool = {@props.selectedDrawingTool?.type} >
-
               <rect
                 ref = "sizeRect"
                 width = {@state.imageWidth}
                 height = {@state.imageHeight} />
-
               <Draggable
                 onStart = {@handleInitStart}
                 onDrag  = {@handleInitDrag}
@@ -451,42 +183,7 @@
                   width = {@state.imageWidth}
                   height = {@state.imageHeight} />
               </Draggable>
-
-              { @state.marks.map ((mark, i) ->
-                  <TextRowTool
-                    key = {i}
-                    mark = {mark}
-                    imageWidth = {@state.imageWidth}
-                    imageHeight = {@state.imageHeight}
-                    getEventOffset = {@getEventOffset}
-                    selected = {mark is @state.selectedMark}
-                    onClickDelete = {@onClickDelete}
-                    onClickTranscribe = {@onClickTranscribe}
-                    submitMark = {@submitMark}
-                    scrubberWidth = {64}
-                    scrubberHeight = {32}
-                    handleDragMark = {@handleDragMark}
-                    handleUpperResize = {@handleUpperResize}
-                    handleLowerResize = {@handleLowerResize}
-                    handleMarkClick = {@handleMarkClick.bind null, mark}
-                  />
-                ), @
-              }
-
             </svg>
-
-            { if @state.showTranscribeTool
-              <TranscribeTool
-                transcribeSteps={transcribeSteps}
-                recordTranscription={@recordTranscription}
-                nextTextEntry={@nextTextEntry}
-                nextSubject = {@nextSubject}
-                selectedMark={@state.selectedMark}
-                xScale={@getScale().horizontal}
-                yScale={@getScale().vertical}
-              />
-            }
-

{@state.subject.location}


Fix spacing of block type text in non-edit state

--- app/assets/javascripts/components/timeline/block.cjsx
+++ app/assets/javascripts/components/timeline/block.cjsx
@@ -61,7 +61,7 @@

-

+

         <Select
           onChange={@updateBlock}
           value={@props.block.kind}
@@ -114,7 +114,7 @@
           onBlur={@props.toggleFocused}
         />
         {deleteBlock}
-

+

       {graded}
       {dueDateRead || (if is_graded then (
{I18n.t('timeline.due_default')}
) else '')}
       <TextAreaInput


participant: Take callback in send(), for error handling

--- src/participant.coffee
+++ src/participant.coffee
@@ -109,11 +109,11 @@
   # Send data on inport
   # Normally only used directly for Source type participants
   # For Transform or Sink type, is called on data from input queue
-  send: (inport, data) ->
+  send: (inport, data, callback = -> ) ->
     debug 'got msg from send()', inport
     @func inport, data, (outport, err, data) =>
-      if not err
-        @onResult outport, data, () ->
+      return callback err if err
+      @onResult outport, data, callback

   # Emit data on outport
   emitData: (outport, data) ->


Use path.sep for cross-platform compatibility

--- src/path-filter.coffee
+++ src/path-filter.coffee
@@ -90,7 +90,11 @@
             deepPath = path.join(deepPath, paths[i])
             addDirectoryMatcher(matchers, deepPath)

-      matchIndex = pattern.search(/\/\*\*$|\/\*$/)
+      directoryPattern = ///
+        #{'\\'+path.sep}\*$|   # Matcher ends with a separator followed by *
+        #{'\\'+path.sep}\*\*$  # Matcher ends with a separator followed by **
+      ///
+      matchIndex = pattern.search(directoryPattern)
       addDirectoryMatcher(matchers, pattern.slice(0, matchIndex)) if matchIndex > -1

       matchers.directory.push(new Minimatch(pattern, PathFilter.MINIMATCH_OPTIONS))
@@ -105,7 +109,11 @@
       pattern = patterns[r].trim()
       continue if (pattern.length == 0 || pattern[0] == '#')

-      if (/\/$|\/\*$|\/\**$/.test(pattern))
+      endsWithSeparatorOrStar = ///
+        #{'\\'+path.sep}$|   # Pattern ends in a separator
+        #{'\\'+path.sep}\**$ # Pattern ends with a seperator followed by a *
+      ///
+      if (endsWithSeparatorOrStar.test(pattern))
         # Is a dir if it ends in a '/' or '/*'
         addDirectoryMatcher(matchers, pattern, deepMatch)
       else if (pattern.indexOf('.') < 1 && pattern.indexOf('*') < 0)


Fixed Bug with async Meeting and User Data Loading

--- app/assets/javascripts/meeting/MeetingNoteCtrl.coffee
+++ app/assets/javascripts/meeting/MeetingNoteCtrl.coffee
@@ -19,11 +19,11 @@
         (data) =>
           @$log.debug "Promise returned #{data.length} Meetings"
           @meeting = data
+          @getActualUser()
       ,
       (error) =>
         @$log.error "Unable to get Meetings: #{error}"
       )
-    @getActualUser()
     @initAvailableUsers()
     @initHotKeys()
     @setBackgroundColor()
@@ -99,6 +99,7 @@
       status: "new",
       color: "color-new"
     }
+    @getActualUser()

   publishMeeting: () ->
     @$log.debug "MeetingNoteCtrl.publishMeeting()"


Updated order model to store token

Updated order model to store token

--- app/assets/javascripts/sprangular/models/order.coffee
+++ app/assets/javascripts/sprangular/models/order.coffee
@@ -22,6 +22,7 @@
     @shipmentState = null
     @shippingRates = []
     @shippingRate = null
+    @token = null

   load: (data) ->
     @clear()
@@ -33,6 +34,7 @@
     @shipTotal = Number(data.ship_total)
     @adjustmentTotal = Number(data.adjustment_total)
     @total = Number(data.total)
+    @token = data.token
     @shipToBillAddress = data.use_billing
     @adjustments = Sprangular.extend(data.adjustments, Sprangular.Adjustment)
     @shippingRates = []


Removed footer translation.

Removed footer translation.

--- app/assets/javascripts/uploadcare/locale/sk.js.coffee
+++ app/assets/javascripts/uploadcare/locale/sk.js.coffee
@@ -131,11 +131,8 @@
             clear: 'Odstrániť všetky'
             done: 'Pridať'
             file:
-              preview: 'Nahliadnuť %file%'
+              preview: 'Nahliadnuť na %file%'
               remove: 'Odstrániť %file%'
-      footer:
-        text: 'sprevádzkuje'
-        link: 'uploadcare'

 # Pluralization rules taken from:


Minor fixes: clean code (II)

Minor fixes: clean code (II)

--- app/coffee/modules/user-settings/notifications.coffee
+++ app/coffee/modules/user-settings/notifications.coffee
@@ -16,11 +16,10 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 #
-# File: modules/user-settings/main.coffee
+# File: modules/user-settings/notifications.coffee
 ###

 taiga = @.taiga
-
 mixOf = @.taiga.mixOf
 bindOnce = @.taiga.bindOnce

@@ -74,19 +73,20 @@

 module.controller("UserNotificationsController", UserNotificationsController)

+
 #############################################################################
 ## User Notifications Directive
 #############################################################################

 UserNotificationsDirective = () ->
     link = ($scope, $el, $attrs) ->
-
         $scope.$on "$destroy", ->
             $el.off()

     return {link:link}

 module.directive("tgUserNotifications", UserNotificationsDirective)
+

 #############################################################################
 ## User Notifications List Directive
@@ -124,11 +124,12 @@
         </div>
         <% }) %>
     """)
+
     link = ($scope, $el, $attrs) ->
-
         render = ->
             $el.off()
             $el.html(template({notifyPolicies: $scope.notifyPolicies}))
+
             $el.on "change", "input[type=radio]", (event) ->
                 target = angular.element(event.currentTarget)
                 policyIndex = target.parents(".policy-table-row").data('index')
@@ -145,7 +146,6 @@
         $scope.$on "$destroy", ->
             $el.off()

-
         bindOnce($scope, $attrs.ngModel, render)

     return {link:link}


Added "filename" option to jade.compile as it is required to use "include"

--- lib/frontend/asset/templates.coffee
+++ lib/frontend/asset/templates.coffee
@@ -27,7 +27,7 @@

   jade: (path, source) ->
     try
-      jade.compile(source)()
+      jade.compile(source, {filename: "#{SS.root}/#{path}"})()
     catch e
       console.error 'Unable to render jade template: ' + path
       throw new Error(e)


adjusting code because of atom api changes - fixing package name in congif.get

--- lib/function-name-status-bar.coffee
+++ lib/function-name-status-bar.coffee
@@ -9,8 +9,8 @@
       me = this
       atom.workspaceView.eachEditorView (editor) ->
           editor.on 'keydown', (evt) =>
-              console.log atom.config, atom.config.get('function-name-status-bar.computeOnKeys')
-              if evt.which in atom.config.get 'function-name-status-bar.computeOnKeys'
+              console.log atom.config, atom.config.get('function-name-in-status-bar.computeOnKeys')
+              if evt.which in atom.config.get 'function-name-in-status-bar.computeOnKeys'
                   me.showFunctionNameInStatus(evt)

           editor.on 'click', (evt) =>


More robust loading buttons service

More robust loading buttons service

--- app/coffee/modules/common/loading.coffee
+++ app/coffee/modules/common/loading.coffee
@@ -23,14 +23,16 @@

 class TgLoadingService extends taiga.Service
     start: (target) ->
-        target.data('loading-old-content', target.html())
-        target.addClass('loading')
-        target.html("")
+        if not target.hasClass('loading')
+            target.data('loading-old-content', target.html())
+            target.addClass('loading')
+            target.html("")

     finish: (target) ->
-        oldContent = target.data('loading-old-content')
-        target.data('loading-old-content', null)
-        target.html(oldContent)
-        target.removeClass('loading')
+        if target.hasClass('loading')
+            oldContent = target.data('loading-old-content')
+            target.data('loading-old-content', null)
+            target.html(oldContent)
+            target.removeClass('loading')

 module.service("$tgLoading", TgLoadingService)


Move marker to private

Move marker to private

--- src/marker.coffee
+++ src/marker.coffee
@@ -10,7 +10,7 @@

 OptionKeys = ['reversed', 'tailed', 'invalidate', 'persistent']

-# Essential: Represents a buffer annotation that remains logically stationary
+# Private: Represents a buffer annotation that remains logically stationary
 # even as the buffer changes. This is used to represent cursors, folds, snippet
 # targets, misspelled words, and anything else that needs to track a logical
 # location in the buffer over time.


Add ctrl-r keybinding.

Add ctrl-r keybinding.

--- src/master.coffee
+++ src/master.coffee
@@ -180,6 +180,8 @@
             @shutdown()
           when '\u0004'  # ctrl-d
             @debug()
+          when '\u0012'  # ctrl-r
+            @reload()

     if @logger
       @on 'worker:exception', (worker, err) =>


Update textbuffer

Update textbuffer

--- src/app/text-buffer.coffee
+++ src/app/text-buffer.coffee
@@ -8,7 +8,7 @@
 BufferChangeOperation = require 'buffer-change-operation'
 BufferMarker = require 'buffer-marker'

-# Public: Represents the contents of a file.
+# Represents the contents of a file.
 #
 # The `Buffer` is often associated with a {File}. However, this is not always
 # the case, as a `Buffer` could be an unsaved chunk of text.
@@ -29,7 +29,7 @@
   invalidMarkers: null
   refcount: 0

-  # Public: Creates a new buffer.
+  # Creates a new buffer.
   #
   # path - A {String} representing the file path
   # initialText - A {String} setting the starting text
@@ -95,17 +95,17 @@

     @file.on "moved", =>
       @trigger "path-changed", this
-
+
   ### Public ###

-  # Public: Identifies if the buffer belongs to multiple editors.
+  # Identifies if the buffer belongs to multiple editors.
   #
   # For example, if the {Editor} was split.
   #
-  # Returns a {Boolean}.
+  # Returns a {Boolean}.
   hasMultipleEditors: -> @refcount > 1

-  # Public: Reloads a file in the {EditSession}.
+  # Reloads a file in the {EditSession}.
   #
   # Essentially, this performs a force read of the file.
   reload: ->
@@ -115,25 +115,25 @@
     @triggerModifiedStatusChanged(false)
     @trigger 'reloaded'

-  # Public: Rereads the contents of the file, and stores them in the cache.
+  # Rereads the contents of the file, and stores them in the cache.
   #
   # Essentially, this performs a force read of the file on disk.
   updateCachedDiskContents: ->
     @cachedDiskContents = @file.read()

-  # Public: Gets the file's basename--that is, the file without any directory information.
+  # Gets the file's basename--that is, the file without any directory information.
   #
   # Returns a {String}.
   getBaseName: ->
     @file?.getBaseName()

-  # Public: Retrieves the path for the file.
+  # Retrieves the path for the file.
   #
   # Returns a {String}.
   getPath: ->
     @file?.getPath()

-  # Public: Sets the path for the file.
+  # Sets the path for the file.
   #
   # path - A {String} representing the new file path
   setPath: (path) ->
@@ -146,7 +146,7 @@

     @trigger "path-changed", this

-  # Public: Retrieves the current buffer's file extension.
+  # Retrieves the current buffer's file extension.
   #
   # Returns a {String}.
   getExtension: ->
@@ -155,25 +155,25 @@
     else
       null

-  # Public: Retrieves the cached buffer contents.
+  # Retrieves the cached buffer contents.
   #
   # Returns a {String}.
   getText: ->
     @cachedMemoryContents ?= @getTextInRange(@getRange())

-  # Public: Replaces the current buffer contents.
+  # Replaces the current buffer contents.
   #
   # text - A {String} containing the new buffer contents.
   setText: (text) ->
     @change(@getRange(), text, normalizeLineEndings: false)

-  # Public: Gets the range of the buffer contents.
+  # Gets the range of the buffer contents.
   #
   # Returns a new {Range}, from `[0, 0]` to the end of the buffer.
   getRange: ->
     new Range([0, 0], [@getLastRow(), @getLastLine().length])

-  # Public: Given a range, returns the lines of text within it.
+  # Given a range, returns the lines of text within it.
   #
   # range - A {Range} object specifying your points of interest
   #
@@ -193,13 +193,13 @@

     return multipleLines.join ''

-  # Public: Gets all the lines in a file.
+  # Gets all the lines in a file.
   #
   # Returns an {Array} of {String}s.
   getLines: ->
     @lines

-  # Public: Given a row, returns the line of text.
+  # Given a row, returns the line of text.
   #
   # row - A {Number} indicating the row.
   #
@@ -213,7 +213,7 @@
   suggestedLineEndingForRow: (row) ->
     @lineEndingForRow(row) ? @lineEndingForRow(row - 1)

-  # Public: Given a row, returns the length of the line of text.
+  # Given a row, returns the length of the line of text.
   #
   # row - A {Number} indicating the row.
   #
@@ -224,10 +224,10 @@
   lineEndingLengthForRow: (row) ->
     (@lineEndingForRow(row) ? '').length

-  # Public: Given a buffer row, this retrieves the range for that line.
+  # Given a buffer row, this retrieves the range for that line.
   #
   # row - A {Number} identifying the row
-  # options - A hash with one key, `includeNewline`, which specifies whether you
+  # options - A hash with one key, `includeNewline`, which specifies whether you
   #           want to include the trailing newline
   #
   # Returns a {Range}.
@@ -237,25 +237,25 @@
     else
       new Range([row, 0], [row, @lineLengthForRow(row)])

-  # Public: Gets the number of lines in a file.
+  # Gets the number of lines in a file.
   #
   # Returns a {Number}.
   getLineCount: ->
     @getLines().length

-  # Public: Gets the row number of the last line.
+  # Gets the row number of the last line.
   #
   # Returns a {Number}.
   getLastRow: ->
     @getLines().length - 1

-  # Public: Finds the last line in the current buffer.
+  # Finds the last line in the current buffer.
   #
   # Returns a {String}.
   getLastLine: ->
     @lineForRow(@getLastRow())

-  # Public: Finds the last point in the current buffer.
+  # Finds the last point in the current buffer.
   #
   # Returns a {Point} representing the last position.
   getEofPosition: ->
@@ -278,13 +278,13 @@

     new Point(row, index)

-  # Public: Given a row, this deletes it from the buffer.
+  # Given a row, this deletes it from the buffer.
   #
   # row - A {Number} representing the row to delete
   deleteRow: (row) ->
     @deleteRows(row, row)

-  # Public: Deletes a range of rows from the buffer.
+  # Deletes a range of rows from the buffer.
   #
   # start - A {Number} representing the starting row
   # end - A {Number} representing the ending row
@@ -303,36 +303,29 @@

     @delete(new Range(startPoint, endPoint))

-  # Public: Adds text to the end of the buffer.
+  # Adds text to the end of the buffer.
   #
   # text - A {String} of text to add
   append: (text) ->
     @insert(@getEofPosition(), text)

-  # Public: Adds text to a specific point in the buffer
+  # Adds text to a specific point in the buffer
   #
   # point - A {Point} in the buffer to insert into
   # text - A {String} of text to add
   insert: (point, text) ->
     @change(new Range(point, point), text)

-  # Public: Deletes text from the buffer
+  # Deletes text from the buffer
   #
   # range - A {Range} whose text to delete
   delete: (range) ->
     @change(range, '')

-  # Internal:
-  change: (oldRange, newText, options) ->
-    oldRange = Range.fromObject(oldRange)
-    operation = new BufferChangeOperation({buffer: this, oldRange, newText, options})
-    range = @pushOperation(operation)
-    range
-
-  # Public: Given a position, this clips it to a real position.
+  # Given a position, this clips it to a real position.
   #
   # For example, if `position`'s row exceeds the row count of the buffer,
-  # or if its column goes beyond a line's length, this "sanitizes" the value
+  # or if its column goes beyond a line's length, this "sanitizes" the value
   # to a real position.
   #
   # Returns the new, clipped {Point}. Note that this could be the same as `position` if no clipping was performed.
@@ -346,11 +339,11 @@
       column = Math.max(position.column, 0)
       column = Math.min(@lineLengthForRow(row), column)
       new Point(row, column)
-
-  # Public: Given a range, this clips it to a real range.
+
+  # Given a range, this clips it to a real range.
   #
   # For example, if `range`'s row exceeds the row count of the buffer,
-  # or if its column goes beyond a line's length, this "sanitizes" the value
+  # or if its column goes beyond a line's length, this "sanitizes" the value
   # to a real range.
   #
   # range - The {Point} to clip
@@ -364,33 +357,23 @@
     prefix: @lines[range.start.row][0...range.start.column]
     suffix: @lines[range.end.row][range.end.column..]

-  # Internal:
-  pushOperation: (operation, editSession) ->
-    if @undoManager
-      @undoManager.pushOperation(operation, editSession)
-    else
-      operation.do()
-
-  # Internal:
-  transact: (fn) ->  @undoManager.transact(fn)
-
-  # Public: Undos the last operation.
+  # Undos the last operation.
   #
   # editSession - The {EditSession} associated with the buffer.
   undo: (editSession) -> @undoManager.undo(editSession)

-  # Public: Redos the last operation.
+  # Redos the last operation.
   #
   # editSession - The {EditSession} associated with the buffer.
   redo: (editSession) -> @undoManager.redo(editSession)
   commit: -> @undoManager.commit()
   abort: -> @undoManager.abort()

-  # Public: Saves the buffer.
+  # Saves the buffer.
   save: ->
     @saveAs(@getPath()) if @isModified()

-  # Public: Saves the buffer at a specific path.
+  # Saves the buffer at a specific path.
   #
   # path - The path to save at.
   saveAs: (path) ->
@@ -403,7 +386,7 @@
     @triggerModifiedStatusChanged(false)
     @trigger 'saved'

-  # Public: Identifies if the buffer was modified.
+  # Identifies if the buffer was modified.
   #
   # Returns a {Boolean}.
   isModified: ->
@@ -412,12 +395,12 @@
     else
       not @isEmpty()

-  # Public: Identifies if a buffer is in a git conflict with `HEAD`.
+  # Identifies if a buffer is in a git conflict with `HEAD`.
   #
   # Returns a {Boolean}.
   isInConflict: -> @conflict

-  # Public: Identifies if a buffer is empty.
+  # Identifies if a buffer is empty.
   #
   # Returns a {Boolean}.
   isEmpty: -> @lines.length is 1 and @lines[0].length is 0
@@ -425,13 +408,13 @@
   getMarkers: ->
     _.values(@validMarkers)

-  # Public: Retrieves the quantity of markers in a buffer.
+  # Retrieves the quantity of markers in a buffer.
   #
   # Returns a {Number}.
   getMarkerCount: ->
     _.size(@validMarkers)

-  # Public: Constructs a new marker at a given range.
+  # Constructs a new marker at a given range.
   #
   # range - The marker {Range} (representing the distance between the head and tail)
   # options - Options to pass to the {BufferMarker} constructor
@@ -446,7 +429,7 @@
     @validMarkers[marker.id] = marker
     marker.id

-  # Public: Constructs a new marker at a given position.
+  # Constructs a new marker at a given position.
   #
   # position - The marker {Point}; there won't be a tail
   # options - Options to pass to the {BufferMarker} constructor
@@ -455,7 +438,7 @@
   markPosition: (position, options) ->
     @markRange([position, position], _.defaults({noTail: true}, options))

-  # Public: Removes the marker with the given id.
+  # Removes the marker with the given id.
   #
   # id - The {Number} of the ID to remove
   destroyMarker: (id) ->
@@ -468,7 +451,7 @@
   setMarkerPosition: (args...) ->
     @setMarkerHeadPosition(args...)

-  # Public: Retrieves the position of the marker's head.
+  # Retrieves the position of the marker's head.
   #
   # id - A {Number} representing the marker to check
   #
@@ -476,7 +459,7 @@
   getMarkerHeadPosition: (id) ->
     @validMarkers[id]?.getHeadPosition()

-  # Public: Sets the position of the marker's head.
+  # Sets the position of the marker's head.
   #
   # id - A {Number} representing the marker to change
   # position - The new {Point} to place the head
@@ -488,7 +471,7 @@
   setMarkerHeadPosition: (id, position, options) ->
     @validMarkers[id]?.setHeadPosition(position)

-  # Public: Retrieves the position of the marker's tail.
+  # Retrieves the position of the marker's tail.
   #
   # id - A {Number} representing the marker to check
   #
@@ -496,7 +479,7 @@
   getMarkerTailPosition: (id) ->
     @validMarkers[id]?.getTailPosition()

-  # Public: Sets the position of the marker's tail.
+  # Sets the position of the marker's tail.
   #
   # id - A {Number} representing the marker to change
   # position - The new {Point} to place the tail
@@ -508,15 +491,15 @@
   setMarkerTailPosition: (id, position, options) ->
     @validMarkers[id]?.setTailPosition(position)

-  # Public: Retrieves the {Range} between a marker's head and its tail.
-  #
+  # Retrieves the {Range} between a marker's head and its tail.
+  #
   # id - A {Number} representing the marker to check
   #
   # Returns a {Range}.
   getMarkerRange: (id) ->
     @validMarkers[id]?.getRange()

-  # Public: Sets the marker's range, potentialy modifying both its head and tail.
+  # Sets the marker's range, potentialy modifying both its head and tail.
   #
   # id - A {Number} representing the marker to change
   # range - The new {Range} the marker should cover
@@ -526,7 +509,7 @@
   setMarkerRange: (id, range, options) ->
     @validMarkers[id]?.setRange(range, options)

-  # Public: Sets the marker's tail to the same position as the marker's head.
+  # Sets the marker's tail to the same position as the marker's head.
   #
   # This only works if there isn't already a tail position.
   #
@@ -536,13 +519,13 @@
   placeMarkerTail: (id) ->
     @validMarkers[id]?.placeTail()

-  # Public: Removes the tail from the marker.
+  # Removes the tail from the marker.
   #
   # id - A {Number} representing the marker to change
   clearMarkerTail: (id) ->
     @validMarkers[id]?.clearTail()

-  # Public: Identifies if the ending position of a marker is greater than the starting position.
+  # Identifies if the ending position of a marker is greater than the starting position.
   #
   # This can happen when, for example, you highlight text "up" in a {Buffer}.
   #
@@ -552,7 +535,7 @@
   isMarkerReversed: (id) ->
     @validMarkers[id]?.isReversed()

-  # Public: Identifies if the marker's head position is equal to its tail.
+  # Identifies if the marker's head position is equal to its tail.
   #
   # id - A {Number} representing the marker to check
   #
@@ -560,14 +543,14 @@
   isMarkerRangeEmpty: (id) ->
     @validMarkers[id]?.isRangeEmpty()

-  # Public: Sets a callback to be fired whenever a marker is changed.
+  # Sets a callback to be fired whenever a marker is changed.
   #
   # id - A {Number} representing the marker to watch
   # callback - A {Function} to execute
   observeMarker: (id, callback) ->
     @validMarkers[id]?.observe(callback)

-  # Public: Given a buffer position, this finds all markers that contain the position.
+  # Given a buffer position, this finds all markers that contain the position.
   #
   # bufferPosition - A {Point} to check
   #
@@ -579,7 +562,7 @@
       ids.push(id) if marker.containsPoint(bufferPosition)
     ids

-  # Public: Identifies if a character sequence is within a certain range.
+  # Identifies if a character sequence is within a certain range.
   #
   # regex - The {RegExp} to check
   # startIndex - The starting row {Number}
@@ -609,14 +592,14 @@

     matches

-  # Public: Scans for text in the buffer, calling a function on each match.
+  # Scans for text in the buffer, calling a function on each match.
   #
   # regex - A {RegExp} representing the text to find
   # iterator - A {Function} that's called on each match
   scan: (regex, iterator) ->
     @scanInRange(regex, @getRange(), iterator)

-  # Public: Scans for text in a given range, calling a function on each match.
+  # Scans for text in a given range, calling a function on each match.
   #
   # regex - A {RegExp} representing the text to find
   # range - A {Range} in the buffer to search within
@@ -659,7 +642,7 @@

       break unless global and keepLooping

-  # Public: Scans for text in a given range _backwards_, calling a function on each match.
+  # Scans for text in a given range _backwards_, calling a function on each match.
   #
   # regex - A {RegExp} representing the text to find
   # range - A {Range} in the buffer to search within
@@ -667,7 +650,7 @@
   backwardsScanInRange: (regex, range, iterator) ->
     @scanInRange regex, range, iterator, true

-  # Public: Given a row, identifies if it is blank.
+  # Given a row, identifies if it is blank.
   #
   # row - A row {Number} to check
   #
@@ -675,7 +658,7 @@
   isRowBlank: (row) ->
     not /\S/.test @lineForRow(row)

-  # Public: Given a row, this finds the next row above it that's empty.
+  # Given a row, this finds the next row above it that's empty.
   #
   # startRow - A {Number} identifying the row to start checking at
   #
@@ -689,7 +672,7 @@
       return row unless @isRowBlank(row)
     null

-  # Public: Given a row, this finds the next row that's blank.
+  # Given a row, this finds the next row that's blank.
   #
   # startRow - A row {Number} to check
   #
@@ -702,7 +685,7 @@
         return row unless @isRowBlank(row)
     null

-  # Public: Identifies if the buffer has soft tabs anywhere.
+  # Identifies if the buffer has soft tabs anywhere.
   #
   # Returns a {Boolean},
   usesSoftTabs: ->
@@ -711,20 +694,33 @@
         return match[0][0] != '\t'
     undefined

-  # Public: Checks out the current `HEAD` revision of the file.
+  # Checks out the current `HEAD` revision of the file.
   checkoutHead: ->
     path = @getPath()
     return unless path
     git?.checkoutHead(path)

-  # Public: Checks to see if a file exists.
+  # Checks to see if a file exists.
   #
   # Returns a {Boolean}.
   fileExists: ->
     @file? && @file.exists()

-
   ### Internal ###
+
+  change: (oldRange, newText, options) ->
+    oldRange = Range.fromObject(oldRange)
+    operation = new BufferChangeOperation({buffer: this, oldRange, newText, options})
+    range = @pushOperation(operation)
+    range
+
+  pushOperation: (operation, editSession) ->
+    if @undoManager
+      @undoManager.pushOperation(operation, editSession)
+    else
+      operation.do()
+
+  transact: (fn) ->  @undoManager.transact(fn)

   scheduleModifiedEvents: ->
     clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout


Properly test json string, rather then model serialization

To test this, we serialize to json, then parse the json

[#102596026]

--- test/loading-serialization-test.coffee
+++ test/loading-serialization-test.coffee
@@ -51,9 +51,10 @@
       @graphStore.addNode @nodeB
       @graphStore.addLink @link

-    describe "The serialize function", ->
+    describe "The toJsonString function", ->
       it "should serialize all the properties of the model", ->
-        model = @graphStore.serialize()
+        jsonString = @graphStore.toJsonString()
+        model = JSON.parse jsonString

         model.version.should.exist
         model.nodes.should.exist
@@ -64,7 +65,8 @@
         model.links.length.should.equal 1

       it "should correctly serialize a node", ->
-        node = @graphStore.serialize().nodes[0]
+        jsonString = @graphStore.toJsonString()
+        node = JSON.parse(jsonString).nodes[0]
         node.key.should.equal "a"
         node.data.title.should.equal "a"
         node.data.x.should.equal 10
@@ -74,7 +76,8 @@
         node.data.valueDefinedSemiQuantitatively.should.equal false

       it "should correctly serialize a link", ->
-        link = @graphStore.serialize().links[0]
+        jsonString = @graphStore.toJsonString()
+        link = JSON.parse(jsonString).links[0]
         link.title.should.equal ""
         link.color.should.equal "#777"
         link.sourceNode.should.equal "a"
@@ -84,14 +87,18 @@
         link.relation.text.should.equal "increases"
         link.relation.formula.should.equal "1 * in"

-    describe "the toJsonString function", ->
-      it "should create a string nodes and links", ->
+      it "should not serialize certain properties", ->
         jsonString = @graphStore.toJsonString()
-        jsonString.should.match(/"nodes":/)
-        jsonString.should.match(/"links":/)
         jsonString.should.not.match(/"description":/)
         jsonString.should.not.match(/"metadata":/)
-        @graphStore.toJsonString(@fakePalette).should.match(/"palette":/)
+
+      it "should be able to serialize the palette", ->
+        console.log @graphStore.toJsonString(@fakePalette)
+        jsonString = @graphStore.toJsonString(@fakePalette)
+        palette = JSON.parse(jsonString).palette
+        palette.a.should.equal 1
+        palette.b.should.equal 2
+

   describe "loadData", ->
     beforeEach ->


:bug: Destroy binding when the repository is destroyed

Fixes #12

--- lib/minimap-git-diff-binding.coffee
+++ lib/minimap-git-diff-binding.coffee
@@ -19,6 +19,8 @@
         @scheduleUpdate()
       @subscriptions.add repository.onDidChangeStatus (changedPath) =>
         @scheduleUpdate() if changedPath is @editor.getPath()
+      @subscriptions.add repository.onDidDestroy =>
+        @destroy()

     @scheduleUpdate()


update for the new resin.models.device.generateUniqueKey

--- lib/actions/device.coffee
+++ lib/actions/device.coffee
@@ -143,7 +143,7 @@
 		resin.models.application.get(params.application).then (application) ->

 			Promise.try ->
-				return options.uuid or resin.models.device.generateUUID()
+				return options.uuid or resin.models.device.generateUniqueKey()
 			.then (uuid) ->
 				console.info("Registering to #{application.app_name}: #{uuid}")
 				return resin.models.device.register(application.app_name, uuid)


Remove useless assignment

Remove useless assignment

--- lib/atom-io-client.coffee
+++ lib/atom-io-client.coffee
@@ -105,7 +105,6 @@
     cached = localStorage.getItem(@cacheKeyForPath(packagePath))
     cached = if cached then JSON.parse(cached)
     if cached? and (not @online() or options.force or (Date.now() - cached.createdOn < @expiry))
-      cached ?= data: {}
       callback(null, cached.data)
     else
       # falsy data means "try to hit the network"


:bug: Update indicator when first attached to status bar

--- lib/soft-wrap-indicator-view.coffee
+++ lib/soft-wrap-indicator-view.coffee
@@ -18,6 +18,10 @@

     @subscribe this, 'click', => @getActiveEditor()?.toggleSoftWrap()

+  # Internal: Executes after the view is added to the status bar.
+  afterAttach: ->
+    @update()
+
   # Gets the currently active `Editor`.
   #
   # Returns the {Editor} that is currently active or `null` if there is not one active.


Added clause to prevent double-population of Model/Scopes in edge cases.

--- lib/ST/Model/Scope.coffee
+++ lib/ST/Model/Scope.coffee
@@ -78,29 +78,31 @@
     @method 'count', ->
       @populate()
       @super()
-
+
+    @method 'sort', ->
+      if @_orders
+        orders = @_orders
+        @_array.sort (a, b) ->
+          for attribute in orders
+            if attribute.reverse
+              a_value = b.get attribute.reverse
+              b_value = a.get attribute.reverse
+            else
+              a_value = a.get attribute
+              b_value = b.get attribute
+            return 1  if a_value > b_value
+            return -1 if a_value < b_value
+          0
+
     @method 'populate', ->
-      unless @_populated
+      unless @_populated || @_array.length
+        @_populated = true
+
         self = this
-
         @index().each (candidate) ->
           self.add candidate if candidate.matches self._conditions

-        if @_orders
-          orders = @_orders
-          @_array.sort (a, b) ->
-            for attribute in orders
-              if attribute.reverse
-                a_value = b.get attribute.reverse
-                b_value = a.get attribute.reverse
-              else
-                a_value = a.get attribute
-                b_value = b.get attribute
-              return 1  if a_value > b_value
-              return -1 if a_value < b_value
-            0
-
-        @_populated = true
+        @sort()

     @method 'forgetAll', (destroy=false) ->
       while item = @first()
@@ -126,6 +128,7 @@

     @method 'targetItemAdded', (target, item) ->
       @add item if item.matches @_conditions
+      @sort()

     @method 'targetItemRemoved', (target, item) ->
       @remove item


add dataKey for col object

add dataKey for col object

--- shared/ironTableController.coffee
+++ shared/ironTableController.coffee
@@ -188,7 +188,7 @@
             record = @collection().findOne(id)
         recordData = []
         for key, col of @_cols()
-
+            dataKey = col.dataKey or key
             if col[type] or col["staticOn_#{type}"]
                 col.displayType = col.type
                 col.checkbox = false
@@ -196,19 +196,19 @@
                 if col.type is 'boolean'
                     col.displayType = 'checkbox'
                     col.checkbox = true
-                    if record?[key]?
-                        if record[key]
+                    if record?[dataKey]?
+                        if record[dataKey]
                             col.checked = 'checked'
                     else if col.default
                         col.checked = 'checked'
-                else if record?[key]?
-                    col.value = col.display?(record[key], record) or record[key]
+                else if record?[dataKey]?
+                    col.value = col.display?(record[dataKey], record) or record[dataKey]
                 else if col.default?
                     col.value = col.default

                 if col["staticOn_#{type}"]
                     col.static = true
-                    col.value = col.display?(record[key], record) or record?[key]
+                    col.value = col.display?(record[dataKey], record) or record?[dataKey]

                 col.header = (col.header || key).capitalize()
                 col.key = key


Remove deprecated test

Remove deprecated test

--- spec/mixins/collectable.coffee
+++ spec/mixins/collectable.coffee
@@ -12,32 +12,7 @@
       createModel()
       expect(createModel).toThrow()

-    it "should store itself in local storage", ->
-     class Fruit extends Tails.Model
-        _.extend @, Tails.Mixable
-        @concern Tails.Mixins.Collectable
-
-      a = Fruit.get(4)
-      a.set "prop", "val"
-      a.store()
-
-
-      class Fruit extends Tails.Model
-        _.extend @, Tails.Mixable
-        @concern Tails.Mixins.Collectable
-
-      Fruit.reset()
-      Fruit.retrieve()
-
-      console.log JSON.stringify Fruit.collection().models
-
-      # b = Fruit.get(4)
-      # expect(b.get('prop')).toEqual("val")

-
-
-
-