Change dictionary url to Daum

Change dictionary url to Daum

--- coffee/main.coffee
+++ coffee/main.coffee
@@ -7,7 +7,7 @@
     query = $input.val()
     return if query is ''

-    dicUrlPrefix = 'http://endic.naver.com/search.nhn?sLn=en&searchOption=all&query='
+    dicUrlPrefix = 'http://dic.daum.net/search.do?dic=eng&q='
     imagesUrlPrefix = 'http://images.search.yahoo.com/search/images?p='
     $dicIframe.prop 'src', dicUrlPrefix + query
     $imagesIframe.prop 'src', imagesUrlPrefix + query


typo in french locale file

typo in french locale file

--- client/app/locales/fr.coffee
+++ client/app/locales/fr.coffee
@@ -1,7 +1,7 @@
 module.exports =

     'bad credentials' : 'Mauvais identifiants'
-    'no bills retrieved': 'Pas de facture trouvées'
+    'no bills retrieved': 'Pas de factures trouvées'
     'key not found': 'Clé non trouvée'
     'last import:': 'Dernière importation :'
     'save and import': 'Sauvegarder et importer'


Removed shim to make pkgman work on client since the require improvements are there now

--- client/modules/pkgman.coffee
+++ client/modules/pkgman.coffee
@@ -17,18 +17,13 @@

 	for name in _packages

-		# TODO only until I improve require()
 		try
 			package_ = require "packages/#{name}"
 		catch error

-			try
-				package_ = require "packages/#{name}/index"
-			catch error
+			continue if error.toString() is "Error: Cannot find module 'packages/#{name}'"

-				continue if error.toString() is "Error: Cannot find module 'packages/#{name}/index'"
-
-				throw error
+			throw error

 		packageCache[name] = package_


use function for component

use function for component


Formatting

Formatting

--- src/server/publications.coffee
+++ src/server/publications.coffee
@@ -1,3 +1,4 @@
 Meteor.publish 'userStatus', ->
-  Meteor.users.find 'status.online': true
+  Meteor.users.find
+    'status.online': true


Mock default flow auth response.

Mock default flow auth response.

--- test/javascripts/support/mock_server.js.coffee
+++ test/javascripts/support/mock_server.js.coffee
@@ -33,6 +33,10 @@
     'Content-Type': 'application/json'
     JSON.stringify {comment_looks: []}
   ]
+@mockFlowManagerAuthResponse = ->
+  @server.respondWith 'GET', '/user_flows/authorization', [
+    403, 'content-type': 'application/html', 'tahi-authorization-check': true, ""
+  ]

 @setupMockServer = ->
   @server.restore() if @server
@@ -44,3 +48,4 @@
   @mockAuthorizedRouteReponse()
   @mockEventStreamResponse()
   @mockCommentLookResponse()
+  @mockFlowManagerAuthResponse()


:bug: #122 stupid degradation from vim-mode, `I` don't go begin of 1st char if already is


:gift: new `insert-at-start-of-occurrence` to quick bulk edit.

- Why I need special care for vB mode for InsertByTarget?, Removed.

--- lib/operator.coffee
+++ lib/operator.coffee
@@ -1355,13 +1355,8 @@
   which: null # one of ['start', 'end', 'head', 'tail']
   execute: ->
     @selectTarget()
-    if @isMode('visual', 'blockwise')
-      @getBlockwiseSelections().forEach (bs) =>
-        bs.removeEmptySelections()
-        bs.setPositionForSelections(@which)
-    else
-      for selection in @editor.getSelections()
-        swrap(selection).setBufferPositionTo(@which)
+    for selection in @editor.getSelections()
+      swrap(selection).setBufferPositionTo(@which)
     super

 class InsertAtStartOfTarget extends InsertByTarget
@@ -1375,6 +1370,14 @@
 class InsertAtEndOfTarget extends InsertByTarget
   @extend()
   which: 'end'
+
+class InsertAtStartOfOccurrence extends InsertAtStartOfTarget
+  @extend()
+  withOccurrence: true
+
+class InsertAtEndOfOccurrence extends InsertAtEndOfTarget
+  @extend()
+  withOccurrence: true

 # Alias for backward compatibility
 class InsertAtEndOfSelection extends InsertAtEndOfTarget


:gift: SurroundSmartWord is pre-targeted InnerSmartWord


Refactor CardQuestionView to use new Command/Aggregate structure

--- src/read_aggregate_root.coffee
+++ src/read_aggregate_root.coffee
@@ -1,7 +1,10 @@
+_                   = require 'underscore'
+Backbone            = require 'backbone'
 eventric            = require 'eventric'
 ReadAggregateEntity = eventric 'ReadAggregateEntity'

 class ReadAggregateRoot extends ReadAggregateEntity

+  _.extend @prototype, Backbone.Events

 module.exports = ReadAggregateRoot


Fixed looping issue

Fixed looping issue

Checked the wrong variable.

--- slider.coffee
+++ slider.coffee
@@ -43,7 +43,7 @@

 					if position < length
 						$this.slider('go', position + 1)
-					else if looping
+					else if settings.looping
 						$this.slider('go', 1)

 			# Previous
@@ -55,7 +55,7 @@

 					if position > 1
 						$this.slider('go', position - 1)
-					else if looping
+					else if settings.looping
 						$this.slider('go', length)

 			# Play


Bongo: Base: use statik, check against signature existence on testSignature

--- node_modules_koding/bongo/lib/base/index.coffee
+++ node_modules_koding/bongo/lib/base/index.coffee
@@ -308,7 +308,7 @@
     i = @describeMethods @instanceMethods_
     a = @getClassAttributes()
     {
-      static      : s
+      statik      : s
       instance    : i
       attributes  : a
     }
@@ -318,7 +318,7 @@
   @setClassAttributes = (@classAttributes_) ->

   @hasSharedMethod =(method)->
-    method of @getSharedMethods()['static']
+    method of @getSharedMethods()['statik']

   hasSharedMethod:(method)->
     {instance} = @constructor.getSharedMethods()
@@ -329,7 +329,9 @@
     when 'instance'   then @instanceMethods_[method]

   @testSignature = (context, method, args) ->
-    signatures = @getSignature context, method
+    unless signatures = @getSignature context, method
+      return [no, []]
+
     signatures = [signatures]  unless Array.isArray signatures
     for signature in signatures
       # if signature isnt there, fail the test


fix https://github.com/sagemathinc/cocalc/issues/3205

--- src/smc-webapp/app_shared.cjsx
+++ src/smc-webapp/app_shared.cjsx
@@ -247,7 +247,7 @@

     render_connection_status: ->
         if @props.connection_status == 'connected'
-            icon_style = {marginRight: 8, fontSize: '13pt', display: 'inline'}
+            icon_style = {marginRight: '16px', fontSize: '13pt', display: 'inline'}
             if (@props.mesg_info?.get('enqueued') ? 0) > 5  # serious backlog of data!
                 icon_style.color = 'red'
             else if (@props.mesg_info?.get('count') ? 0) > 1 # worrisome amount
@@ -256,18 +256,18 @@
                 icon_style.color = '#00c'
             else
                 icon_style.color = 'grey'
-

+

                 <Icon name='wifi' style={icon_style}/>
                 {@render_ping() if @props.show_pingtime}

         else if @props.connection_status == 'connecting'
-
+

                 connecting...
-
+

         else if @props.connection_status == 'disconnected'
-
+

                 disconnected
-
+

     connection_click: ->
         @props.actions.show_connection(true)
@@ -275,16 +275,14 @@
         document.activeElement.blur() # otherwise, it'll be highlighted even when closed again

     render: ->
-        width = if @props.show_pingtime then '6em' else '4em'
         outer_styles =
-            width      : width
             color      : '#666'
             fontSize   : '10pt'
             lineHeight : '10pt'
             cursor     : 'pointer'
             float      : 'left'
         inner_styles =
-            paddingTop : '13.5px'
+            paddingTop : '3px'

         <NavItem style={outer_styles} onClick={@connection_click}>


Use correct reference

Use correct reference

--- src/spec/validator.spec.coffee
+++ src/spec/validator.spec.coffee
@@ -121,7 +121,7 @@
         ,,
         """
       @validator.parse csv
-      .then (content) =>
+      .then (parsed) =>
         @validator.buildProducts parsed.data, CONS.HEADER_VARIANT_ID
         expect(@validator.errors.length).toBe 3
         expect(@validator.errors[0]).toBe '[row 3] Could not be identified as product or variant!'
@@ -154,7 +154,7 @@
         ,456
         """
       @validator.parse csv
-      .then (content) =>
+      .then (parsed) =>
         @validator.buildProducts parsed.data
         expect(@validator.errors.length).toBe 0
         expect(@validator.rawProducts.length).toBe 2


clean up

clean up

--- src/smc-webapp/editor_git.cjsx
+++ src/smc-webapp/editor_git.cjsx
@@ -13,10 +13,7 @@
 TABS = [
     {"name": "Configuration", "icon": "settings", "description": "Configure global git settings as well as repo settings", "init_actions": ['get_git_user_name', 'get_git_user_email']},
     {"name": "Commit", "icon": "git-commit", "description": "Commit files", "init_actions": ['get_changed_tracked_files', 'get_changed_untracked_files', 'update_diff']},
-    #{"name": "Push", "icon": "repo-push", "description": "Push files"},
-    #{"name": "Branches", "icon": "git-branch", "description": "Create and switch between branches"},
     {"name": "Log", "icon": "history", "description": "Log of commits", "init_actions": ['update_log']},
-   # {"name": "Issues", "icon": "issue-opened", "description": "Create and modify Github issues"}
 ]

 TABS_BY_NAME = {}
@@ -31,15 +28,13 @@
     init : (@project_id, @filename) =>
         @path = misc.path_split(@filename).head
         @setState(git_repo_root : @path)
-        #@set_tab('configuration')
-        @set_tab('commit')
-
+        @set_tab('configuration')
+
     exec : (opts) =>
         opts = defaults opts,
             cmd  : required
             args : []
             cb   : required
-        console.log("git editor exec #{opts.cmd} #{opts.args.join(' ')}")
         salvus_client.exec
             project_id  : @project_id
             command     : opts.cmd
@@ -48,7 +43,6 @@
             err_on_exit : true
             cb          : (err, output) =>
                 if err
-                    console.log(JSON.stringify(err), JSON.stringify(output))
                     console.warn("git editor ERROR exec'ing #{opts.cmd} #{opts.args.join(' ')}")
                     opts.cb(err, output)
                 opts.cb(err, output)
@@ -63,7 +57,6 @@

     get_git_user_name : =>
         store = @redux.getStore(@name)
-        console.log('FULL NAME '+@redux.getStore('account').get_fullname())
         @exec
             cmd  : "git"
             args : ['config', '--global', 'user.name']
@@ -88,7 +81,6 @@

     get_git_user_email : =>
         store = @redux.getStore(@name)
-        console.log('FULL email '+@redux.getStore('account').get_email_address())
         @exec
             cmd  : "git"
             args : ['config', '--global', 'user.email']
@@ -154,7 +146,6 @@
     run_git_commit : =>
         store = @redux.getStore(@name)
         commit_message = store.get('commit_message')
-        console.log('CM: '+commit_message)
         if store.get('checked_files')
             checked_tracked_files = JSON.parse(JSON.stringify(store.get('checked_files').get('tracked')))
         else
@@ -187,7 +178,6 @@
         store = @redux.getStore(@name)

         if store
-            console.log('file to diff',store.get('file_to_diff'))
             args = if store.get('file_to_diff') then ['diff', store.get('file_to_diff')] else ['diff']
         else
             args = ['diff']
@@ -218,15 +208,10 @@
             @[action]()

     add_or_removed_checked_files : (name, listing_type) =>
-        console.log('Name '+name+'Listing type'+listing_type)
         store = @redux.getStore(@name)
         window.actions = @; window.store = store
-        console.log('A'+store.get('checked_files'))
         if not store.get('checked_files')
             @setState(checked_files: {"tracked": [], "untracked": []})
-        console.log('B'+store.get('checked_files'))
-        console.log('C'+listing_type+store.get('checked_files')[listing_type])
-        console.log('D'+store.get('checked_files').get(listing_type).indexOf(name) > -1)
         # I was unable to modify the object as is Only worked once I did -> JSON -> object
         if store.get('checked_files').get(listing_type).indexOf(name) > -1
             checked_files = JSON.parse(JSON.stringify(store.get('checked_files')))
@@ -235,9 +220,7 @@
         else
             checked_files = JSON.parse(JSON.stringify(store.get('checked_files')))
             checked_files[listing_type].push(name)
-            console.log(checked_files)
             @setState(checked_files: checked_files)
-        console.log(JSON.stringify(store.get('checked_files')))

 FileCheckbox = rclass
     displayName : 'ProjectGit-FileCheckbox'
@@ -266,9 +249,10 @@
         current_path : rtypes.string
         actions      : rtypes.object.isRequired
         listing_type : rtypes.string
+        key          : rtypes.string

     render : ->
-        <Row onClick={@handle_click} className={'noselect small'}>
+        <Row key={@props.key} onClick={@handle_click} className={'noselect small'}>
             <FileCheckbox
                     name         = {@props.name}
                     checked      = {@props.checked}
@@ -292,18 +276,19 @@
     getDefaultProps : ->
         file_search : ''

-    render_row : (name) ->
+    render_row : (name, key) ->
         checked = true

         return <FileRow
             name         = {name}
+            key          = {key}
             listing_type = {@props.listing_type}
             checked      = {if @props.checked_files then @props.checked_files[@props.listing_type].indexOf(name) > -1 else false}
             current_path = {@props.current_path}
             actions      = {@props.actions} />

     render_rows : ->
-        (@render_row(name) for name in @props.listing)
+        (@render_row(name, idx) for name, idx in @props.listing)

     render : ->
         <Col sm=12>
@@ -324,22 +309,22 @@
 Git = (name) -> rclass
     reduxProps:
         "#{name}" :
-            tab                   : rtypes.string
-            git_repo_root         : rtypes.string
-            git_user_name         : rtypes.string
-            git_user_email        : rtypes.string
-            git_user_name_return  : rtypes.string
-            git_user_email_return : rtypes.string
-            git_commit_return     : rtypes.string
-            commit_message        : rtypes.string
-            git_status            : rtypes.string
-            git_diff              : rtypes.string
-            git_log               : rtypes.string
-            current_branch        : rtypes.string
-            git_changed_tracked_files : rtypes.array
+            tab                         : rtypes.string
+            git_repo_root               : rtypes.string
+            git_user_name               : rtypes.string
+            git_user_email              : rtypes.string
+            git_user_name_return        : rtypes.string
+            git_user_email_return       : rtypes.string
+            git_commit_return           : rtypes.string
+            commit_message              : rtypes.string
+            git_status                  : rtypes.string
+            git_diff                    : rtypes.string
+            git_log                     : rtypes.string
+            current_branch              : rtypes.string
+            git_changed_tracked_files   : rtypes.array
             git_changed_untracked_files : rtypes.array
-            checked_files : rtypes.object
-            file_to_diff          : rtypes.string
+            checked_files               : rtypes.object
+            file_to_diff                : rtypes.string

     propTypes :
         actions : rtypes.object
@@ -377,7 +362,6 @@
             @props.actions.set_git_user_email()

     handle_commit_message_keypress : (e) ->
-        console.log(@refs.commit_message.getValue())
         if e.keyCode == 13 and @props.commit_message != ''
             @props.actions.run_git_commit()

@@ -409,8 +393,6 @@
         window.refs = @refs
         head =

-
-

                     Select the changed tracked files to commit
                     <Space/> <Space/>
@@ -497,7 +479,6 @@
                 </Button>

         <Panel header={head}>
-
             {<FileListing
                 listing             = {@props.git_changed_untracked_files}
                 listing_type        = 'untracked'
@@ -505,36 +486,17 @@
                 actions             = {@props.actions} /> if @props.git_changed_untracked_files}
         </Panel>

-    render_status : ->
-        head =
-
-                git status
-                <Space/> <Space/>
-                <Button onClick={=>@props.actions.update_status()}>
-                    Refresh
-                </Button>
-                <Button onClick={=>@props.actions.setState(git_status:'')}>
-                    Clear
-                </Button>
-
-        <Panel header={head}>
-            {
{@props.git_status}
 if @props.git_status}
-        </Panel>
-
     render_diff_files : ->
         if @props.git_changed_tracked_files
             for file, idx in @props.git_changed_tracked_files
-                console.log(idx)
-                <MenuItem key={idx} eventKey="{file}" onSelect={(e)=>console.log(JSON.stringify(e.target));@props.actions.setState(file_to_diff:e.target.text);@props.actions.update_diff()}>{file}</MenuItem>
+                <MenuItem key={idx} eventKey="{file}" onSelect={(e)=>@props.actions.setState(file_to_diff:e.target.text);@props.actions.update_diff()}>{file}</MenuItem>

     render_diff : ->
         head =

                 git diff
                 <Space/> <Space/>
-                <DropdownButton title='file' id='files_to_diff'
-
-                >
+                <DropdownButton title='file' id='files_to_diff'>
                     {@render_diff_files()}
                 </DropdownButton>
                 <Space/> <Space/>
@@ -562,46 +524,11 @@
     render_commit : ->

             <Row>
-                <Col sm=4>
+                <Col sm=6>
                     {@render_commit_panel()}
                     {@render_changed_untracked_files()}
                 </Col>
-                <Col sm=4>
-                    {@render_diff()}
-                </Col>
-            </Row>
-

-
-    render_push : ->
-

-            <Row>
-                <Col sm=4>
-                    {@render_commit_panel()}
-                    {@render_changed_untracked_files()}
-                </Col>
-                <Col sm=4>
-                    {@render_changed_tracked_files()}
-
-                    {@render_status()}
-                </Col>
-                <Col sm=4>
-                    {@render_diff()}
-                </Col>
-            </Row>
-

-
-    render_pull : ->
-

-            <Row>
-                <Col sm=4>
-                    {@render_commit_panel()}
-                </Col>
-                <Col sm=4>
-                    {@render_changed_tracked_files()}
-                    {@render_changed_untracked_files()}
-                    {@render_status()}
-                </Col>
-                <Col sm=4>
+                <Col sm=6>
                     {@render_diff()}
                 </Col>
             </Row>
@@ -648,7 +575,6 @@
             @render_tab(idx, tab.name, tab.icon, tab.description)

     render : ->
-        console.log('rendering')

Git Repository at {@props.git_repo_root}

             <Tabs animation={false} activeKey={@props.tab} onSelect={(key)=>@props.actions.set_tab(key)}>


activity feed: error check buckets have entries in them

--- client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -165,8 +165,9 @@
     for item in @itemsOrdered
       data = item.getData()

+      continue  if typeof data.group is "string"
       continue  unless data.group
-      continue  if typeof data.group is "string"
+      continue  unless data.group[0]

       if data.group[0].constructorName is followee.bongo_.constructorName
         if data.anchor && data.anchor.id is follower.id


activitylistcontroller class scope vars refactored into instance scoped ones

--- client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
+++ client/app/Applications/Activity.kdapplication/activitylistcontroller.coffee
@@ -1,18 +1,4 @@
 class ActivityListController extends KDListViewController
-
-  hiddenItems               = []
-  hiddenNewMemberItemGroups = [[]]
-  hiddenItemCount           = 0
-
-  prepareNewMemberGroup = ->
-
-    # this is a bit tricky here
-    # if the previous member group isn't empty
-    # create a new group for later new member items
-    if hiddenNewMemberItemGroups.last.length isnt 0
-      hiddenNewMemberItemGroups.push []
-
-  resetNewMemberGroups = -> hiddenNewMemberItemGroups = [[]]

   constructor:(options={}, data)->

@@ -35,7 +21,8 @@
     super options, data

     @resetList()
-    @_state = 'public'
+    @hiddenItems = []
+    @_state      = 'public'

     KD.getSingleton("groupsController").on "MemberJoinedGroup", (member) =>
       @updateNewMemberBucket member.member
@@ -58,7 +45,7 @@
         top   = firstHiddenItem.position().top
         top or= 0
         @scrollView.scrollTo {top, duration : 200}, =>
-          unhideNewHiddenItems hiddenItems
+          @unhideNewHiddenItems @hiddenItems

     @emit "ready"

@@ -110,7 +97,7 @@
         if activity?.teaser
           activity.teaser.createdAtTimestamps = overviewItem.createdAt
           view = @addHiddenItem activity.teaser, index, animation
-          view.slideIn -> removeFromHiddenItems view
+          view.slideIn => @removeFromHiddenItems view
           activityIds.push activity.teaser._id

     @checkIfLikedBefore activityIds  unless isFeaturedContent
@@ -129,7 +116,7 @@

   getLastItemTimeStamp: ->

-    if item = hiddenItems.first
+    if item = @hiddenItems.first
       item.getData().createdAt or item.getData().createdAtTimestamps.last
     else
       @lastItemTimeStamp
@@ -200,11 +187,11 @@
       @getListView().addItem activity, 0
     else
       view = @addHiddenItem activity, 0
-      @utils.defer ->
-        view.slideIn -> removeFromHiddenItems view
-
-  removeFromHiddenItems = (view)->
-    hiddenItems.splice hiddenItems.indexOf(view), 1
+      @utils.defer =>
+        view.slideIn => @removeFromHiddenItems view
+
+  removeFromHiddenItems: (view)->
+    @hiddenItems.splice @hiddenItems.indexOf(view), 1

   fakeActivityArrived:(activity)->
@@ -215,15 +202,15 @@
   addHiddenItem:(activity, index, animation = null)->

     instance = @getListView().addHiddenItem activity, index, animation
-    hiddenItems.push instance
+    @hiddenItems.push instance
     @lastItemTimeStamp = activity.createdAt

     return instance

-  unhideNewHiddenItems = (hiddenItems)->
-
-    repeater = KD.utils.repeat 177, ->
-      item = hiddenItems.shift()
+  unhideNewHiddenItems: ->
+
+    repeater = KD.utils.repeat 177, =>
+      item = @hiddenItems.shift()
       if item then item.show() else KD.utils.killRepeat repeater

   instantiateListItems:(items)->


Keep logs sorted by instance #

Keep logs sorted by instance #

--- SingularityUI/app/components/aggregateTail/AggregateTail.cjsx
+++ SingularityUI/app/components/aggregateTail/AggregateTail.cjsx
@@ -77,7 +77,9 @@
     )[0]?.taskId.instanceNo

   renderIndividualTails: ->
-    @state.viewingInstances.map((taskId, i) =>
+    @state.viewingInstances.sort((a, b) =>
+      @getInstanceNumber(a) > @getInstanceNumber(b)
+    ).map((taskId, i) =>
       if @props.logLines[taskId]
         <div key={taskId} id="tail-#{taskId}" className="col-md-#{@getColumnWidth()} tail-column">
           <IndividualTail


Added blockonomics to blockexplorer list

--- app/src/preferences/defaults.coffee
+++ app/src/preferences/defaults.coffee
@@ -127,6 +127,9 @@
       insight:
         name: 'Insight.is'
         address: 'https://insight.bitpay.com/tx/%s'
+      blockonomics:
+        name: 'Blockonomics.co'
+        address: 'https://www.blockonomics.co/api/tx?txid=%s'
     discoveryGap: 20

 ledger.preferences.litecoin =
@@ -231,4 +234,4 @@
         address: 'http://www.clubcha.in/tx/%s'
     discoveryGap: 20

-ledger.preferences.common.setCoin("bitcoin")+ledger.preferences.common.setCoin("bitcoin")


Activate Vietnamese language option

Activate Vietnamese language option


Remove the requirement to pass in a model

--- components/form/index.coffee
+++ components/form/index.coffee
@@ -10,7 +10,6 @@
   constructor: (options = {}) ->
     { @model, @$form } = options

-    throw new Error 'requires `model`' unless @model?
     throw new Error 'requires `$form`' unless @$form?

     { @$submit, @$errors } = settings = _.defaults options,
@@ -25,6 +24,8 @@
     @validator.isValid() and not @submitting()

   submit: (e, options = {}, send = 'save') ->
+    throw new Error 'requires `model`' unless @model?
+
     e?.preventDefault()

     return unless @isReady()


elektron: disabled badge notifications

elektron: disabled badge notifications

--- client/activity/lib/flux/actions/realtime/actioncreators.coffee
+++ client/activity/lib/flux/actions/realtime/actioncreators.coffee
@@ -149,6 +149,9 @@

 bindAppBadgeNotifiers = ->

+  # removed badge notifications
+  # until we bind stack notifications - SY
+  return
   return  unless window.nodeRequire

   { ipcRenderer } = nodeRequire 'electron'


Apps: Removed install button that allow user to install specified version of an app.

--- client/app/Applications/Apps.kdapplication/Views/appview.coffee
+++ client/app/Applications/Apps.kdapplication/Views/appview.coffee
@@ -125,42 +125,16 @@
       else
         @likeButton.setState "Like"

-    if app.versions?.length > 1 and KD.isLoggedIn()
-      menu = {}
-
-      for version,i in app.versions
-        menu["Install version #{version}"] =
-          version  : version
-          callback : (item)=>
-            {version} = item.data
-            appsController.installApp app, version, (err)=>
-              KD.track "Apps", "Install", app.title unless err
-              if err then warn err
-
-      @installButton = new KDButtonViewWithMenu
-        title     : "Install Now"
-        style     : "cupid-green"
-        loader    :
-          top     : 0
-          diameter: 30
-          color   : "#ffffff"
-        delegate  : @
-        menu      : menu
-        callback  : ->
-          appsController.installApp app, app.versions.last, (err)=>
-            @hideLoader()
-
-    else
-      @installButton = new KDButtonView
-        title     : "Install Now"
-        style     : "cupid-green"
-        loader    :
-          top     : 0
-          diameter: 30
-          color   : "#ffffff"
-        callback  : ->
-          appsController.installApp app, app.versions.last, (err)=>
-            @hideLoader()
+    @installButton = new KDButtonView
+      title     : "Install Now"
+      style     : "cupid-green"
+      loader    :
+        top     : 0
+        diameter: 30
+        color   : "#ffffff"
+      callback  : ->
+        appsController.installApp app, app.versions.last, (err)=>
+          @hideLoader()

     @runButton = new KDButtonView
       title     : "Run"


cleanup

cleanup

--- client/app/MainApp/maincontroller/mainviews/contentpanel.coffee
+++ client/app/MainApp/maincontroller/mainviews/contentpanel.coffee
@@ -4,16 +4,15 @@

     super options, data

-    @registerSingleton "contentPanel", @, yes
+    @registerSingleton "contentPanel", this, yes
     @bindTransitionEnd()
     @listenWindowResize()
-    @state = 'full'
-    @chatMargin = 0
+
+    @state              = 'full'
+    @chatMargin         = 0
     @windowController or= KD.getSingleton 'windowController'
-
-    @navOpenedOnce = yes #if KD.isLoggedIn() then yes else no
-
-    mainViewController = KD.getSingleton "mainViewController"
+    @navOpenedOnce      = yes #if KD.isLoggedIn() then yes else no
+    mainViewController  = KD.getSingleton "mainViewController"
     mainViewController.on "UILayoutNeedsToChange", @bound "changeLayout"
     mainViewController.on "browseRequested", @bound "browseRequested"

@@ -25,10 +24,13 @@
   nameMap =
     Home    : 'adjustForFullWidth'

+
   browseRequested:->
+
     @navOpenedOnce = yes
     @adjustForSocial()
     KD.getSingleton("mainView").mainTabView.changeLayout hideTabs : no
+

   changeLayout:(options)->

@@ -40,36 +42,49 @@

     @adjustLayoutHelper name, type

+
   resetToCurrentState:->
+
     switch @state
       when 'develop' then @adjustForDevelop()
       when 'social'  then @adjustForSocial()
       when 'full'    then @adjustForFullWidth()

+
   adjustShadow:(hideTabs)->
+
     @[if hideTabs then 'setClass' else 'unsetClass'] "no-shadow"

+
   adjustLayoutHelper:(name, type)->
+
     if @navOpenedOnce # KD.isLoggedIn() or
     then @[nameMap[name] or typeMap[type] or typeMap[@state]]?()
     else @adjustForFullWidth()

+
   adjustForFullWidth:->
+
     @setX 0
     @setWidth @windowController.winWidth - @chatMargin
     @setClass 'full'
     @state = 'full'

+
   adjustForDevelop:->
+
     @setX 260
     @setWidth @windowController.winWidth - 260 - @chatMargin
     @setClass 'develop'
     @state = 'develop'

+
   adjustForSocial:->
+
     offset = if @windowController.winWidth < 768 then 50 else 160
     @setWidth @windowController.winWidth - offset - @chatMargin
     @setClass 'social'
     @state = 'social'

+
   _windowDidResize: @::adjustLayoutHelper


changed the rick script to only show rick 1 in 20 times


Add feature flag to detect logical display buffer

--- src/text-editor-element.coffee
+++ src/text-editor-element.coffee
@@ -15,6 +15,7 @@
   tileSize: null
   focusOnAttach: false
   hasTiledRendering: true
+  logicalDisplayBuffer: true

   createdCallback: ->
     @emitter = new Emitter


Disable filetree header

Disable filetree header

--- client/app/MainApp/sidebar/sidebarview.coffee
+++ client/app/MainApp/sidebar/sidebarview.coffee
@@ -195,9 +195,6 @@
       {{> @footerMenu}}

-

-        {{> @finderHeader}}
-

         {{> @finder}}


Add comment

Add comment

--- client/app/lib/payment/paymentworkflow.coffee
+++ client/app/lib/payment/paymentworkflow.coffee
@@ -93,6 +93,7 @@
     { PAYPAL }            = PaymentConstants.provider
     { paymentController } = kd.singletons

+    ## This event always works with "koding" or "stripe" provider.
     @modal.on 'PaymentSubmitted', (formData) =>

       ## Reset provider to "koding"


Alert user that their token is wrong when retrieving logs

--- app/assets/javascripts/farmbot_app/services/devices.js.coffee
+++ app/assets/javascripts/farmbot_app/services/devices.js.coffee
@@ -47,12 +47,16 @@
   moveRel: (x, y, z) -> @send "move_relative", {x: x, y: y, z: z}
   moveAbs: (x, y, z) -> @send "move_absolute", {x: x, y: y, z: z}
   stop: -> @send "emergency_stop"
-  fetchLogs: (cb = (d)-> console.log(d)) ->
+  fetchLogs: (cb) ->
     @socket.emit 'getdata', {
       uuid:  @current.uuid
       token: @current.token
       limit: 10
-    }, cb
+    }, (d) ->
+      if d.result is false
+        alert 'Ensure you have entered the correct token for your FarmBot'
+      else
+        cb(d)

   send: (msg, body = {}) ->
     if @socket.connected()


now that bucket has serializable attributes, .save works again

--- app/components/create-project-page/create-project-page.coffee
+++ app/components/create-project-page/create-project-page.coffee
@@ -10,6 +10,7 @@

     $scope.done = () ->
       if $scope.bucketForm.$valid
-        # temp hack - because, save doesn't set the right request headers
-        $scope.bucket.create().then ->
-          $scope.cancel()+        $scope.bucket.save().then (data) ->
+          bucketId = data.buckets[0].id
+          $location.path("/groups/#{$scope.bucket.groupId}/projects/#{bucketId}")
+          Toast.show('You launched a project for funding')


user cant edit archived bucket

user cant edit archived bucket

--- app/directives/bucket-page-toolbar/bucket-page-toolbar.coffee
+++ app/directives/bucket-page-toolbar/bucket-page-toolbar.coffee
@@ -11,7 +11,7 @@
         $location.path("/buckets/#{$scope.bucket.id}/edit")

       $scope.userCanEditBucket = ->
-        $scope.bucket && $scope.userCanStartFunding()
+        $scope.bucket && !$scope.bucket.isArchived() && $scope.userCanStartFunding()

       $scope.archiveBucketAndRedirect = ->
         LoadBar.start()


Improve content-type matcher

Improve content-type matcher

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

 invalidContent = (xhr) ->
-  !xhr.getResponseHeader('Content-Type').match /text\/html|application\/xhtml\+xml|application\/xml/
+  !xhr.getResponseHeader('Content-Type').match /^(?:text\/html|application\/xhtml\+xml|application\/xml)(?:;|$)/

 extractTrackAssets = (doc) ->
   (node.src || node.href) for node in doc.head.childNodes when node.getAttribute?('data-turbolinks-track')?


Packet now extends ByteBuffer; Adjusted byte order

--- src/wrathnet/net/packet.coffee
+++ src/wrathnet/net/packet.coffee
@@ -10,11 +10,11 @@
 #

 # Denotes a network packet
-class WrathNet.net.Packet extends WrathNet.datastructures.ByteBuffer
+class WrathNet.net.Packet extends ByteBuffer

   # Creates a new packet with given opcode from given source or length
   constructor: (opcode, source, outgoing=true) ->
-    super source
+    super source, ByteBuffer.LITTLE_ENDIAN

     # Holds the opcode for this packet
     @opcode = opcode


can now handle docs with ids and bulk docs

--- src/leaflet.pouch.coffee
+++ src/leaflet.pouch.coffee
@@ -44,7 +44,14 @@
 								else console.log("you sure about that?")

 	addDoc: (doc, cb) ->
-		@localDB.post doc, cb or ()-> true
+		if "type" of doc and doc.type == "Feature"
+			@localDB.post doc, cb or ()-> true unless "_id" of doc
+			@localDB.put doc, cb or ()-> true if "_id" of doc
+		else if "type" of doc and doc.type == "FeatureCollection"
+			@localDB.bulkDocs doc.features, cb or ()-> true
+		else if doc.length
+			@localDB.bulkDocs doc, cb or ()-> true
+

 	deleteDoc: (id) ->
 		@localDB.get id, (err, doc) =>


Order of startupController parameters matches action methods.

--- src/chaplin/dispatcher.coffee
+++ src/chaplin/dispatcher.coffee
@@ -125,8 +125,8 @@
     # We're done! Spread the word!
     @publishEvent 'startupController',
       instance: @currentController
+      params: params
       route: route
-      params: params
       options: options

   # Before actions with chained execution.


Fix typo

Fix typo

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -558,7 +558,7 @@
         when 'ParticipantKicked'
           return @handleParticipantKicked data.target
         when 'PermissionDenied', 'PermissionGranted'
-          return @destoryPermissionRequestMenuItem data.target
+          return @destroyPermissionRequestMenuItem data.target
         else return

     switch type
@@ -602,7 +602,7 @@
         @handlePermissionGranted()  if data.target is nick()

-  destoryPermissionRequestMenuItem: (target) ->
+  destroyPermissionRequestMenuItem: (target) ->

     @statusBar.participantAvatars[target]?.emit 'DestroyMenu'


collaboration: controller minor

collaboration: controller minor

--- client/ide/lib/collaborationcontroller.coffee
+++ client/ide/lib/collaborationcontroller.coffee
@@ -671,11 +671,9 @@
       @endCollaborationForHost
         success: =>
           # TODO: move this into its own method.
-          # @broadcastMessage { type: 'SessionEnded' }
           @statusBar.emit 'CollaborationEnded'
           sharedSuccessFn()
           @cleanupCollaboration()
-          # kd.utils.defer @bound 'cleanupCollaboration'

   endCollaborationForHost: (callbacks) ->


More tests for FrameListView.

More tests for FrameListView.

--- client/src/tests/frame-list-view.tests.coffee
+++ client/src/tests/frame-list-view.tests.coffee
@@ -1,6 +1,4 @@
 describe 'FrameListView', ->
-  @timeout 10000
-
   it 'should display all frames when no predicate is applied', (go) ->
     _ = Steam.ApplicationContext()
     Steam.Xhr _
@@ -52,6 +50,21 @@
           frameListView = Steam.FrameListView _
           link$ _.framesLoaded, ->
             testAsync go, ->
-              dumpAssertions frameListView, 'frameListView'
+              equal frameListView.items().length, 2, 'frameListView.items() array length mismatch'
+              equal frameListView.items()[0].title, 'airlines_test.hex', 'String frameListView.items()[0].title does not equal <airlines_test.hex>'
+              equal frameListView.items()[0].caption, '13 columns', 'String frameListView.items()[0].caption does not equal <13 columns>'
+              equal frameListView.items()[0].cutline, 'fYear, fMonth, fDayofMonth, fDayOfWeek, DepTime, ArrTime, UniqueCarrier, Origin, Dest, Distance, IsDepDelayed, IsDepDelayed_REC, IsDepDelayed_REC_recoded', 'String frameListView.items()[0].cutline does not equal <fYear, fMonth, fDayofMonth, fDayOfWeek, DepTime, ArrTime, UniqueCarrier, Origin, Dest, Distance, IsDepDelayed, IsDepDelayed_REC, IsDepDelayed_REC_recoded>'
+              ok (isFunction frameListView.items()[0].display), 'frameListView.items()[0].display is not a function'
+              equal frameListView.items()[0].isActive(), true, 'Boolean frameListView.items()[0].isActive() does not equal <true>'
+              equal frameListView.items()[1].title, 'airlines_train.hex', 'String frameListView.items()[1].title does not equal <airlines_train.hex>'
+              equal frameListView.items()[1].caption, '13 columns', 'String frameListView.items()[1].caption does not equal <13 columns>'
+              equal frameListView.items()[1].cutline, 'fYear, fMonth, fDayofMonth, fDayOfWeek, DepTime, ArrTime, UniqueCarrier, Origin, Dest, Distance, IsDepDelayed, IsDepDelayed_REC, IsDepDelayed_REC_recoded', 'String frameListView.items()[1].cutline does not equal <fYear, fMonth, fDayofMonth, fDayOfWeek, DepTime, ArrTime, UniqueCarrier, Origin, Dest, Distance, IsDepDelayed, IsDepDelayed_REC, IsDepDelayed_REC_recoded>'
+              ok (isFunction frameListView.items()[1].display), 'frameListView.items()[1].display is not a function'
+              equal frameListView.items()[1].isActive(), false, 'Boolean frameListView.items()[1].isActive() does not equal <false>'
+              equal frameListView.predicateCaption(), 'Showing datasets compatible with\ndl_AirlinesTrain_binary_1', 'String frameListView.predicateCaption() does not equal <Showing datasets compatible with\ndl_AirlinesTrain_binary_1>'
+              ok (isFunction frameListView.clearPredicate), 'frameListView.clearPredicate is not a function'
+              equal frameListView.canClearPredicate(), true, 'Boolean frameListView.canClearPredicate() does not equal <true>'
+              equal frameListView.hasItems(), true, 'Boolean frameListView.hasItems() does not equal <true>'
+              equal frameListView.template, 'frame-list-view', 'String frameListView.template does not equal <frame-list-view>'
           _.loadFrames type: 'compatibleWithModel', modelKey: model.key


表示形式を画面サイズに合わせてcanvasを描画していたのを内部的にQXGA固定で描画するように変更

--- src/coffee/TrainVision.coffee
+++ src/coffee/TrainVision.coffee
@@ -15,9 +15,6 @@
 class @TrainVision
 	timer: null
 	timerCB: []
-
-	imgPath:
-		bg: "img/bg_e233.jpg"

 	SETTING_FPS: 30
 	game: null
@@ -42,7 +39,9 @@
 			time_text: null
 			time: null
 	scene:
+		station: null
 		main: null
+
 	color:
 		green: "#009245"

@@ -63,7 +62,7 @@
 			@set_station "tachikawa"

 		@_initSize()
-		@_initGame @STAGE_SIZE.w, @STAGE_SIZE.h
+		@_initGame @BG_SIZE.w, @BG_SIZE.h
 		if !timer
 			timer = setInterval @_timerHook, 1000
 		@setTimerConf()
@@ -75,13 +74,13 @@
 		if(!@station)
 			@station = Text.station["tachikawa"]

-
 	game_onload: =>
 		@initMainPanel 0
 		return

 	_initGame: (w,h)=>
 		@game = new Game w, h
+		@game.scale = @ZOOM
 		for k, v of @imgPath
 			@game.preload v

@@ -96,24 +95,26 @@
 			@scene.main = new Scene
 			@game.pushScene @scene.main

+		if !@scene.station
+			@scene.station = new Scene
+			@game.pushScene @scene.station
+
 		@set_station_label lang_flag
 		@set_main_panel lang_flag

 	set_station_label: (lang_flag)=>
 		if !@label.station
 			@label.station = new Label(@station[lang_flag])
-			@scene.main.addChild @label.station
+			@scene.station.addChild @label.station

 		station = @label.station
 		station.text = @station[lang_flag]
 		station.textAlign = "center"
 		station.color = @color.green
-		fontsize = @convSize(300)
-
-		# station.x = @convSize(520)
-		# station.y = @convSize(130)
-		station.width = @convSize 1000
-		station.height = @convSize 400
+		fontsize = 300
+
+		station.width = 1000
+		station.height = 400

 		station.scaleX = station.scaleY = 1
 		station._layer._element.style.letterSpacing = 'normal'
@@ -121,18 +122,18 @@

 		if lang_flag == 0
 			# Japanese
-			@label.station.x = @convSize(520)
-			@label.station.y = @convSize(130)
+			@label.station.x = 520
+			@label.station.y = 130

 			if station.text.length >= 5
 				# 文字がはみ出すのでサイズを調整
-				station.width = @convSize 1500
+				station.width = 1500
 				station.textAlign = "left"
 				station.scaleX = station.text.length/8
 				station.x = station.x - (station.x * station.text.length/10.5)
 			if station.text.length == 4
 				station.scaleX = 4/5
-				station.x -= @convSize 75
+				station.x -= 75
 			station.font = station._layer._element.style.font = "normal bold " + fontsize + "px iwata";
 		else if lang_flag == 1
 			# English
@@ -174,10 +175,10 @@
 					x = -20

 			station.font = "normal bold " + fontsize + "px Arial";
-			station.width = @convSize width
+			station.width = width
 			station.textAlign = align
-			@label.station.x = @convSize x
-			@label.station.y = @convSize y
+			@label.station.x = x
+			@label.station.y = y
 			station.scaleX = scale

 		else if lang_flag == 2
@@ -216,24 +217,34 @@

-			station.width = @convSize width
+			station.width = width
 			station.textAlign = align
-			@label.station.x = @convSize x
-			@label.station.y = @convSize y
+			@label.station.x = x
+			@label.station.y = y
 			station.scaleX = scale

 			station.font = station._layer._element.style.font = "normal bold " + fontsize + "px iwata";
 		return

 	set_main_panel: (lang_flag)=>
-		if !@scene
-			@scene = new Scene(@convSize 1000, @convSize 5000)
-			@game.pushScene @scene
-		if !@label.station
-			@label.station = new Label(@station[lang_flag])
-			@scene.addChild @label.station
+		if lang_flag isnt 1 then lang_flag = 0
+		l = {}
+
+		for k, v of @label.main_parts
+			if not @label.main_parts[k]
+				console.log "create instance"
+				@label.main_parts[k] = new Label "次は"
+				@scene.main.addChild @label.main_parts[k]
+			l[k] = @label.main_parts[k]
+
+		l.next.text = Text.next[lang_flag]
+		l.next.textAlign = "right"
+		l.next.moveTo 200, 150
+		l.next.font = "80px"
+
 		if lang_flag == 1
 			 # English
+
 		else
 			# Japanese

@@ -243,22 +254,19 @@
 		# TrainVisonを4:3で固定するため、横の長さを縦の長さから計算します。
 		if(height < width)
 			# 横長の画面の場合
-			@STAGE_SIZE.w = Math.floor($(window).height() * (4/3))
-			@STAGE_SIZE.h = Math.floor $(window).height()
+			@STAGE_SIZE.w = Math.floor(height * (4/3))
+			# @STAGE_SIZE.h = Math.floor $(window).height()
 		else if(width < height)
 			# 縦長の画面の場合
-			@STAGE_SIZE.w = Math.floor $(window).width()
-			@STAGE_SIZE.h = Math.floor $(window).width()  * (3/4)
+			@STAGE_SIZE.w = Math.floor width
+			# @STAGE_SIZE.h = Math.floor $(window).width()  * (3/4)

 		@ZOOM = @STAGE_SIZE.w / @BG_SIZE.w

-		console.log "w: " + @STAGE_SIZE.w
-		console.log "h: " + @STAGE_SIZE.h
-
 		return

 	convSize:(size)=>
-		return @ZOOM * size
+		return size

 	# Āā Īī Ūū Ēē Ōō


remove console log

remove console log

--- assets/coffeescript/pupil_store.coffee
+++ assets/coffeescript/pupil_store.coffee
@@ -597,7 +597,6 @@
     if $(@cartPage).length > 0
       window.Parsley.addValidator('countryvalidator', ((value, requirement) ->
         validity = value of countryList
-        console.log(value)
         if validity
           requirementIds = requirement.split("-")
           postalCodeId = requirementIds[0]


coffeeスクリプトも修正

coffeeスクリプトも修正

--- src/lib/voicetext.coffee
+++ src/lib/voicetext.coffee
@@ -31,6 +31,10 @@
     TAKERU: 'takeru'
     SANTA: 'santa'
     BEAR: 'bear'
+  FORMAT:
+    OGG: 'ogg'
+    WAV: 'wav'
+    AAC: 'aac'

   constructor: (@api_key)->
     @_pitch = 100
@@ -39,6 +43,7 @@
     @_speaker = @SPEAKER.SHOW
     @_emotion = undefined
     @_emotion_level = undefined
+    @_format = @FORMAT.WAV
     @

   speaker: (speaker)->
@@ -78,6 +83,12 @@
       @_volume = lvl
     @

+  format: (format)->
+    for k,v of @FORMAT
+      if format is v
+        @_format = v
+    @
+
   build_params: (text)->
     params =
       volume: @_volume
@@ -86,6 +97,7 @@
       emotion_level: @_emotion_level
       emotion: @_emotion
       speaker: @_speaker
+      format: @_format
       text: text

   speak: (text, callback)->


Fixes crash when using continue in a memory context. Fix $11c

--- packages/landsofillusions/memory/context.coffee
+++ packages/landsofillusions/memory/context.coffee
@@ -285,8 +285,8 @@
     exitAction = => LOI.adventure.exitMemory()

     commandResponse.onExactPhrase
-      form: [Vocabulary.Keys.Directions.Back]
-      # We want to override the back command that just exits the context.
+      form: [[Vocabulary.Keys.Directions.Back, Vocabulary.Keys.Verbs.Continue]]
+      # We want to override the back/continue commands that just exits the context.
       priority: 1
       action: exitAction


focus on inputs when shifting range slider.

Focusing the cursor into the fields helps convey the fields are
indeed fields (and editable).

--- coffee/src/views/time_range_input_view.coffee
+++ coffee/src/views/time_range_input_view.coffee
@@ -317,10 +317,12 @@

   _onRangeSliderLowValueDidChange: (percentValue) =>
     @_percentValues start: percentValue
+    @startInputView.$('input').first().focus()

   _onRangeSliderHighValueDidChange: (percentValue) =>
     @_percentValues end: percentValue
+    @endInputView.$('input').first().focus()

   _onRangeSliderProgressDidChange: (percentValue) =>


Read from optional /etc/cobalt/allowed-ip file

--- code/serv.coffee
+++ code/serv.coffee
@@ -130,13 +130,13 @@

 check_api_and_box = [check_api_key, check_box]

-# Middleware that checks the IP address of the connecting
-# partner (which we expect to be custard).
-checkIP = (req, res, next) ->
-  # Relies on app.set "trust proxy", true in order to get
-  # sensible results.
-  # :todo: get these from file or environment variable.
-  allowed = [
+# Read optional list of allowed IPs from file.
+allowedIP = []
+if fs.existsSync("/etc/cobalt/allowed-ip")
+  allowedIP = fs.readFileSync("/etc/cobalt/allowed-ip")
+  allowedIP = allowedIP.replace /\n$/, ''
+  allowedIP = allowedIP.split "\n"
+allowedIP = allowedIP.concat [
     "127.0.0.1"
     "178.79.181.194"
     "192.168.129.215"
@@ -149,7 +149,13 @@
     "88.211.55.91"
     "176.58.127.147"
     ]
-  if req.ip in allowed
+# Middleware that checks the IP address of the connecting
+# partner (which we expect to be custard).
+checkIP = (req, res, next) ->
+  # Relies on app.set "trust proxy", true in order to get
+  # sensible results.
+  # :todo: get these from file or environment variable.
+  if req.ip in allowedIP
     return next()
   res.send 403, {error: "IP #{req.ip} not allowed"}


fix wrong reference to 'copy'

fix wrong reference to 'copy'

--- src/macro-escaper.coffee
+++ src/macro-escaper.coffee
@@ -404,11 +404,11 @@
         tag_stack.push raw
       when ')'
         if tag_stack.length < 1
-          return [ '.', 'warning', "too many closing regions", ( copy meta ), ]
+          return [ '.', 'warning', "too many closing regions", ( MKTS.MD_READER.copy meta ), ]
         expected = tag_stack.pop()
         if ( raw.length > 0 ) and expected isnt raw
           message = "expected closing region #{rpr expected}, got #{rpr raw}"
-          return [ '.', 'warning', message, ( copy meta ), ]
+          return [ '.', 'warning', message, ( MKTS.MD_READER.copy meta ), ]
         raw = expected
       else
         throw new Error "expected '(' or ')' as region markup, got #{rpr markup}"


Added getCMSFieldsScafoldless snippet

Added getCMSFieldsScafoldless snippet

--- completions.cson
+++ completions.cson
@@ -1552,10 +1552,47 @@
   {
   \t\\$fields = parent::getCMSFields();
   \t\\$fields->${0}
+  \treturn \\$fields;
+  }
+  """
+
+'getCMSFieldsScafoldless':
+  'prefix': 'getcmsfieldsscafoldless'
+  'module': 'silverstripe/framework'
+  'scope': '.text.html.php'
+  'body': """
+  /**
+   * CMS Fields
+   * @return FieldList
+   */
+  public function getCMSFields()
+  {
+  \t\\$fields = FieldList::create();
+  \t\\$fields->push(
+  \t\tTabSet::create(
+  \t\t\t'Root',
+  \t\t\tTab::create('Main')
+  \t\t)
+  \t);
+  \t\\$fields->${0}
   \t\\$this->extend('updateCMSFields', \\$fields);
   \treturn \\$fields;
   }
   """
+  'variations': [
+    {
+      'minVersion': '4.0'
+      'useNamespace': [
+        'SilverStripe\\Forms\\FieldList',
+        'SilverStripe\\Forms\\Tab',
+        'SilverStripe\\Forms\\TabSet'
+      ]
+    }
+    {
+      'maxVersion': '4.0'
+      'minVersion': '3.0'
+    }
+  ]

 'getSettingsFields':
   'prefix': 'getsettingsfields'


Drag & Drop

Drag & Drop

- Fixed a problem with multiple drag instances. A newly created
  instead could accidentally cleanup a running drag operation.
  This showed when a FlexHandle was dragged and while dragging
  an overflow Tab system was re-rendered to show the overflow
  button.

  Fixed by remembering the governing instance of a drag, cleanup
  "globalDrag" when instance matches only.

- Removed code which removed browser selection, this was problematic
  in IE and in general seems no longer to be a problem.

--- src/base/DragDropSelect/Draggable.coffee
+++ src/base/DragDropSelect/Draggable.coffee
@@ -73,18 +73,26 @@
 				check: (v) =>
 					isString(v) or CUI.isFunction(v)

+	readOpts: ->
+		super()
+		@__autoRepeatTimeout = null
+
 	__killTimeout: ->
-		if globalDrag?.autoRepeatTimeout
-			CUI.clearTimeout(globalDrag.autoRepeatTimeout)
-			globalDrag.autoRepeatTimeout = null
+		if @__autoRepeatTimeout
+			CUI.clearTimeout(@__autoRepeatTimeout)
+			@__autoRepeatTimeout = null
 		@

 	__cleanup: ->
+
 		@__killTimeout()
 		if @__ref
 			Events.ignore(instance: @__ref)
 			@__ref = null
-		globalDrag = null
+
+		if globalDrag?.instance == @
+			globalDrag = null
+		return

 	destroy: ->
 		super()
@@ -171,6 +179,7 @@
 			$source: $target
 			startEvent: ev
 			startCoordinates: point
+			instance: @
 			startScroll:
 				top: $target[0].scrollTop
 				left: $target[0].scrollLeft
@@ -209,7 +218,7 @@
 				info:
 					mousemoveEvent: mousemoveEvent

-			globalDrag.autoRepeatTimeout = CUI.setTimeout
+			@__autoRepeatTimeout = CUI.setTimeout
 				ms: 100
 				track: false
 				call: =>
@@ -227,14 +236,7 @@
 				# we drag
 				ev.preventDefault()

-				# remove selection in firefox
-				if document.selection
-					document.selection.empty()
-				else
-					window.getSelection().removeAllRanges()
-
 				@__killTimeout()
-

 				if CUI.browser.firefox
 					# ok, in firefox the target of the mousemove
@@ -280,6 +282,7 @@

 						if not globalDrag.dragStarted
 							@__startDrag(ev, $target, diff)
+
 							if @_get_cursor
 								document.body.setAttribute("data-cursor", @_get_cursor(globalDrag))
 							else
@@ -290,6 +293,7 @@
 				return

 		end_drag = (ev, stop = false) =>
+
 			start_target = globalDrag.$source
 			start_target_parents = CUI.DOM.parents(start_target)


Missing quote

Missing quote

--- src/sentry/static/sentry/coffee/templates.coffee
+++ src/sentry/static/sentry/coffee/templates.coffee
@@ -27,9 +27,9 @@
                 <% if (canResolve) { %>

                         <% if (!isResolved) { %>
-                            ✓
+                            ✓
                         <% } else { %>
-                            ✓
+                            ✓
                         <% } %>


Various improvements to pointer code

Various improvements to pointer code

--- src/coffee/jsEngine/Input/Pointer.coffee
+++ src/coffee/jsEngine/Input/Pointer.coffee
@@ -78,7 +78,6 @@
       button.events.unshift new Date().getTime()
     return

-
   ###
   Registers every onmouseup event to the Mouse object.

@@ -95,7 +94,6 @@
       button.events.unshift -new Date().getTime()
     return

-
   ###
   Registers every onmousemove event to the Mouse object.

@@ -127,7 +125,6 @@
       @cursor.y = @mouse.y
     return

-
   ###
   Registers every ontouchstart event to the Mouse object.

@@ -139,14 +136,10 @@

     # Update pressed touches
     for eventTouch in event.changedTouches
-      if eventTouch.identifier > 20
-        touchNumber = @findTouchNumber()
-      else
-        touchNumber = eventTouch.identifier
-
+      touchNumber = @findTouchNumber()
       pointerTouch = @touches[touchNumber]
       pointerTouch.identifier = eventTouch.identifier
-      pointerTouch.events = pointerTouch.events.slice(0, 1)
+      pointerTouch.events = pointerTouch.events.slice 0, 1
       pointerTouch.events.unshift new Date().getTime()

     # Update all touch positions
@@ -161,29 +154,19 @@
   ###
   onTouchEnd: (event) ->
     throw new Error("Missing argument: event") if event is undefined #dev
-    i = undefined
-    eventTouch = undefined
-    pointerTouch = undefined

     # Update unpressed touches
-    i = 0
-    while i < event.changedTouches.length
-      eventTouch = event.changedTouches[i]
-      if event.changedTouches[i].identifier > 20
-        pointerTouch = @touches.filter((t) ->
-          t.identifier is eventTouch.identifier
-        )[0]
-      else
-        pointerTouch = @touches[eventTouch.identifier]
+    for eventTouch in event.changedTouches
+      pointerTouch = @touches.filter((t) ->
+        t.identifier is eventTouch.identifier
+      )[0]
       pointerTouch.events = pointerTouch.events.slice(0, 1)
       pointerTouch.events.unshift -new Date().getTime()
-      i++

     # Update all touch positions
     @onTouchMove event
     return

-
   ###
   Registers every ontouchmove event to the Mouse object.

@@ -192,19 +175,11 @@
   ###
   onTouchMove: (event) ->
     throw new Error("Missing argument: event") if event is undefined #dev
-    i = undefined
-    eventTouch = undefined
-    pointerTouch = undefined
-    roomPos = undefined
-    i = 0
-    while i < event.touches.length
-      eventTouch = event.touches[i]
-      if event.touches[i].identifier > 20
-        pointerTouch = @touches.filter((t) ->
-          t.identifier is eventTouch.identifier
-        )[0]
-      else
-        pointerTouch = @touches[eventTouch.identifier]
+
+    for eventTouch in event.touches
+      pointerTouch = @touches.filter((t) ->
+        t.identifier is eventTouch.identifier
+      )[0]
       pointerTouch.set eventTouch.pageX - engine.arena.offsetLeft - engine.canvas.offsetLeft + document.body.scrollLeft, eventTouch.pageY - engine.arena.offsetTop - engine.canvas.offsetTop + document.body.scrollTop
       pointerTouch.x = pointerTouch.x / engine.arena.offsetWidth * engine.canvasResX
       pointerTouch.y = pointerTouch.y / engine.arena.offsetHeight * engine.canvasResY
@@ -213,9 +188,7 @@
       roomPos = @calculateRoomPosition(pointerTouch)
       pointerTouch.x = roomPos.x
       pointerTouch.y = roomPos.y
-      i++
-    return
-
+    return

   ###
   Checks if the mouse has been moved between the last and the current frame.
@@ -224,7 +197,6 @@
   ###
   mouseHasMoved: ->
     engine.last < @mouse.lastMoved
-

   ###
   Checks if a mouse button or touch is currently down.
@@ -243,7 +215,6 @@
       when "any"
         pointers = @mouse.buttons.concat(@touches)
     @checkPointer pointers, "down"
-

   ###
   Checks if a mouse button or touch has just been pressed (between the last and the current frame).
@@ -263,7 +234,6 @@
         pointers = @mouse.buttons.concat(@touches)
     @checkPointer pointers, "pressed"

-
   ###
   Checks if a mouse button or touch just been released (between the last and the current frame).

@@ -302,22 +272,18 @@

     # Narrow possible presses down to the pressed pointers within the selected buttons
     pointers = @isPressed(button)
-    return false unless pointers

     # Check each of the pointers to see if they are inside the shape
     ret = []
-    i = 0
-    while i < pointers.length
-      pointer = pointers[i]
+    for pointer in pointers
       continue if pointer.x is false or pointer.y is false
       if not outside and shape.contains(pointer)
         ret.push pointer
-      else ret.push pointer if outside and not shape.contains(pointer)
-      i++
+      else
+        ret.push pointer if outside and not shape.contains(pointer)

     # Return the pointers which are inside the shape
-    (if ret.length then ret else false)
-
+    ret

   ###
   Checks if an area defined by a geometric shape, or its outside, has just been released (between the last and the current frame).
@@ -332,29 +298,21 @@
     button = (if button isnt undefined then button else MOUSE_TOUCH_ANY)
     throw new Error("Missing argument: shape") if shape is undefined #dev
     throw new Error("Argument shape has implement a \"contains\"-function") if typeof shape.contains isnt "function" #dev
-    i = undefined
-    pointers = undefined
-    pointer = undefined
-    ret = undefined

     # Narrow possible presses down to the pressed pointers within the selected buttons
     pointers = @isReleased(button)
-    return false unless pointers

     # Check each of the pointers to see if they are inside the shape
     ret = []
-    i = 0
-    while i < pointers.length
-      pointer = pointers[i]
+    for pointer in pointers
       continue if pointer.x is false or pointer.y is false
       if not outside and shape.contains(pointer)
         ret.push pointer
-      else ret.push pointer if outside and not shape.contains(pointer)
-      i++
+      else
+        ret.push pointer if outside and not shape.contains(pointer)

     # Return the pointers which are inside the shape
-    (if ret.length then ret else false)
-
+    ret

   ###
   Checks if an area defined by a geometric shape, or its outside, is down (currently clicked by mouse or touch).
@@ -369,29 +327,21 @@
     button = (if button isnt undefined then button else MOUSE_TOUCH_ANY)
     throw new Error("Missing argument: shape") if shape is undefined #dev
     throw new Error("Argument shape has implement a \"contains\"-function") if typeof shape.contains isnt "function" #dev
-    i = undefined
-    pointers = undefined
-    pointer = undefined
-    ret = undefined

     # Narrow possible pointers down to the pointers which are down within the selected buttons
-    pointers = @isDown(button)
-    return false unless pointers
+    pointers = @isDown button

     # Check each of the pointers to see if they are inside the shape
     ret = []
-    i = 0
-    while i < pointers.length
-      pointer = pointers[i]
+    for pointer in pointers
       continue if pointer.x is false or pointer.y is false
       if not outside and shape.contains(pointer)
         ret.push pointer
-      else ret.push pointer if outside and not shape.contains(pointer)
-      i++
+      else
+        ret.push pointer if outside and not shape.contains(pointer)

     # Return the pointers which are inside the shape
-    (if ret.length then ret else false)
-
+    ret

   ###
   Returns a string representing the button type.
@@ -435,9 +385,7 @@
           ret.push pointer if -pointer.events[0] > engine.last || -pointer.events[1] > engine.last
         when "down"
           ret.push pointer if pointer.events[0] > 0
-
-    (if ret.length then ret else false)
-
+    ret

   ###
   Converts a coordinate which is relative to the main canvas to a position in the room (based on the room's cameras)
@@ -474,7 +422,6 @@
         return ret
     ret

-
   ###
   Finds the first available spot in the Pointer.touches-array, used for registering the touches as numbers from 0-9.
   In Google Chrome, each touch's identifier can be used directly since the numbers - starting from 0 - are reused, when the a touch is released.
@@ -484,13 +431,9 @@
   @return {number|boolean} The first available spot in the Pointer.touches-array where a new touch can be registered. If no spot is available, false is returned
   ###
   findTouchNumber: ->
-    i = undefined
-    i = 0
-    while i < @touches.length
-      return i unless @checkPointer(@touches[i], "down")
-      i++
+    for touch, i in @touches
+      return i unless touch.events[0] > 0
     false
-

   ###
   Checks if an area defined by a geometric shape, or its outside, is hovered by the mouse pointer.
@@ -505,7 +448,6 @@
       return true
     else return true if outside and (not shape.contains(@mouse))
     false
-

   ###
   Releases a button, and thereby prevents the button from being detected as "pressed" by the isPressed function.
@@ -540,7 +482,6 @@
       i++
     unpressed

-
   ###
   Checks if the mouse pointer is outside the game arena.

@@ -549,7 +490,6 @@
   outside: ->
     new Math.Rectangle(engine.arena.offsetLeft, engine.arena.offsetTop, engine.arena.offsetWidth, engine.arena.offsetHeight).contains(@mouse.window) is false

-
   ###
   Resets the mouse cursor, automatically called by the engine before each frame i executed, unless engine.resetCursorOnEachFrame is set to false

@@ -558,7 +498,6 @@
   resetCursor: ->
     engine.arena.style.cursor = "default"
     return
-

   ###
   Sets the mouse cursor for the arena.


fix dataTable search

fix dataTable search

--- BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
+++ BackofficeBundle/Resources/public/coffee/table/TableviewCollectionView.coffee
@@ -136,7 +136,7 @@

   searchColumn : (event) ->
     value = $(event.target).val()
-    columnIndex = $(event.target).parent().get(0).cellIndex
+    columnIndex = $(event.target).closest("td").get(0).cellIndex
     api = @.$el.find('table').dataTable().api()
     api.column(columnIndex).search(value).draw()


activity: render sidebar inline

activity: render sidebar inline

--- client/activity/lib/components/channelthreadpane/index.coffee
+++ client/activity/lib/components/channelthreadpane/index.coffee
@@ -58,14 +58,6 @@
     </ThreadHeader>

-  renderSidebar: ->
-    <ThreadSidebar
-      channelThread={@state.channelThread}
-      popularMessages={@state.popularMessages}
-      channelParticipants={@state.channelParticipants}/>
-
-
-
   render: ->

@@ -81,7 +73,10 @@

-        {@renderSidebar()}
+        <ThreadSidebar
+          channelThread={@state.channelThread}
+          popularMessages={@state.popularMessages}
+          channelParticipants={@state.channelParticipants}/>


EnvironmentMachineStateModal: wait until machine revives before preparing IDE

--- client/app/lib/providers/environmentsmachinestatemodal.coffee
+++ client/app/lib/providers/environmentsmachinestatemodal.coffee
@@ -127,7 +127,8 @@
       @updatePercentage percentage  if percentage?

     else
-      @state = status
+
+      [ @oldState, @state ] = [ @state, status ]

       if error

@@ -197,14 +198,19 @@

   completeCurrentProcess: (status, initial) ->

+
     @clearEventTimer()
-
-    return  if @switchToIDEIfNeeded status, initial

     @progressBar?.updateBar 100
     @progressBar?.show()

-    kd.utils.wait 500, => @buildViews()
+    if @oldState is Building and @state is Running
+      cc = kd.getSingleton 'computeController'
+      cc.once "revive-#{@machineId}", =>
+        @switchToIDEIfNeeded status, initial
+    else
+      unless @switchToIDEIfNeeded status, initial
+        kd.utils.wait 500, => @buildViews()

   showBusy: (message) ->
@@ -756,25 +762,21 @@

   prepareIDE: (initial) ->

-    {appManager, computeController} = kd.singletons
-
-    # FIXME: We shouldn't use computeController.fetchMachine in this case.
-    computeController.fetchMachines (err) =>
-
-      return if showError err
-
-      environmentDataProvider.fetchMachine @machine.uid, (machine) =>
-
-        # return showError "Couldn't fetch your VMs"  unless machine
-        unless machine
-          return appManager.tell 'IDE', 'quit'
-
-        @machine = machine
-        @setData machine
-
-        @emit 'IDEBecameReady', machine
-
-        computeController.showBuildLogs machine  if initial
+
+    { appManager, computeController } = kd.singletons
+
+    environmentDataProvider.fetchMachine @machine.uid, (machine) =>
+
+      # return showError "Couldn't fetch your VMs"  unless machine
+      unless machine
+        return appManager.tell 'IDE', 'quit'
+
+      @machine = machine
+      @setData machine
+
+      @emit 'IDEBecameReady', machine
+
+      computeController.showBuildLogs machine  if initial

   verifyAccount: ->


[Group Notification Email]  added flags for group join and left

--- workers/emailnotifications/main.coffee
+++ workers/emailnotifications/main.coffee
@@ -51,6 +51,12 @@
   groupApproved        :
     template           : template.instantMail
     definition         : "group membership request approved"
+  groupJoined          :
+    template           : template.instantMail
+    definition         : "a member has joind group"
+  groupLeft            :
+    template           : template.instantMail
+    definition         : "a member has left group"

 sendDailyEmail = (details, content)->
   unless content or details.email


Allow empty sentence IDs

Allow empty sentence IDs

--- app/assets/javascripts/rglossa/controllers/cwb/result/table_controller.coffee
+++ app/assets/javascripts/rglossa/controllers/cwb/result/table_controller.coffee
@@ -14,8 +14,8 @@

     if resultPage
       resultPage.map (row) ->
-        m = row.match(/<s_id (.+)>:\s+(.*)<(.+?)>(.*)/)
-        sId = m[1]
+        m = row.match(/<s_id(.*)>:\s+(.*)<(.+?)>(.*)/)
+        sId = m[1].trim()
         fields = [m[2], m[3], m[4]]

         fields = fields.map (field) ->


Fix news text

Fix news text

--- server/materials/data/news.coffee
+++ server/materials/data/news.coffee
@@ -15,6 +15,8 @@
             уже решенные вами темы поднялись на следующие уровни. Рейтинги, думаю, у большинства из вас должны были только вырасти.

Также принимаются предложения по добавлению еще задач, особенно в темы, где мало задач, и особенно на высоких уровнях,
             а также другие пожелания по структуре алгопрога.

+
Из-за фейковых задач на старших уровнях у многих учеников получился очень большой рейтинг и появились решенные задачи с высоких уровней.
+            Это баг; когда фейковые задачи я уберу, у вас все нормализуется :)

         """),
         newsItem("Про очные занятия в новом учебном году", String.raw"""

Я очень надеюсь, что в новом учебном году будут очные занятия (как всегда, для нижегородских школьников), но на данный момент, насколько я понимаю, никаких официальных разъяснений насчет проведения дополнительных занятий нет (основные школьные занятия вроде как начнутся 1 сентября, но наши занятия — это дополнительные занятия, про них не понятно). Вы можете, как всегда, заниматься через сайт откуда угодно (из дома, из школы и т.д.), но очные занятия начнутся скорее всего не раньше, чем Роспотребнадзор официально разрешит проведение дополнительных занятий; и даже если это разрешение будет в сентябре, наши занятия скорее всего не начнутся раньше 1 октября.


Never show tooltip on touch event

Never show tooltip on touch event

--- resources/assets/coffee/forum-posts-seek.coffee
+++ resources/assets/coffee/forum-posts-seek.coffee
@@ -24,8 +24,7 @@
   constructor: (forum) ->
     @forum = forum
     $(document).on 'mouseenter', '.js-forum__posts-seek', @enter
-    $(document).on 'mouseleave', '.js-forum__posts-seek', @leave
-    $(document).on 'touchstart', @onGlobalTouchstart
+    $(document).on 'mouseleave touchend', '.js-forum__posts-seek', @leave
     $(document).on 'mousemove', '.js-forum__posts-seek', @move
     $(document).on 'click', '.js-forum__posts-seek', @click

@@ -33,11 +32,15 @@

   enter: (e) =>
+    return if @tooltip[0]._visible
+
     @move(e)
     fade.in @tooltip[0]

   leave: (e) =>
+    return unless @tooltip[0]._visible
+
     @tooltip[0]._visible = false
     fade.out @tooltip[0]

@@ -78,14 +81,3 @@

     $target.blur()
     @forum.jumpTo n
-
-
-  onGlobalTouchstart: (e) =>
-    return unless @tooltip.length && @tooltip[0]._visible
-
-    closest = e.target
-    while closest
-      return if closest.classList.contains 'js-forum__posts-seek'
-      closest = closest.parentElement
-
-    @leave()


fix the exp when level is 120

fix the exp when level is 120

--- views/components/main/parts/teitoku-panel.cjsx
+++ views/components/main/parts/teitoku-panel.cjsx
@@ -23,7 +23,7 @@
 getHeader = (state) ->
   if state.nickname?
     if state.level == 120
-      return "Lv. #{state.level} #{state.nickname} [#{rankName[state.rank]}]　　　　Exp. #{state.nextExp}"
+      return "Lv. #{state.level} #{state.nickname} [#{rankName[state.rank]}]　　　　Exp. #{state.exp}"
     else
       return "Lv. #{state.level} #{state.nickname} [#{rankName[state.rank]}]　　　　Next. #{state.nextExp}"
   else
@@ -38,6 +38,7 @@
     nickname: null
     rank: 0
     nextExp: '?'
+    exp: '?'
     material: ['??', '??', '??', '??', '??', '??', '??', '??', '??']
     shipCount: '??'
     maxChara: '??'
@@ -51,6 +52,7 @@
           level: body.api_level
           nickname: body.api_nickname
           rank: body.api_rank
+          exp: body.api_experience
           nextExp: totalExp[body.api_level] - body.api_experience
           maxChara: body.api_max_chara
           maxSlotitem: body.api_max_slotitem
@@ -140,11 +142,13 @@
       when '/kcsapi/api_req_practice/battle_result'
         @setState
           level: body.api_member_lv
+          exp: body.api_member_exp
           nextExp: totalExp[body.api_member_lv] - body.api_member_exp
       when '/kcsapi/api_req_sortie/battleresult'
         @setState
           shipCount: if body.api_get_ship? then @state.shipCount + 1 else @state.shipCount
           level: body.api_member_lv
+          exp: body.api_member_exp
           nextExp: totalExp[body.api_member_lv] - body.api_member_exp
   componentDidMount: ->
     window.addEventListener 'game.response', @handleResponse


Dismiss link has .dismiss class

Dismiss link has .dismiss class

--- test/components/notifications/system.spec.coffee
+++ test/components/notifications/system.spec.coffee
@@ -18,5 +18,5 @@

   it 'remembers notice as ignored when dismiss is clicked', ->
     Testing.renderComponent( SystemNotifications, props: @props ).then ({dom}) =>
-      Testing.actions.click(dom.querySelector('.action'))
+      Testing.actions.click(dom.querySelector('.dismiss'))
       expect(Notifications.acknowledge).to.have.been.calledWith(@props.notice)


Terminal: special chars code styling

Terminal: special chars code styling


Teambuilder: evs/ivs default to reasonable values

--- client/app/js/models/pokemon.coffee
+++ client/app/js/models/pokemon.coffee
@@ -103,10 +103,10 @@
     @set("evs", evs)  # trigger change event

   iv: (stat) ->
-    @get("ivs")[stat]
+    @get("ivs")[stat] || 31

   ev: (stat) ->
-    @get("evs")[stat]
+    @get("evs")[stat] || 0

   natureBoost: (stat) ->
     nature = @get('nature')?.toLowerCase()


Fix SubmitList

Fix SubmitList

--- client/components/SubmitList.coffee
+++ client/components/SubmitList.coffee
@@ -70,13 +70,16 @@

 options =
     urls: (props) ->
-        urls = {"myUser"}
         if props?.myUser?._id
-            urls["data"] = "submits/#{props.myUser._id}/#{props.material._id}"
-        return urls
+            return {data: "submits/#{props.myUser._id}/#{props.material._id}"}
+        return {}

     timeout: 20 * 1000

+myUserOptions =
+    urls: (props) ->
+        return {"myUser"}

+    timeout: 20 * 1000

-export default ConnectedComponent(SubmitList, options)
+export default ConnectedComponent(ConnectedComponent(SubmitList, options), myUserOptions)


Added keyboard wiring

Added keyboard wiring

--- core/app/backbone/views/facts/auto_complete_facts_view.coffee
+++ core/app/backbone/views/facts/auto_complete_facts_view.coffee
@@ -45,20 +45,20 @@
       @model().map (fact, key) =>
         @_childView(fact, key)

-  # fixKeyModulo: (key)->
-  #   if key >= @list.length then 0
-  #   else if key < 0 then @list.length - 1
-  #   else key
+  _fixKeyModulo: (key)->
+    if key >= @model().length then 0
+    else if key < 0 then @model().length - 1
+    else key

-  # moveSelectionUp: ->
-  #   prevKey = if @state.selectedModelKey? then @state.selectedModelKey-1 else -1
-  #   @setState selectedModelKey: prevKey
-  #   # @scrollToCurrent()
+  moveSelectionUp: ->
+    prevKey = if @state.selectedModelKey? then @state.selectedModelKey-1 else -1
+    @setState selectedModelKey: @_fixKeyModulo(prevKey)
+    # @scrollToCurrent()

-  # moveSelectionDown: ->
-  #   nextKey = if @state.selectedModelKey? then @state.selectedModelKey+1 else 0
-  #   @setState selectedModelKey: nextKey
-  #   # @scrollToCurrent()
+  moveSelectionDown: ->
+    nextKey = if @state.selectedModelKey? then @state.selectedModelKey+1 else 0
+    @setState selectedModelKey: @_fixKeyModulo(nextKey)
+    # @scrollToCurrent()

 class window.AutoCompleteFactsView extends Backbone.Marionette.Layout
   className: "auto-complete"
@@ -84,11 +84,11 @@
       onSelect: => @addSelectedModel()

     @_text_input_view = new Backbone.Factlink.ReactTextInputView
+      placeholder: 'Search discussion link to insert...'
       onChange: (value) => @search_collection.searchFor value
-      placeholder: 'Search discussion link to insert...'
-    # @listenTo @_text_input_view, 'down', -> @_search_list_view.moveSelectionDown()
-    # @listenTo @_text_input_view, 'up',   -> @_search_list_view.moveSelectionUp()
-    # @listenTo @_text_input_view, 'return', @addSelectedModel
+      onUp: => @_search_list_view.moveSelectionUp()
+      onDown: => @_search_list_view.moveSelectionDown()
+      onReturn: => @addSelectedModel()

     # @listenTo @model, 'change', @queryChanges


socialapi: add validation method to message events

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -171,6 +171,18 @@
   mapChannels: mapChannels

+  # this method will prevent the arrival of
+  # realtime messages to the individual messages
+  # if the message is mine and current window has focus.
+  isFromThisBrowser = (message) ->
+
+    isMyPost  = KD.isMyPost message
+    isFocused = KD.singletons.windowController.isFocused()
+    isBlocker = isMyPost and isFocused
+
+    return not isBlocker
+
+  isFromThisBrowser : isFromThisBrowser

   forwardMessageEvents = (source, target,  events)->
     events.forEach ({event, mapperFn}) ->
@@ -314,8 +326,8 @@

   getMessageEvents = ->
     [
-      {event: "MessageAdded", mapperFn: mapActivity}
-      {event: "MessageRemoved", mapperFn: mapActivity}
+      {event: "MessageAdded",   mapperFn: mapActivity, validatorFn: isFromThisBrowser}
+      {event: "MessageRemoved", mapperFn: mapActivity, validatorFn: isFromThisBrowser}
       {event: "AddedToChannel", mapperFn: mapParticipant}
       {event: "ChannelDeleted", mapperFn: mapChannel}
     ]


socialapi: add validation to event delegation

--- client/Main/socialapicontroller.coffee
+++ client/Main/socialapicontroller.coffee
@@ -184,10 +184,14 @@

   isFromThisBrowser : isFromThisBrowser

-  forwardMessageEvents = (source, target,  events)->
-    events.forEach ({event, mapperFn}) ->
+  forwardMessageEvents = (source, target, events) ->
+    events.forEach ({event, mapperFn, validatorFn}) ->
       source.on event, (data, rest...) ->
+
         data = mapperFn data
+
+        return  unless validatorFn?(data)
+
         target.emit event, data, rest...

   forwardMessageEvents : forwardMessageEvents


CLI text conforming to coffeescript's.

CLI text conforming to coffeescript's.

--- bin/coffeekup.coffee
+++ bin/coffeekup.coffee
@@ -34,17 +34,17 @@

 usage = '''
   Usage:
-    coffeekup [OPTIONS] path/to/template.coffee
+    coffeekup [options] path/to/template.coffee
 '''

 switches = [
-  ['-w', '--watch', 'Keeps watching the file and recompiling it when it changes']
-  ['-o', '--output [DIR]', 'Specifies a directory to compile into.']
-  ['-p', '--print', 'Prints the compiled html to stdout']
-  ['-f', '--format', 'Applies line breaks to html output']
-  ['-u', '--utils', 'Adds helper locals (currently only "render")']
-  ['-h', '--help', 'Prints this help message']
-  ['-v', '--version', 'Shows CoffeeKup version']
+  ['-w', '--watch', 'watch templates for changes, and recompile']
+  ['-o', '--output [dir]', 'set the directory for compiled html']
+  ['-p', '--print', 'print the compiled html to stdout']
+  ['-f', '--format', 'apply line breaks and indentation to html output']
+  ['-u', '--utils', 'add helper locals (currently only "render")']
+  ['-v', '--version', 'display CoffeeKup version']
+  ['-h', '--help', 'display this help message']
 ]

 parser = new OptionParser switches, usage


Client: Fix log formatting for moves.

Client: Fix log formatting for moves.

--- client/app/js/views/battle_view.coffee
+++ client/app/js/views/battle_view.coffee
@@ -773,8 +773,7 @@
     @timerFrozenAt = @timers[@model.index] - $.now()

   addMoveMessage: (owner, pokemon, moveName) =>
-    @chatView.print("
#{owner}'s #{pokemon.get('name')}
-      used #{moveName}!
")
+    @chatView.print("
#{owner}'s #{pokemon.get('name')} used #{moveName}!
")

   addLog: (message) =>
     @chatView.print("
#{message}
")


load lines before sending to invoice screen

--- client/skr/components/InvoiceLink.cjsx
+++ client/skr/components/InvoiceLink.cjsx
@@ -3,9 +3,10 @@
         invoice: React.PropTypes.instanceOf(Skr.Models.Invoice)

     onClick: ->
-
-        Lanes.Screens.Definitions.all.get('invoice')
-            .display(invoice: @props.invoice)
+        @props.onClick?()
+        @props.invoice.withAssociations(['lines']).then =>
+            Lanes.Screens.Definitions.all.get('invoice')
+                .display(invoice: @props.invoice)

     render: ->
         Invoice # {@props.invoice.visible_id}


group dashboard: wip image upload

group dashboard: wip image upload

--- client/app/Applications/Groups.kdapplication/Views/generalsettingsview.coffee
+++ client/app/Applications/Groups.kdapplication/Views/generalsettingsview.coffee
@@ -13,7 +13,10 @@
       isNewGroup = yes
     isPrivateGroup = 'private' is group.privacy

-    _updateGroupHandler =(group, formData)->
+    _updateGroupHandler =(group, formData)=>
+      log formData
+      formData.avatar = @customUrl if @customUrl
+      log formData
       group.modify formData, (err)->
         if err
           new KDNotificationView
@@ -47,10 +50,16 @@
       fields:
         "Avatar"              :
           label             : "Avatar"
-          itemClass         : KDImageUploadSingleView
-          name              : "avatar"
-          limit             : 1
-          preview           : 'thumbs'
+          cssClass        : 'avatar'
+          limit           : 1
+          preview         : "thumbs"
+          extensions      : null
+          fileMaxSize     : 2048
+          totalMaxSize    : 2048
+          fieldName       : "thumbnails"
+          convertToBlob   : yes
+          title           : ""
+          itemClass         : KDImageUploadView
           actions         : {
             big    :
               [
@@ -133,15 +142,20 @@
     @settingsForm = new KDFormViewWithFields formOptions, @getData()

     avatarUploadView = @settingsForm.inputs["Avatar"]
-
     avatarUploadView.on 'FileReadComplete', ({file, progressEvent})->
+      log 'read'
       avatarUploadView.$('.kdfileuploadarea').css
         backgroundImage : "url(#{file.data})"
       avatarUploadView.$('span').addClass 'hidden'

-    avatarUploadView.on 'FileUploadComplete', (files)->
-      for {filename, resource} in files
-        console.log {filename, resource}
+    avatarUploadView.on 'FileUploadComplete', (res)=>
+      log 'upload',res
+      if res.length and res[0].resource
+        @customUrl = res[0].resource
+
+    # avatarUploadView.on 'FileUploadComplete', (files)->
+    #   for {filename, resource} in files
+    #     console.log {filename, resource}


start using cache for directoryApi calls

--- js/src/http/DirectoryApi.coffee
+++ js/src/http/DirectoryApi.coffee
@@ -8,6 +8,7 @@
   'kryptnostic.requests'
   'kryptnostic.block-ciphertext'
   'kryptnostic.validators'
+  'kryptnostic.caching-service'
 ], (require) ->

   axios             = require 'axios'
@@ -18,6 +19,7 @@
   BlockCiphertext   = require 'kryptnostic.block-ciphertext'
   Promise           = require 'bluebird'
   validators        = require 'kryptnostic.validators'
+  Cache             = require 'kryptnostic.caching-service'

   { validateId }    = validators

@@ -44,6 +46,9 @@

     # returns a serialized cryptoservice for the requested object
     getObjectCryptoService: (objectId) ->
+      cached = Cache.get( Cache.CRYPTO_SERVICES, objectId )
+      if cached?
+        return cached
       Promise.resolve()
       .then ->
         validateId(objectId)
@@ -54,6 +59,8 @@
         })))
       .then (response) ->
         serializedCryptoService = response.data.data
+        if serializedCryptoService
+          Cache.store( Cache.CRYPTO_SERVICES, objectId, serializedCryptoService )
         log.info('getObjectCryptoService', { objectId })
         return serializedCryptoService

@@ -118,6 +125,9 @@

     # gets the public key of a user in the same realm as the caller.
     getPublicKey: (uuid) ->
+      cached = Cache.get(Cache.PUBLIC_KEYS, uuid )
+      if cached?
+        return cached
       Promise.resolve(axios(Requests.wrapCredentials({
         url    : publicKeyUrl() + '/' + uuid
         method : 'GET'
@@ -125,7 +135,9 @@
       .then (response) ->
         envelope = response.data
         log.debug('getPublicKey', { envelope })
-        return new PublicKeyEnvelope(envelope)
+        keyEnv = new PublicKeyEnvelope(envelope)
+        Cache.store( Cache.PUBLIC_KEYS, uuid, keyEnv )
+        return keyEnv
       .catch (e) ->
         return undefined

@@ -178,6 +190,9 @@
     # gets the user's encrypted salt.
     # request is not wrapped because the user has not auth'ed yet.
     getSalt: (uuid) ->
+      cached = Cache.get( Cache.SALTS, uuid )
+      if cached?
+        return cached
       Promise.resolve(axios({
         url    : saltUrl() + '/' + uuid
         method : 'GET'
@@ -188,7 +203,9 @@
         if _.isEmpty(ciphertext)
           throw new Error 'incorrect credentials'
         else
-          return new BlockCiphertext(ciphertext)
+          ciphertext = new BlockCiphertext(ciphertext)
+          Cache.store( Cache.SALTS, uuid, ciphertext )
+          return ciphertext

     # sets the encrypted salt for a new user account.
     # manually sets principal and credential headers since user has not auth'ed yet.


Debug log listener options when exec callback

This makes debugging easier because you can more easily identify which
listener is being executed. This changes the debug output from -- e.g.:

    [Tue Oct 27 2015 07:35:47 GMT-0700 (PDT)] DEBUG Message 'DEVOPS-916' matched regex //(.*)$/i/
    [Tue Oct 27 2015 07:23:59 GMT-0700 (PDT)] DEBUG Executing listener callback for Message 'DEVOPS-916'

to the more helpful:

    [Tue Oct 27 2015 07:35:47 GMT-0700 (PDT)] DEBUG Message 'DEVOPS-916' matched regex //(.*)$/i/; @options = { id: 'hubot-mute.catch_all' }
    [Tue Oct 27 2015 07:28:10 GMT-0700 (PDT)] DEBUG Executing listener callback for Message 'DEVOPS-916'; @options = { id: 'hubot-mute.catch_all' }

--- src/listener.coffee
+++ src/listener.coffee
@@ -55,7 +55,7 @@
     if match = @matcher message
       if @regex
         @robot.logger.debug \
-          "Message '#{message}' matched regex /#{inspect @regex}/"
+          "Message '#{message}' matched regex /#{inspect @regex}/; listener.options = #{inspect @options}"

       # special middleware-like function that always executes the Listener's
       # callback and calls done (never calls 'next')


fixes back button in fairs microsite

fixes back button in fairs microsite

--- components/filter2/router/index.coffee
+++ components/filter2/router/index.coffee
@@ -6,6 +6,7 @@
 module.exports = class FilterRouter extends Backbone.Router

   initialize: ({ @urlRoot, @params, @stuckParamKey }) ->
+    @firstLoad = true
     @params.on 'change', @navigateParams
     @setupRoutes()

@@ -18,7 +19,9 @@
     # Only add '/artworks' to the url if there are params
     # Do not redirect from /gene/:id to gene/:id/artworks on pageload
     if params?.length > 0
-      @navigate "#{@urlRoot}?#{params}"
+      @navigate "#{@urlRoot}?#{params}", replace: @firstLoad
+    @firstLoad = false
+

   artworks: ->
     queryParams = qs.parse(location.search.replace(/^\?/, ''))


fixed holdflick gesture.

fixed holdflick gesture.

--- client_src/holdFlickObserver.coffee
+++ client_src/holdFlickObserver.coffee
@@ -57,7 +57,7 @@
 	checkHoldFlick: (cursorModel) ->
 		nearestNeighborCursorModel =
 			@getNearestNeighborCursor(cursorModel.positionStart, cursorModel.sid)
-		if nearestNeighborCursor?
+		if nearestNeighborCursorModel?
 			App.log "Hold Flick Detected!"
 			flickEvent = {
 				'type' : App.Constants.FINGER_HOLD_FLICK,
@@ -79,6 +79,7 @@
 		# cursor.
 		for cursorSID, cursorModel of @cursorCurrentPresses
 			if cursorSID is sid then continue
+
 			cursorPosition = cursorModel.positionStart
 			positionDelta = App.euclideanDistance(position, cursorPosition)


Add shortcut to post

Add shortcut to post

--- app/assets/javascripts/messages.coffee
+++ app/assets/javascripts/messages.coffee
@@ -24,6 +24,10 @@
     .on 'ajax:error', (event, xhr, status, error) ->
       alert error

+  $('#message_area').on 'keydown', (e) ->
+    if e.ctrlKey && e.keyCode == 13 # Ctrl + Enter
+      $('#message_post_form').submit()
+
   $('#messages').on 'scroll', (e) ->
     scroll = $('#messages').scrollTop()
     if !window.on_loading && scroll < 32


Refresh notes when the page becomes visible

--- app/assets/javascripts/notes.js.coffee
+++ app/assets/javascripts/notes.js.coffee
@@ -49,6 +49,9 @@

     # hide diff note form
     $(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
+
+    # fetch notes when tab becomes visible
+    $(document).on "visibilitychange", @visibilityChange

   cleanBinding: ->
     $(document).off "ajax:success", ".js-main-target-form"
@@ -63,6 +66,7 @@
     $(document).off "click", ".js-choose-note-attachment-button"
     $(document).off "click", ".js-discussion-reply-button"
     $(document).off "click", ".js-add-diff-note-button"
+    $(document).off "visibilitychange"

   initRefresh: ->
@@ -453,4 +457,10 @@
     filename = $(this).val().replace(/^.*[\\\/]/, "")
     form.find(".js-attachment-filename").text filename

+  ###
+  Called when the tab visibility changes
+  ###
+  visibilityChange: =>
+    @refresh()
+
 @Notes = Notes


separate updating fields to be shown for a data model and setting default value. It allows one not to set default values if value is given (e.g. after error) AND - it was broken - show only necessary fields as page was loaded, before one changed data model type

--- app/assets/javascripts/tasks.js.coffee
+++ app/assets/javascripts/tasks.js.coffee
@@ -9,22 +9,31 @@
   if data_model_type == 'PCM'
     $data_model_specifiers.find('.effective_count').hide()
     $data_model_specifiers.find('.pseudocount').show()
-    $model.find('.matrix textarea').val( $model.find('.data_model_examples .pcm').text() )
   else if data_model_type == 'PPM'
     $data_model_specifiers.find('.effective_count').show()
     $data_model_specifiers.find('.pseudocount').show()
-    $model.find('.matrix textarea').val( $model.find('.data_model_examples .ppm').text() )
   else if data_model_type == 'PWM'
     $data_model_specifiers.find('.effective_count').hide()
     $data_model_specifiers.find('.pseudocount').hide()
+
+set_default_data_model = (dataModelSelector)->
+  $model = $(dataModelSelector)
+  data_model_type = ($model.find('.data_model select').prop('value') || '').toUpperCase()
+  if data_model_type == 'PCM'
+    $model.find('.matrix textarea').val( $model.find('.data_model_examples .pcm').text() )
+  else if data_model_type == 'PPM'
+    $model.find('.matrix textarea').val( $model.find('.data_model_examples .ppm').text() )
+  else if data_model_type == 'PWM'
     $model.find('.matrix textarea').val( $model.find('.data_model_examples .pwm').text() )

 window.register_data_model_form = (dataModelSelector)->
   $model = $(dataModelSelector)
+  update_data_model_form($model)
   if $model.find('.matrix textarea').length != 0 && $model.find('.matrix textarea').val().length == 0
-    update_data_model_form($model)
+    set_default_data_model($model)
   $model.find('.data_model select').change ->
     update_data_model_form($model)
+    set_default_data_model($model)

 $(document).ready ->
   update_background_model_form = (background_form)->


when advanced options contain errors - show them expanded

--- app/assets/javascripts/tasks.js.coffee
+++ app/assets/javascripts/tasks.js.coffee
@@ -64,4 +64,7 @@

   $('.expand_button').click ->
     $(this).next('.advanced_options').toggle()
-
+  $('.advanced_options').each ->
+    advanced_section = $(this)
+    if  advanced_section.find('[data-error]').size() > 0
+      advanced_section.show()


change hash key name

change hash key name

--- src/service/index.coffee
+++ src/service/index.coffee
@@ -1,11 +1,11 @@
-module.exports =
-  IdentityService:        require './identity'
-  AccountService:         require './account'
-  ComputeService:         require './compute'
-  BlockStorageService:    require './block_storage'
-  ImageService:           require './image'
-  NetworkService:         require './network'
-  ObjectStorageService:   require './object_storage'
-  DatabaseHostingService: require './database_hosting'
-  DNSService:             require './dns'
-  MailHostingService:     require './mail_hosting'
+module.exports = ServiceClass =
+  identity:        require './identity'
+  account:         require './account'
+  compute:         require './compute'
+  blockstorage:    require './block_storage'
+  image:           require './image'
+  network:         require './network'
+  objectstorage:   require './object_storage'
+  databasehosting: require './database_hosting'
+  dns:             require './dns'
+  mailhosting:     require './mail_hosting'


Refactor

Refactor

--- packages/shrub-core/client/index.coffee
+++ packages/shrub-core/client/index.coffee
@@ -44,16 +44,16 @@
 							promises
 						)

-						promiseKeysArray = []
+						promiseKeys = []
 						promiseArray = for key, promise of promises
-							promiseKeysArray.push key
+							promiseKeys.push key
 							promise

 						originalAll(promiseArray).then (results) ->
 							objectResult = {}

 							for result, index in results
-								objectResult[promiseKeysArray[index]] = result
+								objectResult[promiseKeys[index]] = result

 							objectResult


Do no init Vomnibar pre-DomReady.

Do no init Vomnibar pre-DomReady.

--- content_scripts/vimium_frontend.coffee
+++ content_scripts/vimium_frontend.coffee
@@ -438,7 +438,7 @@
   initializeTopFrame = -> # Only do this initialization once.
   # We only initialize the vomnibar in the tab's top/main frame, because it's only ever opened there.
   if DomUtils.isTopFrame()
-    Vomnibar.init()
+    DomUtils.documentReady -> Vomnibar.init()
   else
     # Ignore requests from other frames (we're not the top frame).
     unless request?


trying to adapt to use local colors

trying to adapt to use local colors

not sure if it can work

--- src/smc-webapp/3d.coffee
+++ src/smc-webapp/3d.coffee
@@ -428,6 +428,11 @@
     add_obj: (myobj)=>
         @show_canvas()

+        if myobj.type == "index_face_set"
+            has_local_colors = myobj.has_local_colors
+        else
+            hase_local_colors = false
+
         vertices = myobj.vertex_geometry
         for objects in [0...myobj.face_geometry.length]
             #console.log("object=", misc.to_json(myobj))
@@ -450,7 +455,6 @@
                 for k in [0...face5.length] by 6   # yep, 6 :-()
                     faces.push(face5.slice(k,k+6))

-
             geometry = new THREE.Geometry()

             for k in [0...vertices.length] by 3
@@ -458,6 +462,12 @@

             push_face3 = (a, b, c) =>
                 geometry.faces.push(new THREE.Face3(a-1, b-1, c-1))
+                #geometry.faces.push(new THREE.Face3(b-1, a-1, c-1))   # both sides of faces, so material is visible from inside -- but makes some things like look really crappy; disable.  Better to just set a property of the material/light, which fixes the same problem.
+
+           push_face3_with_color = (a, b, c, d) =>
+                face = new THREE.Face3(a-1, b-1, c-1)
+                face.color.setHex(d)
+                geometry.faces.push(face)
                 #geometry.faces.push(new THREE.Face3(b-1, a-1, c-1))   # both sides of faces, so material is visible from inside -- but makes some things like look really crappy; disable.  Better to just set a property of the material/light, which fixes the same problem.

             # *polyogonal* faces defined by 4 vertices (squares), which for THREE.js we must define using two triangles
@@ -482,7 +492,10 @@
             for v in faces
                 switch v.length
                     when 3
-                        push_face3(v...)
+                        if has_local_colors
+                            push_face3(v...)
+                        else:
+                            push_face3_with_color(v...)
                     when 4
                         push_face4(v...)
                     when 5
@@ -534,12 +547,19 @@

                 m = myobj.material[mk]

-                material =  new THREE.MeshPhongMaterial
-                    shininess   : "1"
-                    wireframe   : false
-                    transparent : m.opacity < 1
-
-                material.color.setRGB(m.color[0],    m.color[1],    m.color[2])
+                if has_local_colors
+                    material =  new THREE.MeshPhongMaterial
+                        shininess   : "1"
+                        wireframe   : false
+                        transparent : m.opacity < 1
+                    material.color.setRGB(m.color[0],    m.color[1],    m.color[2])
+                else
+                    material =  new THREE.MeshPhongMaterial
+                        shininess   : "1"
+                        wireframe   : false
+                        transparent : m.opacity < 1
+                        vertexColors: THREE.FaceColors
+
                 material.specular.setRGB(m.specular[0], m.specular[1], m.specular[2])
                 material.opacity = m.opacity


added detailed logging on health check failure

--- app/coffee/HealthCheckController.coffee
+++ app/coffee/HealthCheckController.coffee
@@ -10,7 +10,7 @@
 buildUrl = (path) ->
 	"http://localhost:#{port}#{path}"

-module.exports =
+module.exports =
 	check : (callback)->
 		project_id = ObjectId()
 		user_id = ObjectId(settings.tags.healthCheck.user_id)
@@ -20,25 +20,31 @@
 			json:
 				name: tagName
 		}, (err, res, body) ->
-			return callback(err) if err?
+			if err
+				logger.log "Failed executing create tag health check"
+				return callback(err)
 			if res.statusCode != 200
 				return callback new Error("unexpected statusCode: #{res.statusCode}")
 			logger.log {tag: body, user_id, project_id}, "health check created tag"
 			tag_id = body._id
-
-
+
+
 			request.post {
 				url: buildUrl("/user/#{user_id}/tag/#{tag_id}/project/#{project_id}")
 			}, (err, res, body) ->
-				return callback(err) if err?
+				if err
+					logger.log "Failed executing create project in tag health check"
+					return callback(err)
 				if res.statusCode != 204
 					return callback new Error("unexpected statusCode: #{res.statusCode}")
-
+
 				request.get {
 					url: buildUrl("/user/#{user_id}/tag"),
 					json: true
 				}, (err, res, tags) ->
-					return callback(err) if err?
+					if err
+						logger.log "Failed executing list tags health check"
+						return callback(err)
 					if res.statusCode != 200
 						return callback new Error("unexpected statusCode: #{res.statusCode}")
 					hasTag = false
@@ -49,5 +55,11 @@
 							break
 					if !hasTag
 						return callback new Error("tag was not found in response")
-
-					request.del buildUrl("/user/#{user_id}/tag/#{tag_id}"), callback
+
+					request.del {
+						url: buildUrl("/user/#{user_id}/tag/#{tag_id}"),
+						json: true
+					}, (err, res, body) ->
+						if err
+							logger.log "Failed executing delete tags health check"
+						callback(err, res, body)


show title according to settings on discussions page

--- resources/assets/coffee/react/beatmap-discussions/header.coffee
+++ resources/assets/coffee/react/beatmap-discussions/header.coffee
@@ -12,6 +12,7 @@
 import { PlaymodeTabs } from 'playmode-tabs'
 import * as React from 'react'
 import { a, div, h1, h2, p } from 'react-dom-factories'
+import { getTitle } from 'utils/beatmap-helper'
 el = React.createElement

 export class Header extends React.PureComponent
@@ -92,7 +93,7 @@
           href: laroute.route('beatmapsets.show', beatmapset: @props.beatmapset.id)
           h1
             className: "#{bn}__title"
-            @props.beatmapset.title
+            getTitle(@props.beatmapset)
           h2
             className: "#{bn}__title #{bn}__title--artist"
             @props.beatmapset.artist


fix version to match client

fix version to match client

--- app/js/controllers/unlockwallet.coffee
+++ app/js/controllers/unlockwallet.coffee
@@ -23,7 +23,7 @@
       if (Info.info.client_version)
         cancel()
         $scope.client_version = Info.info.client_version
-        if $scope.client_version != "0.4.13"
+        if $scope.client_version != "v0.4.15-a"
             $scope.update_available = true


Remove auto-snippet-expansion when `isSnippet` is defined

This was only in there for the autocomplete-snippets package, which now expands
on its own

--- lib/autocomplete-manager.coffee
+++ lib/autocomplete-manager.coffee
@@ -262,13 +262,6 @@

     @replaceTextWithMatch(suggestion)

-    # FIXME: move this to the snippet provider's onDidInsertSuggestion() method
-    # when the API has been updated.
-    if suggestion.isSnippet
-      setTimeout =>
-        atom.commands.dispatch(atom.views.getView(@editor), 'snippets:expand')
-      , 1
-
     # TODO API: Remove when we remove the 1.0 API
     if apiIs20
       suggestion.provider.onDidInsertSuggestion?({@editor, suggestion, triggerPosition})


Listen for events from the cursor instead of the editor

cursor:moved only fires when the cursor view pixel position changes.

--- lib/bracket-matcher-view.coffee
+++ lib/bracket-matcher-view.coffee
@@ -20,8 +20,9 @@
   initialize: (@editor) ->
     @pairHighlighted = false

-    @subscribe @editor, 'cursor:moved', =>
+    @subscribe @editor.getCursor(), 'moved', =>
       @updateMatch()
+
     @subscribeToCommand @editor, 'editor:go-to-matching-bracket', =>
       @goToMatchingPair()


provide email address on auth

provide email address on auth

--- code/serv.coffee
+++ code/serv.coffee
@@ -185,6 +185,7 @@
           res.send 200,
             shortname: user.shortname
             displayname: user.displayname
+            email: user.email
             apikey: user.apikey
         else
           res.send 403,