Ensure grammars are only activated once

Ensure grammars are only activated once

--- src/package.coffee
+++ src/package.coffee
@@ -129,8 +129,9 @@
     atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus
     atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu

-    grammar.activate() for grammar in @grammars
-    @grammarsActivated = true
+    unless @grammarsActivated
+      grammar.activate() for grammar in @grammars
+      @grammarsActivated = true

     scopedProperties.activate() for scopedProperties in @scopedProperties
     @scopedPropertiesActivated = true
@@ -189,6 +190,7 @@
         console.warn("Failed to load grammar: #{grammarPath}", error.stack ? error)

     @grammarsLoaded = true
+    @grammarsActivated = true

   loadGrammars: ->
     return Q() if @grammarsLoaded


Check remote of current branch

Check remote of current branch

Default to master branch and origin remote when remote lookup
for current branch fails.

Closes #150

--- src/publish.coffee
+++ src/publish.coffee
@@ -229,7 +229,15 @@
     unless repo?.isWorkingDirectory(currentDirectory)
       throw new Error('Package must be in a Git repository before publishing: https://help.github.com/articles/create-a-repo')

-    unless repo.getConfigValue('remote.origin.url')
+
+    if currentBranch = repo.getShortHead()
+      remoteName = repo.getConfigValue("branch.#{currentBranch}.remote")
+    remoteName ?= repo.getConfigValue('branch.master.remote')
+
+    upstreamUrl = repo.getConfigValue("remote.#{remoteName}.url") if remoteName
+    upstreamUrl ?= repo.getConfigValue('remote.origin.url')
+
+    unless upstreamUrl
       throw new Error('Package must pushed up to GitHub before publishing: https://help.github.com/articles/create-a-repo')

   # Run the publish command with the given options


use require.paths when resolving

use require.paths when resolving

--- src/require.coffee
+++ src/require.coffee
@@ -72,7 +72,7 @@
     file = file.replace '../', "#{prefix}/"

   if file[0] isnt '/'
-    paths.some (path) ->
+    require.paths.some (path) ->
       if /\.(.+)$/.test(file) and __exists "#{path}/#{file}"
         file = "#{path}/#{file}"
       else if expanded = __expand "#{path}/#{file}"


Adding the responsiveness mechanism that yields the JavaScript thread every 1-2 seconds. This ensures that the webpage doesn't "lock up", and prevents the web page from asking the user to kill the script (or, in some cases, from the web browser killing the script automatically).

We describe this mechanism in the paper, but I hadn't committed it to public Doppio until now.

--- src/runtime.coffee
+++ src/runtime.coffee
@@ -91,6 +91,7 @@
     @waiting_threads = {}  # map from monitor -> list of waiting thread objects
     @thread_pool = []
     @curr_thread = {$meta_stack: new root.CallStack()}
+    @max_m_count = 100000

   get_bs_cl: -> @bcl

@@ -359,11 +360,25 @@
       @stashed_done_cb = done_cb  # hack for the case where we error out of <clinit>
       try
         setup_fn()
+        start_time = (new Date()).getTime()
+        m_count = @max_m_count
         while true
           sf = @curr_frame()
-          while sf.runner?
+          while sf.runner? and m_count > 0
             sf.runner()
+            m_count--
             sf = @curr_frame()
+          if sf.runner? && m_count == 0
+            # Loop has stopped to give the browser some breathing room.
+            duration = (new Date()).getTime() - start_time
+            # We should yield once every 1-2 seconds or so.
+            if duration > 2000 or duration < 1000
+              # Figure out what to adjust max_m_count by.
+              ms_per_m = duration / @max_m_count
+              @max_m_count = (1000/ms_per_m)|0
+            # Call ourselves to yield and resume.
+            return @run_until_finished (->), no_threads, done_cb
+
           # we've finished this thread, no more runners
           # we're done if the only thread is "main"
           break if no_threads or @thread_pool.length <= 1


Route POST requests to GET

Route POST requests to GET

Fix to treat POST requests as GET

--- src/timbits.coffee
+++ src/timbits.coffee
@@ -1,5 +1,6 @@
 fs = require 'fs'
 path = require 'path'
+querystring = require 'querystring'
 url = require 'url'
 views = require './timbits-views'
 ck = require 'coffeekup'
@@ -47,6 +48,14 @@

 	@server.configure 'production', =>
 		@server.use express.errorHandler()
+
+	# route post requests to get
+	@server.post '*', (req, res) =>
+		delete req.body.submit # remove submit type
+		for attrname of req.body # merge post and get params
+			req.query[attrname] = req.body[attrname]
+		if req.url.indexOf('?') != -1 then req.url = req.url.substring(0, req.url.indexOf('?')) # remove querystring if necessary
+		res.redirect "#{req.url}" + if req.query? then "?#{querystring.stringify(req.query)}" else "" # append new querystring to request url

 	# route root to help page
 	@server.get '/', (req, res) =>


type casting

type casting

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -233,8 +233,8 @@
         new_bl.push obj
         continue
       if bl_search and obj.val.indexOf(bl_search) == -1
-        val = String(obj.val)
-        console.log 'search: indexOf['+ bl_search +'] not found in ['+ val +']'
+        errfmt = 'search: indexOf[%s] not found in [%s]'
+        console.log sprintf errfmt, bl_search, obj.val
         new_bl.push obj
         continue
       if expires.isBefore() # now


fix bad variable

fix bad variable

--- src/uapanfw.coffee
+++ src/uapanfw.coffee
@@ -232,16 +232,15 @@
         continue
       arr.push sprintf fmt, obj.type, obj.val, expires.fromNow(), obj.creator

-    # drop deleted records
-    deltaN = fwdata.length - newdata.length
-    fwdata.blacklist = newdata
-    fs.writeFileSync blacklistfile, JSON.stringify(fwdata), 'utf-8'
-
+    deltaN = fwdata.blacklist.length - newdata.length
     if deltaN > 0
       usermsg = "Removed `#{deltaN}` entries from firewall blacklist.  " +
         "Change will be applied in < 5 minutes.\n" +
         "Removed: ```"+ arr.join("\n") + "```"
-    else usermsg = "Blacklist delete request did not match any records."
+      fwdata.blacklist = newdata
+      fs.writeFileSync blacklistfile, JSON.stringify(fwdata), 'utf-8'
+    else
+      usermsg = "Blacklist delete request did not match any records."
     msg.reply usermsg

     logmsg = "#{modulename}: robot responded to #{msg.envelope.user.name}: " +


Fix sql text

Fix sql text

--- source/javascripts/sql_joins.coffee
+++ source/javascripts/sql_joins.coffee
@@ -121,7 +121,7 @@
     @sqlDetails.find('.sql_description').text(sqlJoinInfo.description)

     @sqlDetails.find('.sql_info').text(sqlJoinInfo.sql)
-    @copyButton.attr('data-clipboard-text', sqlJoinInfo.sql)
+    @copyButton.attr('data-clipboard-text', sqlJoinInfo.sql.replace(/\n/gi, ' '))
     # copy button
     if sqlJoinInfo.sql.length
       @copyButton.removeClass('disabled')


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -19,6 +19,6 @@

 test "#first", ->
   engine.add
-    type
+    type:

 module()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_selector.coffee
+++ test/engine_selector.coffee
@@ -4,7 +4,8 @@
   engine = Engine()

   engine.add
-    findable: t
+    findable: true
+

 module()


make this compatible with node 0.6, although not advertised

--- test/_utils.coffee
+++ test/_utils.coffee
@@ -3,6 +3,7 @@
 _ = require 'lodash'
 chai = require 'chai'
 chai.Assertion.includeStack = true
+existsSync = path.existsSync or fs.existsSync

 module.exports = {
   _
@@ -13,5 +14,5 @@
 }

 customUtilsFile = path.resolve './test/_utils.custom.coffee'
-if fs.existsSync customUtilsFile
+if existsSync customUtilsFile
   _.merge module.exports, require customUtilsFile


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -103,4 +103,20 @@
   equal color.b, 205
   equal color.a, 0.59

+test "accepts hex string of length 3", ->
+  color = Color2('#df2')
+
+  equal color.r, 221
+  equal color.g, 255
+  equal color.b, 34
+  equal color.a, 1.0
+
+test "accepts hex string of length 4", ->
+  color = Color2('#c987')
+
+  equal color.r, 204
+  equal color.g, 153
+  equal color.b, 136
+  equal color.a.toFixed(2), 0.47
+
 module()


update parcel

update parcel

--- .atom/packages.cson
+++ .atom/packages.cson
@@ -1,6 +1,5 @@
 'packages': [
-  'atom-beautifier'
-  'autocomplete-plus'
+  'autocomplete'
   'cucumber'
   'editorconfig'
   'feedback'
@@ -21,7 +20,11 @@
   'solarized-dark-syntax'
   'solarized-light-syntax'
   'archive-view'
-  'autocomplete'
+  'autocomplete-atom-api'
+  'autocomplete-css'
+  'autocomplete-html'
+  'autocomplete-plus'
+  'autocomplete-snippets'
   'autoflow'
   'autosave'
   'background-tips'


added cucumber highlighter

added cucumber highlighter

--- .atom/packages.cson
+++ .atom/packages.cson
@@ -1,6 +1,8 @@
 'packages': [
   'autocomplete-plus'
+  'cucumber'
   'editorconfig'
+  'feedback'
   'language-batch'
   'language-powershell'
   'parcel'
@@ -22,8 +24,8 @@
   'command-palette'
   'deprecation-cop'
   'dev-live-reload'
+  'encoding-selector'
   'exception-reporting'
-  'feedback'
   'find-and-replace'
   'fuzzy-finder'
   'git-diff'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -188,7 +188,7 @@
   engine.setState nextState

   engine.bind "stateEntered", ->
-    ok
+    ok true

   engine.update()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine.coffee
+++ test/engine.coffee
@@ -185,7 +185,7 @@

   object = engine.add
     class: "GameObject"
-    velocity: Point(, 0)
+    velocity: Point(30, 0)

   engine.frameAdvance()


Follow mixin

Follow mixin

--- test/follow.coffee
+++ test/follow.coffee
@@ -1,6 +1,6 @@
 module "Follow"

 test "testing for equality", ->
-
+  obj =

 module()


Matrix copy constructor

Matrix copy constructor

--- test/matrix.coffee
+++ test/matrix.coffee
@@ -16,7 +16,9 @@
     equalEnough(m1.ty, m2.ty, TOLERANCE)

   test "copy constructor", ->
-
+   matrix = Matrix(1)
+
+
   test "Matrix() (Identity)", ->
     matrix = Matrix()


test for problematic design

test for problematic design

--- test/models.coffee
+++ test/models.coffee
@@ -1,6 +1,6 @@
 # see http://net.tutsplus.com/tutorials/javascript-ajax/better-coffeescript-testing-with-mocha/
 should = require('chai').should()
-{Track, Section, Transform, Straight, Bend, Split, Crossover} = require '../src/woodentrack'
+{Track, Section, Transform, Straight, Bend, Split, Crossover, Merge} = require '../src/woodentrack'

 describe 'Track', ->

@@ -200,3 +200,12 @@
 		track.connect new Bend({flip:-1}), "1:D"
 		it 'should have three sections', ->
 			track._sections.should.have.length 3
+
+	describe 'should handle pieces with changing indexes', ->
+		track = new Track
+		track.add new Straight
+		track.connect new Crossover, "0:A"
+		track.connect new Bend, "1:D"
+		track.connect new Bend, "0:B"
+		it 'should have three sections', ->
+			track.pieces().should.have.length 4


Refactor httpRequest to accept a URL directly

--- src/kissmetrics.coffee
+++ src/kissmetrics.coffee
@@ -3,17 +3,11 @@
 https = require 'https' if ENV is 'node'

-httpRequest = (args) ->
-	args.port ?= 80
-	args.path ?= ''
-	url        = "https://#{args.host}:#{args.port}/#{args.path}"
-
+httpRequest = (url) ->
 	if ENV is 'node'
 		https.get url
 	else
 		(new Image()).src = url
-
-	return url

 class KissmetricsClient
@@ -52,10 +46,7 @@
 		@request "#{@query_types[type]}?#{queryString}"

 	request: (endpoint) ->
-		httpRequest
-			host : @host
-			port : @port
-			path : endpoint
+		httpRequest "https://#{@host}:#{@port}/#{endpoint}"

 if ENV is 'node'


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/level_state.coffee
+++ src/level_state.coffee
@@ -1,5 +1,5 @@
 ###*
-A
+A Game State

 @see GameState
 @name LevelState


Allow to --require ruby libraries defined in config

--- src/sass.plugin.coffee
+++ src/sass.plugin.coffee
@@ -54,6 +54,11 @@
 				if config.outputStyle
 					command.push('--style')
 					command.push(config.outputStyle)
+				if config.requireLibraries
+					for name in config.requireLibraries
+						if (typeof name == 'function') continue
+						command.push('--require');
+						command.push(name);

 				# Spawn the appropriate process to render the content
 				balUtil.spawn command, commandOpts, (err,stdout,stderr,code,signal) ->


support on issue commented

support on issue commented

--- src/scripts/fgb.coffee
+++ src/scripts/fgb.coffee
@@ -77,6 +77,27 @@
     message = "#{issueKey} を誰かにレビュー依頼しないの？"
     sendToChat robot, { room, user, message }

+onIssueCommented = (robot, space, w) ->
+  projectKey = w.project.projectKey
+  project = space.getProject projectKey
+  unless project?
+    robot.logger.warning 'hubot-fgb: unknown project key: ' + projectKey
+    return
+  issueKey = "#{project.getKey()}-#{w.content.key_id}"
+  issueUrl = "https://#{space.getId()}.backlog.jp/view/#{issueKey}"
+  summary = w.content.summary
+  description = w.content.description
+  username = w.createdUser.name
+  commentId = w.content.comment.id
+  comment = w.content.comment.content
+  room = project.getRoom()
+  message = """
+  #{username} が課題「#{issueKey} #{summary}」にコメントしたみたい。
+  #{comment}
+  #{issueUrl}#comment-#{commentId}
+  """
+  sendToChat robot, { room, message }
+
 module.exports = (robot) ->
   robot.logger.debug 'hubot-fgb: load config: ' + JSON.stringify config
   space = newSpace config
@@ -88,6 +109,8 @@
       onIssueCreated robot, space, webhook
     else if webhook.type is 2 # issue updated
       onIssueUpdated robot, space, webhook, requests
+    else if webhook.type is 3 # issue commented
+      onIssueCommented robot, space, webhook
     else
       return if webhook.type <= 17 # max webhook.type
       robot.logger.warning "hubot-fgb: unknown webhook type: #{webhook.type}"


Sortable 'receive' event handler added.

Sortable 'receive' event handler added.

This updates the hidden field on the form with the new value that
has been dropped in via Drag&Drool.

--- src/CoffeeScript/BrissyParks.coffee
+++ src/CoffeeScript/BrissyParks.coffee
@@ -47,12 +47,17 @@
                         cursor: "crosshair", cursorAt: { top: -5, left: -5 },
                         connectToSortable: to })

+        extendList = (evt, ui) ->
+                item = $(ui.item).text().trim()
+                cur = $('#feature-form-list').attr('value')
+                $('#feature-form-list').attr('value', "#{cur},#{item}")
+
         # Connect our feature list with the selected feature list.
         connectFeatureLists = () ->
                 all = '.feature-list li.list-group-item'
                 sel = '#sortable-features'

-                $(sel).sortable({ revert: true })
+                $(sel).sortable({ revert: true, receive: extendList })
                 linkDraggable all, sel, true

                 $('ul, li').disableSelection()


Hotfix - Win environment

Hotfix - Win environment

--- src/tasks/_util.coffee
+++ src/tasks/_util.coffee
@@ -3,7 +3,7 @@

 #-- sep
 sep = require('path').sep
-util.sep = (path) -> path.replace /\//g, sep
+util.sep = (path) -> path

 #-- paths


promise: improve actions test

promise: improve actions test

--- test/promise.actions.coffee
+++ test/promise.actions.coffee
@@ -7,7 +7,7 @@

   scratch = test.scratch @

-  describe 'sync', ->
+  describe 'handler', ->

     it 'register actions in callback', (next) ->
       msgs = []
@@ -35,20 +35,7 @@
           content.should.eql 'abcdefhij'
           next()

-    it 'handler errors in handler', (next) ->
-      m = mecano()
-      m.register 'anaction', (options, callback) ->
-        throw Error 'Catchme'
-      m
-      .anaction
-        key: "value"
-      , (err, written) ->
-        err.message.should.eql 'Catchme'
-      .then (err, changed) ->
-        err.message.should.eql 'Catchme'
-        next()
-
-    it 'handler errors in callback', (next) ->
+    it 'can throw error', (next) ->
       # msgs = []
       # m = mecano log: (msg) -> msgs.push msg if /\/file_\d/.test msg
       mecano()
@@ -62,3 +49,31 @@
       .then (err, changed) ->
         err.message.should.eql 'Catchme'
         next()
+
+  describe 'error', ->
+
+    it 'throw in sync action', (next) ->
+      m = mecano()
+      m.register 'anaction', (options, callback) ->
+        throw Error 'Catchme'
+      m
+      .anaction
+        key: "value"
+      , (err, written) ->
+        err.message.should.eql 'Catchme'
+      .then (err, changed) ->
+        err.message.should.eql 'Catchme'
+        next()
+
+    it 'throw in async action', (next) ->
+      m = mecano()
+      m.register 'anaction', (options, callback) ->
+        setImmediate -> callback Error 'Catchme'
+      m
+      .anaction
+        key: "value"
+      , (err, written) ->
+        err.message.should.eql 'Catchme'
+      .then (err, changed) ->
+        err.message.should.eql 'Catchme'
+        next()


Fix merging error.

Fix merging error.

--- test/src/client_test.coffee
+++ test/src/client_test.coffee
@@ -452,7 +452,7 @@
         @client.readFile @newFile, (error, data, stat) =>
           expect(error).to.equal null
           expect(data).to.equal @newFileData
-          unless Dropbox.Xhr.ieXdr  # IE's XDR doesn't do headers.
+          unless Dropbox.Util.Xhr.ieXdr  # IE's XDR doesn't do headers.
             expect(stat).to.be.instanceOf Dropbox.File.Stat
             expect(stat.path).to.equal @newFile
             expect(stat.isFile).to.equal true


bug fix: go through the selections in reverse order

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -886,6 +886,7 @@
   moveLineDown: ->
     selections = @getSelectedBufferRanges()
     selections.sort (a, b) -> a.compare(b)
+    selections = selections.reverse()

     @transact =>
       newSelectionRanges = []


:art:

:art:

--- src/text-editor.coffee
+++ src/text-editor.coffee
@@ -943,10 +943,9 @@
         charToLeftOfSelection = new Range(selection.start.translate(translationDelta), selection.start)

         unless charToLeftOfSelection.start.column < 0
-          insertionPoint = selection.end
           text = @buffer.getTextInRange(charToLeftOfSelection)

-          @buffer.insert(insertionPoint, text)
+          @buffer.insert(selection.end, text)
           @buffer.delete(charToLeftOfSelection)
           translatedRanges.push(selection.translate(translationDelta))

@@ -963,11 +962,10 @@
         charToRightOfSelection = new Range(selection.end, selection.end.translate(translationDelta))

         unless charToRightOfSelection.end.column > @buffer.lineLengthForRow(charToRightOfSelection.end.row)
-          insertionPoint = selection.start
           text = @buffer.getTextInRange(charToRightOfSelection)

           @buffer.delete(charToRightOfSelection)
-          @buffer.insert(insertionPoint, text)
+          @buffer.insert(selection.start, text)
           translatedRanges.push(selection.translate(translationDelta))

       @setSelectedBufferRanges(translatedRanges) if translatedRanges.length > 0


Updated the syntax and standardized quote style

--- src/thesimpsons.coffee
+++ src/thesimpsons.coffee
@@ -117,61 +117,61 @@
    "Everything's coming up Milhouse! -Milhouse Van Houten",
    "I was saying \"Boo-urns.\" -Hans Moleman",
    "I can't promise I'll try, but I'll try to try. -Bart Simpson",
-   "To alcohol! The cause of, and solution to, all of life's problems. -Homer Simpson"
-   "Oh boy, Liver! Iron helps us play! - Rod and Todd"
-   "Look at those poor saps back on land with their laws and ethics! They'll never know the simple joys of a monkey knife fight. - Home Simpson"
-   "Beer. Now there's a temporary solution. - Homer Simpson"
-   "All right, brain. You don't like me and I don't like you, but let's just do this and I can get back to killing you with beer. - Homer Simpson"
-   "And how is education supposed to make me feel smarter? Besides, every time I learn something new, it pushes some old stuff out of my brain. Remember when I took that home winemaking course, and I forgot how to drive? - Homer Simpson"
-   "I hope I didn't brain my damage. - Homer Simpson"
-   "Oh, Lisa, you and your stories: Bart's a vampire, beer kills brain cells. Now let's go back to that... building... thingie... where our beds and TV... is. - Homer Simpson"
-   "What are you gonna do? Sick your dogs on me? Or your bees? Or dogs with bees in their mouth so when they bark they shoot bees at me? --Homer Simpson"
-   "I have misplaced my pants. - Homer Simpson"
-   "Mmmm, 52 slices of American cheese. - Homer Simpson"
-   "Mmmm, forbidden donut. - Homer Simpson"
-   "Mmmm, free goo. - Homer Simpson"
-   "Mmmm, Gummy-beer. - Homer Simpson"
-   "Mmmm, purple. - Homer Simpson"
-   "Mmmm, sacrilicious. - Homer Simpson"
-   "Mmmm...fuzzy. - Homer Simpson"
-   "Mmmm...open faced club sand wedge. - Homer Simpson"
-   "Duff Man can't breathe! Oh no! - Duff Man"
-   "Ooh, sorry, kid, sorry. I'm not used to the laughter of children. It cuts through me like a dentist drill. But no, no, that was funny, that was funny taking away my dignity like that, ha ha ha. - Moe the Bartender"
-   "Everything's coming up Milhouse! - Milhouse"
-   "And this is the snack holder where I put my beverage or, if you will, cupcake. - Homer Simpson"
-   "My eyes. The goggles, they do nothing. - Radioactive Man"
-   "You go through life, you try to be nice to people, you struggle to resist the urge to punch em in the face, and for what? - Moe the Bartender"
-   "Hello Simpson. I'm riding the bus today because Mother hid my car keys to punish me for talking to a woman on the phone. She was right to do it. - Principal Skinner"
-   "Slow down sir. You're going to give yourself a skin failure. - Dr Nick Riviera"
-   "In his house, we obey the laws of thermodynamics. - Homer Simpson"
-   "Y'ello? You'll have to speak up, I'm wearing a towel. - Homer Simpson"
-   "Hmm. Your ideas are intriguing to me and I wish to subscribe to your newsletter. - Homer Simpson"
-   "I don't even believe in Jebus! Save me Jebus! - Homer Simpson"
-   "There's a 4:30 in the morning now? - Bart Simpson"
-   "Oh wow, windows. I don't think I could afford this place. - Otto"
-   "Shoplifting is a victimless crime, like punching someone in the dark. - Nelson Munz"
-   "Can't we have one meeting that doesn't end with us digging up a corpse? - Mayor Quimby"
-   "Um, can you repeat the part of the stuff...where you said about all the...things? - Homer Simpson"
-   "Honey, if we didn't turn it down for the cops, what chance do you have? - Homer Simpson"
-   "Oh boy, sleep! That's where I'm a Viking! - Ralph Wiggum"
-   "A shiny new donkey to the man who brings me the head of Homer Simpson! - Mr Burns"
-   "Sure, I'm flattered, maybe even a little curious, but the answer is no! - Homer Simpson"
-   "Oh, if only we'd listened to that young man, instead of walling him up in the abandoned coke oven. - Mr Burns"
-   "You've made a powerful enemy today, my friend. - Mr Burns"
-   "All right, let's make this sporting, Leonard. If you can tell me why I shouldn't fire you without using the letter "e," you can keep your job. - Mr Burns"
-   "Okay Mr. Burns Here are your messages: You have thirty minutes to move your car. You have ten minutes to move your car. Your car has been impounded. Your car has been crushed into a cube. You have thirty minutes to move your cube. - Homer Simpson"
-   "Ooh, don't poo-poo a nickel, Lisa. A nickel will buy you a steak and kidney pie, a cup of coffee, a slice of cheesecake and a newsreel... with enough change left over to ride the trolley from Battery Park to the polo grounds. - Mr Burns"
-   "Oh, so mother nature needs a favor? Well, maybe she should have thought of that when she was besetting us with droughts and floods and poison monkeys. - Mr Burns"
-   "Release the hounds. - Mr Burns"
-   "I'm afraid all of those players have retired and, uh... passed on. In fact, your right-fielder has been dead for a hundred and thirty years. - Mr Smithers"
-   "Smithers, this monkey is going to need most of your skin. - Mr Burns"
-   "Now to enjoy the Miami of Canada: Chicago. - Mr Burns"
-   "I'm afraid it's not that simple. As punishment for your desertion, it's company policy to give you the plague. - Mr Burns"
-   "Oh, Smithers. I would have said anything to get your stem cells. - Mr Burns"
-   "Oh, balderdash. It's not important how old you are on parchment, it's how old you feel in the humours! - Mr Burns"
-   "Compadres, it is imperative that we crush the freedom fighters before the start of the rainy season. And remember, a shiny new donkey for whomever brings me the head of Colonel Montoya. - Mr Burns"
-   "Have the Rolling Stones killed. - Mr Burns"
-   "Uh, did I say corpse hatch? I meant innocence tube. - Mr Burns"
+   "To alcohol! The cause of, and solution to, all of life's problems. -Homer Simpson",
+   "Oh boy, Liver! Iron helps us play! -Rod and Todd",
+   "Look at those poor saps back on land with their laws and ethics! They'll never know the simple joys of a monkey knife fight. -Homer Simpson",
+   "Beer. Now there's a temporary solution. -Homer Simpson",
+   "All right, brain. You don't like me and I don't like you, but let's just do this and I can get back to killing you with beer. -Homer Simpson",
+   "And how is education supposed to make me feel smarter? Besides, every time I learn something new, it pushes some old stuff out of my brain. Remember when I took that home winemaking course, and I forgot how to drive? -Homer Simpson",
+   "I hope I didn't brain my damage. -Homer Simpson",
+   "Oh, Lisa, you and your stories: Bart's a vampire, beer kills brain cells. Now let's go back to that... building... thingie... where our beds and TV... is. -Homer Simpson",
+   "What are you gonna do? Sick your dogs on me? Or your bees? Or dogs with bees in their mouth so when they bark they shoot bees at me? -Homer Simpson",
+   "I have misplaced my pants. -Homer Simpson",
+   "Mmmm, 52 slices of American cheese. -Homer Simpson",
+   "Mmmm, forbidden donut. -Homer Simpson",
+   "Mmmm, free goo. -Homer Simpson",
+   "Mmmm, Gummy-beer. -Homer Simpson",
+   "Mmmm, purple. -Homer Simpson",
+   "Mmmm, sacrilicious. -Homer Simpson",
+   "Mmmm...fuzzy. -Homer Simpson",
+   "Mmmm...open faced club sand wedge. -Homer Simpson",
+   "Duff Man can't breathe! Oh no! -Duff Man",
+   "Ooh, sorry, kid, sorry. I'm not used to the laughter of children. It cuts through me like a dentist drill. But no, no, that was funny, that was funny taking away my dignity like that, ha ha ha. -Moe the Bartender",
+   "Everything's coming up Milhouse! -Milhouse",
+   "And this is the snack holder where I put my beverage or, if you will, cupcake. -Homer Simpson",
+   "My eyes. The goggles, they do nothing. -Radioactive Man",
+   "You go through life, you try to be nice to people, you struggle to resist the urge to punch em in the face, and for what? -Moe the Bartender",
+   "Hello Simpson. I'm riding the bus today because Mother hid my car keys to punish me for talking to a woman on the phone. She was right to do it. -Principal Skinner",
+   "Slow down sir. You're going to give yourself a skin failure. -Dr Nick Riviera",
+   "In his house, we obey the laws of thermodynamics. -Homer Simpson",
+   "Y'ello? You'll have to speak up, I'm wearing a towel. -Homer Simpson",
+   "Hmm. Your ideas are intriguing to me and I wish to subscribe to your newsletter. -Homer Simpson",
+   "I don't even believe in Jebus! Save me Jebus! -Homer Simpson",
+   "There's a 4:30 in the morning now? -Bart Simpson",
+   "Oh wow, windows. I don't think I could afford this place. -Otto",
+   "Shoplifting is a victimless crime, like punching someone in the dark. -Nelson Munz",
+   "Can't we have one meeting that doesn't end with us digging up a corpse? -Mayor Quimby",
+   "Um, can you repeat the part of the stuff...where you said about all the...things? -Homer Simpson",
+   "Honey, if we didn't turn it down for the cops, what chance do you have? -Homer Simpson",
+   "Oh boy, sleep! That's where I'm a Viking! -Ralph Wiggum",
+   "A shiny new donkey to the man who brings me the head of Homer Simpson! -Mr Burns",
+   "Sure, I'm flattered, maybe even a little curious, but the answer is no! -Homer Simpson",
+   "Oh, if only we'd listened to that young man, instead of walling him up in the abandoned coke oven. -Mr Burns",
+   "You've made a powerful enemy today, my friend. -Mr Burns",
+   "All right, let's make this sporting, Leonard. If you can tell me why I shouldn't fire you without using the letter e, you can keep your job. -Mr Burns",
+   "Okay Mr. Burns Here are your messages: You have thirty minutes to move your car. You have ten minutes to move your car. Your car has been impounded. Your car has been crushed into a cube. You have thirty minutes to move your cube. -Homer Simpson",
+   "Ooh, don't poo-poo a nickel, Lisa. A nickel will buy you a steak and kidney pie, a cup of coffee, a slice of cheesecake and a newsreel... with enough change left over to ride the trolley from Battery Park to the polo grounds. -Mr Burns",
+   "Oh, so mother nature needs a favor? Well, maybe she should have thought of that when she was besetting us with droughts and floods and poison monkeys. -Mr Burns",
+   "Release the hounds. -Mr Burns",
+   "I'm afraid all of those players have retired and, uh... passed on. In fact, your right-fielder has been dead for a hundred and thirty years. -Mr Smithers",
+   "Smithers, this monkey is going to need most of your skin. -Mr Burns",
+   "Now to enjoy the Miami of Canada: Chicago. -Mr Burns",
+   "I'm afraid it's not that simple. As punishment for your desertion, it's company policy to give you the plague. -Mr Burns"
+   "Oh, Smithers. I would have said anything to get your stem cells. -Mr Burns",
+   "Oh, balderdash. It's not important how old you are on parchment, it's how old you feel in the humours! -Mr Burns",
+   "Compadres, it is imperative that we crush the freedom fighters before the start of the rainy season. And remember, a shiny new donkey for whomever brings me the head of Colonel Montoya. -Mr Burns",
+   "Have the Rolling Stones killed. -Mr Burns",
+   "Uh, did I say corpse hatch? I meant innocence tube. -Mr Burns"
 ]

 module.exports = (robot) ->


Disable submits after submit

Disable submits after submit

--- app/assets/javascripts/behaviors/submittable.coffee
+++ app/assets/javascripts/behaviors/submittable.coffee
@@ -1,12 +1,22 @@
 @CodequestManager.Behaviors.Submittable = Marionette.Behavior.extend
   ui:
     form: 'form'
+    submitButton: "[type='submit']"

   triggers:
     'submit @ui.form': 'form:submit'

+  modelEvents:
+    error: '_enableSubmit'
+
   onShow: ->
     @_focusFistInput() unless @view.model.id

+  onFormSubmit: ->
+    @ui.submitButton.prop('disabled',true)
+
   _focusFistInput: ->
-    @ui.form.find('input:first').focus()+    @ui.form.find('input:first').focus()
+
+  _enableSubmit: ->
+    @ui.submitButton.prop('disabled',false)


Lighten scoreboard highlighting

Lighten scoreboard highlighting

--- app/assets/javascripts/matches/scoreboard.js.coffee
+++ app/assets/javascripts/matches/scoreboard.js.coffee
@@ -15,12 +15,13 @@
   #      assists_colorizer(3) # returns rgb(190,190,60)
   colorizer = () ->
     thresholds = arguments
-    factor = 2.0 / thresholds.length
+    highest_brightness = 1.5
+    factor = highest_brightness / thresholds.length
     (val) ->
       val = -val if val < 0
-      yellow = d3.hsl(50,1,0.5)
+      yellow = d3.hsl(50,1,0.6)
       for threshold,idx in thresholds
-        return yellow.brighter(2.0 - factor * idx) if val < threshold
+        return yellow.brighter(highest_brightness - factor * idx) if val < threshold
       yellow

   init = ->


Don't enter infinite loop generating id

Don't enter infinite loop generating id

--- services/web/public/coffee/ide/review-panel/RangesTracker.coffee
+++ services/web/public/coffee/ide/review-panel/RangesTracker.coffee
@@ -55,7 +55,7 @@
 				'0000'.substr(0, 4 - pid.length) + pid

 		@generateId: () ->
-			@generateId() + "000001"
+			@generateIdSeed() + "000001"

 		newId: () ->
 			@id_increment++


default outcome values to 0 if missing

default outcome values to 0 if missing

test plan:
* import the d2l export referenced in the ticket
 (before we actually fix the converters)
* visit the outcomes
* should not get missing value notices on page

refs #CNVS-5047

Change-Id: I8e0d44a764e476f3c9c03943d1c2105d41192a75
Reviewed-on: https://gerrit.instructure.com/19410
Tested-by: Jenkins <d95b56ce41a2e1ac4cecdd398defd7414407cc08@instructure.com>
Reviewed-by: Simon Williams <088e16a1019277b15d58faf0541e11910eb756f6@instructure.com>
QA-Review: Adam Phillipps <0e18f44c1fec03ec4083422cb58ba6a09ac4fb2a@instructure.com>
Product-Review: Bracken Mosbacker <48b846001bca4166dc3fb7711563afc17525921e@instructure.com>

--- app/coffeescripts/views/outcomes/OutcomeView.coffee
+++ app/coffeescripts/views/outcomes/OutcomeView.coffee
@@ -140,6 +140,8 @@
         when 'loading'
           @$el.empty()
         else # show
+          data['points_possible'] ||= 0
+          data['mastery_points'] ||= 0
           @$el.html outcomeTemplate _.extend data,
             readOnly: @readOnly(),
             native: @model.outcomeLink.outcome.context_id == @model.outcomeLink.context_id && @model.outcomeLink.outcome.context_type == @model.outcomeLink.context_type


Client: Activity: Added newLine to separate return statement

--- client/activity/lib/components/comments/commentlist.coffee
+++ client/activity/lib/components/comments/commentlist.coffee
@@ -21,6 +21,7 @@

     return null  unless repliesCount
     return null  if repliesCount <= @props.comments.size
+
     commentSize = @props.comments.size
     repliesText = ""


Fix new posts sorting

Fix new posts sorting

--- assets/javascripts/views/fetch_posts_pool.js.coffee
+++ assets/javascripts/views/fetch_posts_pool.js.coffee
@@ -68,7 +68,7 @@
   emptyPool: =>
     pool = @pool.sortBy (i) -> i.published_at
     for i in [0...@num_new_posts]
-      post = @pool.collection.shift()
+      post = pool.pop()
       @posts.unshift(post)
       TentStatus.Views.Post.insertNewPost(post, @$elements.posts_list, @postsFeedView)


Store reference to Subscriptions instance for convenience

--- actioncable/app/assets/javascripts/action_cable/connection.coffee
+++ actioncable/app/assets/javascripts/action_cable/connection.coffee
@@ -8,6 +8,7 @@
   @reopenDelay: 500

   constructor: (@consumer) ->
+    {@subscriptions} = @consumer
     @monitor = new ActionCable.ConnectionMonitor this

   send: (data) ->
@@ -80,16 +81,16 @@
         when message_types.ping
           @monitor.recordPing()
         when message_types.confirmation
-          @consumer.subscriptions.notify(identifier, "connected")
+          @subscriptions.notify(identifier, "connected")
         when message_types.rejection
-          @consumer.subscriptions.reject(identifier)
+          @subscriptions.reject(identifier)
         else
-          @consumer.subscriptions.notify(identifier, "received", message)
+          @subscriptions.notify(identifier, "received", message)

     open: ->
       ActionCable.log("WebSocket onopen event")
       @disconnected = false
-      @consumer.subscriptions.reload()
+      @subscriptions.reload()

     close: ->
       ActionCable.log("WebSocket onclose event")
@@ -102,5 +103,5 @@
   disconnect: ->
     return if @disconnected
     @disconnected = true
-    @consumer.subscriptions.notifyAll("disconnected")
+    @subscriptions.notifyAll("disconnected")
     @monitor.recordDisconnect()


Check pointer events attribute on drag leave target before handling it

It is possible for a dragLeave event to fire from a child element before the hover class was added on dragEventer and the pointer-events: none style applied.

By the time the event is looked at by droppable js, however, pointer-events: none will had had time to be applied, so we check to make sure the target has pointer-events enabled before cleaning up on dragLeave.

--- src/droppable.coffee
+++ src/droppable.coffee
@@ -67,11 +67,12 @@
     @_cleanUp()

   _handleDragleave: normalizeEventCallback (e, dataTransfer) ->
-    $(e.currentTarget).removeClass(@options.hoverClass) if @options.hoverClass
-    @options.out?(e, e.currentTarget, typesForDataTransfer(dataTransfer))
-    @_cleanUp()
-    e.stopPropagation()
-    e.preventDefault()
+    if window.getComputedStyle(e.target)["pointer-events"] != "none"
+      $(e.currentTarget).removeClass(@options.hoverClass) if @options.hoverClass
+      @options.out?(e, e.currentTarget, typesForDataTransfer(dataTransfer))
+      @_cleanUp()
+      e.stopPropagation()
+      e.preventDefault()

   _handleDragover: normalizeEventCallback (e) ->
     @options.dragOver?(e, e.currentTarget)


AcceptedInvitationsView: Use noItemFoundText instead of noItemFoundWidget

--- client/admin/lib/views/invitations/acceptedinvitationsview.coffee
+++ client/admin/lib/views/invitations/acceptedinvitationsview.coffee
@@ -9,8 +9,6 @@

     options.statusType          = 'accepted'
     options.listViewItemOptions = { statusType: 'accepted' }
-    options.noItemFoundWidget   = new KDCustomHTMLView
-      partial  : 'There is no accepted invitation.'
-      cssClass : 'hidden no-item-view'
+    options.noItemFoundText     = 'There is no accepted invitation.'

     super options, data


billing/paymentinformation: update label

--- client/home/lib/billing/components/paymentinformation/view.coffee
+++ client/home/lib/billing/components/paymentinformation/view.coffee
@@ -31,7 +31,7 @@
           placeholder='Enter your name and surname...' />
       </fieldset>
       <fieldset className='email'>
-        E-mail Address
+        Billing Email Address
         <Email
           value={@pickValue 'email'}
           onChange={@sendValue 'email'}


Build jreview

Build jreview

--- CakefileIncludes.coffee
+++ CakefileIncludes.coffee
@@ -116,6 +116,7 @@
         janswer                   : "./server/app/bongo/models/messages/answer.coffee"
         jquestion                 : "./server/app/bongo/models/messages/question.coffee"
         jprivatemessage           : "./server/app/bongo/models/messages/privatemessage.coffee"
+        jreview                   : "./server/app/bongo/models/messages/review.coffee"
         japp                      : "./server/app/bongo/models/app.coffee"

         jinvitation               : "./server/app/bongo/models/invitation.coffee"


share development

share development

--- client/app/Applications/Activity.kdapplication/views/activityactions.coffee
+++ client/app/Applications/Activity.kdapplication/views/activityactions.coffee
@@ -18,10 +18,51 @@

     @shareLink    = new ActivityActionLink
       partial     : "Share"
-      tooltip     :
-        title     : "Coming Soon"
+      # tooltip     :
+      #   title     : "Coming Soon"
       click:(event)=>
-        event.preventDefault()
+        shareUrl    = "http://kd.io/Activity/#{@getData().slug}"
+        contextMenu = new JContextMenu
+          menuWidth   : @getWidth()
+          delegate    : @
+          x           : @getX()
+          y           : @getY() + 15
+          arrow       :
+            placement : "top"
+            margin    : 75
+          lazyLoad    : yes
+        , customView  : new KDView partial: shareUrl
+
+        KD.singletons.mainView.mainTabView.activePane.subViews[0].$().scroll =>
+          contextMenu.setY @getY() + 15
+
+        return
+        shareModal = new KDModalViewWithForms
+          title   : "You can share this link"
+          content : """"""
+          overlay : true
+          tabs          :
+            forms       :
+              share     :
+                fields  :
+                  share :
+                    type: "text"
+                    defaultValue: shareUrl
+                buttons :
+                  "Share on Twitter":
+                    callback: =>
+                      shareText = "#{@getData().body} - #{shareUrl}"
+                      window.open(
+                        "https://twitter.com/intent/tweet?text=#{encodeURIComponent shareText}&via=koding&source=koding",
+                        "twitter-share-dialog",
+                        "width=500,height=350,left=#{Math.floor (screen.width/2) - (500/2)},top=#{Math.floor (screen.height/2) - (350/2)}"
+                      )
+                  "Open in new Tab":
+                    callback: =>
+                      shareModal.$("#share-dialog-link")[0].click()
+
+        shareModal.modalTabs.forms.share.inputs.share.$().select()
+

     @likeView     = new LikeView {checkIfLikedBefore: no}, activity
     @loader       = new KDLoaderView size : width : 14


NewMemberLinkGroup loader fix

NewMemberLinkGroup loader fix

--- client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
+++ client/app/Applications/Activity.kdapplication/views/newmemberbucket.coffee
@@ -63,7 +63,7 @@

     super

-    @loader = new KDLoaderView
+    # @loader = new KDLoaderView

   createMoreLink:->

@@ -95,7 +95,7 @@
       tmpl += " and {{> @more}}"

     tmpl += " became a member."
-    tmpl += "{{> @loader }}"
+    # tmpl += "{{> @loader }}"

 # SLIGHTLY OLD


Workspace: Fixed height of CodeMirror editor in tabbed container after resize done.

--- client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
+++ client/app/CommonViews/workspace/panes/collaborativetabbededitorpane.coffee
@@ -34,6 +34,13 @@
             @createEditorInstance file, null, data.sessionKey

     @workspaceRef.onDisconnect().remove()  if @workspace.amIHost()
+
+    @on "PaneResized", =>
+      @setHeight @parent.getHeight()
+      for pane in @tabView.panes
+        {codeMirrorEditor} = pane.subViews[0]
+        codeMirrorEditor.display.wrapper.style.height = "#{@parent.getHeight() - 22}px"
+        codeMirrorEditor.refresh()

   createEditorTabs: ->
     @tabHandleContainer = new ApplicationTabHandleHolder


[Activity] added getting timestamp

[Activity] added getting timestamp

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -147,7 +147,7 @@
       #@isExempt (exempt)=>
       #if exempt or @getFilter() isnt activityTypes
       groupObj     = KD.getSingleton("groupsController").getCurrentGroup()
-      mydate = new Date((new Date()).setSeconds(0) + 60000)
+      mydate = new Date((new Date()).setSeconds(0) + 60000).getTime()
       options      =
         to         : options.to or mydate #Date.now() we cant cache if we change ts everytime.
         group      :


activity: continueLoadingTeasers temp fix

--- client/landing/app/Applications/Activity.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Activity.kdapplication/AppController.coffee
@@ -311,12 +311,16 @@
         callback null, cache

   continueLoadingTeasers:->
-
+    # fix me
+    # this is a monkeypatch
+    # find the original problem and get rid of @continueLoadingTeasersLastTimeStamp
+    # and isNaN - SY
     lastTimeStamp = (new Date @lastFrom).getTime()
-    if @continueLoadingTeasersLastTimeStamp is lastTimeStamp
+    if isNaN(lastTimeStamp) or @continueLoadingTeasersLastTimeStamp is lastTimeStamp
+      @listController.hideLazyLoader()
       return

-    lastTimeStamp = (new Date @lastFrom).getTime()
+    @continueLoadingTeasersLastTimeStamp = lastTimeStamp
     @populateActivity {slug : "before/#{lastTimeStamp}", to: lastTimeStamp}

   teasersLoaded:->


Don’t recurse into dependencies of dependencies.

We always want the top level module!

--- build/tasks/docs-task.coffee
+++ build/tasks/docs-task.coffee
@@ -11,6 +11,7 @@
     modulesPath = path.resolve(__dirname, '..', '..', 'node_modules')
     classes = {}
     fs.traverseTreeSync modulesPath, (modulePath) ->
+      return false if modulePath.match(/node_modules/g).length > 1 # dont need the dependencies of the dependencies
       return true unless path.basename(modulePath) is 'package.json'
       return true unless fs.isFileSync(modulePath)


fixed code style on servers.lib.server.oauth_url.coffee

--- servers/lib/server/oauth_url.coffee
+++ servers/lib/server/oauth_url.coffee
@@ -2,24 +2,24 @@
 {
   getClientId
   handleClientIdNotFound
-}       = require "./helpers"
+}       = require './helpers'

-module.exports = (req, res)->
+module.exports = (req, res) ->
   context   = { group: 'koding' }
   clientId  = getClientId req, res

   return handleClientIdNotFound res, req  unless clientId

-  {provider} = req.query
+  { provider } = req.query

   unless provider
-    return res.status(400).send({"message" : "provider is required"})
+    return res.status(400).send { 'message' : 'provider is required' }

   koding.fetchClient clientId, context, (client) ->
     if client.message
       return res.status(500).send client.message

-    {OAuth} = koding.models
-    OAuth.getUrl client, req.query, (err, url)->
+    { OAuth } = koding.models
+    OAuth.getUrl client, req.query, (err, url) ->
       return res.status(400).send err.message  if err
       res.status(200).send url


remove remaining part

remove remaining part

--- workers/social/lib/social/main.coffee
+++ workers/social/lib/social/main.coffee
@@ -108,7 +108,3 @@
 app.post '/xhr', koding.expressify()

 app.listen argv.p
-
-setTimeout ->
-  tagModifier = require './tagmodifier'
-, 1000


tweak dimensions a bit more

tweak dimensions a bit more

--- views/gorillas.coffee
+++ views/gorillas.coffee
@@ -2,28 +2,26 @@
   constructor:(context, color) ->
     @context = context
     @width = 100
-    @height = 150
+    @height = 135
     @color = color

   draw:(x,y) ->
     @context.fillStyle = @color
-    @height = @height + y - 20
+    @height = @height + y
     @context.fillRect x, 640-@height, @width, @height
     @build_windows(x, y)

   build_windows:(x, y) ->
-    height = 600 - @height + y
-    times = Math.round((@height)/46)
-    console.log times
+    times = Math.round (@height)/31
     windows = [ 10, 25, 40, 55, 70, 85 ]
     current_distance = 30
-    total_height = 0
+    total_height = 30
     @context.fillStyle = '#FFFF00'
-    row = 0
+    row = 1
     loop
+      break if times < row
+      @create_window x+z, 620+total_height-@height for z in windows
       total_height += current_distance
-      @create_window x+z, 625+total_height-@height for z in windows
-      break if row>times
       row++

   create_window:(x, y) ->
@@ -45,23 +43,23 @@

 window.onload = ->
   painter = new Painter
-  painter.draw_building(40, 130)
-  painter.draw_building(142, 90)
+  painter.draw_building(40, 145)
+  painter.draw_building(142, 105)

   painter.set_color '#800000'
-  painter.draw_building(244, 150)
-  painter.draw_building(346, 140)
-  painter.draw_building(448, 80)
+  painter.draw_building(244, 165)
+  painter.draw_building(346, 155)
+  painter.draw_building(448, 95)

   painter.set_color '#C0C0C0'
-  painter.draw_building(550, 0)
+  painter.draw_building(550, 15)

   painter.set_color '#800000'
-  painter.draw_building(652, 200)
+  painter.draw_building(652, 215)

   painter.set_color '#C0C0C0'
-  painter.draw_building(754, 40)
+  painter.draw_building(754, 55)

   painter.set_color '#800000'
-  painter.draw_building(856, 30)
-  painter.draw_building(958, -15)
+  painter.draw_building(856, 45)
+  painter.draw_building(958, 0)


Change scripts block to allow custom attributes

--- src/lib/collections/scripts.coffee
+++ src/lib/collections/scripts.coffee
@@ -13,12 +13,12 @@
 		# Prepare
 		opts or= {}
 		opts.defer ?= true
+		opts.attrs or= ''
 		values = [values]  unless _.isArray(values)
-		attrs = ''

 		# Build attrs
 		if opts.defer
-			attrs += """defer="defer" """
+			opts.attrs += """defer="defer" """

 		# Convert urls into script element html
 		for value,key in values
@@ -28,12 +28,12 @@
 				else if value.indexOf(' ') is -1
 					# we are a url
 					values[key] = """
-						<script #{attrs} src="#{value}"></script>
+						<script #{opts.attrs} src="#{value}"></script>
 						"""
 				else
 					# we are javascript not in a script element
 					values[key] = """
-						<script #{attrs}>#{value}</script>
+						<script #{opts.attrs}>#{value}</script>
 						"""

 		# Call the super with our values


Remove erroneous testing code

Remove erroneous testing code


trying more fixes for IE8

trying more fixes for IE8

Former-commit-id: 30066820625fac554878055258cb3a1ff1bc9d54 [formerly 353b922223eb70e19222358abb931638dc719923] [formerly b277b1170cd91736541308f7ac37735121584390 [formerly b277b1170cd91736541308f7ac37735121584390 [formerly 03770b668c165c798e3186642257099cea29bf55]]]
Former-commit-id: fc3dc188c446fb518f2de3a45cf265fc99aca917 [formerly 0435dee1e76cbc0e20a4fbfbefdad879dba90b87]
Former-commit-id: 963da69288da82b4ac018e374d7289fe3ae8baff
Former-commit-id: 0f37bc4639c6adf18b1831f36b5a2195ef6b319d

--- app/assets/javascripts/main.js.coffee
+++ app/assets/javascripts/main.js.coffee
@@ -134,26 +134,29 @@
   yPos = 100
   [xPos, yPos]

+window.handleOpenIdResponse = (parameters) ->
+  parameters.user = parameters.user.user
+  ConsiderIt.app.usermanagerview.handle_third_party_callback(parameters)
+
+window.pollLoginPopup = ->
+  console.log '*'
+  console.log window.location
+  console.log window.openidpopup.location
+  if window.location.origin == window.openidpopup.origin && window.openidpopup? && window.openidpopup.open_id_params?
+    window.handleOpenIdResponse(window.openidpopup.open_id_params)
+    window.openidpopup.close()
+    window.openidpopup = null
+    window.clearInterval(window.polling_interval)
+
 window.openPopupWindow = (url) ->
   window.openidpopup = window.open(url, 'openid_popup', 'width=450,height=500,location=1,status=1,resizable=yes')
   window.openidpopup.open_id_params = null
   coords = getCenteredCoords(450,500)
   openidpopup.moveTo(coords[0],coords[1])
-  console.log window.openidpopup
   window.polling_interval = window.setInterval ->
     window.pollLoginPopup()
   , 200

-window.pollLoginPopup = ->
-  if window.openidpopup? && window.openidpopup.open_id_params?
-    window.handleOpenIdResponse(window.openidpopup.open_id_params)
-    window.openidpopup.close()
-    window.openidpopup = null
-    window.clearInterval(window.polling_interval)
-
-window.handleOpenIdResponse = (parameters) ->
-  parameters.user = parameters.user.user
-  ConsiderIt.app.usermanagerview.handle_third_party_callback(parameters)

 $.event.special.destroyed =
   remove: (o) -> o.handler() if o.handler


Suggest a schema name from the file name on new schema form

--- app/assets/javascripts/schemas.coffee
+++ app/assets/javascripts/schemas.coffee
@@ -21,10 +21,15 @@
     $('#label-file').hide()
     return

+titleize = (string) ->
+  string.split(/[\s|_]/).map((s) -> s[0].toUpperCase() + s.slice(1)).join('')
+
+
 $(document).on 'change', ':file', ->
-  input = $(this)
+  input = $(this)
   numFiles = if input.get(0).files then input.get(0).files.length else 1
   label = input.val().replace(/\\/g, '/').replace(/.*\//, '')
+  $('#schema_name').val(titleize(label.replace(/\..*$/, '')))
   input.trigger 'fileselect', [
     numFiles
     label


Refined ChromeWebUSBService

Refined ChromeWebUSBService

--- app/coffee/modules/usb/service.coffee
+++ app/coffee/modules/usb/service.coffee
@@ -16,8 +16,9 @@
 class ChromeWebUsbService extends Marionette.Service

   radioRequests:
-    'usb devices':  'getDevices'
-    'usb read':     'read'
+    'usb devices':      'getDevices'
+    'usb read:macro':   'readMacro'
+    'usb write:macro':  'writeMacro'

   # getDevices
   getDevices: ->
@@ -84,7 +85,8 @@

       )

-  read: ->
+  # readMacro
+  readMacro: (macroIndex = 0x0000) ->
     # Returns a Promise to manage asynchonous behavior
     return new Promise (resolve, reject) =>

@@ -92,32 +94,44 @@
       # TODO - abstract the controlTransferIn request object into a constant (cloned each time)
       d.controlTransferIn(
         {
-          'requestType': 'vendor',
-          'recipient': 'device',
-          'request': 0x03,
-          'value': 0x0000,
-          'index': 0x02
-        }, 128
+          'requestType':  'vendor',
+          'recipient':    'device',
+          'request':      0x03,
+          'value':        macroIndex,
+          'index':        0x02
+        }, 128 # QUESTION - why "128"?
       )
       .then( (response) =>
         return resolve(new Int8Array(response.data.buffer))
       )
       .catch( (err) =>
-        console.log 'ERROR READING DEVICE'
+        console.log 'ERROR READING MACRO'
         return reject(err)
       )

-  # send
-  send: ->
+  # sendMacro
+  sendMacro: (data) ->

-    window.d.controlTransferOut({
-        requestType:  'vendor',
-        recipient:    'device',
-        request:      0x03,
-        value:        0x0013, # Whatever we want (to some extent)
-        index:        0x0001  # TODO - We can use index for the key the macro corresponds to (low-byte = key, high-byte = number of actions in the macro)
-    }, data);
+    # Returns a Promise to manage asynchonous behavior
+    return new Promise (resolve, reject) =>
+
+      d.controlTransferOut(
+        {
+          requestType:  'vendor',
+          recipient:    'device',
+          request:      0x03,
+          value:        0x0013, # Whatever we want (to some extent)
+          index:        0x0001  # TODO - We can use index for the key the macro corresponds to (low-byte = key, high-byte = number of actions in the macro)
+        }, new Uint8Array(data).buffer
+      )
+      .then( (response) =>
+        return resolve(response)
+      )
+      .catch( (err) =>
+        console.log 'ERROR SENDING MACRO'
+        return reject(err)
+      )

 # # # #


Remove unused function

Remove unused function

--- app/javascript/src/legacy/post.coffee
+++ app/javascript/src/legacy/post.coffee
@@ -110,30 +110,6 @@
       e.addClassName 'has-parent'
     else
       e.removeClassName 'has-parent'
-    return
-  update: (post_id, params, finished) ->
-    notice 'Updating post #' + post_id
-    params['id'] = post_id
-    new (Ajax.Request)('/post/update.json',
-      parameters: params
-      onComplete: (resp) ->
-        resp = resp.responseJSON
-        if resp.success
-          notice 'Post updated'
-          # Update the stored post.
-          Post.register resp.post
-          Post.register_tags resp.tags
-          Post.update_styles resp.post
-          element = element = $$('#p' + post_id + ' > .directlink')
-          if element.length > 0
-            element[0].addClassName 'tag-script-applied'
-            Post.applied_list.push element[0]
-          if finished
-            finished resp.post
-        else
-          notice 'Error: ' + resp.reason
-        return
-)
     return

   activate_posts: (postIds, finished) ->


add note and complete options

add note and complete options

--- app/scripts/component/validate.coffee
+++ app/scripts/component/validate.coffee
@@ -6,28 +6,33 @@
     ###
       usage: $form.validateTip(options)
       options =
-        identifie: {String} || [required]   # 需要校验的表单项
-        direct: 'up' or 'left'              # tip 的方向， 默认是 left, 可以在 identifie 上指定 data-direct
-        interval:  {integer}                # tip 显示的时间，毫秒级，默认是 0 ，可以在 idenifie 上指定 data-interval
+        identifie: {String} || [required]   # 需要校验的表单项，选择器
+        direct: 'left' or 'up'              # tip 的方向， 默认是 left, 可以在 identifie 上指定 data-direct
+        interval:  {Integer} || 0           # tip 显示的时间，毫秒级，默认是 0 (不自动消失)，可以在 idenifie 上指定 data-interval
+        scope: 'all' or 'one'               # tip 的 提示方式，所有一起提示还是提示第一个, 默认是提示所有
         before: {Function}                  # 表单检验之前执行
         after: {Function}                   # 表单校验之后执行，只有返回 True 表单才可能被提交
     ###
-    validate.options = options = options || {}
+    options = options || {}
     identifie = options.identifie || '[required]'
+    options.direct = options.direct || 'left'
+    options.interval = options.interval || 0
+    options.scope = options.scope || 'all'
     before = options.before || -> true
     after = options.after || -> true
     errorCallBack = options.errorCallBack || (fields) ->
+    validate.options = options

-    @.each ->
+    @each ->
       $form = $(@)
       $items = validate.fields(identifie, $form)
       validate.novalidate($form)
       $form.on "submit", (evt)->
         evt.preventDefault()
-        before(@, $items)
+        before(this, $items)
         validate.validateForm($form, $items)
         if validate.errorFields.length
           evt.preventDefault()
           evt.stopImmediatePropagation()
         else
-          after(@, $items)
+          after(this, $items)


ITS USEFUL I SWEAR

ITS USEFUL I SWEAR

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -172,6 +172,7 @@
             "Laughter, sorrow, nonsense",
             "#arrakis",
             "#dickbutt",
+            "butts",
           ]
           Twitter.mediaTweet strip.info.image_jpeg, "#{prefix} - #{strip.info.url}", (err, tweet, url) =>
             sorry = msg.random ["sorry", "it's your fault"]


We got another joker, everyone

We got another joker, everyone

And they think they're funny about it again.

--- scripts/ayp.coffee
+++ scripts/ayp.coffee
@@ -109,6 +109,9 @@

   if /meowth/i.test(name)
     name = 'o_rly'
+
+  if /twerk/i.test(name)
+    name = 'high_twerk'

   return name


typofix

typofix

--- client/app/locales/en.coffee
+++ client/app/locales/en.coffee
@@ -6,7 +6,7 @@
     "Download" : "Download"
     "Edit" : "Edit"
     "Stop editing" : "Stop editing"
-    "It will appears on your homepage." : "It will appears on your homepage."
+    "It will appear on your homepage." : "It will appear on your homepage."
     "Make it Hidden" : "hidden"
     "Make it Private" : "private"
     "Make it Public" : "public"
@@ -87,7 +87,7 @@
     "perm rw album" : "browse and upload photos"
     "change notif" : """
 Check this box to be notified when a contact
-add a photo to this album."""
+adds a photo to this album."""
     "send email hint": "Notification emails will be sent one time on save"
     "yes" : "Yes"
     "no" : "No"


divers typos

divers typos

GoofyNote : attention, cette application ne fournit pas des calendriers mais des agendas.

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -24,8 +24,8 @@
     'days'                              : 'jours'
     "Edit"                              : "Modifier"
     "Email"                             : "Email"
-    "Import"                            : "Import"
-    "Export"                            : "Export"
+    "Import"                            : "Importation"
+    "Export"                            : "Exportation"
     "show"                              : "Montrer"
     "hide"                              : "Cacher"
     "List"                              : "Liste"
@@ -39,12 +39,12 @@
     "Switch to List"                    : "Basculer en mode Liste"
     "Switch to Calendar"                : "Basculer en mode Agenda"
     "time"                              : "Heure"
-    "Today"                             : "Aujourd'hui"
-    'today'                             : 'aujourd\'hui'
-    "What should I remind you ?"        : "Que dois-je vous rappeler ?"
+    "Today"                             : "Aujourd’hui"
+    'today'                             : 'aujourd’hui'
+    "What should I remind you ?"        : "Que dois-je vous rappeler ?"
     "import your icalendar file"        : "Importer votre fichier iCalendar"
     "select an icalendar file"          : "Sélectionner un fichier iCalendar"
-    "confirm import"                    : "Confirmer l'import"
+    "confirm import"                    : "Confirmer l'importation"
     "cancel"                            : "Annuler"
     "Create"                            : "Créer"
     "Events to import"                  : "Évènements à importer"
@@ -62,16 +62,16 @@
     "BOTH"                              : "Email & notification"
     "display previous events"           : "Afficher les évènements précédents"
     "display next events"           : "Afficher les évènements suivants"
-    "are you sure"                      : "Êtes-vous sûr(e) ?"
-    "confirm delete calendar"           : "Vous êtes sur le point de supprimer tous les événements associés à %{calendarName}. Êtes-vous sûr(e) ?"
-    "confirm delete selected calendars" : "Vous êtes sur le point de supprimer tous les calendriers sélectionnés. Êtes-vous sûr(e) ?"
+    "are you sure"                      : "Êtes-vous sûr(e) ?"
+    "confirm delete calendar"           : "Vous êtes sur le point de supprimer tous les événements associés à %{calendarName}. Êtes-vous sûr(e) ?"
+    "confirm delete selected calendars" : "Vous êtes sur le point de supprimer tous les agendas sélectionnés. Êtes-vous sûr(e) ?"
     "advanced"                          : "Détails"
     "enter email"                       : "Entrer l'adresse email"
     "ON"                                : "activée"
     "OFF"                               : "désactivée"
     "no description"                    : "Sans description"
-    "add calendar"                      : "Ajouter un calendrier"
-    "new calendar"                      : "Nouveau calendrier"
+    "add calendar"                      : "Ajouter un agenda"
+    "new calendar"                      : "Nouvel agenda"
     "multiple actions"                  : "Actions multiples"

 # RULE related
@@ -132,11 +132,11 @@
     "To"                                : "À"
     "All day, until"                    : "Journée entière, jusqu'au"
     "All one day"                       : "Toute la journée du"
-    'Reminders before the event'        : 'Rappels avant l\'évènement'
+    'Reminders before the event'        : 'Rappels avant l’évènement'
     "reminder"                          : "Rappel"

 # CONFIRM MODAL
-    'send mails question'               : 'Envoyer un email de notification à :'
+    'send mails question'               : 'Envoyer un email de notification à :'
     'modal send mails'                  : 'Envoyer une notification'
     'yes'                               : 'Oui'
     'no'                                : 'Non'
@@ -154,18 +154,18 @@
 # SYNC
     "synchronization"                   : "Synchronisation"
     "mobile sync"                       : "Synchro Mobile (CalDAV)"
-    "import an ical file"               : "Pour importer un fichier iCal dans votre agenda, commencez par cliquer sur ce bouton pour le précharger :"
-    "link imported events with calendar": "Lier les évènements à importer avec le calendrier suivant :"
-    "download a copy of your calendar"  : "Sélectionner un agenda puis cliquer sur le bouton exporter pour télécharger une copie de l'agenda comme un fichier iCal :"
+    "import an ical file"               : "Pour importer un fichier iCal dans votre agenda, commencez par cliquer sur ce bouton pour le précharger :"
+    "link imported events with calendar": "Lier les évènements à importer avec l'agenda suivant :"
+    "download a copy of your calendar"  : "Sélectionner un agenda puis cliquer sur le bouton exporter pour télécharger une copie de l'agenda comme un fichier iCal :"
     "icalendar export"                  : "Exporter ICalendar"
     "icalendar import"                  : "Importer ICalendar"
     "to sync your cal with"             : "Pour synchroniser votre agenda avec votre mobile vous devez :"
-    "sync headline with data"           : "Pour synchroniser votre agenda, utilisez les identifiants suivant :"
+    "sync headline with data"           : "Pour synchroniser votre agenda, utilisez les identifiants suivants :"
     "sync url"                          : "URL :"
     "sync login"                        : "Nom d'utilisateur :"
     "sync password"                     : "Mot de passe :"
-    "sync help"                         : "Vous êtes perdu(e) ? Suivez le"
-    "sync help link"                    : "guide pas à pas !"
+    "sync help"                         : "Vous êtes perdu(e) ? Suivez le"
+    "sync help link"                    : "guide pas à pas !"
     "install the sync module"           : "Installer le module Sync depuis l'applithèque."
     "connect to it and follow"          : "Vous connecter et suivre les instructions relatives à CalDAV."
     "some event fail to save"           : "La sauvegarde d'un évènement a échoué."
@@ -201,17 +201,17 @@
     'Nov'                               : 'Nov'
     'Dec'                               : 'Déc'

-    'calendar exist error': 'Un calendrier "Nouveau Calendrier" existe déjà.'
+    'calendar exist error': 'Un agenda intitulé "Nouvel agenda" existe déjà.'

 # Emails
     'email date format': 'DD/MM/YYYY [à] HH[h]mm'
     'email date format allday': 'DD/MM/YYYY [toute la journée]'
     'email invitation title': "Invitation à l'évènement \"%{description}\""
     'email invitation content': """
-        Bonjour, je souhaiterais vous inviter à l'évènement suivant :
+        Bonjour, je souhaiterais vous inviter à l'évènement suivant :
         %{description} %{place}
         Le %{date}
-        Serez-vous présent ?
+        Serez-vous présent ?

         Oui
         %{url}?status=ACCEPTED&key=%{key}
@@ -221,7 +221,7 @@
     """
     'email update title': "L'évènement \"%{description}\" a changé"
     'email update content': """
-        Un évènement auquel vous participez a changé :
+        Un évènement auquel vous participez a changé :
         %{description} %{place}
         Le %{date}

@@ -232,7 +232,7 @@
         %{url}?status=DECLINED&key=%{key}"""
     'email delete title': 'Cet évènement a été annulé : %{description}'
     'email delete content': """
-        Cet évènement a été annulé :
+        Cet évènement a été annulé :
         %{description} %{place}
         Le %{date}
     """


Fix specifying channel

Fix specifying channel

--- scripts/net.coffee
+++ scripts/net.coffee
@@ -25,7 +25,7 @@
           "Domains:\n" + result.domains.join("\n") if result.domains?

         if robot.adapter.post?
-          message = {room: msg.envelope.user.room}
+          message = {room: "#" + msg.envelope.user.room}
           attachment = {
             pretext: "*#{host}*"
             fallback: text


changed coffee lines separation

changed coffee lines separation

--- src/angular-lusty-datepicker.coffee
+++ src/angular-lusty-datepicker.coffee
@@ -1,7 +1,7 @@
 angular.module "lustyDatepicker", ["pasvaz.bindonce"]

-angular.module("lustyDatepicker").directive "lustyDatepicker",
-  ["$compile", ($compile) -> pickerTemplate = """
+angular.module("lustyDatepicker").directive "lustyDatepicker", [
+  "$compile", ($compile) -> pickerTemplate = """
     <div class="lusty-picker__wrapper">
       <button type="button" class="lusty-picker__prev-month"
         ng-click="moveMonth(-1)">


chore(): remove unused global

chore(): remove unused global

--- src/angular-vertxbus-adapter.coffee
+++ src/angular-vertxbus-adapter.coffee
@@ -1,5 +1,5 @@
 # jshint undef: true
-# global SockJS: false, vertx: false, EventBus: false
+# global SockJS: false, vertx: false

 ###
   An AngularJS wrapper for projects using the VertX Event Bus


package index might be null

package index might be null

--- client/modules/pkgman.coffee
+++ client/modules/pkgman.coffee
@@ -100,4 +100,4 @@
 exports.invokePackage = (path, hook, args...) ->
 	pathIndex?[path]?[hook]? args...

-exports.packagesImplementing = (hook) -> packageIndex[hook] ? []
+exports.packagesImplementing = (hook) -> packageIndex?[hook] ? []


parseFloat() instead of parseInt() for CPUs in the new deploy UI

--- SingularityUI/app/views/newDeploy.coffee
+++ SingularityUI/app/views/newDeploy.coffee
@@ -53,7 +53,7 @@
         deployObject.id        = @$('#id').val()

         deployObject.resources =
-            cpus:     parseInt(@valOrNothing '#cpus') or config.defaultCpus
+            cpus:     parseFloat(@valOrNothing '#cpus') or config.defaultCpus
             memoryMb: parseInt(@valOrNothing '#memory-mb') or config.defaultMemory
             numPorts: parseInt(@valOrNothing '#num-ports') or 0


removed unnecessary if statement

removed unnecessary if statement

--- src/middleware/messageStore.coffee
+++ src/middleware/messageStore.coffee
@@ -100,8 +100,6 @@
 exports.koaMiddleware =  `function *storeMiddleware(next) {
 		var saveTransaction = Q.denodeify(exports.storeTransaction);
 		yield saveTransaction(this);
-		if (this.transactionId){
-			yield next;
-		}
+		yield next;
 		exports.storeResponse(this, function(){});
 	}`


Ensure correct user id when showing loaded card

--- resources/assets/coffee/_classes/user-card.coffee
+++ resources/assets/coffee/_classes/user-card.coffee
@@ -25,29 +25,34 @@

   onMouseOver: (event) =>
     el = event.currentTarget
+    userId = el.getAttribute('data-user-id')

     # when qtip has already been init for current element
-    if el._tooltip
+    if el._tooltip?
       api = $(el).qtip('api')

-      # disable existing cards when entering 'mobile' mode
-      if osu.isMobile()
-        event.preventDefault()
-        api.disable()
-        el._disable_card = true
+      if el._tooltip == userId
+        # disable existing cards when entering 'mobile' mode
+        if osu.isMobile()
+          event.preventDefault()
+          api.disable()
+          el._disable_card = true
+        else
+          if el._disable_card
+            el._disable_card = false
+            api.enable()
+            $(el).trigger('mouseover')
+
+        return
       else
-        if el._disable_card
-          el._disable_card = false
-          api.enable()
-          $(el).trigger('mouseover')
-
-      return
+        # wrong userId, destroy current tooltip
+        api.destroy()

     # disable usercards on mobile
     if osu.isMobile()
       return

-    el._tooltip = true
+    el._tooltip = userId

     at = el.getAttribute('data-tooltip-position') ? 'right center'
     my = switch at
@@ -63,7 +68,6 @@
         height: 130
       content:
         text: (event, api) =>
-          userId = el.getAttribute('data-user-id')
           $.ajax
             url: laroute.route 'users.card', user: userId
           .done (content) =>


unused js removed

unused js removed

--- softwareplanet-cms/app/assets/javascripts/admin_scripts/cms/pages.js.coffee
+++ softwareplanet-cms/app/assets/javascripts/admin_scripts/cms/pages.js.coffee
@@ -1,25 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
-
-$(document).ready ->
-  update_header_shadow()
-  $(window).scroll ->
-    update_header_shadow()
-
-window.update_header_shadow = () ->
-  if $(window).scrollTop() == 0
-    $(".navbar-inner").addClass("bottom-shadow-off") unless $(".navbar-inner").hasClass("bottom-shadow-off")
-    $(".navbar-inner").removeClass("bottom-shadow-on")
-    $(".navbar-inner").addClass("bottom-border-on")
-    $("#go-top").stop(true, true).fadeOut("slow");
-  else
-    $(".navbar-inner").removeClass("bottom-shadow-off")
-    $(".navbar-inner").addClass("bottom-shadow-on")
-    $(".navbar-inner").removeClass("bottom-border-on")
-    if $(window).scrollTop() >= 300
-      #$("#go-top").stop(true, true).fadeIn("slow");
-      $("#go-top").fadeIn("slow");
-    else if $(window).scrollTop() < 300
-      #$("#go-top").stop(true, true).fadeOut("slow");
-      $("#go-top").fadeOut("slow");


kiosk export pdf with .pdf extension

kiosk export pdf with .pdf extension

--- tools/utils.coffee
+++ tools/utils.coffee
@@ -273,6 +273,8 @@
       else if (type.indexOf 'text/plain')==0 and dir==''
         # top-level document
         path = path+'.json'
+      else if (type.indexOf 'application/pdf')==0
+        path = path+'.pdf'
       else
         console.log "Missing extension for #{path} type #{type}"
     else


Make the camera stick to the rear of the player

--- src/public/scripts/app.game.coffee
+++ src/public/scripts/app.game.coffee
@@ -171,7 +171,6 @@
 		update: ( timestamp ) =>
 			dt = (timestamp - @lastUpdateTime) / 1000

-
 			# If any keys are pressed, apply angular forces to the player
 			if @_aKey
 				@player?.cannon.addAngularForce(new Three.Vector3(0, 1, 0))
@@ -191,7 +190,10 @@

 			# Set the camera to follow the player
 			if @player?
-				@camera.position = @player.position.clone().add(@camera.position.clone().sub(@player.position).normalize().multiplyScalar(30))
+				x = 30 * -Math.cos(@player.cannon.rotation.y)
+				z = 30 * Math.sin(@player.cannon.rotation.y)
+
+				@camera.position.lerp(@player.position.clone().add(new Three.Vector3(x, 2, z)), .05)
 				@camera.lookAt(@player.position)

 			# Render the scene


Delete parse.coffee

Delete parse.coffee

更新為js檔案

--- src/devtools/panel/app/parse.coffee
+++ src/devtools/panel/app/parse.coffee
@@ -1,611 +0,0 @@
-# 数据处理器
-((exports)->
-  exports.tohken = {} if !exports.tohken
-  exports.tohken.parse = {} if !exports.tohken.parse
-  # 处理资源
-  exports.tohken.parse.resource = (resource)->
-    @data['resource']['filling']      = true unless @data['resource']['filling']
-    @data['resource']['charcoal']     = resource['charcoal']
-    @data['resource']['steel']        = resource['steel']
-    @data['resource']['coolant']      = resource['coolant']
-    @data['resource']['file']         = resource['file']
-    @data['resource']['bill']         = resource['bill']
-    @data['resource']['max_resource'] = resource['max_resource']
-    # 记录更新时间
-    @data['resource']['last_update']  = Date.now()
-    # 清除虚拟数据
-    @data['resource']['vcharcoal']    = 0
-    @data['resource']['vsteel']       = 0
-    @data['resource']['vcoolant']     = 0
-    @data['resource']['vfile']        = 0
-    'done'
-  # 处理刀剑信息
-  exports.tohken.parse.sword = (sword)->
-    @data['sword']['filling'] = true unless @data['sword']['filling']
-    # 准备一个空的分支
-    branch = {}
-    # 拆包循环
-    _.forEach sword, (v, k)=>
-      # 判断数据是否已经存在
-      pre = if @data['sword']['data'][k] then _.clone(@data['sword']['data'][k]) else _.clone(exports.tohken.data['sword']['template'])
-      # 填充数据
-      _.forEach pre, (vi, ki)=>
-        # 向目标合并数据
-        if _.has(v, ki)
-          pre[ki] = v[ki]
-          # 清空模拟数据
-        if ki == 'vfatigue'
-          pre[ki] = 0
-        if ki == 'next_exp'
-          pre[ki] = exports.tohken.define.upexp[parseInt(pre['level'], 10)] - parseInt(pre['exp'], 10)
-          pre[ki] = 0 if parseInt(v['level'], 10) == 99
-      # 向分支合并数据
-      branch[k] = pre
-      'done'
-    # 用分支替换主线
-    @data['sword']['data']          = branch
-    # 记录更新时间
-    @data['sword']['last_update']   = Date.now()
-    exports.tohken.parse.view.call this, 'party'
-    'done'
-  # 处理队伍信息
-  exports.tohken.parse.party = (party)->
-    @data['party']['filling']       = true unless @data['party']['filling']
-    @data['party']['data']          = party
-    # 记录更新时间
-    @data['party']['last_update']   = Date.now()
-    exports.tohken.parse.view.call this, 'party'
-    'done'
-  # 处理刀装信息
-  exports.tohken.parse.equip = (equip)->
-    @data['equip']['filling']       = true unless @data['equip']['filling']
-    @data['equip']['data']          = equip
-    # 记录更新时间
-    @data['equip']['last_update']   = Date.now()
-    exports.tohken.parse.view.call this, 'party'
-    exports.tohken.parse.view.call this, 'equip'
-    'done'
-  # 处理锻造信息
-  exports.tohken.parse.forge = (forge)->
-    @data['forge']['filling']       = true unless @data['forge']['filling']
-    # 填充数据
-    @data['forge']['data'] = forge
-    # 记录
-    _.forEach forge, (v, k)=>
-      time = Date.parse "#{v['finished_at']} GMT+0900"
-      id = "#{v['slot_no']}-#{time}"
-      has = _.has @data['logs']['forge'], id
-      @data['logs']['forge'][id] = {} if !has
-      @data['logs']['forge'][id]['slot_no']   = v['slot_no']
-      @data['logs']['forge'][id]['finish_at'] = time
-      @data['logs']['forge'][id]['sword_id']  = v['sword_id']
-      @data['logs']['forge'][id]['resource']  = _.has @data['logs']['forge'][id], 'charcoal'
-      unless @data['logs']['forge'][id]['upload']
-        exports.tohken.parse.upload.call this, id, @data['logs']['forge'][id]
-      if @config['dataCache'] == 1
-        exports.tohken.store.set "TRH_DataCache", JSON.stringify(@data)
-        exports.tohken.store.set "TRH_Logs", JSON.stringify(@data['logs'])
-      'done'
-    # 记录更新时间
-    @data['forge']['last_update'] = Date.now()
-    console.log JSON.stringify(@data['logs']['forge'])
-    'done'
-  exports.tohken.parse.repair = (repair)->
-    @data['repair']['filling'] = true unless @data['repair']['filling']
-    # 填充数据
-    @data['repair']['data'] = repair
-    # 记录更新时间
-    @data['repair']['last_update'] = Date.now()
-    'done'
-  # 处理战斗数据
-  exports.tohken.parse.battle = (battle)->
-    # 好烦好烦好烦的部分啊啊啊啊啊啊啊啊啊
-    # 获取初始数据
-    # 构建一个轻型的数据模型
-    party = {}
-    leader = null
-    # 按刀取出数据存入模型
-    _.forEach battle.player.party, (v, k)->
-      sword                     = {}
-      sword['sword_id']         = v['sword_id']
-      sword['hp']               = v['hp']
-      sword['hp_max']           = v['hp_max']
-      sword['equip_serial_id1'] = v['equip_serial_id1']
-      sword['soldier1']         = v['soldier1']
-      sword['equip_serial_id2'] = v['equip_serial_id2']
-      sword['soldier2']         = v['soldier2']
-      sword['equip_serial_id3'] = v['equip_serial_id3']
-      sword['soldier3']         = v['soldier3']
-      party[v['serial_id']]     = sword
-      leader = v['serial_id'] if leader == null
-      'done'
-    # 解析战斗结果
-    result                      = {}
-    # 地图信息
-    result['battle_episode']    = @status['battle_episode']
-    result['battle_field']      = @status['battle_field']
-    result['battle_pos']        = @status['battle_pos']
-    result['battle_party']      = @status['battle_id']
-    # 新刀，MVP，Rank
-    result['get_sword_id']      = battle.result['get_sword_id']
-    result['mvp']               = battle.result['mvp']
-    result['rank']              = battle.result['rank']
-    # HP之和，兵力之和，获得经验
-    result['life']              = battle.result.player['life']
-    result['life_max']          = battle.result.player['life_max']
-    result['soldier']           = battle.result.player['soldier']
-    result['soldier_max']       = battle.result.player['soldier_max']
-    result['get_exp']           = battle.result.player['get_exp']
-    result['time']              = Date.parse "#{battle['now']} GMT+0900"
-    # ID
-    result['id']                = "#{result['battle_party']}-#{result['battle_episode']}-#{result['battle_field']}-#{result['battle_pos']}-#{result['time']}"
-    # 更新玩家数据
-    player                      = {}
-    player['level']             = battle.result.player['level']
-    player['exp']               = battle.result.player['exp']
-    # 补充刀剑数据
-    result['party_no']          = battle.result.player.party['party_no']
-    _.forEach battle.result.player.party.slot, (v, k)->
-      party[v['serial_id']]['hp']       = v['hp']
-      party[v['serial_id']]['status']   = v['status']
-      party[v['serial_id']]['exp']      = v['exp']
-      party[v['serial_id']]['level']    = v['level']
-      party[v['serial_id']]['mvp']      = if result['mvp'] == v['serial_id'] then true else false
-      party[v['serial_id']]['get_exp']  = v['get_exp']
-      'done'
-    # 刀剑情况
-    result['party'] = party
-    # 疲劳值演算
-    # 出阵时 -10
-    # 开始战斗 -3
-    _.forEach party, (v, k)->
-      party[k]['vfatigue'] = -3
-      'done'
-    # S时+4  A时+3  B时+2  C时+1，败北不增加
-    party[result['mvp']]['vfatigue'] += 10 if parseInt(result['mvp'], 10) != 0
-    # rank: 2-s 3-a 4-b 5-c
-    switch parseInt(result['rank'], 10)
-      when 2
-        _.forEach party, (v, k)->
-          party[k]['vfatigue'] += 4
-          party[k]['vfatigue'] += 3 if leader == k
-      when 3
-        _.forEach party, (v, k)->
-          party[k]['vfatigue'] += 3
-          party[k]['vfatigue'] += 3 if leader == k
-      when 4
-        _.forEach party, (v, k)->
-          party[k]['vfatigue'] += 2
-          party[k]['vfatigue'] += 3 if leader == k
-      when 5
-        _.forEach party, (v, k)->
-          party[k]['vfatigue'] += 1
-          party[k]['vfatigue'] += 3 if leader == k
-    # 胜利时队长+3，失败不增加
-    # 以上结果待验证
-    notify = []
-    _.forEach party, (v, k)->
-      # 刀装破坏提醒
-      notify.push {
-        sword: v['sword_id']
-        type: 'equip'
-        value: 1
-      } if v['equip_serial_id1'] != null && v['soldier1'] == 0
-      notify.push {
-        sword: v['sword_id']
-        type: 'equip'
-        value: 2
-      } if v['equip_serial_id2'] != null && v['soldier2'] == 0
-      notify.push {
-        sword: v['sword_id']
-        type: 'equip'
-        value: 3
-      } if v['equip_serial_id3'] != null && v['soldier3'] == 0
-      # 损伤提醒
-      notify.push {
-        sword: v['sword_id']
-        type: 'broken'
-        value: 0
-      } if v['hp'] < v['hp_max'] && v['status'] == 0
-      # 小破提醒
-      notify.push {
-        sword: v['sword_id']
-        type: 'broken'
-        value: v['status']
-      } if v['status'] != 0
-      'done'
-    # 获得新刀提醒
-    notify.push {
-      sword: result['get_sword_id']
-      type: 'get'
-      value: result['get_sword_id']
-    } if result['get_sword_id'] != 0
-    asia = [3, 5, 7, 9, 25, 31, 33, 55, 57, 59, 65, 79, 103, 105, 132, 134, 138]
-    europe = [3, 5, 31, 33, 57, 103, 105]
-    # 推送提醒
-    _.forEach notify, (n)=>
-      switch n['type']
-        when 'equip'
-          # 刀装提醒
-          return if @config['notify_broken'] < 5
-          @sendMessage {
-            type: 'notify'
-            message: {
-              title: "#{exports.tohken.define.tohkens[n['sword']]['name']}刀装破碎！"
-              message: "请注意损失情况避免碎刀情况发生！"
-              context: "可以在设置中调整提醒等级"
-            }
-          }
-        when 'broken'
-          # 受伤提醒
-          switch n['value']
-            when 0
-              return if @config['notify_broken'] < 4
-              @sendMessage {
-                type: 'notify'
-                message: {
-                  title: "#{exports.tohken.define.tohkens[n['sword']]['name']}已经受到了伤害！"
-                  message: "请注意损失情况避免碎刀情况发生！"
-                  context: "可以在设置中调整提醒等级"
-                }
-              }
-            when 1
-              return if @config['notify_broken'] < 3
-              @sendMessage {
-                type: 'notify'
-                message: {
-                  title: "#{exports.tohken.define.tohkens[n['sword']]['name']}已经轻伤！"
-                  message: "请注意损失情况避免碎刀情况发生！"
-                  context: "可以在设置中调整提醒等级"
-                }
-              }
-            when 2
-              return if @config['notify_broken'] < 2
-              @sendMessage {
-                type: 'notify'
-                message: {
-                  title: "#{exports.tohken.define.tohkens[n['sword']]['name']}已经中伤！"
-                  message: "请注意损失情况避免碎刀情况发生！"
-                  context: "可以在设置中调整提醒等级"
-                }
-              }
-            when 3
-              return if @config['notify_broken'] < 1
-              @sendMessage {
-                type: 'notify'
-                message: {
-                  title: "#{exports.tohken.define.tohkens[n['sword']]['name']}已经重伤！"
-                  message: "请注意损失情况避免碎刀情况发生！"
-                  context: "可以在设置中调整提醒等级"
-                }
-              }
-              @status["need_reload"] = true
-        when 'get'
-          return if @config['notify_getnew'] == 0
-          if @config['notify_getnew'] == 1
-            has = _.findIndex asia, (i)-> return true if i==parseInt(n['sword'])
-            return if has == -1
-          if @config['notify_getnew'] == 2
-            has = _.findIndex europe, (i)-> return true if i==parseInt(n['sword'])
-            return if has == -1
-          @sendMessage {
-            type: 'notify'
-            message: {
-              title: "你捞到了一把新刀！"
-              message: "#{exports.tohken.define.tohkens[n['sword']]['name']}"
-              context: "可以在设置中调整提醒等级"
-            }
-          }
-      'done'
-    # TODO: 提醒推送模块
-    # 更新视图
-    # 更新玩家信息
-    @view['player']['level'] = player['level']
-    @data['player']['level'] = player['level']
-    @data['player']['exp']   = player['exp']
-    # 更新队伍信息
-    target = @view['party']['data'][result['party_no']]['slot']
-    _.forEach target, (v, k)=>
-      return if v['serial_id'] == null
-      sword = target[k]
-      sword['hp']               = party[v['serial_id']]['hp']
-      sword['hp_max']           = party[v['serial_id']]['hp_max']
-      sword['exp']              = party[v['serial_id']]['exp']
-      sword['level']            = party[v['serial_id']]['level']
-      sword['equip_serial_id1'] = party[v['serial_id']]['equip_serial_id1']
-      sword['equip_serial_id2'] = party[v['serial_id']]['equip_serial_id2']
-      sword['equip_serial_id3'] = party[v['serial_id']]['equip_serial_id3']
-      sword['next_exp'] = exports.tohken.define.upexp[parseInt(sword['level'], 10)] - parseInt(sword['exp'], 10)
-      sword['next_exp'] = 0 if parseInt(sword['level'], 10) == 99
-      # 更新演算疲劳
-      fatigue = parseInt(sword['fatigue'], 10) + parseInt(party[v['serial_id']]['vfatigue'], 10)
-      sword['fatigue'] = if fatigue > 100 || fatigue < 0 then 100 else fatigue
-      sword['fatigue'] = 100 if fatigue > 100
-      sword['fatigue'] = 0   if fatigue < 0
-      # 更新刀装数据
-      if party[v['serial_id']]['equip_serial_id1'] != null
-        sword['equip_1']['soldier_now'] = party[v['serial_id']]['soldier1']
-      if party[v['serial_id']]['equip_serial_id2'] != null
-        sword['equip_2']['soldier_now'] = party[v['serial_id']]['soldier2']
-      if party[v['serial_id']]['equip_serial_id3'] != null
-        sword['equip_3']['soldier_now'] = party[v['serial_id']]['soldier3']
-      'done'
-    # 向数据库中合并数据
-    # 更新队伍信息
-    _.forEach party, (v, k)=>
-      sword = @data['sword']['data'][k]
-      sword['hp']               = v['hp']
-      sword['hp_max']           = v['hp_max']
-      sword['exp']              = v['exp']
-      sword['level']            = v['level']
-      sword['equip_serial_id1'] = v['equip_serial_id1']
-      sword['equip_serial_id2'] = v['equip_serial_id2']
-      sword['equip_serial_id3'] = v['equip_serial_id3']
-      # 更新演算疲劳
-      sword['vfatigue']        += v['vfatigue']
-      'done'
-    # 记录日志
-    @data['logs']['battle'][result['id']] = result
-    console.log @data['logs']['battle']
-    if @config['dataCache'] == 1
-      exports.tohken.store.set "TRH_DataCache", JSON.stringify(@data)
-      exports.tohken.store.set "TRH_Logs", JSON.stringify(@data['logs'])
-    # 上传日志
-    preupload = {
-      "sword":    "#{result['get_sword_id']}"
-      "episode":  "#{result['battle_episode']}"
-      "field":    "#{result['battle_field']}"
-      "pos":      "#{result['battle_pos']}"
-      "rank":     "#{result['rank']}"
-    }
-    exports.tohken.parse.uploadmap.call this, preupload
-    'done'
-  # TODO：推送战斗报告
-  exports.tohken.parse.view = (type)->
-    switch type
-      when 'player'
-        # 填充数据
-        source = @data['player']
-        target = @view['player']
-        exports.tohken.parse.fill target, source
-      when 'resource'
-        # 填充数据
-        source = @data['resource']
-        target = @view['resource']
-        exports.tohken.parse.fill target, source
-        # 追加模拟运算数据
-        target['charcoal']  = target['charcoal'] + source['vcharcoal']
-        target['steel']     = target['steel']    + source['vsteel']
-        target['coolant']   = target['coolant']  + source['vcoolant']
-        target['file']      = target['file']     + source['vfile']
-        # 返回设置状态
-        target['set'] = true
-      when 'equip'
-        return if @data['equip']['filling'] == false
-        # 刀装整理
-        # "type": "amount"
-        equ = _.groupBy @data['equip']['data'], (n)-> n['equip_id']
-        equ = _.mapValues equ, (n)-> n.length
-        @view['equips']['data'] = equ
-        @view['equips']['set'] = true
-      when 'party'
-        return if @data['equip']['filling'] == false
-        return if @data['sword']['filling'] == false
-        return if @data['party']['filling'] == false
-        # 向队伍数据中合并刀与刀装数据
-        source = @data['party']
-        # 准备一个分支
-        target = _.clone @data['party']['data']
-        _.forEach target, (v, k)=>
-          # 循环及替换与追加数据
-          lvs = 0
-          eqs = 0
-          ths = 0
-          # 首先替换slot里的数据为实际数据
-          slot = _.clone v['slot']
-          _.forEach v['slot'], (vi, ki)=>
-            soldiers = 0
-            if vi['serial_id'] != null
-              ths += 1
-              slot[ki] = _.clone @data['sword']['data'][vi['serial_id']]
-              # 更新刀装
-              if slot[ki]['equip_serial_id1'] != null
-                # 容错
-                if typeof @data['equip']['data'][slot[ki]['equip_serial_id1']] != "undefined"
-                  slot[ki]['equip_1']                 = _.clone @data['equip']['data'][slot[ki]['equip_serial_id1']]
-                  # 向刀装中追加现存兵力和名称的字段
-                  slot[ki]['equip_1']['name']         = exports.tohken.define['equip'][slot[ki]['equip_1']['equip_id']]['name']
-                  slot[ki]['equip_1']['sname']        = exports.tohken.define.equiptype_s[exports.tohken.define['equip'][slot[ki]['equip_1']['equip_id']]['type']]
-                  slot[ki]['equip_1']['soldier_now']  = exports.tohken.define['equip'][slot[ki]['equip_1']['equip_id']]['soldier']
-                  slot[ki]['equip_1']['soldier_max']  = exports.tohken.define['equip'][slot[ki]['equip_1']['equip_id']]['soldier']
-                  slot[ki]['equip_1']['level']        = parseInt(slot[ki]['equip_1']['equip_id'], 10) % 3
-                  soldiers                           += parseInt slot[ki]['equip_1']['soldier'], 10
-                else
-                  slot[ki]['equip_serial_id1'] = null
-              if slot[ki]['equip_serial_id2'] != null
-                if typeof @data['equip']['data'][slot[ki]['equip_serial_id2']] != "undefined"
-                  slot[ki]['equip_2']                 = _.clone @data['equip']['data'][slot[ki]['equip_serial_id2']]
-                  # 向刀装中追加现存兵力和名称的字段
-                  slot[ki]['equip_2']['name']         = exports.tohken.define['equip'][slot[ki]['equip_2']['equip_id']]
-                  slot[ki]['equip_2']['sname']        = exports.tohken.define.equiptype_s[exports.tohken.define['equip'][slot[ki]['equip_2']['equip_id']]['type']]
-                  slot[ki]['equip_2']['soldier_now']  = exports.tohken.define['equip'][slot[ki]['equip_2']['equip_id']]['soldier']
-                  slot[ki]['equip_2']['soldier_max']  = exports.tohken.define['equip'][slot[ki]['equip_2']['equip_id']]['soldier']
-                  slot[ki]['equip_2']['level']        = parseInt(slot[ki]['equip_2']['equip_id'], 10) % 3
-                  soldiers                           += parseInt slot[ki]['equip_2']['soldier'], 10
-                else
-                  slot[ki]['equip_serial_id2'] == null
-              if slot[ki]['equip_serial_id3'] != null
-                if typeof @data['equip']['data'][slot[ki]['equip_serial_id3']] != "undefined"
-                  slot[ki]['equip_3']                 = _.clone @data['equip']['data'][slot[ki]['equip_serial_id3']]
-                  # 向刀装中追加现存兵力和名称的字段
-                  slot[ki]['equip_3']['name']         = exports.tohken.define['equip'][slot[ki]['equip_3']['equip_id']]
-                  slot[ki]['equip_3']['sname']        = exports.tohken.define.equiptype_s[exports.tohken.define['equip'][slot[ki]['equip_3']['equip_id']]['type']]
-                  slot[ki]['equip_3']['soldier_now']  = exports.tohken.define['equip'][slot[ki]['equip_3']['equip_id']]['soldier']
-                  slot[ki]['equip_3']['soldier_max']  = exports.tohken.define['equip'][slot[ki]['equip_3']['equip_id']]['soldier']
-                  slot[ki]['equip_3']['level']        = parseInt(slot[ki]['equip_3']['equip_id'], 10) % 3
-                  soldiers                           += parseInt slot[ki]['equip_3']['soldier'], 10
-                else
-                  slot[ki]['equip_serial_id3'] == null
-              # 追加兵力和名称与类型数据
-              slot[ki]['name']      = exports.tohken.define['tohkens'][slot[ki]['sword_id']]['name']
-              slot[ki]['type']      = exports.tohken.define['type'][exports.tohken.define['tohkens'][slot[ki]['sword_id']]['type']]
-              slot[ki]['max_equip'] = exports.tohken.define['tohkens'][slot[ki]['sword_id']]['equip']
-              slot[ki]['soldiers']  = soldiers
-              # 合并等级和兵力数据
-              lvs += parseInt slot[ki]['level'], 10
-              eqs += soldiers
-              # 合并虚拟疲劳
-              if @config["cad"]
-                slot[ki]['fatigue'] = parseInt(slot[ki]['fatigue'], 10) + parseInt(slot[ki]['vfatigue'],10)
-              else
-                slot[ki]['fatigue'] = slot[ki]['fatigue']
-          # 合并数据
-          target[k]['slot']       = slot
-          target[k]['amount_lv']  = lvs
-          target[k]['avearge_lv'] = if lvs then (lvs / ths).toFixed(2) else 0
-          target[k]['soldiers']   = eqs
-          'done'
-        @view['party']['set']   = true
-        @view['party']['data']  = target
-    'done'
-  # 远征检查
-  exports.tohken.parse.conquest = (party)->
-    return if @config['notify_conquest'] == 0
-    _.forEach party, (v, k)=>
-      f = v['finished_at']
-      if f != null
-        finish  = Date.parse "#{f} GMT+0900"
-        fd = new Date()
-        fd.setTime(finish)
-        earlier = 0
-        title = "#{v['party_name']}远征结束"
-        message = "结束时间 #{fd.getHours()}:#{fd.getMinutes()}:#{fd.getSeconds()}"
-        context = "请注意查收远征成果"
-        cmsg  = "将在结束时提醒"
-        switch @config['notify_conquest']
-          when 0
-            return
-          when 1
-            title = "#{v['party_name']}远征还有五分钟结束"
-            cmsg  = "将在结束五分钟前提醒"
-            earlier = 5 * 60 * 1000
-          when 2
-            title = "#{v['party_name']}远征还有两分钟结束"
-            cmsg  = "将在结束两分钟前提醒"
-            earlier = 2 * 60 * 1000
-          when 3
-            title = "#{v['party_name']}远征还有半分钟结束"
-            cmsg  = "将在结束半分钟前提醒"
-            earlier = 0.5 * 60 * 1000
-        @sendMessage {
-          'type': 'alarm'
-          'time':　finish - earlier
-          'id': "conquest-#{v['party_no']}-#{finish}"
-          'message': {
-            'title': title
-            'message': message
-            'context': context
-          }
-          'startmsg': {
-            'title': "#{v['party_name']}远征开始"
-            'message': message
-            'context': cmsg
-          }
-        }
-      'done'
-    'done'
-  # 手入检查
-  exports.tohken.parse.check_repair = (repair)->
-    return if @config['notify_repair'] == 0
-    _.forEach repair, (v, k)=>
-      console.log v
-      f = v['finished_at']
-      if f != null
-        finish  = Date.parse "#{f} GMT+0900"
-        fd = new Date()
-        fd.setTime(finish)
-        earlier = 0
-        title = "#{exports.tohken.define.tohkens[@data['sword']['data'][v['sword_serial_id']]['sword_id']]['name']}手入结束"
-        message = "结束时间 #{@t2t(fd)}"
-        context = "快去取刀吧！"
-        cmsg  = "将在结束时提醒"
-        switch @config['notify_conquest']
-          when 0
-            return
-          when 1
-            title = "#{v['party_name']}手入还有五分钟结束"
-            cmsg  = "将在结束五分钟前提醒"
-            earlier = 5 * 60 * 1000
-          when 2
-            title = "#{v['party_name']}手入还有两分钟结束"
-            cmsg  = "将在结束两分钟前提醒"
-            earlier = 2 * 60 * 1000
-          when 3
-            title = "#{v['party_name']}手入还有半分钟结束"
-            cmsg  = "将在结束半分钟前提醒"
-            earlier = 0.5 * 60 * 1000
-        @sendMessage {
-          'type': 'alarm'
-          'time':　finish - earlier
-          'id': "conquest-#{v['party_no']}-#{finish}"
-          'message': {
-            'title': title
-            'message': message
-            'context': context
-          }
-          'startmsg': {
-            'title': "#{exports.tohken.define.tohkens[@data['sword']['data'][v['sword_serial_id']]['sword_id']]['name']}手入开始"
-            'message': message
-            'context': cmsg
-          }
-        }
-      'done'
-    'done'
-  # 数据传送
-  exports.tohken.parse.upload = (id, data)->
-    $.post "http://api.toulove.org",
-      JSON.stringify({
-        "sword":     data['sword_id']
-        "charcoal":  data['charcoal']
-        "coolant":   data['coolant']
-        "steel":     data['steel']
-        "file":      data['file']
-        "secretary": data['secretary']
-        "time":      Date.now()
-        "username":  if @data['player']['name'] != null then @data['player']['name'] else 'empty'
-        "server":    if @data['player']['world'] != null then @data['player']['world'] else 'empty'
-      }),
-      (data,status)=>
-        if status == 'success'
-          if data['status'] == 'success'
-            console.log "#{status}", data
-            @data['logs']['forge'][id]['upload'] = true
-            console.log @data['logs']['forge'][id]['upload']
-    'done'
-  # 数据传送
-  exports.tohken.parse.uploadmap = (data)->
-    $.post "http://api.toulove.org",
-      JSON.stringify({
-        "type":      'map'
-        "sword":     data['sword']
-        "episode":   data['episode']
-        "field":     data['field']
-        "pos":       data['pos']
-        "rank":      data['rank']
-        "time":      Date.now()
-        "username":  if @data['player']['name'] != null then @data['player']['name'] else 'empty'
-        "server":    if @data['player']['world'] != null then @data['player']['world'] else 'empty'
-      }),
-      (data,status)=>
-        if status == 'success'
-          console.log 'upload'
-    'done'
-  # 数据填充工具
-  exports.tohken.parse.fill = (target, source)->
-    _.forEach target, (vi, ki)->
-      # 向目标合并数据
-      if _.has(source, ki)
-        target[ki] = source[ki]
-      'done'
-    'done'
-)(window)