Add delete buttons to squad load modal.

Add delete buttons to squad load modal.

fixes #129

--- coffeescripts/backend.coffee
+++ coffeescripts/backend.coffee
@@ -150,26 +150,75 @@

#{squad.additional_data.points} Points

-

-

+

+

                                 #{squad.additional_data.description}

-

+

                                 <button class="btn load-squad">Load</button>
+
+                                <button class="btn btn-danger delete-squad">Delete</button>
+

+

+

+

+                                Really delete #{squad.name}?
+

+

+                                <button class="btn btn-danger confirm-delete-squad">Delete</button>
+
+                                <button class="btn cancel-delete-squad">Cancel</button>

                     """
-                    li.click (e) =>
+                    li.find('.squad-delete-confirm').hide()
+
+                    li.find('button.load-squad').click (e) =>
                         e.preventDefault()
                         button = $ e.target
                         li = button.closest 'li'
                         builder = li.data('builder')
                         @squad_list_modal.modal 'hide'
                         if builder.current_squad.dirty
-                            @warnUnsaved builder, () =>
+                            @warnUnsaved builder, () ->
                                 builder.container.trigger 'xwing-backend:squadLoadRequested', li.data('squad')
                         else
                             builder.container.trigger 'xwing-backend:squadLoadRequested', li.data('squad')
+
+                    li.find('button.delete-squad').click (e) ->
+                        e.preventDefault()
+                        button = $ e.target
+                        li = button.closest 'li'
+                        builder = li.data('builder')
+                        do (li) ->
+                            li.find('.squad-description').fadeOut 'fast', ->
+                                li.find('.squad-delete-confirm').fadeIn 'fast'
+
+                    li.find('button.cancel-delete-squad').click (e) ->
+                        e.preventDefault()
+                        button = $ e.target
+                        li = button.closest 'li'
+                        builder = li.data('builder')
+                        do (li) ->
+                            li.find('.squad-delete-confirm').fadeOut 'fast', ->
+                                li.find('.squad-description').fadeIn 'fast'
+
+                    li.find('button.confirm-delete-squad').click (e) =>
+                        e.preventDefault()
+                        button = $ e.target
+                        li = button.closest 'li'
+                        builder = li.data('builder')
+                        li.find('.cancel-delete-squad').fadeOut 'fast'
+                        li.find('.confirm-delete-squad').addClass 'disabled'
+                        li.find('.confirm-delete-squad').text 'Deleting...'
+                        @delete li.data('squad').id, (results) ->
+                            if results.success
+                                li.slideUp 'fast', ->
+                                    $(li).remove()
+                            else
+                                li.html $.trim """
+                                    Error deleting #{li.data('squad').name}: #{results.error}
+                                """

             loading_pane.fadeOut 'fast'
             list_ul.fadeIn 'fast'


Biiiiig changes

Biiiiig changes

--- sew.coffee
+++ sew.coffee
@@ -5,67 +5,90 @@
 cs = require 'coffee-script'
 less = require 'less'
 stitch = require 'stitch'
+strata = require 'strata'
+argv = require("optimist").argv

 class Worker
+
+  configFile: './config.json'
+  options:
+    publicDir: './testfiles'
+    jsPath: './js/mylibs'
+    cssPath: './css/style.less'
+    outputJs: './js/script.js'
+    outputCss: './css/style.css'

-  options:
-    config: './config.json'
-    js_path: './js'
-    css_path: './css'
-    main_js: './js/script.coffee'
-    main_css: './css/style.less'
+  constructor: ->
+    @readConfig()
+    @package = stitch.createPackage { paths: [@options.jsPath] }

-  constructor: (path) ->
-    util.log __dirname
-    @path = path;
-    @package = stitch.createPackage({
-      paths: [ '.testfiles/js']
-    })
+    switch argv._[0]
+      when 'new' then @new()
+      when 'build' then @compile()
+      when 'watch' then @watch()
+      when 'serve' then @serve()
+
+  # Actions
+  new: ->
+    console.log (!fpath.existsSync @configFile), argv.force
+      util.log 'Creating config file'
+      fs.writeFileSync @configFile, JSON.stringify(@options, null, 2)
+    else
+      util.log 'Config file already exists use --force to override'
+
+  compile: ->
+    @compileScriptsAndTemplates()
+    @compileStyles()
+
+  watch: ->
     @compile()
-    @walk(@path)
+    @walk @options.publicDir, (file) =>
+      fs.watchFile file, (curr, prev) =>
+        if curr and (curr.nlink is 0 or +curr.mtime isnt +prev.mtime)
+          switch fpath.extname file
+            when '.coffee' then @compileScriptsAndTemplates()
+            when '.less' then @compileStyles()
+
+  serve: ->
+    @watch()
+    @app.use strata.commonLogger
+    @app.use strata.static, @options.publicDir, ['index.html', 'index.htm']
+    strata.run(@app)

+  # Compilers
+  compileScriptsAndTemplates: ->
+    util.log 'Building scripts...'
+    @package.compile (err, source) =>
+      fs.writeFile @options.outputJs, source, (err) ->
+        util.log err.message if err
+
+  compileStyles: ->
+    util.log 'Building styles...'
+    less.render fs.readFileSync(@options.cssPath, 'utf8'), (e, css) =>
+      util.log "LESS - #{e.name} | #{e.message} | #{e.extract}" if e
+      fs.writeFile @options.outputCss, css, (err) ->
+        util.log err.message if err
+
+  # Utiliity
   readConfig: ->
-    config = fs.readFileSync @options.config if fpath.extstsSync(@options.config)
-    @options[key] = config[key] for key, value in @options
-
+    if fpath.existsSync @configFile
+      config = fs.readFileSync @configFile
+      config = JSON.parse config
+      @options[key] = value for key, value of config
+      @app = new strata.Builder

-  walk: (path) ->
+  walk: (path, callback) ->
     for f in fs.readdirSync(path)
       f = fpath.join(path, f)
       stats = fs.statSync(f)
       if stats.isDirectory()
-        @walk f
+        @walk f, callback
       else
-        @watch(f) if @isWatchable(f)
+        callback.call(@, f) if @isWatchable(f)

   isWatchable: (file) ->
-    ext = fpath.extname file
-    switch ext
-      when '.coffee', '.less' then return true
-    return false
+    switch fpath.extname file
+      when '.js', '.coffee', '.less', '.eco' then return true
+    false

-  watch: (f) ->
-    fs.watchFile f, (curr, prev) =>
-      if curr and (curr.nlink is 0 or +curr.mtime isnt +prev.mtime)
-        @compile()
-
-  compile: ->
-    @compileScripts()
-    @compileStyles()
-
-  compileScripts: ->
-    util.log 'Building scripts...'
-    @package.compile (err, source) ->
-      fs.writeFile 'application.js', source, (err) ->
-        util.log err if err;
-        util.log 'Compiled js!!!'
-
-  compileStyles: ->
-    util.log 'Building styles...'
-    less.render fs.readFileSync('./testfiles/css/style.less', 'utf8'), (e, css) ->
-      util.log e if e
-      fs.writeFile 'application.css', css, (err) ->
-        util.log err if err
-
-wkr = new Worker('./testfiles')
-
+new Worker()


Remove useless `console.log` added by 19df886f82, refs #333

--- app/assets/javascripts/app/spaces/index.js.coffee
+++ app/assets/javascripts/app/spaces/index.js.coffee
@@ -36,7 +36,6 @@
       # remove all 'selected' classes and adds only to the selected div
       $("div.space-item.selected").removeClass("selected")
       $(this).addClass("selected")
-      console.log $(this).html()

       # updates the position of the description div
       $("#space-description-wrapper").sticky("update")


Fix active submit button in node popup.

Fix active submit button in node popup.

--- app/assets/javascripts/controllers/node.js.coffee
+++ app/assets/javascripts/controllers/node.js.coffee
@@ -8,7 +8,7 @@

   isUnknown: (()->
     @get('oldWheelchair') == 'unknown' or @get('wheelchair') == 'unknown'
-  ).property('wheelchair')
+  ).property('oldWheelchair', 'wheelchair')

   wheelchairSubmit: (()->
     @get('oldWheelchair')? and @get('wheelchair') != @get('oldWheelchair') and !@get('isPosting')
@@ -27,7 +27,8 @@

   actions:
     setWheelchair: (wheelchair)->
-      @set('wheelchair', wheelchair)
+      unless @get('isPosting')
+        @set('wheelchair', wheelchair)

     saveWheelchair: (wheelchair)->
       self = @


Select first tab if tab_id do not exist

Select first tab if tab_id do not exist

--- app/assets/javascripts/notebook/magic/tabs.coffee
+++ app/assets/javascripts/notebook/magic/tabs.coffee
@@ -37,7 +37,7 @@
     )

     saved_tab_id = get_saved_state().tab_id
-    if saved_tab_id
+    if saved_tab_id && $('a[href=' + saved_tab_id + ']').length > 0
       # console.log("selecting a saved tab", saved_tab_id)
       $('a[href=' + saved_tab_id + ']').click()
     else


fixed uploading image for ie10

fixed uploading image for ie10

--- app/coffeescripts/bundles/account_homepage.coffee
+++ app/coffeescripts/bundles/account_homepage.coffee
@@ -210,9 +210,7 @@
         beforeSubmit: ->
           return validateUploadedImage $("#widget_image").val()
         success: (data)->
-          img = """
-
-                """
+          img = ""
           $("#widget_body").editorBox "insert_code", img
           $("#widget_image").val ""
           afterUploadedImageSuccess()


bugfix

bugfix

--- app.iced
+++ app.iced
@@ -52,7 +52,7 @@
       task.total += file.size
       if file.finished
         task.fetched += file.size
-      else if file.retrieving
+      else if file.retrieving && client.stats.retrieving?.progress
         task.fetched += client.stats.retrieving.progress.currentSize
     task.progress = task.fetched * 100 / task.total
   data =


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -27,7 +27,8 @@
 test "should move player up while key is pressed", ->
   ;

-test "should move player down while key is pres"
+test "should move player down while key is pressed", ->
+

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/controllable.coffee
+++ test/controllable.coffee
@@ -14,4 +14,5 @@

+
 module()


removed unneeded semicolon

removed unneeded semicolon

--- core/app/backbone/collections/subchannels.coffee
+++ core/app/backbone/collections/subchannels.coffee
@@ -4,4 +4,4 @@
   initialize: (models, opts) ->
     @channel = opts.channel

-  url: -> @channel.url() + '/subchannels';
+  url: -> @channel.url() + '/subchannels'


Renamed other_channel to channel

Renamed other_channel to channel

--- core/app/backbone/views/notification_view.coffee
+++ core/app/backbone/views/notification_view.coffee
@@ -32,25 +32,14 @@
   initialize: ->
     @activity = @model.get('activity')

-  suggested_topics: ->
-    topic1 = new Topic
-                  title:      @activity.channel_title,
-                  slug_title: @activity.channel_slug_title
-
-    topic2 = new Topic
-                  title:      @activity.to_channel_title,
-                  slug_title: @activity.to_channel_slug_title
-
-    return new SuggestedTopics [topic1, topic2]
-
-  other_channel: ->
-    new Channel
+  channel: ->
+    channel = new Channel
       id: @activity.to_channel_id
       containing_channel_ids: @activity.to_channel_containing_channel_ids

   onRender: ->
     super()
-    add_back_button = new FollowChannelButtonView(model: @other_channel())
+    add_back_button = new FollowChannelButtonView(model: @channel())

     @addBackRegion.show add_back_button


IE9 didn’t return the proper filepath

IE9 didn’t return the proper filepath

--- app/assets/javascripts/s3_direct_upload.js.coffee
+++ app/assets/javascripts/s3_direct_upload.js.coffee
@@ -137,8 +137,11 @@
       content.filepath       = $('<a />').attr('href', content.url)[0].pathname
     else # IE <= 9 retu      rn a null result object so we use the file object instead
       domain                 = $uploadForm.attr('action')
-      content.filepath       = $uploadForm.find('input[name=key]').val().replace('/${filename}', '')
-      content.url            = domain + content.filepath + '/' + encodeURIComponent(file.name)
+      filepath               = $uploadForm.find('input[name=key]').val().replace('/${filename}', '')
+      content.url            = domain + filepath + '/' + encodeURIComponent(file.name)
+      parser                 = document.createElement('a')
+      parser.href            = content.url
+      content.filepath       = '/' + parser.pathname

     content.filename         = file.name
     content.filesize         = file.size if 'size' of file


Main feature, removes jQuery dependency (#173)

This was cherry picked from https://github.com/dm67x/social-share-button/commit/71b9fd926912d372272e8cf70281d8474cc17d25

but without the addition of another social network

--- app/assets/javascripts/social-share-button.coffee
+++ app/assets/javascripts/social-share-button.coffee
@@ -7,14 +7,17 @@
     false

   share : (el) ->
-    site = $(el).data('site')
-    appkey = $(el).data('appkey') || ''
-    $parent = $(el).parent()
-    title = encodeURIComponent($(el).data(site + '-title') || $parent.data('title') || '')
-    img = encodeURIComponent($parent.data("img") || '')
-    url = encodeURIComponent($parent.data("url") || '')
-    via = encodeURIComponent($parent.data("via") || '')
-    desc = encodeURIComponent($parent.data("desc") || ' ')
+    if el.getAttribute == null
+      el = document.querySelector(el)
+
+    site = el.getAttribute("data-site")
+    appkey = el.getAttribute("data-appkey") || ''
+    $parent = el.parentNode
+    title = encodeURIComponent(el.getAttribute("data-" + site + "-title") || $parent.getAttribute('data-title') || '')
+    img = encodeURIComponent($parent.getAttribute("data-img") || '')
+    url = encodeURIComponent($parent.getAttribute("data-url") || '')
+    via = encodeURIComponent($parent.getAttribute("data-via") || '')
+    desc = encodeURIComponent($parent.getAttribute("data-desc") || ' ')

     # tracking click events if google analytics enabled
     ga = window[window['GoogleAnalyticsObject'] || 'ga']
@@ -29,7 +32,7 @@
       when "weibo"
         SocialShareButton.openUrl("http://service.weibo.com/share/share.php?url=#{url}&type=3&pic=#{img}&title=#{title}&appkey=#{appkey}", 620, 370)
       when "twitter"
-        hashtags = encodeURIComponent($(el).data(site + '-hashtags') || $parent.data('hashtags') || '')
+        hashtags = encodeURIComponent(el.getAttribute("data-" + site + "-hashtags") || $parent.getAttribute("data-hashtags") || '')
         via_str = ''
         via_str = "&via=#{via}" if via.length > 0
         SocialShareButton.openUrl("https://twitter.com/intent/tweet?url=#{url}&text=#{title}&hashtags=#{hashtags}#{via_str}", 650, 300)
@@ -59,12 +62,12 @@
         throw new Error("You should require social-share-button/wechat to your application.js") unless window.SocialShareWeChatButton
         window.SocialShareWeChatButton.qrcode
           url: decodeURIComponent(url)
-          header: $(el).attr('title')
-          footer: $(el).data('wechat-footer')
+          header: el.getAttribute('title')
+          footer: el.getAttribute("data-wechat-footer")

       when "tumblr"
         get_tumblr_extra = (param) ->
-          cutom_data = $(el).attr("data-#{param}")
+          cutom_data = el.getAttribute("data-#{param}")
           encodeURIComponent(cutom_data) if cutom_data

         tumblr_params = ->


add weixin callback

add weixin callback

--- app/assets/javascripts/social-share-button.coffee
+++ app/assets/javascripts/social-share-button.coffee
@@ -5,6 +5,12 @@
     else
       window.open(url)
       false
+
+  troggleCallback : (cb) ->
+    if cb && typeof cb === "function"
+      cb()
+    else
+      throw 'cb is not function or is undefined'

   share : (el) ->
     site = $(el).data('site')
@@ -17,6 +23,7 @@
     desc = encodeURIComponent($parent.data("desc") || ' ')
     popup = encodeURIComponent($parent.data("popup") || 'false')
     videos = encodeURIComponent($parent.data("videos") || '')
+    fn = $(el).data('fn') || ''

     if url.length == 0
       url = encodeURIComponent(location.href)
@@ -33,6 +40,8 @@
         SocialShareButton.openUrl("http://shuo.douban.com/!service/share?href=#{url}&name=#{title}&image=#{img}&sel=#{desc}",popup)
       when "facebook"
         SocialShareButton.openUrl("http://www.facebook.com/sharer.php?u=#{url}",popup)
+      when "wechat"
+        SocialShareButton.troggleCallback(fn)
       when "qq"
         SocialShareButton.openUrl("http://connect.qq.com/widget/shareqq/index.html?url=#{url}&title=#{title}&videos=#{videos}&desc=#{desc}&summary=#{desc}&site=#{appkey}", popup)
       when "qqzone"


dont use from keyword as vairable name

dont use from keyword as vairable name

--- app/assets/javascripts/uploadcare/settings.coffee
+++ app/assets/javascripts/uploadcare/settings.coffee
@@ -111,8 +111,8 @@
     settings

   transformOptions = (settings, transforms) ->
-    for key, {from, to} of transforms when settings[key]?
-      settings[key] = to if settings[key] == from
+    for key, transform of transforms when settings[key]?
+      settings[key] = transform.to if settings[key] == transform.from
     settings

   constrainOptions = (settings, constraints) ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/core_object.coffee
+++ src/core_object.coffee
@@ -11,161 +11,162 @@

 ( ->
   root = exports ? this
-
-Core = (I={}) ->
-  self =
-    ###*
-    External access to instance variables. Use of this property should be avoided
-    in general, but can come in handy from time to time.
-
-        I =
-          r: 255
-          g: 0
-          b: 100
-
-        myObject = Core(I)
-
-        # a bad idea most of the time, but it's
-        # pretty convenient to have available.
-        myObject.I.r
-        # => 255
-
-        myObject.I.g
-        # => 0
-
-        myObject.I.b
-        # => 100
-
-    @name I
-    @fieldOf Core#
-    ###
-    I: I
-
-    includedModules: []
-
-    ###*
-    Generates a public jQuery style getter / setter method for each
-    String argument.
-
-        myObject = Core
-          r: 255
-          g: 0
-          b: 100
-
-        myObject.attrAccessor "r", "g", "b"
-
-        myObject.r(254)
-        myObject.r()
-
-        => 254
-
-    @name attrAccessor
-    @methodOf Core#
-    ###
-    attrAccessor: (attrNames...) ->
-      attrNames.each (attrName) ->
-        self[attrName] = (newValue) ->
-          if newValue?
-            I[attrName] = newValue
-            return self
+
+  Core = (I={}) ->
+    self =
+      ###*
+      External access to instance variables. Use of this property should be avoided
+      in general, but can come in handy from time to time.
+
+          I =
+            r: 255
+            g: 0
+            b: 100
+
+          myObject = Core(I)
+
+          # a bad idea most of the time, but it's
+          # pretty convenient to have available.
+          myObject.I.r
+          # => 255
+
+          myObject.I.g
+          # => 0
+
+          myObject.I.b
+          # => 100
+
+      @name I
+      @fieldOf Core#
+      ###
+      I: I
+
+      includedModules: []
+
+      ###*
+      Generates a public jQuery style getter / setter method for each
+      String argument.
+
+          myObject = Core
+            r: 255
+            g: 0
+            b: 100
+
+          myObject.attrAccessor "r", "g", "b"
+
+          myObject.r(254)
+          myObject.r()
+
+          => 254
+
+      @name attrAccessor
+      @methodOf Core#
+      ###
+      attrAccessor: (attrNames...) ->
+        attrNames.each (attrName) ->
+          self[attrName] = (newValue) ->
+            if newValue?
+              I[attrName] = newValue
+              return self
+            else
+              I[attrName]
+
+      ###*
+      Generates a public jQuery style getter method for each String argument.
+
+          myObject = Core
+            r: 255
+            g: 0
+            b: 100
+
+          myObject.attrReader "r", "g", "b"
+
+          myObject.r()
+          => 255
+
+          myObject.g()
+          => 0
+
+          myObject.b()
+          => 100
+
+      @name attrReader
+      @methodOf Core#
+      ###
+      attrReader: (attrNames...) ->
+        attrNames.each (attrName) ->
+          self[attrName] = ->
+            I[attrName]
+
+      ###*
+      Extends this object with methods from the passed in object. A shortcut for Object.extend(self, methods)
+
+          I =
+            x: 30
+            y: 40
+            maxSpeed: 5
+
+          # we are using extend to give player
+          # additional methods that Core doesn't have
+          player = Core(I).extend
+            increaseSpeed: ->
+              I.maxSpeed += 1
+
+          player.I.maxSpeed
+          => 5
+
+          player.increaseSpeed()
+
+          player.I.maxSpeed
+          => 6
+
+      @name extend
+      @methodOf Core#
+      @see Object.extend
+      @returns self
+      ###
+      extend: (options) ->
+        Object.extend self, options
+
+        return self
+
+      ###*
+      Includes a module in this object.
+
+          myObject = Core()
+          myObject.include(Bindable)
+
+          # now you can bind handlers to functions
+          myObject.bind "someEvent", ->
+            alert("wow. that was easy.")
+
+      @name include
+      @methodOf Core#
+      @param {String} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
+      ###
+      include: (modules...) ->
+        for Module in modules
+          if Module.isString?()
+            moduleName = Module
+            Module = Module.constantize()
+          else if moduleName = Module._name
+            # Nothing, captured name in if condition
           else
-            I[attrName]
-
-    ###*
-    Generates a public jQuery style getter method for each String argument.
-
-        myObject = Core
-          r: 255
-          g: 0
-          b: 100
-
-        myObject.attrReader "r", "g", "b"
-
-        myObject.r()
-        => 255
-
-        myObject.g()
-        => 0
-
-        myObject.b()
-        => 100
-
-    @name attrReader
-    @methodOf Core#
-    ###
-    attrReader: (attrNames...) ->
-      attrNames.each (attrName) ->
-        self[attrName] = ->
-          I[attrName]
-
-    ###*
-    Extends this object with methods from the passed in object. A shortcut for Object.extend(self, methods)
-
-        I =
-          x: 30
-          y: 40
-          maxSpeed: 5
-
-        # we are using extend to give player
-        # additional methods that Core doesn't have
-        player = Core(I).extend
-          increaseSpeed: ->
-            I.maxSpeed += 1
-
-        player.I.maxSpeed
-        => 5
-
-        player.increaseSpeed()
-
-        player.I.maxSpeed
-        => 6
-
-    @name extend
-    @methodOf Core#
-    @see Object.extend
-    @returns self
-    ###
-    extend: (options) ->
-      Object.extend self, options
-
-      return self
-
-    ###*
-    Includes a module in this object.
-
-        myObject = Core()
-        myObject.include(Bindable)
-
-        # now you can bind handlers to functions
-        myObject.bind "someEvent", ->
-          alert("wow. that was easy.")
-
-    @name include
-    @methodOf Core#
-    @param {String} Module the module to include. A module is a constructor that takes two parameters, I and self, and returns an object containing the public methods to extend the including object with.
-    ###
-    include: (modules...) ->
-      for Module in modules
-        if Module.isString?()
-          moduleName = Module
-          Module = Module.constantize()
-        else if moduleName = Module._name
-          # Nothing, captured name in if condition
-        else
-          # Attempt to look up module in global namespace
-          for key, value of root
-            if value is Module
-              moduleName = key
-
-        if moduleName
-          unless I.included_modules.include moduleName
-            I.included_modules.push moduleName
+            # Attempt to look up module in global namespace
+            for key, value of root
+              if value is Module
+                moduleName = key
+
+          if moduleName
+            unless I.included_modules.include moduleName
+              I.included_modules.push moduleName
+              self.extend Module(I, self)
+          else
+            warn "Unable to discover name for module: ", Module
             self.extend Module(I, self)
-        else
-          warn "Unable to discover name for module: ", Module
-          self.extend Module(I, self)
-
-      return self
-
-    send: (name, args...) ->
-      self[name](args...)
+
+        return self
+
+      send: (name, args...) ->
+        self[name](args...)
+{


GenerateKonfig: fix gitlab tests for dev environments

--- config/generateKonfig.coffee
+++ config/generateKonfig.coffee
@@ -7,10 +7,10 @@
     forcedRecipientEmail: null
     forcedRecipientUsername: null

-  githubapi      =
-    debug       : options.debugGithubAPI
-    timeout     : 5000
-    userAgent   : "Koding-Bridge-#{options.configName}"
+  githubapi =
+    debug: options.debugGithubAPI
+    timeout: 5000
+    userAgent: "Koding-Bridge-#{options.configName}"

   gitlab =
     host: options.gitlabHost or credentials.gitlab.host
@@ -20,7 +20,7 @@
     team: credentials.gitlab.team
     redirectUri: "http://#{credentials.gitlab.team}.#{options.host}/-/oauth/gitlab/callback"
     systemHookToken: options.gitlabToken or credentials.gitlab.systemHookToken
-    hooksEnabled: options.gitlabHost or credentials.gitlab.host
+    hooksEnabled: options.gitlabHost? or credentials.gitlab?.host? or credentials.gitlab?.hooksEnabled

   regions =
     kodingme: "#{options.configName}"


add appearance_code and offering_id to fixtures

--- tutor/specs/courses-test-data.coffee
+++ tutor/specs/courses-test-data.coffee
@@ -2,6 +2,8 @@
   id: 1
   name: 'Local Test Course One'
   book_id: '123'
+  appearance_code: 'testing'
+  offering_id: 1
   roles: [
     {
       "type": "student"
@@ -13,6 +15,8 @@
   id: 2
   name: 'Local Test Course Two'
   book_id: '123'
+  appearance_code: 'testing'
+  offering_id: 1
   roles: [
     {
       "type": "teacher"
@@ -24,6 +28,8 @@
   id: 3
   book_id: '123'
   name:'Local Test Course Three'
+  appearance_code: 'testing'
+  offering_id: 1
   roles: [
     {
       type: 'student'
@@ -38,6 +44,8 @@
   id: 4
   book_id: '123'
   name:'Local Test Course Three'
+  appearance_code: 'testing'
+  offering_id: 1
   roles: []
 }


docs(app generator demo app): correct example model's documentation

It's not a singleton.

--- generators/app/templates/demo/models/example.coffee
+++ generators/app/templates/demo/models/example.coffee
@@ -17,12 +17,13 @@
     ###*
     #   Example of a model
     #
-    #   @author Raymond de Wit
-    #   @class ExampleModel
-    #   @static
-    #   @extends Backbone.Model
-    #   @moduletype model
+    #   @author         Raymond de Wit
+    #   @class          ExampleModel
+    #   @extends        Backbone.Model
+    #   @constructor
+    #   @moduletype     model
     ###
+
     class ExampleModel extends Backbone.Model

         defaults:


build mockup for dpi-results Word document

--- server/methods/word.coffee
+++ server/methods/word.coffee
@@ -49,7 +49,7 @@
     desc.reference = Reference.findOne(_id: desc.referenceID)
     desc.coexposuresList = desc.coexposures.join(', ')
     desc.isCaseControl = desc.studyDesign in CaseControlTypes
-    desc.notes = desc.comments or ""
+    desc.notes = desc.notes or ""

 prepareEpiResult = (res) ->
     res.covariatesList = share.capitalizeFirst(res.covariates.join(', '))


Comment out tests

Comment out tests

--- test/general.test.coffee
+++ test/general.test.coffee
@@ -7,29 +7,29 @@
 describe 'Check the main method at high level', ()->

   it 'Should return an array given any input', ()->
-    expect(removeWords('Hello World!')).to.be.instanceof(Array);
-    expect(removeWords('')).to.be.instanceof(Array);
-    expect(removeWords('', '')).to.be.instanceof(Array);
+#    expect(removeWords('Hello World!')).to.be.instanceof(Array);
+#    expect(removeWords('')).to.be.instanceof(Array);
+#    expect(removeWords('', '')).to.be.instanceof(Array);

   it 'Should strip out generic keywords', ()->
-    expect(removeWords('Is it going to be sunny in London?'))
-    .eql([ 'sunny', 'london' ])
+#    expect(removeWords('Is it going to be sunny in London?'))
+#    .eql([ 'sunny', 'london' ])

-    expect(removeWords('Thanks JavaScript for brightening my day with TypeError'))
-    .eql(['thanks', 'javascript', 'brightening', 'day', 'typeerror'])
+#    expect(removeWords('Thanks JavaScript for brightening my day with TypeError'))
+#    .eql(['thanks', 'javascript', 'brightening', 'day', 'typeerror'])

   it 'Should still strip out words even if the case is different', () ->
-    expect(removeWords('foo bar BAR and FOO AND bar'))
-    .eql(['foo', 'bar'])
+#    expect(removeWords('foo bar BAR and FOO AND bar'))
+#    .eql(['foo', 'bar'])

-    expect(removeWords('Dinosaurs ARE AWESOME '))
-    .eql(['dinosaurs', 'awesome'])
+#    expect(removeWords('Dinosaurs ARE AWESOME '))
+#    .eql(['dinosaurs', 'awesome'])

   it 'Should be able to cope with all special characters', ()->
-    expect(removeWords('omg!! *CoffeeScript* is so totally amazing!!!*&("&*&"^'))
-    .eql(['omg', 'coffeescript', 'totally', 'amazing'])
-    expect(removeWords('t.o.o muc%h p*u*n*c*t*u-a-t-i-o=n i*s n^o$t co[o]l}{'))
-    .eql(['punctuation', 'cool'])
+#    expect(removeWords('omg!! *CoffeeScript* is so totally amazing!!!*&("&*&"^'))
+#    .eql(['omg', 'coffeescript', 'totally', 'amazing'])
+#    expect(removeWords('t.o.o muc%h p*u*n*c*t*u-a-t-i-o=n i*s n^o$t co[o]l}{'))
+#    .eql(['punctuation', 'cool'])

   it 'Should remove specified keywords from an array in the second param', ()->
     expect(removeWords('everything is stupid', ['everything', 'is'])).eql(['stupid'])
@@ -41,16 +41,16 @@
     .eql(['everything', 'is'])

   it 'Should remove duplicates', ()->
-    expect(removeWords('foo bar and is foo bar If bar foo why'))
-    .eql(['foo', 'bar'])
+#    expect(removeWords('foo bar and is foo bar If bar foo why'))
+#    .eql(['foo', 'bar'])

   it 'should not crash with invalid sentence (first param)', ()->
-    removeWords()           # Forgot any paramaters
-    removeWords([])         # Passes array as first param
-    removeWords(undefined)  # Passes undefined as first param
-    removeWords(0)          # Passes integer as first param
-    removeWords('')         # Passes empty string as first param
+#    removeWords()           # Forgot any paramaters
+#    removeWords([])         # Passes array as first param
+#    removeWords(undefined)  # Passes undefined as first param
+#    removeWords(0)          # Passes integer as first param
+#    removeWords('')         # Passes empty string as first param

   it 'should not crash with invalid optional second param', ()->


simplify admin ajax success handling

simplify admin ajax success handling

--- app/assets/javascripts/admin/enrollments.js.coffee
+++ app/assets/javascripts/admin/enrollments.js.coffee
@@ -1,25 +1,11 @@
 $ ->
   pageIdentifier = '#admin-enrollments_index '
-  $(document)
-    .on 'ajax:success', pageIdentifier + '.email-panel form', ->
-      $('.email-panel').hide().find('textarea').val('')
-      swal
-        title: 'Send Successfully!'
-        type: "success"
-
-    .on 'ajax:success', pageIdentifier + '.enrollments .pay', ->
-      swal
-        title: 'Set Successfully!'
-        type: "success"
-      $('td.paid').text 'true'
-
-    .on 'ajax:success', pageIdentifier + '.enrollments .send-welcome-email', ->
-      swal
-        title: 'Send Successfully'
-        type: 'success'
+  $(document).on "ajax:success", "#admin-enrollments_index", ->
+    swal
+      title: 'Send Successfully!'
+      type: "success"

-


fixing typo

fixing typo

--- client/views/app/message.coffee
+++ client/views/app/message.coffee
@@ -26,7 +26,7 @@
 				return this.html

 	system: ->
-		return 'system' this.t in ['s', 'p', 'f', 'r', 'au', 'ru', 'ul', 'nu', 'wm', 'uj']
+		return 'system' if this.t in ['s', 'p', 'f', 'r', 'au', 'ru', 'ul', 'nu', 'wm', 'uj']

 	isEditing: ->
 		return this._id is Session.get('editingMessageId')


flux/environment: add setLabel action

flux/environment: add setLabel action

--- client/app/lib/flux/environment/actions.coffee
+++ client/app/lib/flux/environment/actions.coffee
@@ -769,6 +769,10 @@

     async.series queue, (err) -> if err then reject() else resolve()

+setLabel = (machineUId, label) ->
+
+  fetchMachineByUId machineUId, (machine) ->
+    machine.setLabel label

 module.exports = {
@@ -815,4 +819,5 @@
   unshareMachineWithUser
   unshareMachineWihAllUsers
   disconnectMachine
+  setLabel
 }


video: model minor

video: model minor

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -25,7 +25,7 @@

     super options, data

-    @state = _.assign {}, options.state, @defaultState
+    @state = _.assign {}, @defaultState, options.state

     { @channel, @view } = options

@@ -123,7 +123,7 @@
       { connection, name } = event.stream

       # Emit necessary events for that participant
-      @setParticipantLeft connectionId
+      @setParticipantLeft connection.connectionId

       # end session if there are no other subscribers that are sending a video,
       # or audio to the session.


video/model: minor

video/model: minor

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -149,6 +149,16 @@
     session.on 'signal:mute', => @setAudioState off

+  ###*
+   * Subscribes to given session's given stream.
+   *
+   * It uses helper method to subscribe, it's success callback will return the
+   * `OT.Subscriber` object. From that on, the conversion from `OT.Subscriber`
+   * to `ParticipantType.Subscriber` and other jobs happen in success handler.
+   *
+   * @param {OT.Session} session
+   * @param {OT.Stream} stream
+  ###
   subscribeToStream: (session, stream) ->

     helper.subscribeToStream session, stream, @getView().getContainer(),
@@ -165,13 +175,13 @@
   ###
   enableVideo: (callbacks) ->

-    options = { publishAudio: @isMySession(), publishVideo: @isMySession() }
+    @setActive()
+
     success = (publisher) =>
       @handlePublishSuccess publisher
       callbacks.success? publisher

-    @setActive()
-
+    options = { publishAudio: @isMySession(), publishVideo: @isMySession() }
     @startPublishing options,
       success : success
       error   : callbacks.error
@@ -308,7 +318,7 @@
     @unregisterSubscriber connectionId

     { nick } = _participant
-    @emit 'ParticipantLeft', {nick}
+    @emit 'ParticipantLeft', helper.defaultSubscriber nick

     # if leaving participant is selected or active participant
     if @state.activeParticipant is nick
@@ -504,6 +514,10 @@
     @emit 'SelectedParticipantChanged', nick, yes

+  ###*
+   * @param {string} nickname
+   * @return {boolean}
+  ###
   isParticipantOnline: (nickname) -> @getParticipant(nickname)?


video: model add calls for ending session

--- client/app/lib/videocollaboration/model.coffee
+++ client/app/lib/videocollaboration/model.coffee
@@ -206,6 +206,13 @@
     @setState { active: yes }

+  setEnded: (type) ->
+    return  unless @state.active
+
+    @emit 'VideoCollaborationEnded'
+    @setState { active: no }
+
+
   ###*
    * Action for joining to VideoCollaboration session. It calls
    * `startPublishing` method and connects handlers for success and error states.
@@ -219,6 +226,13 @@
       error   : (err) =>

+  end: ->
+
+    @stopSession
+      success : @bound 'handleStopSuccess'
+      error   : (err) =>
+
+
   ###*
    * Handler for successful video publishing. It transforms and registers given
    * `OT.Publisher` instance. Sets active participant to user.
@@ -233,6 +247,12 @@

     @setActive()
     @changeActiveParticipant getNick()
+
+
+  handleStopSuccess: ->
+
+    @setState { publishing: off }
+    @setEnded()

   ###*
@@ -270,6 +290,16 @@
         else callbacks.success publisher

+  stopSession: (callbacks) ->
+
+    @_service.destroyPublisher @channel, @publisher, (err) =>
+      return callbacks.error err  if err
+      @_service.sendSessionEndSignal @channel, (err) =>
+        return callbacks.error err  if err
+        callbacks.success()
+
+
+
   ###*
    * Changes active user defensively.
    *


bugFuchs

bugFuchs

--- app/assets/javascripts/application.js.erb.coffee
+++ app/assets/javascripts/application.js.erb.coffee
@@ -72,7 +72,7 @@
       showNotification "An error occurred, a developer should check the console for details", false
     complete: (jqxhr, status) ->
       btn.button "reset"
-      $("textarea[name=qb-question]").removeAttr "readonly"
+      $("textarea[name=qb-all-question]").removeAttr "readonly"

 $(document).on "keydown", "textarea[name=ib-answer]", (evt) ->
   iid = $(this)[0].dataset.id


fixed a bug preventing the resizing of the sidebar

--- app/assets/javascripts/cargo_wiki/home.js.coffee
+++ app/assets/javascripts/cargo_wiki/home.js.coffee
@@ -11,6 +11,8 @@

   # setup the nav bar
   $('nav').height($(document).height())
+  $(window).resize ->
+    $('nav').height($(document).height())

   # hide the flash
   setTimeout(hideFlash, 5000)


Remove editSession options support

Remove editSession options support

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -86,8 +86,7 @@
     if editorOrOptions instanceof Editor
       editor = editorOrOptions
     else
-      {editor, editSession, @mini, placeholderText} = editorOrOptions ? {}
-      editor ?= editSession # TODO: Remove this line after packages have updated their api to use Editor and EditorView
+      {editor, @mini, placeholderText} = editorOrOptions ? {}

     @id = EditorView.nextEditorId++
     @lineCache = []


Remove duplicate . from default nonWordCharacters

--- src/editor-view.coffee
+++ src/editor-view.coffee
@@ -50,7 +50,7 @@
     showLineNumbers: true
     autoIndent: true
     normalizeIndentOnPaste: true
-    nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
+    nonWordCharacters: "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"
     preferredLineLength: 80
     tabLength: 2
     softWrap: false


engine data module

engine data module

--- src/engine_data.coffee
+++ src/engine_data.coffee
@@ -11,7 +11,9 @@
   Object.reverseMerge I,
     data: {}

-
+  Object.defineProperty Number::, 'degrees',
+    get: ->
+      this * Math.TAU / 360

   get: (key) ->
     I.data[key]


Tests: ApplicationManager: Improvements for spy and restoreSpies.

--- client/app/test/applicationmanager.test.coffee
+++ client/app/test/applicationmanager.test.coffee
@@ -20,18 +20,10 @@
 describe 'kd.singletons.appManager', ->

-  beforeEach ->
-
-    spy        = null
-    appManager = new ApplicationManager
-
-
-  afterEach ->
-
-    if spy
-      spy.restore()
-      expect.restoreSpies()
-      spy = null
+  beforeEach -> appManager = new ApplicationManager
+
+
+  afterEach -> expect.restoreSpies()

   describe 'constructor', ->
@@ -45,7 +37,7 @@

       isRegistered = no
       appInstance  = new AppController name: 'FooApp'
-      spy = expect.spyOn appManager, 'setListeners'
+      expect.spyOn appManager, 'setListeners'

       appManager.on 'AppRegistered', -> isRegistered = yes
       appManager.register appInstance
@@ -135,7 +127,7 @@

     it 'should warn if there is no name', ->

-      spy = expect.spyOn kd, 'warn'
+      expect.spyOn kd, 'warn'

       appManager.tell()
       expect(kd.warn).toHaveBeenCalled()
@@ -224,7 +216,7 @@
     it 'should load app', (done) ->

       globals.config.apps.InternalApp = name: 'InternalApp'
-      spy = expect.spyOn KodingAppsController, 'loadInternalApp'
+      expect.spyOn KodingAppsController, 'loadInternalApp'

       appManager.create 'InternalApp', {}

@@ -244,7 +236,7 @@
       createApps [ 'FooApp', 'BarApp' ]

       appManager.on 'AppIsBeingShown', -> isAppShown = yes
-      spy = expect.spyOn appManager, 'setLastActiveIndex'
+      expect.spyOn appManager, 'setLastActiveIndex'

       appManager.appControllers.FooApp.instances.first.appIsShown = ->
         isAppIsShownCallbackExecuted = yes
@@ -275,7 +267,7 @@

       createApps [ 'App1', 'App2', 'App3' ], yes
       appManager.on 'AppIsBeingShown', -> isEventEmitted = yes
-      spy = expect.spyOn appManager, 'setLastActiveIndex'
+      expect.spyOn appManager, 'setLastActiveIndex'

       expect(appManager.getFrontApp().options.name).toBe 'App3'

@@ -369,7 +361,7 @@

     it 'should warn if open called with no name', ->

-      spy = expect.spyOn(kd, 'warn')
+      expect.spyOn(kd, 'warn')
       appManager.open()
       expect(kd.warn).toHaveBeenCalled()


Restored dontFetch param to objectAt method

Simply handed over to requestObjectAt method

This strategy still used by `deselectObject` in selectable mixin

--- packages/emberella/lib/controllers/sparse_array_controller.coffee
+++ packages/emberella/lib/controllers/sparse_array_controller.coffee
@@ -154,14 +154,15 @@

     @method objectAt
     @param {Integer} idx The index to obtain content for
+    @param {Boolean} dontFetch Won't obtain remote data if `true`
     @return {Object}
   ###
-  objectAt: (idx) ->
+  objectAt: (idx, dontFetch) ->
     idx = parseInt idx, 10
     return undefined if (isNaN(idx) or (idx < 0) or (idx >= get(@, 'length')))
     result = @_super(idx) ? @insertSparseArrayItem(idx)
     return result if (result and result.isStale isnt true)
-    @requestObjectAt(idx)
+    @requestObjectAt(idx, dontFetch)

   ###
     Fetches data at the specified index. If `rangeSize` is greater than 1, this


Use promises for pods store database.

Use promises for pods store database.

--- app/assets/javascripts/pods/pods_store.js.coffee
+++ app/assets/javascripts/pods/pods_store.js.coffee
@@ -3,7 +3,7 @@
     @writeObjects(new_records)
   all: (sortBy, asc=true) ->
     promise = new Promise (resolve, reject) =>
-      @database (db) ->
+      @database().then (db) ->
         records = []
         t = db.transaction 'pods', 'readonly'
         store = t.objectStore('pods')
@@ -23,14 +23,14 @@
           reject(e)
     promise
   writeObjects: (pods) ->
-    @database (db) ->
+    @database().then (db) ->
       t = db.transaction 'pods', 'readwrite'
       s = t.objectStore('pods')
       for pod in pods
         r = s.put(pod)
   countAll: () ->
     promise = new Promise (resolve, reject) =>
-      @database (db) ->
+      @database.then (db) ->
         t = db.transaction 'pods', 'readonly'
         s = t.objectStore('pods')
         r = s.count()
@@ -39,15 +39,18 @@
         r.onerror = ->
           reject(e)
     promise
-  database: (callback) ->
-    r = indexedDB.open('pods', 1)
-    r.onupgradeneeded = (e) ->
-      db = event.target.result
-      if !db.objectStoreNames.contains 'pods'
-        store = db.createObjectStore 'pods',
-          keyPath: 'name'
-        store.createIndex "stars", "stars",
-          unique: false
-    r.onsuccess = (e) ->
-      db = r.result
-      callback(db)+  database: () ->
+    new Promise (resolve, reject) =>
+      r = indexedDB.open('pods', 1)
+      r.onupgradeneeded = (e) ->
+        db = event.target.result
+        if !db.objectStoreNames.contains 'pods'
+          store = db.createObjectStore 'pods',
+            keyPath: 'name'
+          store.createIndex "stars", "stars",
+            unique: false
+      r.onsuccess = (e) ->
+        db = r.result
+        resolve(db)
+      r.onerror = (e) ->
+        reject(e)


fixed epsilon-rounding

fixed epsilon-rounding

--- app/assets/javascripts/pullVerticesWorker.coffee
+++ app/assets/javascripts/pullVerticesWorker.coffee
@@ -59,9 +59,12 @@
 		max_z = min_z = cubeVertices[0][2] | 0
 		for i in [1...cubeVertices.length]
 			vertex = cubeVertices[i]
-			x = vertex[0] | 0
-			y = vertex[1] | 0
-			z = vertex[2] | 0
+			x = vertex[0]
+			y = vertex[1]
+			z = vertex[2]
+			x = (x + if x >= 0 then EPSILON else -EPSILON) | 0
+			y = (y + if y >= 0 then EPSILON else -EPSILON) | 0
+			z = (z + if z >= 0 then EPSILON else -EPSILON) | 0
 			max_x = if x > max_x then x else max_x
 			max_y = if y > max_y then y else max_y
 			max_z = if z > max_z then z else max_z


Draggable

Draggable

- Limit helper to body scrollWidth / scrollHeight.
- Fixed helper containment when helper has margins.

--- src/base/DragDropSelect/Draggable.coffee
+++ src/base/DragDropSelect/Draggable.coffee
@@ -430,34 +430,39 @@
 				top: globalDrag.helperNodeStart.top
 				left: globalDrag.helperNodeStart.left

+		pos =
+			x: helper_pos.left
+			y: helper_pos.top
+			w: globalDrag.helperNodeStart.width
+			h: globalDrag.helperNodeStart.height
+
 		if @_helper_contain_element
 			dim_contain = CUI.DOM.getDimensions(@_helper_contain_element)
-
-			pos =
-				x: helper_pos.left
-				y: helper_pos.top
-				w: globalDrag.helperNodeStart.width
-				h: globalDrag.helperNodeStart.height

 			# pos is changed in place
 			Draggable.limitRect pos,
 				min_x: dim_contain.viewportLeft + dim_contain.borderLeftWidth
-				max_x: dim_contain.viewportRight - dim_contain.borderRightWidth
+				max_x: dim_contain.viewportRight - dim_contain.borderRightWidth - globalDrag.helperNodeStart.marginHorizontal
 				min_y: dim_contain.viewportTop + dim_contain.borderTopWidth
-				max_y: dim_contain.viewportBottom - dim_contain.borderBottomWidth
-
-			helper_pos =
-				top: pos.y
-				left: pos.x
-
-			helper_pos.dragDiff =
-				y: helper_pos.top - globalDrag.helperNodeStart.top
-				x: helper_pos.left - globalDrag.helperNodeStart.left
-
+				max_y: dim_contain.viewportBottom - dim_contain.borderBottomWidth - globalDrag.helperNodeStart.marginVertical
 		else
-			helper_pos.dragDiff =
-				y: diff.y
-				x: diff.x
+			dim_contain = globalDrag.helperNodeStart.body_dim
+
+			Draggable.limitRect pos,
+				min_x: dim_contain.borderLeftWidth
+				max_x: dim_contain.scrollWidth - dim_contain.borderRightWidth - globalDrag.helperNodeStart.marginHorizontal
+				min_y: dim_contain.borderTopWidth
+				max_y: dim_contain.scrollHeight - dim_contain.borderBottomWidth - globalDrag.helperNodeStart.marginVertical
+
+		# console.debug "limitRect", dump(pos), dim_contain
+
+		helper_pos =
+			top: pos.y
+			left: pos.x
+
+		helper_pos.dragDiff =
+			x: helper_pos.left - globalDrag.helperNodeStart.left
+			y: helper_pos.top - globalDrag.helperNodeStart.top

 		@_helper_set_pos?(globalDrag, helper_pos)

@@ -523,6 +528,9 @@
 		start.height = dim.borderBoxHeight
 		start.marginTop = dim.marginTop
 		start.marginLeft = dim.marginLeft
+		start.marginVertical = dim.marginVertical
+		start.marginHorizontal = dim.marginHorizontal
+		start.body_dim = CUI.DOM.getDimensions(document.body)

 		globalDrag.helperNodeStart = start

@@ -578,7 +586,7 @@
 				pos.w -= diff
 				continue

-			# CUI.debug "correcting #{skey} by #{diff} from #{pos[skey]}"
+			# console.debug "correcting #{skey} by #{diff} from #{pos[skey]}"

 			pos[skey]-=diff


Grab the username off the body, not the query

--- apps/badges/routes.coffee
+++ apps/badges/routes.coffee
@@ -74,7 +74,7 @@
             res.redirect '/badges'

     app.post '/issue/:slug', (req, res, next) ->
-      username = req.query.username
+      username = req.body.username
       Badge.findOne slug: req.params.slug, (err, badge) ->
         next(err) if err

@@ -86,19 +86,19 @@
               res.send JSON.stringify(response),
                 'content-type': 'application/json'

-    app.get '/issue/:slug', (req, res, next) ->
-      username = req.query.username
-      Badge.findOne slug: req.params.slug, (err, badge) ->
-        next(err) if err
+    # app.get '/issue/:slug', (req, res, next) ->
+    #   username = req.query.username
+    #   Badge.findOne slug: req.params.slug, (err, badge) ->
+    #     next(err) if err

-        User.findOrCreate username,
-          {issuer_id: badge.issuer_id, tags: req.query.tags},
-          (err, user) ->
-            next(err) if err
-            user.earn badge, (err, response) ->
-              next(err) if err
-              res.send JSON.stringify(response),
-                'content-type': 'application/json'
+    #     User.findOrCreate username,
+    #       {issuer_id: badge.issuer_id, tags: req.query.tags},
+    #       (err, user) ->
+    #         next(err) if err
+    #         user.earn badge, (err, response) ->
+    #           next(err) if err
+    #           res.send JSON.stringify(response),
+    #             'content-type': 'application/json'

 formatBadgeResponse = (req, res, badge) ->
   cb = req.query.callback


Stop reloading even if it fails.

Stop reloading even if it fails.

--- src/coffee/services/redmineLoader.coffee
+++ src/coffee/services/redmineLoader.coffee
@@ -37,7 +37,7 @@
         $q.all([@_fetchProjects(a), @_fetchStatuses(a)])
           .then(@_fetchSavedIssues(a))
           .then(@fetchTicketsCount(a))
-          .then(()->
+          .finally(()->
             State.isLoadingAllData = false
             Log.debug("fetchAllData() finish"))


Update environment.coffee

Update environment.coffee

remove unnecessary code in env object

--- src/environment.coffee
+++ src/environment.coffee
@@ -5,7 +5,6 @@
 ###

 @env =
-    _env: this
     #frame rate
     FRAME_RATE: 25
     #Greenhorn engine style
@@ -148,4 +147,4 @@
         borderRadius: 'inherit'
         borderTop: "1px solid #{env.ENGINE.accentColor}"
         fontSize: '1em'
-#end _style object+#end _style object


Binding isColor to `Color` class

Binding isColor to `Color` class

--- framer/Color.coffee
+++ framer/Color.coffee
@@ -269,7 +269,7 @@
 	@toColor: (color) -> return new Color(color)
 	@validColorValue: (color) -> return color instanceof Color or color is null

-	@isColor: (color) ->
+	@isColor: (color) =>
 		if _.isString(color)
 			return @isColorString(color)
 		else


Fixed #390 export link keep the original path

--- wzk/ui/grid/ExportLink.coffee
+++ wzk/ui/grid/ExportLink.coffee
@@ -31,6 +31,7 @@
     'yaml': 'application/x-yaml'
     'picle': 'application/python-pickle'
     'pdf': 'application/pdf'
+    'zip': 'application/zip'

   ###*
     @param {Object} params
@@ -47,9 +48,10 @@
     @override
   ###
   decorateInternal: (el) ->
-    super el
-    goog.events.listen el, goog.events.EventType.CLICK, @handleAction
-    @implicitUriParams = new goog.Uri(@getElement().href).getQueryData()
+    super(el)
+    goog.events.listen(el, goog.events.EventType.CLICK, @handleAction)
+    @origUri = new goog.Uri(@getElement().href)
+    @implicitUriParams = @origUri.getQueryData()
     @implicitUriParams.remove(col) for col in @watcher.getGrid().getColumns()
     undefined

@@ -78,6 +80,7 @@
   ###
   buildUri: ->
     uri = @watcher.getQuery().cloneUri()
+    uri.setPath(@origUri.getPath())
     uri.setParameterValue '_accept', @getType()
     for k in @implicitUriParams.getKeys() when not uri.getParameterValue(k)?
       uri.setParameterValue k, @implicitUriParams.getValues(k)


Put `index.html` under `assets/`

Put `index.html` under `assets/`

--- cli/Gruntfile.coffee
+++ cli/Gruntfile.coffee
@@ -100,7 +100,6 @@
     'autoprefixer'

     'inject:mode'
-    'copy:index'
     'inject:dev'
     'inject:params'
     'concat:params'
@@ -170,10 +169,6 @@
         cwd: 'dev/'
         src: '**/*'
         dest: 'public/'
-
-      index:
-        src: 'app/index.html'
-        dest: 'public/index.html'

       js:
         expand: true


Say "outer", not "global"

Say "outer", not "global"

--- coffeescript/scope_fixes.coffee
+++ coffeescript/scope_fixes.coffee
@@ -1,7 +1,7 @@
-[a, b, c] = [1, 2, 3]      # globals
+[a, b, c] = [1, 2, 3]

 bad = ->
-  a = 10                   # clobbers global a
+  a = 10                   # clobbers outer a

 okay = ->
   ((b) -> b = 20)()        # writes to local b


Disable generation of debug symbols in angularjs, for better performance.

Angularjs by default generates ng-scope, ng-isolated-scope classes necessary for some debugging tools. Generating these classes dynamically has a performance cost, and we don't need them in production. The configuration has been changed to disable the generation of these debug symbols.

For more information see:

https://medium.com/swlh/improving-angular-performance-with-1-line-of-code-a1fb814a6476#.np4myl2dj
http://blog.thoughtram.io/angularjs/2014/12/22/exploring-angular-1.3-disabling-debug-info.html

--- app/assets/javascripts/ng-modules/ng-app.js.coffee
+++ app/assets/javascripts/ng-modules/ng-app.js.coffee
@@ -4,7 +4,7 @@

 module = angular.module 'feedbunch', ['infinite-scroll', 'ngSanitize', 'ngTouch']

-module.config ["$httpProvider", ($httpProvider)->
+module.config ["$httpProvider", "$compileProvider", ($httpProvider, $compileProvider)->
   # Configure $http service to send the CSRF-prevention token expected by rails,
   # otherwise POST, DELETE etc requests will be rejected.
   # Angularjs reads the CSRF token from the "XSRF-TOKEN" cookie by default. It sends it back in the
@@ -21,4 +21,8 @@
       else
         return $q.reject rejection
   ]
-]+
+  # Disable debug info from app. This removes ng-scope and ng-isolated-scope classes needed by some
+  # debuggers, along with the performance hit of adding and removing these clases dynamically.
+  $compileProvider.debugInfoEnabled false
+]


Expose defaults

Expose defaults

--- app/assets/javascripts/uploadcare/widget.js.coffee
+++ app/assets/javascripts/uploadcare/widget.js.coffee
@@ -13,6 +13,7 @@

 expose 'whenReady'
 uploadcare.whenReady ->
+  expose 'defaults'
   expose 'fileFrom'
   expose 'openDialog'
   expose 'Circle', uploadcare.ui.progress.Circle


TEST:VMSharing fix coffeelint error

TEST:VMSharing fix coffeelint error

--- client/test/lib/helpers/terminalhelpers.coffee
+++ client/test/lib/helpers/terminalhelpers.coffee
@@ -35,7 +35,7 @@
         .click                   openNewTerminal
         .pause                   5000, -> callback null #wait for terminal to be displayed

-
+
   createTerminalSession: ( browser, user, callback = -> ) ->

     userName                   = user.username


used "xline" for the line inserted by <down> at eof

--- client/editor.coffee
+++ client/editor.coffee
@@ -57,7 +57,7 @@
   {id, emit} = opts
   ($e = $ e).html EDITOR_HTML
   isDebugger = opts.debugger
-  volatileLine = null # the line number of the empty line inserted when cursor is at eof and you press <down>
+  xline = null # the line number of the empty line inserted when cursor is at eof and you press <down>

   cm = CodeMirror $e.find('.cm')[0],
     firstLineNumber: 0, scrollButtonHeight: 12, lineNumberFormatter: (i) -> "[#{i}]"
@@ -78,16 +78,16 @@
       Down: ->
         l = cm.getCursor().line
         if l != cm.lineCount() - 1 || /^\s*$/.test cm.getLine l then cm.execCommand 'goLineDown'
-        else cm.execCommand 'goDocEnd'; cm.execCommand 'newlineAndIndent'; volatileLine = l + 1
+        else cm.execCommand 'goDocEnd'; cm.execCommand 'newlineAndIndent'; xline = l + 1
         return

   cm.on 'cursorActivity', ->
-    if volatileLine != null
+    if xline != null
       n = cm.lineCount(); l = cm.getCursor().line
-      if l != volatileLine || l != n - 1 || !/^\s*$/.test cm.getLine n - 1
-        if l < volatileLine && volatileLine == n - 1 && /^\s*$/.test cm.getLine n - 1
+      if l != xline || l != n - 1 || !/^\s*$/.test cm.getLine n - 1
+        if l < xline && xline == n - 1 && /^\s*$/.test cm.getLine n - 1
           cm.replaceRange '', {line: n - 2, ch: cm.getLine(n - 2).length}, {line: n - 1, ch: 0}, 'D'
-        volatileLine = null
+        xline = null
     return

   createBreakpointElement = ->


Close overlay when done

Close overlay when done

--- client/import.coffee
+++ client/import.coffee
@@ -35,6 +35,11 @@

             Meteor.call 'finishPublicationUpload', publicationId, (err) ->
               throw err if err
+              if Publications.find(
+                'importing.uploadProgress':
+                  $lt: 1
+              ).count() == 0
+                Session.set 'uploadOverlayActive', false
               console.log 'Upload successful'

       reader.readAsArrayBuffer file


Verify if the file is really a PDF file before uploading.

--- client/import.coffee
+++ client/import.coffee
@@ -10,6 +10,116 @@
 ImportingFiles = new Meteor.Collection null

 UPLOAD_CHUNK_SIZE = 128 * 1024 # bytes
+
+verifyFile = (file, fileContent, publicationId, samples) ->
+  samplesData = _.map samples, (sample) ->
+    new Uint8Array fileContent.slice sample.offset, sample.offset + sample.size
+  Meteor.call 'verifyPublication', publicationId, samplesData, (err) ->
+    if err
+      ImportingFiles.update file._id,
+        $set:
+          status: err.toString()
+      return
+
+    ImportingFiles.update file._id,
+      $set:
+        finished: true
+        publicationId: publicationId
+
+uploadFile = (file, publicationId) ->
+  meteorFile = new MeteorFile file,
+    collection: ImportingFiles
+
+  meteorFile.upload file, 'uploadPublication',
+    size: UPLOAD_CHUNK_SIZE,
+    publicationId: publicationId
+  ,
+    (err) ->
+      if err
+        ImportingFiles.update file._id,
+          $set:
+            status: err.toString()
+        return
+
+      ImportingFiles.update file._id,
+        $set:
+          finished: true
+          publicationId: publicationId
+
+testPDF = (file, fileContent, callback) ->
+  PDFJS.getDocument(data: fileContent, password: '').then callback, (message, exception) ->
+    ImportingFiles.update file._id,
+      $set:
+        status: "Invalid file: #{ exception or message }"
+
+importFile = (file) ->
+  reader = new FileReader()
+  reader.onload = ->
+    fileContent = @result
+
+    testPDF file, fileContent, ->
+      # TODO: Compute SHA in chunks
+      # TODO: Compute SHA in a web worker?
+      hash = new Crypto.SHA256()
+      hash.update fileContent
+      sha256 = hash.finalize()
+
+      alreadyImporting = ImportingFiles.findOne(sha256: sha256)
+      if alreadyImporting
+        ImportingFiles.update file._id,
+          $set:
+            status: "File is already importing"
+            finished: true
+            # publicationId might not yet be available, but let's try
+            publicationId: alreadyImporting.publicationId
+        return
+
+      ImportingFiles.update file._id,
+        $set:
+          sha256: sha256
+
+      Meteor.call 'createPublication', file.name, sha256, (err, result) ->
+        if err
+          ImportingFiles.update file._id,
+            $set:
+              status: err.toString()
+          return
+
+        if result.already
+          ImportingFiles.update file._id,
+            $set:
+              status: "File already imported"
+              finished: true
+              publicationId: result.publicationId
+          return
+
+        if result.verify
+          verifyFile file, fileContent, result.publicationId, result.samples
+        else
+          uploadFile file, result.publicationId
+
+  ImportingFiles.insert
+    name: file.name
+    status: "Preprocessing file"
+    readProgress: 0
+    uploadProgress: 0
+    finished: false
+  ,
+    # We are using callback to make sure ImportingFiles really has the file now
+    (err, id) ->
+      throw err if err
+
+      # So that meteor-file knows what to update
+      file._id = id
+
+      # We try to make sure list of files is rendered before hashing
+      Deps.flush()
+
+      # Just temporary return to the event thread so that everything finishes rendering
+      Meteor.setTimeout ->
+        # TODO: We should read in chunks, not whole file
+        reader.readAsArrayBuffer file
+      , 5 # 0 does not seem to work, 5 seems to work

 Meteor.startup ->
   $(document).on 'dragenter', (e) ->
@@ -54,100 +164,7 @@
       Session.set 'importOverlayActive', false
       return

-    _.each e.dataTransfer.files, (file) ->
-      reader = new FileReader()
-      reader.onload = ->
-        # TODO: Compute SHA in chunks
-        # TODO: Compute SHA in a web worker?
-        hash = new Crypto.SHA256()
-        hash.update @result
-        sha256 = hash.finalize()
-
-        alreadyImporting = ImportingFiles.findOne(sha256: sha256)
-        if alreadyImporting
-          ImportingFiles.update file._id,
-            $set:
-              status: "File is already importing"
-              finished: true
-              # publicationId might not yet be available, but let's try
-              publicationId: alreadyImporting.publicationId
-          return
-
-        ImportingFiles.update file._id,
-          $set:
-            sha256: sha256
-
-        Meteor.call 'createPublication', file.name, sha256, (err, result) ->
-          if err
-            ImportingFiles.update file._id,
-              $set:
-                status: err.toString()
-            return
-
-          if result.already
-            ImportingFiles.update file._id,
-              $set:
-                status: "File already imported"
-                finished: true
-                publicationId: result.publicationId
-            return
-
-          if result.verify
-            samplesData = _.map result.samples, (sample) ->
-              new Uint8Array reader.result.slice sample.offset, sample.offset + sample.size
-            Meteor.call 'verifyPublication', result.publicationId, samplesData, (err) ->
-              if err
-                ImportingFiles.update file._id,
-                  $set:
-                    status: err.toString()
-                return
-
-              ImportingFiles.update file._id,
-                $set:
-                  finished: true
-                  publicationId: result.publicationId
-          else
-            meteorFile = new MeteorFile file,
-              collection: ImportingFiles
-
-            meteorFile.upload file, 'uploadPublication',
-              size: UPLOAD_CHUNK_SIZE,
-              publicationId: result.publicationId
-            ,
-              (err) ->
-                if err
-                  ImportingFiles.update file._id,
-                    $set:
-                      status: err.toString()
-                  return
-
-                ImportingFiles.update file._id,
-                  $set:
-                    finished: true
-                    publicationId: result.publicationId
-
-      ImportingFiles.insert
-        name: file.name
-        status: "Preprocessing file"
-        readProgress: 0
-        uploadProgress: 0
-        finished: false
-      ,
-        # We are using callback to make sure ImportingFiles really has the file now
-        (err, id) ->
-          throw err if err
-
-          # So that meteor-file knows what to update
-          file._id = id
-
-          # We try to make sure list of files is rendered before hashing
-          Deps.flush()
-
-          # Just temporary return to the event thread so that everything finishes rendering
-          Meteor.setTimeout ->
-            # TODO: We should read in chunks, not whole file
-            reader.readAsArrayBuffer file
-          , 5 # 0 does not seem to work, 5 seems to work
+    _.each e.dataTransfer.files, importFile

   'click': (e, template) ->
     # We hide overlay, but in the background we are still uploading


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/flickerable.coffee
+++ src/flickerable.coffee
@@ -60,8 +60,8 @@

   @name flicker
   @methodOf Flickerable#
-  @param {Number} [duration=30] How long the effect lasts
-  @param {Number} [frequency=3] The number of frames in between opacity changes
+  @param {Number} [duration=2] How long the effect lasts
+  @param {Number} [frequency=0.1] The number of frames in between opacity changes
   @param {Number} [alpha=0.5] The alpha value to flicker to
   ###
   flicker: (options={}) ->


add post id to slugs

add post id to slugs

--- api/lib/migrate.coffee
+++ api/lib/migrate.coffee
@@ -134,7 +134,7 @@
       # Map Gravity data into a Positron schema
       data =
         _id: post._id
-        slugs: post._slugs
+        slugs: (post._slugs or []).concat([post._id.toString()])
         author_id: ObjectId(post.author_id)
         thumbnail_title: post.title
         thumbnail_teaser: $?('p')?.first()?.text()


yayfications, it works

yayfications, it works

--- coffee/hyperbolic_tessellation_test_app.coffee
+++ coffee/hyperbolic_tessellation_test_app.coffee
@@ -1,6 +1,6 @@
 "use strict"
 {Tessellation} = require "./hyperbolic_tessellation.coffee"
-{NodeHashMap, nodeMatrixRepr, newNode, showNode, chainEquals, nodeHash} = require "./vondyck_chain.coffee"
+{NodeHashMap, nodeMatrixRepr, newNode, showNode, chainEquals, nodeHash, node2array} = require "./vondyck_chain.coffee"
 {makeAppendRewrite, makeAppendRewriteRef, makeAppendRewriteVerified, vdRule, eliminateFinalA} = require "./vondyck_rewriter.coffee"
 {RewriteRuleset, knuthBendix} = require "./knuth_bendix.coffee"
 {mooreNeighborhood, evaluateTotalisticAutomaton} = require "./field.coffee"
@@ -14,7 +14,44 @@

 colors = ["red", "green", "blue", "yellow", "cyan", "magenta", "gray", "orange"]

-
+class FieldObserver
+  constructor: (@tessellation, @appendRewrite, @minCellSize=1.0/400.0)->
+    @center = null
+    @cells = visibleNeighborhood @tessellation, @appendRewrite, @minCellSize
+    @cellOffsets = (node2array(c) for c in @cells)
+    @cellTransforms = (nodeMatrixRepr(c, @tessellation.group) for c in @cells)
+  rebuildAt: (newCenter) ->
+    @center = newCenter
+    @cells = for offset in @cellOffsets
+      #it is important to make copy since AR empties the array!
+      eliminateFinalA @appendRewrite(newCenter, offset[..]), @appendRewrite, @tessellation.group.n
+    return
+
+  draw: (cells, viewMatrix, context) ->
+    context.fillStyle = "black"
+    context.lineWidth = 1.0/400.0
+    context.strokeStyle = "rgb(128,128,128)"
+
+    #first borders
+    context.beginPath()
+    for cell, i in @cells
+      unless cells.get cell
+        cellTfm = @cellTransforms[i]
+        mtx = M.mul viewMatrix, cellTfm
+        @tessellation.makeCellShapePoincare mtx, context
+    context.stroke()
+
+    #then cells
+    context.beginPath()
+    for cell, i in @cells
+      if cells.get cell
+        cellTfm = @cellTransforms[i]
+        mtx = M.mul viewMatrix, cellTfm
+        @tessellation.makeCellShapePoincare  mtx, context
+    context.fill()
+    return
+
+
 drawVisibleCells = (visibleCells, cells, viewMatrix, tessellation, context) ->
   context.fillStyle = "black"
   context.lineWidth = 1.0/400.0
@@ -214,8 +251,11 @@
 xytFromCell = xyt2cell tessellation.group, appendRewrite

 viewCenter = null
-visibleCells = visibleNeighborhood tessellation, appendRewrite, minVisibleSize #farNeighborhood viewCenter, 5, appendRewrite, tessellation.group.n, tessellation.group.m
-console.log "Visible field contains #{visibleCells.length} cells"
+#visibleCells = visibleNeighborhood tessellation, appendRewrite, minVisibleSize #farNeighborhood viewCenter, 5, appendRewrite, tessellation.group.n, tessellation.group.m
+
+observer = new FieldObserver tessellation, appendRewrite, minVisibleSize
+
+#console.log "Visible field contains #{visibleCells.length} cells"

 transitionFunc = parseTransitionFunction "B 3 S 2 3", tessellation.group.n, tessellation.group.m
 dragHandler = null
@@ -245,7 +285,8 @@
       context.save()
       context.scale s, s
       context.translate 1, 1
-      drawVisibleCells visibleCells, cells, tfm, tessellation, context
+      #drawVisibleCells visibleCells, cells, tfm, tessellation, context
+      observer.draw cells, tfm, context
       context.restore()
     #console.log "Redraw. Population is #{cells.count}"
     #E("population").innerHTML = ""+cells.count
@@ -382,11 +423,18 @@
   #centerCoord = M.mulv tfm, [0.0, 0.0, 1.0]
   pathToCenterCell = xytFromCell centerCoord
   console.log "Jump by #{showNode pathToCenterCell}"
+
   m = nodeMatrixRepr pathToCenterCell, tessellation.group
   #console.log "dMatrix is #{JSON.stringify m}"
   #modifyView M.inv m
   tfm = M.mul tfm, m
   checkViewMatrix()
+
+  #move observation point
+  newCenter = appendRewrite observer.center, node2array(pathToCenterCell)
+  console.log  "New center at #{showNode newCenter}"
+  observer.rebuildAt newCenter
+
   redraw()

 redraw()
@@ -398,12 +446,11 @@
 #precision falls from 1e-16 to 1e-9 in 1000 steps.
 maxViewUpdatesBeforeCleanup = 500
 checkViewMatrix = ->
-  me = [-1,0,0,  0,-1,0, 0,0,-1]
-  d = M.add( me, M.mul(tfm, M.hyperbolicInv tfm))
-  ad = (Math.abs(x) for x in d)
-  maxDiff = Math.max( ad ... )
-  console.log "Step: #{viewUpdates}, R: #{maxDiff}"
-
+  #me = [-1,0,0,  0,-1,0, 0,0,-1]
+  #d = M.add( me, M.mul(tfm, M.hyperbolicInv tfm))
+  #ad = (Math.abs(x) for x in d)
+  #maxDiff = Math.max( ad ... )
+  #console.log "Step: #{viewUpdates}, R: #{maxDiff}"
   if (viewUpdates+=1) > maxViewUpdatesBeforeCleanup
     viewUpdates = 0
     tfm = cleanupHyperbolicMoveMatrix tfm


Don't fade if already visible

Don't fade if already visible

--- core/app/backbone/modules/focus_overlay.coffee
+++ core/app/backbone/modules/focus_overlay.coffee
@@ -5,7 +5,7 @@
   $focus_el = null

   FocusOverlay.show = ($el) ->
-    $overlay_elements.fadeIn 'fast'
+    $overlay_elements.fadeTo 'fast', 1

     FocusOverlay.removeFocus()
     $focus_el = $el


Port ImageToCanvas to Process API

Port ImageToCanvas to Process API

--- components/ImageToCanvas.coffee
+++ components/ImageToCanvas.coffee
@@ -1,26 +1,18 @@
 noflo = require 'noflo'

-class ImageToCanvas extends noflo.Component
-  description: 'Convert image to canvas.'
-  icon: 'file-image-o'
-
-  constructor: ->
-    @image = null
-
-    @inPorts =
-      image: new noflo.Port 'object'
-    @outPorts =
-      canvas: new noflo.Port 'object'
-
-    @inPorts.image.on 'data', (image) =>
-      @image = image
-      @imageToCanvas()
-
-  imageToCanvas: ->
-    return unless @outPorts.canvas.isAttached()
-    return unless @image
-
-    image = @image
+exports.getComponent = ->
+  c = new noflo.Component
+  c.description = 'Convert image to canvas.'
+  c.icon = 'file-image-o'
+  c.inPorts.add 'image',
+    datatype: 'object'
+  c.outPorts.add 'canvas',
+    datatype: 'object'
+  c.forwardBrackets =
+    image: ['canvas']
+  c.process (input, output) ->
+    return unless input.hasData 'image'
+    image = input.getData 'image'

     if noflo.isBrowser()
       canvas = document.createElement 'canvas'
@@ -33,9 +25,6 @@
     context = canvas.getContext '2d'
     context.drawImage image, 0, 0

-    @outPorts.canvas.send canvas
-
-exports.getComponent = -> new ImageToCanvas
-
-
-
+    output.sendDone
+      canvas: canvas
+    return


fix codemirror gutter order

fix codemirror gutter order

--- src/smc-webapp/code-editor/cm-options.coffee
+++ src/smc-webapp/code-editor/cm-options.coffee
@@ -154,9 +154,10 @@
         extraKeys["Alt-Q"]  = (cm) -> cm.foldCodeSelectionAware()
         options.foldGutter  = true
         options.gutters     = ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
+    else
+        options.gutters     = ["CodeMirror-linenumbers"]

     if gutters
-        options.gutters ?= []
         for gutter_id in gutters
             options.gutters.push(gutter_id)


let's get that sourcemap on our file object

--- lib/compiler.coffee
+++ lib/compiler.coffee
@@ -299,9 +299,11 @@
       .with(@)
       .tap(=> @opts = configure_options.call(@))
       .then(compile_or_pass)
-      .then((o) => @content = o)
-      .then(=> sequence(hooks('compile_hooks.after_pass'), @))
-      .then(=> @content)
+      .then (o) =>
+        @content = o.result
+        @sourcemap = o.sourcemap
+        return @content
+      .tap(=> sequence(hooks('compile_hooks.after_pass'), @))

   ###*
    * This function is responsible for getting all the options together for the
@@ -345,5 +347,5 @@
   ###

   compile_or_pass = ->
-    if not @adapter.name then return @content
-    @adapter.render(@content, @opts).then((r) -> r.result)
+    if not @adapter.name then return { result: @content }
+    @adapter.render(@content, @opts)


login tests work

login tests work

--- test/test_app/tests/jasmine/client/integration/Login.coffee
+++ test/test_app/tests/jasmine/client/integration/Login.coffee
@@ -73,3 +73,8 @@
           expect($('#become-houston-admin').length).toEqual(0)
           expect($('.form-heading').first().html()).toMatch(/You are not an Admin./)
           done()
+
+  it "logged in as admin? come see the page", ->
+      setup {logged_in: true, as_admin: true, other_admin: false}, ->
+        Houston._go "home"
+        expect($('.houston').html()).toMatch(/Meteor Admin Panel/)


Implement fadeout/in in control iframe; return deferred for completion handlers

--- app/js/jail_iframe/classes/control_iframe.coffee
+++ app/js/jail_iframe/classes/control_iframe.coffee
@@ -1,3 +1,5 @@
+control_visibility_transition_time = 300+1000/60 #keep in sync with scss
+
 class FactlinkJailRoot.ControlIframe
   constructor: ->
     @el = document.createElement('iframe')
@@ -21,12 +23,24 @@
     bodyEl.appendChild(contentNode)
     @resizeFrame()

+
   resizeFrame: ->
     @el.style.width = @doc.body.clientWidth  + 'px'
     @el.style.height = @doc.body.clientHeight + 'px'
+
+  fadeIn: ->
+    @$el.addClass 'factlink-control-visible'
+    FactlinkJailRoot.Timer control_visibility_transition_time
+
+  fadeOut: ->
+    @$el.removeClass 'factlink-control-visible'
+    FactlinkJailRoot.Timer control_visibility_transition_time
+
+  #feature:should we communicate visibility to the control contents?

   destroy: ->
     return unless @el
     @doc = null
     @el.parentElement?.removeChild(@el)
     @$el = @el = null
+


Render: Minor background fix.

Render: Minor background fix.

--- engine/render.coffee
+++ engine/render.coffee
@@ -708,6 +708,9 @@
     # Draws the scene background in a quad,
     # usually after opaque pass and before transparent pass
     draw_background: (scene, world2cam, cam2world, projection_matrix) ->
+        # workaround
+        # TODO: only needed where uniforms are not removed when unused
+        @bg_mesh.scene = scene
         @draw_mesh(@bg_mesh, cam2world, -1, scene.world_material,
             world2cam, projection_matrix)

@@ -877,7 +880,7 @@
         gl.depthMask true

         # PASS -1  (background)
-        if scene.bg_pass and scene.bg_pass.length
+        if scene.bg_pass.length
             for ob in scene.bg_pass
                 if ob.visible == true
                     @draw_mesh(ob, ob.world_matrix, 0)


First draft of dynamic graph.

First draft of dynamic graph.

--- picoCTF-web/web/coffee/progression_graphs.coffee
+++ picoCTF-web/web/coffee/progression_graphs.coffee
@@ -66,13 +66,14 @@

   return maxValues

-progressionDataToPoints = (data, bucketWindow, dataPoints) ->
+progressionDataToPoints = (data, dataPoints) ->

   sortedData = _.sortBy _.flatten(data), (submission) ->
     return submission.time

   min = _.first(sortedData).time
   max = _.last(sortedData).time
+  bucketWindow = Math.max(Math.floor((max - min) / dataPoints), 1)

   dataSets = []

@@ -95,7 +96,7 @@
     if data.data.length >= 2

       scoreData = (team.score_progression for team in data.data)
-      dataPoints = _.zip.apply _, progressionDataToPoints scoreData, 2, 360
+      dataPoints = _.zip.apply _, progressionDataToPoints scoreData, 360

       teamNameData = (team.name for team in data.data)

@@ -118,7 +119,7 @@
         ["Time", "Score", {role: "tooltip"}]
       ]

-      steps = progressionDataToPoints data.data, 2, 360
+      steps = progressionDataToPoints data.data, 360
       (graphData.push(["", score, score]) for score in steps)

       packagedData = google.visualization.arrayToDataTable graphData


Prevent errors when disable package oembed. Fixes #338

--- client/views/app/message.coffee
+++ client/views/app/message.coffee
@@ -54,7 +54,7 @@
 		ul = lastNode.parentElement
 		wrapper = ul.parentElement

-		if context.urls?.length > 0
+		if context.urls?.length > 0 and Template.oembedBaseWidget?
 			for item in context.urls
 				do (item) ->
 					urlNode = lastNode.querySelector('.body a[href="'+item.url+'"]')


fixed wrong ivar

fixed wrong ivar

--- plugins/help.coffee
+++ plugins/help.coffee
@@ -54,5 +54,5 @@
       secs = (new Date().getTime() - start_at) / 1000
       aday  = 86400
       ahour = 3600
-      [day,hour,minute,second] = [secs/ aday,secs%aday/ ahour,secs%ahour/ 60,sec%60].map (i)-> parseInt(i)
+      [day,hour,minute,second] = [secs/ aday,secs%aday/ ahour,secs%ahour/ 60,secs%60].map (i)-> parseInt(i)
       send "up #{day} days, #{hour}:#{minute}:#{second}"


decreased num of social workers

decreased num of social workers

--- config/main.kontrol-prod.coffee
+++ config/main.kontrol-prod.coffee
@@ -69,7 +69,7 @@
     watch       : yes
   social        :
     login       : 'prod-social'
-    numberOfWorkers: 20
+    numberOfWorkers: 4
     watch       : yes
     queueName   : socialQueueName
   cacheWorker   :


work around for synctex paths containing /./

--- services/web/app/coffee/Features/Compile/CompileController.coffee
+++ services/web/app/coffee/Features/Compile/CompileController.coffee
@@ -163,7 +163,15 @@
 	proxySyncCode: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		{file, line, column} = req.query
-		if not file? or Path.resolve("/", file) isnt "/#{file}"
+		if not file?
+			return next(new Error("missing file parameter"))
+		# Check that we are dealing with a simple file path (this is not
+		# strictly needed because synctex uses this parameter as a label
+		# to look up in the synctex output, and does not open the file
+		# itself).  Since we have valid synctex paths like foo/./bar we
+		# allow those by replacing /./ with /
+		testPath = file.replace '/./', '/'
+		if Path.resolve("/", testPath) isnt "/#{testPath}"
 			return next(new Error("invalid file parameter"))
 		if not line?.match(/^\d+$/)
 			return next(new Error("invalid line parameter"))


Fix bugs on error array

Fix bugs on error array

lab80/hellomoney#1892

--- error_logger_server.coffee
+++ error_logger_server.coffee
@@ -1,6 +1,7 @@
-REPETITIVE_ERRORS =
+REPETITIVE_ERRORS = [
   "Connection timeout. No sockjs heartbeat received."
   "TypeError: Cannot read property 'invalidate' of undefined"
+]
 LOG_ROUTE = '/errorlog'
 ERROR_FIELDS = ['errorType', 'ipAddress', 'location', 'browser', 'details']
 ERROR_TYPE_TO_SUBJECT_GETTER =


computecontroller: check for group stacks before reloading stacks

--- client/app/lib/providers/computecontroller.coffee
+++ client/app/lib/providers/computecontroller.coffee
@@ -1047,7 +1047,9 @@
       # TMS-1919: This can stay as is, but this time it will create the first
       # avaiable stacktemplate for who has no stacks yet. ~ GG

-      @createDefaultStack yes  if @stacks.length is 0
+      groupStacks = @stacks.filter (stack) -> stack.config?.groupStack
+
+      @createDefaultStack yes  if groupStacks.length is 0

       @checkGroupStackRevisions()


UserEnvironmentDataProvider: Make an helper method to get machine by a query.

--- client/app/lib/userenvironmentdataprovider.coffee
+++ client/app/lib/userenvironmentdataprovider.coffee
@@ -81,13 +81,13 @@
     callback machine, workspace

-  getMachineByLabel: (machineLabel, callback) ->
+  machineGetter_: (field, expectedValue, callback) ->

     m = null
     w = null

     for obj in @getAllMachines()
-      if obj.machine.label is machineLabel
+      if obj.machine[field] is expectedValue
         m = obj.machine
         w = obj.workspaces

@@ -95,6 +95,10 @@

     callback m, w

+
+  getMachineByLabel: (machineLabel, callback) ->
+
+    @machineGetter_ 'label', machineLabel, callback

   getMachineAndWorkspaceByChannelId: (channelId, callback) ->


Initialize window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewTracks.IndexView on music library index page ready.

--- app/assets/javascripts/voluntary_music_metadata_enrichment/library/index.js.coffee
+++ app/assets/javascripts/voluntary_music_metadata_enrichment/library/index.js.coffee
@@ -16,5 +16,5 @@

   new window.VoluntaryMusicMetadataEnrichment.Library.YearsInReview.IndexView(true)
   new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewReleases.IndexView()
-  new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewTracks.NewView()
+  new window.VoluntaryMusicMetadataEnrichment.Library.YearInReviewTracks.IndexView()
   new window.Likes.List()


adding elapsedTime

adding elapsedTime

--- src/game_object.coffee
+++ src/game_object.coffee
@@ -132,8 +132,8 @@
     ###
     update: (elapsedTime) ->
       if I.active
-        self.trigger('step')
-        self.trigger 'update', elapsedTim
+        self.trigger 'step', elapsedTime
+        self.trigger 'update', elapsedTime
         I.age += 1

       I.active


Don't know why but it may fail to register sometimes since there was no groupChannel

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -196,7 +196,7 @@
     # we need to close the group channel so we don't receive the cycleChannel event.
     # getting the cycleChannel even for our own MemberAdded can cause a race condition
     # that'll leak a guest account.
-    KD.getSingleton('groupsController').groupChannel.close()
+    KD.getSingleton('groupsController').groupChannel?.close()

     KD.remote.api.JUser.convert formData, (err, replacementToken)=>
       account = KD.whoami()


:gift: inser-at-start-of-subword-occurrence and it's end version

--- lib/operator-insert.coffee
+++ lib/operator-insert.coffee
@@ -250,6 +250,14 @@
   which: 'end'
   occurrence: true

+class InsertAtStartOfSubwordOccurrence extends InsertAtStartOfOccurrence
+  @extend()
+  occurrenceType: 'subword'
+
+class InsertAtEndOfSubwordOccurrence extends InsertAtEndOfOccurrence
+  @extend()
+  occurrenceType: 'subword'
+
 class InsertAtStartOfSmartWord extends InsertByTarget
   @extend()
   which: 'start'


Referrer System : resolved the race condition on referral system. while opening the main page, client id is not set yet sometimes, so we are not able to save referrer code to session. now removed all session related usages, saving to cookie

--- client/landing/app/MainApp/login/loginview.coffee
+++ client/landing/app/MainApp/login/loginview.coffee
@@ -189,6 +189,7 @@

   doRegister:(formData)->
     formData.agree = 'on'
+    formData.referrer = $.cookie 'referrer'
     @registerForm.notificationsDisabled = yes
     @registerForm.notification?.destroy()


reactivity: order of emojis while searching

--- client/activity/lib/flux/chatinput/getters.coffee
+++ client/activity/lib/flux/chatinput/getters.coffee
@@ -47,9 +47,14 @@
     return immutable.List()  unless query

     isBeginningMatch = query.length < 3
-    emojis.filter (emoji) ->
-      index = emoji.indexOf(query)
-      if isBeginningMatch then index is 0 else index > -1
+    emojis
+      .filter (emoji) ->
+        index = emoji.indexOf(query)
+        if isBeginningMatch then index is 0 else index > -1
+      .sort (emoji1, emoji2) ->
+        return -1  if emoji1.indexOf(query) is 0
+        return 1  if emoji2.indexOf(query) is 0
+        return 0
 ]


Smooth scroll; tune key-repeat code.

Smooth scroll; tune key-repeat code.

--- content_scripts/scroller.coffee
+++ content_scripts/scroller.coffee
@@ -2,7 +2,6 @@
 # activatedElement is different from document.activeElement -- the latter seems to be reserved mostly for
 # input elements. This mechanism allows us to decide whether to scroll a div or to scroll the whole document.
 #
-root = exports ? window
 activatedElement = null
 settings = null

@@ -84,12 +83,12 @@
   lastActivationId = -1
   keyupHandler = null

-  # When the keyboard is repeating and the key is released, sometimes the last key event is delivered *after*
-  # the keyup event.  This could cause indefinite scrolling with no key pressed, because no further keyup
-  # event is delivered (we behave as if the final key is still pressed).  To get around this, we briefly hold
-  # a lock to prevent a new smooth scrolling event from beginning too soon after the end of the previous
-  # scroll.  This is a hack.
-  keyLock = false
+  # When the keyboard is repeating and then the key is released, sometimes the last key event is delivered
+  # *after* the keyup event.  This could cause indefinite scrolling with no key pressed, because no further
+  # keyup event is delivered (so we behave as if the key is still pressed).  To get around this, we briefly
+  # hold a lock; this prevents a new smooth scrolling event from starting too soon after the end of the
+  # previous one.
+  smoothScrollLock = false

   (element, direction, amount, wantSmooth) ->
     axisName = scrollProperties[direction].axisName
@@ -97,24 +96,18 @@
     unless wantSmooth and settings.get "smoothScroll"
       return performScroll element, axisName, amount

-    # Assumption.  If animator A is started before animator B, then A will also finish before B.  We need to
-    # verify this - it may not always be true.
-
-    console.log("locked") if keyLock
-    if lastActivationId == time or keyLock
-      # This is a keyboard repeat, and the most-recently activated animator will handle it.
+    if lastActivationId == time or smoothScrollLock
+      # This is a keyboard repeat (and the most-recently activated animator -- which is still active -- will
+      # keep going), or it's too soon to begin smooth scrolling again.
       return

     if not keyupHandler
       keyupHandler = root.handlerStack.push keyup: ->
         time += 1
-        keyLock = true
-        setTimeout((-> keyLock = false), 10)
-        console.log "keyup", time, lastActivationId
-        true # Do not prevent propagation.
+        setTimeout((-> smoothScrollLock = false), 50)
+        smoothScrollLock = true # Hereby also returning true, so allowing further propagation of the event.

     lastActivationId = activationId = ++time
-    console.log activationId

     duration = 100 # Duration in ms.
     fudgeFactor = 25
@@ -131,9 +124,9 @@
     start = null
     scrolledAmount = 0

-    stopScrolling = (progress) ->
+    shouldStopScrolling = (progress) ->
       if activationId == time
-        # This is the most recent animator, and the key is still pressed.
+        # This is the most recent animator and the key is still pressed, so keep going.
         false
       else
         duration <= progress
@@ -145,10 +138,11 @@
       scrollDelta = roundOut(delta * progress) - scrolledAmount
       scrolledAmount += scrollDelta

-      if performScroll(element, axisName, scrollDelta, false) != scrollDelta or stopScrolling progress
+      if performScroll(element, axisName, scrollDelta, false) != scrollDelta or shouldStopScrolling progress
           # One final call of performScroll to check the visibility of the activated element.
           performScroll(element, axisName, 0, true)
           window.cancelAnimationFrame(animatorId)
+          # Advance time if this is the most-recently activated animator and time hasn't otherwise advanced.
           time += 1 if activationId == time
       else
         animatorId = window.requestAnimationFrame(animate)
@@ -186,4 +180,5 @@
     amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName]
     doScrollBy element, direction, amount, wantSmooth

+root = exports ? window
 root.Scroller = Scroller


Fix context menu showing up as empty gray square below HUD on initial level load

--- app/views/play/level/SurfaceContextMenuView.coffee
+++ app/views/play/level/SurfaceContextMenuView.coffee
@@ -12,16 +12,19 @@
   subscriptions:
     'level:surface-context-menu-pressed': 'showView'
     'level:surface-context-menu-hide': 'hideView'
-
+
   constructor: (options) ->
     @supermodel = options.supermodel # Has to go before super so events are hooked up
     super options
     @level = options.level
     @session = options.session
-

   destroy: ->
     super()
+
+  afterInsert: ->
+    super()
+    @hideView()

   afterRender: ->
     super()
@@ -37,8 +40,6 @@
       textArea.select()
       document.execCommand('copy')
       document.body.removeChild(textArea)
-
-

   setPosition: (e) ->
     @$el.css('left', e.posX)
@@ -56,4 +57,4 @@
     @$el.show()
     @setCoordinates(e)
     @setPosition(e)
-    @render()+    @render()


updated test case to use virtualProperty getter-only shortcut

--- test/tests/art/engine/core/element_base.coffee
+++ test/tests/art/engine/core/element_base.coffee
@@ -41,7 +41,7 @@
       location:
         preprocess: (l) -> point l
         setter: (l) -> @setElementToParentMatrix @getPendingElementToParentMatrix().withLocation l
-        getterNew: (pending) -> @getState(pending)._elementToParentMatrix.getLocation()
+        getterNew: -> @getState(pending)._elementToParentMatrix.getLocation()

     @getter
       redrawRequired: -> true


Hide "No keys" field when updating key list.

--- client/app/Applications/Account.kdapplication/account/accSettingsDevSshKeys.coffee
+++ client/app/Applications/Account.kdapplication/account/accSettingsDevSshKeys.coffee
@@ -9,6 +9,8 @@
       newKeys = @getItemsOrdered().map (item)-> item.getData()
       if newKeys.length is 0
         @addCustomItem "You have no SSH keys."
+      else
+        @customItem?.destroy()
       KD.remote.api.JUser.setSSHKeys newKeys, ->
         console.log "Saved keys."

@@ -21,6 +23,7 @@

   loadItems: ()->
     @removeAllItems()
+    @customItem?.destroy()
     @showLazyLoader no

     KD.remote.api.JUser.getSSHKeys (keys)=>


Add show/hide dot files menu option for vmitems

--- client/landing/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
+++ client/landing/app/MainApp/filetree/controllers/findercontextmenucontroller.coffee
@@ -231,6 +231,12 @@
       Collapse                    :
         action                    : 'collapse'
         separator                 : yes
+      'Hide dot Files'            :
+        action                    : 'hideDotFiles'
+        separator                 : yes
+      'Show dot Files'            :
+        action                    : 'showDotFiles'
+        separator                 : yes
       'New File'                  :
         action                    : 'createFile'
       'New Folder'                :
@@ -243,6 +249,12 @@
       delete items.Expand
     else
       delete items.Collapse
+
+    fc = KD.getSingleton 'finderController'
+    if fc.isNodesHiddenFor fileData.vmName
+      delete items['Hide dot Files']
+    else
+      delete items['Show dot Files']

     return items


removed lingering debug state

removed lingering debug state

the debug defeated the when-to-ping interval

--- community/browser/app/scripts/services/UDC.coffee
+++ community/browser/app/scripts/services/UDC.coffee
@@ -109,7 +109,7 @@
             today = new Date()
             today = new Date(today.getFullYear(), today.getMonth(), today.getDay())

-            if (pingTime < today) || true
+            if (pingTime < today)
               @set("pingTime", today.getTime())
               return true
             else


Fixed typo

Fixed typo

--- lib/assets/javascripts/turbolinks.js.coffee
+++ lib/assets/javascripts/turbolinks.js.coffee
@@ -35,7 +35,7 @@

   xhr?.abort()
   xhr = new XMLHttpRequest
-  xhr.open 'GET', removeHashForIE10compatiblity(url), true
+  xhr.open 'GET', removeHashForIE10compatibility(url), true
   xhr.setRequestHeader 'Accept', 'text/html, application/xhtml+xml, application/xml'
   xhr.setRequestHeader 'X-XHR-Referer', referer

@@ -141,7 +141,7 @@

 # Intention revealing function alias
-removeHashForIE10compatiblity = (url) ->
+removeHashForIE10compatibility = (url) ->
   removeHash url

 removeHash = (url) ->


payment: token: add signature annotations

--- workers/social/lib/social/models/payment/token.coffee
+++ workers/social/lib/social/models/payment/token.coffee
@@ -2,7 +2,7 @@

 module.exports = class JPaymentToken extends Module

-  {secure}    = require 'bongo'
+  {secure, signature}    = require 'bongo'
   crypto      = require 'crypto'

   Emailer     = require '../../emailer'
@@ -12,7 +12,11 @@

   @set
     sharedMethods :
-      static      : ['checkToken', 'createToken']
+      static      :
+        checkToken:
+          (signature Object, Function)
+        createToken:
+          (signature Object, Function)
     schema        :
       userCode    : String
       planCode    : String


Display ajax error if response is not json.

--- core/htdocs_source/src/pods/openxpki/route.coffee
+++ core/htdocs_source/src/pods/openxpki/route.coffee
@@ -51,6 +51,7 @@
             source

     sendAjax: (req) ->
+        req.dataType = "json"
         req.type ?= if req?.data?.action then "POST" else "GET"
         req.url ?= @controllerFor("config").get "url"
         req.data._ = new Date().getTime()
@@ -66,52 +67,59 @@
             else
                 target = "top"

-        Em.$.ajax(req).then (doc) =>
-            source.beginPropertyChanges()
+        new Em.RSVP.Promise (resolve, reject) =>
+            Em.$.ajax(req).then (doc) =>
+                source.beginPropertyChanges()

-            source.set "status", doc.status
-            source.set "modal", null
+                source.set "status", doc.status
+                source.set "modal", null

-            if doc.goto
-                if doc.target == '_blank' || /^(http|\/)/.test doc.goto
-                    window.location.href = doc.goto
+                if doc.goto
+                    if doc.target == '_blank' || /^(http|\/)/.test doc.goto
+                        window.location.href = doc.goto
+                    else
+                        @transitionTo "openxpki", doc.goto
+
+                else if doc.structure
+                    source.set "navEntries", doc.structure
+                    source.set "user", doc.user
+
                 else
-                    @transitionTo "openxpki", doc.goto
+                    if doc.page and doc.main
+                        tab =
+                            active: true
+                            page: doc.page
+                            main: doc.main
+                            right: doc.right

-            else if doc.structure
-                source.set "navEntries", doc.structure
-                source.set "user", doc.user
+                        if target is "modal"
+                            source.set "modal", tab
+                        else if target is "tab"
+                            tabs = source.get "tabs"
+                            tabs.setEach "active", false
+                            source.get("tabs").pushObject tab
+                        else if target is "active"
+                            tabs = source.get "tabs"
+                            index = tabs.indexOf tabs.findBy "active"
+                            tabs.replace index, 1, [tab]
+                        else # top
+                            source.get("tabs")
+                                .clear()
+                                .pushObject tab

-            else
-                if doc.page and doc.main
-                    tab =
-                        active: true
-                        page: doc.page
-                        main: doc.main
-                        right: doc.right
+                    Em.run.scheduleOnce "afterRender", ->
+                        $(".loading").removeClass "in-progress"

-                    if target is "modal"
-                        source.set "modal", tab
-                    else if target is "tab"
-                        tabs = source.get "tabs"
-                        tabs.setEach "active", false
-                        source.get("tabs").pushObject tab
-                    else if target is "active"
-                        tabs = source.get "tabs"
-                        index = tabs.indexOf tabs.findBy "active"
-                        tabs.replace index, 1, [tab]
-                    else # top
-                        source.get("tabs")
-                            .clear()
-                            .pushObject tab
+                source.endPropertyChanges()
+                resolve doc
+            , (err) ->
+                $(".loading").removeClass "in-progress"
+                console.log "Ajax error", err
+                source.set "status",
+                    level: "error"
+                    message: "Server did not return JSON data as expected.
+                        Authentication session expired?"
+                resolve {}

-                Em.run.scheduleOnce "afterRender", ->
-                    $(".loading").removeClass "in-progress"
-
-            source.endPropertyChanges()
-            doc
-        , (err) ->
-            $(".loading").removeClass "in-progress"
-            console.log "Ajax error", err

 `export default Route`


Add /SEEN URLs used in magazine ad

Add /SEEN URLs used in magazine ad

--- app/core/Router.coffee
+++ app/core/Router.coffee
@@ -131,6 +131,8 @@
     'privacy': go('PrivacyView')

     'schools': go('NewHomeView')
+    'seen': go('NewHomeView')
+    'SEEN': go('NewHomeView')

     'teachers': redirect('/teachers/classes')
     'teachers/classes': go('courses/TeacherClassesView') #, { teachersOnly: true })


Compact component syntax

Compact component syntax

I'm evaluating the possibility to save a couple of lines and one level of
indentation by aligning HTML tags with their parentheses.

--- app/gem/component.cjsx
+++ app/gem/component.cjsx
@@ -12,11 +12,9 @@

   render: ->
     statusStr = if @state?.status then 'activated' else 'deactivated'
-    (
-

-        Gem is {statusStr}
-        <button onClick={@onClick}>toggle</button>
-

-    )
+    (

+      Gem is {statusStr}
+      <button onClick={@onClick}>toggle</button>
+
)

 module.exports = Gem


Fix to/from/max numbers in pagination

Fix to/from/max numbers in pagination

--- backbone.tableview.coffee
+++ backbone.tableview.coffee
@@ -221,9 +221,10 @@

     # Refresh the pagination div at the bottom
     refreshPagination: =>
-        from = (@data.page - 1) * @data.size + 1
-        to   = from + @collection.size() - 1
-        max  = @collection.count or -1
+        from = (@data.page - 1) * @data.size
+        to   = from + @collection.size()
+        if @collection.size() > 0 then from++
+        max  = if @collection.count? then @collection.count() else -1
         if max < 0
             maxPage  = 1
             pageFrom = @data.page