去掉了不使用requirejs的gruntfile配置文件

去掉了不使用requirejs的gruntfile配置文件

--- Gruntfile-default.coffee
+++ Gruntfile-default.coffee
@@ -1,217 +0,0 @@
-path = require 'path'
-fs = require 'fs'
-#生产目录
-SOURCE = "#{__dirname}/src"
-BUILD = "#{__dirname}/build"
-module.exports = (grunt)->
-    #项目配置
-    grunt.initConfig
-        pkg: grunt.file.readJSON 'package.json'
-        #根据项目自身情况，决定是否先清空构建目录
-        clean:
-            build: BUILD
-        #复制不用构建的文件
-        copy:
-            app_css:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/app"
-                        src: ['**','!**/*.css','!**/*.less']
-                        dest: "#{BUILD}/css/app"
-                    }
-                ]
-            lib_css:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/lib"
-                        src: ['**','!**/*.css','!**/*.min.css']
-                        dest: "#{BUILD}/css/lib"
-                    }
-                ]
-            app_js:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/js/app"
-                        src: ['**','!**/*.js','!**/*.min.js']
-                        dest: "#{BUILD}/js/app"
-                    }
-                ]
-            lib_js:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/js/lib"
-                        src: ['**','!**/*.js','!**/*.min.js']
-                        dest: "#{BUILD}/js/lib"
-                    }
-                ]
-            image:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/img"
-                        src: ['**','!**/*.{jpg|png|gif}']
-                        dest: "#{BUILD}/img"
-
-                    }
-                ]
-        #检测js语法
-        jshint:
-            options:
-                ignores: ["#{SOURCE}/js/app/**/*.min.js"]
-            app: ["#{SOURCE}/js/app/**/*.js"]
-        #压缩js
-        uglify:
-            app:
-                files: [
-                    expand: true
-                    cwd: "#{SOURCE}/js/app"
-                    src: ['**/*.js','!**/*.min.js']
-                    ext: '.js'
-                    dest: "#{BUILD}/js/app"
-                ]
-            lib:
-                files: [
-                    expand: true
-                    cwd: "#{SOURCE}/js/lib"
-                    src: ['**/*.js','!**/*.min.js']
-                    ext: '.js'
-                    dest: "#{BUILD}/js/lib"
-                ]
-        #编译less成css
-        less:
-            options:
-                paths: ["#{SOURCE}/css"]
-            app:
-                #自动添加后缀
-                options:
-                    plugins: [
-                        new (require('less-plugin-autoprefix')) {browsers: ["last 20 versions"]}
-                    ]
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/app"
-                        src: '**/*.less'
-                        ext: '.css'
-                        dest: "#{SOURCE}/css/app"
-                    }
-                ]
-            lib:
-                options:
-                    compress: true
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/lib"
-                        src: ['**/*.css','!**/*.min.css']
-                        ext: '.css'
-                        dest: "#{BUILD}/css/lib"
-                    }
-                ]
-        #检测编译好的css语法
-        csslint:
-            options:
-                csslintrc: '.csslintrc.json'
-            app:
-                src: ["#{SOURCE}/css/app/**/*.css","!#{SOURCE}/css/app/**/*.min.css"]
-        #自动根据指定排列css属性
-        csscomb:
-            options:
-                config: '.csscomb.json'
-            app:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/app"
-                        src: ['**/*.css','!**/*.min.css']
-                        ext: '.css'
-                        dest: "#{SOURCE}/css/app"
-                    }
-                ]
-        #压缩css文件
-        cssmin:
-            app:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/css/app"
-                        src: ['**/*.css','!**/*.min.css']
-                        ext: '.css'
-                        dest: "#{BUILD}/css/app"
-                    }
-                ]
-        md5:
-            options:
-                encoding: null
-                keepBasename: true
-                keepExtension: true
-            css:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{BUILD}"
-                        src: ['css/app/**/*.css','js/app/**/*.js']
-                        dest: "#{BUILD}"
-                        filter: (src)->
-                            !/-[a-f0-9]{32}.(js|css)$/.test src
-                    }
-                ]
-                options:
-                    afterEach: (filename, options)->
-                        # 旧文件随你删不删除
-                        fs.unlinkSync filename.newPath
-                    after: (fileChanges, options)->
-                        resourceMap = []
-                        resourceMap = fileChanges.map (fileChange)->
-                            oldPath = path.relative "#{BUILD}", fileChange.oldPath
-                            newPath = path.relative "#{BUILD}", fileChange.newPath
-                            oldPath = (oldPath.split path.sep).join '/'
-                            newPath = (newPath.split path.sep).join '/'
-                            # console.log oldPath,newPath
-                            "    \"/#{oldPath}\": \"/#{newPath}\""
-                        resourceMap = '{\n'+resourceMap.join(',\n')+'\n}'
-                        fs.writeFileSync "#{__dirname}/resource-map.json",resourceMap
-        #压缩图片素材
-        imagemin:
-            common:
-                files: [
-                    {
-                        expand: true
-                        cwd: "#{SOURCE}/img"
-                        src: "**/*.{png,jpg,gif}"
-                        dest: "#{BUILD}/img"
-                    }
-                ]
-        #监控文件变化，实时进行编译
-        watch:
-            options:
-                spawn: true
-            #因为js开发过程不需要实时编译，所以不进行压缩合并等，但只做语法检测
-            app_js:
-                files: ["src/js/app/**/*.js"]
-                tasks: ['jshint']
-            #css因为开发过程需要编译less，所以进行监听，但只进行编译和语法检测
-            app_css:
-                files: ["src/css/app/**/*.less"]
-                tasks: ['less:app','csslint:app']
-    # Load the plugin that provides the "uglify" task.
-    grunt.loadNpmTasks('grunt-contrib-clean')
-    grunt.loadNpmTasks('grunt-contrib-copy')
-    grunt.loadNpmTasks('grunt-contrib-concat')
-    grunt.loadNpmTasks('grunt-contrib-uglify')
-    grunt.loadNpmTasks('grunt-contrib-jshint')
-    grunt.loadNpmTasks('grunt-contrib-requirejs')
-    grunt.loadNpmTasks('grunt-contrib-less')
-    grunt.loadNpmTasks('grunt-contrib-csslint')
-    grunt.loadNpmTasks('grunt-csscomb')
-    grunt.loadNpmTasks('grunt-contrib-cssmin')
-    grunt.loadNpmTasks('grunt-md5')
-    grunt.loadNpmTasks('grunt-contrib-imagemin')
-    grunt.loadNpmTasks('grunt-contrib-watch')
-
-    # Default task(s). 上线前一次性构建一次
-    grunt.registerTask('default', ['clean', 'copy', 'jshint', 'uglify', 'less', 'csslint', 'csscomb', 'cssmin', 'md5', 'imagemin'])


Don't shut down on uncaught EPIPE

Don't shut down on uncaught EPIPE

--- services/real-time/app.coffee
+++ services/real-time/app.coffee
@@ -141,6 +141,8 @@
 	if Settings.errors?.catchUncaughtErrors
 		process.removeAllListeners('uncaughtException')
 		process.on 'uncaughtException', (error) ->
+			if error.code == 'EPIPE'
+				return logger.warn err: error, 'attempted to write to disconnected client'
 			logger.error err: error, 'uncaught exception'
 			if Settings.errors?.shutdownOnUncaughtError
 				drainAndShutdown('SIGABRT')


Method group comments

Method group comments

--- src/trix/models/editor.coffee
+++ src/trix/models/editor.coffee
@@ -18,8 +18,6 @@
     @undoManager = new Trix.UndoManager @composition
     @composition.loadSnapshot(snapshot)

-  # --
-
   getDocument: ->
     @composition.document

@@ -32,7 +30,7 @@
   toJSON: ->
     @getSnapshot()

-  # --
+  # Document manipulation

   deleteInDirection: (direction) ->
     @composition.deleteInDirection(direction)
@@ -49,7 +47,7 @@
   insertString: (string) ->
     @composition.insertString(string)

-  # --
+  # Selection

   getClientRectAtPosition: (position) ->
     location = @getDocument().locationFromPosition(position)
@@ -67,7 +65,7 @@
   setSelectedRange: (selectedRange) ->
     @composition.setSelectedRange(selectedRange)

-  # --
+  # Current attributes

   getCurrentAttributes: ->
     @composition.getCurrentAttributes()
@@ -81,7 +79,7 @@
   setCurrentAttribute: (name, value) ->
     @composition.setCurrentAttribute(name, value)

-  # --
+  # Indentation level

   canDecreaseIndentationLevel: ->
     @composition.canDecreaseIndentationLevel()
@@ -97,7 +95,7 @@
     if @canIncreaseIndentationLevel()
       @composition.increaseIndentationLevel()

-  # --
+  # Undo/redo

   canRedo: ->
     @undoManager.canRedo()


Fix IE11 and Edge compatibility in notification setTimeout

--- src/views/notification.coffee
+++ src/views/notification.coffee
@@ -32,7 +32,7 @@
             f = (e) ->
                 if not($(e.target).is(view.$el) or view.$el.has(".#{e.target.className}").length)
                     $('body').off 'keydown click zoomstart', f
-                    view.hideTimer = setTimeout () ->
+                    view.hideTimer = window.setTimeout () ->
                         view.model.set 'show', false
                     , 6000
             @delayedHideListener = f


account: minor

account: minor

--- client/account/lib/index.coffee
+++ client/account/lib/index.coffee
@@ -74,6 +74,7 @@
     wrapper = new AccountListWrapper
       cssClass : "settings-list-wrapper #{kd.utils.slugify title}"
       , itemData
+
     wrapper.on 'ModalCloseRequested', @bound 'closeModal'

     new KDTabPaneView


parse XML from PubMed to reteive both full citation and short citation

--- client/coffee/references.coffee
+++ client/coffee/references.coffee
@@ -72,14 +72,50 @@
         spinner.toggleClass('spinner-active')
         pubmedID = tmpl.find('input[name=pubmedID]').value
         citation = tmpl.find('textarea[name=fullCitation]')
-        url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=#{pubmedID}&rettype=docsum&retmode=text"
+        name = tmpl.find('input[name=name]')
+
+        url= "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=#{pubmedID}&rettype=docsum&retmode=xml"
         HTTP.get url, (err, result) ->
-            if err
-                text = "An error occurred."
-            else
-                text = result.content.replace('1: ','').replace(/[\n\r]/g, '')
-            citation.value = text
+            # assume an error occurred by default
+            fullCitation = "An error occurred."
+            shortCitation = ""
+
+            if result
+                xmlDoc = $.parseXML(result.content)
+                xml = $(xmlDoc)
+                window.xml = xml
+
+                err = xml.find("ERROR")
+                if err.length >= 1
+                    fullCitation = xml.find("ERROR").text()
+                else
+                    # Parse XML for text, we use the AuthorList children to
+                    # filter for both "Author" and "CollectiveName" fields,
+                    # as an example see PMID 187847.
+                    authors = (auth.innerHTML for auth in xml.find('Item[Name=AuthorList]').children())
+                    title = xml.find("Item[Name=Title]").text()
+                    journal_source = xml.find("Item[Name=Source]").text()
+                    so =  xml.find("Item[Name=SO]").text()
+                    pmid = xml.find("Id").text()
+                    year = pubDate = xml.find("Item[Name=PubDate]").text().substr(0,4)
+
+                    # build short-citation
+                    first = authors[0].substr(0, authors[0].search(" "))
+                    shortCitation = "#{first} #{year}"
+                    if authors.length>2
+                        shortCitation = "#{first} et al. #{year}"
+                    else if authors.length is 2
+                        second = authors[1].substr(0, authors[1].search(" "))
+                        shortCitation = "#{first} and #{second} #{year}"
+
+                    # build full-citation, using the PubMed Summary format, found here:
+                    # http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=#{pubmedID}&rettype=docsum&retmode=text
+                    fullCitation = "#{authors.join(', ')}. #{title}. #{journal_source}. #{so}. PubMed PMID: #{pmid}."
+
+            # turn-off spinner, and add retrieved content to input fields
             spinner.toggleClass('spinner-active')
+            citation.value = fullCitation
+            name.value = shortCitation

     'change select[name=referenceType]': (evt, tmpl) ->
         toggleFieldDisplays(tmpl);


chat: start chat for visible state check

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1133,7 +1133,7 @@

     return @createChatPane()  unless @chat

-    @chat.show()
+    @chat.start()

   prepareCollaboration: ->


Collaboration: Improve participant leave session procedure

--- client/ide/AppController.coffee
+++ client/ide/AppController.coffee
@@ -1501,10 +1501,10 @@

   setMachineUser: (accounts, share = yes, callback = noop) ->

-    return  unless @mountedMachine.jMachine.credential is KD.nick()
-
     usernames = accounts.map (account) -> account.profile.nickname
-    usernames = usernames.filter (username) -> username isnt KD.nick()
+
+    if @amIHost
+      usernames = usernames.filter (username) -> username isnt KD.nick()

     return  unless usernames.length

@@ -1570,7 +1570,7 @@

       when 'ParticipantWantsToLeave'

-        @unshareMachineAndKlient data.origin  if @amIHost
+        @statusBar.removeParticipantAvatar origin

       when 'ParticipantKicked'

@@ -1644,11 +1644,14 @@

     @showModal options, =>
       @broadcastMessages.push origin: KD.nick(), type: 'ParticipantWantsToLeave'
-      @removeMachineNode()
       @modal.destroy()
       @rtm = null
-      KD.singletons.socialapi.channel.leave channelId: @socialChannel.getId()
-      KD.singletons.router.handleRoute '/IDE'
+
+      options = channelId: @socialChannel.getId()
+      KD.singletons.socialapi.channel.leave options, (err) =>
+        return KD.showError err  if err
+        @setMachineUser [KD.whoami()], no, ->
+          KD.singletons.router.handleRoute '/IDE'

   removeMachineNode: ->


Correct beginner mode loop test name

Correct beginner mode loop test name

--- test/src/jstest.coffee
+++ test/src/jstest.coffee
@@ -456,7 +456,7 @@
       'Combines if-else')
   start()

-asyncTest 'JS Elif', ->
+asyncTest 'JS beginner mode loops', ->
   customJS = new JavaScript({
     categories: {loops: {color: 'green', beginner: true}}
   })


remove most side-effects

remove most side-effects

--- src/builtins.coffee
+++ src/builtins.coffee
@@ -33,7 +33,13 @@
     FunctionDocumentationComponent fns: _.map documented_fns, (name) -> {name, doc: fns[name].doc}

   modules.create 'builtins', ({fn, cmd}) ->
-    cmd 'help', 'Shows this help', (cmd) ->
+    component_cmd= (name, doc, f) ->
+      cmd name, doc, -> @add_component f.apply @, arguments
+
+    component_fn = (name, doc, f) ->
+      fn name, doc, -> @add_component f.apply @, arguments
+
+    component_cmd 'help', 'Shows this help', (cmd) ->
       if arguments.length > 0
         if _.isString cmd
           op = @imported_context_fns[cmd]
@@ -46,11 +52,10 @@
           else
             fns = _.object _.map cmd, (v, k) -> [k, v._lead_context_fn]
         unless fns?
-          @pre "#{cmd} is not a command."
-          return
+          return pre value: "#{cmd} is not a command."
       else
         fns = @imported_context_fns
-      @add_component help(fns)
+      help fns

     KeyBindingComponent = React.createClass
       render: ->
@@ -61,7 +66,7 @@
             React.DOM.td {}, command.doc
           ]

-    cmd 'keys', 'Shows the key bindings', ->
+    component_cmd 'keys', 'Shows the key bindings', ->
       all_keys = {}
       # TODO some commands are functions instead of names
       build_map = (map) ->
@@ -74,24 +79,24 @@
           build_map CodeMirror.keyMap[name] for name in fallthroughs
       build_map CodeMirror.keyMap.lead

-      @add_component KeyBindingComponent keys: all_keys, commands: CodeMirror.commands
+      KeyBindingComponent keys: all_keys, commands: CodeMirror.commands

     fn 'In', 'Gets previous input', (n) ->
       @value @get_input_value n

-    fn 'object', 'Prints an object as JSON', (o) ->
+    component_fn 'object', 'Prints an object as JSON', (o) ->
       try
         s = JSON.stringify(o, null, '  ')
       catch
         s = null
       s ||= new String o
-      @add_component source value: s, language: 'json'
+      source value: s, language: 'json'

     MarkdownComponent = React.createClass
       render: -> React.DOM.div className: 'user-html', dangerouslySetInnerHTML: __html: marked @props.value

-    fn 'md', 'Renders Markdown', (string) ->
-      @add_component MarkdownComponent value: string
+    component_fn 'md', 'Renders Markdown', (string) ->
+      MarkdownComponent value: string

     text = React.createClass
       render: -> React.DOM.p {}, @props.value
@@ -102,20 +107,20 @@
     html = React.createClass
       render: -> React.DOM.div className: 'user-html', dangerouslySetInnerHTML: __html: @props.value

-    fn 'text', 'Prints text', (string) ->
-      @add_component text value: string
+    component_fn 'text', 'Prints text', (string) ->
+      text value: string

-    fn 'pre', 'Prints preformatted text', (string) ->
-      @add_component pre value: string
+    component_fn 'pre', 'Prints preformatted text', (string) ->
+      pre value: string

-    fn 'html', 'Adds some HTML', (string) ->
-      @add_component html value: string
+    component_fn 'html', 'Adds some HTML', (string) ->
+      html value: string

     ErrorComponent = React.createClass
       render: -> React.DOM.pre {className: 'error'}, @props.message

-    fn 'error', 'Shows a preformatted error message', (message) ->
-      @add_component ErrorComponent {message}
+    component_fn 'error', 'Shows a preformatted error message', (message) ->
+      ErrorComponent {message}

     example = React.createClass
       render: -> React.DOM.div {className: 'example', onClick: @on_click}, @transferPropsTo source()
@@ -125,19 +130,19 @@
         else
           @props.ctx.set_code @props.value

-    fn 'example', 'Makes a clickable code example', (value, opts) ->
-      @add_component example {ctx: @, value, run: opts?.run ? true, language: 'coffeescript'}
+    component_fn 'example', 'Makes a clickable code example', (value, opts) ->
+      example {ctx: @, value, run: opts?.run ? true, language: 'coffeescript'}

     source = React.createClass
       render: -> React.DOM.pre()
       componentDidMount: (node) -> format_code @props.value, @props.language, node

-    fn 'source', 'Shows source code with syntax highlighting', (language, value) ->
-      @add_component source {language, value}
+    component_fn 'source', 'Shows source code with syntax highlighting', (language, value) ->
+      source {language, value}

-    cmd 'intro', 'Shows the intro message', ->
+    component_cmd 'intro', 'Shows the intro message', ->
       ctx = @
-      @add_component React.createClass(
+      React.createClass(
         render: -> React.DOM.div {}, [
           text value: "Welcome to lead.js!\n\nPress Shift+Enter to execute the CoffeeScript in the console. Try running"
           example value: "browser '*'", ctx: ctx, run: true
@@ -154,14 +159,14 @@
     LinkComponent = React.createClass
       render: -> React.DOM.a {href: @props.href}, @props.value

-    cmd 'permalink', 'Create a link to the code in the input cell above', (code) ->
+    component_cmd 'permalink', 'Create a link to the code in the input cell above', (code) ->
       a = document.createElement 'a'
       # TODO app should generate links
       a.href = location.href
       a.hash = null
       code ?= @previously_run()
       a.search = '?' + encodeURIComponent btoa code
-      @add_component LinkComponent href: a.href, value: a.href
+      LinkComponent href: a.href, value: a.href

     fn 'websocket', 'Runs commands from a web socket', (url) ->
       ws = new WebSocket url


Texture: Reject non-power-of-two JPG/PNG textures.

--- engine/texture.coffee
+++ engine/texture.coffee
@@ -125,6 +125,9 @@
                     @image.onload = =>
                         @context.main_loop.add_frame_callback =>
                             {@width, @height} = @image
+                            if nearest_POT(@width) != @width or nearest_POT(@height) != @height
+                                @loaded = false
+                                reject "Texture #{@name} has non-power-of-two size #{@width}x#{@height}"
                             @type = 'image'
                             @restore() #TODO: use @upload and @configure instead when possible
                             resolve @
@@ -316,4 +319,13 @@
     context.textures[name] = tex
     return tex

+next_POT = (x) ->
+    x = Math.max(0, x-1)
+    return Math.pow(2, Math.floor(Math.log(x)/Math.log(2))+1)
+
+nearest_POT = (x) ->
+    x = Math.max(0, x)
+    return Math.pow(2, Math.round(Math.log(x)/Math.log(2)))
+
+
 module.exports = {Texture, get_texture_from_path_legacy}


Namespace changes (BREAKS COMPATIBILITY)

--- src/checkout.coffee
+++ src/checkout.coffee
@@ -1,6 +1,3 @@
-window.vtex or= {}
-window.vtex.checkout or= {}
-
 ###
   UTILITY FUNCTIONS
 ###
@@ -30,10 +27,10 @@
 readSubcookie = (name, cookie) ->
 	mapize(cookie, '&', '=', ((s)->s), unescape)[name]

-# vtex.js
+# Checkout module
 #
-# Offers convenient methods for using the API in JS.
-class VTEX
+# Offers convenient methods for using the Checkout API in JS.
+class Checkout
 	# Instantiate the SDK.
 	#
 	# @param ajax [Function] (default = $.ajax) an AJAX function that must follow the convention, i.e., accept an object of options such as 'url', 'type' and 'data', and return a promise.
@@ -379,9 +376,8 @@
 	_getRemoveGiftRegistryURL: =>
 		@HOST_ORDER_FORM_URL + "/giftRegistry/#{@_getOrderFormId()}/remove"

-# Compatibility with old clients - DEPRECATED!
-window.vtex.checkout.API = CheckoutAPI
-window.vtex.checkout.API.version = 'VERSION'
-
-window.vtex.checkout.SDK = CheckoutAPI
-window.vtex.checkout.SDK.version = 'VERSION'+
+window.vtex or= {}
+window.vtex.Checkout = Checkout
+window.vtex.Checkout.version = 'VERSION'
+window.vtex.checkout = new window.vtex.Checkout()


Update all conditions

Update all conditions


Removed apostrophies and quotes

Removed apostrophies and quotes

--- src/slug.coffee
+++ src/slug.coffee
@@ -85,7 +85,7 @@
             char = unicode.name.toLowerCase()
             char = char.replace(word, '') for word in removelist
             char = char.replace(/^\s+|\s+$/g, '')
-        char = char.replace(/[^\w\s$\*\_\+~\.\(\)\'\"\!\-:@]/g, '') # allowed
+        char = char.replace(/[^\w\s$\*\_\+~\.\(\)\!\-:@]/g, '') # allowed
         result += char
     result = result.replace(/^\s+|\s+$/g, '') # trim leading/trailing spaces
     result = result.replace(/[-\s]+/g, replacement) # convert spaces


improve checking for fixed positioning

improve checking for fixed positioning

--- src/step.coffee
+++ src/step.coffee
@@ -4,22 +4,35 @@
     restrict: 'EAC'
     controller: ['$scope', ($scope) ->
       @element = null
+      _stepIsFixed = null

       _isFixed = (element) ->
-        while element.parentNode.tagName != 'HTML'
-          if $window.getComputedStyle(element)['position'] == 'fixed'
-            return true
-          element = element.parentNode
+        if !_stepIsFixed?
+          while element.parentNode.tagName != 'HTML'
+            if $window.getComputedStyle(element)['position'] == 'fixed'
+              _stepIsFixed = true
+              break
+            else
+              _stepIsFixed = false
+              element = element.parentNode

-        return false
+        _stepIsFixed

       # JQLite doesnt support offset, so we implement it here
       _boundingOffset = (element) ->
         return unless element?
         doc = element.ownerDocument
         documentElem = doc.documentElement
-        documentOffset = if _isFixed(element) then { x: 0, y: 0 } else { x: $window.pageXOffset || documentElem.scrollLeft, y: $window.pageYOffset || documentElem.scrollTop }
         box = element.getBoundingClientRect?()
+
+        if _isFixed(element)
+          documentOffset =
+            x: 0,
+            y: 0
+        else
+          documentOffset =
+            x: $window.pageXOffset || documentElem.scrollLeft,
+            y: $window.pageYOffset || documentElem.scrollTop

         return unless box?


--harmony_collections is no more needed

--harmony_collections is no more needed

--- src/task.coffee
+++ src/task.coffee
@@ -98,8 +98,7 @@
     taskPath = taskPath.replace(/\\/g, "\\\\")

     env = _.extend({}, process.env, {taskPath, userAgent: navigator.userAgent})
-    args = [bootstrap, '--harmony_collections']
-    @childProcess = child_process.fork '--eval', args, {env, cwd: __dirname}
+    @childProcess = child_process.fork '--eval', [bootstrap], {env, cwd: __dirname}

     @on "task:log", -> console.log(arguments...)
     @on "task:warn", -> console.warn(arguments...)


Check CHOCOLATEYINSTALL env var first

Check CHOCOLATEYINSTALL env var first

--- src/test.coffee
+++ src/test.coffee
@@ -26,12 +26,12 @@
   showHelp: (argv) -> @parseOptions(argv).showHelp()

   getChocolateyAtomPath: ->
+    if process.env.CHOCOLATEYINSTALL
+      atomCommand = path.join(process.env.CHOCOLATEYINSTALL, 'bin', 'atom.exe')
+      return atomCommand if fs.existsSync(atomCommand)
+
     if process.env.ALLUSERSPROFILE
       atomCommand = path.join(process.env.ALLUSERSPROFILE, 'chocolatey', 'bin', 'atom.exe')
-      return atomCommand if fs.existsSync(atomCommand)
-
-    if process.env.CHOCOLATEYINSTALL
-      atomCommand = path.join(process.env.CHOCOLATEYINSTALL, 'bin', 'atom.exe')
       return atomCommand if fs.existsSync(atomCommand)

     null


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/text.coffee
+++ src/text.coffee
@@ -13,7 +13,7 @@

   self.unbind "draw"
   self.bind "afterTransform", (canvas) ->
-    canvas.font "20px
+    canvas.font "20px #{I.f}
     canvas.drawText
       color: "rgba(0, 0, 0, #{I.alpha})"
       x: I.x + 1


Default esper to off for now

Default esper to off for now

--- src/defaults.coffee
+++ src/defaults.coffee
@@ -19,4 +19,4 @@
   includeStyle: true
   protectAPI: false
   protectBuiltins: true
-  useInterpreter: true
+  useInterpreter: false


show absolute time if 4 weeks or more

show absolute time if 4 weeks or more

--- resources/assets/coffee/react/_components/beatmapset-mapping.coffee
+++ resources/assets/coffee/react/_components/beatmapset-mapping.coffee
@@ -52,7 +52,7 @@
             TimeWithTooltip
               dateTime: @props.beatmapset[attribute]
               key: 'timeago'
-              relative: true
+              relative: Math.abs(moment().diff(moment(@props.beatmapset[attribute]), 'weeks')) < 4

   userLink: (user) ->


Remove missed debugging console.log

Remove missed debugging console.log

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -124,7 +124,6 @@
 		fetchLogs = (logFile, blgFile) ->

 			getFile = (name, file) ->
-				console.log "[getFile]", name, file
 				opts =
 					method:"GET"
 					params:


add missing cache bust on log file

add missing cache bust on log file

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -194,6 +194,9 @@
 					opts.url = "/project/#{$scope.project_id}/build/#{file.build}/output/#{name}"
 				else
 					opts.url = "/project/#{$scope.project_id}/output/#{name}"
+				# check if we need to bust cache (build id is unique so don't need it in that case)
+				if not file?.build?
+					opts.params.cache_bust = "#{Date.now()}"
 				return $http(opts)

 			# accumulate the log entries


load output files from cached build when known

--- services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
+++ services/web/public/coffee/ide/pdf/controllers/PdfController.coffee
@@ -41,18 +41,22 @@
 				$scope.pdf.failure = true
 				fetchLogs()
 			else if response.status == "success"
+				# define the base url
 				$scope.pdf.url = "/project/#{$scope.project_id}/output/output.pdf?cache_bust=#{Date.now()}"
+				# add a query string parameter for the compile group
 				if response.compileGroup?
 					$scope.pdf.compileGroup = response.compileGroup
 					$scope.pdf.url = $scope.pdf.url + "&compileGroup=#{$scope.pdf.compileGroup}"
+				# make a cache to look up files by name
 				fileByPath = {}
 				for file in response.outputFiles
 					fileByPath[file.path] = file
+				# if the pdf file has a build number, pass it to the clsi
 				if fileByPath['output.pdf']?.build?
 					build = fileByPath['output.pdf'].build
 					$scope.pdf.url = $scope.pdf.url + "&build=#{build}"

-				fetchLogs()
+				fetchLogs(fileByPath['output.log'])

 			IGNORE_FILES = ["output.fls", "output.fdb_latexmk"]
 			$scope.pdf.outputFiles = []
@@ -65,8 +69,9 @@
 						file.name = file.path
 					$scope.pdf.outputFiles.push file

-		fetchLogs = () ->
-			$http.get "/project/#{$scope.project_id}/output/output.log"
+		fetchLogs = (outputFile) ->
+			qs = if outputFile?.build? then "?build=#{outputFile.build}" else ""
+			$http.get "/project/#{$scope.project_id}/output/output.log" + qs
 				.success (log) ->
 					$scope.pdf.rawLog = log
 					logEntries = LogParser.parse(log, ignoreDuplicates: true)
@@ -156,7 +161,8 @@
 			$scope.pdf.showRawLog = !$scope.pdf.showRawLog

 		$scope.openOutputFile = (file) ->
-			window.open("/project/#{$scope.project_id}/output/#{file.path}")
+			qs = if file.build? then "?build=#{file.build}" else ""
+			window.open("/project/#{$scope.project_id}/output/#{file.path}#{qs}")

 		$scope.openClearCacheModal = () ->
 			modalInstance = $modal.open(


Add unit test for ProjectController.userProjectsJson

--- services/web/test/unit/coffee/Project/ProjectControllerTests.coffee
+++ services/web/test/unit/coffee/Project/ProjectControllerTests.coffee
@@ -521,7 +521,32 @@
 				@ProjectUpdateHandler.markAsOpened.calledWith(@project_id).should.equal true
 				done()
 			@ProjectController.loadEditor @req, @res
-
+
+	describe 'userProjectsJson', ->
+		beforeEach (done) ->
+			projects = [
+				{archived: true,  id: 'a', name: 'A', accessLevel: 'a', somethingElse: 1}
+				{archived: false, id: 'b', name: 'B', accessLevel: 'b', somethingElse: 1}
+				{archived: false, id: 'c', name: 'C', accessLevel: 'c', somethingElse: 1}
+				{archived: false, id: 'd', name: 'D', accessLevel: 'd', somethingElse: 1}
+			]
+			@ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, [])
+			@ProjectController._buildProjectList = sinon.stub().returns(projects)
+			@AuthenticationController.getLoggedInUserId = sinon.stub().returns 'abc'
+			done()
+
+		it 'should produce a list of projects', (done) ->
+			@res.json = (data) =>
+				expect(data).to.deep.equal {
+					projects: [
+						{_id: 'b', name: 'B', accessLevel: 'b'},
+						{_id: 'c', name: 'C', accessLevel: 'c'},
+						{_id: 'd', name: 'D', accessLevel: 'd'}
+					]
+				}
+				done()
+			@ProjectController.userProjectsJson @req, @res, @next
+
 	describe '_isInPercentageRollout', ->
 		before ->
 			@ids = [


fix jquery impl

fix jquery impl

--- lib/assets/javascripts/nice_imp_jquery.js.coffee
+++ lib/assets/javascripts/nice_imp_jquery.js.coffee
@@ -29,9 +29,19 @@
 	# remove all elements which are not of current state and all elements
 	# which are of current state and secondly annotated to be always updated.
 	@remove_state_elements: (event) ->
-		$("[data-state]").not("[data-state~='#{event.curr_state}']").not("[data-state~='all']").remove()
-		$("[data-state~='#{event.curr_state}'][data-state-update]").not("[data-state~='all']").remove()
-		$("[data-state~='all'][data-state-update]").remove()
+		a = jQuery.makeArray( $("[data-state]").not("[data-state~='#{event.curr_state}']").not("[data-state~='all']") )
+		a = a.concat jQuery.makeArray( $("[data-state~='#{event.curr_state}'][data-state-update]").not("[data-state~='all']") )
+		a = a.concat jQuery.makeArray( $("[data-state~='all'][data-state-update]") )
+
+		for elem in a
+			$(elem).addClass('state-removing')
+			if $(elem).css("-webkit-transition-property") == "none" || $(elem).css("-webkit-transition-property") == "" || $(elem).css("-webkit-transition-property") == "all"
+				$(elem).remove()
+			else
+				$(elem).bind 'transitionend webkitTransitionEnd oTransitionEnd otransitionend', (e) ->
+					$(this).remove()
+
+		return

 	# remove everything under the "root tag"
 	@clean_root_tree: (event) ->


Changing how SLI admin is hidden from role mapping view

--- sli/admin-tools/admin-rails/app/assets/javascripts/realms.js.coffee
+++ sli/admin-tools/admin-rails/app/assets/javascripts/realms.js.coffee
@@ -18,18 +18,6 @@
         return toReturn
 }`

-`extractData = function() {
-	var map = {};
-	$("#mTable > tr").each(function () {
-		var cRole = $(this).children("td:eq(0)")[0].innerText;
-		var sRole = $(this).children("td:eq(1)")[0].innerText;
-		if (!map[sRole])
-			map[sRole] = [];
-		map[sRole].push(cRole);
-	});
-	return map;
-}
-`

 # Takes our table data and converts it into the mapping object
 # needed by the API.
@@ -79,6 +67,9 @@
         table.empty();

         for (var i in data) {
+		if (data[i][1] == 'SLI Administrator') {
+			continue
+		}
 		var tr = $("
");
         	tr.append($("" + data[i][0] +  "" + data[i][1] +  "	"));

@@ -88,9 +79,7 @@
 		//fade in last one
 		table.append(tr);
 		// We typically wouldnt expect a user to remap sli admin, so dont show in list
-		if (data[i][1] != 'SLI Administrator') {
-			tr.show();
-		}
+		tr.show();
 		if (editable) {
         		$(tr).find(".deleteButton").each(function() {
                 		$(this).click(function() {
@@ -208,7 +197,7 @@
                 for (var i in SLI_ROLES) {
                       rMap.push([SLI_ROLES[i], SLI_ROLES[i]]);
                 }
-		rMap.push(["SLI Administrator", "SLI Administrator"]);
+                rMap.push(["SLI Administrator", "SLI Administrator"]);
                 var success = function(data) {
                         rMap = getTableData(data.mappings);
                         sortTable(rMap, sortCol, sortOrder[sortCol]);


Fix MutliSelectSplitInteractionHandler specs

--- spec/components/multiselect-split-interaction-handler-spec.coffee
+++ spec/components/multiselect-split-interaction-handler-spec.coffee
@@ -14,6 +14,9 @@
     @itemAfterKeyboardFocus = new Thread(id:'after-keyboard-focus')

     data = [@item, @itemFocus, @itemAfterFocus, @itemKeyboardFocus, @itemAfterKeyboardFocus]
+
+    @onFocusItem = jasmine.createSpy('onFocusItem')
+    @onSetCursorPosition = jasmine.createSpy('onSetCursorPosition')
     @selection = []
     @dataSource =
       selection:
@@ -34,13 +37,19 @@
         _.findIndex data, (item) -> item.id is id
       count: -> data.length

+    @props =
+      dataSource: @dataSource
+      keyboardCursorId: 'keyboard-focus'
+      focused: @itemFocus
+      focusedId: 'focus'
+      onFocusItem: @onFocusItem
+      onSetCursorPosition: @onSetCursorPosition
+
     @collection = 'threads'
-    @handler = new MultiselectSplitInteractionHandler(@dataSource, @collection)
     @isRootSheet = true
+    @handler = new MultiselectSplitInteractionHandler(@props)

     spyOn(WorkspaceStore, 'topSheet').andCallFake => {root: @isRootSheet}
-    spyOn(Actions, 'setFocus')
-    spyOn(Actions, 'setCursorPosition')

   it "should always show focus", ->
     expect(@handler.shouldShowFocus()).toEqual(true)
@@ -56,25 +65,17 @@
   describe "onClick", ->
     it "should focus the list item and indicate it was focused via click", ->
       @handler.onClick(@item)
-      expect(Actions.setFocus).toHaveBeenCalledWith({
-        collection: @collection
-        item: @item
-        usingClick: true
-      })
+      expect(@onFocusItem).toHaveBeenCalledWith(@item)

   describe "onMetaClick", ->
     describe "when there is currently a focused item", ->
-      beforeEach ->
-        spyOn(FocusedContentStore, 'focused').andCallFake => @itemFocus
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> 'focus'
-
       it "should turn the focused item into the first selected item", ->
         @handler.onMetaClick(@item)
         expect(@dataSource.selection.add).toHaveBeenCalledWith(@itemFocus)

       it "should clear the focus", ->
         @handler.onMetaClick(@item)
-        expect(Actions.setFocus).toHaveBeenCalledWith({collection: @collection, item: null})
+        expect(@onFocusItem).toHaveBeenCalledWith(null)

     it "should toggle selection", ->
       @handler.onMetaClick(@item)
@@ -87,9 +88,6 @@

   describe "onShiftClick", ->
     describe "when there is currently a focused item", ->
-      beforeEach ->
-        spyOn(FocusedContentStore, 'focused').andCallFake => @itemFocus
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> 'focus'

       it "should turn the focused item into the first selected item", ->
         @handler.onMetaClick(@item)
@@ -97,7 +95,7 @@

       it "should clear the focus", ->
         @handler.onMetaClick(@item)
-        expect(Actions.setFocus).toHaveBeenCalledWith({collection: @collection, item: null})
+        expect(@onFocusItem).toHaveBeenCalledWith(null)

     it "should expand selection", ->
       @handler.onShiftClick(@item)
@@ -124,41 +122,33 @@

     describe "when the select option is passed", ->
       it "should turn the existing focused item into a selected item", ->
-        spyOn(FocusedContentStore, 'focused').andCallFake => @itemFocus
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> 'focus'
         @handler.onShift(1, {select: true})
         expect(@dataSource.selection.add).toHaveBeenCalledWith(@itemFocus)

       it "should walk the selection to the shift target", ->
-        spyOn(FocusedContentStore, 'focused').andCallFake => @itemFocus
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> 'focus'
         @handler.onShift(1, {select: true})
         expect(@dataSource.selection.walk).toHaveBeenCalledWith({current: @itemFocus, next: @itemAfterFocus})

     describe "when one or more items is selected", ->
       it "should move the keyboard cursor", ->
         @selection = [@itemFocus, @itemAfterFocus, @itemKeyboardFocus]
-        spyOn(FocusedContentStore, 'keyboardCursor').andCallFake => @itemKeyboardFocus
-        spyOn(FocusedContentStore, 'keyboardCursorId').andCallFake -> 'keyboard-focus'
         @handler.onShift(1, {})
-        expect(Actions.setCursorPosition).toHaveBeenCalledWith({collection: @collection, item: @itemAfterKeyboardFocus})
+        expect(@onSetCursorPosition).toHaveBeenCalledWith(@itemAfterKeyboardFocus)

     describe "when no items are selected", ->
       it "should move the focus", ->
-        spyOn(FocusedContentStore, 'focused').andCallFake => @itemFocus
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> 'focus'
         @handler.onShift(1, {})
-        expect(Actions.setFocus).toHaveBeenCalledWith({collection: @collection, item: @itemAfterFocus})
+        expect(@onFocusItem).toHaveBeenCalledWith(@itemAfterFocus)

   describe "_checkSelectionAndFocusConsistency", ->
     describe "when only one item is selected", ->
       beforeEach ->
-        spyOn(FocusedContentStore, 'focused').andCallFake -> null
-        spyOn(FocusedContentStore, 'focusedId').andCallFake -> null
         @selection = [@item]
+        @props.focused = null
+        @handler = new MultiselectSplitInteractionHandler(@props)

       it "should clear the selection and make the item focused", ->
         @handler._checkSelectionAndFocusConsistency()
         expect(@dataSource.selection.clear).toHaveBeenCalled()
-        expect(Actions.setFocus).toHaveBeenCalledWith({collection: @collection, item: @item})
+        expect(@onFocusItem).toHaveBeenCalledWith(@item)


Revert "Add delete icon for working periods"

This reverts commit c29ca7598aff89542455ab919c5dd4b7f18f21bf.

Conflicts:
	app/assets/javascripts/backend/intervention_participations.js.coffee

--- app/assets/javascripts/backend/intervention_participations.js.coffee
+++ app/assets/javascripts/backend/intervention_participations.js.coffee
@@ -49,17 +49,17 @@
     $(participationResult).append('')
     $(participationResult).append(' → ')
     $(participationResult).append('')
-    $(new_line).append(participationResult)
+    $(newLine).append(participationResult)

-    $('#working_times .participations').append(new_line)
-
+    $('#working_times .participations').append(newLine)
+
     return

-  $(document).on "submit", '.edit_intervention_participation input[type="submit"]', (event) ->
-    event.preventDefault()
+  $(document).on "submit", '.edit_intervention_participation', (event) ->
+    # event.preventDefault()

-    $('.edit_intervention_participation').submit()
+    # $('.edit_intervention_participation').submit()

     @workingTimesModal = new ekylibre.modal('#working_times')
     @workingTimesModal.getModal().modal 'hide'


DE19697: updating rendering tests

DE19697: updating rendering tests

--- test/spec/roadmapplanningboard/RoadmapPlanningBoardAppSpec.coffee
+++ test/spec/roadmapplanningboard/RoadmapPlanningBoardAppSpec.coffee
@@ -59,6 +59,18 @@
     @createApp().then =>
       expect(@planningBoard.timeline.getId()).toBe @timelineStore.first().getId()

+  it 'should define height based on content window', ->
+    Ext.DomHelper.append Ext.getBody(), '

'
+    @createApp().then =>
+      #test range as jasmine does not like to render html the same with local and test server
+      appHeight = @app._computePanelContentAreaHeight()
+      expect(appHeight).toBe >= 570
+      expect(appHeight).toBe <= 600
+
+  it 'should define height for app', ->
+    @createApp(false, {height: 1000}).then =>
+      expect(@app._computePanelContentAreaHeight()).toBe = 1000
+
   it 'should notify of error if the timeline store fails to load', ->
     @stub @timelineStore, 'load', ->
       deferred = new Deft.promise.Deferred()


Show only results of most recent search.

--- core/htdocs_source/src/pods/oxifield-certidentifier/component.coffee
+++ core/htdocs_source/src/pods/oxifield-certidentifier/component.coffee
@@ -15,8 +15,9 @@
         evt.stopPropagation()
         evt.preventDefault()

+    searchIndex: 0
     searchChanged: Em.observer "search", ->
-        return if @get "mutex"
+        searchIndex = @incrementProperty "searchIndex"
         search = @get "search"
         @set "content.value", search
         @container.lookup("route:openxpki").sendAjax
@@ -24,6 +25,7 @@
                 action: "certificate!autocomplete"
                 query: search
         .then (doc) =>
+            return if searchIndex isnt @get "searchIndex"
             @set "searchResults", doc
             @$().find(".drowdown").addClass "open"


anchoring runs .first against the result of running the anchor selector against the document; only want a single anchor

--- source/view.coffee
+++ source/view.coffee
@@ -57,7 +57,7 @@
     @dom.base = @__localDom = if base?
       base.to_jQuery()
     else if anchor?
-      $(anchor)
+      $(anchor).first()
     else
       jQuery()
     do @rehook


Use turbolinks:load instead

Use turbolinks:load instead

I guess this changed with version bump

--- app/assets/javascripts/trades.coffee
+++ app/assets/javascripts/trades.coffee
@@ -58,5 +58,5 @@
   redditUsernameSearch($($el)) for $el in $(".reddit-username-input")

-$(document).on('page:load', ready)
+$(document).on('turbolinks:load', ready)
 $ ready


Fix the check for a success response from S3.

We need to handle both a 200 and 206 (partial content) response.

--- app/coffee/S3PersistorManager.coffee
+++ app/coffee/S3PersistorManager.coffee
@@ -77,8 +77,8 @@
 			if res.statusCode == 404
 				logger.log bucketName:bucketName, key:key, "file not found in s3"
 				return callback new Errors.NotFoundError("File not found in S3: #{bucketName}:#{key}"), null
-			if res.stausCode != 200
-				logger.log bucketName:bucketName, key:key, "error getting file from s3"
+			if res.statusCode not in [200, 206]
+				logger.log bucketName:bucketName, key:key, "error getting file from s3: #{res.statusCode}"
 				return callback new Error("Got non-200 response from S3: #{res.statusCode}"), null
 			callback null, res
 		s3Stream.on 'error', (err) ->


Remove blanks

Remove blanks

--- app/coffee/modules/base/model.coffee
+++ app/coffee/modules/base/model.coffee
@@ -45,7 +45,7 @@
     getAttrs: (patch=false) ->
         if @._attrs.version?
             @._modifiedAttrs.version = @._attrs.version
-
+
         if patch
             return _.extend({}, @._modifiedAttrs)
         return _.extend({}, @._attrs, @._modifiedAttrs)


horizon3: legend in non log10bands mode

horizon3: legend in non log10bands mode

--- app/frontend/horizon/horizon3.coffee
+++ app/frontend/horizon/horizon3.coffee
@@ -20,6 +20,9 @@
   numBands = 4
   valueExtent = null
   timeExtent = null
+
+  # colors = ["#08519c","#3182bd","#6baed6","#bdd7e7",
+  #           "#bae4b3","#74c476","#31a354","#006d2c"]

   negativeColorRange = [d3.hcl(-139.23, 6.94, 94.62), d3.hcl(-60.84, 60.56, 10)]
   positiveColorRange = [d3.hcl(137.27, 12.24, 85.88), d3.hcl(147.32, 35.76, 10)]
@@ -71,12 +74,13 @@

     (scale(i) for i in [1..numColors])

-
-  # colors = ["#08519c","#3182bd","#6baed6","#bdd7e7",
-  #           "#bae4b3","#74c476","#31a354","#006d2c"]
-  # colors =
-  #   colorsBetween("#e0f3f8","#313695", 6).reverse()  # negative
-  #   .concat(colorsBetween("#e5f5e0","#00441b", 6))   # positive
+  pow10 = (n) ->
+    v = 1
+    if n > 0
+      v *= 10 for i in [1..n]; return v
+    else
+      v /= 10 for i in [1..-n]; return v
+

   colors = []
@@ -307,20 +311,64 @@
     maxOrdMagn = Math.ceil(log10(max))
     m = numBands

+
+    # automatically switch to the linear scale when there is no large difference
+    # between orders of magnitudes
     useLog10Bands = (useLog10BandSplitting  and  maxOrdMagn >= m)

+
+    if showLegend then do ->
+      n = if showNegativeLegend then 2*m else m
+
+
+      parent.select("div.legend")
+        .append("svg")
+          .attr("width", maxOrdMagn*8 + (if useLog10Bands then 20 else 40))
+          .attr("height", n * 15 + 15)
+        .append("g")
+          .attr("class", "content")
+          .attr("transform", "translate(0, 10)")
+
+      legend = parent.select("div.legend").select("g.content")
+
+      #items = bands.map (band) -> [ i, [start, end] ] = band; [ colors[m + i - 1], end ]
+
+      legendItem = legend.selectAll("g.item")
+        .data(
+          if showNegativeLegend
+            ["white"].concat colors
+          else
+            ["white"].concat colors.slice(m, 2*m)
+        )
+        .enter()
+          .append("g")
+            .attr("class", "item")
+            .attr("transform", (d, i) ->
+              "translate(0, #{(n - i) * 15})")
+
+      legendItem.append("rect")
+        .attr("x", 5)
+        .attr("y", 0)
+        .attr("width", 15)
+        .attr("height", 15)
+        .attr("fill", (d, i) -> d)
+
+      legendItem.append("text")
+        .attr("dominant-baseline", "central")
+        .attr("x", 25)
+        .attr("y", 0)
+
+
+
+
     if useLog10Bands
+

       # TODO: find min nonzero abs value in the data and uniformly
       # split the interval between the min and max orders of magnitude
       # (not always by 10, but possibly by 100 or 1000)

-      # TODO: deal with situations when there is no large difference
-      # between orders of magnitudes
-      # (maybe automatically switch to the linear scale?)
-
-      #minOrdMagn = Math.ceil(log10(min))
-      pow10 = (n) -> v = 1; v *= 10 for i in [1..n]; return v
+
       bands = (for i in [1..m]
         magn = maxOrdMagn + (i - m)
         start = (if i is 1 then 0 else pow10(magn - 1))
@@ -328,62 +376,36 @@
         [ i - 1, [ start, end ] ]
       )

-      if showLegend then do ->
-        n = if showNegativeLegend then 2*m else m
-
-
-        parent.select("div.legend")
-          .append("svg")
-            .attr("width", maxOrdMagn*8 + 20)
-            .attr("height", n * 15 + 15)
-          .append("g")
-            .attr("class", "content")
-            .attr("transform", "translate(0, 10)")
-
-        legend = parent.select("div.legend").select("g.content")
-
-        #items = bands.map (band) -> [ i, [start, end] ] = band; [ colors[m + i - 1], end ]
-
-        gitem = legend.selectAll("g.item")
-          .data(
-            #([[-1, [0, 0]]]).concat(bands)
-            #items
-            if showNegativeLegend
-              ["white"].concat colors
+      # colorScale = d3.scale.threshold()
+      # colorScale.range(colors)
+      # colorScale.domain(
+      #   for i in [-m..-1].concat([1..m])
+      #     magn = maxOrdMagn + (Math.abs(i) - m)
+      #     if i == 0
+      #       0
+      #     else if i < 0
+      #       -pow10(magn)
+      #     else
+      #       pow10(magn)
+      # )
+
+
+
+      parent.select("div.legend").selectAll("g.item").select("text")
+        .text((d, i) ->
+          if showNegativeLegend
+            if i < m
+              valueFormat -bands[m - 1 - i][1][1]
+            else if i == m
+              valueFormat 0
             else
-              ["white"].concat colors.slice(m, 2*m)
-          )
-          .enter()
-            .append("g")
-              .attr("class", "item")
-              .attr("transform", (d, i) ->
-                "translate(0, #{(n - i) * 15})")
-
-        gitem.append("rect")
-          .attr("x", 5)
-          .attr("y", 0)
-          .attr("width", 15)
-          .attr("height", 15)
-          .attr("fill", (d, i) -> d)
-
-        gitem.append("text")
-          .attr("dominant-baseline", "central")
-          .attr("x", 25)
-          .attr("y", 0)
-          .text((d, i) ->
-            if showNegativeLegend
-              if i < m
-                valueFormat -bands[m - 1 - i][1][1]
-              else if i == m
-                valueFormat 0
-              else
-                valueFormat bands[i - m - 1][1][1]
+              valueFormat bands[i - m - 1][1][1]
+          else
+            if i > 0
+              valueFormat bands[i - 1][1][1]
             else
-              if i > 0
-                valueFormat bands[i - 1][1][1]
-              else
-                valueFormat 0
-          )
+              valueFormat 0
+        )

       # returns array [ band, [startLimit, endLimit] ]
@@ -397,6 +419,33 @@
               return bands[i - 1]

           return bands[0]
+
+
+    else
+
+
+      parent.select("div.legend").selectAll("g.item").select("text")
+        .text((d, i) ->
+          domain = yscale.domain()
+
+          v = if showNegativeLegend
+            if i < m
+              - (max / m) * (m - i)
+            else if i == m
+              0
+            else
+              (max / m) * (i - m)
+          else
+            if i > 0
+              (max / m) * (i)
+            else
+              0
+
+          valueFormat v
+        )
+
+
+

     parent.selectAll("div.horizon").each (data) ->


showModal : Use only 1 metho to display modal

--- client/app/components/compose.coffee
+++ client/app/components/compose.coffee
@@ -154,7 +154,7 @@

                 ComposeToolbox
                     onSend    : @sendMessage
-                    onDelete  : @onDelete
+                    onDelete  : @deleteDraft
                     onDraft   : @saveDraft
                     onCancel  : @onCancel
                     canDelete : @props.message?
@@ -236,90 +236,56 @@
                 , 0
                 @setState focus: ''

+    showModal: (params, success) ->
+        return unless @state.originalConversationID
+
+        doDelete = =>
+            messageID = @state.id
+            MessageActionCreator.delete {messageID}
+            LayoutActionCreator.hideModal()
+
+            if @props.callback
+                @props.callback()
+                return
+
+            # specific callback
+            success() if _.isFunction success
+
+        _.extend params, action: doDelete
+        LayoutActionCreator.displayModal params
+
     componentWillUnmount: ->
-        # delete draft
-        doDelete = =>
-            window.setTimeout =>
-                LayoutActionCreator.hideModal()
-                messageID = @state.id
-                MessageActionCreator.delete {messageID, silent, isDraft: true, inReplyTo: @props.inReplyTo}
-            , 5
-
-        # save draft one last time
         doSave = =>
-            if @state.originalConversationID?
-                # save one last time the draft, adding the conversationID
-                message =
-                    id            : @state.id
-                    accountID     : @state.accountID
-                    mailboxIDs    : @state.mailboxIDs
-                    from          : @state.from
-                    to            : @state.to
-                    cc            : @state.cc
-                    bcc           : @state.bcc
-                    subject       : @state.subject
-                    isDraft       : true
-                    attachments   : @state.attachments
-                    inReplyTo     : @state.inReplyTo
-                    references    : @state.references
-                    text          : @state.text
-                    html          : @state.html
-                    conversationID: @state.originalConversationID
-                MessageActionCreator.send message, (error, message) ->
-                    if error? or not message?
-                        msg = "#{t "message action draft ko"} #{error}"
-                        LayoutActionCreator.alertError msg
-                    else
-                        msg = "#{t "message action draft ok"}"
-                        LayoutActionCreator.notify msg, autoclose: true
-                        if message.conversationID?
-                            # reload conversation to update its length
-                            cid = message.conversationID
-                            MessageActionCreator.fetchConversation cid
-            else
-
-
-        # If message has not been sent, ask if we should keep it or not
-        #  - if yes, and the draft belongs to a conversation, add the
-        #    conversationID and save the draft
-        #  - if no, delete the draft
-        if not @state.isDeleted and @state.isDraft and @state.id?
-
-            if @state.composeInHTML
-                newContent = MessageUtils.cleanReplyText(@state.html).replace /\s/gim, ''
-                oldContent = MessageUtils.cleanReplyText(@state.initHtml).replace /\s/gim, ''
-                updated = newContent isnt oldContent
-            else
-                updated = @state.text isnt @state.initText
-
-            # if draft has not been updated, delete without asking confirmation
-            silent = @state.isNew and not updated
-            if silent
-                doDelete()
-            else
-                # we need a timeout because of React's components life cycle
-                setTimeout ->
-                    # display a modal asking if we should keep or delete the draft
-                    modal =
-                        title       : t 'app confirm delete'
-                        subtitle    : t 'compose confirm keep draft'
-                        closeModal  : ->
-                            doSave()
-                            LayoutActionCreator.hideModal()
-                        closeLabel  : t 'compose confirm draft keep'
-                        actionLabel : t 'compose confirm draft delete'
-                        action      : doDelete
-                    LayoutActionCreator.displayModal modal
-                , 0
-
+            LayoutActionCreator.hideModal()
+            MessageActionCreator.send @state, (error, message) ->
+                if error? or not message?
+                    msg = "#{t "message action draft ko"} #{error}"
+                    LayoutActionCreator.alertError msg
+                    return
+
+                msg = "#{t "message action draft ok"}"
+                LayoutActionCreator.notify msg, autoclose: true
+
+                # reload conversation to update its length
+                if message.conversationID?
+                    cid = message.conversationID
+                    MessageActionCreator.fetchConversation cid
+
+        init = =>
+            @showModal
+                title       : t 'app confirm delete'
+                subtitle    : t 'compose confirm keep draft'
+                closeLabel  : t 'compose confirm draft keep'
+                actionLabel : t 'compose confirm draft delete'
+                closeModal  : doSave
+
+        setTimeout init, 0

     getInitialState: ->
-
         # edition of an existing draft
         if message = @props.message
             state =
                 composeInHTML: @props.settings.get 'composeInHTML'
-                isNew: false
             if (not message.get('html')?) and message.get('text')
                 state.composeInHTML = false

@@ -331,6 +297,7 @@
         # new draft
         else
             account = @props.accounts.get @props.selectedAccountID
+
             state = MessageUtils.makeReplyMessage(
                 account.get('login'),
                 @props.inReplyTo,
@@ -338,7 +305,12 @@
                 @props.settings.get('composeInHTML'),
                 account.get('signature')
             )
-            state.isNew = true
+
+            from = {}
+            from.name = name if (name = account.get 'name')
+            from.address = address if (address = account.get 'login')
+            state.from = [from]
+
             state.accountID ?= @props.selectedAccountID

         state.isDraft  = true
@@ -375,29 +347,14 @@
             'subject': ['subject']
         getGroupedError error, @state

-    getMessageFromDOM: ->
-        state = _.clone @state
-        message = {}
-
-        from = {}
-        account = @props.accounts.get @state.accountID
-        from.name = name if (name = account.get 'name')
-        from.address = address if (address = account.get 'login')
-        message.from = [from]
-
-        # TODO : get CC
-        # TODO : get BCC
-        # TODO : get TO
-
-        _.extend state, message
-
     sendActionMessage: ->
+        return if @props.isSaving
         if (validate = @validateMessage())
+            console.log 'ERROR', validate
             LayoutActionCreator.alertError t 'compose error no ' + validate[1]
             return

-        message = @getMessageFromDOM()
-        return unless (changed = hasChanged @state, message)
+        message = _.clone @state

         @props.isSaving = true
         MessageActionCreator.send message, (error, message) =>
@@ -438,46 +395,31 @@

             @props.isSaving = false

-    onDelete: (e) ->
-        e.preventDefault()
-
-        subject = @props.message.get 'subject'
-        if subject? and subject isnt ''
+    deleteDraft: (event) ->
+        event.preventDefault() if event
+
+        if _.isEmpty (subject = @state.subject)
             params = subject: subject
             confirmMessage = t 'mail confirm delete', params
         else
             confirmMessage = t 'mail confirm delete nosubject'

         doDelete = =>
-            LayoutActionCreator.hideModal()
-            messageID = @props.message.get('id')
-
-            # this will prevent asking
-            # a second time when unmounting component
-            @setState isDeleted: true, =>
-                MessageActionCreator.delete {messageID}, (error) =>
-                    unless error?
-                        if @props.callback
-                            @props.callback()
-                        else
-                            # Close Message Box
-                            @redirect
-                                direction: 'first'
-                                action: 'account.mailbox.messages'
-                                parameters: [
-                                    @props.selectedAccountID
-                                    @props.selectedMailboxID
-                                ]
-                                fullWidth: true
-
-        modal =
+            @redirect
+                direction: 'first'
+                action: 'account.mailbox.messages'
+                fullWidth: true
+                parameters: [
+                    @props.selectedAccountID
+                    @props.selectedMailboxID
+                ]
+
+        @showModal
             title       : t 'mail confirm delete title'
             subtitle    : confirmMessage
             closeLabel  : t 'mail confirm delete cancel'
             actionLabel : t 'mail confirm delete delete'
-            action      : doDelete
-        LayoutActionCreator.displayModal modal
-
+        , doDelete

     onToggleCc: (e) ->
         toggle = (e) -> e.classList.toggle 'shown'
@@ -496,7 +438,6 @@
     # Get the file picker component (method used to pass it to the editor)
     getPicker: ->
         return @refs.attachments
-

 # set source of attached images
 cleanHTML = (html) ->


Fix Jordans change:y bug that gets fired even when vertical is disabled

--- framer/LayerDraggable.coffee
+++ framer/LayerDraggable.coffee
@@ -177,9 +177,8 @@
 		# See if horizontal/vertical was set and set the offset

 		point = @layer.point
-		point.x = @_layerStartPoint.x + (event.offset.x * (1 / @layer.canvasScaleX() * @layer.scale * @layer.scaleX))
-		point.y = @_layerStartPoint.y + (event.offset.y * (1 / @layer.canvasScaleY() * @layer.scale * @layer.scaleY))
-
+		point.x = @_layerStartPoint.x + (event.offset.x * (1 / @layer.canvasScaleX() * @layer.scale * @layer.scaleX)) if @horizontal
+		point.y = @_layerStartPoint.y + (event.offset.y * (1 / @layer.canvasScaleY() * @layer.scale * @layer.scaleY)) if @vertical

 		# Constraints and overdrag
 		point = @_constrainPosition(point, @_constraints, @overdragScale) if @_constraints
@@ -195,8 +194,8 @@

 		# Pixel align all moves
 		if @pixelAlign
-			point.x = parseInt(point.x)
-			point.y = parseInt(point.y)
+			point.x = parseInt(point.x) if @horizontal
+			point.y = parseInt(point.y) if @vertical

 		# Update the dragging status
 		if point.x isnt @_layerStartPoint.x or point.y isnt @_layerStartPoint.y
@@ -447,6 +446,8 @@

 	_onSimulationStop: (axis, state) =>

+		return if axis is "x" and @horizontal is false
+		return if axis is "y" and @vertical is false
 		return unless @_simulation

 		# Round the end position to whole pixels


use 192.168.1.7 for app/config.coffee

use 192.168.1.7 for app/config.coffee


Update en.coffee

Update en.coffee

Moved Ben Hoyle to retired team members.

--- app/lib/en.coffee
+++ app/lib/en.coffee
@@ -1162,7 +1162,6 @@
       szalay: '''Alex Szalay - Cosmologist, Johns Hopkins University
One of the creators of the SDSS SkyServer, and currently involved in building the Virtual Observatory. Plays some music in his 'spare' time.'''
       thomas: '''Daniel Thomas - Astronomer, University of Portsmouth
Interested in galaxies of any shape, age, colour and habitat in the universe. When he's not sunbathing, his research focuses on the stars in galaxies and what they tell us about galaxy formation and evolution.'''
       haussler: 'Boris Häußler - Astronomer, University of Oxford/Hertfordshire
An expert on analysing Hubble Space Telescope images and automatic morphology methods.'
-      hoyle: 'Ben Hoyle - Astronomer, University of Portsmouth
Currently developing exciting connections between Google Sky and Galaxy Zoo. Also working on using the lensing of distant quasars to learn about the properties of nearby Galaxy Zoo galaxies.'
       kaviraj: 'Sugata Kaviraj - Astronomer, University of Hertfordshire and University of Oxford
Works on the formation and evolution of galaxies.'
       raddick: 'Jordan Raddick - Science Education and Outreach Coordinator, Johns Hopkins University
A science writer and educator, creator of the science projects on the SDSS SkyServer web site, and an avid science fiction reader and writer. Jordan wishes he could visit galaxies in their natural habitat.'
       wong: 'Ivy Wong - Astronomer, ICRAR/Perth
 A Super Science Fellow in Australia, Ivy studies why galaxies start and stop forming stars, how they grow supermassive black holes in their centre, and why they look like they do today, especially their radio properties.'
@@ -1217,7 +1216,7 @@
       simpson: 'Robert Simpson
Zooniverse Researcher and developer at Oxford University. PI of the Milky Way Project.'
       smith: 'Arfon Smith
As an undergraduate, Arfon studied Chemistry at the University of Sheffield before completing his Ph.D. in Astrochemistry at The University of Nottingham in 2006. He worked as a senior developer at the Wellcome Trust Sanger Institute (Human Genome Project) in Cambridge before joining the Galaxy Zoo team in Oxford. He was responsible for leading the development of a platform for citizen science called Zooniverse, and was Director of Citizen Science at the Adler Planetarium where he led the software and infrastructure development for the Zooniverse.'
       whyte: '''Laura Whyte
Having spent her PhD trying to get computers to classify galaxies, Laura knows only too well the true value of the contribution to science made by the Galaxy Zoo volunteers. Since leaving astronomy she retrained as a maths teacher and picked up a few programming skills. It was with great delight she joined the team at the Zooniverse, developing online education and research tools, allowing her to use bits and pieces of all the skills she's picked up over the last decade.'''
-
+      hoyle: 'Ben Hoyle - Astronomer, Ludwig-Maximilians-Universität München, Physics
While a PhD student in Portsmouth worked on connections between Google Sky and Galaxy Zoo.'

   examine:
     title: 'Examine'


Support for subviews with the same name (array)

--- src/view.coffee
+++ src/view.coffee
@@ -42,9 +42,16 @@
 		@hidden = @el.hasClass('hidden')
 		@enabled = !@el.hasClass('disabled')

+		@initializeViews()
+
+		super
+
+	initializeViews: ->
 		for name, view of @views
-			view.initialize()
-		super
+			if Array.isArray(view)
+				v.initialize() for v in view
+			else
+				view.initialize()

 	_loadTemplate: ->
 		@el = $(LRSView.templates[@template])
@@ -55,17 +62,27 @@
 		#console.log @, viewEls
 		for view in viewEls
 			$view = $(view)
-			if !$view.attr('data-view') then continue
+			continue unless $view.attr('data-view')
 			info = $view.attr('data-view').split(':')
 			$view.removeAttr('data-view')
 			#console.log info[0] || info[1], viewEls, '1'
+
 			if (info.length == 1)
-				@views[info[0]] = new lrs.LRSView($view, null,  @)
-			else
-				@views[info[1]] = new lrs.LRSView.views[info[0]+'View']($view, {subTemplate: info[2]}, @)
-
-			if info[0] == 'view' then @view = @views[info[0]]
-			else if info[1] == 'view' then @view = @views[info[1]]
+				name = info[0]
+				view = new lrs.LRSView($view, null,  @)
+			else
+				name = info[1]
+				view = new lrs.LRSView.views[info[0]+'View']($view, {subTemplate: info[2]}, @)
+
+			if name is 'view'
+				@view = view
+			else
+				if @views[name]
+					unless Array.isArray(@views[name])
+						@views[name] = [@views[name]]
+					@views[name].push(view)
+				else
+					@views[name] = view

 	_createOutlets: ->
 		outletEls = @el.find('[data-outlet]')
@@ -233,7 +250,11 @@
 		@removeClass('disabled') if updateClass
 		if recursive is true
 			for viewName, view of @views
-				view.enable(recursive, updateClass)
+				if Array.isArray(view)
+					v.enable(recursive, updateClass) for v in view
+				else
+					view.enable(recursive, updateClass)
+
 		@

 	disable: (recursive = true, updateClass = true) ->
@@ -241,7 +262,10 @@
 		@addClass('disabled') if updateClass
 		if recursive is true
 			for viewName, view of @views
-				view.disable(recursive, updateClass)
+				if Array.isArray(view)
+					v.disable(recursive, updateClass) for v in view
+				else
+					view.disable(recursive, updateClass)
 		@

 	show: ->
@@ -261,18 +285,49 @@
 class lrs.LRSView.views.LRSListView extends lrs.LRSView

 	initialize: (content = null) ->
-		@permanentContent = @el.children()
-		@permanentViews = _.clone(@views)
-		@permanentViewsArray = (view.el[0] for name, view of @permanentViews)
-		@setContent(content)
+		unless @permanentContent
+			@permanentContent = @el.children()
+			@permanentViews = _.clone(@views)
+			delete @permanentViews.content
+			@permanentViewsArray = (view.el[0] for name, view of @permanentViews)
+
 		super()
+
+		if content and @views.content
+			@setPreloadedContent(content)
+		else
+			@setContent(content)
+
+	setPreloadedContent: (content) ->
+		previousView = null
+		for object, i in content
+			@content.push(object)
+
+			view = null
+			for v in @views.content
+				if v.el.attr('data-id') is object.id
+					view = v
+					break
+
+			if view
+				view.initialize(object)
+			else
+				view = new @options.itemClass(null, null, @).initialize(object)
+				if previousView
+					view.insertAfter(previousView.el)
+					@views.content.splice(i, 0, view)
+				else
+					view.appendTo(@el)
+
+
+			#@views[_.uniqueId('lvi_')] = view

 	setContent: (content) ->
 		return if not _.isNull(content) and not _.isArray(content)
 		return if content is null and @content = []

-		@views = _.clone(@permanentViews)
-		@listViews = []
+		@views[name] = view for name, view of @permanentViews
+		@views.content = []
 		@el.children().not(@permanentViewsArray).remove()

 		if content is null
@@ -309,12 +364,11 @@
 		else
 			view = object

-		if i is @listViews.length
+		if i is @views.content.length
 			view.appendTo(@el)
 		else
 			view.el.insertBefore(@listViews[i].el)
-		@listViews.splice(i, 0, view)
-		@views[_.uniqueId('lvi_')] = view
+		@views.content.splice(i, 0, view)

 class lrs.LRSView.views.LRSListItemView extends lrs.LRSView


fix old method name

fix old method name

--- src/document.coffee
+++ src/document.coffee
@@ -68,7 +68,7 @@
   setHTML: (html) ->
     @lines = new LinkedList()
     @lineMap = {}
-    @root.innerHTML = Normalizer.normalizeHTML(html)
+    @root.innerHTML = Normalizer.stripWhitespace(html)
     lineNode = @root.firstChild
     while lineNode?
       this.appendLine(Normalizer.normalizeLine(lineNode))


joined build variable replacements

joined build variable replacements

--- src/mist-ninja-builder.coffee
+++ src/mist-ninja-builder.coffee
@@ -135,6 +135,8 @@
           when 'o' then target.build_vars[d] = target.main_outputs
           else target.build_vars[d] = build_vars[k]

+        target.build_vars[d] = target.build_vars[d].join ' '
+
       console.log target

   render: ->


updated binary-search.coffee

updated binary-search.coffee

--- algo/binary-search.coffee
+++ algo/binary-search.coffee
@@ -1,12 +1,23 @@
+
+{ isListAscending } = require './is-list-ascending.coffee'

 _binarySearch = (sortedList, needle, start, end)->
-  return -1 if end <= 0
-  mid = start + ((end - 1) / 2)
+
+  return -1 if end < start
+
+  mid = Math.floor (start + ((end - start) / 2))
+
   return mid if sortedList[mid] is needle
+
   if sortedList[mid] > needle
-    _binarySearch sortedList, needle, start, mid-1
+
+    return _binarySearch sortedList, needle, start, mid - 1
+
   else
-    _binarySearch sortedList, needle, mid+1, end
+
+    return _binarySearch sortedList, needle, mid + 1, end

 @binarySearch = (sortedList, needle)->
-  return _binarySearch sortedList, needle, 0, sortedList.length-1+  throw new Error 'Unsorted List' unless isListAscending sortedList
+  return _binarySearch sortedList, needle, 0, sortedList.length-1
+


BUG-FIX clearing element, unused throw statement

--- src/modules/controller.coffee
+++ src/modules/controller.coffee
@@ -30,8 +30,8 @@
       when undefined
         @template = opts.template if opts.template
       when true
-        @template = @$el.html()
-        @$el.innerHTML = ''
+        @template = @el.innerHTML
+        @el.innerHTML = ''

     @params = opts.params or {}

@@ -153,7 +153,6 @@
         if e instanceof TypeError
           undefined
         else
-          throw "#{e} in running accessor '#{original or evaluate}'"
           throw e

   _setter: (scope, evaluate, original)->
@@ -166,7 +165,6 @@
       try
         setter(scope, newValue)
       catch e
-        throw "#{e} in running accessor '#{original or evaluate}'"
         throw e

   watch: (keys, callback, initCall = true)->


sortable headers

sortable headers

--- source/javascripts/all.coffee
+++ source/javascripts/all.coffee
@@ -86,6 +86,33 @@
     filters = getFilterParams()
     highlight filters.subject, filters.type

+  d3.selectAll('.js-sort').on 'click', ->
+    event = d3.event
+    event.preventDefault()
+    params = d3.select(this).attr('href')
+      .substr(1)
+      .split('&')
+      .map (val) -> val.split '='
+
+    filters = getFilterParams()
+    sort = params.filter((p) -> p[0] == 'dir')[0]
+    type = params.filter((p) -> p[0] == 'prop')[0]
+
+    type = if type[1] is 'change'
+      3
+    else
+      if filters.type is '2010' then 1 else 0
+
+    query = []
+    for param in params
+      [key, val] = param
+      val = (if val is 'asc' then 'desc' else 'asc') if key == 'dir'
+      query.push "#{key}=#{val}"
+
+    d3.select(this).attr 'href', "?#{query.join('&')}"
+
+    updateInfo filters.subject, type, __mappings[filters.subject], sort[1]
+
   d3.select('.js-menu-type').on 'change', (d) ->
     filters = getFilterParams()
     highlight filters.subject, filters.type
@@ -166,8 +193,9 @@
       index += 1

 updateInfo = (subject, type, data, sort = 'desc') ->
+  sortFunc = if sort == 'desc' then d3.descending else d3.ascending
   nhoods = places.map((place) -> name: place.key, value: place.value[subject])
-    .sort((a, b) -> d3.descending a.value[type], b.value[type])[0..19]
+    .sort((a, b) -> sortFunc a.value[type], b.value[type])[0..19]

   [min, max] = d3.extent nhoods, (d) -> d.value[type]


mise en place du debug pour deadlocked positions

--- app/assets/javascripts/game/controllers/level_controller.js.coffee
+++ app/assets/javascripts/game/controllers/level_controller.js.coffee
@@ -98,6 +98,8 @@

       # "there is a deadlock" div
       if deadlocked_positions.length != 0
+        console.log(@level)
+        console.log(deadlocked_positions)
         $('#deadlock').fadeIn(1000)
       else
         $('#deadlock').fadeOut(500)


clean up commented out code I found during code review

--- app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
+++ app/assets/javascripts/hanuman/hanuman-conditional-logic.js.coffee
@@ -166,9 +166,7 @@
       selected_array = $(named_string).map(->
         $(this).attr('data-label-value')
       ).get()
-      # # force is equal to operator to contains since multiple checkboxes with multiple rules associated with them needs to check for contains
-      # if operator == "is equal to"
-      #   operator = "contains"
+      # force is equal to operator to contains since multiple checkboxes with multiple rules associated with them needs to check for contains
       hideQuestions = self.evaluateCheckboxConditions(operator, answer, selected_array)
     else if element_type == 'multiselect'
       selected_values = self.getValue($triggerElement)


Entrance

Entrance

--- src/entrance.coffee
+++ src/entrance.coffee
@@ -11,16 +11,16 @@
 #   logikz

 welcomeMessage = '''
-Welcome to Slack! :allthethings:
-Please enjoy the use of our ingress bot - Jarvis.  You can type ```jarvis help``` for command usage.
-Please only use jarvis in general or in private message directly to Jarvis.  He can be quite the talker :).
+Welcome to Slack! :allthethings:
+Please enjoy the use of our ingress bot - Tardis.  You can type ```tardis help``` for command usage.
+Please only use tardis in random or in private message directly to Tardis.  He can be quite the talker :).
 Feel free to make public and private chat rooms to discuss various topics.

-If you need any help, feel free to message Logikz
+If you need any help, feel free to message The82ndDoctor
 '''

 module.exports = (robot) ->
 	robot.enter (msg)->
 		room = msg.message.room
-		if(room == "general" or room == "tests")
+		if(room == "general" or room == "random")
 			msg.reply(welcomeMessage)


Remove FPS counter

Remove FPS counter

--- app/frontend/javascripts/views/visualization-canvas-base.js.coffee
+++ app/frontend/javascripts/views/visualization-canvas-base.js.coffee
@@ -84,7 +84,8 @@
     # Remove loading class
     @$el.removeClass 'loading'

-    # a FPS meter
+    ###
+    # FPS meter
     fpsMeter = d3.select(@el).append('div')
       .style 'font-family', 'monospace'
       .style 'position', 'absolute'
@@ -99,6 +100,7 @@
         ticks.shift()
       avgFrameLength = (ticks[ticks.length-1] - ticks[0])/ticks.length
       fpsMeter.html Math.round(1/avgFrameLength*1000) + ' fps'
+    ###

   setupData: (data) ->


:art: Use for loop instead of foreach in editor-linter::validateResults

--- lib/editor-linter.coffee
+++ lib/editor-linter.coffee
@@ -73,8 +73,8 @@
   @validateResults: (results) ->
     if (not results) or results.constructor.name isnt 'Array'
       throw new Error "Got invalid response from Linter, Type: #{typeof results}"
-    results.forEach (Result) ->
-      unless Result.type
-        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(results)}"
+    for result in results
+      unless result.type
+        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
     results
 module.exports = EditorLinter


Prevent document scrolling on mobile

Prevent document scrolling on mobile

--- framer/LayerDraggable.coffee
+++ framer/LayerDraggable.coffee
@@ -100,7 +100,7 @@
 		@_stopSimulation()
 		@_resetLockDirection()

-		# event.preventDefault()
+		event.preventDefault()
 		# event.stopPropagation() if ! @_propagateEvents

 		# Extract the event (mobile may have multiple)
@@ -144,7 +144,7 @@

 		return unless @enabled

-		# event.preventDefault()
+		event.preventDefault()
 		# event.stopPropagation() unless @_propagateEvents

 		@emit(Events.DragWillMove, event)


minor form for

minor form for

--- src/form_for.coffee
+++ src/form_for.coffee
@@ -1,15 +1,16 @@
 angular
 .module('angular-w').directive 'wFormFor', ['$window', ($window)->
   restrict: 'A'
+  require: '?form'
   scope:
     object: '=wFormFor'
   compile: (tElement, tAttrs) ->
     tElement.attr('class', 'form-horizontal')
     tElement.attr('role', 'form')

-    (scope, element, attrs) ->
+    (scope, element, attrs, formController) ->
       $window.addEventListener 'beforeunload', ->
-        if element.controller('form').$dirty
+        if formController.$dirty
           'You will lose unsaved changes unless you stay on this page'

@@ -20,6 +21,6 @@
     @getObjectName = ->
       $attrs.wFormFor

-    return null
+    return
 ]


Improved file browser

Improved file browser

--- SingularityUI/app/views/fileBrowserSubview.coffee
+++ SingularityUI/app/views/fileBrowserSubview.coffee
@@ -29,8 +29,7 @@
             breadcrumbs: breadcrumbs

         if @scrollWhenReady
-            offset = @$el.offset().top
-            $(window).scrollTop offset
+            @scrollToTop()

             # Only do it once
             @scrollWhenReady = false
@@ -38,9 +37,12 @@
     catchAjaxError: ->
         @$('.span-12').html '
Could not get files :(
'

+    scrollToTop: ->
+        offset = @$el.offset().top
+        $(window).scrollTop offset
+
     navigate: (event) ->
         event.preventDefault()
-

         $table = @$ 'table'
         # Get table height for later
@@ -55,11 +57,11 @@

         app.router.navigate "#task/#{ @collection.taskId }/files/#{ @collection.path }"

-        @collection.reset()
-        @collection.fetch()
+        @collection.fetch reset: true

         @render()

+        @scrollWhenReady = true
         $loaderContainer = @$ '.page-loader-container'
         if tableHeight?
             $loaderContainer.css 'height', "#{ tableHeight }px"


Everything is an array

Everything is an array

--- src/cheerio.coffee
+++ src/cheerio.coffee
@@ -23,9 +23,6 @@
     # we want to wrap it.
     else
       @context = select
-
-    if @context.length is 1
-      @context = @context[0]

     return this

@@ -40,42 +37,48 @@
     return renderer.render @context

   selector::text = (text, value) ->
-    exists = (@context.children.length is 1 and @context.children[0].type is "text")
+    if @context.length is 1
+      elem = @context[0]
+    else
+      return ""
+
+    exists = (elem.children.length is 1 and elem.children[0].type is "text")
     if not value
       if exists
-        return @context.children[0].data
+        return elem.children[0].data
       else
         return ""
     else
       if exists
-        @context.children[0].data = value
+        elem.children[0].data = value
       else
         return this

   selector::attr = (attribute, value) ->
-    if not value
-      if @context.attribs and @context.attribs[attribute]
-        return @context.attribs[attribute]
+    this.each (elem) ->
+      if not value
+        if elem.attribs and elem.attribs[attribute]
+          return elem.attribs[attribute]
+        else
+          return ""
+
       else
-        return ""
-
-    else
-      if @context.attribs
-        @context.attribs[attribute] = value
+        if elem.attribs
+          elem.attribs[attribute] = value

       return this

   selector::remove = () ->
-    # Kind of a hacky way to remove an element
-    @context.raw = null
+    this.each (elem) ->
+      elem.raw = null
+
     return this

   selector::each = (fn) ->
-    context = if !_.isArray @context then [@context] else @context
-    for elem, i in context
+    for elem, i in @context
       selected = selector elem
-      fn.call selected, selected, i
+      fn.call selected, elem, i

     return this

@@ -98,8 +101,6 @@
   if not callback and _.isFunction options
     callback = options

-
-
   done = (err, dom) ->
     throw err if err
     selector = cheerio dom


Show the unit berry while showing district geometry

--- src/control.coffee
+++ src/control.coffee
@@ -132,9 +132,12 @@
                 @selectedDivision.clear()
             else
                 @selectedDivision.wrap division
+                id = @selectedDivision.attributes.value.attributes.unit?.id or null
+                # clear @units so the previous one doesn't persist if there is no new unit to draw
+                if id? then @renderUnitById(id, false) else @units.set []
                 division.set 'selected', true

-        renderUnitById: (id) ->
+        renderUnitById: (id, unitSelect=true) ->
             deferred = $.Deferred()
             unit = new Models.Unit id: id
             unit.fetch
@@ -142,7 +145,7 @@
                     include: 'department,municipality,services'
                 success: =>
                     @setUnit unit
-                    @selectUnit unit
+                    if unitSelect then @selectUnit unit
                     deferred.resolve unit
             deferred.promise()


simplified graph intersection using helper class

--- src/digraph.coffee
+++ src/digraph.coffee
@@ -94,48 +94,60 @@
     vertex_list.push(last_vertex)
     vertex_list

+  class IntersectionHelper
+    constructor: (graph1, graph2, intersection) ->
+      @unvisited = {}
+      @product_vertexes = {}
+      root_key = @vkey(graph1.source, graph2.source)
+      @unvisited[root_key] = [graph1.source, graph2.source]
+      @product_vertexes[root_key] = intersection.source
+
+    vkey: (v1, v2) ->
+      "#{v1.id},#{v2.id}"
+
+    product_vertex: (v1, v2) ->
+      key = @vkey(v1, v2)
+      @product_vertexes[key]
+
+    add_unvisited: (v1, v2, v3) ->
+      key = @vkey(v1, v2)
+      @unvisited[key] = [v1, v2]
+      @product_vertexes[key] = v3
+
+    get_next_vertices: ->
+      keys = Object.keys(@unvisited)
+      if keys.length == 0
+        null
+      else
+        key = keys[0]
+        [v1, v2] = @unvisited[key]
+        delete @unvisited[key]
+        [v1, v2, @product_vertexes[key]]
+
+
   # return a new digraph that is the intersection of the two graphs.
   intersect: (other) ->
     digraph = @
-    intersection = new @constructor()
-
-    vkey = (v1, v2) ->
-      "#{v1.id},#{v2.id}"
-    object_pop = (obj) ->
-      key = Object.keys(obj)[0]
-      val = obj[key]
-      delete obj[key]
-      [key, val]
-
-    unvisited_vertices = {}
-    root_key = vkey(@source, other.source)
-    unvisited_vertices[root_key] = [@source, other.source]
-    vertex_mapper = {}
-    vertex_mapper[root_key] = intersection.source
-
-    while Object.keys(unvisited_vertices).length >0
-      # "pop" a state from unvisited_vertices
-      [key, [this_vertex, other_vertex]] = object_pop(unvisited_vertices)
-      out_current = vertex_mapper[key]
-
-      # for the two current states, iterate over the
+    product = new @constructor()
+    helper = new IntersectionHelper(digraph, other, product)
+
+    while (next_vertices = helper.get_next_vertices())
+      [this_vertex, other_vertex, product_vertex] = next_vertices
+
+      # for the two current vertices, iterate over the
       # transitions with same values
       for this_arc in this_vertex.arcs()
         for other_arc in other_vertex.arcs() when this_arc.test(other_arc.value)
           this_next = this_arc.next_vertex
           other_next = other_arc.next_vertex

-          next_key = vkey(this_next, other_next)
-          # if the new graph does not already have the intersection state...
-          out_next = vertex_mapper[next_key]
-          if !out_next
-            # create an intersection state and index it
-            out_next = intersection.create_vertex()
-            vertex_mapper[next_key] = out_next
-            unvisited_vertices[next_key] = [this_next, other_next]
-          # add the transition
-          intersection.add_arc(out_current, out_next, this_arc.value)
-    intersection
+          # if the new graph does not already have the intersection state,
+          # create an intersection state and index it
+          unless (product_next = helper.product_vertex(this_next, other_next))
+            product_next = product.create_vertex()
+            helper.add_unvisited(this_next, other_next, product_next)
+          product.add_arc(product_vertex, product_next, this_arc.value)
+    product


fixed the maximum photo size logic

fixed the maximum photo size logic

--- src/embedly.coffee
+++ src/embedly.coffee
@@ -98,7 +98,7 @@

     toPhoto: (json) ->
         ratio = json.height / json.width
-        width = Math.max @options.max_photo_width, json.width
+        width = Math.min @options.max_photo_width, json.width
         height = parseInt ratio * width, 10

         """<img class="embed"


Added Case methods as well as a helper method that handles options for all cases.

--- src/fogbugz.coffee
+++ src/fogbugz.coffee
@@ -15,7 +15,7 @@
 		if err then callback(new Error('Error logging off: ' + err.message)) else callback(null)
 	)

-#Projects
+#--------------- Projects ---------------------
 ListProjects = (options, token, callback) ->
  	command = 'cmd=listProjects'
  	if(options['fWrite']) then command += "&fWrite=1"
@@ -26,6 +26,7 @@
  		if err then callback(new Error('Error listing projects: ' + err.message)) else callback(null, result['projects'][0]['project'])
  	)

+#-------------- End of Projects ---------------

 # ---------------- Cases -----------------------


Add ".rar" to list of compressed file extensions

--- src/fs-plus.coffee
+++ src/fs-plus.coffee
@@ -532,6 +532,7 @@
   '.lz':   true
   '.lzma': true
   '.lzo':  true
+  '.rar':  true
   '.tar':  true
   '.tgz':  true
   '.war':  true


Fix constructor for CatchAllMessages

Fix constructor for CatchAllMessages

--- src/message.coffee
+++ src/message.coffee
@@ -54,6 +54,7 @@
   #
   # message - The original message.
   constructor: (@message) ->
+    super @message.user

 module.exports = {
   Message


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -163,6 +163,7 @@
         I["max#{name.capitalize()}"] = 100

     I.meters[name] = options
+
   ###*
   Configures a meter to be drawn each overlay event.


vtrace also shows the name of the current thread, if possible

--- src/methods.coffee
+++ src/methods.coffee
@@ -12,7 +12,7 @@
 {vtrace,trace,debug_vars} = logging
 {ReturnException} = require './exceptions'
 {native_methods,trapped_methods} = natives
-{JavaArray,JavaObject} = require './java_object'
+{JavaArray,JavaObject,thread_name} = require './java_object'

 # things assigned to root will be available outside this module
 root = exports ? this.methods = {}
@@ -233,7 +233,7 @@
       unless RELEASE? or logging.log_level < logging.STRACE
         vtrace "#{@cls.get_type()}::#{@name}:#{pc} => #{op.name}" + annotation
         depth = rs.meta_stack().length()
-        vtrace "D: #{depth}, S: [#{debug_vars cf.stack}], L: [#{debug_vars cf.locals}]"
+        vtrace "D: #{depth}, S: [#{debug_vars cf.stack}], L: [#{debug_vars cf.locals}], T: #{thread_name(rs, rs.curr_thread) if rs.curr_thread.get_field? }"

       cf.pc += 1 + op.byte_count
       op = code[cf.pc]


Oops, broke iinc.

Oops, broke iinc.

--- src/opcodes.coffee
+++ src/opcodes.coffee
@@ -100,7 +100,8 @@
       arg_size = 2
       @byte_count = 5
     else
-      @byte_count = arg_size = 1
+      arg_size = 1
+      @byte_count = 2
     @index = code_array.get_uint arg_size
     @const = code_array.get_int arg_size


fix scope error in pdf viewer

fix scope error in pdf viewer

--- services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfViewer.coffee
@@ -25,7 +25,7 @@
 			# $scope.pages = []

 			$scope.document.destroy() if $scope.document?
-			scope.loadCount = scope.loadCount? ? scope.loadCount + 1 : 1
+			$scope.loadCount = $scope.loadCount? ? $scope.loadCount + 1 : 1
 			# TODO need a proper url manipulation library to add to query string
 			$scope.document = new PDFRenderer($scope.pdfSrc + '&pdfng=true' , {
 				scale: 1,


external link window.open hint

external link window.open hint

--- coffee/views/application_view.coffee
+++ coffee/views/application_view.coffee
@@ -238,13 +238,15 @@
       #console.debug '\thref »' + href + '«'
       #console.debug '\thostname »' + hostname + '«'
       return unless href and hostname
-
       currentHostname = location.hostname.replace('.', '\\.')
       hostnameRegExp = new RegExp("#{currentHostname}$", 'i')
       external = not hostnameRegExp.test(hostname)
       #console.debug '\texternal?', external
       if external
         # Open external links normally
+        # You might want to enforce opening in a new tab here:
+        #e.preventDefault()
+        #window.open href
         return

       @openInternalLink e


Enable linter on Python Django syntax

Enable linter on Python Django syntax

If atom-language-django is installed then python source files in a Django project use "Python Django" syntax. On that syntax the linter don't want to run. I need to switch to default Python syntax in order to have linter working.

--- lib/linter-flake8.coffee
+++ lib/linter-flake8.coffee
@@ -4,7 +4,7 @@
 {findFile} = require "#{linterPath}/lib/utils"

 class LinterFlake8 extends Linter
-  @syntax: ['source.python']
+  @syntax: ['source.python', 'source.python.django']

   executablePath: null


Added json to supported source languages

--- lib/linter-jshint.coffee
+++ lib/linter-jshint.coffee
@@ -5,7 +5,7 @@
 class LinterJshint extends Linter
   # The syntax that the linter handles. May be a string or
   # list/tuple of strings. Names should be all lowercase.
-  @syntax: ['source.js', 'source.js.jquery', 'text.html.basic'] # , 'text.html.twig', 'text.html.erb', 'text.html.ruby']
+  @syntax: ['source.js', 'source.js.jquery', 'text.html.basic', 'source.json'] # , 'text.html.twig', 'text.html.erb', 'text.html.ruby']

   # A string, list, tuple or callable that returns a string, list or tuple,
   # containing the command line (with arguments) used to lint.


Fixed bug in which getCmd was being called but no longer existed in parent class.

--- lib/linter-scalac.coffee
+++ lib/linter-scalac.coffee
@@ -5,11 +5,13 @@
 class LinterScalac extends Linter
 	@syntax: ['source.scala']

-	classpath: null
-
 	cmd: 'scalac'

+	executablePath: ''
+
 	linterName: 'scalac'
+
+	options: ''

 	regex: 'scala:(?<line>\\d+): ((?<error>error)|(?<warning>warning)): (?<message>(.+)\\n(.+))\\n'

@@ -17,23 +19,28 @@
 		super(editor)

 		atom.config.observe('linter-scalac.scalacExecutablePath', =>
+			@executablePath = atom.config.get('linter-scalac.scalacExecutablePath')
+		)
+
+		atom.config.observe('linter-scalac.scalacOptions', =>
 			dotClasspath = atom.project.path + '/.classpath'

 			if atom.config.get('linter-scalac.scalacOptions')?
-				@cmd = @cmd + ' ' + atom.config.get('linter-scalac.scalacOptions')
+				@options = atom.config.get('linter-scalac.scalacOptions')

 			if fs.existsSync(dotClasspath)
-				@classpath = fs.readFileSync(dotClasspath).toString().trim()
-				@cwd = @classpath.split(':')[0]
-				@cmd = @cmd + ' -classpath "' + @classpath + '"'
-
-			@executablePath = atom.config.get('linter-scalac.scalacExecutablePath')
+				classpath = fs.readFileSync(dotClasspath).toString().trim()
+				@cwd = classpath.split(':')[0]
+				@options = @options + ' -classpath "' + classpath + '"'
 		)

-	destroy: -> atom.config.unobserve('linter-scalac.scalacExecutablePath')
+	destroy: ->
+		atom.config.unobserve('linter-scalac.scalacExecutablePath')
+		atom.config.unobserve('linter-scalac.scalacOptions')

-	lintFile: (filePath, callback) -> exec(@getCmd(filePath), cwd: @cwd, (error, stdout, stderr) =>
-		if stderr then @processMessage(stderr, callback)
-	)
+	lintFile: (filePath, callback) ->
+		command = @executablePath + '/' + @cmd + ' ' + filePath + ' ' + @options
+
+		exec(command, cwd: @cwd, (error, stdout, stderr) => if stderr then @processMessage(stderr, callback))

 module.exports = LinterScalac


Reduced time to reload ticket.

Reduced time to reload ticket.

--- app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom.js.coffee
@@ -54,7 +54,7 @@
           update = =>
             @fetch( @ticket_id, false )
           if !@ticketUpdatedAtLastCall || ( new Date(data.updated_at).toString() isnt new Date(@ticketUpdatedAtLastCall).toString() )
-            @delay( update, 1800, 'ticket-zoom-' + @ticket_id )
+            @delay( update, 1200, 'ticket-zoom-' + @ticket_id )
     )

     # rerender view, e. g. on langauge change


Fixed sortBy().

Fixed sortBy().

--- app/assets/javascripts/app/models/_application_model.js.coffee
+++ app/assets/javascripts/app/models/_application_model.js.coffee
@@ -258,10 +258,11 @@
         attribute = 'displayName'

       # check if displayName exists
-      if attribute is 'displayName' && item.displayName
-        return item.displayName().toLowerCase()
-      else
-        attribute = 'name'
+      if attribute is 'displayName'
+        if item.displayName
+          return item.displayName().toLowerCase()
+        else
+          attribute = 'name'

       return '' if item[ attribute ] is undefined
       return '' if item[ attribute ] is null


Update query after adding or removing a term

--- app/assets/javascripts/rglossa/components/cwb-multiword.coffee
+++ app/assets/javascripts/rglossa/components/cwb-multiword.coffee
@@ -147,6 +147,7 @@
     dq = @get('displayedQuery')
     dq[dq.length-1].set('isLast', false)
     dq.pushObject(newTerm)
+    @updateQuery()

   removeTerm: (term) ->
@@ -159,6 +160,7 @@
     # first.
     dq.objectAt(0).set('isFirst', true) if term.isFirst
     dq.get('lastObject').set('isLast', true) if term.isLast
+    @updateQuery()

   action: 'search'


OX-59 increase click threshold

OX-59 increase click threshold

--- app/assets/javascripts/oxalis/view/abstractTreeViewer.coffee
+++ app/assets/javascripts/oxalis/view/abstractTreeViewer.coffee
@@ -4,7 +4,7 @@

 NODE_RADIUS = 2
 MAX_NODE_DISTANCE = 100
-CLICK_TRESHOLD = 4
+CLICK_TRESHOLD = 6

 MODE_NORMAL = 0     # draw every node and the complete tree
 MODE_NOCHAIN = 1    # draw only decision points


Also keep active class on input container

--- app/assets/javascripts/record_collection/optionals.js.coffee
+++ app/assets/javascripts/record_collection/optionals.js.coffee
@@ -62,6 +62,7 @@
       value_field = container.find('select,input')
       # INITIAL STATE IS DISABLED, Activation by triggering click if needed
       value_field.attr 'name', "disabled_#{value_field.attr('name')}"
+      container.addClass('inactive')

       label_text = container.find('label').text()

@@ -75,11 +76,11 @@
         if activator_container.hasClass('active')
           value_field.attr 'name', value_field.attr('name').replace(/^disabled_/, '')
           #value_toggle.show()
-          container.removeClass('inactive')
+          container.addClass('active').removeClass('inactive')
         else
           value_field.attr 'name', "disabled_#{value_field.attr('name')}"
           #value_toggle.hide()
-          container.addClass('inactive')
+          container.removeClass('active').addClass('inactive')
       activator_label = $('').addClass('optional-input-activator-label').text label_text
       activator_container.append activator_toggle
       activator_container.append activator_label


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/number_extensions.coffee
+++ test/number_extensions.coffee
@@ -18,8 +18,8 @@

 test "#clamp", ->
   equals 5.clamp(0, 3), 3
-  equals 5.clamp(0, 0), 0
-  equals
+  equals 5.clamp(-1, 0), 0
+  equals -5.clamp(0)

 test "#floor", ->
   equals 4.9.floor(), 4, "(4.9).floor() equals 4"