OX-603 Fix hasChanged in Plane2d

OX-603 Fix hasChanged in Plane2d

--- app/assets/javascripts/oxalis/model/binary/plane2d.coffee
+++ app/assets/javascripts/oxalis/model/binary/plane2d.coffee
@@ -88,7 +88,7 @@

   hasChanged : ->

-    @changed
+    not (@dataTexture.ready and @volumeTexture.ready)

   getImpl : (position, zoomStep, area) ->


Fixed zoom level bug related to links with no number of lanes set

--- src/main/webapp/lib/views/MapLinkView.coffee
+++ src/main/webapp/lib/views/MapLinkView.coffee
@@ -289,7 +289,7 @@
     numLines = @model.lanes()
     zoomLevel = $a.map.getZoom()
     if (zoomLevel >= 17)
-      lineWidth = if numLines > 5 then 5 else numLines
+      lineWidth = if numLines > 5 then 5 else 3
     else if (zoomLevel >= 16)
       lineWidth = $a.MapLinkView.STROKE_WEIGHT_THIN
     else


Login: show hide logic changed / outside click handler removed

--- client/Login/AppView.coffee
+++ client/Login/AppView.coffee
@@ -365,51 +365,36 @@

   hide:(callback)->

-    @setY -KD.getSingleton('windowController').winHeight
-
-    cb = =>
-      @$('.flex-wrapper').removeClass 'expanded'
-
-      @emit "LoginViewHidden"
-      @hidden = yes
-      @hideTimer = @utils.wait 2000, => @setClass 'hidden'
-      callback?()
-
-    unless @hidden then do cb
-    else @once "transitionend", cb
+    @$('.flex-wrapper').removeClass 'expanded'
+    @emit "LoginViewHidden"
+    @setClass 'hidden'
+    callback?()

     KD.mixpanel "Cancelled Login/Register"

   show:(callback)->

-    @utils.killWait @hideTimer
-    cb = =>
-      @emit "LoginViewShown"
-      @hidden = no
-      callback?()
-
     @unsetClass 'hidden'
-    @utils.defer =>
-      @setY 0
-      unless @hidden then do cb
-      else @once "transitionend", cb
-
-  click:(event)->
-    if $(event.target).is('.login-screen')
-      @hide ->
-        router = KD.getSingleton('router')
-        routed = no
-        for route in router.visitedRoutes by -1
-          {entryPoint} = KD.config
-          routeWithoutEntryPoint =
-            if entryPoint?.type is 'group' and entryPoint.slug
-            then route.replace "/#{entryPoint.slug}", ''
-            else route
-          unless routeWithoutEntryPoint in ['/Login', '/Register', '/Recover', '/ResendToken']
-            router.handleRoute route
-            routed = yes
-            break
-        router.clear()  unless routed
+    @emit "LoginViewShown"
+    @hidden = no
+    callback?()
+
+  # click:(event)->
+  #   if $(event.target).is('.login-screen')
+  #     @hide ->
+  #       router = KD.getSingleton('router')
+  #       routed = no
+  #       for route in router.visitedRoutes by -1
+  #         {entryPoint} = KD.config
+  #         routeWithoutEntryPoint =
+  #           if entryPoint?.type is 'group' and entryPoint.slug
+  #           then route.replace "/#{entryPoint.slug}", ''
+  #           else route
+  #         unless routeWithoutEntryPoint in ['/Login', '/Register', '/Recover', '/ResendToken']
+  #           router.handleRoute route
+  #           routed = yes
+  #           break
+  #       router.clear()  unless routed

   animateToForm: (name)->


badge controller added to includes

badge controller added to includes

--- client/Main/includes.coffee
+++ client/Main/includes.coffee
@@ -148,6 +148,8 @@
   # LOCATION
   "locationcontroller.coffee",
   "CommonViews/location/locationform.coffee",
+  # BadgeController
+  "badgecontroller.coffee",

   # PAYMENT
   # controller


Snapshots: Handle snapshot resurrection for permanent machines.

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -478,7 +478,7 @@

             # Just resurrect snapshot for host or without collaboration.
             # Because we need check the `@myWatchMap` and it is not possible here.
-            if snapshot and @amIHost
+            if (snapshot and @amIHost) or @mountedMachine.isPermanent()
               return @layoutManager.resurrectSnapshot snapshot

             # Be quiet. Don't write initial views's changes to snapshot.


IDEAppController: quit: add beforeQuit helper, and call quit when needed

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -1546,21 +1546,23 @@
       delete @modal

-  quit: ->
+  quit: (destroy = yes) ->

     return  if @getView().isDestroyed

-    @emit 'IDEWillQuit'
+    @emit 'IDEWillQuit'  if destroy

     @mountedMachine?.getBaseKite(createIfNotExists = no).disconnect()
-
     @stopCollaborationSession()
-    kd.singletons.appManager.quit this
-
-    kd.utils.defer ->
-      kd.singletons.router.handleRoute '/IDE'
-
-    @once 'KDObjectWillBeDestroyed', @lazyBound 'emit', 'IDEDidQuit'
+
+    if destroy
+      kd.singletons.appManager.quit this
+      kd.utils.defer ->
+        kd.singletons.router.handleRoute '/IDE'
+      @once 'KDObjectWillBeDestroyed', @lazyBound 'emit', 'IDEDidQuit'
+
+
+  beforeQuit: -> @quit no

   removeParticipantCursorWidget: (targetUser) ->


styleguide: Corrected error string

styleguide: Corrected error string

--- client/ide/lib/index.coffee
+++ client/ide/lib/index.coffee
@@ -358,7 +358,7 @@
   openFiles: (files) ->

     unless files
-      return kd.error "IDEAppController#openFiles: Called with empty files"
+      return kd.error "IDEAppController.openFiles: Called with empty files"

     files.forEach (path) =>
       file = FSHelper.createFileInstance { path, machine: @mountedMachine }


Removes empty constructor on EmailValidator

--- src/model/validations/email_validator.coffee
+++ src/model/validations/email_validator.coffee
@@ -6,9 +6,6 @@
   # WebKit's email validation regexp
   emailRegexp: /[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*/

-  constructor: (options) ->
-    super
-
   validateEach: (errors, record, key, callback) ->
     value = record.get(key)
     if !value? || value == '' || !@emailRegexp.test(value)


Optional Authorization token closes #279

--- wzk/net/AuthMiddleware.coffee
+++ wzk/net/AuthMiddleware.coffee
@@ -7,6 +7,13 @@
   ###
   @HEADER:
     AUTH: 'Authorization'
+    CSRF: 'X-CsrfToken'
+
+  ###*
+    @enum {string}
+  ###
+  @COOKIES:
+    CSRF: 'csrftoken'

   ###*
     @param {Document} doc
@@ -19,7 +26,11 @@
     @param {Object} headers
   ###
   apply: (headers) ->
-    auth  = @cookies.get wzk.net.AuthMiddleware.HEADER.AUTH
-    if auth?
-      headers[wzk.net.AuthMiddleware.HEADER.AUTH] = auth
+    csrf = @cookies.get wzk.net.AuthMiddleware.COOKIES.CSRF
+    if csrf
+      headers[wzk.net.AuthMiddleware.HEADER.CSRF] = csrf
+    else
+      auth  = @cookies.get wzk.net.AuthMiddleware.HEADER.AUTH
+      if auth?
+        headers[wzk.net.AuthMiddleware.HEADER.AUTH] = auth
     undefined # Coffee & Closure


Update attr to data

Update attr to data

You can use .data() func instead of attr to manage all data-* attr in DOM

--- app/assets/javascripts/adminpanel/spinner.js.coffee
+++ app/assets/javascripts/adminpanel/spinner.js.coffee
@@ -1,5 +1,5 @@
 $(document).on 'click', 'a.accordion-toggle', ->
-  $( this ).attr('data-clicked', true)
+  $( this ).data('clicked', true)

 $(document).on 'page:fetch', ->
   $('a.accordion-toggle[data-clicked="true"]').children('i:first').toggleClass('hidden')
@@ -8,4 +8,4 @@
 $(document).on 'page:restore', ->
   $('a.accordion-toggle[data-clicked="true"]').children('i:first').remove()
   $('a.accordion-toggle[data-clicked="true"]').children('i:first').toggleClass 'hidden'
-  $('a.accordion-toggle[data-clicked="true"]').attr 'data-clicked', false
+  $('a.accordion-toggle[data-clicked="true"]').data 'clicked', false


Fix show password id

Fix show password id

--- app/assets/javascripts/form/show_password.js.coffee
+++ app/assets/javascripts/form/show_password.js.coffee
@@ -2,7 +2,7 @@
 #
 class SublimeVideo.Form.ShowPassword
   constructor: (@field, @index) ->
-    @showPasswordCheckbox = jQuery('<input />', type: 'checkbox', id: 'show_password_#{@index}')
+    @showPasswordCheckbox = jQuery('<input />', type: 'checkbox', id: "show_password_#{@index}")

     this.storeThisInData()
     this.insertShowPasswordCheckbox()
@@ -18,7 +18,7 @@
   #
   insertShowPasswordCheckbox: ->
     showPasswordWrap  = jQuery('<div>', class: 'checkbox_wrap')
-    showPasswordLabel = jQuery('<label />', for: 'show_password_#{@index}').text 'Show password'
+    showPasswordLabel = jQuery('<label />', for: "show_password_#{@index}").text 'Show password'
     showPasswordWrap.append(@showPasswordCheckbox).append(showPasswordLabel)

     this.injectShowPasswordCheckboxInDom(showPasswordWrap)


default photo if error

default photo if error

--- app/assets/javascripts/helpers/photo_builder.coffee
+++ app/assets/javascripts/helpers/photo_builder.coffee
@@ -16,9 +16,14 @@
     deferred = Q.defer()
     _500px.api "/photos/#{@grabPhotoId()}", (response) =>
       if response.error
-        console.log response.error_message
-        deferred.reject(response.err)
-      deferred.resolve(city: @city, data: @cityPhotoObject(response.data.photo))
+        console.log @city + ": " + response.error_message
+        _500px.api "/photos/#{@defaultPhoto}", (response) =>
+          if response.error
+            deferred.reject(response.err)
+          else
+            deferred.resolve(city: @city, data: @cityPhotoObject(response.data.photo))
+      else
+        deferred.resolve(city: @city, data: @cityPhotoObject(response.data.photo))
     return deferred.promise

   cityPhotoObject: (obj) ->


Check workflow exists

Check workflow exists

--- app/classifier/tasks/drawing/markings-renderer.cjsx
+++ app/classifier/tasks/drawing/markings-renderer.cjsx
@@ -42,7 +42,7 @@
         taskDescription = @props.workflow.tasks[annotation.task]
         if taskDescription.type is 'drawing'
           <g key={annotation._key} className="marks-for-annotation" data-disabled={isPriorAnnotation || null}>
-            {for mark, i in annotation.value when @props.workflow.configuration.multi_image_clone_markers or parseInt(mark.frame) is parseInt(@props.frame)
+            {for mark, i in annotation.value when @props.workflow?.configuration.multi_image_clone_markers or parseInt(mark.frame) is parseInt(@props.frame)
               mark._key ?= Math.random()

               if skippedMarks < @props.classification._hideMarksBefore and not @props.classification.completed


getQuestions may not be in use, but we shouldn't ship it broken

--- app/classifier/tasks/flexible-survey/utility.coffee
+++ app/classifier/tasks/flexible-survey/utility.coffee
@@ -4,5 +4,5 @@
     return task.questionsOrder unless task.questionsMap? and choiceID of task.questionsMap
     task.questionsMap[choiceID]
   getQuestions: (task, choiceID) ->
-    @getQuestionIDs(task, choiceID).map ((idx) ->
-      task.questions[idx])
+    @getQuestionIDs(task, choiceID).map (idx) ->
+      task.questions[idx]


remove RCE reference from profiles

remove RCE reference from profiles

closes CNVS-27769

TEST PLAN:
 1) no behavior change
 2) profile editing should not break

Change-Id: I0536ffaeaf8aa69dff614d4531dfe27121269d9d
Reviewed-on: https://gerrit.instructure.com/73798
Reviewed-by: Jacob Fugal <f862f167b85d41b225785c70d70808bc7337c1fe@instructure.com>
Tested-by: Jenkins
QA-Review: Jeremy Putnam <b030d6d6f199f844ec424882be473c2ab9adc93b@instructure.com>
Product-Review: Ethan Vizitei <73eeb0b2f65d05a906adf3b21ee1f9f5a2aa3c1c@instructure.com>

--- app/coffeescripts/views/profiles/ProfileShow.coffee
+++ app/coffeescripts/views/profiles/ProfileShow.coffee
@@ -24,7 +24,6 @@
   'compiled/util/AvatarWidget'
   'compiled/tinymce'
   'jquery.instructure_forms'
-  'tinymce.editor_box'
 ], (I18n, Backbone, $, addLinkRow, AvatarWidget) ->

   class ProfileShow extends Backbone.View
@@ -75,8 +74,6 @@
         @addLinkField()
         @addLinkField()

-      # setTimeout so tiny has some width to read
-      #setTimeout -> @$('#profile_bio').editorBox()
       @showEditForm()

     cancelEditProfile: ->


allow to pass in sandbox through settings

--- src/eval-cs.coffee
+++ src/eval-cs.coffee
@@ -28,7 +28,7 @@
   language                  = settings?.language ? 'coffee'
   local_filename            = '<STRING>'
   macro_output              = []
-  sandbox                   = {}
+  sandbox                   = settings?.sandbox ? {}
   # sandbox_backup            = MK.TS.DIFFPATCH.snapshot sandbox
   VM.createContext sandbox
   #.........................................................................................................


Add file recieving functionality

Add file recieving functionality

--- coffee/routes/routes.coffee
+++ coffee/routes/routes.coffee
@@ -1,7 +1,15 @@
 fs = require 'fs'

 module.exports = (app) ->
-  app.get '/', (req, res) ->
-    res.send "Upload something!"
+  app.get '/', (request, response) ->
+    response.send "Upload something!"

+  app.post '/submit', (request, response) ->
+    req.pipe(req.busboy)
+    req.busboy.on('file', (fieldname, file, filename) ->
+      console.log "Got #{filename}"
+      fileStream = fs.createWriteStream "./sounds/#{filename}"
+      file.pipe fileStream
+      fileStream.on 'close', ->
+        console.log "Upload of #{filename} complete!"


Added the exports for each method.

Added the exports for each method.

--- src/fogbugz.coffee
+++ src/fogbugz.coffee
@@ -217,4 +217,14 @@
 exports.LogOff = LogOff
 exports.SetURL = SetURL
 exports.ListProjects = ListProjects
-exports.SearchCases = SearchCases+exports.SearchCases = SearchCases
+exports.FowardCase = FowardCase
+exports.ReplyCase = ReplyCase
+exports.EmailCase = EmailCase
+exports.CloseCase = CloseCase
+exports.ResolveCase = ResolveCase
+exports.ReopenCase = ReopenCase
+exports.ReactivateCase = ReactivateCase
+exports.AssignCase = AssignCase
+exports.EditCase = EditCase
+exports.NewCase = NewCase


Update config.cson

Update config.cson

--- files/atom/config.cson
+++ files/atom/config.cson
@@ -109,6 +109,7 @@
   "one-dark-ui":
     fontSize: 14
     tabCloseButton: "Left"
+    tabSizing: "Maximum"
   organized: {}
   parinfer:
     "file-extensions": [


Menos paréntesis

Menos paréntesis

--- spec/javascripts/jquery.paginator_spec.coffee
+++ spec/javascripts/jquery.paginator_spec.coffee
@@ -3,7 +3,7 @@

 describe "Paginator", ->
   beforeEach ->
-    loadFixtures('paginator.html')
+    loadFixtures 'paginator.html'
     $('#list').ajaxPaginator()

   describe 'clicking a link', ->
@@ -33,6 +33,6 @@

     afterEach ->
       if browser_supports_history()
-        history.replaceState(null, null, '/')
+        history.replaceState null, null, '/'
       else
         location.hash = null


Correct spelling

Correct spelling

--- scripts/bungie.coffee
+++ scripts/bungie.coffee
@@ -70,7 +70,7 @@
         deferred.promise()
         itemCategoriesDefers.push deferred

-      Promice.all(itemCategoriesDefers).done (bountyItems) ->
+      Promise.all(itemCategoriesDefers).done (bountyItems) ->
         console.log 'bountyItems = '
         console.log bountyItems


Cover helper. First step towards shifting all responsibility for cover files to assets

--- bookmaker/epub.coffee
+++ bookmaker/epub.coffee
@@ -103,7 +103,17 @@
 extendBook = (Book) ->
   Book.prototype.init = [] unless Book.prototype.init
   Book.prototype.init.push((book) -> handlebars.registerHelper 'relative', book.relative.bind(book))
+  Book.prototype.init.push((book) -> handlebars.registerHelper 'isCover', book.isCover.bind(book))
   Book.prototype.relative = (current, target) ->
+    absolutecurrent = path.dirname path.resolve("/", current)
+    absolutetarget = path.resolve("/", target)
+    relativetarget = path.relative(absolutecurrent, absolutetarget)
+    return relativetarget
+  Book.prototype.isCover = (path) ->
+    if @meta.cover = path
+      return new handlebars.SafeString(' properties="cover-image"')
+    else
+      return ""
     absolutecurrent = path.dirname path.resolve("/", current)
     absolutetarget = path.resolve("/", target)
     relativetarget = path.relative(absolutecurrent, absolutetarget)


Add a mods command for Skyrim.

Add a mods command for Skyrim.

--- scripts/common.coffee
+++ scripts/common.coffee
@@ -39,3 +39,6 @@

     robot.respond /(donate|support)$/i, (msg) ->
         msg.send "If you really want to, you can support the stream at https://imraising.tv/u/masonest — please know that it’s never expected or required but will always be appreciated avalonLOVE"
+
+    robot.respond /mods$/i, (msg) ->
+        msg.send "Take a look at http://bit.ly/1E3hGeu to see a current lost of the Skyrim mods we're using."


add option to dinner command

add option to dinner command

--- scripts/dinner.coffee
+++ scripts/dinner.coffee
@@ -1,10 +1,21 @@
-# Choice dinner
+# Choice dinner
 #
 # hubot dinner add <restraint> - Add restraint
 # hubot dinner choice - Choice dinner by Hubot
-# hubot dinner clear - Clear dinner data
+# hubot dinner clear - Clear all dinner data
+# hubot dinner delete - Delete specific dinner data
+# hubot dinners - List dinner

 module.exports = (robot) ->
+  robot.respond /dinner$/i, (msg) ->
+    robot.brain.data.restraints ?= []
+
+    if robot.brain.data.restraints.length > 0
+      restraint = msg.random robot.brain.data.restraints
+      msg.send restraint.name
+    else
+      msg.send "I don't know"
+
   robot.respond /dinner add (.+)/i, (msg) ->
     restraint = msg.match[1]

@@ -13,15 +24,33 @@

     msg.send 'Got it! Added: ' + restraint

+  robot.respond /dinners/i, (msg) ->
+    robot.brain.data.restraints ?= []
+
+    if robot.brain.data.restraints.length > 0
+      for restraint in robot.brain.data.restraints
+         msg.send restraint.name
+    else
+      msg.send "I don't know"
+
+  robot.respond /dinner (delete|remove) (.*) ?$/i, (msg) ->
+    restraint_name = msg.match[2]
+
+    robot.brain.data.restraints ?= []
+    length_before = robot.brain.data.restraints.length
+
+    index_of = -1
+    for restraint, i in robot.brain.data.restraints
+      if restraint.name == restraint_name
+        index_of = i
+
+    robot.brain.data.restraints.splice( index_of, 1 ) if -1 != index_of
+
+    if length_before > robot.brain.data.restraints.length
+      msg.send 'Removed: ' + restraint_name
+    else
+      msg.send "I couldn't find that restraint."
+
   robot.respond /dinner (clear|flush)/i, (msg) ->
     robot.brain.data.restraints = []
     msg.send "All dinner data cleared."
-
-  robot.respond /dinner choice$/i, (msg) ->
-    robot.brain.data.restraints ?= []
-
-    if robot.brain.data.restraints.length > 0
-      restraint = msg.random robot.brain.data.restraints
-      msg.send restraint.name
-    else
-      msg.send "I don't know"


extra quote

extra quote

--- scripts/dr-mat.coffee
+++ scripts/dr-mat.coffee
@@ -37,7 +37,9 @@
   "Choose your enemies wisely they will be with you longer than your friends",
   "What has the world become to if an honest man cannot burn corpses without a suspicion",
   "Death awaits us all",
-  "Flesh may forget, but blade never does"
+  "Flesh may forget, but blade never does",
+  "The purpose of life is not to be happy it is to be useful, to be honorable, to be compassionate, to have make some difference that you have lived and lived well.",
+  "Time's a b*£$$"
 ]

 module.exports = (robot) ->


Change mumble CD to 1 minutes

Change mumble CD to 1 minutes

--- scripts/mumble.coffee
+++ scripts/mumble.coffee
@@ -2,9 +2,7 @@
 fs = require('fs')

 module.exports = (robot) ->
-
   timeMap = {}
-
   options =
     key: fs.readFileSync './certs/private_key.pem'
     cert: fs.readFileSync './certs/cert.pem'
@@ -20,7 +18,7 @@
     cli.on 'user-move', (user) ->
       if user.channel.name == 'Games'
         currentTime = Date.now()
-        if !timeMap[user.name] || (currentTime - timeMap[user.name]) > 300000
+        if !timeMap[user.name] || (currentTime - timeMap[user.name]) > 60000
             timeMap[user.name] = currentTime
             robot.adapter.send {}, "#{user.name} wants to play games!"


Testing brain.data outputs.

Testing brain.data outputs.

--- scripts/points.coffee
+++ scripts/points.coffee
@@ -122,11 +122,20 @@
     #
     #         msg.send "The top #{pointcount} users with the most points are: #{topscore}"

-    robot.respond /scores$/i, (msg) ->
+    robot.respond /output$/i, (msg) ->
         if robot.auth.hasRole(msg.envelope.user, ['admin', 'moderator'])

             # Returns active users only with their points
             # score = ["#{username} has #{points[username]}" for username in people]

-            score = ["#{username} has #{points[username]}" for username in people by +1]
-            msg.send "Stored scores: #{score}"
+            # Also only returns active users at the moment
+            # score = ["#{username} has #{points[username]}" for points in robot.brain.data.points]
+
+            output1 = [robot.brain.data.points]
+            output2 = [robot.brain.data.points.usernames]
+            output3 = [robot.brain.data.points.points]
+            output4 = [robot.brain.data.points.usernames.points]
+
+            outputs = ["Output 1: #{output1}, Output 2: #{output2}, Output 3: #{output3}, Output 4: #{output4}"]
+
+            msg.send "Brain output tests: #{scores}"


Fix weather issue

Fix weather issue

--- scripts/yamada.coffee
+++ scripts/yamada.coffee
@@ -81,9 +81,13 @@
                           .get()
      request (err, res, body) ->
        json = JSON.parse body
+       console.log(json)
+       console.log(json['forecasts'][0]['temperature'])
        weather = json['forecasts'][0]['telop']
-       max_temperature =  json['forecasts'][0]['temperature']['max']['celsius']
-       msg.send "今日の天気は#{weather}ってとこだな。最高気温は#{max_temperature}度らしいよ。"
+       max_temperature =  json['forecasts'][0]['temperature']['max']
+       message = "今日の天気は#{weather}ってとこだな。"
+       message += "最高気温は#{max_temperature['celsius']}度らしいよ。" if max_temperature?
+       msg.send message

    # example for scraping
    robot.respond /yahoo-news/i, (msg) ->


Try compiling all files

Try compiling all files

--- src/install.coffee
+++ src/install.coffee
@@ -481,7 +481,8 @@
           path.basename(directoryPath) isnt 'node_modules'

         onFile = (filePath) ->
-          CoffeeCache.addPathToCache(filePath)
+          try
+            CoffeeCache.addPathToCache(filePath)

         fs.traverseTreeSync(packageDirectory, onFile, onDirectory)
       callback(null)


Typo fix

Typo fix

--- app/collections/collaborators.cjsx
+++ app/collections/collaborators.cjsx
@@ -16,7 +16,7 @@
 ROLES_INFO =
   owner:
     label: "Owner"
-    description: "The colleciton creator. There can only be one."
+    description: "The collection creator. There can only be one."
   collaborator:
     label: "Collaborator"
     description: "Collaborators have full access to add and remove subjects from the collection."


Cache game data in localStorage

Cache game data in localStorage

--- index.cjsx
+++ index.cjsx
@@ -38,6 +38,7 @@
   'poi.notify.morale.audio': 27
   'plugin.prophet.notify.damagedAudio': 21
 }
+LOCALSTORAGE_DATA_KEY = "secretaryData"

 zerofill = (n) ->
@@ -55,13 +56,19 @@

 SecretaryArea = React.createClass
   getInitialState: ->
+    # Load game data from localStorage.
+    try
+      {ships, shipgraph} = JSON.parse localStorage[LOCALSTORAGE_DATA_KEY]
+    catch
+      ships = null
+      shipgraph = null
+
     # 0=fleet secretary, -1=disable, 1~*=$ships[]
     notifySecretary: config.get('plugin.secretary.ship', 0)
     fleetSecretary: null
     # Game data
-    isLogin: false
-    ships: []
-    shipgraph: []
+    ships: ships  # Index by sortno
+    shipgraph: shipgraph
   componentDidMount: ->
     window.addEventListener 'game.response', @handleResponse
     window.addEventListener 'secretary.load', @pluginDidLoad
@@ -91,8 +98,9 @@
         shipgraph = []
         for ship in body.api_mst_shipgraph
           shipgraph[ship.api_id] = ship
+
+        localStorage[LOCALSTORAGE_DATA_KEY] = JSON.stringify {ships, shipgraph}
         @setState
-          isLogin: true
           ships: ships
           shipgraph: shipgraph
       when '/kcsapi/api_port/port', '/kcsapi/api_get_member/deck', '/kcsapi/api_req_hensei/change'
@@ -163,7 +171,7 @@
       <Grid>
         <Col xs={12}>
         {
-          if @state.isLogin
+          if @state.ships?
             options = []
             if @state.notifySecretary == -1
               options.push(
@@ -182,7 +190,7 @@
               </option>
             )
             for ship, i in @state.ships
-              continue unless ship?.api_sortno
+              continue unless ship?
               options.push(
                 <option key={i} value={ship.api_id}>
                   No.{zerofill ship.api_sortno} {__r ship.api_name}


Add event pass

Add event pass

--- index.cjsx
+++ index.cjsx
@@ -91,8 +91,8 @@
           console.error err
   # Drop ship report
   window.addEventListener 'battle.result', async (e) ->
-    {rank, boss, map, mapCell, quest, enemy, dropShipId, enemyShipId, enemyFormation} = e.detail
-    {_teitokuLv} = window
+    {rank, boss, map, mapCell, quest, enemy, dropShipId, enemyShipId, enemyFormation, getEventItem} = e.detail
+    {_teitokuLv, _nickNameId} = window
     info =
       shipId: dropShipId
       quest: quest
@@ -113,6 +113,20 @@
           'User-Agent': 'Reporter v2.1.0'
     catch err
       console.error err
+    if getEventItem
+      info =
+        teitokuId: _nickNameId
+        teitokuLv: _teitokuLv
+        mapId: map
+        mapLv: mapLv[map] or 0
+      try
+        yield request.postAsync "http://#{SERVER_HOSTNAME}/api/report/v2/pass_event",
+          form:
+            data: JSON.stringify info
+          headers:
+            'User-Agent': 'Reporter v2.1.0'
+      catch err
+        console.error err

 module.exports =
   name: 'Reporter'


Remove remote update error's alert temporarily

--- index.cjsx
+++ index.cjsx
@@ -43,8 +43,8 @@
     err = yield fs.writeFileAsync subtitlesFile, data
     console.error err if err
     subtitles = JSON.parse data
-    window.warn "语音字幕自动更新失败，请联系有关开发人员，并手动更新插件以更新字幕数据",
-      stickyFor: 5000
+    # window.warn "语音字幕自动更新失败，请联系有关开发人员，并手动更新插件以更新字幕数据",
+    #   stickyFor: 5000
     return
   window.success "语音字幕数据更新成功(#{subtitles.version})",
     stickyFor: 3000


add annotation

add annotation

--- index.cjsx
+++ index.cjsx
@@ -66,12 +66,6 @@
   __("Air Denial")
 ]

-initHp =
-  now: [0, 0, 0, 0, 0, 0]
-  max: [0, 0, 0, 0, 0, 0]
-  dmg: [0, 0, 0, 0, 0, 0]
-  atk: [0, 0, 0, 0, 0, 0]
-
 initPlaneCount =
   seiku: -1
   sortie: [0, 0]
@@ -84,9 +78,9 @@
     @hp =
       now: 0
       max: 0
-      injure: 0
-      damage: 0
-      dayInjure: 0
+      injure: 0 # the injure value
+      damage: 0  # the damage value that it makes
+      dayInjure: 0 # the daytime injure value has been got
     @id = -1
     @lv = -1
     @cond = 0
@@ -94,7 +88,7 @@
     @slot = []
     # owner: 0 ours 1 enemy
     @owner = 1
-    @back = 0
+    @back = 0 # 1: be taken to port

 class Fleet


adding isOpen to the js driver

adding isOpen to the js driver

--- drivers/javascript/net.coffee
+++ drivers/javascript/net.coffee
@@ -250,6 +250,9 @@
     use: ar (db) ->
         @db = db

+    isOpen: () ->
+        @open
+
     _start: (term, cb, opts) ->
         unless @open then throw new err.RqlDriverError "Connection is closed."


fixed bug in publish.

fixed bug in publish.

--- server/publish.coffee
+++ server/publish.coffee
@@ -47,9 +47,9 @@
   if user_id?
     handle = null
     q = null
+    ids = []
     first_action = (item, idx) =>
       # gather ids of my posts and posts i've replied to
-      ids = []
       ids.push( item['parent_id'] ) if 'parent_id' of item
       ids.push( item['_id'] )


Draggable: Fixed typo which caused runtime error.

--- src/base/DragDropSelect/DragDropSelect.coffee
+++ src/base/DragDropSelect/DragDropSelect.coffee
@@ -29,7 +29,7 @@
 		@element = @_element
 		DragDropSelect.getInstance(@element, @cls)?.destroy()
 		DOM.data(@element, "drag-drop-select-"+@cls, @)
-		DOM.addClass(@element, @__getClass())
+		DOM.addClass(@element, @getClass())

 	getClass: ->
 		"cui-drag-drop-select cui-drag-drop-select-"+@cls


Fixed wrong icon for multiple playable cards

--- static/script/games/settlers/settlers.coffee
+++ static/script/games/settlers/settlers.coffee
@@ -39,7 +39,7 @@

 '
 window.settlers.templates.game.cards_can_be_used_cell = doT.template '
-   0}}id="card_{{= it[0].id}}"{{?}}>{{? it.length > 0}}
 1}}icon-documents{{??}}icon-document{{?}} card card-unused" />{{= it.length}}x{{?}}
+   0}}id="card_{{= it[0].id}}"{{?}}>{{? it.length > 0}}
 1}}icon-docs{{??}}icon-document{{?}} card card-unused" />{{= it.length}}x{{?}}
 '
 window.settlers.templates.game.cards_free_cell = doT.template '
   {{? it.length > 0}}
 1}}icon-docs{{??}}icon-document{{?}} card card-free" />{{= it.length}}x{{?}}


add failing test for #407

add failing test for #407

--- spec/BaconSpec.coffee
+++ spec/BaconSpec.coffee
@@ -2042,6 +2042,14 @@
     expectPropertyEvents(
       -> Bacon.combineAsArray(Bacon.constant(1), 2, 3)
     [[1,2,3]])
+  describe "works with synchronous sources and flatMap (#407)", ->
+    expectStreamEvents(
+      ->
+        Bacon
+          .once(123)
+          .flatMap ->
+              Bacon.combineAsArray(Bacon.once(1), Bacon.once(2), 3)
+    [[1,2,3]])
   it "preserves laziness", ->
     calls = 0
     id = (x) ->


Add sharing API

Add sharing API

--- src/api.coffee
+++ src/api.coffee
@@ -200,6 +200,71 @@
                 callback()

+    # Public: Create a Sharing and send request to all the targets
+    # Warning : Your app need to ask the 'Sharing' permission
+    # data - {Object}
+    #   :rules - [rule], composed of id and docType fields
+    #   :targets - [target], composed of recipientUrl fields
+    #   :continuous - {boolean}, sync sharing
+    #   :desc - {String}, human-readable description
+    # callback - {Function}({Error} err)
+    #
+    # Returns null
+    createSharing: (data, callback) ->
+        client.post "services/sharing/", data, (err, res, body) ->
+            callback err, body
+
+
+    # Public: Answer a Sharing request
+    # Warning : Your app need to ask the 'Sharing' permission
+    # data - {Object}
+    #   :id - {String}, id of the recipient's Sharing document
+    #   :accepted - {boolean}
+    # callback - {Function}({Error} err)
+    #
+    # Returns null
+    answerSharing: (data, callback) ->
+        client.post "services/sharing/sendAnswer", data, (err, res, body) ->
+            callback err, body
+
+
+    # Public: Revoke a Sharing from the sharer side
+    # Warning : Your app need to ask the 'Sharing' permission
+    # id - {String}, id of the sharer's Sharing document, also called shareID
+    # callback - {Function}({Error} err)
+    #
+    # Returns null
+    revokeSharingFromSharer: (id, callback) ->
+        path = "services/sharing/sharer/#{id}"
+        client.post path, {}, (err, res, body) ->
+            callback err, body
+
+
+    # Public: Revoke a target from the sharer side
+    # Warning : Your app need to ask the 'Sharing' permission
+    # id - {String}, id of the sharer's Sharing document, also called shareID
+    # target - {String}, url of the target
+    # callback - {Function}({Error} err)
+    #
+    # Returns null
+    revokeSharingTargetFromSharer: (id, target, callback) ->
+        path = "services/sharing/sharer/#{id}/#{target}/"
+        client.post path, {}, (err, res, body) ->
+            callback err, body
+
+
+    # Public: Revoke a Sharing from the recipient side
+    # Warning : Your app need to ask the 'Sharing' permission
+    # id - {String}, id of the recipient's Sharing document
+    # callback - {Function}({Error} err)
+    #
+    # Returns null
+    revokeSharingFromRecipient: (id, callback) ->
+        path = "services/sharing/target/#{id}"
+        client.post path, {}, (err, res, body) ->
+            callback err, body
+
+
 module.exports = api = new Api()
 module.exports.setupModels = setupModels
 module.exports.CozyInstance = CozyInstance


Remove deprecated App.require methods.

Remove deprecated App.require methods.

--- src/app.coffee
+++ src/app.coffee
@@ -27,47 +27,6 @@
   @classAccessor 'controllers', -> @get('dispatcher.controllers')

   @classAccessor '_renderContext', -> Batman.RenderContext.base.descend(@)
-
-  # Require path tells the require methods which base directory to look in.
-  @requirePath: ''
-
-  # The require class methods (`controller`, `model`, `view`) simply tells
-  # your app where to look for coffeescript source files. This
-  # implementation may change in the future.
-  Batman.developer.do =>
-    requireDeprecated = "Please use whatever means you'd like to load your code before calling App.run."
-
-    App.require = (path, names...) ->
-      Batman.developer.deprecated("App.require", requireDeprecated)
-      base = @requirePath + path
-      for name in names
-        @prevent 'run'
-
-        path = base + '/' + name + '.coffee'
-        new Batman.Request
-          url: path
-          type: 'html'
-          success: (response) =>
-            CoffeeScript.eval response
-            @allow 'run'
-            if not @isPrevented 'run'
-              @fire 'loaded'
-
-            @run() if @wantsToRun
-      @
-
-    @controller = (names...) ->
-      Batman.developer.deprecated("App.controller", requireDeprecated)
-      names = names.map (n) -> n + '_controller'
-      @require 'controllers', names...
-
-    @model = ->
-      Batman.developer.deprecated("App.model", requireDeprecated)
-      @require 'models', arguments...
-
-    @view = ->
-      Batman.developer.deprecated("App.view", requireDeprecated)
-      @require 'views', arguments...

   # Layout is the base view that other views can be yielded into. The
   # default behavior is that when `app.run()` is called, a new view will


string tests

string tests

--- spec/core.spec.coffee
+++ spec/core.spec.coffee
@@ -58,16 +58,6 @@
       it 'should not take null arguments', ->
         expect(-> fake_function null).toThrow()

-      it 'should not escape backslashes in strings', ->
-        result = fake_function '\\'
-        expect(core.to_target_string result).not.toMatch /\\\\/
-
-      it 'should not escape a single kind of quotes', ->
-        result = fake_function '"'
-        expect(core.to_target_string result).not.toMatch /\\/
-        result = fake_function "'"
-        expect(core.to_target_string result).not.toMatch /\\/
-
     describe 'raw strings', ->
       raw_string = null

@@ -112,3 +102,28 @@

       it 'should have their value as the js string', ->
         expect(core.to_js_string boolean).toBe 'false'
+
+    describe 'strings', ->
+      string = null
+
+      beforeEach ->
+        string = new core.type.s 'avocado'
+
+      it 'should be lead nodes', ->
+        expect(core.is_lead_node string).toBe true
+
+      it 'should have their quoted value as the target', ->
+        expect(core.to_target_string string).toBe "'avocado'"
+
+      it 'should have their json-serialized value as the js string', ->
+        expect(core.to_js_string string).toBe '"avocado"'
+
+      it 'should not escape backslashes', ->
+        s = new core.type.s '\\'
+        expect(core.to_target_string s).not.toMatch /\\\\/
+
+      it 'should not escape a single kind of quotes', ->
+        s = new core.type.s '"'
+        expect(core.to_target_string s).not.toMatch /\\/
+        s = new core.type.s "'"
+        expect(core.to_target_string s).not.toMatch /\\/


--- spec/ipso_spec.coffee
+++ spec/ipso_spec.coffee
@@ -288,11 +288,30 @@
                 error.expected.should.equal 'something else'

-        it 'throws expected functions on timeout even if timeout is reset', ipso (facto, Something) ->
-
-            @timeout 10
-            Something.does something: -> 'hrumph'
-
-
-
-
+        # it 'throws expected functions not called instead of timeout even if timeout is reset', ipso (facto, Something) ->
+
+        #     @timeout 10
+        #     Something.does
+        #         something: -> 'okgood'
+        #         somethingElse: -> facto()
+
+        #     Something.something()
+        #     # Something.somethingElse()
+
+        #     # 1 | {
+        #     # 2 |   "Something": {
+        #     # 3 |     "functions": {
+        #     # 4 |       "Object.something()": "was called",
+        #     # 5 |       "Object.somethingElse()": "was NOT called"
+        #     # 6 |     }
+        #     # 7 |   }
+        #     # 8 | }
+
+        #     #
+        #     # not bothering to try and make a 'passing test' capable
+        #     # of testing that this test fails by timeout
+        #     #
+
+
+
+


Documentation for [up.magic]

Documentation for [up.magic]

--- lib/assets/javascripts/up/magic.js.coffee
+++ lib/assets/javascripts/up/magic.js.coffee
@@ -126,6 +126,22 @@
   @method up.magic.data
   @param {String|Element|jQuery} elementOrSelector
   ###
+
+  ###
+  Stores a JSON-string with the element.
+
+  If an element annotated with [`up-data`] is inserted into the DOM,
+  Up will parse the JSON and pass the resulting object to any matching
+  [`up.awaken`](/up.magic#up.magic.awaken) handlers.
+
+  Similarly, when an event is triggered on an element annotated with
+  [`up-data`], the parsed object will be passed to any matching
+  [`up.on`](/up.magic#up.on) handlers.
+
+  @ujs
+  @method [up-data]
+  @param {JSON} [up-data]
+  ###
   data = (elementOrSelector) ->
     $element = $(elementOrSelector)
     json = $element.attr('up-data')


add mute and harmonic characters to chords

--- src/marktab.coffee
+++ src/marktab.coffee
@@ -19,9 +19,9 @@
 	bendPattern = /b/
 	harmonicPattern = /\*/
 	vibratoPattern = /~/
-	chordPattern = /\([0-9\s:]+\)/
+	chordPattern = /\([0-9\s:x\*]+\)/
 	riffPattern = /\[.*\]/
-	chordMultiplierPattern = /\([0-9\s:]+\)\s*x[0-9]+/
+	chordMultiplierPattern = /\([0-9\s:x\*]+\)\s*x[0-9]+/
 	riffMultiplierPattern = /\[.*\]\s*x[0-9]+/
 	multiplierPattern = /x[0-9]+/
 	ignorePattern = /[\s\n]/