code refactoring

code refactoring

--- client/app/lib/util/showSaveDialog.coffee
+++ client/app/lib/util/showSaveDialog.coffee
@@ -4,7 +4,7 @@
 KDInputView = kd.InputView
 KDLabelView = kd.LabelView
 KDView = kd.View
-IDEFinderItem = require '../../../ide/lib/finder/idefinderitem'
+IDEFinderItem = require 'ide/finder/idefinderitem'

 module.exports = (container, callback = kd.noop, options = {}) ->


Proxifier: use base domain instead

Proxifier: use base domain instead

--- client/app/lib/util/proxifier.coffee
+++ client/app/lib/util/proxifier.coffee
@@ -14,7 +14,8 @@
     parser.href = url

     # if url is already proxyfied return it as is
-    return callback url  if /p\.koding\.com/.test url
+    baseDomain = "#{globals.config.domains.base}".replace '.', '\\.'
+    return callback url  if ///p\.#{baseDomain}///.test url
     return callback url  if parser.hostname in ['127.0.0.1', 'dev.kodi.ng']

     # check if running under production environment


Leave this scope alone so it refers to the el

--- client/lanes/screens/Instance.coffee
+++ client/lanes/screens/Instance.coffee
@@ -22,11 +22,11 @@
         this.root.tooltip({
             viewport: '.lanes'
             selector: '[data-tooltip-message]'
-            title: => @getAttribute('data-tooltip-message')
+            title: -> this.getAttribute('data-tooltip-message')
         });
         Lanes.Views.Keys.initialize();
         Lanes.Models.PubSub.initialize() if options.pub_sub;
-
+
         view = Lanes.getPath(options.root_view);
         if view
             this.displayInitialView(view);
@@ -45,8 +45,5 @@
     displayInitialView:(view)->
         this.view = new view({parent: this, model: this.viewport}).render();
         this.viewport.el = this.view.$el;
-
         this.root.append( this.view.el );
         Lanes.Extensions.fireOnAvailable(this);
-
-


debounce the location omnibox

debounce the location omnibox

--- client/controllers/locationSelect2.coffee
+++ client/controllers/locationSelect2.coffee
@@ -48,19 +48,19 @@
     multiple: @data.multiple
     placeholder: "Search for a location..."
     minimumInputLength: 1
-
   $.fn.select2.amd.define('select2/data/queryAdapter',
     [ 'select2/data/array', 'select2/utils' ],
     (ArrayAdapter, Utils) =>
       CustomDataAdapter = ($element, options) ->
         CustomDataAdapter.__super__.constructor.call(@, $element, options)
       Utils.Extend(CustomDataAdapter, ArrayAdapter)
-      CustomDataAdapter.prototype.query = (params, callback) =>
+      CustomDataAdapter.prototype.query = _.debounce (params, callback) =>
         term = params.term?.trim()
         if term # Query the remote server for any matching locations
           @ajax(term, callback)
         else # Show recently used locations for the current event
           @suggestLocations(term, callback)
+      , 600
       CustomDataAdapter
   )
   queryDataAdapter = $.fn.select2.amd.require('select2/data/queryAdapter')


remove existing records when setting an association

--- client/lanes/models/AssociationMap.coffee
+++ client/lanes/models/AssociationMap.coffee
@@ -143,6 +143,7 @@
                 if options?.silent isnt true and not association.isProxy
                     model.trigger("change:#{name}", value, {})
             else
+                options = _.extend({}, options, {remove: true})
                 if value then association[fn_name]( value, options ) else association.clear()

     pk: (name) ->


A href to demo attachments

A href to demo attachments

--- assets/documents/basic.coffee
+++ assets/documents/basic.coffee
@@ -54,6 +54,7 @@
           "attachment": {
             "contentType": "image/png",
             "url": "basecamp.png",
+            "href": "basecamp.png",
             "filename": "basecamp.png",
             "identifier": "Attachment:1"
           }
@@ -103,6 +104,7 @@
             "filename": "Basecamp.pdf",
             "identifier": "dcd7027677de68739253b9114f6973b7",
             "url": "/attachments/dcd7027677de68739253b9114f6973b7",
+            "href": "/attachments/dcd7027677de68739253b9114f6973b7",
             "filesize": 1992294
           }
         }


Use record.status as default sort type

Use record.status as default sort type

--- tutor/src/components/scores/student-data-sorter.coffee
+++ tutor/src/components/scores/student-data-sorter.coffee
@@ -39,8 +39,6 @@
             else
               record.completed_on_time_exercise_count
           percent(progress, record.exercise_count)
-    when 'external' # events are also of type 'external' and require no special handling
-      record.status
     when 'concept_coach'
       switch dataType
         when 'score'
@@ -52,6 +50,8 @@
         when 'completed'
           progress = record.completed_exercise_count
           percent(progress, record.exercise_count)
+    else # default to using record status for external and other type of events
+      record.status


Make sure removed all nodes completely

Make sure removed all nodes completely

--- components/tree/treeviewcontroller.coffee
+++ components/tree/treeviewcontroller.coffee
@@ -248,9 +248,17 @@

   removeAllNodes:->

-    if @listControllers["0"]
-      @listControllers["0"].itemsOrdered.forEach (itemView)=>
-        @removeNodeView itemView
+    for id, listController of @listControllers
+      listController.itemsOrdered.forEach @bound 'removeNodeView'
+      listController?.getView().destroy()
+      delete @listControllers[id]
+      delete @listData[id]
+
+    @nodes           = {}
+    @listData        = {}
+    @indexedNodes    = []
+    @selectedNodes   = []
+    @listControllers = {}

   removeChildNodes:(id)->


groups: wip image preview for avatar.

groups: wip image preview for avatar.

--- components/upload/KDFileUploadView.coffee
+++ components/upload/KDFileUploadView.coffee
@@ -66,6 +66,7 @@
     reader = new FileReader()
     reader.onload = (event)=>
       @propagateEvent KDEventType : "FileReadComplete", {progressEvent : event, file : file}
+      @emit "FileReadComplete", {progressEvent : event, file : file}

     reader.readAsDataURL file


rename $.dummyXPathEvaluate -> $.xpath

rename $.dummyXPathEvaluate -> $.xpath

--- src/range.coffee
+++ src/range.coffee
@@ -52,10 +52,10 @@
       # This does not necessarily make any sense, but this what we see
       # happening.
       if exception?.code is 52
-        console.log "XPath evaluation failed with code 52. Trying manual..."
-        # We have a really simple and narrow-minded, but very robust
-        # 'evaluation engine' prepared for these cases.
-        $.dummyXPathEvaluate xp, root
+        console.log "XPath evaluation failed with code 52. Trying fallback..."
+        # We have a an 'evaluator' for the really simple expressions that
+        # should work for the simple expressions we generate.
+        $.xpath xp, root
       else
         throw exception


fix ack/nack methods

fix ack/nack methods

--- src/stomp.coffee
+++ src/stomp.coffee
@@ -324,23 +324,25 @@
   #
   # * `messageID` & `subscription` are MANDATORY.
   # * `transaction` is OPTIONAL.
-  nack: (messageID, subscription, transaction = null) ->
-    @_transmit "ACK", {
+  ack: (messageID, subscription, transaction = null) ->
+    headers = {
       "message-id": messageID
-      subscription: subscription
-      transaction: transaction if transaction
-    }
+      subscription: subscription
+    }
+    headers.transaction = transaction if transaction
+    @_transmit "ACK", headers

   # [NACK Frame](http://stomp.github.com/stomp-specification-1.1.html#NACK)
   #
   # * `messageID` & `subscription` are MANDATORY.
   # * `transaction` is OPTIONAL.
   nack: (messageID, subscription, transaction = null) ->
-    @_transmit "NACK", {
+    headers = {
       "message-id": messageID
-      subscription: subscription
-      transaction: transaction if transaction
-    }
+      subscription: subscription
+    }
+    headers.transaction = transaction if transaction
+    @_transmit "NACK", headers

 Stomp.Frame = Frame
 if window?


use the correct api var

use the correct api var

--- components/main_layout/client.coffee
+++ components/main_layout/client.coffee
@@ -18,7 +18,7 @@
   # Log out of Force if you're not logged in to Gravity
   if sd.CURRENT_USER
     $.ajax
-      url: "#{sd.ARTSY_URL}/api/v1/me"
+      url: "#{sd.API_URL}/api/v1/me"
       error: -> window.location = '/users/sign_out'

 setupAnalytics = ->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/tween.coffee
+++ src/tween.coffee
@@ -19,7 +19,7 @@

       else
         f = Easing[data.easing](data.start, data.end)
-        t = (I.age - data.startTime)
+        t = (I.age - data.startTime) /
         I[property] = f()

   tween: (duration, properties) ->


Rename _eh event handlers to _on

Rename _eh event handlers to _on

Signed-off-by: Michael Diolosa <1fe9c163c3612846d8518a0042166a6cea333eed@gmail.com>

--- src/views.coffee
+++ src/views.coffee
@@ -28,10 +28,10 @@
     selected: null

-  _ehkeydown: (event) => @_keydown(event)
-  _ehkeyup: (event) => @_keyup(event)
-  _ehblur: (event) => @_blur(event)
-  _ehfocus: (event) => @_focus(event)
+  _onkeydown: (event) => @_keydown(event)
+  _onkeyup: (event) => @_keyup(event)
+  _onblur: (event) => @_blur(event)
+  _onfocus: (event) => @_focus(event)

   ### Initializes the object ###
   initialize: ->
@@ -55,22 +55,22 @@
     @_controller.is_enabled()

   _enabled: ->
-    @el.bind (if $.browser.opera then 'keypress' else 'keydown'), @_ehkeydown
+    @el.bind (if $.browser.opera then 'keypress' else 'keydown'), @_onkeydown
     @el.bind
-      keyup: @_ehkeyup
-      blur: @_ehblur
-      focus: @_ehfocus
+      keyup: @_onkeyup
+      blur: @_onblur
+      focus: @_onfocus

     @_specified?.enabled?()

   _disabled: ->
     @el.blur()

-    @el.unbind (if $.browser.opera then 'keypress' else 'keydown'), @_ehkeydown
+    @el.unbind (if $.browser.opera then 'keypress' else 'keydown'), @_onkeydown
     @el.unbind
-      keyup: @_ehkeyup
-      blur: @_ehblur
-      focus: @_ehfocus
+      keyup: @_onkeyup
+      blur: @_onblur
+      focus: @_onfocus

     @_specified?.disabled?()


unused

unused

--- resources/assets/coffee/_classes/account-edit.coffee
+++ resources/assets/coffee/_classes/account-edit.coffee
@@ -67,7 +67,6 @@
     data = {}

     for checkbox in form.querySelectorAll('.js-account-edit__input')
-      field = checkbox.name
       data[checkbox.name] = checkbox.checked

     data


Remove extra newline

Remove extra newline

--- resources/assets/coffee/react/contest/voting/voter.coffee
+++ resources/assets/coffee/react/contest/voting/voter.coffee
@@ -94,5 +94,4 @@
           props.onClick = @handleClick
           icon = i className: 'fas fa-fw fa-star'

-
     component props, icon


Needed to keep the click event for the hide button from bubbling up to the row component's onClick event handler.

--- src/components/student-dashboard/event-row.cjsx
+++ src/components/student-dashboard/event-row.cjsx
@@ -25,7 +25,8 @@
       # url is 1 based so it matches the breadcrumb button numbers. 1==first step
       {courseId:@props.courseId, id: @props.event.id, stepIndex: 1}

-  hideTask: ->
+  hideTask: (event) ->
+    event.stopPropagation()
     StudentDashboardActions.hide(@props.event.id)
     StudentDashboardStore.on('hidden', @hidden)


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/text.coffee
+++ test/text.coffee
@@ -18,7 +18,7 @@
 module "FloatingTextEffect"

 test "text fades out", ->
-  text = FloatingT()
+  text = FloatingTextEffect()

   text.update(1)
   text.trigger "afterUpdate", 1


Only sortable if user can edit the page

Only sortable if user can edit the page

--- resources/assets/coffee/react/profile-page/extra.coffee
+++ resources/assets/coffee/react/profile-page/extra.coffee
@@ -42,6 +42,7 @@

     $(@refs.tabs).sortable
       cursor: 'move'
+      disabled: !@props.withEdit
       revert: 150
       scrollSpeed: 0
       update: =>


added togglePressed method to ImageButton, to set selected state for buttons

--- Framework/Views/Controls/ImageButton_Framework.coffee
+++ Framework/Views/Controls/ImageButton_Framework.coffee
@@ -6,18 +6,21 @@
       backgroundColor: "transparent"
     }, options)

+    @bg = ""
+    @bgPressed = ""
+
     if options.type == "back"
       @view = @createBackButton(options)
     else
       @view = @createButton(options)

   createBackButton: (settings) =>
-    bg = "/Common/Framework/Images/iOS/TitleBar/Buttons/back.png"
-    bgPressed = "/Common/Framework/Images/iOS/TitleBar/Buttons/backPressed.png"
+    @bg = "/Common/Framework/Images/iOS/TitleBar/Buttons/back.png"
+    @bgPressed = "/Common/Framework/Images/iOS/TitleBar/Buttons/backPressed.png"

-    button = Ti.UI.createLabel({
+    @button = Ti.UI.createLabel({
       backgroundColor: settings.backgroundColor
-      backgroundImage: bg
+      backgroundImage: @bg
       backgroundLeftCap: 15
       backgroundRightCap: 10
       height: 30
@@ -30,8 +33,8 @@
     })

     if settings.iconSettings?
-      icon = Ti.UI.createImageView(settings.iconSettings)
-      button.add(icon)
+      @icon = Ti.UI.createImageView(settings.iconSettings)
+      @button.add(@icon)

     if settings.text?
       @label = Ti.UI.createLabel({
@@ -44,29 +47,29 @@
         width: Ti.UI.SIZE
         height: 28
       })
-      button.add(@label)
+      @button.add(@label)

-    button.addEventListener("touchstart", () => button.backgroundImage = bgPressed)
-    button.addEventListener("touchend", () =>
+    @button.addEventListener("touchstart", () => @button.backgroundImage = @bgPressed)
+    @button.addEventListener("touchend", () =>
       settings.onClick()
-      button.backgroundImage = bg
+      @button.backgroundImage = @bg if !@button.isPressed
     )
-    button
+    @button

   createButton: (settings) =>
     if settings.bg
-      bg = settings.bg
+      @bg = settings.bg
     else
-      bg = "/Common/Framework/Images/iOS/TitleBar/Buttons/button.png"
+      @bg = "/Common/Framework/Images/iOS/TitleBar/Buttons/button.png"

     if settings.bgPressed
-      bgPressed = settings.bgPressed
+      @bgPressed = settings.bgPressed
     else
-      bgPressed = "/Common/Framework/Images/iOS/TitleBar/Buttons/buttonPressed.png"
+      @bgPressed = "/Common/Framework/Images/iOS/TitleBar/Buttons/buttonPressed.png"

-    button = Ti.UI.createLabel({
+    @button = Ti.UI.createLabel({
       backgroundColor: settings.backgroundColor
-      backgroundImage: bg
+      backgroundImage: @bg
       backgroundLeftCap: 10
       backgroundRightCap: 10
       height: 30
@@ -80,7 +83,7 @@

     if settings.iconSettings?
       @icon = Ti.UI.createImageView(settings.iconSettings)
-      button.add(@icon)
+      @button.add(@icon)

     if settings.text?
       @label = Ti.UI.createLabel({
@@ -93,18 +96,26 @@
         width: Ti.UI.SIZE
         height: 28
       })
-      button.add(@label)
+      @button.add(@label)

-    button.addEventListener("touchstart", () => button.backgroundImage = bgPressed)
-    button.addEventListener("touchend", () =>
+    @button.addEventListener("touchstart", () => @button.backgroundImage = @bgPressed)
+    @button.addEventListener("touchend", () =>
       settings.onClick()
-      button.backgroundImage = bg
+      @button.backgroundImage = @bg if !@button.isPressed
     )
-    button
+    @button

   setTitle: (title) =>
     @label.text = title

   setIcon: (image) =>
-    @icon.setImage(image)+    @icon.setImage(image)
+
+  togglePressed: =>
+    if @button.isPressed
+      @button.isPressed = false
+      @button.backgroundImage = @bg
+    else
+      @button.isPressed = true
+      @button.backgroundImage = @bgPressed


Terminal: fail and start events added

Terminal: fail and start events added

--- client/Terminal/AppView.coffee
+++ client/Terminal/AppView.coffee
@@ -110,7 +110,7 @@
       , =>
         KD.mixpanel "Open Webterm, fail", {vmName}
         KD.logToExternalWithTime "oskite: Can't open Webterm", vmName
-
+        @emit 'TerminalFailed'
         @emit 'message', """

Couldn't connect to your VM.

@@ -252,13 +252,16 @@
     , =>
       terminalView.connectToTerminal()
       @messagePane.hide()
+      @emit 'TerminalStarted'
     , =>
       KD.utils.defer =>
         @addNewTab vmName
         @messagePane.hide()
+        @emit 'TerminalStarted'
     , =>
       KD.mixpanel "Open Webterm, fail", {vmName}
       KD.logToExternalWithTime "oskite: Can't open Webterm", vmName
+      @emit 'TerminalFailed'
       @setMessage """

Couldn't connect to your VM.


Use thin arrow instead of fat arrow

Use thin arrow instead of fat arrow

--- client/finder/lib/index.coffee
+++ client/finder/lib/index.coffee
@@ -63,7 +63,7 @@
       hoverDetect : no
       delegate    : controller

-    onDrag = (event) =>
+    onDrag = (event) ->

       { internalDragging }  = controller.treeController


avoid duplicate filling of UserInfo in getDocUpdates

--- services/track-changes/app/coffee/UpdatesManager.coffee
+++ services/track-changes/app/coffee/UpdatesManager.coffee
@@ -119,9 +119,7 @@
 			#console.log "options", options
 			PackManager.getOpsByVersionRange project_id, doc_id, options.from, options.to, (error, updates) ->
 				return callback(error) if error?
-				UpdatesManager.fillUserInfo updates, (err, results) ->
-					return callback(err) if err?
-					callback null, results
+				callback null, updates

 	getDocUpdatesWithUserInfo: (project_id, doc_id, options = {}, callback = (error, updates) ->) ->
 		UpdatesManager.getDocUpdates project_id, doc_id, options, (error, updates) ->


fix readable class names and add role button doesnt reqire loader, because there is nothing for waiting

--- client/landing/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
@@ -11,15 +11,13 @@

     roles = (role.title for role in options.roles when role.title isnt 'owner')

+    addRoleDialog = null
     options.buttons or=
       "Add Role"          :
         style             : "modal-clean-gray"
         cssClass          : 'add-role'
-        loader            :
-          color           : "#444444"
-          diameter        : 12
         callback          : =>
-
+          addRoleDialog?.destroy()
           KD.getSingleton('contentPanel').addSubView addRoleDialog = new KDDialogView
             cssClass      : "add-role-dialog"
             duration      : 200
@@ -124,10 +122,14 @@
   readableText = (text)->
     dictionary =
       "JTag"        : "Tags"
-      "JGroup"      : 'Groups'
-      "JPost"       :'Posts'
-      "JVocabulary" : 'Vocabularies'
+      "JGroup"      : "Groups"
+      "JPost"       : "Posts"
+      "JVocabulary" : "Vocabularies"
       "JVM"         : "Compute"
+      "CActivity"   : "Activity"
+      "JGroupBundle": "Group Bundles"
+      "JDomain"     : "Domains"
+      "JProxyFilter": "Proxy Filters"
     return dictionary[text] or text.charAt(0).toUpperCase()+text.slice(1)

   _getCheckboxName =(module, permission, role)->


Remove stray console.log().

Remove stray console.log().

--- services/web/public/coffee/ide/directives/layout.coffee
+++ services/web/public/coffee/ide/directives/layout.coffee
@@ -123,7 +123,6 @@
 						if state[customTogglerPane]?.initClosed == true
 							customTogglerScope.isOpen = false

-						console.log customTogglerScope.isOpen
 						customTogglerScope.tooltipMsgWhenOpen = customTogglerMsgWhenOpen
 						customTogglerScope.tooltipMsgWhenClosed = customTogglerMsgWhenClosed


let the user know whether the permissions are updated or not

--- client/landing/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
+++ client/landing/app/Applications/Groups.kdapplication/Views/permissionsmodal.coffee
@@ -114,6 +114,12 @@
             log 'updated permissions',err,res
             @buttons["Save"].hideLoader()
             # TODO: do something with this callback
+            unless err
+              new KDNotificationView
+                title : "Group permissions have been updated."
+            else
+              new KDNotificationView # should change to KD.showError when merged
+                title : "An error occured while updating permissions."

     options.fields or= optionizePermissions roles, permissionSet
     super options,data


removed load defaults if undefined from game Manager

--- web/scripts/gameManager.coffee
+++ web/scripts/gameManager.coffee
@@ -6,7 +6,6 @@
 class window.GameManager
     constructor: (@environment) ->
         @config = @environment.description
-        @loadDefaultsIfUndefined()

         @editorDiv = 'codeEditor'
         @visualDiv = 'gameVisual'
@@ -88,66 +87,6 @@
         @interpreter.scanText @codeEditor.getStudentCode()
         @startGame()
         @interpreter.executeCommands @commandMap
-        return
-
-    loadDefaultsIfUndefined: ->
-        if not @config.editor?
-            @config.editor = {}
-        if not @config.code?
-            @config.code = {}
-
-        if not @config.editor.buttons?
-            @config.editor.buttons = ['switchUp', 'switchDown',
-                'deleteLine', 'insertButtons']
-        if not @config.editor.commands?
-            @config.editor.commands = {
-                    go: {
-                        inputs: 1,
-                        maxUses: 3
-                    },
-                    turnRight: {
-                        inputs: 0,
-                        maxUses: 2
-                    },
-                    turn: {
-                        inputs: 2,
-                        maxUses: 3
-                    }
-                }
-
-        if not @config.code.prefix?
-            @config.code.prefix = """
-                public class Student {
-                public static void main(String[] args) {\n
-                """
-        if not @config.code.initial?
-            @config.code.initial = """
-                go(15);
-                turnRight();
-                turn(__, __);
-                go(2);
-                """
-        if not @config.code.postfix?
-            @config.code.postfix = '}\n}'
-
-        if not @environment.visualMaster?
-            @environment.visualMaster = {}
-
-        if not @environment.visualMaster.container?
-            @environment.visualMaster.container = {
-                width: 360,
-                height: 360
-            }
-
-        if not @environment.visualMaster.preLoading?
-            @environment.visualMaster.preLoading = {
-                protagonist: [
-                    "img/wmn1_bk1.gif","img/wmn1_bk2.gif",
-                    "img/wmn1_rt1.gif","img/wmn1_rt2.gif",
-                    "img/wmn1_fr1.gif","img/wmn1_fr2.gif",
-                    "img/wmn1_lf1.gif","img/wmn1_lf2.gif"
-                ]
-            }
         return

 class MapGameState


release

release

--- webpack/scripts/release.coffee
+++ webpack/scripts/release.coffee
@@ -15,11 +15,12 @@
 if process.env.CIRCLE_SHA1
   process.env.CIRCLE_COMMIT_MSG = require('child_process').execSync("git log --format=%B -n 1 #{process.env.CIRCLE_SHA1}").toString().trim()

-  if process.env.CIRCLE_COMMIT_MSG.match(/^[0-9]+(\.[0-9]+)+$/) && (process.env.CIRCLE_COMMIT_MSG != pkg.version || process.env.CIRCLE_BRANCH != 'master')
-    console.log("Suggested release #{process.env.CIRCLE_BRANCH}.#{process.env.CIRCLE_COMMIT_MSG} is not master.#{pkg.version}, skipping release")
-    process.exit(1)
-
-  process.env.CIRCLE_RELEASE = pkg.version
+  if process.env.CIRCLE_COMMIT_MSG.match(/^[0-9]+(\.[0-9]+)+$/)
+    if process.env.CIRCLE_COMMIT_MSG == pkg.version && process.env.CIRCLE_BRANCH == 'master'
+      process.env.CIRCLE_RELEASE = pkg.version
+    else
+      console.log("Suggested release #{process.env.CIRCLE_BRANCH}.#{process.env.CIRCLE_COMMIT_MSG} is not master.#{pkg.version}, skipping release")
+      process.exit(1)

 if process.env.CIRCLE_BRANCH.startsWith('@')
   console.log("Not releasing #{process.env.CIRCLE_BRANCH}")


display filename to be uploaded

display filename to be uploaded

--- src/views/themes/default/coffee/global/forms.coffee
+++ src/views/themes/default/coffee/global/forms.coffee
@@ -181,7 +181,8 @@
   $('input.file').on(
     'change'
     (event) ->
-      $this = $(this)
+      filename = if this.files then this.files[0].name || this.files.item(0).fileName else this.value.replace(/^.*(\\|\/|\:)/, '')
+      document.getElementById('label-' + this.id).innerHTML = filename
     #return
   )
   ### workaround browsers that have two-tab focus on file input ###


Select tests use jsdom #99

Select tests use jsdom #99

--- wzk/ui/form/Select_test.coffee
+++ wzk/ui/form/Select_test.coffee
@@ -2,14 +2,22 @@

   select = null
   opts = null
+  doc = null
+  dom = null

   setup ->
+    doc = jsdom """
+    <html><head></head>
+    <body>
+    </body>
+    </html>
+    """
+    dom = new wzk.dom.Dom doc
     opts =
       bar1: 'foo1'
       bar2: 'foo2'
       3: '3a'
-    select = new wzk.ui.form.Select options: opts
-
+    select = new wzk.ui.form.Select dom: dom, options: opts

   test 'Should set value as a string', ->
     select.setValue 3
@@ -20,12 +28,12 @@
     assert.isTrue selected.length > 0

   test 'Should select first option after setting new options', ->
-    select = new wzk.ui.form.Select()
+    select = new wzk.ui.form.Select dom: dom
     select.setOptions foo: 'bar'
     assert.equal select.getValue(), 'foo'

   test 'Should select an option with a current value', ->
-    select = new wzk.ui.form.Select value: 2
+    select = new wzk.ui.form.Select value: 2, dom: dom
     select.setOptions
       "1": 1
       "2": 2


Example scene works, but there are some problems on the LoD system. The armature doesn't work with LoD.

--- engine/armature.coffee
+++ engine/armature.coffee
@@ -45,7 +45,7 @@

 class Armature extends GameObject

-    type = 'ARMATURE'
+    type : 'ARMATURE'

     constructor: ->
         super()


[IMPAC-856] Improve formatting of chart xaxis dates

--- src/services/highcharts-factory/highcharts-factory.svc.coffee
+++ src/services/highcharts-factory/highcharts-factory.svc.coffee
@@ -52,7 +52,9 @@
       xAxis:
         labels:
           formatter: ->
-            moment.utc(this.value).format('Do MMM YYYY')
+            e = this.axis.getExtremes()
+            f = if moment.utc(e.min).isSame(e.max, 'month') then 'ddd Do' else 'Do MMM'
+            moment.utc(this.value).format(f)
       yAxis:
         labels:
           formatter: ->


refined connect/disconnect logic; added maxReconnectAttempts option to broker; also exposed other reconnection backoff-related options: maxDelayMs, initialDelayMs, multiplyFactor.  use a function statement for Broker for better stack traces. bongo forwards connect/disconnect events, disconnect works, onconnect and ondisconnect callbacks.

--- core/KDWindowController.coffee
+++ core/KDWindowController.coffee
@@ -86,12 +86,12 @@
       @redirectMouseMoveEvent e if @dragView
     , yes

-    # unless window.location.hostname is 'localhost'
-    #   window.onbeforeunload = (event) =>
-    #     event or= window.event
-    #     msg = "Please make sure that you saved all your work."
-    #     event.returnValue = msg if event # For IE and Firefox prior to version 4
-    #     return msg
+    unless window.location.hostname is 'localhost'
+      window.onbeforeunload = (event) =>
+        event or= window.event
+        msg = "Please make sure that you saved all your work."
+        event.returnValue = msg if event # For IE and Firefox prior to version 4
+        return msg

   setDragInAction:(action = no)->


Update app/coffee/Router.coffee

Update app/coffee/Router.coffee

Co-Authored-By: Jakob Ackermann <caa3e4f01d56b797520cbfed54bdab4c5e3b0ebb@outlook.com>

--- services/real-time/app/coffee/Router.coffee
+++ services/real-time/app/coffee/Router.coffee
@@ -43,7 +43,7 @@

 		session.on 'connection', (error, client, session) ->
 			client?.on "error", (err) ->
-				logger.err "socket.io client error", { err }
+				logger.err { err }, "socket.io client error"
 				if client.connected
 					client.emit("reconnectGracefully")
 					client.disconnect()


change sample source

change sample source

--- src/sample.coffee
+++ src/sample.coffee
@@ -1,4 +1,3 @@
-# motionObj, kind, x, y, xs, ys, g, image, chara_w, chara_h, opacity, animlist, anime, visible
 class sample extends _stationary
     #**************************
     # character constructor


Handle bullet collision & player health/respawn

--- src/server.coffee
+++ src/server.coffee
@@ -25,6 +25,7 @@
 WIDTH = 1000
 HEIGHT = 600
 MAX_VELOCITY = 0.25
+RESPAWN_TIMER = 3000

 players = {}
 bullets = []
@@ -45,11 +46,24 @@
     when 'up-stop' then players[this.id].thrust -= 1
     when 'down-stop' then players[this.id].thrust -= -1
     when 'shoot'
-      bullets.push new Bullet(this.id,
+      bullet = new Bullet(this.id,
                               players[this.id].position.x + Math.cos(players[this.id].rotation) * players[this.id].size / 2,
                               players[this.id].position.y + Math.sin(players[this.id].rotation) * players[this.id].size / 2,
                               Math.cos(players[this.id].rotation) + players[this.id].velocity.x,
                               Math.sin(players[this.id].rotation) + players[this.id].velocity.y)
+      bullets.push bullet
+      players[this.id].health -= bullet.cost
+
+respawn = (player) ->
+  player.dead = null
+  player.position.x = WIDTH / 2
+  player.position.y = HEIGHT / 2
+  player.velocity.x = player.velocity.y = 0
+  player.acceleration.x = player.acceleration.y = 0
+  player.rotation = 90
+  player.health = player.maxHealth
+  player.bounty = 1
+  util.log "Respawning player ##{player.id}"

 onConnection = (client) ->
   util.log "New client connected: #{client.id}"
@@ -62,6 +76,7 @@
 onDisconnect = ->
   delete players[this.id]
   util.log "Player #{this.id} has disconnected"
+  socket.sockets.emit 'removePlayer', { id: this.id }

 # Listen for connection events
 socket.sockets.on 'connection', onConnection
@@ -75,6 +90,17 @@

   # Update player position
   for id, player of players
+
+    # Handle health
+    if player.health < 0 and player.dead is null
+      player.dead = Date.now()
+    else if player.health < player.maxHealth and player.dead is null
+      player.health += player.regen
+    else if player.dead? and Date.now() - player.dead > RESPAWN_TIMER
+      respawn player
+
+    if player.dead? then continue
+
     player.rotation += player.rotate * delta * 2
     player.acceleration.x = Math.cos(player.rotation) * player.thrust * delta
     player.acceleration.y = Math.sin(player.rotation) * player.thrust * delta
@@ -99,7 +125,12 @@
     if bullet.position.x < 0 || bullet.position.x > WIDTH then bullets.splice i, 1
     if bullet.position.y < 0 || bullet.position.y > HEIGHT then bullets.splice i, 1

-    # TODO: bullet collision
+    # bullet collision
+    for id, player of players
+      if bullet.owner != player.id and player.dead is null and player.collidesWith(bullet)
+        player.health -= bullet.damage
+        bullets.splice i, 1
+        util.log "Bullet collision! Player health: #{player.health}"

   # Send to all connections
   socket.sockets.emit 'data', { players: players, bullets: bullets }


OHM-125: Added "type" check to confirm message comes from get-uptime call

--- src/server.coffee
+++ src/server.coffee
@@ -80,11 +80,6 @@
     worker = cluster.fork()

     worker.on 'message', (msg) ->
-
-      if msg.uptime
-        # send response back to worker requesting uptime
-        worker.send
-          masterUptime: process.uptime()

       logger.debug "Message recieved from worker #{worker.id}", msg
       if msg.type is 'restart-all'
@@ -106,6 +101,11 @@
         for id, worker of cluster.workers
           logger.debug "Stopping TCP channel on worker #{worker.id}..."
           worker.send msg
+      else if msg.type is 'get-uptime'
+        # send response back to worker requesting uptime
+        worker.send
+          type: 'get-uptime'
+          masterUptime: process.uptime()

   # start all workers
   for i in [1..clusterSize]
@@ -609,12 +609,13 @@
     else
       # send request to master
       process.send
-        uptime: true
+        type: 'get-uptime'

       # listen for response from master
       process.once 'message', (uptime) ->
-        uptime =
-          master: uptime.masterUptime
-
-        # send reponse back to API request
-        callback null, uptime+        if uptime.type is 'get-uptime'
+          uptime =
+            master: uptime.masterUptime
+
+          # send reponse back to API request
+          callback null, uptime


Make sure to stop `idris` before spawning again

--- lib/idris-model.coffee
+++ lib/idris-model.coffee
@@ -13,7 +13,10 @@
   oldCompilerOptions: { }

   ideMode: (compilerOptions) ->
-    if !@ideModeRef || !JS.objectEqual(@oldCompilerOptions, compilerOptions)
+    if @ideModeRef && !JS.objectEqual(@oldCompilerOptions, compilerOptions)
+      @ideModeRef.stop()
+      @ideModeRef = null
+    if !@ideModeRef
       @ideModeRef = new IdrisIdeMode
       @ideModeRef.on 'message', @handleCommand
       @ideModeRef.start compilerOptions


testing

testing

--- stylus/app.coffee
+++ stylus/app.coffee
@@ -9,7 +9,7 @@
       color: #fff
       background: #630 noise
       font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif
-      font-weight: 200
+      font-weight: 20
       font-size: 14px

     a


weird stuff

weird stuff

--- app/assets/javascripts/models/concerns/order_item/order_item_base_mixin.js.coffee
+++ app/assets/javascripts/models/concerns/order_item/order_item_base_mixin.js.coffee
@@ -48,7 +48,6 @@
   ivaZeroFor: (orderType) ->
     return 0 if @get("item.iva") or @get("item.ice")
     @get("price#{orderType}") * (@get("item.iceTarifa") / 100)
-  ivaZeroCompra: (-> @ivaZeroFor("Compra") )

   ivaZeroCompra: (-> @ivaZeroFor("Compra") )
   .property("item.iva", "item.ice", "priceCompra", "item.iceTarifa")


More readable error message

More readable error message

--- test/assertions/contain_consecutive_elements_assertion.coffee
+++ test/assertions/contain_consecutive_elements_assertion.coffee
@@ -15,8 +15,8 @@
     array = @_obj
     @assert ContainConsecutiveElementsAssertion.has_consecutive_values(array,
                                                                        consecutive_values),
-            "expected #{array.join ' '} to contain #{consecutive_values} consecutively",
-            "expected #{array.join ' '} to not contain #{consecutive_values} consecutively"
+            "expected #{array.join ' '} to contain #{consecutive_values.join ' '} consecutively",
+            "expected #{array.join ' '} to not contain #{consecutive_values.join ' '} consecutively"

   # Returns whether the given array contains the given values consecutively.


point real time to web api

point real time to web api

--- services/real-time/config/settings.defaults.coffee
+++ services/real-time/config/settings.defaults.coffee
@@ -29,7 +29,7 @@

 	apis:
 		web:
-			url: "http://#{process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_PORT'] or 3000}"
+			url: "http://#{process.env['WEB_API_HOST'] or process.env['WEB_HOST'] or "localhost"}:#{process.env['WEB_API_PORT'] or process.env['WEB_PORT'] or 3000}"
 			user: process.env['WEB_API_USER'] or "sharelatex"
 			pass: process.env['WEB_API_PASSWORD'] or "password"
 		documentupdater:


Only highlight nodes if connectable, highlight by color

--- src/scripts/controllers/editorController.coffee
+++ src/scripts/controllers/editorController.coffee
@@ -83,12 +83,12 @@
 			.attr('width', (node) -> NetStorage.getNodeFromData(node).width)
 			.attr('height', (node) -> NetStorage.getNodeFromData(node).height)
 			.on 'mouseover', (node) ->
-				return if !mouseDownNode or node == mouseDownNode
-				d3.select(this).attr('transform', 'scale(1.1)') # enlarge target node
+				return if !mouseDownNode or node == mouseDownNode or !net.isConnectable(mouseDownNode, node)
+				d3.select(this).style('fill', 'rgb(235, 235, 235)') # highlight target node

 			.on 'mouseout', (node) ->
 				return if !mouseDownNode or node == mouseDownNode
-				d3.select(this).attr 'transform', '' # unenlarge target node
+				d3.select(this).attr 'style', '' # unhighlight target node

 			.on 'mousedown', (node) ->
 				# select node
@@ -113,8 +113,7 @@
 					resetMouseVars()
 					return

-				# unenlarge target node
-				d3.select(this).attr 'transform', ''
+				d3.select(this).style('fill', '') # unhighlight target node

 				# add link to graph (update if exists)
 				if mouseDownNode.id < mouseUpNode.id


Revert "Put a guard on sentry dsn"

Revert "Put a guard on sentry dsn"

This reverts commit 95e052d05910680aeeca2521b65b089122c4e249.

--- services/clsi/config/settings.defaults.coffee
+++ services/clsi/config/settings.defaults.coffee
@@ -39,7 +39,6 @@
 	parallelSqlQueryLimit:1
 	filestoreDomainOveride: process.env["FILESTORE_DOMAIN_OVERRIDE"]
 	texliveImageNameOveride: process.env["TEX_LIVE_IMAGE_NAME_OVERRIDE"]
-if process.env['SENTRY_DSN']
 	sentry:
 		dsn: process.env['SENTRY_DSN']


add auth test

add auth test

--- tests/auth.coffee
+++ tests/auth.coffee
@@ -1,7 +1,20 @@
 expect = require('chai').expect
+request = require 'request-json-light'
+server = require '../server'

 describe 'Auth', ->

+    before (done) ->
+        @client = request.newClient 'http://localhost:8282'
+        server.start silent: true, done
+
     # We don't manage auth yet. So user should always be considered as logged.
-    it 'should always be logged in ', ->
+    it 'should always be logged in ', (done) ->

+        testReturnIsOk = (err, res, body) ->
+            expect(body.registered).to.be.ok
+            expect(body.password).to.be.ok
+            expect(body.authenticated).to.be.ok
+            done()
+
+        @client.get '/user/state', testReturnIsOk


OX-723 Implement color mapping

OX-723 Implement color mapping

--- app/assets/javascripts/oxalis/geometries/plane.coffee
+++ app/assets/javascripts/oxalis/geometries/plane.coffee
@@ -54,41 +54,50 @@
       uniform sampler2D texture, volumeTexture;
       uniform vec2 offset, repeat;
       varying vec2 vUv;
+
+      /* Inspired from: https://github.com/McManning/WebGL-Platformer/blob/master/shaders/main.frag */
+      vec4 hsv_to_rgb(vec4 HSV)
+      {
+        vec4 RGB; /* = HSV.z; */
+
+        float h = HSV.x;
+        float s = HSV.y;
+        float v = HSV.z;
+
+        float i = floor(h);
+        float f = h - i;
+
+        float p = (1.0 - s);
+        float q = (1.0 - s * f);
+        float t = (1.0 - s * (1.0 - f));
+
+        if (i == 0.0) { RGB = vec4(1.0, t, p, 1.0); }
+        else if (i == 1.0) { RGB = vec4(q, 1.0, p, 1.0); }
+        else if (i == 2.0) { RGB = vec4(p, 1.0, t, 1.0); }
+        else if (i == 3.0) { RGB = vec4(p, q, 1.0, 1.0); }
+        else if (i == 4.0) { RGB = vec4(t, p, 1.0, 1.0); }
+        else /* i == -1 */ { RGB = vec4(1.0, p, q, 1.0); }
+
+        RGB *= v;
+
+        return RGB;
+      }
+
       void main() {
         vec4 volumeColor = texture2D(volumeTexture, vUv * repeat + offset);
         float id = (volumeColor[0] * 255.0);
         float golden_ratio = 0.618033988749895;

         /* Color map (<= to fight rounding mistakes) */
-
-        /*
+
         if (id > 0.1) {
-          vec4 HSV = vec4( mod( id * golden_ratio, 1.0), 1, 0.3, 1 );
-          vec4 hue;
-          hue.x = abs(HSV.x - 3.0) - 1.0;
-          hue.y = 2.0 - abs(HSV.x - 2.0);
-          hue.z = 2.0 - abs(HSV.x - 4.0);
-          volumeColor = ((clamp(hue,0.0,1.0) - 1.0) * HSV.y + 1.0) * HSV.z;
+          vec4 HSV = vec4( mod( 6.0 * id * golden_ratio, 6.0), 1.0, 1.0, 1.0 );
+          gl_FragColor = 0.7 * texture2D(texture, vUv * repeat + offset) + 0.3 * hsv_to_rgb( HSV );
         } else {
-            volumeColor = vec4(0.0, 0.0, 0.0, 1);
-        }*/
-
-             if(id <= 0.1) volumeColor = vec4(0.0, 0.0, 0.0, 1);
-        else if(id <= 1.1) volumeColor = vec4(0.3, 0.0, 0.0, 1);
-        else if(id <= 2.1) volumeColor = vec4(0.0, 0.3, 0.0, 1);
-        else if(id <= 3.1) volumeColor = vec4(0.0, 0.0, 0.3, 1);
-        else if(id <= 4.1) volumeColor = vec4(0.3, 0.3, 0.0, 1);
-        else if(id <= 5.1) volumeColor = vec4(0.0, 0.3, 0.3, 1);
-        else if(id <= 6.1) volumeColor = vec4(0.3, 0.0, 0.3, 1);
-
-        else if(id <= 7.1) volumeColor = vec4(0.3, 0.0, 0.0, 1);
-        else if(id <= 8.1) volumeColor = vec4(0.0, 0.3, 0.0, 1);
-        else if(id <= 9.1) volumeColor = vec4(0.0, 0.0, 0.3, 1);
-        else if(id <= 10.1) volumeColor = vec4(0.3, 0.3, 0.0, 1);
-        else if(id <= 11.1) volumeColor = vec4(0.0, 0.3, 0.3, 1);
-        else if(id <= 12.1) volumeColor = vec4(0.3, 0.0, 0.3, 1);
-
-        gl_FragColor = texture2D(texture, vUv * repeat + offset) + volumeColor; }"
+          gl_FragColor = texture2D(texture, vUv * repeat + offset);
+        }
+      }
+        "
     uniforms = {
       texture : {type : "t", value : texture},
       volumeTexture : {type : "t", value : volumeTexture},


calibrage du div des deadlocks

calibrage du div des deadlocks

--- app/assets/jax/controllers/level_controller.js.coffee
+++ app/assets/jax/controllers/level_controller.js.coffee
@@ -106,8 +106,10 @@
       box_position = @position_of_last_pushed_box()
       deadlocked_positions = @deadlock.deadlocked_boxes(@level)
       deadlocked_positions = @deadlock.deadlocked_last_push(@level, box_position, deadlocked_positions)
+      @level.highlight(deadlocked_positions)
+
+      # "there is a deadlock" div
       if deadlocked_positions.length != 0
-        @level.highlight(deadlocked_positions)
         if $('#deadlock').css('display') == 'none'
           $('#deadlock').show().clearQueue()
                         .transition({opacity:0}, 0).transition({opacity:0.93}, 1000)


[Email Confirmation] Add status field for referrals which are waiting for user's email confirmation

--- client/landing/app/Applications/Account.kdapplication/views/referralsystem.coffee
+++ client/landing/app/Applications/Account.kdapplication/views/referralsystem.coffee
@@ -159,7 +159,14 @@
     super options, data

   viewAppended: ->
-    super
+    @getData().isEmailVerified (err, status)=>
+      unless (err or status)
+        @addSubView editLink = new KDCustomHTMLView
+           tagName      : "a"
+           partial      : "Mail Verification Waiting"
+           cssClass     : "action-link"
+
+      super

   partial: (data)->
     """


Disable classroom invite test

Disable classroom invite test

--- spec/server/functional/classrooms.spec.coffee
+++ spec/server/functional/classrooms.spec.coffee
@@ -530,7 +530,7 @@
         expect(prepaid.get('redeemers').length).toBe(0)
         done()

-describe 'POST /db/classroom/:id/invite-members', ->
+xdescribe 'POST /db/classroom/:id/invite-members', ->

   it 'takes a list of emails and sends invites', utils.wrap (done) ->
     user = yield utils.initUser({role: 'teacher', name: 'Mr Professerson'})


Refactor app.ErrorReporter.

Refactor app.ErrorReporter.

--- app/client/js/errorreporter.coffee
+++ app/client/js/errorreporter.coffee
@@ -2,6 +2,8 @@

 goog.require 'goog.Promise'
 goog.require 'goog.debug.ErrorReporter'
+goog.require 'app.Xhr.OfflineError'
+goog.require 'goog.net.HttpStatus'

 class app.ErrorReporter

@@ -21,30 +23,44 @@
     @param {*} reason
   ###
   report: (action, reason) ->
-    # Cancellation is not considered as a real error. For example, este.Router
-    # cancels loading when another route is requested before previous is done.
-    if reason instanceof goog.Promise.CancellationError
-      return
+    return if !@isWorthReport_ reason

-    # Don't report already reported reason.
-    reasonAsString = reason.toString()
-    return if @alreadyReported_[reasonAsString]
-    @alreadyReported_[reasonAsString] = true
-
-    if reason instanceof app.Xhr.OfflineError
-      alert reason.message
-      return
-
-    # TODO: Show something more beautiful then alert with a button to reload
-    # app. No, auto-reload isn't a good idea since it can cause looping.
+    # TODO: Show something more beautiful then alert. Add button to reload app.
+    # Auto-reload is bad idea because it can cause reload looping.
     alert 'Application error. Please reload browser.'

-    if @reporter
-      @reporter.setAdditionalArguments
+    if !@isAlreadyReported_ reason
+      @reporter?.setAdditionalArguments
         action: action
         user: @userStore?.user?.displayName || ''

-    # Don't swallow error since it can be handled by any other promise.
-    # Throw also ensures error is shown in console and therefore catched by
-    # goog.debug.ErrorReporter.
-    throw reason
+      # Propagate error to other promises. It also ensures reason is shown in
+      # console therefore catched and reported by goog.debug.ErrorReporter.
+      throw reason
+
+  ###*
+    @param {*} reason
+    @return {boolean}
+  ###
+  isWorthReport_: (reason) ->
+    # 404 is common.
+    return false if reason == 404
+    # Cancellation is ok. For example este.Router cancels aborted requests.
+    return false if reason instanceof goog.Promise.CancellationError
+    # User is offline. It happens.
+    if reason instanceof app.Xhr.OfflineError
+      alert reason.message
+      return false
+    true
+
+  ###*
+    Don't report already reported reason.
+    @param {*} reason
+    @return {boolean}
+  ###
+  isAlreadyReported_: (reason) ->
+    stringReason = String reason
+    if @alreadyReported_[stringReason]
+      return true
+    @alreadyReported_[stringReason] = true
+    false


payment: fix a race condition

payment: fix a race condition

--- client/app/Applications/Dashboard.kdapplication/views/planaddproductsmodal.coffee
+++ client/app/Applications/Dashboard.kdapplication/views/planaddproductsmodal.coffee
@@ -69,9 +69,11 @@

     plan.quantities = quantities

-    plan.modify { quantities }, callback
+    plan.modify { quantities }, (err) =>
+      return callback err  if err
+      callback null

-    @emit 'ProductsAdded'
+      @emit 'ProductsAdded'

   setProducts: (products) ->
     @loader.hide()


payment: refactor plan list item to use common product view

--- client/landing/app/Applications/Dashboard.kdapplication/views/planlistitem.coffee
+++ client/landing/app/Applications/Dashboard.kdapplication/views/planlistitem.coffee
@@ -1,26 +1,12 @@
 class GroupPlanListItem extends GroupProductListItem
   viewAppended: ->
+    @planView = new GroupProductView {}, @getData()
+
     super()

-  prepareData: ->
-    product = @getData()
-
-    title = product.title
-    price = (product.feeAmount / 100).toFixed(2)
-
-    subscriptionType =
-      if product.subscriptionType is 'single'
-        "Single payment"
-      else
-        "Recurring payment"
-
-    { title, price, subscriptionType }
-
   pistachio:->
-    { title, price, subscriptionType } = @prepareData()
-
     """
-    One of these #{ title } #{ price } #{ subscriptionType }
+    {{> @planView}}
     {{> @embedButton}}
     {{> @deleteButton}}
     {{> @clientsButton}}


Fixing small bug in ontario outdoors card number

--- src/zfields/ontario_outdoors_card_number.coffee
+++ src/zfields/ontario_outdoors_card_number.coffee
@@ -61,7 +61,7 @@

 $.formance.fn.formatOntarioOutdoorsCardNumber = ->
-    @.formance('restrictAlphaNumeric')
+    @.formance('restrictNumeric')
     @on('keypress', restrictOntarioOutdoorsCardNumber)
     @on('keypress', formatOntarioOutdoorsCardNumber)
     @on('keydown',  formatBackOntarioOutdoorsCardNumber)


fix for issue #961 - typo in JS driver

fix for issue #961 - typo in JS driver

--- drivers/javascript/src/errors.coffee
+++ drivers/javascript/src/errors.coffee
@@ -52,7 +52,7 @@
             optargs[key] = if key == argNum
                              composeCarrots(arg, frames)
                            else
-                             comoseTerm(arg)
+                             composeTerm(arg)
         if argNum >= 0
             term.compose(args, optargs)
         else


minor cleanup on sandbox escape

minor cleanup on sandbox escape

--- chrome/content/zotero-better-bibtex/keymanager.coffee
+++ chrome/content/zotero-better-bibtex/keymanager.coffee
@@ -133,7 +133,7 @@
     return @journalAbbrevs[item.publicationTitle]

   extract: (item, insitu) ->
-    [itemID, insitu] = Array.slice(arguments, 1, 3) if itemID._sandboxManager
+    [item, insitu] = Array.slice(arguments, 1, 3) if item._sandboxManager

     switch
       when item.getField
@@ -321,7 +321,7 @@
     return clone

   get: (item, pinmode) ->
-    [item, pinmode] = Array.slice(arguments, 1, 3) if itemID._sandboxManager
+    [item, pinmode] = Array.slice(arguments, 1, 3) if item._sandboxManager

     Zotero.BetterBibTeX.debug("keymanager.get: item=#{item.itemID}, pinmode=#{pinmode}")


Added AMD header and fixed a bug in PubNub#history

--- app/scripts/backbone-pubnub.coffee
+++ app/scripts/backbone-pubnub.coffee
@@ -1,283 +1,294 @@
+((factory) ->
+    if typeof define is "function" and define.amd
+        define(["underscore", "backbone", "exports"], factory)
+    else if typeof exports is "object"
+        factory(require("underscore"), require("backbone"), exports)
+    else
+        factory(_, Backbone, {})
+) (_, Backbone, PubNub) ->
+
 ################
 # Backbone.PubNub
 # -----------------
 # The Backbone.PubNub object is used as a plugin to make collections real-time
 ################
-Backbone.PubNub = (ref, name) ->
-  @name = name
-  @ref = ref
-  @uuid = @ref.uuid()
-  @channel = "backbone-#{@name}"
-  @records = []
-
-  # Subscribe to publish methods and react to what happens on the other side
-  @ref.subscribe
-    channel: @channel
-    callback: (message) =>
-      # We don't want to cause a loop by listening to our own events
-      unless message.uuid is @uuid
-        switch message.method
-          when "create" then @create message.model
-          when "update" then @update message.model
-          when "delete" then @destroy message.model
-
-_.extend Backbone.PubNub.prototype,
-  # Publishes a change to the pubnub channel
-  publish: (method, model, options) ->
-    message =
-      method: method
-      model: model
-      options: options
-      uuid: @uuid
-
-    @ref.publish
-      channel: @channel
-      message: message
-
-  # Finds a model in the local array
-  read: (model) ->
-    unless model.id?
-      @find model.id
-    else
-      @findAll()
-
-  # Find an individual record
-  find: (id) ->
-    _.find @records, (record) ->
-      record.id is id
-
-  # Return all records from the local array
-  findAll: () ->
-    @records
-
-  # Create a new record and publish it to other instances
-  create: (model) ->
-    unless model.id?
-      model.id = @ref.uuid()
-      model.set model.idAttribute, model.id
-
-    @records.push model
-    @publish "create", model
-    model
-
-  # When a record updates, publish the changes to other instances
-  update: (model) ->
-    oldModel = @find model.id
-    @records[@records.indexOf(oldModel)] = model
-    @publish "update", model
-    model
-
-  # When a record is removed, publish the changes to other instances
-  destroy: (model) ->
-    if model.isNew()
-      return false
-    @records = _.reject @records, (record) ->
-      record.id is model.id
-    @publish "delete", model
-    model
-
-# Custom sync method for using the plugin version
-Backbone.PubNub.sync = (method, model, options) ->
-  pubnub = model.pubnub ? model.collection.pubnub
-
-  console.log method, model, options, pubnub
-
-  try
-    switch method
-      when "read" then resp = pubnub.read model
-      when "create" then resp = pubnub.create model
-      when "update" then resp = pubnub.update model
-      when "delete" then resp = pubnub.destroy model
-  catch error
-    errorMessage = error.message
-    console.log "Could not sync: #{errorMessage}"
-
-# Save the old sync method for later use
-_sync = Backbone.sync
-
-# Override the sync method to use pubnub if it is available
-Backbone.sync = (method, model, options) ->
-  syncMethod = _sync
-
-  if model.pubnub or (model.collection and model.collection.pubnub)
-    syncMethod = Backbone.PubNub.sync
-
-  syncMethod.apply this, [method, model, options]
-
-################
-# Backbone.PubNub.Collection
-# ----------------------------
-# A real-time implementation of a Backbone Collection.
-# This works by publishing a transaction of all methods (create, update, delete)
-# to other instances.
-################
-Backbone.PubNub.Collection = Backbone.Collection.extend
-  # Publishes a change to the pubnub channel
-  publish: (method, model, options) ->
-    message =
-      method: method
-      model: model
-      options: options
-      uuid: @uuid
-
-    @pubnub.publish
-      channel: @channel
-      message: message
-
-  constructor: (models, options) ->
-    Backbone.Collection.apply this, arguments
-
-    if options and options.pubnub
-      @pubnub = options.pubnub
-
-    @uuid = @pubnub.uuid()
-    @channel = "backbone-collection-#{@name}"
-
-    # Subscribe to this colleciton's channel and listen for changes to
-    # other client's collections
-    @pubnub.subscribe
-      channel: @channel
-      callback: (message) =>
-        @off 'change', @_updateModel, this
-
-        unless message.uuid is @uuid
-          switch message.method
-            when "create" then @_onAdded message.model, message.options
-            when "update" then @_onChanged message.model, message.options
-            when "delete" then @_onRemoved message.model, message.options
-        @on 'change', @_updateModel, this
-
-    # When the collection changes we post updates to everyone else
-    @on 'change', @_updateModel, this
-
-  _updateModel: (model) ->
-    @pubnub.publish "update", model
-
-  # fetch messages from the history
-  # TODO options should not be hardcoded here, should be taken as argument
-  history: (options) ->
-    defaults =
-        count:100
-        channel: @channel
-        callback: (results) =>
-            messages = _.chain(results)
-            .first()
-            .pluck('model')
-            .reject(_.isUndefined)
-            .value()
-            @off 'change', @_updateModel, @
-            @reset messages
-            @on 'change', @_updateModel, @
-
-
-
-    @pubnub.hist_.extend defaults, ory options
+    Backbone.PubNub = (ref, name) ->
+        @name = name
+        @ref = ref
+        @uuid = @ref.uuid()
+        @channel = "backbone-#{@name}"
+        @records = []
+
+        # Subscribe to publish methods and react to what happens on the other side
+        @ref.subscribe
+            channel: @channel
+            callback: (message) =>
+# We don't want to cause a loop by listening to our own events
+                unless message.uuid is @uuid
+                    switch message.method
+                        when "create" then @create message.model
+                        when "update" then @update message.model
+                        when "delete" then @destroy message.model
+
+    _.extend Backbone.PubNub.prototype,
+# Publishes a change to the pubnub channel
+        publish: (method, model, options) ->
+            message =
+                method: method
+                model: model
+                options: options
+                uuid: @uuid
+
+            @ref.publish
+                channel: @channel
+                message: message
+
+# Finds a model in the local array
+        read: (model) ->
+            unless model.id?
+                @find model.id
+            else
+                @findAll()
+
+# Find an individual record
+        find: (id) ->
+            _.find @records, (record) ->
+                record.id is id
+
+# Return all records from the local array
+        findAll: () ->
+            @records
+
+# Create a new record and publish it to other instances
+        create: (model) ->
+            unless model.id?
+                model.id = @ref.uuid()
+                model.set model.idAttribute, model.id
+
+            @records.push model
+            @publish "create", model
+            model
+
+# When a record updates, publish the changes to other instances
+        update: (model) ->
+            oldModel = @find model.id
+            @records[@records.indexOf(oldModel)] = model
+            @publish "update", model
+            model
+
+# When a record is removed, publish the changes to other instances
+        destroy: (model) ->
+            if model.isNew()
+                return false
+            @records = _.reject @records, (record) ->
+                record.id is model.id
+            @publish "delete", model
+            model
+
+    # Custom sync method for using the plugin version
+    Backbone.PubNub.sync = (method, model, options) ->
+        pubnub = model.pubnub ? model.collection.pubnub
+
+        console.log method, model, options, pubnub
+
+        try
+            switch method
+                when "read" then resp = pubnub.read model
+                when "create" then resp = pubnub.create model
+                when "update" then resp = pubnub.update model
+                when "delete" then resp = pubnub.destroy model
+        catch error
+            errorMessage = error.message
+            console.log "Could not sync: #{errorMessage}"
+
+    # Save the old sync method for later use
+    _sync = Backbone.sync
+
+    # Override the sync method to use pubnub if it is available
+    Backbone.sync = (method, model, options) ->
+        syncMethod = _sync
+
+        if model.pubnub or (model.collection and model.collection.pubnub)
+            syncMethod = Backbone.PubNub.sync
+
+        syncMethod.apply this, [method, model, options]
+
+    ################
+    # Backbone.PubNub.Collection
+    # ----------------------------
+    # A real-time implementation of a Backbone Collection.
+    # This works by publishing a transaction of all methods (create, update, delete)
+    # to other instances.
+    ################
+    Backbone.PubNub.Collection = Backbone.Collection.extend
+# Publishes a change to the pubnub channel
+        publish: (method, model, options) ->
+            message =
+                method: method
+                model: model
+                options: options
+                uuid: @uuid
+
+            @pubnub.publish
+                channel: @channel
+                message: message
+
+        constructor: (models, options) ->
+            Backbone.Collection.apply this, arguments
+
+            if options and options.pubnub
+                @pubnub = options.pubnub
+
+            @uuid = @pubnub.uuid()
+            @channel = "backbone-collection-#{@name}"
+
+            # Subscribe to this colleciton's channel and listen for changes to
+            # other client's collections
+            @pubnub.subscribe
+                channel: @channel
+                callback: (message) =>
+                    @off 'change', @_updateModel, this
+
+                    unless message.uuid is @uuid
+                        switch message.method
+                            when "create" then @_onAdded message.model, message.options
+                            when "update" then @_onChanged message.model, message.options
+                            when "delete" then @_onRemoved message.model, message.options
+                    @on 'change', @_updateModel, this
+
+            # When the collection changes we post updates to everyone else
+            @on 'change', @_updateModel, this
+
+        _updateModel: (model) ->
+            @pubnub.publish "update", model
+
+# fetch messages from the history
+# TODO options should not be hardcoded here, should be taken as argument
+        history: (options) ->
+            defaults =
+                count:100
+                channel: @channel
+                callback: (results) =>
+                    messages = _.chain(results)
+                    .first()
+                    .pluck('model')
+                    .reject(_.isUndefined)
+                    .value()
+                    @off 'change', @_updateModel, @
+                    @reset messages
+                    @on 'change', @_updateModel, @
+
+
+
+            @pubnub.history _.extend defaults, options

 # Called when another client adds a record
-  _onAdded: (model, options) ->
-    Backbone.Collection.prototype.add.apply this, [model, options]
-
-  # Called when another client changes a record
-  _onChanged: (model, options) ->
-    unless not model.id
-      record = _.find @models, (record) ->
-        record.id is model.id
-
-      unless record?
-        console.log "Could not find model with ID: #{model.id}"
-        return false
-
-      # Since there is no native update record we have to find the differences manually
-      diff = _.difference _.keys(record.attributes), _.keys(model)
-      _.each diff, (key) ->
-        record.unset key
-
-      record.set model, options
-
-  # Called when another client removes a record
-  _onRemoved: (model, options) ->
-    Backbone.Collection.prototype.remove.apply this, [model, options]
-
-  add: (models, options) ->
-    models = if _.isArray(models) then models.slice() else [models]
-
-    for model in models
-      # We need to manually create an ID for each record, otherwise
-      # Backbone will not give us change or remove events
-      unless model.id?
-        model.id = @pubnub.uuid()
-
-      @publish "create", model, options
-
-    Backbone.Collection.prototype.add.apply this, arguments
-
-  remove: (models, options) ->
-    models = if _.isArray(models) then models.slice() else [models]
-
-    for model in models
-      @publish "delete", model, options
-
-    Backbone.Collection.prototype.remove.apply this, arguments
-
-################
-# Backbone.PubNub.Model
-# ----------------------------
-# A real-time implementation of a Backbone Model.
-# This will publish all change events to other instances of the same model. It
-# will also remove itself if another instance is removed
-################
-Backbone.PubNub.Model = Backbone.Model.extend
-  # Publishes a change to the pubnub channel
-  publish: (method, model, options) ->
-    message =
-      method: method
-      model: model
-      options: options
-      uuid: @uuid
-
-    @pubnub.publish
-      channel: @channel
-      message: message
-
-  constructor: (model, options) ->
-    Backbone.Model.apply this, arguments
-
-    if options and options.pubnub and options.name
-      @pubnub = options.pubnub
-      @name = options.name
-
-    @uuid = @pubnub.uuid()
-    @channel = "backbone-model-#{@name}"
-
-    @on 'change', @_onChange, this
-
-    # Subscribe to updates from other instances of this model
-    @pubnub.subscribe
-      channel: @channel
-      callback: (message) =>
-        unless message.uuid is @uuid
-          switch message.method
-            when "update" then @_onChanged message.model, message.options
-            when "delete" then @_onRemoved message.options
-
-  # Publish changes when this model is changed
-  _onChange: (model, options) ->
-    @publish "update", model, options
-
-  _onChanged: (model, options) ->
-    @off 'change', @_onChange, this
-    # Manually find the difference and update this model
-    diff = _.difference _.keys(@attributes), _.keys(model)
-    _.each diff, (key) =>
-      @unset key
-
-    @set model
-    @on 'change', @_onChange, this
-
-  _onRemoved: (options) ->
-    Backbone.Model.prototype.destroy.apply this, arguments
-
-  destroy: (options) ->
-    @publish "delete", null, options
-    Backbone.Model.prototype.destroy.apply this, arguments+        _onAdded: (model, options) ->
+            Backbone.Collection.prototype.add.apply this, [model, options]
+
+# Called when another client changes a record
+        _onChanged: (model, options) ->
+            unless not model.id
+                record = _.find @models, (record) ->
+                    record.id is model.id
+
+                unless record?
+                    console.log "Could not find model with ID: #{model.id}"
+                    return false
+
+                # Since there is no native update record we have to find the differences manually
+                diff = _.difference _.keys(record.attributes), _.keys(model)
+                _.each diff, (key) ->
+                    record.unset key
+
+                record.set model, options
+
+# Called when another client removes a record
+        _onRemoved: (model, options) ->
+            Backbone.Collection.prototype.remove.apply this, [model, options]
+
+        add: (models, options) ->
+            models = if _.isArray(models) then models.slice() else [models]
+
+            for model in models
+# We need to manually create an ID for each record, otherwise
+# Backbone will not give us change or remove events
+                unless model.id?
+                    model.id = @pubnub.uuid()
+
+                @publish "create", model, options
+
+            Backbone.Collection.prototype.add.apply this, arguments
+
+        remove: (models, options) ->
+            models = if _.isArray(models) then models.slice() else [models]
+
+            for model in models
+                @publish "delete", model, options
+
+            Backbone.Collection.prototype.remove.apply this, arguments
+
+    ################
+    # Backbone.PubNub.Model
+    # ----------------------------
+    # A real-time implementation of a Backbone Model.
+    # This will publish all change events to other instances of the same model. It
+    # will also remove itself if another instance is removed
+    ################
+    Backbone.PubNub.Model = Backbone.Model.extend
+# Publishes a change to the pubnub channel
+        publish: (method, model, options) ->
+            message =
+                method: method
+                model: model
+                options: options
+                uuid: @uuid
+
+            @pubnub.publish
+                channel: @channel
+                message: message
+
+        constructor: (model, options) ->
+            Backbone.Model.apply this, arguments
+
+            if options and options.pubnub and options.name
+                @pubnub = options.pubnub
+                @name = options.name
+
+            @uuid = @pubnub.uuid()
+            @channel = "backbone-model-#{@name}"
+
+            @on 'change', @_onChange, this
+
+            # Subscribe to updates from other instances of this model
+            @pubnub.subscribe
+                channel: @channel
+                callback: (message) =>
+                    unless message.uuid is @uuid
+                        switch message.method
+                            when "update" then @_onChanged message.model, message.options
+                            when "delete" then @_onRemoved message.options
+
+# Publish changes when this model is changed
+        _onChange: (model, options) ->
+            @publish "update", model, options
+
+        _onChanged: (model, options) ->
+            @off 'change', @_onChange, this
+            # Manually find the difference and update this model
+            diff = _.difference _.keys(@attributes), _.keys(model)
+            _.each diff, (key) =>
+                @unset key
+
+            @set model
+            @on 'change', @_onChange, this
+
+        _onRemoved: (options) ->
+            Backbone.Model.prototype.destroy.apply this, arguments
+
+        destroy: (options) ->
+            @publish "delete", null, options
+            Backbone.Model.prototype.destroy.apply this, arguments
+
+    return Backbone.PubNub


Use modern signal/slot mechanism

Use modern signal/slot mechanism

--- client/Framework/components/tabs/KDTabPaneView.coffee
+++ client/Framework/components/tabs/KDTabPaneView.coffee
@@ -9,18 +9,9 @@
     @setClass "clearfix"
     @setHeight @$().parent().height()

-    @listenTo
-      KDEventTypes        : [ eventType : "KDTabPaneActive" ]
-      listenedToInstance  : @
-      callback            : @becameActive
-    @listenTo
-      KDEventTypes        : [ eventType : "KDTabPaneInactive" ]
-      listenedToInstance  : @
-      callback            : @becameInactive
-    @listenTo
-      KDEventTypes        : [ eventType : "KDTabPaneDestroy" ]
-      listenedToInstance  : @
-      callback            : @aboutToBeDestroyed
+    @on "KDTabPaneActive"   , @becameActive
+    @on "KDTabPaneInactive" , @becameInactive
+    @on "KDTabPaneDestroy"  , @aboutToBeDestroyed

   becameActive: noop
   becameInactive: noop
@@ -29,12 +20,12 @@
   show:()->
     @getDomElement().removeClass("kdhiddentab").addClass("active")
     @active = yes
-    @handleEvent type : "KDTabPaneActive"
+    @emit "KDTabPaneActive"

   hide:()->
     @getDomElement().removeClass("active").addClass("kdhiddentab")
     @active = no
-    @handleEvent type : "KDTabPaneInactive"
+    @emit "KDTabPaneInactive"

   viewAppended:()->
     {name} = @getOptions()


client/app: remove NavigationController

client/app: remove NavigationController

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/app/lib/navigation/navigationcontroller.coffee
+++ client/app/lib/navigation/navigationcontroller.coffee
@@ -1,46 +0,0 @@
-kd = require 'kd'
-KDListViewController = kd.ListViewController
-isLoggedIn = require '../util/isLoggedIn'
-globals = require 'globals'
-
-
-module.exports = class NavigationController extends KDListViewController
-
-  reset: ->
-    previousSelection = @selectedItems.slice()
-    @removeAllItems()
-    @instantiateListItems @getData().items
-    @selectItemByName name  for { name } in previousSelection
-
-  getItemByName: (name) ->
-    for navItem in @getListItems() when navItem.getData()?.title is name
-      return navItem
-
-  selectItemByName: (name) ->
-    if item = @getItemByName name
-    then @selectItem item
-    else @deselectAllItems()
-    return item
-
-  removeItemByTitle: (name) ->
-    for navItem in @getListItems() when navItem?.name is name
-      @removeItem navItem
-
-  instantiateListItems: (items) ->
-    { roles } = globals.config
-
-    for itemData in items
-      # if not defined, do not check loggedIn state
-      if itemData.loggedIn?
-        # loggedIn:yes = do not show if not logged in
-        if itemData.loggedIn
-          continue  unless isLoggedIn() # do not show if not logged in
-        # loggedIn:no = do not show if logged in
-        unless itemData.loggedIn
-          continue  if     isLoggedIn()
-
-      if itemData.role
-        if itemData.role in roles
-          @getListView().addItem itemData
-      else
-        @getListView().addItem itemData


updated interactive icon component svg

updated interactive icon component svg


fix(message-bodies): Ensure each message body is processed

Relevant for #436

--- src/flux/stores/message-body-processor.coffee
+++ src/flux/stores/message-body-processor.coffee
@@ -24,7 +24,7 @@
   # optimistically display the message before the latest changes
   # persisted.
   _key: (message) ->
-    return crypto.createHash('md5').update(message.body).digest('hex')
+    return message.id + crypto.createHash('md5').update(message.body ? "").digest('hex')

   version: ->
     @_version
@@ -38,6 +38,8 @@

   process: (message) =>
     body = message.body
+    return "" unless body
+
     key = @_key(message)
     if @_recentlyProcessedD[key]
       return @_recentlyProcessedD[key].body


add seeked event to player api

add seeked event to player api

--- src/javascripts/extensions/playerjs/adapter.coffee
+++ src/javascripts/extensions/playerjs/adapter.coffee
@@ -26,6 +26,9 @@
       @receiver.emit 'buffered',
         percent: @player.media.buffered.length

+    @player.media.addEventListener 'seeked', () =>
+      @receiver.emit('seeked')
+
   setupMethods: () ->
     @receiver.on 'play', () =>
       @player.play()


Fix code spacing.

Fix code spacing.

--- js/src/service/caching-provider/CachingService.coffee
+++ js/src/service/caching-provider/CachingService.coffee
@@ -17,13 +17,13 @@
   #
   class CachingService

-    @USERS           = 'users'
-    @UUIDS           = 'uuids'
-    @SALTS           = 'user_salts'
-    @OBJECTS         = 'objects'
-    @PUBLIC_KEYS     = 'public_keys'
-    @DEFAULT_GROUP   = 'default_group'
-    @CRYPTO_SERVICES = 'object_crypto_services'
+    @USERS                 = 'users'
+    @UUIDS                 = 'uuids'
+    @SALTS                 = 'user_salts'
+    @OBJECTS               = 'objects'
+    @PUBLIC_KEYS           = 'public_keys'
+    @DEFAULT_GROUP         = 'default_group'
+    @CRYPTO_SERVICES       = 'object_crypto_services'
     @CRYPTO_SERVICE_LOADER = 'crypto_service_loader'

     @MASTER_AES_CRYPTO_SERVICE_ID = 'master_aes_crypto_service_id'


Don't remove non-existent item

Don't remove non-existent item

It seems that during the Popover rewrite this line got left in
accidentally, causing a message to be printed to the developer console
whenever this package is disabled or Atom is reloaded.

Fixes #312.

--- lib/tooltip/abstract-provider.coffee
+++ lib/tooltip/abstract-provider.coffee
@@ -45,7 +45,6 @@
      * Deactives the provider.
     ###
     deactivate: () ->
-        document.removeChild(@popover)
         @subAtom.dispose()
         @removePopover()


code tidy

code tidy

--- src/scripts/controllers/frogController.coffee
+++ src/scripts/controllers/frogController.coffee
@@ -40,7 +40,7 @@
       $scope.frogs = spawn($scope._red, $scope._blue)

       # tell the container how much room we need
-      $scope.container.width = 130*$scope.frogs.length if $scope.container?
+      # $scope.container.width = 130*$scope.frogs.length if $scope.container?

       # create a move list for current frog counts
       $scope.moves = new MoveList(
@@ -55,12 +55,10 @@
       $scope.minimum = false
       $scope.showReplay = $scope.savedMoves.length > 0

+      # cancel any moves that are outstanding
       promises.forEach (p) -> $timeout.cancel p

     reset()
-
-    equals = (a,b) ->
-      a.length == b.length && a.every (aVal, i) -> aVal == b[i]

     # check whether frog states a and b are mirrored
     reversed = (a, b) ->
@@ -84,11 +82,9 @@
       [frog.x, space.x] = [space.x, frog.x]

       # check for end
-      state = (d.colour for d in $scope.frogs)
-
       $scope.done = reversed($scope.frogs, initialState)
       $scope.minimum = $scope.done && $scope.moves.list.length == $scope.minMove
-      $scope.showReplay |= $scope.done
+      $scope.showReplay ||= $scope.done

     $scope.replay = (index = null) ->
       #console.log "replay(", index, ")"


added more spawning variants

added more spawning variants

--- src/scripts/controllers/frogController.coffee
+++ src/scripts/controllers/frogController.coffee
@@ -6,10 +6,22 @@
   $scope._red = 2
   $scope._blue = 2

+
+  ### - coffeescript comprehension
+  frog = (z) -> if z < 0 then 0 else if z == 0 then 1 else 2
+  spawn = (red, blue) -> (frog(z) for z in [-red..blue])
+  ###
+
+  # javascript array handling 2
+  frog = (z) -> if z < 0 then 0 else if z == 0 then 1 else 2
+  spawn = (red,blue) -> [-red..blue].map frog
+
+  ### javascript array handling 1
   spawn = (red,blue) ->
     [1..red].map(()-> 0).concat([1]).concat([1..blue].map(()-> 2))
+  ###

-  ###
+  ### - simple loop
   spawn = (red,blue) ->
     ar = []
     i=0
@@ -23,6 +35,7 @@
       ++i
     return ar
   ###
+
   doneState = []
   redraw = ->
     initialState = spawn($scope._red,$scope._blue)


Use coffeescript syntax

Use coffeescript syntax

--- src/scripts/helpers/handlers/analytics.coffee
+++ src/scripts/helpers/handlers/analytics.coffee
@@ -37,7 +37,7 @@
       window._gaq.push(['_setAccount', module.config().analyticsID])

       # ## Add tracking with ga.js
-      loadUrl = Backbone.History.prototype.loadUrl
+      loadUrl = Backbone.History::loadUrl
       Backbone.History::loadUrl = () ->
         matched = loadUrl.apply(@, arguments)
         fragment = @fragment


Load selection manager first so it catches the initial focus event

--- src/trix/controllers/editor_controller.coffee
+++ src/trix/controllers/editor_controller.coffee
@@ -10,12 +10,12 @@
 class Trix.EditorController extends Trix.AbstractEditorController
   constructor: ->
     super
+    @selectionManager = new Trix.SelectionManager @documentElement
+    @selectionManager.delegate = this
+
     @documentController = new Trix.DocumentController @documentElement, @document
     @documentController.delegate = this
-    @documentController.focus() if @config.autofocus
-
-    @selectionManager = new Trix.SelectionManager @documentElement
-    @selectionManager.delegate = this
+    @documentController.focus()

     @composition = new Trix.Composition @document, @config
     @composition.delegate = this


Temporarily disable standup

Temporarily disable standup

--- scripts/standup.coffee
+++ scripts/standup.coffee
@@ -19,23 +19,23 @@
 #
 # Author:
 #   <github username of the original script author>
-module.exports = (robot) ->
-  robot.brain.set 'standup', {}
-
-  robot.hear /\/standup$/i, (msg) ->
-    standup = robot.brain.get('standup')
-    if !standup[msg.message.room]
-      msg.send "No standup recorded yet in this room."
-    else
-      for user,status of standup[msg.message.room]
-        msg.send user + " - " + status
-
-  robot.hear /\/standup (.*)$/i, (msg) ->
-    status = msg.match[1] + "\n"
-    standup = robot.brain.get('standup')
-    standup[msg.message.room] = {
-      "#{msg.message.user.name}": status
-    }
-    robot.brain.set 'standup', standup
-    msg.send "Standup recorded."
-
+#module.exports = (robot) ->
+#  robot.brain.set 'standup', {}
+#
+#  robot.hear /\/standup$/i, (msg) ->
+#    standup = robot.brain.get('standup')
+#    if !standup[msg.message.room]
+#      msg.send "No standup recorded yet in this room."
+#    else
+#      for user,status of standup[msg.message.room]
+#        msg.send user + " - " + status
+#
+#  robot.hear /\/standup (.*)$/i, (msg) ->
+#    status = msg.match[1] + "\n"
+#    standup = robot.brain.get('standup')
+#    standup[msg.message.room] = {
+#      "#{msg.message.user.name}": status
+#    }
+#    robot.brain.set 'standup', standup
+#    msg.send "Standup recorded."
+#


Add Scum only tag to K4 and Outlaw.

Add Scum only tag to K4 and Outlaw.

fixes #180

--- coffeescripts/cards-en.coffee
+++ coffeescripts/cards-en.coffee
@@ -543,9 +543,9 @@
         "R4 Agromech":
             text: """When attacking, after you spend a focus token, you may acquire a target lock on the defender."""
         "K4 Security Droid":
-            text: """After executing a green maneuver, you may acquire a target lock."""
+            text: """%SCUMONLY%%LINEBREAK%After executing a green maneuver, you may acquire a target lock."""
         "Outlaw Tech":
-            text: """After you execute a red maneuver, you may assign 1 focus token to your ship."""
+            text: """%SCUMONLY%%LINEBREAK%After you execute a red maneuver, you may assign 1 focus token to your ship."""
         "Advanced Targeting Computer":
             text: """TIE Advanced only.%LINEBREAK%When attacking with your primary weapon, if you have a target lock on the defender, you may add 1 %CRIT% result to your roll.  If you do, you cannot spend target locks during this attack."""
         "Ion Cannon Battery":


fix for #210: rename the `filesselectedFiles` variable and remove hardcoded text

--- modules/file-input/main/index.coffee
+++ modules/file-input/main/index.coffee
@@ -40,7 +40,7 @@
       dragEnabled: true
       buttonClass: 'hx-action'
       noFilesText: 'No File Chosen'
-      filesselectedFiles: 'Files Selected'
+      filesSelectedText: 'Files Selected'
       buttonText: 'Choose File'

     resolvedOptions = hx.merge defaults, options
@@ -125,7 +125,7 @@
         else
           selectedFiles
             .classed 'hx-btn', true
-            .add hx.section().text "#{length} Files Selected"
+            .add hx.section().text "#{length} #{resolvedOptions.filesSelectedText}"
             .add hx.detached('i').class 'hx-file-input-dropdown-icon hx-icon hx-icon-chevron-down'
             .on 'click', 'hx.file-input', -> dropdown.show()
       else


FIX: Allow fillWithUnStartedTasks to use task def id or obj

--- src/app/common/services/unit-service.coffee
+++ src/app/common/services/unit-service.coffee
@@ -229,13 +229,14 @@
     #
     # Add any missing tasks and return the new collection
     #
-    unit.fillWithUnStartedTasks = (tasks, task_def) ->
-      projs = _.filter(unit.students, (s) -> s.target_grade >= task_def.target_grade)
+    unit.fillWithUnStartedTasks = (tasks, taskDef) ->
+      taskDef = unit.taskDef(taskDef)
+      projs = _.filter(unit.students, (s) -> s.target_grade >= taskDef.target_grade)

       _.map projs, (p) ->
-        t = _.find tasks, (t) -> t.project_id == p.project_id && t.task_definition_id == task_def.id
+        t = _.find tasks, (t) -> t.project_id == p.project_id && t.task_definition_id == taskDef.id
         if ! t?
-          _.find p.tasks, (t) -> t.task_definition_id == task_def.id
+          _.find p.tasks, (t) -> t.task_definition_id == taskDef.id
         else
           t


Fixed the nested sortable so it is easier to sort items.

--- app/assets/javascripts/cmsimple/panels/sitemap/sortable.coffee
+++ app/assets/javascripts/cmsimple/panels/sitemap/sortable.coffee
@@ -5,11 +5,15 @@

   setup: ->
     @el.nestedSortable
+              connectWith: 'ul.child'
+              forcePlaceholderSize: true
+              handle: 'article'
+              helper: 'clone'
               items: 'li'
               listType: 'ul'
-              helper: 'clone'
-              connectWith: 'ul.child'
-              update: @proxy @update
+              stop: @proxy @update
+              tolerance: 'pointer',
+              toleranceElement: '> article'

     @el.disableSelection()


Update globbing routes

Update globbing routes

--- app/assets/javascripts/sprangular/static_content/routes.coffee
+++ app/assets/javascripts/sprangular/static_content/routes.coffee
@@ -1,16 +1,16 @@
 angular.module('Sprangular.StaticContent')
   .config ($routeProvider) ->
-    $routeProvider.when '/pages/:id',
+    $routeProvider.when '/pages/:slug*',
       controller: 'PageShowCtrl'
       templateUrl: 'pages/show.html'
       resolve:
         page: (StaticContent, $route)->
-          slug = $route.current.params.id
+          slug = $route.current.params.slug
           StaticContent.find(slug)

-Sprangular.StaticContent = {
+Sprangular.StaticContent =
   addRoutes: (routeProvider) ->
-    routeProvider.when '/:slug',
+    routeProvider.when '/:slug*',
       controller: 'PageShowCtrl'
       templateUrl: 'pages/show.html'
       resolve:
@@ -19,4 +19,3 @@

           StaticContent.find(slug).then (->), ->
             $location.path('/404')
-}


Fixes for IE8

Fixes for IE8

--- app/assets/javascripts/spree/backend/taxon_tree_menu.js.coffee
+++ app/assets/javascripts/spree/backend/taxon_tree_menu.js.coffee
@@ -2,34 +2,20 @@

 root.taxon_tree_menu = (obj, context) ->

-  id = obj.attr("id")
-
-  admin_base_url = Spree.url(Spree.routes.admin_taxonomy_taxons_path)
-
-  edit_url = admin_base_url.clone()
-  edit_url.setPath(edit_url.path() + '/' + id + "/edit");
-
-  translation_url = admin_base_url.clone()
-
-  translation_base_path = admin_base_url.path().replace(/taxons/, "translations")
-  translation_base_path = translation_base_path.replace(/taxonomies/, "taxons")
-  translation_base_path = translation_base_path.replace(/\d+/, id)
-  translation_url.setPath(translation_base_path);
+  base_url = Spree.routes.taxonomy_taxons_path
+  admin_base_url = Spree.routes.admin_taxonomy_taxons_path
+  edit_url = admin_base_url + '/' + obj.attr("id") + "/edit"

   create:
-    label: " " + Spree.translations.add,
+    label: " " + "添加子类",
     action: (obj) -> context.create(obj)
   rename:
-    label: " " + Spree.translations.rename,
+    label: " " + "重命名",
     action: (obj) -> context.rename(obj)
   remove:
-    label: " " + Spree.translations.remove,
+    label: " " + "移除",
     action: (obj) -> context.remove(obj)
   edit:
     separator_before: true,
-    label: " " + Spree.translations.edit,
+    label: " " + "编辑",
     action: (obj) -> window.location = edit_url.toString()
-  translate:
-    separator_before: true,
-    label: " " + Spree.translations.translations,
-    action: (obj) -> window.location = translation_url.toString()


Don't create but add model when setting relation in belongs-to

--- src/associations/belongs_to_relation.coffee
+++ src/associations/belongs_to_relation.coffee
@@ -15,7 +15,7 @@
         @set foreignKey, null
         return
       unless to.all().get(model.id)?
-        to.all().create(model)
+        to.all().add(model, parse: true)
       owner.set foreignKey, model.id

     super


switch to updates view when validation error link clicked

--- app/assets/javascripts/trade/controllers/annual_report_upload_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/annual_report_upload_controller.js.coffee
@@ -147,6 +147,7 @@

     setVisibleShipments: (shipments) ->
       @resetFilters()
+      @set('filtersVisible', false)
       @set('tableController.shipments', shipments)

     saveChanges: () ->


mark deleted records as modified to enable highlighting

--- app/assets/javascripts/trade/controllers/annual_report_upload_controller.js.coffee
+++ app/assets/javascripts/trade/controllers/annual_report_upload_controller.js.coffee
@@ -186,7 +186,7 @@
     deleteSelection: () ->
       @beginPropertyChanges()
       @get('visibleShipments').forEach (shipment) ->
-        shipment.set('_destroyed', true)
+        shipment.setProperties({'_destroyed': true, '_modified': true})
       @endPropertyChanges()
       @resetVisibleShipments()


scroll to bottom when chat list is updated

--- client/app/scripts/comments.coffee
+++ client/app/scripts/comments.coffee
@@ -11,6 +11,10 @@
         @props.text

 commentsList = React.createClass
+  componentDidUpdate: ->
+    node = @getDOMNode()
+    node.scrollTop = node.scrollHeight
+
   render: ->
     nodes = @props.comments.map (c) ->
       comment(c)
@@ -22,6 +26,7 @@
     @props.submit {text: text.state.value}
     text.getDOMNode().value = ""
     false
+
   render: ->
     form className: "comment-form", onSubmit: @submit,
       input type: "text", ref: "text"
@@ -34,13 +39,15 @@
       dataType: 'json'
       success: (comments) =>
         @setState {comments: comments}
-        console.log comments[0]
       error: -> console.error "error"
     )
+
   getInitialState: ->
     {comments: []}
+
   componentWillMount: ->
     @loadComments()
+
   submit: (comment) ->
     comments = @state.comments
     comment.name = @props.name


Some extra voices

Some extra voices


修复本地__pub_font不对的bug

--- honey/index.coffee
+++ honey/index.coffee
@@ -81,7 +81,7 @@
       __img: "/image/"
       __pub_img: "/imgotv-pub/image/"
       __img_demo: "/image-demo/"
-      __pub_font: "/font/"
+      __pub_font: "/imgotv-pub/font/"
       __base_url: "/"
       __pub_css_comp: "/imgotv-pub/css/component/"
       __honey_root: honeyRootDev


GitProvider: if stack template exists with given remote data, return it

--- workers/social/lib/social/models/gitprovider/index.coffee
+++ workers/social/lib/social/models/gitprovider/index.coffee
@@ -66,29 +66,42 @@
       delete importData.template
       delete importData.readme

-      requiredProviders = providersParser template.content
-      requiredData      = requirementsParser template.content
-      config            = {
-        remoteDetails   : importData
-        requiredProviders
-        requiredData
+      JStackTemplate = require '../computeproviders/stacktemplate'
+
+      # FIXME This fields requires index or we need to
+      # find a better way for this one ~ GG
+      selector = {
+        'config.remoteDetails.user': importData.user
+        'config.remoteDetails.repo': importData.repo
       }

-      description = readme.content
-      rawContent  = template.content
+      JStackTemplate.one$ client, selector, (err, stacktemplate) ->

-      convertedDoc = yamlToJson Encoder.htmlDecode template.content
-      if convertedDoc.err
-        return callback new KodingError 'Failed to convert YAML to JSON'
+        if not err and stacktemplate
+          return callback null, stacktemplate

-      { contentObject } = convertedDoc
+        requiredProviders = providersParser template.content
+        requiredData      = requirementsParser template.content
+        config            = {
+          remoteDetails   : importData
+          requiredProviders
+          requiredData
+        }

-      addUserInputOptions contentObject, requiredData
-      config.buildDuration = contentObject.koding?.buildDuration
+        description = readme.content
+        rawContent  = template.content

-      template = convertedDoc.content
-      title  or= "#{importData.repo} Stack"
+        convertedDoc = yamlToJson Encoder.htmlDecode template.content
+        if convertedDoc.err
+          return callback new KodingError 'Failed to convert YAML to JSON'

-      JStackTemplate = require '../computeproviders/stacktemplate'
-      data = { rawContent, template, title, description, config }
-      JStackTemplate.create client, data, callback
+        { contentObject } = convertedDoc
+
+        addUserInputOptions contentObject, requiredData
+        config.buildDuration = contentObject.koding?.buildDuration
+
+        template = convertedDoc.content
+        title  or= "#{importData.repo} Stack"
+
+        data = { rawContent, template, title, description, config }
+        JStackTemplate.create client, data, callback


Closes #31; Fixed real-time line plot thickness

The plot now takes the device pixel ratio into account to ensure that lines are rendered with the same apparent thickness across displays with different pixel ratios. Also changed the range of the y scale so that partial line segments wouldn't be clipped by the edges of the canvas.

--- coffee/time/line.coffee
+++ coffee/time/line.coffee
@@ -7,7 +7,12 @@
     styles = @getStyles "g.#{className.replace(/\s/g,'.')} path.line"
     @ctx.fillStyle = styles.fill
     @ctx.strokeStyle = styles.stroke
-    @ctx.lineWidth = styles['stroke-width'].replace('px', '')
+    @ctx.lineWidth = @pixelRatio * styles['stroke-width'].replace('px', '')
+
+  y: ->
+    d3.scale.linear()
+      .domain(@extent((d) -> d.y))
+      .range([@innerHeight() - @pixelRatio/2, @pixelRatio])

   # Draws the line chart.
   draw: (delta=0) ->


group : getPermissionSet method signature

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -221,6 +221,8 @@
         addSubscription:
           (signature String, Function)
         fetchSubscription:
+          (signature Function)
+        getPermissionSet:
           (signature Function)
     schema          :
       title         :


Updated comments

Updated comments

--- coffee/jquery.tweetscroller.coffee
+++ coffee/jquery.tweetscroller.coffee
@@ -12,7 +12,7 @@
     username: 'divshot'
     count: 50
     speed: 30 # ms, 30-80 ms recommended, slower = better performance
-    autoplay: false # CPU intensive, need to optimize
+    autoplay: false # CPU intensive

   class TweetScroller
     constructor: (@element, options) ->
@@ -68,7 +68,6 @@
             scrollActive = 0
             clearInterval(scrollInterval)
       else
-        # Warning: Autoplay eats up CPU for now (need to optimize)
         scrollInterval = setInterval(autoScroll, @options.speed)

     scrollRefresh: (e) ->


reqorded auto-complete disable input

reqorded auto-complete disable input

--- client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
+++ client/landing/app/Applications/Activity.kdapplication/widgets/statuswidget.coffee
@@ -128,8 +128,8 @@
       pistachio : """

For links, please provide a protocol such as
         http://
-
-        Don't touch my Status Update.<input name="stop-sanitizing" class="stop-sanitizing" type="checkbox" />
+
+        Disable URL auto-complete.<input name="stop-sanitizing" class="stop-sanitizing" type="checkbox" />

       """


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/camera.coffee
+++ test/camera.coffee
@@ -1,4 +1,6 @@
 module "Camera"
+
+

 test "create", ->
   ok Camera()
@@ -8,6 +10,6 @@

   camera = Camera()

-  c
+  camera.trigger 'overlay',
 # Clear out the module
 module()


update test for issue 122 to be failed

update test for issue 122 to be failed

--- test/spec/ecosystem/backbone-relational.tests.coffee
+++ test/spec/ecosystem/backbone-relational.tests.coffee
@@ -985,10 +985,13 @@
         @hasNested = ko.computed => !!@nested()

     view_model = new ViewModel(model = new Model1())
+    assert.ok !view_model.hasNested(), 'not have a nested'
     view_model.addNested()
     assert.ok !!model.get('nested'), 'added a nested'
+    assert.ok view_model.hasNested(), 'added a nested'
     view_model.removeNested()
     assert.ok !model.get('nested'), 'removed a nested'
+    assert.ok !view_model.hasNested(), 'removed a nested'

     kb.release(view_model)


travis debugging

travis debugging

--- test/cucumber/step_definitions/cli_stepdefs.coffee
+++ test/cucumber/step_definitions/cli_stepdefs.coffee
@@ -22,7 +22,7 @@
   Then /^exit status is (\d+)$/, (expectedExitStatus, callback) ->

     cmd = "PATH=$PATH:" + process.cwd() + "/bin; cd ./test/fixtures " + @commandBuffer
-
+    console.error cmd
     child = exec cmd, (error, stdout, stderr) ->
       if error
         unless parseInt(error.code) == parseInt(expectedExitStatus)


Firewall: Copy change.

Firewall: Copy change.

--- client/Environments/views/firewall/addfirewallrulemodal.coffee
+++ client/Environments/views/firewall/addfirewallrulemodal.coffee
@@ -62,7 +62,7 @@
     if data then @createExistingRules() else @createRuleWidget no

     button      = new KDButtonView
-      title     : "Add more rules"
+      title     : "Add more filters"
       icon      : yes
       iconClass : "plus"
       cssClass  : "solid green small add-rule"


Update about page, add me

Update about page, add me

--- client/landing/app/Applications/Home.kdapplication/ContentDisplays/AboutView.coffee
+++ client/landing/app/Applications/Home.kdapplication/ContentDisplays/AboutView.coffee
@@ -132,48 +132,52 @@

#{name}#{job}

         """

-
   theTeam:
     [
       {
-        name      : 'Aleksey Mykhailov'
-        job       : 'Sys Admin & node.js Developer'
-        image     : '../images/people/aleksey.jpg'
+        name      : 'Devrim Yasar'
+        job       : 'Co-Founder & CEO'
+        image     : '../images/people/devrim.jpg'
       },
-      # {
-      #   name      : 'Bob Budd'
-      #   job       : 'Senior Frontend Engineer'
-      #   image     : '../images/people/bob.jpg'
-      # },
       {
-        name      : 'Chris Thorn (w/ Milo)'
+        name      : 'Sinan Yasar'
+        job       : 'Co-Founder'# & Chief UI Engineer'
+        image     : '../images/people/sinan.jpg'
+      },
+      {
+        name      : 'Chris Thorn (w/ Milo)'
         job       : 'Director of Engineering'
         image     : '../images/people/chris.jpg'
       },
       {
-        name      : 'Devrim Yasar'
-        job       : 'Co-Founder & CEO'
-        image     : '../images/people/devrim.jpg'
+        name      : 'Aleksey Mykhailov'
+        job       : 'Sys Admin & node.js Developer'
+        image     : '../images/people/aleksey.jpg'
       },
       {
-        name      : 'Ryan Goodman'
-        job       : 'Director of User Experience'
-        image     : '../images/people/ryan.jpg'
+        name      : 'Gökmen Göksel'
+        job       : 'Software Engineer'
+        image     : '../images/people/gokmen.jpg'
       },
+      # {
+      #   name      : 'Bob Budd'
+      #   job       : 'Senior Frontend Engineer'
+      #   image     : '../images/people/bob.jpg'
+      # },
+      # {
+      #   name      : 'Ryan Goodman'
+      #   job       : 'Director of User Experience'
+      #   image     : '../images/people/ryan.jpg'
+      # },
       # {
       #   name      : 'Saleem Abdul Hamid'
       #   job       : 'Senior node.js Engineer'
       #   image     : '../images/people/saleem.jpg'
       # },
-      {
-        name      : 'Sinan Yasar'
-        job       : 'Co-Founder & Chief UI Engineer'
-        image     : '../images/people/sinan.jpg'
-      },
-      {
-        name      : 'Victor Bucataru'
-        job       : 'C++ Developer'
-        image     : '../images/people/victor.jpg'
-      },
+      # {
+      #   name      : 'Victor Bucataru'
+      #   job       : 'C++ Developer'
+      #   image     : '../images/people/victor.jpg'
+      # },
     ]


save array of user ids

save array of user ids

--- src/scripts/modules/media/tabs/metadata/metadata.coffee
+++ src/scripts/modules/media/tabs/metadata/metadata.coffee
@@ -35,13 +35,13 @@
         select2: () ->
           @$el.find('.keywords > input').val(@model.get(@getModel('keywords')) or [])
           _.extend({}, s2Multi, tags: @model.get(@getModel('keywords')) or [])
-      '.authors':
+      '.authors > input':
         value: () -> @getModel('authors') or []
         type: 'select2'
         select2: () ->
           _.extend {}, s2Multi,
             multiple: true
-            formatResult: (item, $container, query) -> $('

').append(item.full_name)
+            formatResult: (item, $container, query) -> $('

').append(item.text)
             ajax:
               url: "#{window.location.origin}/users/search"
               dataType: 'json'
@@ -49,7 +49,8 @@
                 q: term # search term

               results: (data, page) -> # parse the results into the format expected by Select2.
-                results: data.users
+                results: _.map data.users, (item) ->
+                  {id:item.id, text:item.full_name or item.username}

     initialize: () ->
       super()