a few more tests

a few more tests

--- test_docopt.coffee
+++ test_docopt.coffee
@@ -79,32 +79,82 @@
 `with (require('./docopt')) { //`

 test "Option.parse", ->
-    eq Option.parse('-h'), new Option('-h', null)
-    eq Option.parse('-h'), new Option('-h', null)
-    eq Option.parse('--help'), new Option(null, '--help')
-    eq Option.parse('-h --help'), new Option('-h', '--help')
-    eq Option.parse('-h, --help'), new Option('-h', '--help')
+    eq(
+        Option.parse('-h')
+        new Option('-h', null)
+    )
+    eq(
+        Option.parse('-h')
+        new Option('-h', null)
+    )
+    eq(
+        Option.parse '--help'
+        new Option null, '--help'
+    )
+    eq(
+        Option.parse '-h --help'
+        new Option '-h', '--help'
+    )
+    eq(
+        Option.parse '-h, --help'
+        new Option '-h', '--help'
+    )

-    eq Option.parse('-h TOPIC'), new Option('-h', null, 1)
-    eq Option.parse('--help TOPIC'), new Option(null, '--help', 1)
-    eq Option.parse('-h TOPIC --help TOPIC'), new Option('-h', '--help', 1)
-    eq Option.parse('-h TOPIC, --help TOPIC'), new Option('-h', '--help', 1)
-    eq Option.parse('-h TOPIC, --help=TOPIC'), new Option('-h', '--help', 1)
+    eq(
+        Option.parse '-h TOPIC'
+        new Option '-h', null, 1
+    )
+    eq(
+        Option.parse '--help TOPIC'
+        new Option null, '--help', 1
+    )
+    eq(
+        Option.parse '-h TOPIC --help TOPIC'
+        new Option '-h', '--help', 1
+    )
+    eq(
+        Option.parse '-h TOPIC, --help TOPIC'
+        new Option '-h', '--help', 1
+    )
+    eq(
+        Option.parse '-h TOPIC, --help=TOPIC'
+        new Option '-h', '--help', 1
+    )

-    eq Option.parse('-h  Description...'), new Option('-h', null)
-    eq Option.parse('-h --help  Description...'), new Option('-h', '--help')
-    eq Option.parse('-h TOPIC  Description...'), new Option('-h', null, 1)
-
-    eq Option.parse('    -h'), new Option('-h', null)
-
-    eq Option.parse('-h TOPIC  Descripton... [default: 2]'),
-           new Option('-h', null, 1, '2')
-    eq Option.parse('-h TOPIC  Descripton... [default: topic-1]'),
-           new Option('-h', null, 1, 'topic-1')
-    eq Option.parse('--help=TOPIC  ... [default: 3.14]'),
-           new Option(null, '--help', 1, '3.14')
-    eq Option.parse('-h, --help=DIR  ... [default: ./]'),
-           new Option('-h', '--help', 1, "./")
+    eq(
+        Option.parse '-h  Description...'
+        new Option '-h', null
+    )
+    eq(
+        Option.parse '-h --help  Description...'
+        new Option '-h', '--help'
+    )
+    eq(
+        Option.parse '-h TOPIC  Description...'
+        new Option '-h', null, 1
+    )
+
+    eq(
+        Option.parse '    -h'
+        new Option '-h', null
+    )
+
+    eq(
+        Option.parse '-h TOPIC  Descripton... [default: 2]'
+        new Option '-h', null, 1, '2'
+    )
+    eq(
+        Option.parse '-h TOPIC  Descripton... [default: topic-1]'
+        new Option '-h', null, 1, 'topic-1'
+    )
+    eq(
+        Option.parse '--help=TOPIC  ... [default: 3.14]'
+        new Option null, '--help', 1, '3.14'
+    )
+    eq(
+        Option.parse '-h, --help=DIR  ... [default: ./]'
+        new Option '-h', '--help', 1, "./"
+    )

 test "TokenStream", ->
     eq new TokenStream(['-o', 'arg']), ['-o', 'arg']
@@ -113,27 +163,83 @@
     eq new TokenStream('-o arg').current(), '-o'

 test "parse_shorts", ->
-    eq(parse_shorts(new TokenStream('-a'), [new Option('-a')]),
-        [new Option('-a', null, 0, true)])
-    eq(parse_shorts(new TokenStream('-ab'), [new Option('-a'), new Option('-b')]),
-        [new Option('-a', null, 0, true), new Option('-b', null, 0, true)])
-    eq(parse_shorts(new TokenStream('-b'), [new Option('-a'), new Option('-b')]),
-        [new Option('-b', null, 0, true)])
-    eq(parse_shorts(new TokenStream('-aARG'), [new Option('-a', null, 1)]),
-        [new Option('-a', null, 1, 'ARG')])
-    eq(parse_shorts(new TokenStream('-a ARG'), [new Option('-a', null, 1)]),
-        [new Option('-a', null, 1, 'ARG')])
+    eq(
+        parse_shorts(
+            new TokenStream '-a'
+            [new Option '-a']
+        )
+        [new Option '-a', null, 0, true]
+    )
+    eq(
+        parse_shorts(
+            new TokenStream '-ab'
+            [
+                new Option '-a'
+                new Option '-b'
+            ]
+        )
+        [
+            new Option '-a', null, 0, true
+            new Option '-b', null, 0, true
+        ]
+    )
+    eq(
+        parse_shorts(
+            new TokenStream '-b'
+            [
+                new Option '-a'
+                new Option '-b'
+            ]
+        )
+        [new Option '-b', null, 0, true]
+    )
+    eq(
+        parse_shorts(
+            new TokenStream '-aARG'
+            [new Option '-a', null, 1]
+        )
+        [new Option '-a', null, 1, 'ARG']
+    )
+    eq(
+        parse_shorts(
+            new TokenStream '-a ARG'
+            [new Option '-a', null, 1]
+        )
+        [new Option '-a', null, 1, 'ARG']
+    )

 test "parse_long", ->
-    eq(parse_long(new TokenStream('--all'), [new Option(null, '--all')]),
-        [new Option(null, '--all', 0, true)])
-    eq(parse_long(new TokenStream('--all'), [new Option(null, '--all'),
-                                          new Option(null, '--not')]),
-        [new Option(null, '--all', 0, true)])
-    eq(parse_long(new TokenStream('--all=ARG'), [new Option(null, '--all', 1)]),
-        [new Option(null, '--all', 1, 'ARG')])
-    eq(parse_long(new TokenStream('--all ARG'), [new Option(null, '--all', 1)]),
-        [new Option(null, '--all', 1, 'ARG')])
+    eq(
+        parse_long(
+            new TokenStream '--all'
+            [new Option null, '--all']
+        )
+        [new Option null, '--all', 0, true]
+    )
+    eq(
+        parse_long(
+            new TokenStream '--all'
+            [
+                new Option null, '--all'
+                new Option null, '--not'
+            ]
+        )
+        [new Option null, '--all', 0, true]
+    )
+    eq(
+        parse_long(
+            new TokenStream '--all=ARG'
+            [new Option null, '--all', 1]
+        )
+        [new Option null, '--all', 1, 'ARG']
+    )
+    eq(
+        parse_long(
+            new TokenStream '--all ARG'
+            [new Option null, '--all', 1]
+        )
+        [new Option null, '--all', 1, 'ARG']
+    )

 test "parse_args", ->
     test_options = [new Option(null, '--all'), new Option('-b'), new Option('-W', null, 1)]
@@ -191,9 +297,14 @@
     doc = '''-h, --help  Print help message.
     -o FILE     Output file.
     --verbose   Verbose mode.'''
-    eq parse_doc_options(doc), [new Option('-h', '--help'),
-                                  new Option('-o', null, 1),
-                                  new Option(null, '--verbose')]
+    eq(
+        parse_doc_options doc
+        [
+            new Option '-h', '--help'
+            new Option '-o', null, 1
+            new Option null, '--verbose'
+        ]
+    )

 test "printable_and_formal_usage", ->
@@ -259,11 +370,13 @@

 test "parse_pattern", ->
-    o = [new Option('-h'),
-         new Option('-v', '--verbose'),
-         new Option('-f', '--file', 1)]
-    eq(
-        parse_pattern('[ -h ]', o),
+    o = [
+        new Option '-h'
+        new Option '-v', '--verbose'
+        new Option '-f', '--file', 1
+    ]
+    eq(
+        parse_pattern('[ -h ]', o)
         new Required [
             new Optional [
                 new Option '-h', null, 0, true
@@ -512,29 +625,122 @@
     )

 test "pattern_any_option", ->
-    eq new AnyOptions().match([new Option('-a')]), [true, [], []]
-    eq new AnyOptions().match([new Option('-b')]), [true, [], []]
-    eq new AnyOptions().match([new Option('-l', '--long')]), [true, [], []]
-    eq new AnyOptions().match([new Option(null, '--long')]), [true, [], []]
-    eq new AnyOptions().match([new Option('-a'), new Option('-b')]), [true, [], []]
-    eq new AnyOptions().match([new Option('-a'),
-                               new Option(null, '-long')]), [true, [], []]
+    eq(
+        new AnyOptions().match [
+            new Option '-a'
+        ]
+        [true, [], []]
+    )
+    eq(
+        new AnyOptions().match [
+            new Option '-b'
+        ]
+        [true, [], []]
+    )
+    eq(
+        new AnyOptions().match [
+            new Option '-l', '--long'
+        ]
+        [true, [], []]
+    )
+    eq(
+        new AnyOptions().match [
+            new Option null, '--long'
+        ]
+        [true, [], []]
+    )
+    eq(
+        new AnyOptions().match [
+            new Option '-a'
+            new Option '-b'
+        ]
+        [true, [], []]
+    )
+    eq(
+        new AnyOptions().match [
+            new Option '-a'
+            new Option null, '-long'
+        ]
+        [true, [], []]
+    )
     #eq not new AnyOptions().match([new Argument('N')])[0]

 test "pattern_either", ->
-    eq new Option('-a').either(), new Either([new Required([new Option('-a')])])
-    eq new Argument('A').either(), new Either([new Required([new Argument('A')])])
-    eq new Required([new Either([new Option('-a'), new Option('-b')]), new Option('-c')]).either(),\
-            new Either([new Required([new Option('-a'), new Option('-c')]),
-                   new Required([new Option('-b'), new Option('-c')])])
-    eq new Optional([new Option('-a'), new Either([new Option('-b'), new Option('-c')])]).either(),\
-            new Either([new Required([new Option('-b'), new Option('-a')]),
-                   new Required([new Option('-c'), new Option('-a')])])
-    eq new Either([new Option('-x'), new Either([new Option('-y'), new Option('-z')])]).either(), \
-            new Either([new Required([new Option('-x')]),
-                   new Required([new Option('-y')]),
-                   new Required([new Option('-z')])])
+    eq(
+        new Option('-a').either()
+        new Either [
+            new Required [
+                new Option '-a'
+            ]
+        ]
+    )
+    eq(
+        new Argument('A').either()
+        new Either [
+            new Required [
+                new Argument 'A'
+            ]
+        ]
+    )
+    eq(
+        new Required([
+            new Either [
+                new Option '-a'
+                new Option '-b'
+            ]
+            new Option '-c'
+        ]).either()
+        new Either [
+            new Required [
+                new Option '-a'
+                new Option '-c'
+            ]
+            new Required [
+                new Option '-b'
+                new Option '-c'
+            ]
+        ]
+    )
+    eq(
+        new Optional([
+            new Option '-a'
+            new Either [
+                new Option '-b'
+                new Option '-c'
+            ]
+        ]).either()
+        new Either [
+            new Required [
+                new Option '-b'
+                new Option '-a'
+            ]
+            new Required [
+                new Option '-c'
+                new Option '-a'
+            ]
+        ]
+    )
+    eq(
+        new Either([
+            new Option '-x'
+            new Either [
+                new Option '-y'
+                new Option '-z'
+            ]
+        ]).either()
+        new Either [
+            new Required [
+                new Option '-x'
+            ]
+            new Required [
+                new Option '-y'
+            ]
+            new Required [
+                new Option '-z'
+            ]
+        ]
+    )
     eq(
         new OneOrMore([
             new Argument 'N'
@@ -587,7 +793,10 @@

 test "pattern_fix_identities_1", ->
-    pattern = new Required([new Argument('N'), new Argument('N')])
+    pattern = new Required [
+        new Argument 'N'
+        new Argument 'N'
+    ]
     eq pattern.children[0], pattern.children[1]
     #pattern.children[0] isnt pattern.children[1]
     #pattern.fix_identities()
@@ -595,7 +804,13 @@

 test "pattern_fix_identities_2", ->
-    pattern = new Required([new Optional([new Argument('X'), new Argument('N')]), new Argument('N')])
+    pattern = new Required [
+        new Optional [
+            new Argument 'X'
+            new Argument 'N'
+        ]
+        new Argument 'N'
+    ]
     eq pattern.children[0].children[1], pattern.children[1]
     #assert pattern.children[0].children[1] is not pattern.children[1]
     #pattern.fix_identities()
@@ -645,20 +860,49 @@

 test "allow_double_underscore_in_pattern", ->
-    eq docopt('usage: prog [-o] [--] <arg>\n\n-o',
-           {'argv': '-- -o'}), new Dict([['-o', false], ['<arg>', '-o']])
+    eq(
+        docopt(
+            'usage: prog [-o] [--] <arg>\n\n-o',
+            'argv': '-- -o'
+        )
+        new Dict [
+            ['-o', false]
+            ['<arg>', '-o']
+        ]
+    )

 test "allow_empty_pattern", ->
-    docopt('usage: prog', {'argv': ' '})
+    docopt(
+        'usage: prog'
+        argv: ' '
+    )

 test "docopt", ->
     doc = '''Usage: prog [-v] A

     -v  Be verbose.'''
-    #eq docopt(doc, {'argv': 'arg'}), new Dict([['-v', false], ['A', 'arg']])
-    eq docopt(doc, {'argv': '-v arg'}), new Dict([['-v', true], ['A', 'arg']])
+    eq(
+        docopt(
+            doc
+            argv: 'arg'
+        )
+        new Dict [
+            ['-v', false]
+            ['A', 'arg']
+        ]
+    )
+    eq(
+        docopt(
+            doc
+            argv: '-v arg'
+        )
+        new Dict [
+            ['-v', true]
+            ['A', 'arg']
+        ]
+    )

     doc = """Usage: prog [-vqr] [FILE]
               prog INPUT OUTPUT
@@ -668,20 +912,47 @@
       -v        print status messages
       -q        report only file names [default: true]
       -r        show all occurrences of the same error
-      --help
+      --help    show this help

     """
-#    a = docopt(doc, '-v file.py')
-#    eq a, {'-v': true, '-q': true, '-r': null, '--help': null,
-#                 'FILE': 'file.py', 'INPUT': null, 'OUTPUT': null}
-
-#    a = docopt(doc, '-v')
-#    eq a, {'-v': true, '-q': true, '-r': null, '--help': null,
-#                 'FILE': null, 'INPUT': null, 'OUTPUT': null}
-
-#    a = docopt(doc, '-q')
-#    eq a, {'-v': null, '-q': false, '-r': null, '--help': null,
-#                 'FILE': null, 'INPUT': null, 'OUTPUT': null}
+    eq(
+        docopt(doc, argv: '-v file.py')
+        new Dict [
+            ['-v', true]
+            ['-q', false]
+            ['-r', false]
+            ['--help', false]
+            ['FILE', 'file.py']
+            ['INPUT', false]
+            ['OUTPUT', false]
+        ]
+    )
+
+    eq(
+        docopt(doc, argv: '-v')
+        new Dict [
+            ['-v', true]
+            ['-q', false]
+            ['-r', false]
+            ['--help', false]
+            ['FILE', false]
+            ['INPUT', false]
+            ['OUTPUT', false]
+        ]
+    )
+
+    eq(
+        docopt(doc, argv: '-q')
+        new Dict [
+            ['-v', false]
+            ['-q', true]
+            ['-r', false]
+            ['--help', false]
+            ['FILE', false]
+            ['INPUT', false]
+            ['OUTPUT', false]
+        ]
+    )

 #    with raises(DocoptExit):  # does not match
 #        docopt(doc, '-v input.py output.py')
@@ -689,7 +960,7 @@
 #    with raises(DocoptExit):
 #        docopt(doc, '--fake')
 #
-#    with raises(SystemExit):
+#    with raises(DocoptExit):
 #        docopt(doc, '--hel')

     #with raises(SystemExit):
@@ -698,8 +969,24 @@

 test "bug_not_list_argument_if_nothing_matched", ->
     d = 'usage: prog [NAME [NAME ...]]'
-#    eq docopt(d, 'a b'), {'NAME': ['a', 'b']}
-#    eq docopt(d, ''), {'NAME': []}
+    eq(
+        docopt(
+            d
+            argv: 'a b'
+        )
+        new Dict [
+            ['NAME', ['a', 'b']]
+        ]
+    )
+    eq(
+        docopt(
+            d
+            argv: ' '
+        )
+        new Dict [
+            ['NAME', []]
+        ]
+    )

 test "option_arguments_default_to_none", ->
@@ -709,7 +996,16 @@
     -m <msg>  Message

     """
-#    eq docopt(d, '-a'), {'-m': null, '-a': true}
+    eq(
+        docopt(
+            d
+            argv: '-a'
+        )
+        new Dict [
+            ['-m', false]
+            ['-a', true]
+        ]
+    )

 `}`


examples/kaleidoscope: fix check for function redefinition

--- examples/kaleidoscope.coffee
+++ examples/kaleidoscope.coffee
@@ -795,22 +795,13 @@

   F = TheModule.getOrInsertFunction(@name, FT, llvm.Function.ExternalLinkage)

-  # If F conflicted, there was already something named 'Name'.  If it has a
-  # body, don't allow redefinition or reextern.
-  if F.name != @name
-    # Delete the one we just made and get the existing one.
-    F.eraseFromParent()
-    F = TheModule.getFunction(Name)
-
-    # If F already has a body, reject this.
-    if F.empty()
-      Error("redefinition of function")
-      return null
-
-    # If F took a different number of args, reject.
-    if F.arguments.length != Args.length
-      Error("redefinition of function with different # args")
-      return null
+  # If F already has a body, reject this.
+  if F.basicBlocks.length
+    throw new Error("Redefinition of function")
+
+  # If F took a different number of args, reject.
+  if F.arguments.length != @args.length
+    throw new Error("Redefinition of function with different # args")

   # Set names for all arguments.
   for AI, i in F.arguments


added missing done() call to wire added fn to _popup

--- modules/gui/lib/editor/Editor.coffee
+++ modules/gui/lib/editor/Editor.coffee
@@ -28,6 +28,8 @@

                 @_buildButtons @__buttons
                 @_wireMouse()
+
+                done()

         ##
@@ -46,7 +48,7 @@
         ##
         _buildButtons: (buttons)->

-            _popup = (editor)=>
+            _popup = (editor, fn)=>
                 floyd.tools.gui.popup @,
                     type: editor.popup || 'gui.widgets.Popup'

@@ -55,7 +57,8 @@

                     view:
                         children: [ editor ]
-
+                , fn
+
             for action, conf of buttons
                 do(action, conf)=>


add setting to avoid optimisations outside docker

--- app/coffee/OutputCacheManager.coffee
+++ app/coffee/OutputCacheManager.coffee
@@ -172,8 +172,13 @@
 				logger.error err: err, src: src, dst: dst, "copy error for file in cache"
 				callback(err)
 			else
-				# call the optimiser for the file too
-				OutputFileOptimiser.optimiseFile src, dst, callback
+				if Settings.clsi?.optimiseInDocker
+					# don't run any optimisations on the pdf when they are done
+					# in the docker container
+					callback()
+				else
+					# call the optimiser for the file too
+					OutputFileOptimiser.optimiseFile src, dst, callback

 	_checkIfShouldCopy: (src, callback = (err, shouldCopy) ->) ->
 		return callback(null, !Path.basename(src).match(/^strace/))


JComputeStack: remove: use ::destroy instead

--- workers/social/lib/social/models/stack.coffee
+++ workers/social/lib/social/models/stack.coffee
@@ -328,8 +328,6 @@
       JProposedDomain  = require './domain'
       JMachine = require './computeproviders/machine'

-      instanceCount = (@getAt('machines') ? []).length
-
       @domains?.forEach (_id) ->
         JProposedDomain.one { _id }, (err, domain) ->
           if not err? and domain?
@@ -344,10 +342,7 @@
               if err then console.error \
                 "Failed to remove machine: #{machine.title}", err

-      ComputeProvider = require './computeproviders/computeprovider'
-      ComputeProvider.updateGroupResourceUsage {
-        group, change: 'decrement', instanceCount
-      }, => @unuseStackTemplate => @remove callback
+      @destroy => @remove callback

   delete$: permit


Fix open-flow REST api url

Fix open-flow REST api url

--- src/ext/modules/proxy.coffee
+++ src/ext/modules/proxy.coffee
@@ -412,7 +412,7 @@
     doPost uri, { value: JSON.stringify value, null, 2 }, unwrap go, (result) -> result.name

   requestUploadObject = (type, name, formData, go) ->
-    uri = "/3/NodePersistentStorage/#{encodeURIComponent type}"
+    uri = "/3/NodePersistentStorage.bin/#{encodeURIComponent type}"
     uri += "/#{encodeURIComponent name}" if name
     doUpload uri, formData, unwrap go, (result) -> result.name


reattach to video if it gets removed and rerendered

Former-commit-id: ca8e3e72b9114396ef425d16a580b1a2a8423587 [formerly 8e6003c9d029810e44187e130b9037086147b2eb] [formerly 6b9b62ad5b2b8a98207a688e3ac735b6972efbae [formerly 18885b630de276905c486adf20e9e4423d219c01]] [formerly 6bd60b104f6e07063badf0b5575a5746f2ba23da [formerly 20fdb1d29098093c4be67269db8f47f50696666f] [formerly a2abb1c27dc8dba6fb3f48582ff54b36991e587c [formerly a2abb1c27dc8dba6fb3f48582ff54b36991e587c [formerly 5b4034961092f529a5960603084c3225733f5f9e]]]]
Former-commit-id: 2002aeeb420622c0a7707971251f59f8ded8dffb [formerly 22296b3d297fe0dc5d45f4fcb748a69a44e7fb9a] [formerly 39a9691dbe979b7ba8695fa3b0c93c4275ea8f6c [formerly fd6e23ecec368754cf5c1ce9fabbd91a8ee16f06]]
Former-commit-id: cb93b437c3b4420370497903d8717010b80513c5 [formerly fa753971ee23d431c877c21e357285deb3115e63]
Former-commit-id: 45f399b7879c4403890114749c1ee85c3b18a76d
Former-commit-id: ed7729206a82a9f6101e02dfab85c732f68e46ff

--- @client/saas_landing_page.coffee
+++ @client/saas_landing_page.coffee
@@ -363,61 +363,61 @@
           src: asset("saas_landing_page/#{ui.video_file_name}.webm")
           type: "video/webm"

-
-
-
-
-
-
-
-  componentDidMount: ->
-
+
+  componentDidUpdate: -> @attachToVideo()
+  componentDidMount: -> @attachToVideo()
+
+  attachToVideo : ->
     # we use timeupdate rather than tracks / cue changes / vtt
     # because browser implementations are not complete (and often buggy)
     # and polyfills poor.
+
     v = @refs.video.getDOMNode()
-    v.addEventListener 'timeupdate', (ev) ->
-      chapter = fetch("video_chapter")
-      controls = fetch('video_controls')
-
-      text =
-        if v.currentTime < 5.5
-          "Proposals often require careful thought"
-        else if v.currentTime < 10.5
-          "The Pro/Con list encourages thinking about tradeoffs"
-        else if v.currentTime < 18
-          "Singular points describe a consideration"
-        else if v.currentTime < 25
-          "Learn and build from other\’s thoughts"
-        else if v.currentTime < 33.5
-          "Use a slider to express your conclusion"
-        else if v.currentTime < 39
-          "Integrate your considered opinion with the group"
-        else if v.currentTime < 43
-          "Histogram shows the spectrum of opinion"
-        else if v.currentTime < 46
-          "Points ranked by salience across all individuals"
-        else if v.currentTime < 55
-          "Now explore patterns of thought!"
-        else if v.currentTime < 61
-          "Understand the reservations of proposal opposers"
-        else if v.currentTime < 67.5
-          "Inspect what a particular individual believes"
-        else if v.currentTime < 72
-          "Figure out who has been persuaded by the top Pro"
-        else if v.currentTime < 87
-          "Drill into points for focused discussion"
-        else
-          ""
-
-      controls.value = v.currentTime / v.duration
-
-      save controls
-
-      if chapter.text != text
-        chapter.text = text
-        save chapter
-    , false
+
+    if @v != v
+      @v = v
+      v.addEventListener 'timeupdate', (ev) ->
+        chapter = fetch("video_chapter")
+        controls = fetch('video_controls')
+
+        text =
+          if v.currentTime < 5.5
+            "Proposals often require careful thought"
+          else if v.currentTime < 10.5
+            "The Pro/Con list encourages thinking about tradeoffs"
+          else if v.currentTime < 18
+            "Singular points describe a consideration"
+          else if v.currentTime < 25
+            "Learn and build from other\’s thoughts"
+          else if v.currentTime < 33.5
+            "Use a slider to express your conclusion"
+          else if v.currentTime < 39
+            "Integrate your considered opinion with the group"
+          else if v.currentTime < 43
+            "Histogram shows the spectrum of opinion"
+          else if v.currentTime < 46
+            "Points ranked by salience across all individuals"
+          else if v.currentTime < 55
+            "Now explore patterns of thought!"
+          else if v.currentTime < 61
+            "Understand the reservations of proposal opposers"
+          else if v.currentTime < 67.5
+            "Inspect what a particular individual believes"
+          else if v.currentTime < 72
+            "Figure out who has been persuaded by the top Pro"
+          else if v.currentTime < 87
+            "Drill into points for focused discussion"
+          else
+            ""
+
+        controls.value = v.currentTime / v.duration
+
+        save controls
+
+        if chapter.text != text
+          chapter.text = text
+          save chapter
+      , false


No more flash of timestamps

No more flash of timestamps

--- app/assets/javascripts/_classes/timeago.coffee
+++ app/assets/javascripts/_classes/timeago.coffee
@@ -3,12 +3,8 @@

 class @Moe.Timeago
   constructor: ->
-    $ @initialize
-
-
-  initialize: =>
+    @observe()
     @run()
-    @observe()

   run: (nodes = document.body) =>


Revert "Changed total_amount computation from tax*pretax into pretax*tax+pretax (#494)"

This reverts commit 353174350c3f10d6ff1fcc9467eb59f312401c25.

--- app/assets/javascripts/backend/trade.js.coffee
+++ app/assets/javascripts/backend/trade.js.coffee
@@ -118,7 +118,7 @@
       # Compute unit_pretax_amount
       updates.unit_pretax_amount = E.trade.round(values.pretax_amount / (values.quantity * (100.0 - values.reduction_percentage) / 100.0), 2)
       # Compute amount
-      updates.amount = E.trade.round(values.pretax_amount + (values.pretax_amount) * values.tax, 2)
+      updates.amount = E.trade.round(values.pretax_amount * values.tax, 2)
       E.trade.itemValues(item, updates)

     # Compute other amounts from amount


Bug: Remove a console statement I forgot about.

--- app/assets/javascripts/common_interface.coffee
+++ app/assets/javascripts/common_interface.coffee
@@ -35,8 +35,6 @@
   unless Modernizr.placeholder
     search      = $("#header_inside input[name=q]")
     placeholder = search.attr('placeholder')
-
-    console.log(placeholder)

     if search.val().length is 0
       search.val(placeholder)


Removing unused Family History Code

Removing unused Family History Code

--- app/assets/javascripts/familyhistory.js.coffee
+++ app/assets/javascripts/familyhistory.js.coffee
@@ -24,8 +24,3 @@
   @returns {CodedValue}
   ###
   onsetAge: -> new hQuery.Scalar @json['onsetAge']
-
-  ###*
-  @returns {CodedValue}
-  ###
-  recordedDatetime: -> hQuery.dateFromUtcSeconds @json['recordedDatetime']


improve anti-cache JS / turbolinks

improve anti-cache JS / turbolinks

--- app/assets/javascripts/folio/anti-cache.coffee
+++ app/assets/javascripts/folio/anti-cache.coffee
@@ -4,17 +4,20 @@
 write = (url, html) ->
   localStorage?.setItem?(url, html)

-swapItems = ($old, $new) ->
+swapItems = ($old, $new, immediate) ->
   $old.find('[data-anti-cache-item]').each ->
     $item = $(this)
     id = $item.data('anti-cache-item')
     replacement = $new.find("[data-anti-cache-item='#{id}']")
     $item.replaceWith(replacement)

-  setTimeout (-> $old.addClass('folio-anti-cache--done')), 0
+  if immediate
+    $old.addClass('folio-anti-cache--done')
+  else
+    setTimeout (-> $old.addClass('folio-anti-cache--done')), 0

 fetchFresh = ($el, url) ->
-  $.get url, (res) -> swapItems($el, $(res))
+  $.get url, (res) -> swapItems($el, $(res), false)

 performAntiCache = (e) ->
   $body = $(e.originalEvent.data.newBody || 'body')
@@ -24,7 +27,7 @@
     url = $el.data('anti-cache')
     value = read(url)

-    swapItems($el, $(value)) if value
+    swapItems($el, $(value), true) if value

     fetchFresh($el, url)

@@ -37,4 +40,3 @@
   .one 'turbolinks:load', performAntiCache
   .on 'turbolinks:before-render', performAntiCache
   .on 'turbolinks:before-cache', saveAntiCacheHtml
-


add autorefreh inbox count with multiplier drop off

--- app/assets/javascripts/notifications.js.coffee
+++ app/assets/javascripts/notifications.js.coffee
@@ -20,12 +20,14 @@
   #$('#inbox-container').load('ccn

 $ ->
-  load_inbox_count = ->
+  timeout_multiplier = 1
+  load_inbox_count = () =>
+    timeout_multiplier += 1
     $.ajax
       url: '/inbox/size',
       success: (count) ->
         $('#inbox-count').text(count);
       complete: ->
-        setTimeout(load_inbox_count, 60*1000);
+        setTimeout(load_inbox_count, timeout_multiplier*60*1000);

   load_inbox_count()


Updates $.visualization to async load layers and series (with spinner) + support for controls refreshing + handles async error + pretty loads legend.

--- app/assets/javascripts/visualization.js.coffee
+++ app/assets/javascripts/visualization.js.coffee
@@ -207,6 +207,31 @@
       this._refreshView()
       this._refreshControls()

+      @async_loading()
+
+    async_loading: ->
+      return unless @options.asyncUrl?
+
+      $.ajax
+        method: 'GET'
+        dataType: 'json'
+        url: @options.asyncUrl
+        beforeSend: () =>
+          @dynamic_loading = new L.Control(position: "bottomleft")
+          @dynamic_loading.onAdd = (map) =>
+            L.DomUtil.create('div', 'leaflet-dynamic loading')
+          @map.addControl @dynamic_loading
+        success: (data) =>
+          $.extend(true, @options, data)
+          @_refreshControls()
+        error: () =>
+          dynamic_error = new L.Control(position: "bottomleft")
+          dynamic_error.onAdd = (map) =>
+            L.DomUtil.create('div', 'leaflet-dynamic error')
+          @map.addControl dynamic_error
+        complete: () =>
+          @map.removeControl @dynamic_loading
+
     _destroy: ->
       @mapElement.remove()

@@ -251,6 +276,10 @@

     # Displays all given controls
     _refreshControls: ->
+      if @controls?
+        for name, control of @controls
+          @map.removeControl(control)
+      @controls = {}
       console.log "Refresh controls...", @options.controls
       unless @options and @options.controls?
         console.log "No controls..."
@@ -268,8 +297,8 @@

     _addFullscreenControl: (options) ->
       options = $.extend true, {}, @options.controlDefaults.fullscreen, options
-      control = new L.Control.FullScreen options
-      @map.addControl control
+      @controls.fullscreen = new L.Control.FullScreen options
+      @map.addControl @controls.fullscreen
       @map.on "enterFullscreen", (e) =>
         @map.scrollWheelZoom.enable();

@@ -279,8 +308,8 @@

     _addGeocoderControl: (options) ->
       options = $.extend true, {}, @options.controlDefaults.geocoder, options
-      control = new L.Control.OSMGeocoder options
-      @map.addControl control
+      @controls.geocoder = new L.Control.OSMGeocoder options
+      @map.addControl @controls.geocoder

     _addLayerSelectorControl: (options) ->
       baseLayers = {}
@@ -321,10 +350,13 @@

         overlays[layer.name] = L.tileLayer(layer.url, opts)

-      legendControl = new L.control(position: "bottomright")
-      legendControl.onAdd = (map) ->
+      @controls.legendControl = new L.control(position: "bottomright")
+      @controls.legendControl.onAdd = (map) ->
         L.DomUtil.create('div', 'leaflet-legend-control')
-      @map.addControl legendControl
+      @map.addControl @controls.legendControl
+
+      L.DomUtil.addClass(@controls.legendControl.getContainer(), 'leaflet-hidden-control')
+

       $('.leaflet-legend-control').on 'click', () ->
         $(this).find('#legend-activity').toggleClass('minified')
@@ -359,8 +391,9 @@
             if bounds.getNorthEast().equals bounds.getSouthWest()
               @map.setZoom 18
           # Add legend
-          legend = legendControl.getContainer()
+          legend = @controls.legendControl.getContainer()
           legend.innerHTML += renderedLayer.buildLegend()
+          L.DomUtil.removeClass(legend, 'leaflet-hidden-control')
         else
           console.warn "Cannot add layer #{layer.type}"

@@ -369,34 +402,34 @@
       @map.on "overlayadd", (event) =>
         @layersScheduler.schedule event.layer
         console.log "Add legend control..."
-        legend = $(legendControl.getContainer())
+        legend = $(@controls.legendControl.getContainer())
         legend.children("#legend-#{event.layer.name}").show()
         legend.children(".first").removeClass("first")
         legend.children(":visible:first").addClass("first")
         legend.removeClass("empty")
         return

-      @map.on "overlayremove", (event) ->
+      @map.on "overlayremove", (event) =>
         console.log "Remove legend control..."
-        legend = $(legendControl.getContainer())
+        legend = $(@controls.legendControl.getContainer())
         legend.children("#legend-#{event.layer.name}").hide()
         legend.children(".first").removeClass("first")
         legend.children(":visible:first").addClass("first")
         legend.addClass("empty") if legend.children(":visible").length <= 0
         return

-      control = new L.Control.Layers(baseLayers, overlays, @options.controlDefaults.layerSelector)
-      @map.addControl control
+      @controls.layerSelector = new L.Control.Layers(baseLayers, overlays, @options.controlDefaults.layerSelector)
+      @map.addControl @controls.layerSelector

     _addScaleControl: (options) ->
       options = $.extend true, {}, @options.controlDefaults.scale, options
-      control = new L.Control.Scale options
-      @map.addControl control
+      @controls.scale = new L.Control.Scale options
+      @map.addControl @controls.scale

     _addZoomControl: (options) ->
       options = $.extend true, {}, @options.controlDefaults.zoom, options
-      control = new L.Control.Zoom options
-      @map.addControl control
+      @controls.zoom = new L.Control.Zoom options
+      @map.addControl @controls.zoom

     # Build a popup from given parameters. For now it only uses popup attribute of


Don't remove the paragraph button when the paragraph is not visible, that does tether for us

--- app/js/jail_iframe/classes/icon_buttons.coffee
+++ app/js/jail_iframe/classes/icon_buttons.coffee
@@ -97,7 +97,7 @@
     @destroy() unless @_valid?

   _valid: =>
-    @$paragraph.find('.factlink').length <= 0 && @$paragraph.is(':visible')
+    @$paragraph.find('.factlink').length <= 0

   _textFromElement: (element) ->
     selection = document.getSelection()


Update lion_female.coffee

Update lion_female.coffee

--- app/lib/field-guide-content/lion_female.coffee
+++ app/lib/field-guide-content/lion_female.coffee
@@ -3,7 +3,7 @@
     Lions are iconic African large cats, built to prey on animals many times their size. Their coat is a smooth tawny color with whitish underparts. They have a muscular build, heavy muzzle, strong jaws, large paws, and a long tail that ends in a black tassel. They are sexually dimorphic, with males being larger in size and having full manes that vary in color from blond to black. Cubs have spots that tend to fade by 3 months of age.
   '''

-  scientificName: '(Panthera leo)'
+  scientificName: 'Panthera leo'
   mainImage: 'assets/fieldguide-content/mammals/lion/lion-feature.jpg'#
   conservationStatus: 'Near Threatened' # Options are Least Concern, Near Threatened, Vulnerable, and Endangered; all have their own little icons as well.#

@@ -15,7 +15,7 @@
     value: '1.1-1.2 m'
   }, {
     label: 'Weight'
-    value: '260 kg'
+    value: '120-200 kg'
   }, {
     label: 'Lifespan'
     value: '10-14 years in the wild; up to 20 years in captivity'


Check that file exists before opening

Check that file exists before opening

--- lib/archive-editor.coffee
+++ lib/archive-editor.coffee
@@ -13,8 +13,9 @@

   @activate: ->
     atom.workspace.registerOpener (filePath='') ->
-      # Don't open URIs with a protocol such as http:
-      if not url.parse(filePath).protocol and archive.isPathSupported(filePath)
+      # Check that the file path exists before opening in case something like
+      # an http: URI is being opened.
+      if archive.isPathSupported(filePath) and fs.isFileSync(filePath)
         new ArchiveEditor(path: filePath)

   constructor: ({path}) ->


add auth docs

add auth docs

--- lib/argumenta/auth.coffee
+++ lib/argumenta/auth.coffee
@@ -19,10 +19,11 @@
   #
   #     auth = new Auth( argumenta )
   #
-  constructor: (@argumenta, options) ->
+  # @param [Argumenta] argumenta Provides storage access.
+  constructor: (@argumenta) ->

-  # Prototype
-  # ---------
+  # Instance Methods
+  # ----------------

   # Verify login credentials `username` and `password` match a user account:
   #
@@ -30,14 +31,19 @@
   #       if result and not err
   #           console.log 'verified!'
   #
+  # @param [String]   username The login username.
+  # @param [String]   password The login password.
+  # @param [Function] callback(err, result) Called on success or error.
+  # @param [AuthError|StorageError] err Any auth or storage error.
+  # @param [Boolean]  result The verification result.
   verifyLogin: (username, password, callback) ->

     @argumenta.storage.getPasswordHash username, (err, hash) ->
       return callback err, null if err

-      Auth.verifyPassword password, hash, (err, verifyResult) ->
+      Auth.verifyPassword password, hash, (err, result) ->
         return callback err, null if err
-        return callback null, verifyResult
+        return callback null, result

   # Static Methods
   # --------------
@@ -49,7 +55,11 @@
   #       if not err                 # success
   #         typeof hash is 'string'  # true, it's the bcrypt hash
   #
-  # See `Auth.bcryptCost`.
+  # @see Auth.bcryptCost
+  # @param [String]    password The password to hash.
+  # @param [Function]  callback(err, hash) Called on completion or error.
+  # @param [AuthError] err Wraps any bcrypt error; otherwise null.
+  # @param [String]    hash Passed to callback on success.
   @hashPassword: (password, callback) ->

     cost = Auth.bcryptCost
@@ -69,11 +79,11 @@
   #         else
   #           console.log 'password incorrect!'
   #
-  # - `password`           _String_    The password to verify.
-  # - `hash`               _String_    The original bcrypt hash.
-  # - `callback(err, res)` _Function_  Called on completion or error.
-  # - `err`                _AuthError_ An AuthError wrapping any bcrypt error, or null.
-  # - `res`                _Boolean_   The verification result.
+  # @param [String]     password The password to verify.
+  # @param [String]     hash The original bcrypt hash.
+  # @param [Function]   callback(err, res) Called on completion or error.
+  # @param [AuthError]  err Wraps any bcrypt error; otherwise null.
+  # @param [Boolean]    res The verification result.
   @verifyPassword: (password, hash, callback) ->

     bcrypt.compare password, hash, (err, res) ->


Error --> Warning toxWorker send message

--- lib/atox-toxWorker.coffee
+++ lib/atox-toxWorker.coffee
@@ -283,7 +283,7 @@
     try
       return @TOX.sendFriendMessageSync e.fID, e.msg
     catch e
-      @err "Failed to send MSG to #{e.fID}"
+      @warn "Failed to send MSG to #{e.fID}"
       return -1

   onlineStatus: (newS) ->


in a content entry popup, the content of the rich text editor input was not sent when clicking directly in the save button of the popup

--- app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee
+++ app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee
@@ -15,6 +15,8 @@
     return @

   save: (event) ->
+    @touch_richtexteditor()
+
     @save_in_ajax event,
       headers:  { 'X-Flash': true }
       on_success: (response, xhr) =>
@@ -88,5 +90,9 @@
   enable_many_to_many_fields: ->
     # disabled in a popup form

+  touch_richtexteditor: ->
+    _.each @$('li.input.rte textarea.html'), (textarea) =>
+      $(textarea).tinymce().save()
+
   tinyMCE_settings: ->
     window.Locomotive.tinyMCE.popupSettings


Fixed regression which prevented actions on modal buttons to be triggered

--- app/scripts/components/BsModalComponent.coffee
+++ app/scripts/components/BsModalComponent.coffee
@@ -115,6 +115,7 @@
             title: title
             manual: true
             footerButtons: footerButtons
+            targetObject: controller
         )

         if Ember.typeOf(view) is 'string'


Simpler default values

Simpler default values

--- src/TransloaditClient.coffee
+++ src/TransloaditClient.coffee
@@ -133,8 +133,7 @@

     form.append "params", jsonParams

-    if !fields?
-      fields = []
+    fields ?= []

     for key of fields
       val = fields[key]
@@ -163,9 +162,7 @@
     return url

   _getBoredInstance: (url, customBoredLogic, cb) ->
-    if url == null
-      url = @_serviceUrl() + "/instances/bored"
-
+    url ?= @_serviceUrl() + "/instances/bored"
     opts =
       url     : url
       timeout : 5000
@@ -231,14 +228,9 @@
       cb null, url

   _prepareParams: (params) ->
-    if !params?
-      params = {}
-
-    if !params.auth?
-      params.auth = {}
-
-    if !params.auth.key?
-      params.auth.key = @_authKey
+    params          ?= {}
+    params.auth     ?= {}
+    params.auth.key ?= @_authKey

     params.auth.expires = @_getExpiresDate()
     return JSON.stringify params


Fix problem where renew button will show if no checkout items have nonzero renewals remaining

--- src/account/checkouts.coffee
+++ src/account/checkouts.coffee
@@ -95,14 +95,6 @@
 							circ_type: type
 						}

-						# Show action buttons as necessary.
-						# FIXME: should consider circ.renewal_remaining.
-						if type is 'out'
-							if $renew_all.is ':visible'
-								$renew_some.show()
-							else
-								$renew_all.show()
-
 						((type, $x) ->
 							$('.status_line', $x).openils 'checkout status', 'circ.retrieve.authoritative', circ_id, (circ) ->
 								show_status_line.call @.parent(), circ
@@ -113,6 +105,9 @@
 								# Disable items that cannot be renewed.
 								if circ.renewal_remaining is 0
 									$x.find(':checkbox').attr 'disabled', true
+								# Show relevant action buttons.
+								if type is 'out' and circ.renewal_remaining > 0
+									if $renew_all.is ':visible' then $renew_some.show() else $renew_all.show()
 						) type, $("#circ_id_#{circ_id}")

 			return false


don't pass empty string to rightLabel

don't pass empty string to rightLabel

--- lib/atom-ternjs-provider.coffee
+++ lib/atom-ternjs-provider.coffee
@@ -73,7 +73,6 @@
               replacementPrefix: prefix
               className: null
               type: obj._typeSelf
-              rightLabel: " "
               leftLabel: obj.leftLabel
               snippet: obj._snippet
               _rightLabelDoc: obj.rightLabelDoc


Logging.

Logging.

--- src/adapters/campfire.coffee
+++ src/adapters/campfire.coffee
@@ -38,9 +38,12 @@
             author.room = room
             callback id, created, room, user, body, author

-    withSelf = (callback) -> (id, created, room, user, body) ->
-        console.log bot.info.id
-        console.log bot.info
+    withNoAuthor = (callback) -> (id, created, room, body) ->
+        author = self.userForId(bot.info.id, bot.info)
+        self.robot.brain.data.users[userData.user.id].name = bot.info.name
+        self.robot.brain.data.users[userData.user.id].email_address = bot.info.email_address
+        author.room = room
+        callback id, created, room, user, body, author

     bot.on "TextMessage", withAuthor (id, created, room, user, body, author) ->
       unless bot.info.id == author.id
@@ -54,8 +57,10 @@
       unless bot.info.id == author.id
         self.receive new Robot.LeaveMessage(author)

-    bot.on "SoundMessage", withSelf (id, created, room, user, body, author) ->
+    bot.on "SoundMessage", withNoAuthor (id, created, room, body, author) ->
       console.log "Timestamp"
+      console.log author.room
+      console.log author

     bot.Me (err, data) ->
       bot.info = data.user


#97 clanup

#97 clanup

--- lib/cursor-style-manager.coffee
+++ lib/cursor-style-manager.coffee
@@ -70,7 +70,8 @@
     return unless (@vimState.isMode('visual') and settings.get('showCursorInVisualMode'))

     cursors = @editor.getCursors()
-    cursorsToShow = if @vimState.submode is 'blockwise'
+    {submode} = @vimState
+    cursorsToShow = if submode is 'blockwise'
       (cursor for cursor in cursors when swrap(cursor.selection).isBlockwiseHead())
     else
       cursors
@@ -87,7 +88,6 @@
     # is available by component in sync to model.
     @editorElement.component.updateSync()

-    {submode} = @vimState
     for cursor in cursorsToShow
       @subscriptions.add setStyleOffset(cursor, {submode, @editor, @editorElement})


Preparing the way for native angular main tools

--- src/coffee/directives.coffee
+++ src/coffee/directives.coffee
@@ -133,35 +133,20 @@
         else
           element.removeAttr('seamless')

-  Directives.directive 'currentStep', Array '$window', 'Mines', 'stepConfig', ($window, mines, conf) ->
-    restrict: 'C'
-    scope:
-      tool:     '='
-      step:     '='
-      fullSize: '='
-      onToggle: '&'
-      hasItems: '&'
-      has:      '&'
-      wants:    '&'
-      nextStep: '&'
-    template: """
-

-
-        {{tool.heading}}
-

+  Directives.directive 'iframeTool', Array '$window', 'Mines', 'stepConfig', (win, mines, conf) ->
+    restrict: 'E'
+    replace: true
+    template: """

     """
     link: (scope, element, attrs) ->
-      element.addClass('panel panel-default')
       iframe = element.find('iframe')

-      do resize = -> iframe.css height: ng.element($window).height() * 0.85
-
-      $window.addEventListener 'resize', resize
+      do resize = -> iframe.css height: ng.element(win).height() * 0.85
+
+      win.addEventListener 'resize', resize

       channel = Channel.build
         window: iframe[0].contentWindow
@@ -183,7 +168,7 @@

       channel.bind 'wants', (trans, {what, data}) -> scope.wants {what, data}

-      for link in $window.document.getElementsByTagName('link') then do (link) ->
+      for link in win.document.getElementsByTagName('link') then do (link) ->
         channel.call
           method: 'style'
           params:
@@ -224,6 +209,38 @@
           else
             init()

+  Directives.directive 'currentStep', Array '$compile', ($compile) ->
+    restrict: 'C'
+    scope:
+      tool:     '='
+      step:     '='
+      fullSize: '='
+      onToggle: '&'
+      hasItems: '&'
+      has:      '&'
+      wants:    '&'
+      nextStep: '&'
+    template: """
+

+
+        {{tool.heading}}
+

+    """
+    link: (scope, element, attrs) ->
+      element.addClass('panel panel-default')
+      scope.$watch 'tool.type', (toolType) ->
+        console.log toolType
+
+        toAppend = if toolType is 'IFrame'
+          ""
+        else if toolType is 'native'
+          "<angular-tool/>"
+
+        if toAppend?
+          $compile(toAppend) scope, (cloned) -> element.append cloned
+
   Directives.directive 'filterTerm', ->
     restrict: 'AE'
     replace: true,
@@ -293,35 +310,40 @@

   toolCssLoaded = {}

+  injectingLinker = (window, compile, injector) -> (scope, element, attrs) ->
+    scope.$watch 'tool.controllerURI', ->
+      if scope.tool
+
+        if scope.tool.style and not toolCssLoaded[scope.tool.style]
+          link = window.document.createElement('link')
+          link.rel = 'stylesheet'
+          link.href = scope.tool.style
+          window.document.getElementsByTagName('head')[0].appendChild(link)
+          toolCssLoaded[scope.tool.style] = true
+
+        ctrl = window.location.origin + scope.tool.controllerURI
+        tmpl = window.location.origin + scope.tool.templateURI
+
+        require [ctrl, "text!#{ tmpl }"], (controller, template) ->
+
+          injector.invoke controller, this, {'$scope': scope}
+
+          element.html(template)
+          compile(element.contents())(scope)
+
+          scope.$apply()
+
   Directives.directive 'nativeTool', ($window, $compile, $injector) ->
     restrict: 'E'
     scope:
       tool: '=tool'
       actions: '=actions'
       state: '=state'
-    link: (scope, element, attrs) ->
-
-      scope.$watch 'tool.controllerURI', ->
-        if scope.tool
-
-          if scope.tool.style and not toolCssLoaded[scope.tool.style]
-            link = $window.document.createElement('link')
-            link.rel = 'stylesheet'
-            link.href = scope.tool.style
-            document.getElementsByTagName('head')[0].appendChild(link)
-            toolCssLoaded[scope.tool.style] = true
-
-          ctrl = $window.location.origin + scope.tool.controllerURI
-          tmpl = $window.location.origin + scope.tool.templateURI
-
-          require [ctrl, "text!#{ tmpl }"], (controller, template) ->
-
-            $injector.invoke controller, this, {'$scope': scope}
-
-            element.html(template)
-            $compile(element.contents())(scope)
-
-            scope.$apply()
+    link: injectingLinker $window, $compile, $injector
+
+  Directives.directive 'angular-tool', Array ($window, $compile, $injector) ->
+    restrict: 'E'
+    link: injectingLinker $window, $compile, $injector

   Directives.directive 'nextStep', ($window, $compile, $injector) ->
     restrict: 'E'


add space in deposit recrod

add space in deposit recrod

--- noddy/service/v2/deposit.coffee
+++ noddy/service/v2/deposit.coffee
@@ -2,23 +2,23 @@
 import moment from 'moment'

 API.add 'service/oab/deposit',
-  get:
-    authOptional: true
-    action: () ->
+  get:
+    authOptional: true
+    action: () ->
       return API.service.oab.deposit undefined, this.queryParams, API.http.getFiles(this.queryParams.url), this.userId
-  post:
-    authOptional: true
-    action: () ->
+  post:
+    authOptional: true
+    action: () ->
       return API.service.oab.deposit undefined, this.bodyParams, this.request.files, this.userId

 API.add 'service/oab/deposit/:did',
-  get:
-    authOptional: true
-    action: () ->
+  get:
+    authOptional: true
+    action: () ->
       return API.service.oab.deposit this.urlParams.did, this.queryParams
-  post:
-    authOptional: true
-    action: () ->
+  post:
+    authOptional: true
+    action: () ->
       return API.service.oab.deposit this.urlParams.did, this.bodyParams, this.request.files

 API.add 'service/oab/deposits',
@@ -37,13 +37,13 @@
       return oab_catalogue.search this.bodyParams, {restrict:restrict}

 API.add 'service/oab/deposit/config',
-  get:
+  get:
     authOptional: true
     action: () ->
       try
         return API.service.oab.deposit.config this.queryParams.uid ? this.user._id
       return 404
-  post:
+  post:
     authRequired: 'openaccessbutton.user'
     action: () ->
       opts = this.request.body ? {}
@@ -94,7 +94,7 @@
     dm = {demo: true}
     dm.zenodo = {url: 'https://zenodo.org/record/DEMO'} if options.metadata?.doi is '10.1234/oab-syp-aam' or options.doi is '10.1234/oab-syp-aam' # without this the UI will treat it as if a wrong version was given
     return dm
-
+
   if typeof d is 'string' # a catalogue ID
     d = oab_catalogue.get d
   else
@@ -133,7 +133,7 @@
     description = description.trim()
     description += '.' if description.lastIndexOf('.') isnt description.length-1
     description += ' ' if description.length
-    description += 'Deposited by shareyourpaper.org and openaccessbutton.org. We\'ve taken reasonable steps to ensure this content doesn\'t violate copyright, however, if you think it does you can request a takedown by emailing help@openaccessbutton.org.'
+    description += '

Deposited by shareyourpaper.org and openaccessbutton.org. We\'ve taken reasonable steps to ensure this content doesn\'t violate copyright, however, if you think it does you can request a takedown by emailing help@openaccessbutton.org.'
     meta =
       title: d.metadata.title ? 'Unknown',
       description: description.trim(),
@@ -149,7 +149,7 @@
       if in_zenodo
         dep.zenodo.already = in_zenodo.id # we don't put it in again although we could with doi as related field - but leave for review for now
       else if meta.version is 'postprint' or meta.version is 'AAM' or meta.version is 'preprint'
-        meta['related_identifiers'] = [{relation: (if meta.version is 'postprint' or meta.version is 'AAM' or meta.version is 'preprint' then 'isPreviousVersionOf' else 'isIdenticalTo'), identifier: d.metadata.doi}]
+        meta['related_identifiers'] = [{relation: (if meta.version is 'postprint' or meta.version is 'AAM' or meta.version is 'preprint' then 'isPreviousVersionOf' else 'isIdenticalTo'), identifier: d.metadata.doi}]
       else
         meta.doi = d.metadata.doi
     else
@@ -217,7 +217,7 @@
   text += 'This email notifies the institution that the depositor wishes to deposit their article with the institutional repository, but we do not yet have the article - the institution should contact the depositor directly.\n\n' if dep.typ is 'dark'
   text += 'We have deposited this in Zenodo.\n\n' if dep.type is 'zenodo'
   text += 'We have deposited this in Zenodo under embargo until ' + perms.embargo + '\n\n' if dep.type is 'embargoed'
-  text += 'Author email to contact: \n' + options.email + '\n\n' if options.email
+  text += 'Author email to contact: \n' + options.email + '\n\n' if options.email
   text += 'The depositor has confirmed that this file is the correct item to deposit.\n\n' if options.confirmed? and options.confirmed isnt perms.file?.checksum
   text += 'The administrator has confirmed that this file is the correct item to deposit.\n\n' if options.confirmed? and options.confirmed is perms.file?.checksum
   text += 'There is already an open URL for this article at \n' + options.redeposit + '\n\n' if typeof options.redeposit is 'string'
@@ -336,7 +336,7 @@
       z = API.use.zenodo.deposition.create meta, up, API.settings.service.openaccessbutton?.zenodo?.token
       r.received.zenodo = 'https://zenodo.org/record/' + z.id if z.id
       r.received.zenodo_doi = z.metadata.prereserve_doi.doi if z.metadata?.prereserve_doi?.doi?
-
+
     oab_request.update r._id, {hold:'$DELETE',received:r.received,status:(if up.publish is false and not r.received.url? then 'moderate' else 'received')}
     API.service.oab.admin(r._id,'successful_upload') if up.publish
     API.mail.send
@@ -372,4 +372,3 @@
   oab_request.update rid, {hold:'$DELETE',email:'$DELETE',holds:r.holds,refused:r.refused,status:r.status}
   #API.mail.send(); # inform requestee that their request has been refused
   return r
-


Set to ready after graph so componentloader knows they're there

--- nodejs/MicroFloComponent.coffee
+++ nodejs/MicroFloComponent.coffee
@@ -57,6 +57,10 @@
         @prepareInport port, priv for port, priv of graph.inports
       if graph.outports
         @prepareOutport port, priv for port, priv of graph.outports
+
+      process.nextTick =>
+        @ready = true
+        @emit 'ready'

       do @checkConnect

@@ -124,12 +128,16 @@
       unless @upload
         @ready = true
         @emit 'ready'
+        return
+
+      console.error 'Preparing upload', @devname

       debugLevel = 'Detailed'
       data = microflo.commandstream.cmdStreamFromGraph componentLib, @graph, debugLevel
       microflo.runtime.uploadGraph @transport, data, @graph, @handleRecv

   handleRecv: (args...) =>
+    console.error args
     if args[0] is 'NETSTART'
       @ready = true
       @emit 'ready'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/engine_game_state.coffee
+++ src/engine_game_state.coffee
@@ -34,7 +34,7 @@

   # Just pass through to the current state
-  add: (className, entityData) ->
+  add: (classNameOrObjectData, entityData) ->
     # Allow optional add "Class", data form
     if entityData?
       entityData.class = className


got queries ... done with coffeescript

got queries ... done with coffeescript

--- test/datomic.coffee
+++ test/datomic.coffee
@@ -42,16 +42,6 @@
       entity.should.include ':db/id 1'
       done()

-  it 'should register to events', (done) ->
-    client = datomic.events()
-    client.onmessage = (event) ->
-      event.data.should.include ':db-after'
-      client.close()
-      done()
-
-    datomic.transact schema.movies, ->
-
-###
   it 'should get an entity with options', (done) ->

     datomic.entity {e:1, since:0}, (err, entity) ->
@@ -73,3 +63,11 @@
           movies.should.equal '[[2]]'
           done()

+  it 'should register to events', (done) ->
+    client = datomic.events()
+    client.onmessage = (event) ->
+      event.data.should.include ':db-after'
+      client.close()
+      done()
+
+    datomic.transact schema.movies, ->


test: fix tests for Mongoose 6 compatibility

--- test/encrypt.coffee
+++ test/encrypt.coffee
@@ -1683,7 +1683,7 @@
       assert.equal err, null
       ctForSwap = doc2._ct.buffer
       BasicEncryptedModel.update({_id: @testDoc._id}, {$set: _ct: doc2._ct}).exec (err, raw) =>
-        n = raw.n || raw
+        n = raw.n || raw.modifiedCount
         assert.equal err, null
         assert.equal n, 1
         BasicEncryptedModel.findOne(_id: @testDoc._id).exec (err, doc) =>
@@ -1732,7 +1732,7 @@

   it 'find should succeed if non-authenticated field is modified directly', (done) ->
     AuthenticatedFieldsModel.update({_id: @testDocAF._id}, {$set: num: 48}).exec (err, raw) =>
-      n = raw.n || raw
+      n = raw.n || raw.modifiedCount
       assert.equal err, null
       assert.equal n, 1
       AuthenticatedFieldsModel.findById @testDocAF._id, (err, doc) =>
@@ -1743,7 +1743,7 @@

   it 'find should fail if non-authenticated field is modified directly', (done) ->
     AuthenticatedFieldsModel.update({_id: @testDocAF._id}, {$set: bool: false}).exec (err, raw) =>
-      n = raw.n || raw
+      n = raw.n || raw.modifiedCount
       assert.equal err, null
       assert.equal n, 1
       AuthenticatedFieldsModel.findById @testDocAF._id, (err, doc) =>
@@ -1778,7 +1778,7 @@

       LessSecureModel.collection.insert [plainDoc, plainDoc2], (err, raw) =>
         assert.equal err, null
-        docs = raw.ops || raw
+        docs = raw.ops || raw.insertedIds
         @docId = docs[0]._id
         @doc2Id = docs[1]._id
         done()
@@ -1960,7 +1960,7 @@

         OriginalModel.collection.insert [docEncryptedWithOldVersion, docEncryptedWithOldVersion2], (err, raw) =>
           assert.equal err, null
-          docs = raw.ops || raw
+          docs = raw.ops || raw.insertedIds
           @docId = docs[0]._id
           @doc2Id = docs[1]._id
           OriginalModel.findById @docId, (err, doc) ->
@@ -2030,7 +2030,7 @@

         PreviouslyUnencryptedModel.collection.insert [plainDoc, plainDoc2], (err, raw) =>
           assert.equal err, null
-          docs = raw.ops || raw
+          docs = raw.ops || raw.insertedIds
           @docId = docs[0]._id
           @doc2Id = docs[1]._id
           done()
@@ -2128,7 +2128,7 @@

         @OriginalParentModel.collection.insert [docWithChildrenFromOldVersion], (err, raw) =>
           assert.equal err, null
-          docs = raw.ops || raw
+          docs = raw.ops || raw.insertedIds
           @docId = docs[0]._id
           done()

@@ -2186,7 +2186,7 @@

       UnsignedModel.collection.insert [plainDoc, plainDoc2], (err, raw) =>
         assert.equal err, null
-        docs = raw.ops || raw
+        docs = raw.ops || raw.insertedIds
         @docId = docs[0]._id
         @doc2Id = docs[1]._id
         done()


Delete example.coffee

Delete example.coffee

--- test/example.coffee
+++ test/example.coffee
@@ -1,13 +0,0 @@
-"use strict"
-
-Yoi       = require "yoi"
-
-module.exports = ->
-
-  tasks = []
-  tasks.push _setName(user) for user in test.users
-  tasks
-
-# Private methods
-_setName = (user) -> ->
-  Yoi.Test "GET", "api", user, null, "Set #{user.name} for parameter 'name'", 200


test for calling hotkey.callback programmatically

(cherry picked from commit 6ba78bbf48c5a7fb4a4184a2845927027ff67ae1)

--- test/hotkeys.coffee
+++ test/hotkeys.coffee
@@ -131,6 +131,17 @@
     expect(keypressA).toBe false
     expect(keypressB).toBe true
     expect(hotkeys.get('a').action).toBe 'keyup'
+
+  it 'should allow to invoke hotkey.callback programmatically without event object', ->
+    called = false;
+
+    hotkeys.add 'a', ->
+      called = true
+    , 'keyup'
+
+    hotkeys.get('a').callback()
+    expect(called).toBe true
+

   it 'should run routes-defined hotkey callbacks when scope is available', ->
     executed = false


Sort methods

Sort methods

--- src/formula_evaluator.coffee
+++ src/formula_evaluator.coffee
@@ -95,6 +95,38 @@
       res = "0".repeat(len - res.length) + res
     res

+  CEILING: (args)->
+    @checkArgumentSize(args, 2)
+    expr = @expectNumber(args[0])
+    digit = @expectNumber(args[1])
+    if digit == 0
+      return 0
+    if expr >= 0 && digit < 0
+      @error(ERROR_NUM)
+    Math.ceil(expr/digit) * digit
+
+  "CEILING.PRECISE": (args)->
+    @checkArgumentSize(args, 1, 2)
+    if args.length == 1
+      args.push 1
+    args[1] = Math.abs(@expectNumber(args[1]))
+    @CEILING(args)
+
+
+  "CEILING.MATH": (args)->
+    @checkArgumentSize(args, 1, 3)
+    if args.length == 1
+      args.push 1
+    expr = @expectNumber(args[0])
+    digit = Math.abs(@expectNumber(args[1]))
+    sign = 1
+    if digit == 0
+      return 0
+    if args.length == 3
+      sign = Math.sign(expr)
+      expr = Math.abs(expr)
+    sign * @CEILING([expr, digit])
+
   COMBIN: (args)->
     @checkArgumentSize(args, 2)
     @PERMUT(args) / @FACT([args[1]])
@@ -171,6 +203,42 @@
       else
         x * fact2(x-2)
     fact2(n)
+
+
+  FLOOR: (args)->
+    @checkArgumentSize(args, 2)
+    expr = @expectNumber(args[0])
+    digit = @expectNumber(args[1])
+    if digit == 0
+      @error(ERROR_DIV0)
+    if expr >= 0 && digit < 0
+      @error(ERROR_NUM)
+    Math.floor(expr/digit) * digit
+
+  "FLOOR.PRECISE": (args)->
+    @checkArgumentSize(args, 1, 2)
+    if args.length == 1
+      args.push 1
+    args[1] = Math.abs(@expectNumber(args[1]))
+    if args[1] == 0
+      return 0
+    @FLOOR(args)
+
+
+  "FLOOR.MATH": (args)->
+    @checkArgumentSize(args, 1, 3)
+    if args.length == 1
+      args.push 1
+    expr = @expectNumber(args[0])
+    digit = Math.abs(@expectNumber(args[1]))
+    sign = 1
+    if digit == 0
+      return 0
+    if args.length == 3
+      sign = Math.sign(expr)
+      expr = Math.abs(expr)
+    sign * @FLOOR([expr, digit])
+

   GCD: (args)->
     @checkArgumentSize(args, 1, Number.MAX_VALUE)
@@ -183,18 +251,24 @@
       g = @__gcd__(n, g)
     g

+  IF: (args)->
+    @checkArgumentSize(args, 3)
+    [cond, true_expr, false_expr] = args
+    cond = @getValue(cond)
+    if Utils.isNumber(cond)
+      cond = cond == 1
+    else if !Utils.isBoolean(cond)
+      @error(ERROR_VALUE)
+
+    if cond
+      true_expr
+    else
+      false_expr
+
   INT: (args)->
     @checkArgumentSize(args, 1)
     args.push 1
     @FLOOR(args)
-
-  ODD: (args)->
-    @checkArgumentSize(args, 1)
-    num = @expectNumber(args[0])
-    if num >= 0
-      @EVEN([num+1])-1
-    else
-      @EVEN([num-1])+1

   "ISO.CEILING":@::["CEILING.PRECISE"]

@@ -211,6 +285,28 @@
       l /= g
     l

+  LN: (args)->
+    @checkArgumentSize(args, 1)
+    Math.log @expectNumber(args[0])
+
+  LOG: (args)->
+    @checkArgumentSize(args, 1, 2)
+    if args.length == 1
+      args.push 10
+    Math.log(@expectNumber(args[0])) / Math.log(@expectNumber(args[1]))
+
+  LOG10: (args)->
+    @checkArgumentSize(args, 1)
+    Math.log10 @expectNumber(args[0])
+
+  ODD: (args)->
+    @checkArgumentSize(args, 1)
+    num = @expectNumber(args[0])
+    if num >= 0
+      @EVEN([num+1])-1
+    else
+      @EVEN([num-1])+1
+
   PERMUT: (args)->
     @checkArgumentSize(args, 2)
     n = @expectInteger(args[0])
@@ -230,6 +326,35 @@
     n = @expectInteger(args[0])
     r = @expectInteger(args[1])
     Math.pow(n, r)
+
+
+  ROUND: (args)->
+    @checkArgumentSize(args, 2)
+    expr = @expectNumber(args[0])
+    sign = Math.sign(expr)
+    expr = Math.abs(expr)
+    n = Math.floor(@expectNumber(args[1]))
+    digit = Math.pow(10, n)
+    sign * Math.round(expr*digit) / digit
+
+
+  ROUNDUP: (args)->
+    @checkArgumentSize(args, 2)
+    expr = @expectNumber(args[0])
+    sign = Math.sign(expr)
+    expr = Math.abs(expr)
+    n = Math.floor(@expectNumber(args[1]))
+    digit = Math.pow(10, n)
+    sign * @CEILING([expr, digit])
+
+  ROUNDDOWN: (args)->
+    @checkArgumentSize(args, 2)
+    expr = @expectNumber(args[0])
+    sign = Math.sign(expr)
+    expr = Math.abs(expr)
+    n = Math.floor(@expectNumber(args[1]))
+    digit = Math.pow(10, n)
+    sign * @FLOOR([expr, digit])

   SUM: (args)->
@@ -241,125 +366,12 @@
     sum

-  ROUND: (args)->
-    @checkArgumentSize(args, 2)
-    expr = @expectNumber(args[0])
-    sign = Math.sign(expr)
-    expr = Math.abs(expr)
-    n = Math.floor(@expectNumber(args[1]))
-    digit = Math.pow(10, n)
-    sign * Math.round(expr*digit) / digit
-
-
-  ROUNDUP: (args)->
-    @checkArgumentSize(args, 2)
-    expr = @expectNumber(args[0])
-    sign = Math.sign(expr)
-    expr = Math.abs(expr)
-    n = Math.floor(@expectNumber(args[1]))
-    digit = Math.pow(10, n)
-    sign * @CEILING([expr, digit])
-
-  ROUNDDOWN: (args)->
-    @checkArgumentSize(args, 2)
-    expr = @expectNumber(args[0])
-    sign = Math.sign(expr)
-    expr = Math.abs(expr)
-    n = Math.floor(@expectNumber(args[1]))
-    digit = Math.pow(10, n)
-    sign * @FLOOR([expr, digit])
-
-  CEILING: (args)->
-    @checkArgumentSize(args, 2)
-    expr = @expectNumber(args[0])
-    digit = @expectNumber(args[1])
-    if digit == 0
-      return 0
-    if expr >= 0 && digit < 0
-      @error(ERROR_NUM)
-    Math.ceil(expr/digit) * digit
-
-  "CEILING.PRECISE": (args)->
-    @checkArgumentSize(args, 1, 2)
-    if args.length == 1
-      args.push 1
-    args[1] = Math.abs(@expectNumber(args[1]))
-    @CEILING(args)
-
-
-  "CEILING.MATH": (args)->
-    @checkArgumentSize(args, 1, 3)
-    if args.length == 1
-      args.push 1
-    expr = @expectNumber(args[0])
-    digit = Math.abs(@expectNumber(args[1]))
-    sign = 1
-    if digit == 0
-      return 0
-    if args.length == 3
-      sign = Math.sign(expr)
-      expr = Math.abs(expr)
-    sign * @CEILING([expr, digit])
-
-
-
-  FLOOR: (args)->
-    @checkArgumentSize(args, 2)
-    expr = @expectNumber(args[0])
-    digit = @expectNumber(args[1])
-    if digit == 0
-      @error(ERROR_DIV0)
-    if expr >= 0 && digit < 0
-      @error(ERROR_NUM)
-    Math.floor(expr/digit) * digit
-
-  "FLOOR.PRECISE": (args)->
-    @checkArgumentSize(args, 1, 2)
-    if args.length == 1
-      args.push 1
-    args[1] = Math.abs(@expectNumber(args[1]))
-    if args[1] == 0
-      return 0
-    @FLOOR(args)
-
-
-  "FLOOR.MATH": (args)->
-    @checkArgumentSize(args, 1, 3)
-    if args.length == 1
-      args.push 1
-    expr = @expectNumber(args[0])
-    digit = Math.abs(@expectNumber(args[1]))
-    sign = 1
-    if digit == 0
-      return 0
-    if args.length == 3
-      sign = Math.sign(expr)
-      expr = Math.abs(expr)
-    sign * @FLOOR([expr, digit])
-
-
   TRUNC: (args)->
     @checkArgumentSize(args, 1, 2)
     if args.length == 1
       args.push 0

     @ROUNDDOWN(args)
-
-
-  IF: (args)->
-    @checkArgumentSize(args, 3)
-    [cond, true_expr, false_expr] = args
-    cond = @getValue(cond)
-    if Utils.isNumber(cond)
-      cond = cond == 1
-    else if !Utils.isBoolean(cond)
-      @error(ERROR_VALUE)
-
-    if cond
-      true_expr
-    else
-      false_expr
-

   error:(err)->


load iframe from http when site is http

load iframe from http when site is http

--- src/javascripts/embed.coffee
+++ src/javascripts/embed.coffee
@@ -17,13 +17,18 @@
     @configuration.parentLocationHash = window.location.hash
     @configuration.embedCode = @elem.outerHTML

-    scriptPath = $(@elem).attr('src').match(/(^.*\/)/)[0].replace(/javascripts\/$/, '').replace(/\/$/, '')
-    @url = "#{scriptPath}/podigee-podcast-player.html?id=#{@id}&iframeMode=script"
+    @url = "#{@origin()}/podigee-podcast-player.html?id=#{@id}&iframeMode=script"

     @buildIframe()
     @setupListeners()
     @replaceElem()
     @injectConfiguration() if @configuration
+
+  origin: () ->
+    scriptSrc = $(@elem).attr('src')
+    unless window.location.protocol.match(/^https/)
+      scriptSrc = scriptSrc.replace(/^https/, 'http')
+    scriptSrc.match(/(^.*\/)/)[0].replace(/javascripts\/$/, '').replace(/\/$/, '')

   buildIframe: ->
     @iframe = document.createElement('iframe')


Remove test for tracker

Remove test for tracker

We are using our own tracker. The code related to tracker can be removed.

--- test/tracker.coffee
+++ test/tracker.coffee
@@ -1,277 +0,0 @@
-should = require 'should'
-path = require 'path'
-VASTParser = require '../src/parser'
-VASTUtil = require('../src/util.coffee')
-VASTTracker = require '../src/tracker'
-
-urlfor = (relpath) ->
-    return 'file://' + path.resolve(path.dirname(module.filename), relpath).replace(/\\/g, '/')
-
-describe 'VASTTracker', ->
-    describe '#constructor', ->
-        @Tracker = null
-        _eventsSent = []
-        @templateFilterCalls = []
-
-        before (done) =>
-            VASTParser.addURLTemplateFilter (url) =>
-              @templateFilterCalls.push url
-              return url
-            VASTParser.parse urlfor('wrapper.xml'), (@response) =>
-                # Init tracker
-                ad = @response.ads[0]
-                creative = @response.ads[0].creatives[0]
-                @Tracker = new VASTTracker ad, creative
-                # Mock emit
-                @Tracker.emit = (event) =>
-                    _eventsSent.push(event)
-                done()
-
-        after () =>
-            VASTParser.clearUrlTemplateFilters()
-
-        it 'should have firstQuartile set', =>
-            @Tracker.quartiles.firstQuartile.should.equal 22.53
-
-        it 'should have midpoint set', =>
-            @Tracker.quartiles.midpoint.should.equal 45.06
-
-        it 'should have thirdQuartile set', =>
-            @Tracker.quartiles.thirdQuartile.should.equal 67.59
-
-        it 'should have skipDelay disabled', =>
-            @Tracker.skipDelay.should.equal -1
-
-
-        describe '#setProgress', =>
-
-            beforeEach (done) =>
-                _eventsSent = []
-                done()
-
-            it 'should send start event when set at 1', =>
-                @Tracker.setProgress 1
-                _eventsSent.should.eql ["start"]
-
-            it 'should send skip-countdown event', =>
-                @Tracker.skipDelay = 5
-                @Tracker.setProgress 6
-                _eventsSent.should.eql ["skip-countdown"]
-
-            it 'should send rewind event when set back at 5', =>
-                @Tracker.setProgress 5
-                _eventsSent.should.eql ["rewind"]
-
-            it 'should send firstQuartile event', =>
-                @Tracker.setProgress 23
-                _eventsSent.should.eql ["firstQuartile"]
-
-            it 'should send progress-30 event VAST 3.0', =>
-                @Tracker.setProgress 30
-                _eventsSent.should.eql ["progress-30"]
-
-            it 'should send midpoint event', =>
-                @Tracker.setProgress 46
-                _eventsSent.should.eql ["midpoint"]
-
-            it 'should send progress-60% event VAST 3.0', =>
-                @Tracker.setProgress 54
-                _eventsSent.should.eql ["progress-60%"]
-
-            it 'should send thirdQuartile event', =>
-                @Tracker.setProgress 68
-                _eventsSent.should.eql ["thirdQuartile"]
-
-
-        describe '#setMuted', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.trackingEvents['mute'] = 'http://example.com/muted'
-                @Tracker.trackingEvents['unmute'] = 'http://example.com/muted'
-                @Tracker.setMuted yes
-                done()
-
-            it 'should be muted', =>
-                @Tracker.muted.should.eql yes
-
-            it 'should send muted event', =>
-                _eventsSent.should.eql ["mute"]
-
-            it 'should be unmuted', =>
-                _eventsSent = []
-                @Tracker.setMuted no
-                @Tracker.muted.should.eql no
-
-            it 'should send unmuted event', =>
-                _eventsSent.should.eql ["unmute"]
-
-            it 'should send no event', =>
-                _eventsSent = []
-                @Tracker.setMuted no
-                _eventsSent.should.eql []
-
-
-        describe '#setPaused', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.setPaused yes
-                done()
-
-            it 'should be paused', =>
-                @Tracker.paused.should.eql yes
-
-            it 'should send pause event', =>
-                _eventsSent.should.eql ["pause"]
-
-            it 'should be resumed', =>
-                _eventsSent = []
-                @Tracker.setPaused no
-                @Tracker.paused.should.eql no
-
-            it 'should send resume event', =>
-                _eventsSent.should.eql ["resume"]
-
-            it 'should send no event', =>
-                _eventsSent = []
-                @Tracker.setPaused no
-                _eventsSent.should.eql []
-
-
-        describe '#setFullscreen', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.trackingEvents['fullscreen'] = 'http://example.com/fullscreen'
-                @Tracker.trackingEvents['exitFullscreen'] = 'http://example.com/exitFullscreen'
-                @Tracker.setFullscreen yes
-                done()
-
-            it 'should be in fullscreen mode', =>
-                @Tracker.fullscreen.should.eql yes
-
-            it 'should send fullscreen event', =>
-                _eventsSent.should.eql ["fullscreen"]
-
-            it 'should be in exitFullscreen mode', =>
-                _eventsSent = []
-                @Tracker.setFullscreen no
-                @Tracker.fullscreen.should.eql no
-
-            it 'should send exitFullscreen event', =>
-                _eventsSent.should.eql ["exitFullscreen"]
-
-            it 'should send no event', =>
-                _eventsSent = []
-                @Tracker.setFullscreen no
-                _eventsSent.should.eql []
-
-
-        describe '#setSkipDelay', =>
-
-            it 'should have skipDelay set to 3', =>
-                @Tracker.setSkipDelay 3
-                @Tracker.skipDelay.should.eql 3
-
-            it 'should have skipDelay still set to 3', =>
-                @Tracker.setSkipDelay 'blabla'
-                @Tracker.skipDelay.should.eql 3
-
-
-        describe '#load', =>
-
-            before (done) =>
-                _eventsSent = []
-                VASTUtil.track = (URLTemplates, variables) ->
-                    _eventsSent.push @resolveURLTemplates(URLTemplates, variables)
-                @Tracker.load()
-                done()
-
-            it 'should have impressed set to true', =>
-                @Tracker.impressed.should.eql yes
-
-            it 'should have called impression urls', =>
-                _eventsSent[0].should.eql ['http://example.com/wrapper-impression', 'http://127.0.0.1:8080/second/wrapper_impression', 'http://example.com/impression1', 'http://example.com/impression2', 'http://example.com/impression3']
-
-            it 'should have sent creativeView event', =>
-                _eventsSent[1].should.eql 'creativeView'
-
-            it 'should only be called once', =>
-                _eventsSent = []
-                @Tracker.load()
-                _eventsSent.should.eql []
-
-
-        describe '#errorWithCode', =>
-
-            before (done) =>
-                _eventsSent = []
-                VASTUtil.track = (URLTemplates, variables) ->
-                    _eventsSent.push @resolveURLTemplates(URLTemplates, variables)
-                @Tracker.errorWithCode('errorCode')
-                done()
-
-            it 'should have called error urls', =>
-                _eventsSent[0].should.eql [ 'http://example.com/wrapper-error', 'http://example.com/error']
-
-
-        describe '#complete', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.complete()
-                done()
-
-            it 'should have sent complete event and urls', =>
-                _eventsSent.should.eql ['complete', ["http://example.com/complete", "http://example.com/wrapper-complete"]]
-
-            it 'should be called multiples times', =>
-                _eventsSent = []
-                @Tracker.complete()
-                _eventsSent.should.eql ['complete', ["http://example.com/complete", "http://example.com/wrapper-complete"]]
-
-
-        describe '#close', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.close()
-                done()
-
-            it 'should have sent close event and urls VAST 2.0', =>
-                _eventsSent.should.eql ['close', [ 'http://example.com/close']]
-
-            it 'should have sent closeLinear event and urls VAST 3.0', =>
-                _eventsSent = []
-                @Tracker.trackingEvents['closeLinear'] = ['http://example.com/closelinear']
-                delete @Tracker.trackingEvents['close']
-                @Tracker.close()
-                _eventsSent.should.eql ['closeLinear', [ 'http://example.com/closelinear']]
-
-
-        describe '#skip', =>
-
-            before (done) =>
-                _eventsSent = []
-                @Tracker.skip()
-                done()
-
-            it 'should have sent skip event', =>
-                _eventsSent.should.eql ['skip']
-
-
-        describe '#click', =>
-
-            before (done) =>
-                _eventsSent = []
-                VASTUtil.track = (URLTemplates, variables) ->
-                    _eventsSent.push @resolveURLTemplates(URLTemplates, variables)
-                @Tracker.click()
-                done()
-
-            it 'should have sent clicktracking events', =>
-                _eventsSent[0].should.eql ['http://example.com/clicktracking', 'http://example.com/wrapper-clicktracking']
-
-            it 'should have sent clickthrough event', =>
-                _eventsSent[1].should.eql 'clickthrough'


Refactor reserved key deletion as a list comprehension

--- src/kissmetrics-batch.coffee
+++ src/kissmetrics-batch.coffee
@@ -145,12 +145,8 @@
       when 'record' then data.event = data._n
       when 'alias' then data.alias = data._n

-    delete data._k
-    delete data._n
-    delete data._p
-    delete data._t
-    delete data._d
-    delete data.__type
+    reservedKeys = ['_k', '_n', '_p', '_t', '_d', '__type']
+    delete data[reservedKey] for reservedKey in reservedKeys

     data


Social: add social api controller as a singleton

--- client/Main/maincontroller/maincontroller.coffee
+++ client/Main/maincontroller/maincontroller.coffee
@@ -55,6 +55,7 @@
     @ready =>
       KD.registerSingleton "widgetController",        new WidgetController
       KD.registerSingleton "activityController",      new ActivityController
+      KD.registerSingleton "socialapiController",     new SocialApiController
       KD.registerSingleton "appStorageController",    new AppStorageController
       KD.registerSingleton "kodingAppsController",    new KodingAppsController
       KD.registerSingleton "localSync",               new LocalSyncController


Fixed bug in time poll

Fixed bug in time poll

--- client/app/services/Recent/Recent.service.coffee
+++ client/app/services/Recent/Recent.service.coffee
@@ -26,7 +26,7 @@
 				difference = now - playerData.data.modifiedDate
 				outdated = difference > 600000
 			if not outdated
-				future = difference + (800000-playerData.data.modifiedDate)
+				future = (600000+playerData.data.modifiedDate-now)
 				rec.poll = setTimeout(searchPlayer.bind(undefined, playerName), future)
 				console.log 'set poll for', playerName, future + ' ms in the future'
 				return playerData.data


:racehorse: Remove unneeded line

:racehorse: Remove unneeded line

It looks like this was causing Chrome to deoptimize this method as well
as its caller.

--- lib/mixins/canvas-drawer.coffee
+++ lib/mixins/canvas-drawer.coffee
@@ -74,10 +74,7 @@
   # token - A token {Object}.
   #
   # Returns a {String}.
-  getTokenColor: (token) ->
-    #Retrieve color from cache if available
-    flatScopes = (token.scopeDescriptor or token.scopes).join()
-    @retrieveTokenColorFromDom(token)
+  getTokenColor: (token) -> @retrieveTokenColorFromDom(token)

   # Returns the background color for the passed-in `decoration` object.
   #


:bug: Fix duplicated lines at the end of the minimap

This problem occurs when the only changes are screen lines changes due
to soft wrapping (by splitting panes, resizing the window, or just at
intialization). In that case, we don’t compute an extra intact range
that would copy a part of the offscreen buffer at the wrong place.

Fixes #292

--- lib/mixins/canvas-drawer.coffee
+++ lib/mixins/canvas-drawer.coffee
@@ -444,11 +444,14 @@
               end: change.start - 1
               domStart: range.domStart)
           if change.end < range.end
-            newIntactRanges.push(
-              start: change.end + change.screenDelta + 1
-              end: range.end + change.screenDelta
-              domStart: range.domStart + change.end + 1 - range.start
-            )
+            # If the bufferDelta is 0 then it's a change in the screen lines
+            # due to soft wrapping, we don't need to touch to the intact ranges
+            unless change.bufferDelta is 0
+              newIntactRanges.push(
+                start: change.end + change.screenDelta + 1
+                end: range.end + change.screenDelta
+                domStart: range.domStart + change.end + 1 - range.start
+              )

         intactRange = newIntactRanges[newIntactRanges.length - 1]


:fire: Remove dead code

:fire: Remove dead code

--- lib/mixins/canvas-drawer.coffee
+++ lib/mixins/canvas-drawer.coffee
@@ -12,9 +12,6 @@
     @offscreenContext = @offscreenCanvas.getContext('2d')

   updateCanvas: ->
-    #is this scroll only or has content changed?
-    hasChanges = @pendingChanges.length > 0
-
     firstRow = @minimap.getFirstVisibleScreenRow()
     lastRow = @minimap.getLastVisibleScreenRow()
     intactRanges = @computeIntactRanges(firstRow, lastRow)


:racehorse: Speed up decorations retrieval when rendering lines

There’s no longer a loop each time we need to get decorations of a
given type for a given row, everything was already computed in the
`minimap.decorationsForScreenRowRangeByRowAndType` method.

--- lib/mixins/canvas-drawer.coffee
+++ lib/mixins/canvas-drawer.coffee
@@ -134,7 +134,7 @@
     charWidth = @minimap.getCharWidth() * devicePixelRatio
     canvasWidth = @canvas.width
     displayCodeHighlights = @displayCodeHighlights
-    decorations = @minimap.decorationsForScreenRowRange(firstRow, lastRow)
+    decorations = @minimap.decorationsForScreenRowRangeByRowAndType(firstRow, lastRow)

     line = lines[0]

@@ -155,13 +155,15 @@
       y0 = y*lineHeight

       # Line decorations are first drawn on the canvas.
-      lineDecorations = @minimap.decorationsByTypesForRow(screenRow, 'line', decorations)
-      @drawLineDecorations(context, lineDecorations, y0, canvasWidth, lineHeight) if lineDecorations.length
+      lineDecorations = decorations['line']?[screenRow]
+
+      @drawLineDecorations(context, lineDecorations, y0, canvasWidth, lineHeight) if lineDecorations?.length

       # Then comes the highlight decoration with `highlight-under` type.
-      highlightDecorations = @minimap.decorationsByTypesForRow(firstRow + row, 'highlight-under', decorations)
-      for decoration in highlightDecorations
-        @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)
+      highlightDecorations = decorations['highlight-under']?[firstRow + row]
+      if highlightDecorations?.length
+        for decoration in highlightDecorations
+          @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)

       # Then the line tokens are drawn
       for token in line.tokens
@@ -182,9 +184,10 @@
         break if x > canvasWidth

       # Finally the highlight over decorations are drawn.
-      highlightDecorations = @minimap.decorationsByTypesForRow(firstRow + row, 'highlight', 'highlight-over', decorations)
-      for decoration in highlightDecorations
-        @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)
+      highlightDecorations = decorations['highlight-over']?[firstRow + row]
+      if highlightDecorations?.length
+        for decoration in highlightDecorations
+          @drawHighlightDecoration(context, decoration, y, screenRow, lineHeight, charWidth, canvasWidth)

     context.fill()


Add move-to-bottom functionality

Add move-to-bottom functionality

--- lib/project/results-view.coffee
+++ lib/project/results-view.coffee
@@ -36,6 +36,9 @@
         @selectPreviousResult()
       'core:move-left': => @collapseResult()
       'core:move-right': => @expandResult()
+      'core:move-to-bottom': =>
+        @renderResults(renderAll: true)
+        @selectLastResult()
       'core:confirm': =>
         @find('.selected').view()?.confirm?()
         false
@@ -96,6 +99,9 @@

   selectFirstResult: ->
     @find('.search-result:first').addClass('selected')
+
+  selectLastResult: ->
+    @find('.search-result:last').addClass('selected')

   selectNextResult: ->
     selectedView = @find('.selected').view()


Missing equals sign @marcneuwirth

Missing equals sign @marcneuwirth

--- SingularityUI/app/env.coffee
+++ SingularityUI/app/env.coffee
@@ -3,8 +3,8 @@
         env: 'local'
         SINGULARITY_BASE: 'http://heliograph.iad01.hubspot-networks.net:7005'
 else
-    env
+    env =
         env: 'prod'
         SINGULARITY_BASE: ''

-module.exports = env
+module.exports = env


Removed unused code

Removed unused code

--- core/app/backbone/views/notification_view.coffee
+++ core/app/backbone/views/notification_view.coffee
@@ -31,22 +31,6 @@

   initialize: ->
     @activity = @model.get('activity')
-
-  channel: ->
-    channel = new Channel
-      id:         @activity.channel_id
-      title:      @activity.channel_title
-      slug_title: @activity.channel_slug_title
-
-  channel_as_topic: ->
-    new Topic
-      title:      @activity.channel_title
-      slug_title: @activity.channel_slug_title
-
-  other_channel_as_topic: ->
-    new Topic
-      title:      @activity.to_channel_title
-      slug_title: @activity.to_channel_slug_title

   other_channel: ->
     new Channel


Simplify logic in PageSelectionCtrl

Simplify logic in PageSelectionCtrl

--- app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee
+++ app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee
@@ -3,14 +3,14 @@
     # The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`.
     # We remove the slash at the beginning.
     page = $location.path()[1..]
-    if page in $scope.whitelist
-      $scope.lastPage = page
-      page
-    else if page
-      # The path points to an unrelated path like `/login`. Stay where we were.
-      $scope.lastPage
-    else
-      $scope.whitelist[0]
+
+    return $scope.whitelist[0] unless page
+
+    # If the path points to an unrelated path like `/login`, stay where we were.
+    return $scope.lastPage unless page in $scope.whitelist
+
+    $scope.lastPage = page
+    page

   $scope.whitelistPages = (pages) ->
     $scope.whitelist = pages


fixes a bug in String::toDash.

fixes a bug in String::toDash.

--- app/assets/javascripts/habberdash/dependencies/native_extensions.js.coffee
+++ app/assets/javascripts/habberdash/dependencies/native_extensions.js.coffee
@@ -1,4 +1,4 @@
 String::trim = -> @replace(/^\s+|\s+$/g, '')
 String::toCamel = -> @replace(/([\-|_][a-z])/g, ($1) -> $1.toUpperCase().replace(/[\-|_]/, ''))
-String::toDash = -> @replace(/([A-Z])/g, ($1) -> "-#{$1.toLowerCase()}")
+String::toDash = -> @replace(/([A-Z])/g, ($1) -> "-#{$1.toLowerCase()}").replace(/^-|-$/, '')
 String::toUnderscore = -> @replace(/([A-Z])/g, ($1) -> "_#{$1.toLowerCase()}")


Fix listener in visualization-graph-configuration

--- app/frontend/javascripts/views/visualization-graph-configuration.js.coffee
+++ app/frontend/javascripts/views/visualization-graph-configuration.js.coffee
@@ -170,7 +170,7 @@
     @$el.find('#reset-defaults').click        @onResetDefaults

     # Add new custom fields to nodes-color-column select when created
-    @model.on 'change:custom_fields', onCustomFieldAdded
+    @model.on 'change:custom_fields', @onCustomFieldAdded

     return this


errorhandler

errorhandler

--- crowler/feed.coffee
+++ crowler/feed.coffee
@@ -67,6 +67,7 @@
       app.emit 'error',err
       callback()
     d.run ->
+      return if not page.link or page.link is "" or page.link is null

       request page.link,(err,res,body)->
         if err


Add the master task to the cakefile module

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -63,6 +63,27 @@
     option "-w", "--watch", "Watch the library sources and recompile"
     option "-g", "--git-hub-pages", "Publish documents to GitHub pages"
     option "-n", "--clean-modules", "Remove node_modules during cleanup"
+
+    # This task launches an instance of application where this task
+    # is invoked at. It should be either an application build within
+    # the framework or the framework itself (it can be launched all
+    # by itself as a standalone). Please refer to the implementation!
+    # In terms of scalability - it starts the master server istance.
+    task "master", "bootstrap as the master server", (options) ->
+        library = options.library or DEFAULT_LIBRARY
+        scoping = options.scoping or DEFAULT_SCOPING
+        logging = options.logging or DEFAULT_LOGGING
+        process.env["NODE_ENV"] = scoping.toString()
+        process.env["log:level"] = logging.toString()
+        granite = require "#{__dirname}/../../index"
+        assert resolved = paths.resolve library or null
+        missingLibrary = "missing library: #{resolved}"
+        assert _.isObject(granite), "framework failed"
+        assert fs.existsSync(library), missingLibrary
+        compiled = granite.collectPackages no, library
+        assert _.isObject(compiled), "invalid library"
+        conf = new Object master: yes, instance: no
+        granite.cachedKernel(library).bootstrap conf

     # This task launches an instance of application where this task
     # is invoked at. It should be either an application build within


intercept exceptions on the $root

intercept exceptions on the $root

--- library/exposure/tracked.coffee
+++ library/exposure/tracked.coffee
@@ -98,5 +98,5 @@
         error = deb -> clear(); toastr.error srv, 0, pos
         do => socket.on "reconnecting", => recon.call @
         do => socket.on "disconnect", => dropd.call @
-        do => socket.on "exception", => error.call @
+        do => $root?.on "exception", => error.call @
         do => socket.on "connect", => connd.call @


add conditional code compression

add conditional code compression

--- library/nucleus/cakefile.coffee
+++ library/nucleus/cakefile.coffee
@@ -69,6 +69,7 @@
     option "-i", "--logging [LEVEL]", "The level to use for the logging output"
     option "-w", "--watch", "Watch the library sources and recompile it"
     option "-g", "--git-hub-pages", "Publish documents to GitHub pages"
+    option "-c", "--compress-code", "Compress JS code once is compiled"

     # This is one of the major tasks in this Cakefile, it implements
     # the generation of the documentation for the library, using the
@@ -104,7 +105,9 @@
         parameters.unshift "-w" if options.watch or false
         assert watching = "Watching the %s directory".blue
         logger.info watching, library.bold if options.watch
-        opts = mangle: no, compress: no, output: beautify: yes
+        norm = mangle: no, compress: no, output: beautify: yes
+        comp = mangle: no, compress: yes, output: beautify: no
+        opts = if "compress-code" of options then comp else norm
         optimize = (p) -> writeFileSync p, minify(p, opts).code
         assert _.isObject compiler = spawn "coffee", parameters
         assert _.isObject compiler.stdout.pipe process.stdout


Fix sourcemaps, remove website prefix for sourceMapUrl

--- Builder.coffee
+++ Builder.coffee
@@ -375,7 +375,8 @@

       fileLineOffset += file.content.split("\n").length

-    js += "//@ sourceMappingURL=/#{project.outputs.script}.map"
+    mapUrl = project.outputs.script.replace /^website\//, ''
+    js += "//@ sourceMappingURL=/#{mapUrl}.map"

     filepath = project.outputs.script
     fs.writeFileSync filepath, js


Builder: on error give fullpath rather than just the filename

--- Builder.coffee
+++ Builder.coffee
@@ -237,7 +237,7 @@
           if file.sourcePath in @blackList
             @blackList.splice (@blackList.indexOf file.sourcePath), 1
           else if not checkFileCase file.sourcePath
-            log.error "File name case is wrong: #{ includePath }"
+            log.error "File name case is wrong: #{ file.sourcePath }"
             process.exit 1

           switch file.extension


Update config

Update config

--- services/track-changes/config/settings.defaults.coffee
+++ services/track-changes/config/settings.defaults.coffee
@@ -16,8 +16,8 @@
 			url: "http://#{process.env["DOCSTORE_HOST"] or "localhost"}:3016"
 		web:
 			url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}"
-			user: "sharelatex"
-			pass: "password"
+			user: process.env['WEB_API_USER'] or "sharelatex"
+			pass: process.env['WEB_API_PASSWORD'] or "password"
 	redis:
 		lock:
 			host: process.env["REDIS_HOST"] or "localhost"


model_postgres: fix & simplify purgeNode()

--- src/local/model_postgres.coffee
+++ src/local/model_postgres.coffee
@@ -210,17 +210,15 @@

     purgeNode: (node, cb) ->
         db = @db
-        async.series [(cb2) ->
-            db.query "DELETE FROM items WHERE node=$1", [ node ], cb2
-        , (res, cb2) ->
-            db.query "DELETE FROM subscriptions WHERE node=$1", [ node ], cb2
-        , (res, cb2) ->
-            db.query "DELETE FROM affiliations WHERE node=$1", [ node ], cb2
-        , (res, cb2) ->
-            db.query "DELETE FROM node_config WHERE node=$1", [ node ], cb2
-        , (res, cb2) ->
-            db.query "DELETE FROM nodes WHERE node=$1", [ node ], cb2
-        , (res, cb2) ->
+        q = (sql) ->
+            (cb2) ->
+                db.query sql, [ node ], cb2
+        async.series [
+            q "DELETE FROM items WHERE node=$1"
+            q "DELETE FROM subscriptions WHERE node=$1"
+            q "DELETE FROM affiliations WHERE node=$1"
+            q "DELETE FROM node_config WHERE node=$1"
+            q "DELETE FROM nodes WHERE node=$1"
         ], (err) ->
             logger.info "Purged all data of node #{node}"
             cb err


groups: hide loader when add-role gets canceled

--- client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
+++ client/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
@@ -145,7 +145,8 @@
               Cancel :
                 style     : "add-role-cancel modal-cancel"
                 cssClass  : 'add-role-cancel'
-                callback  : ()->
+                callback  : ()=>
+                  @buttons["Save"].hideLoader()
                   addRoleDialog.hide()

           addRoleDialog.addSubView wrapper = new KDView


Scroll the active element into the view

Scroll the active element into the view

Comes in handy when keyboard navigating. Existing bug that the full page scrolls down a bit when the last element is activated still exists here.

--- core/app/backbone/views/channels/auto_complete_search_channels_view.coffee
+++ core/app/backbone/views/channels/auto_complete_search_channels_view.coffee
@@ -8,6 +8,8 @@
   tagName: "li"

   template: "channels/auto_complete_search_channel"
+
+  scrollIntoView: -> scrollIntoViewWithinContainer @$el, $('.auto-complete-search-list')

   templateHelpers: ->
     query = @options.query


Add error classes and logger

Add error classes and logger

--- kites/applications/utils.coffee
+++ kites/applications/utils.coffee
@@ -1,3 +1,10 @@
+
+# Config
+config    = require './config'
+
+# Logger
+log4js    = require 'log4js'
+log       = log4js.getLogger("[#{config.name}]")

 # Custom Libraries for this Util
 fs        = require 'fs'
@@ -16,7 +23,17 @@
     @message = message or "You are not authorized to do this."
     @name = 'AuthorizationError'

-normalizeUserPath = (username, path)-> path?.replace(/\~/g, '/Users/#{username}')
+# General Purpose Error
+class KodingError extends Error
+  constructor:(message, details)->
+    # log.error details if details
+    return new KodingError(message) unless @ instanceof KodingError
+    Error.call @
+    @message = message
+    @details = details if details
+    @name = 'KodingError'
+
+normalizeUserPath = (username, path)-> path?.replace(/\~/g, "/Users/#{username}")
 safeForUser       = (username, path)-> path?.indexOf("/Users/#{username}/") is 0
 escapePath        = (path)-> if path then nodePath.normalize path.replace(/[^a-zA-Z0-9\/\-. ]/g, '')
                                                                  .replace(/\s/g, '\\ ')
@@ -27,12 +44,12 @@
     # Needs to FIX
     chownr {path, username}, callback

-createAppsDir = (user, cb)->
-  path = escapePath "/Users/#{user}/Applications"
-  if not safeForUser user, path
-    cb new AuthorizationError user
+createAppsDir = (username, cb)->
+  path = escapePath "/Users/#{username}/Applications"
+  if not safeForUser username, path
+    cb new AuthorizationError username
   else
-    makedirp path, user, cb
+    makedirp path, username, cb

 chownr = (options, callback)->
   {path, username, uid, gid} = options


add pane id class to workspace element

add pane id class to workspace element

--- lib/workspace/view.coffee
+++ lib/workspace/view.coffee
@@ -37,6 +37,7 @@
           ]
       div 'context', [div('header', context), table('items', rows)]
     @view = views.render div 'contexts', contexts
+    @classList.add @model.id
     @appendChild @view

   initialize: (@model) ->


initial boot.coffee - commit.

initial boot.coffee - commit.

boot.coffee to boot or load required JavaScript, written in CoffeeScript.
http://coffeescript.org/


Properly remove class when title text is set

--- app/assets/javascripts/components/content_editable_component.js.coffee
+++ app/assets/javascripts/components/content_editable_component.js.coffee
@@ -43,7 +43,6 @@
     @set '_userIsTyping', false
     @setPlaceholder() if @elementIsEmpty()

-
   elementIsEmpty: ->
     Em.isEmpty(@.$().text())

@@ -58,6 +57,7 @@

   setHTMLFromValue: ->
     @.$().html(@get('value'))
+    @.$().removeClass('placeholder')

   setValueFromHTML: ->
     if @get('plaintext')