Apply a lot of :fire:

Apply a lot of :fire:

--- lib/git-service.coffee
+++ lib/git-service.coffee
@@ -19,107 +19,8 @@
       @_instance = new GitService
     @_instance

-  @statusCodes: ->
-    Git.Status.STATUS
-
   constructor: ->
-    @tmpDir   = os.tmpDir()
     @repoPath = atom.project.getPaths()[0]
-    @emitter = new Emitter
-
-  emit: (event) ->
-    @emitter.emit(event)
-
-  onDidUpdateRepository: (callback) ->
-    @emitter.on('did-update-repository', callback)
-
-  updateRepository: ->
-    @emitter.emit('did-update-repository')
-
-  getBranchName: ->
-    Git.Repository.open(@repoPath).then (repo) ->
-      repo.getBranch('HEAD')
-    .then (branch) =>
-      @normalizeBranchName(branch.name())
-    .catch ->
-      Promise.resolve("master")
-
-  normalizeBranchName: (name) ->
-    name.replace('refs/heads/','')
-
-  localBranches: ->
-    data = {}
-    branches = []
-    @getBranchName()
-    .then (branchName) =>
-      data.branchName = branchName
-      Git.Repository.open(@repoPath)
-    .then (repo) ->
-      repo.getReferenceNames()
-    .then (refs) ->
-      for ref in refs
-        if matches = ref.match /^refs\/heads\/(.*)/
-          branch =
-            name: matches[1]
-            current: matches[1] == data.branchName
-          branches.push branch
-
-      for ref in refs
-        if matches = ref.match /^refs\/remotes\/origin\/(.*)/
-          branch =
-            name: matches[1]
-            current: matches[1] == data.branchName
-            remote: true
-
-          local = _.find branches, (br) ->
-            br.name == branch.name
-
-          branches.push branch unless local or branch.name is "HEAD"
-
-      branches.sort (a, b) ->
-        aName = a.name.toLowerCase()
-        bName = b.name.toLowerCase()
-        if aName < bName
-          -1
-        else if aName > bName
-          1
-        else
-          0
-    .catch ->
-      Promise.resolve(name: 'master', current: true)
-
-  createBranch: ({name, from}) ->
-    data = {}
-    name = @normalizeBranchName(name)
-    Git.Repository.open(@repoPath)
-    .then (repo) ->
-      data.repo = repo
-      repo.getBranchCommit(from)
-    .then (branch) =>
-      signature = data.repo.defaultSignature()
-      message = "Created #{name} from #{from}"
-      data.repo.createBranch(name, branch, 0, signature, message).then =>
-        @checkoutBranch(name)
-    .then =>
-      @emitter.emit('did-update-repository')
-
-  trackRemoteBranch: (name) ->
-    @createBranch({name: name, from: "origin/#{name}"})
-    .then =>
-      Git.Repository.open(@repoPath)
-    .then (repo) ->
-      repo.getBranch(name)
-    .then (branch) ->
-      Git.Branch.setUpstream(branch, "origin/#{name}")
-    .then =>
-      @emitter.emit('did-update-repository')
-
-  checkoutBranch: (name) ->
-    Git.Repository.open(@repoPath)
-    .then (repo) ->
-      repo.checkoutBranch(name)
-    .then =>
-      @emitter.emit('did-update-repository')

   getDiffForPath: (path, state) ->
     @diffsPromise.then (diffs) ->
@@ -188,54 +89,6 @@
         @statuses[status.path()] = status
       statuses

-  getComparisonBranch: (names, branchName) ->
-    origin = "refs/remotes/origin/#{branchName}"
-    if names.indexOf(origin) >= 0
-      origin
-    else if branchName != "master"
-      "master"
-    else
-      null
-
-  getLatestUnpushed: ->
-    data = {}
-    Git.Repository.open(@repoPath)
-    .then (repo) ->
-      data.repo = repo
-      repo.getCurrentBranch()
-    .then (branch) =>
-      data.branch     = branch
-      data.branchName = @normalizeBranchName(branch.name())
-      data.walker     = data.repo.createRevWalk()
-      data.walker.pushHead()
-      data.repo.getReferenceNames()
-    .then (names) =>
-      data.compareBranch = @getComparisonBranch(names, data.branchName)
-      new Promise (resolve, reject) ->
-        if data.compareBranch
-          data.repo.getBranchCommit(data.compareBranch)
-          .then (compare) ->
-            data.walker.hide(compare)
-            resolve()
-        else
-          resolve()
-    .then ->
-      data.walker.next()
-    .then (oid) ->
-      if oid then data.repo.getCommit(oid) else null
-
-  resetBeforeCommit: (commit) ->
-    commit.getParents().then (parents) ->
-      Git.Reset.reset(commit.repo,
-        if parents.length then parents[0] else null,
-        Git.Reset.TYPE.SOFT)
-    .then ->
-      commit.repo.openIndex()
-    .then (index) ->
-      index.write()
-    .then =>
-      @emitter.emit('did-update-repository')
-
   stagePath: (path) ->
     @stageAllPaths([path])

@@ -255,8 +108,6 @@
           index.addByPath(path)

       index.write()
-    .then =>
-      @emitter.emit('did-update-repository')

   unstagePath: (path) ->
     @unstageAllPaths([path])
@@ -281,8 +132,6 @@
                 status.headToIndex().oldFile().path())

             Git.Reset.default(data.repo, commit, path)
-    .then =>
-      @emitter.emit('did-update-repository')

   wordwrap: (str) ->
     return str unless str.length
@@ -310,8 +159,6 @@
         @wordwrap(message),
         data.indexTree,
         parents)
-    .then =>
-      @emitter.emit('did-update-repository')

   parseHeader: (header) ->
     headerParts =
@@ -421,8 +268,6 @@
       data.index.removeByPath(oldPath) if oldPath != newPath
       data.index.add(entry)
       data.index.write()
-    .then =>
-      @emitter.emit('did-update-repository')
     .catch (error) ->
       console.log error.message
       console.log error.stack
@@ -459,8 +304,6 @@
           mode: fileDiff.getMode()
         data.index.add(entry)
         data.index.write()
-    .then =>
-      @emitter.emit('did-update-repository')

   createIndexEntry: ({oid, path, fileSize, mode}) ->
     entry  = new Git.IndexEntry()
@@ -492,11 +335,6 @@

     newLines.unshift(newHeader)
     newLines.join("\n")
-
-  workingBlob: (path) ->
-    new Git.Promise (resolve, reject) =>
-      fse.readFile "#{@repoPath}/#{path}", "utf8", (e, text) ->
-        resolve(text)

   indexBlob: (path) ->
     data = {}
@@ -532,94 +370,3 @@
             ""
       else
         ""
-
-  getCommitBlobs: (commit, patch) ->
-    oldPath = patch.oldFile().path()
-    oldSha = commit.parents()[0]
-    newPath = patch.newFile().path()
-    newSha = commit.id()
-
-    oldBlob = @treeBlob(oldPath, oldSha) unless patch.isAdded()
-    newBlob = @treeBlob(newPath, newSha) unless patch.isDeleted()
-
-    if oldBlob and newBlob
-      Git.Promise.all([oldBlob, newBlob]).then (blobs) ->
-        data =
-          old: blobs[0]
-          new: blobs[1]
-    else if newBlob
-      newBlob.then (blob) ->
-        data =
-          old: ''
-          new: blob
-    else if oldBlob
-      oldBlob.then (blob) ->
-        data =
-          old: blob
-          new: ''
-    else
-      data =
-        old: ''
-        new: ''
-
-  getBlobs: ({patch, status, commit}) ->
-    if commit
-      @getCommitBlobs(commit, patch)
-    else
-      if status == 'staged'
-        @getStagedBlobs(patch)
-      else
-        @getUnstagedBlobs(patch)
-
-  getStagedBlobs: (patch) ->
-    oldPath = patch.oldFile().path()
-    newPath = patch.newFile().path()
-
-    if patch.isAdded() or patch.isUntracked()
-      @indexBlob(newPath).then (newBlob) ->
-        data =
-          new: newBlob
-          old: ''
-    else if patch.isDeleted()
-      @treeBlob(oldPath).then (oldBlob) ->
-        data =
-          old: oldBlob
-          new: ''
-    else
-      Git.Promise.all([@treeBlob(oldPath), @indexBlob(newPath)])
-      .then (blobs) ->
-        data =
-          old: blobs[0]
-          new: blobs[1]
-
-  getUnstagedBlobs: (patch) ->
-    oldPath = patch.oldFile().path()
-    newPath = patch.newFile().path()
-
-    if patch.isAdded() or patch.isUntracked()
-      @workingBlob(newPath).then (newBlob) ->
-        data =
-          new: newBlob
-          old: ''
-    else if patch.isDeleted()
-      @indexBlob(oldPath).then (oldBlob) ->
-        data =
-          old: oldBlob
-          new: ''
-    else
-      Git.Promise.all([@indexBlob(oldPath), @workingBlob(newPath)])
-      .then (blobs) ->
-        data =
-          old: blobs[0]
-          new: blobs[1]
-
-  forceCheckoutPath: (path) ->
-    opts =
-      checkoutStrategy: Git.Checkout.STRATEGY.FORCE
-      paths: path
-
-    Git.Repository.open(@repoPath)
-    .then (repo) ->
-      Git.Checkout.head(repo, opts)
-    .then =>
-      @emitter.emit('did-update-repository')


chore: #1136 - Update build dictionary with image scaling feature

--- lib/grunt/utils.coffee
+++ lib/grunt/utils.coffee
@@ -94,6 +94,7 @@
         fuImageValidationModule: false
         fuSessionModule: false
         fuFormSupportModule: false
+        fuScaling: false
         fuSrcJquery: false
         fuSrcS3Jquery: false
         fuSrcAzureJquery: false


fix expectation support for overwriteMethod

--- lib/expectation.coffee
+++ lib/expectation.coffee
@@ -17,15 +17,24 @@
 Assertion = chai.Assertion

 module.exports = class Expectation extends Assertion
-  @overwriteMethod:    (name, fn) -> super(name, @promisify(fn, name))
   @addMethod:          (name, fn) -> super(name, @promisify(fn, name))
   @addChainableMethod: (name, fn, chainingBehavior) ->
     super(name, @promisify(fn, name), chainingBehavior)

+  @overwriteMethod: (name, fn) ->
+    _method = Expectation::[name]
+    _super = -> this
+
+    _super = _method if _method and _.isFunction(_method)
+
+    Expectation::[name] = ->
+      result = Expectation.promisify(fn(_super)).apply(this, arguments)
+
   @promisify: (fn, name = null) ->
     (args...) ->
-      task = @resolveFlags().then =>
-        Q.all(args).then (values) => fn.apply(this, values)
+      task = @resolveFlags()
+        .then => Q.all(args)
+        .then (values) => fn.apply(this, values)

       barrierContext.pushTask(task, "expectation #{name}")


Minor fixes

Minor fixes

--- lib/ide-backend.coffee
+++ lib/ide-backend.coffee
@@ -103,7 +103,8 @@
   cabalBuild: (cmd, opts) =>
     # It shouldn't be possible to call this function until cabalProcess
     # exits. Otherwise, problems will ensue.
-    return opts.onDone?() if @cabalProcess?.running
+    return Promise.resolve({}) if @running
+    @running = true

     Promise.all [@upi.getConfigParam('builder'), @upi.getConfigParam('target')]
     .then ([builder, target]) =>
@@ -132,20 +133,29 @@
             spawnOpts: @spawnOpts(cabalRoot)
             buildDir: @getConfigOpt('buildDir')
           }
-          @cabalProcess = buildf args
+          buildf args
         else
           throw new Error("Unkown builder '#{builder?.name ? builder}'")
       else
-        @cabalFileError()
-    .catch (error) ->
+        @upi.addMessages [
+          message: 'No cabal file found'
+          severity: 'error'
+        ]
+        return {}
+    .then (res) =>
+      @running = false
+      return res
+    .catch (error) =>
+      @running = false
       if error?
+        console.error error
         atom.notifications.addFatalError error.toString(),
           detail: error
           dismissable: true
-      opts.onDone?()
+      return {}

   builders:
-    none: ({opts}) -> opts.onDone(0, false)
+    none: ({opts}) -> return {exitCode: 0, hasError: false}
     cabal: ({cmd, opts, target, cabalRoot, spawnOpts, buildDir}) ->
       cabalArgs = [cmd]
       switch cmd
@@ -175,7 +185,7 @@
                 require('./cabal-process') 'cabal', ['sandbox', 'init'], spawnOpts, opts
               if notificationContent?
                 notificationContent.appendChild install
-            return opts.onDone()
+            return {}
           cabalArgs = ['install', '--only-dependencies']
       cabalArgs.push '--builddir=' + buildDir
       cabalArgs.push target.target if target.target? and cmd is 'build'
@@ -189,7 +199,7 @@
         cabalArgs = ['new-build']
       else
         atom.notifications.addWarning "Command '#{cmd}' is not implemented for cabal-nix"
-        return opts.onDone()
+        return {}

       cabalArgs.push target.target if target.target? and cmd is 'build'
       require('./cabal-process') 'cabal', cabalArgs, spawnOpts, opts
@@ -317,10 +327,3 @@
       messageTypes: ['build']
       defaultSeverity: 'build'
       canCancel: true
-
-  cabalFileError: ->
-    @upi.addMessages [
-      message: 'No cabal file found'
-      severity: 'error'
-    ]
-    @upi.setStatus status: 'error'


format dates

format dates

--- angular/core/services/intercom_service.coffee
+++ angular/core/services/intercom_service.coffee
@@ -12,11 +12,11 @@
     plan: group.subscriptionKind
     subscription_kind: group.subscriptionKind
     subscription_plan: group.subscriptionPlan
-    subscription_expires_at: group.subscriptionExpiresAt
+    subscription_expires_at: group.subscriptionExpiresAt.format()
     creator_id: group.creatorId
     group_privacy: group.groupPrivacy
     cohort_id: group.cohortId
-    created_at: group.createdAt
+    created_at: group.createdAt.format()
     locale: group.locale
     motions_count: group.motionsCount
     discussions_count: group.discussionsCount


Add support for submodules

Add support for submodules

Specify the file path to the Git APIs so that the right repository
is used when the project's repository contains submodules.

Closes #15
Closes #17

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -100,7 +100,7 @@
   # Internal
   gitUrl: ->
     remoteOrBestGuess = @remoteName() ? 'origin'
-    @repo.getConfigValue("remote.#{remoteOrBestGuess}.url")
+    @repo.getConfigValue("remote.#{remoteOrBestGuess}.url", @filePath)

   # Internal
   githubRepoUrl: ->
@@ -124,24 +124,24 @@

   # Internal
   repoRelativePath: ->
-    @repo.relativize(@filePath)
+    @repo.getRepo(@filePath).relativize(@filePath)

   # Internal
   remoteName: ->
-    shortBranch = @repo.getShortHead()
+    shortBranch = @repo.getShortHead(@filePath)
     return null unless shortBranch

-    branchRemote = @repo.getConfigValue("branch.#{shortBranch}.remote")
+    branchRemote = @repo.getConfigValue("branch.#{shortBranch}.remote", @filePath)
     return null unless branchRemote?.length > 0

     branchRemote

   # Internal
   branchName: ->
-    shortBranch = @repo.getShortHead()
+    shortBranch = @repo.getShortHead(@filePath)
     return null unless shortBranch

-    branchMerge = @repo.getConfigValue("branch.#{shortBranch}.merge")
+    branchMerge = @repo.getConfigValue("branch.#{shortBranch}.merge", @filePath)
     return shortBranch unless branchMerge?.length > 11
     return shortBranch unless branchMerge.indexOf('refs/heads/') is 0


:bug: Delete messages of a linter when removed

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -41,7 +41,9 @@
     @linters.addLinter(linter)

   deleteLinter: (linter) ->
-    @linters.deleteLinter(linter)
+    if @linters.hasLinter(linter)
+      @linters.deleteLinter(linter)
+      @messages.deleteMessages(linter)

   hasLinter: (linter) ->
     @linters.hasLinter(linter)


Support for the wordSpacing and characterSpacing text options

--- lib/mixins/text.coffee
+++ lib/mixins/text.coffee
@@ -14,6 +14,7 @@
             fontSize: null
             mode: 0
             wordSpacing: 0
+            characterSpacing: 0

         # state of the wrapping algorithm
         @_wrapState = {}
@@ -32,6 +33,10 @@

         # add current font to page if necessary
         @page.fonts[@_font.id] ?= @_font.ref
+
+        # if the wordSpacing option is specified, remove multiple consecutive spaces
+        if options.wordSpacing
+            text = text.replace(/\s+/g, ' ')

         # word wrapping
         if options.width
@@ -85,14 +90,17 @@
     _fragment: (text, x, y, options = {}) ->
         state = @_textState
         wrap = @_wrapState
+
+        # handle options
+        text = '' + text
         align = options.align or 'left'
-        text = '' + text
         indent = (wrap.firstLine and options.indent) or 0
-        wordSpacing = 0
+        wordSpacing = options.wordSpacing or 0
+        characterSpacing = options.characterSpacing or 0

         # text alignments
         if options.width
-            lineWidth = options.width - indent
+            lineWidth = options.width - indent - wrap.extraSpace

             switch align
                 when 'right'
@@ -113,9 +121,6 @@
                     textWidth = @widthOfString text.replace(/\s+/g, '')
                     wordSpacing = (lineWidth - textWidth) / (words.length - 1) - @widthOfString(' ')

-                    # Remove double spaces
-                    text = text.replace(/\s+/g, ' ')
-
         # indentation support
         x += indent

@@ -140,6 +145,9 @@

         # Word spacing
         @addContent wordSpacing + ' Tw' unless wordSpacing is state.wordSpacing
+
+        # Character spacing
+        @addContent characterSpacing + ' Tc' unless characterSpacing is state.characterSpacing

         # add the actual text
         @addContent "(#{text}) Tj"
@@ -167,8 +175,12 @@
         # split the line into words
         words = text.match(WORD_RE)

+        # calculate the extra width
+        wrap.extraSpace = (options.wordSpacing or 0) * (words.length - 1) +   # wordSpacing
+                          (options.characterSpacing or 0) * (text.length - 1) # characterSpacing
+
         # space left on the line to fill with words
-        spaceLeft = lineWidth - (options.indent or 0)
+        spaceLeft = lineWidth - indent - wrap.extraSpace

         # word width cache
         wordWidths = {}
@@ -201,7 +213,7 @@
                 return if @y > maxY

                 # reset the space left and buffer
-                spaceLeft = lineWidth - w
+                spaceLeft = lineWidth - w - wrap.extraSpace
                 buffer = if word is '\n' then '' else word

             else


Erased and modified unnecessary expressions.

--- lib/mylist_atom.coffee
+++ lib/mylist_atom.coffee
@@ -16,20 +16,20 @@
     b = $(@body)
     @title = b.find('title').text()
     @video_id = b.find('link').attr('href').split('/')[4]
-    @timelike_id = new Number(b.find('id').text().split(',')[1].split(':')[1].split('/')[2])
+    @timelike_id = Number(b.find('id').text().split(',')[1].split(':')[1].split('/')[2])
     @published = b.find('published').text()
     @updated = b.find('updated').text()

     c = b.find('content')
     @thumbnail_url = c.find('img').attr('src')
     @description = c.find('.nico-description').text()
-    @length = @.convert_to_sec(c.find('.nico-info-length').text())
-    @info_date = @.convert_to_unix_time(c.find('.nico-info-date').text())
+    @length = @.convert_to_sec c.find('.nico-info-length').text()
+    @info_date = @.convert_to_unix_time c.find('.nico-info-date').text()

   convert_to_sec : (string) ->
     s = string.split(':')
-    minute = new Number s[0]
-    second = new Number s[1]
+    minute = Number s[0]
+    second = Number s[1]
     minute * 60 + second

   convert_to_unix_time : (string) ->


:new: Add onDidClassifyMessages event

:new: Add onDidClassifyMessages event

--- lib/linter-plus.coffee
+++ lib/linter-plus.coffee
@@ -87,6 +87,10 @@
   onDidChangeMessages: (callback)->
     return @messages.onDidChange(callback)

+  # Classify as in sort
+  onDidClassifyMessages: (callback)->
+    return @messages.onDidClassify(callback)
+
   onDidChangeProjectMessages: (callback) ->
     console.warn("Linter::onDidChangeProjectMessages is deprecated, use Linter::onDidChangeMessages instead")
     return @onDidChangeMessages(callback)


Fix for file: targets

Fix for file: targets

--- lib/load-config.coffee
+++ lib/load-config.coffee
@@ -73,12 +73,16 @@
           target: host.default
           _path: "/"
           rewrite: (reqUrl)->
+			targetPathBase= if @target.protocol == 'file:'
+				''
+			else
+				@target.pathname
             if @target.nopath
-              @target.pathname
+              targetPathBase
             else if @target.nostrippath
-              path.join @target.pathname, reqUrl
+              path.join targetPathBase, reqUrl
             else
-              path.join @target.pathname, reqUrl.substring(@_path.length)
+              path.join targetPathBase, reqUrl.substring(@_path.length)

       match


handling csvs in color and size

handling csvs in color and size

--- app/assets/javascripts/admin/stores.js.coffee
+++ app/assets/javascripts/admin/stores.js.coffee
@@ -150,16 +150,26 @@
                         update_notice("#{photos_with_tags.length} #{pluralized_photo} from your feed #{pluralized_has} been linked.")

             .on 'click', '.add-size', ->
-                size = ($ '#product_sizes').val()
-                if size
-                    ($ '#product_sizes').val('')
-                    ($ @).closest('.control-group').append(Mustache.render(templates.product_form_label_template, {value: size, name: 'size'}))
+                _sizes = ($ '#product_sizes').val()
+                return unless _sizes
+                ($ '#product_sizes').val('')
+                for size in _sizes.split(',')
+                    ($ @).closest('.control-group')
+                        .append(Mustache.render(
+                                templates.product_form_label_template, {
+                                    value: size,
+                                    name: 'size'}))

             .on 'click', '.add-color', ->
-                size = ($ '#product_colors').val()
-                if size
-                    ($ '#product_colors').val('')
-                    ($ @).closest('.control-group').append(Mustache.render(templates.product_form_label_template, {value: size, name: 'color'}))
+                _colors = ($ '#product_colors').val()
+                return unless _colors
+                ($ '#product_colors').val('')
+                for color in _colors.split(',')
+                    ($ @).closest('.control-group')
+                        .append(Mustache.render(
+                                templates.product_form_label_template, {
+                                    value: color,
+                                    name: 'color'}))

             .on 'click', '.remove-label', ->
                 ($ @).parent().remove()


fix XSS

fix XSS

--- app/assets/javascripts/pirates/codegui.coffee
+++ app/assets/javascripts/pirates/codegui.coffee
@@ -293,8 +293,8 @@
       return $(this).find('td:first').text() is variable
     )

-    variable = variable.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
-    allocation = allocation.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+#    variable = variable.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+#    allocation = allocation.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');

     if $row.length < 1 # new row
       $row = $ "

"


doActions: gather task info first

doActions: gather task info first

--- lib/phabricator.coffee
+++ lib/phabricator.coffee
@@ -694,12 +694,19 @@

   doActions: (user, id, commandString, comment) ->
     @getBotPHID()
+    .bind({ botphid: null })
     .then (bot_phid) =>
-      commands = @parseAction commandString
+      @botphid = botphid
+      @getUser(user, user)
+    .then (userPhid) =>
+      query = { 'task_id': id }
+      @request(query, 'maniphest.info')
+    .then (body) =>
+      commands = @parseAction body.result, commandString
       console.log commands
       { id: id, messages: [ 'ok' ] }

-  parseAction: (str, res = []) ->
+  parseAction: (item, str, res = []) ->
     p = new RegExp("^(in|not in|on|is|to) ([^ ]*)")
     r = str.trim().match p
     switch r[1]
@@ -718,7 +725,7 @@
           res.push({ type: 'priority', value: r[2] })
     next = str.trim().replace(p, '')
     if next.trim() isnt ''
-      res = @parseAction(next, res)
+      res = @parseAction(item, next, res)
     res

   listTasks: (projphid) ->


refactoring & added docs

refactoring & added docs

--- lib/precompiler.coffee
+++ lib/precompiler.coffee
@@ -12,46 +12,50 @@
 module.exports = ->
   global.options.debug.log "precompiling templates", "yellow"
   return false if typeof global.options.templates is "undefined"
-  root = path.join(global.options.templates, "/")
+  template_dir = path.join process.cwd(), global.options.templates
+
+  precompiler = new Precompiler(
+    templates: _.map(
+      fs.readdirSync(template_dir),
+      (f) -> path.join template_dir, f
+    )
+  )
+
+  buf = precompiler.compile()
+  buf = compressor buf, 'js'
+
+  # TODO: make output folder dynamic
   output_path = path.normalize("public/js/templates.js")
   mkdirp.sync path.dirname(output_path)
-
-  precompiler = new Precompiler(
-    source: path.normalize(process.cwd() + '/' + root)
-    output: output_path
-    templates: _.map(fs.readdirSync(
-      path.join(process.cwd(), root)),
-      (f) -> path.basename f, ".jade"
-    )
-  )
-  buf = precompiler.compile()
-  buf = compressor buf, 'js'
   fs.writeFileSync output_path, buf

 class Precompiler
+  ###*
+   * deals with setting up the variables for options
+   * @param {Object} options = {} an object holding all the options to be
+     passed to the compiler. 'templates' must be specified.
+  ###
   constructor: (options = {}) ->
     defaults =
+      include_helpers: true
       inline: false
       debug: false
       namespace: "templates"
-      source: ''
-      output: ''
-      templates: undefined
+      templates: undefined # an array of template filenames

     _.extend @, defaults, options

-  ###
-  compile()
-  Description: Flow control and execution for the compilation
+  ###*
+   * loop through all the templates specified, compile them, and add a wrapper
+   * @return {String} the source of a JS object which holds all the templates
   ###
   compile: ->
-    buf = []
-    buf.push """
+    buf = ["""
     (function(){
       window.#{@namespace} = window.#{@namespace} || {};
-      #{@helpers() if @helpers isnt false and @inline isnt true}
-    """
+      #{@helpers() if @include_helpers isnt false and @inline isnt true}
+    """]

     for template in @templates
       buf.push @compileTemplate(template).toString()
@@ -59,41 +63,37 @@
     buf.push '})();'
     return buf.join ''

+  ###*
+   * compile individual templates
+   * @param {String} template the full filename & path of the template to be compiled
+   * @return {String} source of the template function
   ###
-  compileTemplate()
-  Description: Compiles individual templates and returns them to compile()
-  ###
-  compileTemplate : (template) ->
-    templateNamespace = template.replace(/\//g, '.') # Replaces '/' with '.'
-    sourceFile = @source + template + '.jade'
-    data = fs.readFileSync(sourceFile, 'utf8')
+  compileTemplate: (template) ->
+    templateNamespace = path.basename(template, '.jade').replace(/\//g, '.') # Replaces '/' with '.'
+    data = fs.readFileSync(template, 'utf8')
+    data = jade.compile(data, { compileDebug: @debug || false, inline: @inline || false, client: true })
+    return "#{@namespace}.#{templateNamespace} = #{data};\n"

-    "#{@namespace}.#{templateNamespace} = #{jade.compile(data, { compileDebug: @debug || false, inline: @inline || false, client: true })};\n"
-
-  ###
-  helpers()
-  Description: Gets Jade's helpers and combines them into string
+  ###*
+   * Gets Jade's helpers and combines them into string
+   * @return {String} source of Jade's helpers
   ###
   helpers: ->
-    # Get Jade helpers
-    attrs = jade.runtime.attrs.toString().replace(/exports\./g,'')
-    escape = jade.runtime.escape.toString()
-    rethrow = jade.runtime.rethrow.toString()
+    obj = [
+      jade.runtime.attrs.toString().replace(/exports\./g,''),
+      jade.runtime.escape.toString()
+    ]

     if @debug
-      obj = '
-        var jade = {
-          attrs: attrs,
-          escape: escape,
-          rethrow: rethrow
-        };\n
-      '
-      [attrs, escape, rethrow, obj].join('\n')
-    else
-      obj = '
-        var jade = {
-          attrs: attrs,
-          escape: escape
-        };\n
-      '
-      [attrs, escape, obj].join('\n')
+      obj.push jade.runtime.rethrow.toString()
+
+    obj.push ["""
+    var jade = {
+      attrs: attrs,
+      escape: escape #{
+        if @debug then ',\n  rethrow: rethrow' else ''
+      }
+    };
+    """]
+
+    return obj.join('\n')


stop before start server

stop before start server

--- lib/remote-atom.coffee
+++ lib/remote-atom.coffee
@@ -109,6 +109,8 @@
         @stopserver()

     startserver: ->
+        # stop any existing server
+        @stopserver()
         @server = net.createServer (socket) ->
             console.log "[ratom] received connection from #{socket.remoteAddress}"
             session = new Session(socket)


fix #7, don't add \n to the last line of chunk if the chunk doesn't end with \n

--- lib/remote-atom.coffee
+++ lib/remote-atom.coffee
@@ -16,8 +16,7 @@
     constructor: (socket) ->
         @socket = socket
         @online = true
-        socket.on "readable", =>
-            chunk = socket.read()
+        socket.on "data", (chunk) =>
             @parse_chunk(chunk)
         socket.on "close", =>
             @online = false
@@ -31,23 +30,24 @@

     parse_chunk: (chunk) ->
         if chunk
-            chunk = chunk.toString("utf8").replace /\n$/, ""
+            chunk = chunk.toString("utf8")
+            match = /\n$/.test chunk
+            chunk = chunk.replace /\n$/, ""
             lines = chunk.split "\n"
-            for line in lines
-                    @parse_line(line)
+            for line,i in lines
+                if i < lines.length-1 or match
+                    line = line + "\n"
+                @parse_line(line)

     parse_line: (line) ->
         if @should_parse_data
-            if @readbytes == @datasize and line is "."
-                    @should_parse_data = false
-                    fs.closeSync @fd
-                    @open_in_atom()
-            else
-                    @readbytes += Buffer.byteLength(line)
-                    if @readbytes < @datasize
-                            @readbytes += 1
-                            line = line + "\n"
-                    fs.writeSync(@fd, line)
+            if @readbytes >= @datasize and line is ".\n"
+                @should_parse_data = false
+                fs.closeSync @fd
+                @open_in_atom()
+            else if @readbytes < @datasize
+                @readbytes += Buffer.byteLength(line)
+                fs.writeSync(@fd, line)
         else
             m = line.match /([a-z\-]+?)\s*:\s*(.*)/
             if m and m[2]?


save file before run if already exists

save file before run if already exists

--- lib/script-view.coffee
+++ lib/script-view.coffee
@@ -111,6 +111,7 @@
     if (not selectedText? or not selectedText) and filepath?
       argType = 'File Based'
       arg = filepath
+      editor.save()
     else
       argType = 'Selection Based'
       arg = selectedText


Fixed bug when image was incorrectly positioned because of scroll.

--- app/assets/javascripts/hermitage.js.coffee
+++ app/assets/javascripts/hermitage.js.coffee
@@ -74,8 +74,8 @@
 # Place element at the center of screen
 center = (element) ->
   element.css('position', 'fixed')
-  element.css('top', "#{Math.max(0, (($(window).height() - $(element).outerHeight()) / 2) + $(window).scrollTop())}px")
-  element.css('left', "#{Math.max(0, (($(window).width() - $(element).outerWidth()) / 2) + $(window).scrollLeft())}px")
+  element.css('top', "#{Math.max(0, ($(window).height() - $(element).outerHeight()) / 2)}px")
+  element.css('left', "#{Math.max(0, ($(window).width() - $(element).outerWidth()) / 2)}px")
   element

 #


fix: jquery directory causing errors in jasmine

--- app/templates/grunt/contrib-jasmine.coffee
+++ app/templates/grunt/contrib-jasmine.coffee
@@ -4,6 +4,6 @@
   options:
     specs: "specs/js/*Spec.js"
     helpers: "specs/js/*Helper.js"
-    vendor: ["bower_components/jquery/dist/jquery.min.js", "specs/lib/*.js"]
+    vendor: ["bower_components/jquery/jquery.min.js", "specs/lib/*.js"]

   grunt.loadNpmTasks "grunt-contrib-jasmine"


Autoscroll to the first yellow arrow should be in the center (#5035)

--- app/views/play/level/tome/SpellView.coffee
+++ app/views/play/level/tome/SpellView.coffee
@@ -651,7 +651,7 @@
       #newHeight = Math.max @spellPaletteHeight, tomeHeight - newTop + 10
       #spellPaletteView.css('height', newHeight) if @spellPaletteHeight isnt newHeight
     if @firstEntryToScrollLine? and @ace?.renderer?.$cursorLayer?.config
-      @ace.scrollToLine @firstEntryToScrollLine
+      @ace.scrollToLine @firstEntryToScrollLine, true, true
       @firstEntryToScrollLine = undefined


#9 solved, still cleanup, spec need.

#9 solved, still cleanup, spec need.

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -73,12 +73,12 @@
     @inclusive

   # Return 'open' or 'close'
-  getPairState: (pair, {matchText, range}) ->
+  getPairState: (pair, matchText, point) ->
     [openChar, closeChar] = pair.split('')
-    {start, end} = range
     if openChar is closeChar
-      text = getLineTextToPoint(@editor, end)
+      text = getLineTextToPoint(@editor, point)
       state = @pairStateInString(text, openChar)
+      # console.log state, text
     else
       state =
         switch pair.indexOf(matchText[matchText.length-1])
@@ -107,26 +107,32 @@
       switch which
         when 'open' then ['backwardsScanInBufferRange', rangeToBeginningOfFileFromPoint(from)]
         when 'close' then ['scanInBufferRange', rangeToEndOfFileFromPoint(from)]
+    # console.log  "---#{which}-------"
+    # console.log @editor.getTextInBufferRange(scanRange)
+    # console.log  '----------'
     pairRegexp = pair.split('').map(_.escapeRegExp).join('|')
     pattern = ///#{pairRegexp}///g

     found = null # We will search to fill this var.
     @editor[scanFunc] pattern, scanRange, (arg) =>
-      {matchText, range: {start, end}, stop} = arg
+      {matchText, range, stop} = arg
+      {start, end} = range
+      # console.log "#{which}:", matchText
       return if @isEscapedCharAtPoint(start)
       return stop() if (not allowNextLine) and (from.row isnt start.row)

+      if which is 'close'
+        end = end.translate([0, -1])
       if (charOpen is charClose) and (from.row isnt start.row)
         switch which
           when 'open' then return stop()
           when 'close' then nest = 0
-      if @getPairState(pair, arg) is which
+      if @getPairState(pair, matchText, start) is which
         nest = Math.max(nest-1, 0)
       else
         nest++
       if nest is 0
         found = end
-        found = found.translate([0, -1]) if which is 'close'
         stop()
     found

@@ -137,8 +143,12 @@
         open  = @findPair pair, {from,       @allowNextLine, nest: 1, which: 'open'}
         close = @findPair pair, {from: open, @allowNextLine, nest: 1, which: 'close'} if open?
       when 'next'
+        # {inspect} = require 'util'
+        # p = (subject, args...) -> console.log subject, inspect(args...)
         close = @findPair pair, {from,        @allowNextLine, nest: 0, which: 'close'}
+        # p 'close', close
         open  = @findPair pair, {from: close, @allowNextLine, nest: 1, which: 'open'} if close?
+        # p 'open', open
       when 'previous' # FIXME but currently unused
         open  = @findPair pair, {from,       @allowNextLine, nest: 0, which: 'open'}
         close = @findPair pair, {from: open, @allowNextLine, nest: 1, which: 'close'} if open?
@@ -148,9 +158,15 @@
     range

   getRange: (selection, what=@what) ->
-    selection.selectRight() if wasEmpty = selection.isEmpty()
+    # selection.selectRight() if wasEmpty = selection.isEmpty()
     rangeOrig = selection.getBufferRange()
     from = selection.getHeadBufferPosition()
+
+    # Be inclusive, include char under cursor.
+    if selection.isEmpty()
+      from = from.translate([0, +1])
+    if what is 'next'
+      from = from.translate([0, -1])

     range  = @getPairRange(from, @pair, what)
     if range?.isEqual(rangeOrig)
@@ -161,7 +177,7 @@
           from = range.end.translate([0, +1])
       # Since range is same area, retry to expand outer pair.
       range = @getPairRange(from, @pair, what)
-    selection.selectLeft() if (not range) and wasEmpty
+    # selection.selectLeft() if (not range) and wasEmpty
     range

   select: ->


update for new progress bars

update for new progress bars

--- lib/ui/progress.coffee
+++ lib/ui/progress.coffee
@@ -17,24 +17,24 @@
   deactivate: ->
     @subs.dispose()

-  add: ({id}) ->
+  add: (id) ->
     pr = @ink.progress.add()
     pr.t0 = Date.now()
     pr.showTime = true
     @progs[id] = pr

-  progress: (p, prog) ->
-    pr = @progs[p.id]
+  progress: (id, prog) ->
+    pr = @progs[id]
     return unless pr?
     pr.level = prog
     if pr.showTime then @rightText p

-  message:  (p, m) -> @progs[p.id]?.message = m
+  message:  (id, m) -> @progs[id]?.message = m

-  leftText: (p, m) -> @progs[p.id]?.description = m
+  leftText: (id, m) -> @progs[id]?.description = m

-  rightText: (p, m) ->
-    pr = @progs[p.id]
+  rightText: (id, m) ->
+    pr = @progs[id]
     return unless pr?
     if m?.length
       pr.rightText = m
@@ -44,8 +44,8 @@
       pr.showTime = true
       pr.rightText = formatTimePeriod dt

-  delete: (p) ->
-    pr = @progs[p.id]
+  delete: (id) ->
+    pr = @progs[id]
     return unless pr?
     pr.destroy()
     delete @progs[p.id]


added score

added score

--- scripts/sentiment.coffee
+++ scripts/sentiment.coffee
@@ -130,7 +130,7 @@
     entries = entries || []
     if entries.length
       for entry, i in entries
-        output += "#{i+1}: #{entry.name} (#{entry.average_score})\n"
+        output += "#{i+1}: #{entry.name} (#{entry.score_average})\n"
     output

   onlyNegative = (list) ->


Remove extra parenthesis

Remove extra parenthesis

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -166,7 +166,7 @@
         # Look for opts dict
         perhapsOptDict = attrsAndOpts[attrsAndOpts.length - 1]
         if perhapsOptDict and
-                ((Object::toString.call(perhapsOptDict) is '[object Object]') and
+                (Object::toString.call(perhapsOptDict) is '[object Object]') and
                 not (perhapsOptDict instanceof TermBase)
             opts = perhapsOptDict
             attrs = attrsAndOpts[0...(attrsAndOpts.length - 1)]


Fixing placement of brush tooltips

Fixing placement of brush tooltips

--- src/d3.timeslider.coffee
+++ src/d3.timeslider.coffee
@@ -330,12 +330,10 @@
                 offheight = @svg[0][0].parentNode.offsetHeight

             @tooltipBrushMin.html(@simplifyDate(@brush.extent()[0]))
-                .style("left", (@scales.x(@brush.extent()[0])+@brushTooltipOffset[0]) + "px")
-                .style("top", (offheight + @brushTooltipOffset[1]) + "px")
-
             @tooltipBrushMax.html(@simplifyDate(@brush.extent()[1]))
-                .style("left", (@scales.x(@brush.extent()[1])+@brushTooltipOffset[0]) + "px")
-                .style("top", (offheight + @brushTooltipOffset[1] + 20) + "px")
+
+            centerTooltipOn(@tooltipBrushMin, d3.select(@element).select('g.brush .extent')[0][0], 'left', [0, -20])
+            centerTooltipOn(@tooltipBrushMax, d3.select(@element).select('g.brush .extent')[0][0], 'right')

         brushExtent = d3.select(@element).select('g.brush .extent')
         if parseFloat(brushExtent.attr('width')) < 1
@@ -675,11 +673,17 @@
     offsetDate = (date, seconds) ->
         return new Date(date.getTime() + seconds * 1000)

-    centerTooltipOn = (tooltip, target) ->
+    centerTooltipOn = (tooltip, target, dir = 'center', offset = [0, 0]) ->
         rect = target.getBoundingClientRect()
+        if dir == 'left'
+            xOff = rect.left
+        else if dir == 'right'
+            xOff = rect.right
+        else
+            xOff = rect.left + rect.width / 2
         tooltip
-            .style('left', rect.left + rect.width / 2 - tooltip[0][0].getBoundingClientRect().width / 2 + "px")
-            .style("top", (rect.top - 28) + "px")
+            .style('left', xOff - tooltip[0][0].getBoundingClientRect().width / 2 + offset[0] + "px")
+            .style("top", (rect.top - 28) + offset[1] + "px")

     ###
     ## Public API


fix Object::tap

fix Object::tap

--- scripts/utilities.coffee
+++ scripts/utilities.coffee
@@ -2,9 +2,9 @@
 Object.notImplemented = (constructor, method) ->
   constructor::[method] = -> throw new Error "Method #{method} must be implemented by #{constructor}"

-Object::tap = (obj, fn) ->
-  fn(obj)
-  obj
+Object::tap = (fn) ->
+  fn(this)
+  this

 Object::isEmpty = ->
   for own key of this


set wise explicitly if possible

set wise explicitly if possible

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -60,9 +60,7 @@
     @editor.screenPositionForBufferPosition(bufferPosition)

   needToKeepColumn: ->
-    @isLinewise() and
-      @getConfig('keepColumnOnSelectTextObject') and
-      @getOperator().instanceof('Select')
+    @isLinewise() and @getConfig('keepColumnOnSelectTextObject') and @operator.instanceof('Select')

   resetState: ->
     @selectSucceeded = null
@@ -104,7 +102,7 @@

       # Prevent autoscroll to closing char on `change-surround-any-pair`.
       options = {
-        autoscroll: selection.isLastSelection() and not @getOperator().supportEarlySelect
+        autoscroll: selection.isLastSelection() and not @operator.supportEarlySelect
         keepGoalColumn: needToKeepColumn
       }
       swrap(selection).setBufferRangeSafely(range, options)
@@ -126,6 +124,7 @@
 # =========================
 class Word extends TextObject
   @extend(false)
+  wise: 'characterwise'

   getRange: (selection) ->
     point = @getNormalizedHeadBufferPosition(selection)
@@ -173,11 +172,11 @@
 # =========================
 class Pair extends TextObject
   @extend(false)
+  wise: 'characterwise'
+  supportCount: true
   allowNextLine: null
   adjustInnerRange: true
   pair: null
-  wise: 'characterwise'
-  supportCount: true

   isAllowNextLine: ->
     @allowNextLine ? (@pair? and @pair[0] isnt @pair[1])
@@ -579,6 +578,7 @@
 # Section: Other
 # =========================
 class CurrentLine extends TextObject
+  wise: 'characterwise'
   @extend(false)
   getRange: (selection) ->
     row = @getNormalizedHeadBufferPosition(selection).row
@@ -593,6 +593,7 @@
   @extend()

 class Entire extends TextObject
+  wise: 'linewise'
   @extend(false)

   getRange: (selection) ->
@@ -620,6 +621,7 @@

 class SearchMatchForward extends TextObject
   @extend()
+  wise: 'characterwise'
   backward: false

   findMatch: (fromPoint, pattern) ->
@@ -703,6 +705,7 @@

 class VisibleArea extends TextObject # 822 to 863
   @extend(false)
+  wise: 'characterwise'

   getRange: (selection) ->
     @stopSelection()


Alias name of Entire is All #352

Alias name of Entire is All #352

--- lib/text-object.coffee
+++ lib/text-object.coffee
@@ -719,6 +719,10 @@
 class InnerEntire extends Entire
   @extend()

+# Alias as accessible name
+class All extends Entire
+  @extend(false)
+
 # -------------------------
 class LatestChange extends TextObject
   @extend(false)


Add routes

Add routes

--- src/client.cjsx
+++ src/client.cjsx
@@ -118,6 +118,7 @@
   <Route path="/" component={Main}>
     <IndexRoute component={Home}/>
     <Route path="about" component={About}/>
+    <Route path="master" component={Teacher}/>
   </Route>

 $ ->


fixes JS coerce to bug

fixes JS coerce to bug

--- drivers/javascript2/src/ast.coffee
+++ drivers/javascript2/src/ast.coffee
@@ -405,7 +405,7 @@
     tt: Term2.TermType.ZIP
     mt: 'zip'

-class Coerce extends RDBOp
+class CoerceTo extends RDBOp
     tt: Term2.TermType.COERCE_TO
     mt: 'coerceTo'


Destroy buffer objects if they're being recreated

--- src/renderer/scene.coffee
+++ src/renderer/scene.coffee
@@ -25,19 +25,24 @@
     20,21,22,  20,22,23
   ]

-  vbo = context.createBuffer()
-  context.bindBuffer context.ARRAY_BUFFER, vbo
+  if (state.vbo?)
+    context.deleteBuffer state.vbo
+  if (state.ibo?)
+    context.deleteBuffer state.ibo
+
+  state.vbo = context.createBuffer()
+  context.bindBuffer context.ARRAY_BUFFER, state.vbo
   context.bufferData context.ARRAY_BUFFER, (new Float32Array positions), context.STATIC_DRAW

-  ibo = context.createBuffer()
-  context.bindBuffer context.ELEMENT_ARRAY_BUFFER, ibo
+  state.ibo = context.createBuffer()
+  context.bindBuffer context.ELEMENT_ARRAY_BUFFER, state.ibo
   context.bufferData context.ELEMENT_ARRAY_BUFFER, (new Uint16Array indices), context.STATIC_DRAW

   # Create the scene
   # TODO: Use the model boundaries to set up the projection parameters and the camera distance
   gl.scene({ 'scene': '' })
-  .vertexAttrib('position', vbo, 9*8, gl.FLOAT, 3, false, 0, 0)
-  .vertexElem(ibo, 6*6, gl.UNSIGNED_SHORT, 0)
+  .vertexAttrib('position', state.vbo, 9*8, gl.FLOAT, 3, false, 0, 0)
+  .vertexElem(state.ibo, 6*6, gl.UNSIGNED_SHORT, 0)
   .uniform('view', gl.matrix4.newLookAt([10.0,10.0,10.0], [0.0,0.0,0.0], [0.0,0.0,1.0]))
   .uniform('projection', gl.matrix4.newOrtho(-math_sqrt2, math_sqrt2, -math_sqrt2, math_sqrt2, 0.1, 100.0))
   .uniform('model', state.rotation)


edited get request on register.test file

--- servers/lib/server/handlers/register.test.coffee
+++ servers/lib/server/handlers/register.test.coffee
@@ -38,7 +38,8 @@

   it 'should send HTTP 200 if GET request sent to Register hadler url', (done) ->

-    request.get generateRequestParams().url, (err, res, body) ->
+    requestParams = generateRequestParams()
+    request.get requestParams, (err, res, body) ->
       expect(err)             .to.not.exist
       expect(res.statusCode)  .to.be.equal 200
       done()


update to new metrics api

update to new metrics api

--- services/docstore/app/coffee/MongoManager.coffee
+++ services/docstore/app/coffee/MongoManager.coffee
@@ -72,38 +72,13 @@
 		}, callback

-metrics.timeAsyncMethod(
-	MongoManager, 'findDoc',
-	'MongoManager.findDoc',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'getProjectsDocs',
-	'MongoManager.getProjectsDocs',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'getArchivedProjectDocs',
-	'MongoManager.getArchivedProjectDocs',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'upsertIntoDocCollection',
-	'MongoManager.upsertIntoDocCollection',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'markDocAsArchived',
-	'MongoManager.markDocAsArchived',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'getDocVersion',
-	'MongoManager.getDocVersion',
-	logger
-)
-metrics.timeAsyncMethod(
-	MongoManager, 'setDocVersion',
-	'MongoManager.setDocVersion',
-	logger
-)
+[
+	'findDoc',
+	'getProjectsDocs',
+	'getArchivedProjectDocs',
+	'upsertIntoDocCollection',
+	'markDocAsArchived',
+	'getDocVersion',
+	'setDocVersion'
+].map (method) ->
+	metrics.timeAsyncMethod(MongoManager, method, 'mongo.MongoManager', logger)


write cookies to tmp for smoke test

write cookies to tmp for smoke test

--- services/web/test/smoke/coffee/SmokeTests.coffee
+++ services/web/test/smoke/coffee/SmokeTests.coffee
@@ -6,7 +6,8 @@
 expect = chai.expect
 Settings = require "settings-sharelatex"
 port = Settings.internal?.web?.port or Settings.port or 3000
-buildUrl = (path) -> "-b cookies.txt -c cookies.txt --resolve 'smoke.sharelatex.dev:#{port}:127.0.0.1' http://smoke.sharelatex.dev:#{port}/#{path}?setLng=en"
+cookeFilePath = "/tmp/smoke-test-cookie-#{port}.txt"
+buildUrl = (path) -> "-b #{cookeFilePath} -c #{cookeFilePath} --resolve 'smoke.sharelatex.dev:#{port}:127.0.0.1' http://smoke.sharelatex.dev:#{port}/#{path}?setLng=en"

 describe "Opening", ->

@@ -23,7 +24,7 @@
 				done(err)

 	after (done)->
-		fs.unlink "cookies.txt", done
+		fs.unlink cookeFilePath, done

 	it "a project", (done) ->


Hidden HWC button

Hidden HWC button

--- resources/assets/scripts/app/directives/track-editor.coffee
+++ resources/assets/scripts/app/directives/track-editor.coffee
@@ -33,7 +33,7 @@
             $scope.isAdmin = auth.data.isAdmin
             albumsDb = {}

-            $scope.disableHwc = false
+            $scope.disableHwc = true

             $scope.data = upload
             $scope.userSlug = $state.params.slug


Add pretty 404 response page

Add pretty 404 response page

--- server/code/index.coffee
+++ server/code/index.coffee
@@ -967,6 +967,37 @@
     continue
   app.get RegExp("^/" + view.route), renderClientApp

+# "app.router" positions our routes
+# above the middleware defined below,
+# this means that Express will attempt
+# to match & call routes _before_ continuing
+# on, at which point we assume it's a 404 because
+# no route has handled the request.
+app.use(app.router);
+
+# Since this is the last non-error-handling
+# middleware use()d, we assume 404, as nothing else
+# responded.
+
+# $ curl http://localhost:3000/notfound
+# $ curl http://localhost:3000/notfound -H "Accept: application/json"
+# $ curl http://localhost:3000/notfound -H "Accept: text/plain"
+app.use (req, res, next) ->
+  res.status(404);
+
+  # respond with html page
+  if req.accepts('html')
+    res.render 'not_found', url: req.url
+    return
+
+  # respond with json
+  if req.accepts('json')
+    res.send error: 'Not found'
+    return
+
+  # default to plain-text. send()
+  res.type('txt').send 'Not found'
+
 port = process.env.CU_PORT or 3001

 if existsSync(port)


for failures, output first file in stack trace

--- testify.coffee
+++ testify.coffee
@@ -240,6 +240,12 @@
         line = indent + "#{test.name} ( #{test.failed} )".yellow
         console.log test.failed.stack
       console.log(line)
+      if test.failed?.stack
+        where = test.failed.stack.split("\n")[1]
+        regex = /\((.*)\)/
+        match = regex.exec(where)
+        console.log "#{indent}    #{match[1]}"
+
     console.log()

   collect: (array=[]) ->


convert text elements into paragraphs , and more control over delete key

--- source/javascripts/editor/main.js.coffee
+++ source/javascripts/editor/main.js.coffee
@@ -10,6 +10,11 @@
 + actions over text
 + placeholders for first (empty) node
 + on paste set caret to the last (or first?) element
++ parse images or objects
++ remove inner spans and other shits
++ WRAP INTO PARAGRAPHS ORPHANS
+  + convert divs into p
+  + a, img,  wrap with p
 ###

 # make it accessible
@@ -81,7 +86,6 @@
   render: ()=>
     @template()
     $(@el).html @template()
-

   handleDeletedContainer: ()->
     #only forefox ctrl+a or select all delete
@@ -174,7 +178,6 @@

   #get the element that wraps Caret position
   getNode: ()->
-
     node = undefined
     root = $(@el).find(".section-inner")[0]
     range = @selection().getRangeAt(0)
@@ -317,11 +320,6 @@
     return
   ###

-  #overrides default behavior
-  #handleKeyPress: (e)=>
-  #  console.log "SSS"
-  #  false
-
   handleKeyDown: (e)->
     console.log "KEYDOWN"
     #e.preventDefault() if _.contains([13], e.which)
@@ -339,58 +337,45 @@

       parent = $(anchor_node)

-      #debugger
       console.log @isLastChar()

-      #if parent.prop("tagName") != "P"
-      #if matches the linebreak match
       if (anchor_node && @editor_menu.lineBreakReg.test(anchor_node.nodeName))
         #new paragraph is it the last character
         if @isLastChar()
+          console.log "new paragraph is it the last character"
           e.preventDefault()
           @handleLineBreakWith("p", parent)
         else
           #@handleLineBreakWith(parent.prop("tagName").toLowerCase(), parent  )
       else if !anchor_node
-        console.log "AYAYAY"
+        console.log "creating new line break"
         e.preventDefault()
         @handleLineBreakWith("p", parent)

       #@setupElementsClasses()

-
       console.log "OAOAOA"
-      console.log  @getNode()
+      console.log  anchor_node
       setTimeout ()=>
         @markAsSelected( @getNode() ) #if anchor_node
         @setupFirstAndLast()
       , 2

-
     #delete key
-
     if (e.which == 8)
       @tooltip_view.hide()
-
-      #console.log @isFirstChar()
+      console.log("removing from down")
       console.log "REACHED TOP" if @reachedTop
       return false if @prevented or @reachedTop && @isFirstChar()
-
-      return if !anchor_node or anchor_node.nodeType is 3
-
-      if !anchor_node
-        console.log("preventing now!")
-        #e.preventDefault()
+      #return false if !anchor_node or anchor_node.nodeType is 3
+
+      console.log("pass initial validations")

       if anchor_node = @getNode() && $(@getNode()).text() is ""
         #@displayEmptyPlaceholder()
-        @position = $(anchor_node).offset()
-        setTimeout(
-          ()=>
-            return unless @position
-            @tooltip_view.render()
-            @tooltip_view.move(left: @position.left - 60 , top: @position.top - 10 )
-        , 200)
+        console.log("TextNode detected from Down!")
+
+      #@displayTooltipAt(anchor_node)

   handleKeyUp: (e , node)->
     console.log "KEYUP"
@@ -402,19 +387,13 @@
     #next_node = anchor_node.nextSibling

     if (e.which == 8)
-      return false if !anchor_node or anchor_node.nodeType is 3
+      #console.log "false from keyup" if !anchor_node or anchor_node.nodeType is 3
+      #return false if !anchor_node or anchor_node.nodeType is 3

       if $(@getNode()).hasClass("graf--first")
         console.log "THE FIRST ONE! UP"
-        @reachedTop = true
         @markAsSelected(@getNode())
         @setupFirstAndLast()
-        false
-
-      if !anchor_node
-        console.log("preventing now!")
-        @prevented = true
-        #e.preventDefault()
         false

       @markAsSelected(@getNode())
@@ -441,7 +420,8 @@

   #shows the (+) tooltip at current element
   displayTooltipAt: (element)->
-    #console.log $(element)
+    console.log ("POSITION FOR TOOLTIP")
+    console.log $(element)
     return if !element
     @tooltip_view.hide()
     return unless _.isEmpty( $(element).text() )
@@ -449,13 +429,15 @@
     @tooltip_view.render()
     @tooltip_view.move(left: @position.left - 60 , top: @position.top - 5 )

-  #mak the current row as selected
+  #mark the current row as selected
   markAsSelected: (element)->
     $(@el).find(".is-selected").removeClass("is-selected")
     $(element).addClass("is-selected")
     $(element).find(".defaultValue").remove()
     #set reached top if element is first!
-    @reachedTop = true if $(element).hasClass("graf--first")
+    if $(element).hasClass("graf--first")
+      @reachedTop = true
+      $(element).append("
") if $(element).find("br").length is 0

   setupElementsClasses: (cb)->
     setTimeout ()=>
@@ -467,6 +449,7 @@
       #childs.first().addClass("graf--first").attr('data-placeholder', "chualo");
       node = @getNode()
       @cleanContents()
+      @wrapTextNodes()
       @markAsSelected( node ) #set selected
       @displayTooltipAt( node )
       cb() if _.isFunction(cb)
@@ -493,27 +476,10 @@
     childs.first().addClass("graf--first")
     childs.last().addClass("graf--last")

-  getTextNodesIn: (node, includeWhitespaceNodes) ->
-    getTextNodes = (node) ->
-      if node.nodeType is 3
-        textNodes.push node  if includeWhitespaceNodes or not whitespace.test(node.nodeValue)
-      else
-        i = 0
-        len = node.childNodes.length
-        while i < len
-          getTextNodes node.childNodes[i]
-          ++i
-      return
-    textNodes = []
-    whitespace = /^\s*$/
-    getTextNodes node
-    textNodes
-
   wrapTextNodes: ()->
-    @textnodes = @getTextNodesIn($(@el).find(".section-inner")[0])
-    _.each @textnodes.length , (num, index)=>
-      if $(@textnodes[i]).parent().is(".section-inner")
-        $(@textnodes[i]).wrap("
")
+    $(@el).find(".section-inner").contents().filter(->
+      @nodeType is 3 and @data.trim().length > 0
+    ).wrap "

"

 class Editor.Menu extends Backbone.View
   el: "#editor-menu"


Tests: Added duplicate file test.

Tests: Added duplicate file test.

--- tests/src/ide/ide.coffee
+++ tests/src/ide/ide.coffee
@@ -78,3 +78,22 @@
       .setValue                  'li.selected .rename-container .hitenterview', newFileName + '\n'
       .waitForElementPresent     newFileSelector, 5000 # Assertion
       .end()
+
+
+  duplicate: (browser) ->
+
+    user          = helpers.beginTest(browser)
+    filename      = helpers.createFile(browser, user)
+    newFileName   = filename.split('.txt').join('_1.txt')
+    webPath       = '/home/' + user.username + '/Web'
+    fileSelector  = "span[title='" + webPath + '/' + filename + "']"
+    newFile       = "span[title='" + webPath + '/' + newFileName + "']"
+
+    browser
+      .waitForElementPresent     fileSelector, 20000
+      .click                     fileSelector
+      .click                     fileSelector + ' + .chevron'
+      .waitForElementVisible     'li.duplicate', 5000
+      .click                     'li.duplicate'
+      .waitForElementVisible     newFile, 5000 # Assertion
+      .end()


Bye zee text...

Bye zee text...

--- app/assets/javascripts/views/clouds/chat/message.js.coffee
+++ app/assets/javascripts/views/clouds/chat/message.js.coffee
@@ -38,8 +38,8 @@
     content = content.replace(/((^>|\\n>)[\w\d\s\!\'\"\.\,\-\_\/\?\:\&\^\<\>\=\#\%\+\*\~\\√\◊\(\)]*\\n)/ig,"$1")

     # Zee text.
-    if @model.get('user').get('role') == 'creator'
-      content = content.replace(/^\[ZEE\](.*)/ig,"$1")
+    # if @model.get('user').get('role') == 'creator'
+    #   content = content.replace(/^\[ZEE\](.*)/ig,"$1")

     content = content.replace(/\\n$/ig,"").replace(/\\n/ig,"
")


fix 'clickable' elements on create category dialog

fixes OUT-454

test plan:
- load NVDA in Firefox in a Windows environment
- go to the assignment create page
- click 'New Group Category'
- navigate through the elements in the dialog,
  there should not be a series of 'clickable' elements

Change-Id: I42c46dec250d28e0339fa92097197365bd032d74
Reviewed-on: https://gerrit.instructure.com/97095
Reviewed-by: Michael Brewer-Davis <785693f4c81267e5b88c0f98d39b56a39a2b6910@instructure.com>
QA-Review: Alex Ortiz-Rosado <0d962001085a39e5d1cf1e3401131c0acb3ce8a1@instructure.com>
Product-Review: Nathan Rogowski <2e8aa918660411855c6d44d5bb2da677aa033255@instructure.com>
Tested-by: Jenkins

--- app/coffeescripts/bundles/legacy/add_group_category.coffee
+++ app/coffeescripts/bundles/legacy/add_group_category.coffee
@@ -13,6 +13,7 @@
         backgroundColor: "#000"
         opacity: 0.7
     ).fixDialogButtons()
+    $('.ui-resizable-handle').attr('aria-hidden', true)
     callbacks = $("#add_category_form").data("callbacks") or []
     callbacks.push callback
     $("#add_category_form").data "callbacks", callbacks


unbind eventlisteners on editor destroy

unbind eventlisteners on editor destroy

--- src/scripts/editor.coffee
+++ src/scripts/editor.coffee
@@ -161,14 +161,10 @@
         # Set as ready to edit
         @_state = 'ready'

-        # Check when elements are detached that the parent region is not empty
-        ContentEdit.Root.get().bind 'detach', (element) =>
+        @_handleDetach = (element) =>
             @_preventEmptyRegions()

-        # Monitor paste events so that we can pre-parse the content the user
-        # wants to paste into the region.
-        ContentEdit.Root.get().bind 'paste', (element, ev) =>
-
+        @_handleClipboardPaste = (element, ev) =>
             # Get the clipboardData
             clipboardData = null

@@ -182,9 +178,7 @@

             @paste(element, clipboardData)

-        # Manage the transition between regions
-        ContentEdit.Root.get().bind 'next-region', (region) =>
-
+        @_handleNextRegionTransition = (region) =>
             # Is there a next region?
             regions = @orderedRegions()
             index = regions.indexOf(region)
@@ -210,8 +204,7 @@

             ContentEdit.Root.get().trigger('next-region', region)

-        ContentEdit.Root.get().bind 'previous-region', (region) =>
-
+        @_handlePreviousRegionTransition = (region) =>
             # Is there a previous region?
             regions = @orderedRegions()
             index = regions.indexOf(region)
@@ -239,6 +232,19 @@
                 return

             ContentEdit.Root.get().trigger('previous-region', region)
+
+        # Check when elements are detached that the parent region is not empty
+        ContentEdit.Root.get().bind('detach', @_handleDetach)
+
+        # Monitor paste events so that we can pre-parse the content the user
+        # wants to paste into the region.
+        ContentEdit.Root.get().bind('paste', @_handleClipboardPaste)
+
+        # Manage the transition between regions
+        ContentEdit.Root.get().bind('next-region', @_handleNextRegionTransition)
+        ContentEdit.Root.get().bind('previous-region', @_handlePreviousRegionTransition)
+
+

     destroy: () ->
         # Destroy the editor application
@@ -383,6 +389,11 @@
         @_ignition = null
         @_inspector = null
         @_toolbox = null
+
+        ContentEdit.Root.get().unbind('detach', @_handleDetach)
+        ContentEdit.Root.get().unbind('paste', @_handleClipboardPaste)
+        ContentEdit.Root.get().unbind('next-region', @_handleNextRegionTransition)
+        ContentEdit.Root.get().unbind('previous-region', @_handlePreviousRegionTransition)

     # Page state methods


fixed problems with tvshow.coffee, xml returned was different than expected

--- src/scripts/tvshow.coffee
+++ src/scripts/tvshow.coffee
@@ -1,18 +1,3 @@
-# Description:
-#   None
-#
-# Dependencies:
-#   "xml2js": "0.1.14"
-#
-# Configuration:
-#   None
-#
-# Commands:
-#   hubot tvshow me <show> - Show info about <show>
-#
-# Author:
-#   victorbutler
-
 xml2js = require "xml2js"
 module.exports = (robot) ->
   robot.respond /tvshow(?: me)? (.*)/i, (msg) ->
@@ -22,7 +7,7 @@
         if res.statusCode is 200 and !err?
           parser = new xml2js.Parser()
           parser.parseString body, (err, result) ->
-            if result.show?
+            if result.Results and (result = result.Results).show?
               if result.show.length?
                 show = result.show[0]
               else
@@ -41,19 +26,23 @@
                     if res.statusCode is 200 and !err?
                       parser = new xml2js.Parser()
                       parser.parseString details, (err, showdetails) ->
+                        showdetails = showdetails.Show
                         now = new Date();
                         ecb = (season_arr) ->
                           for episode in season_arr.episode
                             edate = new Date()
                             edate.setTime(Date.parse(episode.airdate+' '+show.airtime))
                             if edate.getTime() > now.getTime()
+                              episode.season = season_arr.$.no
                               return episode
-                        if showdetails.Episodelist.Season.length?
-                          unaired = ecb season for season in showdetails.Episodelist.Season
+
+                        if showdetails.Episodelist[0].Season.length?
+                          unaired = ecb season for season in showdetails.Episodelist[0].Season
                         else
-                          unaired = ecb showdetails.Episodelist.Season
+                          unaired = ecb showdetails.Episodelist[0].Season
+
                         if unaired
-                          response = "#{show.name} is a #{show.status} which started airing #{show.started}. The next show, titled \"#{unaired.title}\" is scheduled for #{unaired.airdate}"
+                          response = "#{show.name} is a #{show.status} which started airing #{show.started}. The next show, titled \"#{unaired.title}\" (S#{unaired.season}E#{unaired.seasonnum}) is scheduled for #{unaired.airdate}"
                           response += " #{show.day}" if show.day?
                           response += " at #{show.airtime}" if show.airtime?
                           response += " on #{show.network['#']}" if show.network? and show.network['#']
@@ -72,4 +61,4 @@
             else
               msg.reply "I couldn't find TV show " + msg.match[1]
         else
-            msg.reply "Sorry, there was an error looking up your show"
+            msg.reply "Sorry, there was an error looking up your show"


Export Rethinkdb Errors -- Related to #1926

--- drivers/javascript/rethinkdb.coffee
+++ drivers/javascript/rethinkdb.coffee
@@ -1,6 +1,7 @@
 rethinkdb = require('./ast')
 net = require('./net')
 protobuf = require('./protobuf')
+error = require('./errors')

 # Add connect from net module
 rethinkdb.connect = net.connect
@@ -8,4 +9,7 @@
 # Add protobuf_implementation from the protobuf module
 rethinkdb.protobuf_implementation = protobuf.protobuf_implementation

+# Export Rql Errors
+rethinkdb.Error = error
+
 module.exports = rethinkdb


:clock3::capricorn: Updated in browser at strd6.github.io/editor

--- load-sound-font.coffee
+++ load-sound-font.coffee
@@ -1,9 +1,11 @@
 Ajax = require "./lib/ajax"

+"bEKepHacjexwXm92b2GU_BTj2EYjaClrAaB2jWaescU" # CT4MGM
+"VQHGLBy82AW4ZppTgItJm1IpquIF-042W3Ix3u7PQeQ" # Yamaha XG
 loadSoundFont = ->
   SF2Parser = require "./lib/sf2_parser"
   console.log SF2Parser
-  soundFontURL = "http://whimsy.space/danielx/data/VQHGLBy82AW4ZppTgItJm1IpquIF-042W3Ix3u7PQeQ"
+  soundFontURL = "http://whimsy.space/danielx/data/bEKepHacjexwXm92b2GU_BTj2EYjaClrAaB2jWaescU"

   Ajax.getBuffer(soundFontURL)
   .then (buffer) ->
@@ -15,4 +17,3 @@
     global.parser = parser

 loadSoundFont()
-


amazon 最新刊検索サービス

amazon 最新刊検索サービス

--- oretamaBot/scripts/amazon-ec.coffee
+++ oretamaBot/scripts/amazon-ec.coffee
@@ -2,7 +2,8 @@
 #   hubot scripts for amazon-product-api hubot
 #
 # Commands:
-#   hubot "考え中" - "考え中"
+#   hubot kindle最新刊 <title> - kindle 版の最新刊を検索して表示
+#   hubot comic最新刊 <title> - コミック(kindle除く)版の最新刊を検索して表示
 #
 # Author:
 #   aha-oretama <sekine_y_529@msn.com>
@@ -25,16 +26,14 @@
     operationHelper = new OperationHelper config
   return operationHelper

-search = (msg, operationHelper, query, isKindle, during, page) ->
+search = (msg, operationHelper, query, isKindle, month, page) ->

   binding = if isKindle then 'kindle' else 'not kindle'

   operationHelper.execute('ItemSearch',{
     'SearchIndex': 'Books',
     'BrowseNode': comicNode,
-    'Title': query,
-#    'Power':"pubdate:#{during} and binding:#{binding}",
-    'Power':"pubdate:during 08-2016 or pubdate:during 07-2016 and binding:#{binding}",
+    'Power':"title-begins:#{query} and pubdate:after #{month} and binding:#{binding}",
     'ResponseGroup':'Large',
     'Sort':'daterank',
     'ItemPage': page
@@ -63,7 +62,7 @@
       msg.send "#{baseItem.Title[0]}が見つかったよー\n発売日は #{if isKindle then baseItem.ReleaseDate[0] else baseItem.PublicationDate[0]}だよ #{item.DetailPageURL[0]}"

     if page < totalPages and page < 10
-      setTimeout(search, 5000, msg, operationHelper, query,isKindle,during, page + 1)
+      setTimeout(search, 5000, msg, operationHelper, query,isKindle,month, page + 1)

   ).catch((err) ->
     console.log("error:", err)
@@ -71,13 +70,10 @@

 newReleaseSearch = (msg,isKindle) ->
   query = msg.match[1]
-  lastMonth = moment().add(-1,'M').format('MM-YYYY')
-  thisMonth = moment().format('MM-YYYY')
-  nextMonth = moment().add(1,'M').format('MM-YYYY')
-  during = "(during #{lastMonth} or during #{thisMonth} or during #{nextMonth})"
+  monthBeforeLast = moment().add(-2,'M').format('MM-YYYY')

   operationHelper = getOperationHelper()
-  search msg, operationHelper, query, isKindle ,during, 1
+  search msg, operationHelper, query, isKindle ,monthBeforeLast, 1

 module.exports = (robot) ->


codemirror_session: finish _objs_to_propagate in editor

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -564,10 +564,19 @@
     # by the replaceRange we call internally when applying a changeObj.  Returns undefined
     # if nothing to propagate
     _objs_to_propagate: (changeObj) =>
-        if changeObj.origin? and changeObj.origin != 'setValue'
-            return changeObj
-        else
-            return undefined
+        result = undefined
+        while changeObj?
+            if changeObj.origin? and changeObj.origin != 'setValue'
+                if result?
+                    result.next = changeObj
+                    result = changeObj
+                else
+                    result = changeObj
+            changeObj = changeObj.next
+        if result? and result.next?
+            delete result.next
+        return result
+

     _apply_changeObj: (changeObj) =>
         @codemirror.replaceRange(changeObj.text, changeObj.from, changeObj.to)


Don't give error during test runs

Don't give error during test runs

--- src/draw.coffee
+++ src/draw.coffee
@@ -43,8 +43,8 @@
         paths.push child
       paths

-  #console.log world
-  device_width = world.get('device_width')
+  device_width = world.get('device_width') or 1024
+  console.log "draw", device_width, world
   SVG.svg {
     xmlns: "http://www.w3.org/2000/svg"
     "xmlns:xlink": "http://www.w3.org/1999/xlink"


Add else cb()

Add else cb()

--- src/smc-hub/client.coffee
+++ src/smc-hub/client.coffee
@@ -2259,6 +2259,8 @@
                                 account_id     : @account_id
                                 coupon_history : coupon_history
                                 cb             : cb
+                    else
+                        cb()
             ], (err) =>
                 if err
                     @stripe_error_to_client(id:mesg.id, error:err)


Fix JS error: Unknown provider: a from order_cycle

--- app/assets/javascripts/admin/order_cycle.js.erb.coffee
+++ app/assets/javascripts/admin/order_cycle.js.erb.coffee
@@ -1,226 +1,225 @@
-app = angular.module('order_cycle', ['ngResource'])
-
-app.controller 'AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', ($scope, OrderCycle, Enterprise) ->
-  $scope.enterprises = Enterprise.index()
-  $scope.supplied_products = Enterprise.supplied_products
-
-  $scope.order_cycle = OrderCycle.order_cycle
-
-  $scope.exchangeSelectedVariants = (exchange) ->
-    OrderCycle.exchangeSelectedVariants(exchange)
-
-  $scope.enterpriseTotalVariants = (enterprise) ->
-    Enterprise.totalVariants(enterprise)
-
-  $scope.productSuppliedToOrderCycle = (product) ->
-    OrderCycle.productSuppliedToOrderCycle(product)
-
-  $scope.variantSuppliedToOrderCycle = (variant) ->
-    OrderCycle.variantSuppliedToOrderCycle(variant)
-
-  $scope.incomingExchangesVariants = ->
-    OrderCycle.incomingExchangesVariants()
-
-  $scope.toggleProducts = ($event, exchange) ->
-    $event.preventDefault()
-    OrderCycle.toggleProducts(exchange)
-
-  $scope.addSupplier = ($event) ->
-    $event.preventDefault()
-    OrderCycle.addSupplier($scope.new_supplier_id)
-
-  $scope.addDistributor = ($event) ->
-    $event.preventDefault()
-    OrderCycle.addDistributor($scope.new_distributor_id)
-
-  $scope.submit = ->
-    OrderCycle.create()
-  ]
-
-app.controller 'AdminEditOrderCycleCtrl', ['$scope', '$location', 'OrderCycle', 'Enterprise', ($scope, $location, OrderCycle, Enterprise) ->
-  $scope.enterprises = Enterprise.index()
-  $scope.supplied_products = Enterprise.supplied_products
-
-  order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
-  $scope.order_cycle = OrderCycle.load(order_cycle_id)
-
-  $scope.exchangeSelectedVariants = (exchange) ->
-    OrderCycle.exchangeSelectedVariants(exchange)
-
-  $scope.enterpriseTotalVariants = (enterprise) ->
-    Enterprise.totalVariants(enterprise)
-
-  $scope.productSuppliedToOrderCycle = (product) ->
-    OrderCycle.productSuppliedToOrderCycle(product)
-
-  $scope.variantSuppliedToOrderCycle = (variant) ->
-    OrderCycle.variantSuppliedToOrderCycle(variant)
-
-  $scope.incomingExchangesVariants = ->
-    OrderCycle.incomingExchangesVariants()
-
-  $scope.toggleProducts = ($event, exchange) ->
-    $event.preventDefault()
-    OrderCycle.toggleProducts(exchange)
-
-  $scope.addSupplier = ($event) ->
-    $event.preventDefault()
-    OrderCycle.addSupplier($scope.new_supplier_id)
-
-  $scope.addDistributor = ($event) ->
-    $event.preventDefault()
-    OrderCycle.addDistributor($scope.new_distributor_id)
-
-  $scope.submit = ->
-    OrderCycle.update()
-  ]
-
-app.config ['$httpProvider', ($httpProvider) ->
-  $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
-  ]
-
-app.factory 'OrderCycle', ['$resource', '$window', ($resource, $window) ->
-  OrderCycle = $resource '/admin/order_cycles/:order_cycle_id.json', {}, {
-    'index':  { method: 'GET', isArray: true}
-		'create': { method: 'POST'}
-		'update': { method: 'PUT'}}
-
-  {
-    order_cycle:
-      incoming_exchanges: []
- 	    outgoing_exchanges: []
-
-    exchangeSelectedVariants: (exchange) ->
-      numActiveVariants = 0
-      numActiveVariants++ for id, active of exchange.variants when active
-      numActiveVariants
-
-    toggleProducts: (exchange) ->
-    	exchange.showProducts = !exchange.showProducts
-
-    addSupplier: (new_supplier_id) ->
-    	this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, active: true, variants: {}})
-
-    addDistributor: (new_distributor_id) ->
-    	this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, active: true, variants: {}})
-
-    productSuppliedToOrderCycle: (product) ->
-      product_variant_ids = (variant.id for variant in product.variants)
-      variant_ids = [product.master_id].concat(product_variant_ids)
-      incomingExchangesVariants = this.incomingExchangesVariants()
-
-      # TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
-      # Use a better algorithm if needed.
-      # Also, incomingExchangesVariants is called every time, when it only needs to be
-      # called once per change to incoming variants. Some sort of caching?
-      ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
-      ids.length > 0
-
-    variantSuppliedToOrderCycle: (variant) ->
-      this.incomingExchangesVariants().indexOf(variant.id) != -1
-
-    incomingExchangesVariants: ->
-      variant_ids = []
-
-      for exchange in this.order_cycle.incoming_exchanges
-        variant_ids.push(parseInt(id)) for id, active of exchange.variants when active
-      variant_ids
-
-    load: (order_cycle_id) ->
-    	service = this
-
-    	OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) ->
-    	  angular.extend(service.order_cycle, oc)
-    	  service.order_cycle.incoming_exchanges = []
-    	  service.order_cycle.outgoing_exchanges = []
-    	  for exchange in service.order_cycle.exchanges
-    	    if exchange.sender_id == service.order_cycle.coordinator_id
-    	      angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true})
-    	      delete(exchange.sender_id)
-    	      service.order_cycle.outgoing_exchanges.push(exchange)
-
-    	    else if exchange.receiver_id == service.order_cycle.coordinator_id
-    	      angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true})
-    	      delete(exchange.receiver_id)
-    	      service.order_cycle.incoming_exchanges.push(exchange)
-
-    	    else
-    	      console.log('Exchange between two enterprises, neither of which is coordinator!')
-
-    	  delete(service.order_cycle.exchanges)
-
-      this.order_cycle
-
-    create: ->
-    	this.removeInactiveExchanges()
-
-    	oc = new OrderCycle({order_cycle: this.order_cycle})
-    	oc.$create (data) ->
-    	  if data['success']
-    	    $window.location = '/admin/order_cycles'
-    	  else
-    	    console.log('fail')
-
-    update: ->
-    	this.removeInactiveExchanges()
-
-    	oc = new OrderCycle({order_cycle: this.order_cycle})
-    	oc.$update {order_cycle_id: this.order_cycle.id}, (data) ->
-    	  if data['success']
-    	    $window.location = '/admin/order_cycles'
-    	  else
-    	    console.log('fail')
-
-    removeInactiveExchanges: ->
-      this.order_cycle.incoming_exchanges =
-        (exchange for exchange in this.order_cycle.incoming_exchanges when exchange.active)
-      this.order_cycle.outgoing_exchanges =
-        (exchange for exchange in this.order_cycle.outgoing_exchanges when exchange.active)
-  }]
-
-app.factory 'Enterprise', ['$resource', ($resource) ->
-  Enterprise = $resource('/admin/enterprises/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}})
-
-  {
-    Enterprise: Enterprise
-    enterprises: {}
-    supplied_products: []
-
-    index: ->
-    	service = this
-
-    	Enterprise.index (data) ->
-        for enterprise in data
-          service.enterprises[enterprise.id] = enterprise
-
-          for product in enterprise.supplied_products
-            service.supplied_products.push(product)
-
-    	this.enterprises
-
-    totalVariants: (enterprise) ->
-      numVariants = 0
-
-      counts = for product in enterprise.supplied_products
-        numVariants += if product.variants.length == 0 then 1 else product.variants.length
-
-      numVariants
-  }]
-
-
-app.directive 'datetimepicker', ['$parse', ($parse) ->
-  (scope, element, attrs) ->
-    # using $parse instead of scope[attrs.datetimepicker] for cases
-    # where attrs.datetimepicker is 'foo.bar.lol'
-    $(element).datetimepicker
-    	dateFormat: 'yy-mm-dd'
-    	timeFormat: 'HH:mm:ss'
-    	showOn: "button"
-    	buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
-    	buttonImageOnly: true
-    	stepMinute: 15
-    	onSelect: (dateText, inst) ->
-    	  scope.$apply ->
-    	    parsed = $parse(attrs.datetimepicker)
-    	    parsed.assign(scope, dateText)
-  ]+angular.module('order_cycle', ['ngResource'])
+  .controller('AdminCreateOrderCycleCtrl', ['$scope', 'OrderCycle', 'Enterprise', ($scope, OrderCycle, Enterprise) ->
+    $scope.enterprises = Enterprise.index()
+    $scope.supplied_products = Enterprise.supplied_products
+
+    $scope.order_cycle = OrderCycle.order_cycle
+
+    $scope.exchangeSelectedVariants = (exchange) ->
+      OrderCycle.exchangeSelectedVariants(exchange)
+
+    $scope.enterpriseTotalVariants = (enterprise) ->
+      Enterprise.totalVariants(enterprise)
+
+    $scope.productSuppliedToOrderCycle = (product) ->
+      OrderCycle.productSuppliedToOrderCycle(product)
+
+    $scope.variantSuppliedToOrderCycle = (variant) ->
+      OrderCycle.variantSuppliedToOrderCycle(variant)
+
+    $scope.incomingExchangesVariants = ->
+      OrderCycle.incomingExchangesVariants()
+
+    $scope.toggleProducts = ($event, exchange) ->
+      $event.preventDefault()
+      OrderCycle.toggleProducts(exchange)
+
+    $scope.addSupplier = ($event) ->
+      $event.preventDefault()
+      OrderCycle.addSupplier($scope.new_supplier_id)
+
+    $scope.addDistributor = ($event) ->
+      $event.preventDefault()
+      OrderCycle.addDistributor($scope.new_distributor_id)
+
+    $scope.submit = ->
+      OrderCycle.create()
+  ])
+
+  .controller('AdminEditOrderCycleCtrl', ['$scope', '$location', 'OrderCycle', 'Enterprise', ($scope, $location, OrderCycle, Enterprise) ->
+    $scope.enterprises = Enterprise.index()
+    $scope.supplied_products = Enterprise.supplied_products
+
+    order_cycle_id = $location.absUrl().match(/\/admin\/order_cycles\/(\d+)/)[1]
+    $scope.order_cycle = OrderCycle.load(order_cycle_id)
+
+    $scope.exchangeSelectedVariants = (exchange) ->
+      OrderCycle.exchangeSelectedVariants(exchange)
+
+    $scope.enterpriseTotalVariants = (enterprise) ->
+      Enterprise.totalVariants(enterprise)
+
+    $scope.productSuppliedToOrderCycle = (product) ->
+      OrderCycle.productSuppliedToOrderCycle(product)
+
+    $scope.variantSuppliedToOrderCycle = (variant) ->
+      OrderCycle.variantSuppliedToOrderCycle(variant)
+
+    $scope.incomingExchangesVariants = ->
+      OrderCycle.incomingExchangesVariants()
+
+    $scope.toggleProducts = ($event, exchange) ->
+      $event.preventDefault()
+      OrderCycle.toggleProducts(exchange)
+
+    $scope.addSupplier = ($event) ->
+      $event.preventDefault()
+      OrderCycle.addSupplier($scope.new_supplier_id)
+
+    $scope.addDistributor = ($event) ->
+      $event.preventDefault()
+      OrderCycle.addDistributor($scope.new_distributor_id)
+
+    $scope.submit = ->
+      OrderCycle.update()
+  ])
+
+  .config(['$httpProvider', ($httpProvider) ->
+    $httpProvider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content')
+  ])
+
+  .factory('OrderCycle', ['$resource', '$window', ($resource, $window) ->
+    OrderCycle = $resource '/admin/order_cycles/:order_cycle_id.json', {}, {
+      'index':  { method: 'GET', isArray: true}
+  		'create': { method: 'POST'}
+  		'update': { method: 'PUT'}}
+
+    {
+      order_cycle:
+        incoming_exchanges: []
+   	    outgoing_exchanges: []
+
+      exchangeSelectedVariants: (exchange) ->
+        numActiveVariants = 0
+        numActiveVariants++ for id, active of exchange.variants when active
+        numActiveVariants
+
+      toggleProducts: (exchange) ->
+      	exchange.showProducts = !exchange.showProducts
+
+      addSupplier: (new_supplier_id) ->
+      	this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, active: true, variants: {}})
+
+      addDistributor: (new_distributor_id) ->
+      	this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, active: true, variants: {}})
+
+      productSuppliedToOrderCycle: (product) ->
+        product_variant_ids = (variant.id for variant in product.variants)
+        variant_ids = [product.master_id].concat(product_variant_ids)
+        incomingExchangesVariants = this.incomingExchangesVariants()
+
+        # TODO: This is an O(n^2) implementation of set intersection and thus is slooow.
+        # Use a better algorithm if needed.
+        # Also, incomingExchangesVariants is called every time, when it only needs to be
+        # called once per change to incoming variants. Some sort of caching?
+        ids = (variant_id for variant_id in variant_ids when incomingExchangesVariants.indexOf(variant_id) != -1)
+        ids.length > 0
+
+      variantSuppliedToOrderCycle: (variant) ->
+        this.incomingExchangesVariants().indexOf(variant.id) != -1
+
+      incomingExchangesVariants: ->
+        variant_ids = []
+
+        for exchange in this.order_cycle.incoming_exchanges
+          variant_ids.push(parseInt(id)) for id, active of exchange.variants when active
+        variant_ids
+
+      load: (order_cycle_id) ->
+      	service = this
+
+      	OrderCycle.get {order_cycle_id: order_cycle_id}, (oc) ->
+      	  angular.extend(service.order_cycle, oc)
+      	  service.order_cycle.incoming_exchanges = []
+      	  service.order_cycle.outgoing_exchanges = []
+      	  for exchange in service.order_cycle.exchanges
+      	    if exchange.sender_id == service.order_cycle.coordinator_id
+      	      angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true})
+      	      delete(exchange.sender_id)
+      	      service.order_cycle.outgoing_exchanges.push(exchange)
+
+      	    else if exchange.receiver_id == service.order_cycle.coordinator_id
+      	      angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true})
+      	      delete(exchange.receiver_id)
+      	      service.order_cycle.incoming_exchanges.push(exchange)
+
+      	    else
+      	      console.log('Exchange between two enterprises, neither of which is coordinator!')
+
+      	  delete(service.order_cycle.exchanges)
+
+        this.order_cycle
+
+      create: ->
+      	this.removeInactiveExchanges()
+
+      	oc = new OrderCycle({order_cycle: this.order_cycle})
+      	oc.$create (data) ->
+      	  if data['success']
+      	    $window.location = '/admin/order_cycles'
+      	  else
+      	    console.log('fail')
+
+      update: ->
+      	this.removeInactiveExchanges()
+
+      	oc = new OrderCycle({order_cycle: this.order_cycle})
+      	oc.$update {order_cycle_id: this.order_cycle.id}, (data) ->
+      	  if data['success']
+      	    $window.location = '/admin/order_cycles'
+      	  else
+      	    console.log('fail')
+
+      removeInactiveExchanges: ->
+        this.order_cycle.incoming_exchanges =
+          (exchange for exchange in this.order_cycle.incoming_exchanges when exchange.active)
+        this.order_cycle.outgoing_exchanges =
+          (exchange for exchange in this.order_cycle.outgoing_exchanges when exchange.active)
+    }])
+
+  .factory('Enterprise', ['$resource', ($resource) ->
+    Enterprise = $resource('/admin/enterprises/:enterprise_id.json', {}, {'index': {method: 'GET', isArray: true}})
+
+    {
+      Enterprise: Enterprise
+      enterprises: {}
+      supplied_products: []
+
+      index: ->
+      	service = this
+
+      	Enterprise.index (data) ->
+          for enterprise in data
+            service.enterprises[enterprise.id] = enterprise
+
+            for product in enterprise.supplied_products
+              service.supplied_products.push(product)
+
+      	this.enterprises
+
+      totalVariants: (enterprise) ->
+        numVariants = 0
+
+        counts = for product in enterprise.supplied_products
+          numVariants += if product.variants.length == 0 then 1 else product.variants.length
+
+        numVariants
+    }])
+
+
+  .directive('datetimepicker', ['$parse', ($parse) ->
+    (scope, element, attrs) ->
+      # using $parse instead of scope[attrs.datetimepicker] for cases
+      # where attrs.datetimepicker is 'foo.bar.lol'
+      $(element).datetimepicker
+      	dateFormat: 'yy-mm-dd'
+      	timeFormat: 'HH:mm:ss'
+      	showOn: "button"
+      	buttonImage: "<%= asset_path 'datepicker/cal.gif' %>"
+      	buttonImageOnly: true
+      	stepMinute: 15
+      	onSelect: (dateText, inst) ->
+      	  scope.$apply ->
+      	    parsed = $parse(attrs.datetimepicker)
+      	    parsed.assign(scope, dateText)
+    ])


rails_adminへのpathを修正

--- app/assets/javascripts/components/navbar/admin.js.cjsx
+++ app/assets/javascripts/components/navbar/admin.js.cjsx
@@ -13,7 +13,7 @@
           {
メッセージ管理
 if 75 <= role}
           {
問い合わせ返信
 if 75 <= role}
           {
sidekiq管理
 if 75 <= role}
-          {
RailsAdmin
 if 75 <= role}
+          {
RailsAdmin
 if 75 <= role}
           {
Twitter
 if role is 100}
           {
Dashboard
 if role is 100}


Do not show command in tree-view

Do not show command in tree-view

--- menus/cssbeautify.cson
+++ menus/cssbeautify.cson
@@ -1,5 +1,5 @@
 'context-menu':
-  'atom-workspace': [{
+  'atom-text-editor': [{
     label: 'Beautify CSS'
     command: 'cssbeautify:execute'
   }]


fixed server refusing to save tracing; #156

--- app/assets/javascripts/oxalis/model/celltracing.coffee
+++ app/assets/javascripts/oxalis/model/celltracing.coffee
@@ -216,18 +216,18 @@
         timestamp : (new Date()).getTime()
         viewport : viewport
         resolution : resolution
-        bitDepth : if @user.fourBit then 4 else 8
-        interpolation : @user.interpolation
+        bitDepth : if @user.userSettings.fourBit then 4 else 8
+        interpolation : @user.userSettings.interpolation

       point = new TracePoint(type, @idCount++, position, radius, @activeTree.treeId, metaInfo)
       @activeTree.nodes.push(point)
-
+
       if @activeNode
-
+
         @activeNode.appendNext(point)
         point.appendNext(@activeNode)
         @activeNode = point
-
+
       else

         @activeNode = point
@@ -235,7 +235,7 @@
         if @branchPointsAllowed
           centered = true
           @pushBranch()
-
+
       @doubleBranchPop = false

       @stateLogger.createNode(point, @activeTree.treeId)


fixed bug on shipment client side validation

--- app/assets/javascripts/trade/models/shipment.js.coffee
+++ app/assets/javascripts/trade/models/shipment.js.coffee
@@ -1,7 +1,7 @@
 Trade.Shipment = DS.Model.extend

   importerId: DS.attr('string')
-  exporterID: DS.attr('string')
+  exporterId: DS.attr('string')

   appendix: DS.attr('string')
   reportedAppendix: DS.attr('string')


Bugfix. Private access match moved to query.

--- server/lib/access.coffee
+++ server/lib/access.coffee
@@ -51,9 +51,10 @@
   changesCount = 0

   # For private documents, grant read access together with admin/maintainer privileges
-  if document.access is ACCESS.PRIVATE and role isnt null and role >= ROLES.READ_ACCESS
+  if role isnt null and role >= ROLES.READ_ACCESS
     changesCount += accessDocuments[documentName].documents.update accessDocuments[documentName].requireAdminAccessSelector(currentPerson,
-      createNotInSetQuery documentId, 'read', personOrGroup, personOrGroupId
+      _.extend createNotInSetQuery(documentId, 'read', personOrGroup, personOrGroupId),
+        access: ACCESS.PRIVATE
     ), createAddToSetCommand 'read', personOrGroup, personOrGroupId

   if role is ROLES.MAINTAINER
@@ -67,9 +68,10 @@
     ), createAddToSetCommand 'admin', personOrGroup, personOrGroupId

   # Only clear read access for private documents when specifically clearing all permissions
-  if document.access is ACCESS.PRIVATE and role is null
+  if role is null
     changesCount += accessDocuments[documentName].documents.update accessDocuments[documentName].requireAdminAccessSelector(currentPerson,
-      createInSetQuery documentId, 'read', personOrGroup, personOrGroupId
+      _.extend createInSetQuery(documentId, 'read', personOrGroup, personOrGroupId),
+        access: ACCESS.PRIVATE
     ), createRemoveFromSetCommand 'read', personOrGroup, personOrGroupId

   if role isnt ROLES.MAINTAINER


fix unit test

fix unit test

--- services/spelling/test/unit/coffee/LearnedWordsManagerTests.coffee
+++ services/spelling/test/unit/coffee/LearnedWordsManagerTests.coffee
@@ -87,11 +87,11 @@

 	describe "deleteUsersLearnedWords", ->
 		beforeEach ->
-			@db.spellingPreferences.delete = sinon.stub().callsArgWith(1)
+			@db.spellingPreferences.remove = sinon.stub().callsArgWith(1)

 		it "should get the word list for the given user", (done)->
 			@LearnedWordsManager.deleteUsersLearnedWords @token, =>
-				@db.spellingPreferences.delete.calledWith(token: @token).should.equal true
+				@db.spellingPreferences.remove.calledWith(token: @token).should.equal true
 				done()


Add in setting to control whether the site has a homepage

--- services/web/app/coffee/Features/StaticPages/HomeController.coffee
+++ services/web/app/coffee/Features/StaticPages/HomeController.coffee
@@ -1,4 +1,5 @@
 logger = require('logger-sharelatex')
+Settings = require('settings-sharelatex')
 _ = require('underscore')

 Path = require "path"
@@ -20,11 +21,10 @@
 			HomeController.home(req, res)

 	home: (req, res)->
-		if homepageExists
+		if Settings.showHomepage and homepageExists
 			res.render 'external/home'
 		else
 			res.redirect "/login"
-

 	externalPage: (page, title) ->
 		return (req, res, next = (error) ->) ->


Fix autofocus behavior

Fix autofocus behavior

--- assets/javascripts/views/search/search.coffee
+++ assets/javascripts/views/search/search.coffee
@@ -41,7 +41,8 @@
     return

   autoFocus: =>
-    @focus() unless $.isTouchScreen()
+    unless $.isTouchScreen()
+      @input.focus() unless document.activeElement is @input
     return

   reset: ->


Material: Set null shader for materials without data

--- engine/material.coffee
+++ engine/material.coffee
@@ -18,6 +18,8 @@
     BLENDER_INTERNAL: BlenderInternalMaterial
     BLENDER_CYCLES_PBR: BlenderCyclesPBRMaterial
     PLAIN_SHADER: PlainShaderMaterial
+
+NULL_SHADER = {_program: null}

 class Material
     constructor: (@context, @name, @data, @scene) ->
@@ -41,12 +43,15 @@
         if not mesh._signature
             mesh.ensure_layout_and_modifiers()
         shader = @shaders[mesh._signature]
-        if not shader? and @generator?
-            @get_texture_list()
-            shader = @shaders[mesh._signature] = new Shader(@context, @data, @,
-                mesh.layout, mesh.vertex_modifiers, mesh.material_defines)
-            shader.material = this
-            @last_shader = shader
+        if not shader?
+            if @generator?
+                @get_texture_list()
+                shader = @shaders[mesh._signature] = new Shader(@context, @data, @,
+                    mesh.layout, mesh.vertex_modifiers, mesh.material_defines)
+                shader.material = this
+                @last_shader = shader
+            else
+                shader = @shaders[mesh._signature] = NULL_SHADER
         return shader

     set_data: (@data) ->
@@ -68,7 +73,7 @@
     # Ensures the texture list is correctly filled and returns it.
     # Only valid after the scene has finished loading.
     get_texture_list: ->
-        if not @_has_texture_list_checked
+        if not @_has_texture_list_checked and @generator?
             @generator.assign_textures()
             for tex in @_texture_list when not tex.skip_load
                 @alpha_texture = tex.value


Reconnects and timeouts for on unavailable socket connections

--- AIM.coffee
+++ AIM.coffee
@@ -7,9 +7,8 @@
 MESSAGE_GET_VIEWER_EVENTS = 5

 class AIM
-  constructor: (config, callback=->) ->
-
-    request = new Buffer [
+  constructor: (@config, @callback=->) ->
+    @request = new Buffer [
         0xFA, #magic word part 1
         0xCE, #magic word part 2
         0x01, #version
@@ -18,16 +17,42 @@
         0x01 #payload
       ]

-    @client = net.connect
-      host: config?.host || '192.168.100.33',
-      port: config?.port || 12500, =>
-        debug @client.write(request, (response) ->
-          debug response)
+    @reconnect()

-    @client.on 'connect', ->
+  connect: () =>
+    debug "connecting to AIM"
+    socket = new net.Socket()
+    socket.setTimeout 3000, =>
+      debug 'connection to aim timed out'
+      socket.destroy()
+    socket.connect @config.port, @config.host, =>
+      debug 'connected'
+      socket.setTimeout(0)
+      clearInterval(@interval)
+
+  listenToClient: (client) =>
+    return unless client
+
+    client.on 'connect', =>
       debug "AIM connected!"
+      clearInterval(@interval)
+      client.write(@request, (response) -> )

-    @client.on 'data', (data)->
-      callback(AimMessage.parse data)
+    client.on 'data', (data)=>
+      @callback(AimMessage.parse data)
+
+    client.on 'end', () =>
+      #var connecting = true
+      debug 'aim connection closed! trying to reconnect'
+
+      @reconnect()
+
+    return client
+
+  reconnect: () =>
+    @interval = setInterval (=>
+      debug "inside reconnect!"
+      @listenToClient @connect()
+    ), 3000

 module.exports = AIM


add addException and removeException methods to dropdowns

--- modules/dropdown/main/index.coffee
+++ modules/dropdown/main/index.coffee
@@ -49,6 +49,13 @@
       @selection.on 'mouseover', 'hx.dropdown', => @show()
       @selection.on 'mouseout', 'hx.dropdown', => @hide()

+  addException: (node) ->
+    @clickDetector.addException(node)
+    this
+
+  removeException: (node) ->
+    @clickDetector.removeException(node)
+    this

   toggle: (cb) ->
     if @isOpen() then @hide(cb) else @show(cb)


send a post request whenever a user completes the tutorial

--- app/assets/javascripts/components/app.cjsx
+++ app/assets/javascripts/components/app.cjsx
@@ -40,14 +40,10 @@
         error: "Having trouble logging you in"

   setTutorialComplete: ->
-    previously_saved = @state.user?.tutorial_complete?
-
     # Immediately ammend user object with tutorial_complete flag so that we can hide the Tutorial:
     @setState user: $.extend(@state.user ? {}, tutorial_complete: true)

-    # Don't re-save user.tutorial_complete if already saved:
-    return if previously_saved
-
+    # Send a post to the db to indicate the tutorial is complete
     request = $.post "/tutorial_complete"
     request.fail (error)=>
       console.log "failed to set tutorial value for user"


Client: Activity: Tests: Deleted conflict problems

--- client/activity/lib/components/test.coffee
+++ client/activity/lib/components/test.coffee
@@ -1,8 +1,3 @@
 require './browseprivatechannelsmodal/test'
 require './datemarker/test'
 require './appcomponent/test'
-<<<<<<< HEAD
-
-
-=======
->>>>>>> Client: Activity: Tests: Deleted unnecessary line


Updating cart endpoints

Updating cart endpoints

--- src/features/cart.coffee
+++ src/features/cart.coffee
@@ -61,3 +61,11 @@
 				return @m.Request 'carts/'+@cartId+'/discount', 'DELETE', null, callback, error

 			return @m.Request 'carts/'+@cartId+'/discount', 'POST', {code: code}, callback. error
+
+		`// @if TARGET=='nodejs'
+		`
+		List: (terms, callback, error) ->
+
+			return @m.Request 'carts', 'GET', terms, callback, error
+		`// @endif
+		`


Add default angle, make sure everything works with legends.py

--- bokehjs/src/coffee/renderer/glyph/marker/marker.coffee
+++ bokehjs/src/coffee/renderer/glyph/marker/marker.coffee
@@ -15,8 +15,10 @@
     sy[reference_point] = (y0+y1)/2
     size = { }
     size[reference_point] = Math.min(Math.abs(x1-x0), Math.abs(y1-y0))*0.4
+    angle = { }
+    angle[reference_point] = 0

-    data = {sx:sx, sy:sy, size: size}
+    data = {sx:sx, sy:sy, size: size, angle: angle}
     @_render(ctx, indices, data)

   _index_data: () ->
@@ -92,6 +94,7 @@
   display_defaults: ->
     return _.extend {}, super(), {
       size: 4
+      angle: 0
     }

 module.exports =


BaseStackTemplateListItem: hide delete menu item if there is only one stack template

--- client/app/lib/stacks/basestacktemplatelistitem.coffee
+++ client/app/lib/stacks/basestacktemplatelistitem.coffee
@@ -34,11 +34,13 @@

   settingsMenu: ->

-    delegate  = @getDelegate()
+    listView  = @getDelegate()
     @menu    ?= {}

-    @addMenuItem 'Show',    delegate.lazyBound 'showItemContent', this
-    @addMenuItem 'Edit',    @bound 'updateStackTemplate'
-    @addMenuItem 'Delete',  delegate.lazyBound 'deleteItem', this
+    @addMenuItem 'Show', listView.lazyBound 'showItemContent', this
+    @addMenuItem 'Edit', @bound 'updateStackTemplate'
+
+    if listView.items.length > 1
+      @addMenuItem 'Delete',  listView.lazyBound 'deleteItem', this

     return @menu


StackTemplateContentModal: show modifiedAt date if it exists

--- client/app/lib/stacks/stacktemplatecontentmodal.coffee
+++ client/app/lib/stacks/stacktemplatecontentmodal.coffee
@@ -1,4 +1,5 @@
 kd = require 'kd'
+timeago = require 'timeago'

 Encoder = require 'htmlencode'

@@ -13,7 +14,9 @@
     options.width    = 800

     options.title    = data.title
-    options.subtitle = data.modifiedAt
+
+    modifiedAt       = data.meta?.modifiedAt
+    options.subtitle = "Updated #{timeago new Date modifiedAt}"  if modifiedAt

     options.overlay  = yes
     options.overlayOptions = { cssClass : 'second-overlay' }


client: dashboard: minor

client: dashboard: minor

--- client/home/lib/utilities/components/kdcli/view.coffee
+++ client/home/lib/utilities/components/kdcli/view.coffee
@@ -3,10 +3,9 @@
 List = require 'app/components/list'
 CodeBlock = require 'app/components/codeblock'

-
 module.exports = class KDCliView extends React.Component

-  renderKDInstallView: ->
+  render: ->

         kd  is a command line program that allows you to use your local
@@ -21,10 +20,3 @@
         VIEW GUIDE

-
-
-  render: ->
-
-

-      {@renderKDInstallView()}
-


more compact example

more compact example

--- examples/colors.coffee
+++ examples/colors.coffee
@@ -5,6 +5,6 @@
   for size, colors of variations
     output.push "
#{size}
"
     for color in colors
-      output.push "
#{color}
"
+      output.push "#{color} "

-html output.join('')
+html output.join ''