refactoring: adjust buffer after fetch split off adjust buffer

--- src/ui-scroll.coffee
+++ src/ui-scroll.coffee
@@ -299,7 +299,7 @@
 								wrapper.unregister()
 						false

-				processBufferedItems = ->
+				processBufferedItems = (rid) ->
 					promises = []
 					toBePrepended = []
 					toBeRemoved = []
@@ -346,52 +346,64 @@

 					# re-index the buffer
 					item.scope.$index = first + i for item,i in buffer
-					[keepFetching, promises]
-
-				adjustBuffer = (rid, adjustAfterFetch) ->
+
+					# schedule another adjustBuffer after animation completion
+					if (promises.length)
+						$q.all(promises).then ->
+							#log "Animation completed rid #{rid}"
+							adjustBuffer rid
+
+					keepFetching
+
+				calculateTopProperties = ->
+					topHeight = 0
+					for item in buffer
+						itemTop = item.element.offset().top
+						newRow = rowTop isnt itemTop
+						rowTop = itemTop
+						itemHeight = item.element.outerHeight(true) if newRow
+						if newRow and (builder.topDataPos() + topHeight + itemHeight < topVisiblePos())
+							topHeight += itemHeight
+						else
+							topVisible(item) if newRow
+							break
+
+				adjustBuffer = (rid) ->

 					# We need the item bindings to be processed before we can do adjustment
 					$timeout ->

-						[keepFetching, promises] = processBufferedItems()
+						processBufferedItems(rid)
+
+						if shouldLoadBottom()
+							enqueueFetch(rid, true)
+						else
+							if shouldLoadTop()
+								enqueueFetch(rid, false)
+
+						if pending.length == 0
+							calculateTopProperties()
+
+				adjustBufferAfterFetch = (rid) ->
+
+					# We need the item bindings to be processed before we can do adjustment
+					$timeout ->
+
+						keepFetching = processBufferedItems(rid)

 						if shouldLoadBottom()
 							# keepFetching = true means that at least one item app/prepended in the last batch had height > 0
-							# !adjustAfterFetch = false means that this is chained fetch (a fetch after another fetch)
-							enqueueFetch(rid, true) if !adjustAfterFetch || keepFetching
+							enqueueFetch(rid, true) if keepFetching
 						else
 							if shouldLoadTop()
 								# pending[0] = true means that previous fetch was appending. We need to force at least one prepend
 								# BTW there will always be at least 1 element in the pending array because bottom is fetched first
-								enqueueFetch(rid, false) if !adjustAfterFetch || keepFetching || pending[0]
-
-						adjustAfterFetch(rid) if adjustAfterFetch
-
-						if pending.length == 0
-							topHeight = 0
-							for item in buffer
-								itemTop = item.element.offset().top
-								newRow = rowTop isnt itemTop
-								rowTop = itemTop
-								itemHeight = item.element.outerHeight(true) if newRow
-								if newRow and (builder.topDataPos() + topHeight + itemHeight < topVisiblePos())
-									topHeight += itemHeight
-								else
-									topVisible(item) if newRow
-									break
-
-						# the promise from the timeout should be added to promises array
-						# I just could not make promises work with the jasmine tests
-						if (promises.length)
-							$q.all(promises).then ->
-								#log "Animation completed rid #{rid}"
-								adjustBuffer rid
-
-				adjustBufferAfterFetch = (rid) ->
-					adjustBuffer rid, ->
+								enqueueFetch(rid, false) if keepFetching || pending[0]
+
 						pending.shift()
 						if pending.length == 0
 							loading(false)
+							calculateTopProperties()
 						else
 							fetch(rid)


GuestCleaner: Removed JLimit, it was a part of groupbundle and not used anymore.

--- workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
+++ workers/guestcleaner/lib/guestcleaner/guestcleanerwoker.coffee
@@ -7,7 +7,7 @@
   {Relationship} = jraphical
   constructor: (@bongo, @options = {}) ->

-  whitlistedModels = ["JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JLimit", "JName"]
+  whitlistedModels = ["JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JName"]

   collectDataAndRelationships:(relationships, callback)->
     toBeDeletedData = {}


updated url regex to support absolute paths

--- hexaflip.coffee
+++ hexaflip.coffee
@@ -29,7 +29,7 @@
 cssClass = baseName.toLowerCase()
 faceNames = ['front', 'bottom', 'back', 'top', 'left', 'right']
 faceSequence = faceNames[...4]
-urlRx = /^((((https?)|(file)):)?\/\/)|(data:)|(\.\.?\/)/i
+urlRx = /^((((https?)|(file)):)?\/\/)|(data:)|((\.\.?)?\/)/i

 class window.HexaFlip


markdown display is now xss-safe

markdown display is now xss-safe

--- core/utils.coffee
+++ core/utils.coffee
@@ -110,8 +110,7 @@
           return text

     text = Encoder.htmlDecode text
-
-    marked text
+    marked Encoder.XSSEncode text

   applyLineBreaks: (text)->
     return null unless text


Workspace: Minor MR fixes for #866.

Workspace: Minor MR fixes for #866.

--- core/utils.coffee
+++ core/utils.coffee
@@ -686,13 +686,12 @@
         i += 3
     string

-  applyGradient: (kdViewInstance, color1, color2) ->
+  applyGradient: (view, color1, color2) ->
     rules = [
       "-moz-linear-gradient(100% 100% 90deg, #{color2}, #{color1})"
       "-webkit-gradient(linear, 0% 0%, 0% 100%, from(#{color1}), to(#{color2}))"
-      "-o-linear-gradient(#{color1}, #{color2})"
     ]
-    kdViewInstance.setCss "backgroundImage", rule for rule in rules
+    view.setCss "backgroundImage", rule for rule in rules

   # Return true x% of time based on argument.
   #


More work on going to function definitions

--- lib/atom-slime-editor.coffee
+++ lib/atom-slime-editor.coffee
@@ -11,10 +11,17 @@

   constructor: (@editor, @statusView, @swank) ->
     # Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
+    @editorElement = atom.views.getView(@editor)
     @subs = new CompositeDisposable
     @subs.add editor.onDidStopChanging => @stoppedEditingCallback()
     @subs.add editor.onDidChangeCursorPosition => @cursorMovedCallback()
     @subs.add editor.onDidDestroy => @editorDestroyedCallback()
+
+    # TODO - make this a context menu item... not a command that only works through
+    # command pallette in that window...
+    @subs.add atom.commands.add @editorElement, 'slime:goto-definition': =>
+      @openDefinition()
+
     # Pretend we just finished editing, so that way things get up to date
     @stoppedEditingCallback()

@@ -63,6 +70,20 @@
       sexp = text[start...end]
     return sexp: sexp, relativeCursor: index - start

+  openDefinition: ->
+    if @swank.connected
+      # TODO - make this better, to include characters like dashes via wordRegExp
+      #word = editor.getWordUnderCursor(includeNonWordCharacters: true)
+      word = @editor.getSelectedText()
+      console.log word
+
+      @swank.find_definitions(word, @pkg).then (result) =>
+        console.log result
+
+
+    else
+      atom.notifications.addWarning("Not connected to Lisp", detail:"Going to a definition requires querying the Lisp image. So connect to it first!")
+

   editorDestroyedCallback: ->
     console.log "Closed!"


team: welcome tab WIP

team: welcome tab WIP

--- client/landing/site.landing/coffee/team/tabs/teamwelcometab.coffee
+++ client/landing/site.landing/coffee/team/tabs/teamwelcometab.coffee
@@ -17,11 +17,41 @@
       cssClass : 'team'
       navItems : []

+    @correct = new KDButtonView
+      title      : 'Yes, that\'s the right address'
+      style      : 'TeamsModal-button TeamsModal-button--green'
+      callback   : ->
+        console.log 'right address!'
+
+    @decorateTeamMembers()
+
+
+  decorateTeamMembers: ->
+
+    KD.utils.fetchTeamMembers KD.config.groupName, (err, members) ->
+      console.log err, members
+
+
   pistachio: ->

     """
     {{> @header }}

-

+
Welcome

+
You are about to join to #{KD.config.group.title} on Koding

+

+

+

+

+

+

+

+        and 34 of your team members are already here.
+

+
Your invitation was sent to:

+
<email>#{KD.utils.getTeamData().invitation.email}<email>

+
Is the email address above correct?

+      {{> @correct}}
+
If that's not right please contact the team administrators to get a new invitation.

     """


Replace validation to node built-in functions

--- src/utils.coffee
+++ src/utils.coffee
@@ -1,24 +1,21 @@
-fun = (f) -> typeof f is 'function'
-str = (s) -> typeof s is 'string'
-arr = (a) -> a instanceof Array
-obj = (o) -> o instanceof Object and not fun(o) and not arr(o)
+util = require 'util'

 exports.is =
-  fun: fun
-  str: str
-  arr: arr
-  obj: obj
+  fun: util.isFunction
+  str: util.isString
+  arr: util.isArray
+  obj: util.isObject

 exports.normalize = (params, options, fn) ->
-  if fun params
+  if util.isFunction params
     fn = params
     params = null
     options = null
-  else if fun options
+  else if util.isFunction options
     fn = options
     options = null

-  if not fun fn
+  if not util.isFunction fn
     fn = ->

   {params, options, fn}


Fix hotkeys for combined open ended

Fix hotkeys for combined open ended

--- common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
+++ common/lib/xmodule/xmodule/js/src/combinedopenended/display.coffee
@@ -90,6 +90,7 @@
     @element=element
     @reinitialize(element)
     $(window).keydown @keydown_handler
+    $(window).keyup @keyup_handler

   reinitialize: (element) ->
     @wrapper=$(element).find('section.xmodule_CombinedOpenEndedModule')
@@ -104,6 +105,7 @@
     @location = @el.data('location')
     # set up handlers for click tracking
     Rubric.initialize(@location)
+    @is_ctrl = false

     @allow_reset = @el.data('allow_reset')
     @reset_button = @$('.reset-button')
@@ -362,10 +364,15 @@
     else
       @errors_area.html(@out_of_sync_message)

-  keydown_handler: (e) =>
-    # only do anything when the key pressed is the 'enter' key
-    if e.which == 13 && @child_state == 'assessing' && Rubric.check_complete()
-      @save_assessment(e)
+  keydown_handler: (event) =>
+    if event.which == 17 && @is_ctrl==false
+      @is_ctrl=true
+    else if @is_ctrl==true && event.which == 13 && @child_state == 'assessing' && Rubric.check_complete()
+      @save_assessment(event)
+
+  keyup_handler: (event) =>
+    if event.which == 17 && @is_ctrl==true
+      @is_ctrl=false

   save_assessment: (event) =>
     event.preventDefault()


fix(keymaps): create user keymaps file in .nylas/ if non existent

--- internal_packages/preferences/lib/tabs/preferences-keymaps.cjsx
+++ internal_packages/preferences/lib/tabs/preferences-keymaps.cjsx
@@ -97,6 +97,9 @@
       return keystrokes.replace(/-/gi,'').replace(/cmd/gi, '⌘').replace(/alt/gi, '⌥').replace(/shift/gi, '⇧').replace(/ctrl/gi, '^').toUpperCase()

   _onShowUserKeymaps: =>
-    require('shell').showItemInFolder(atom.keymaps.getUserKeymapPath())
+    keymapsFile = atom.keymaps.getUserKeymapPath()
+    if !fs.existsSync(keymapsFile)
+      fs.writeSync(fs.openSync(keymapsFile, 'w'), '')
+    require('shell').showItemInFolder(keymapsFile)

 module.exports = PreferencesKeymaps


allow for multiple envs to be passed to stop-env

--- lib/commands/stop_env.coffee
+++ lib/commands/stop_env.coffee
@@ -19,10 +19,12 @@
 module.exports = (args, options, done) ->
   docker = new Docker()

-  if args.length isnt 1
+  if args.length is 0
     return help args, options, done

-  env = args[0]
+  envRegExps = []
+  for env in args
+    envRegExps.push new RegExp("\.#{env}$")

   reporter = options.reporter or new ConsoleReporter(process.stderr)

@@ -36,13 +38,12 @@
       id = containerInfo.Id

       for name in containerInfo.Names
-        if name.match new RegExp("\.#{env}$")
+        for envRegExp in envRegExps when name.match(envRegExp)
           found = true

           do (id, name) ->
             promise = promise.then ->
               stopContainer name, docker.getContainer(id), reporter
-          break

     if found
       promise.then done


introducing new raw, command escaping

introducing new raw, command escaping

--- src/MKTS.coffee
+++ src/MKTS.coffee
@@ -1006,7 +1006,7 @@
     ### TAINT what to do with useful data appearing environment? ###
     ### TAINT environment becomes important for footnotes ###
     environment = {}
-    # md_source   = @_escape_command_fences md_source
+    md_source   = MKTS.XXX_escape_raw_spans md_source
     tokens      = md_parser.parse md_source, environment
     # @set_meta R, 'environment', environment
     confluence.write token for token in tokens


Fix to chain after using set, pause and reset methods

--- src/Moon.coffee
+++ src/Moon.coffee
@@ -242,6 +242,8 @@
                 for key, value of args
                     el.style[this._getPrefix(key)] = value

+            return this
+
         pause: ->
             this._paused = new Date()
             for el in this._collection
@@ -252,6 +254,8 @@
                     el.style[this._getPrefix(key)] = computedStyle.getPropertyValue(this._getCssPrefix(key))

                 el.style[this._getPrefix("transition")] = ""
+
+            return this

         # set loop
         loop: (looping) ->
@@ -269,4 +273,6 @@
                 "transition": null
             )

+            return this
+
     window.Moon = Moon


optimized background rendering

optimized background rendering

--- lib/editor-background.coffee
+++ lib/editor-background.coffee
@@ -6,6 +6,7 @@
 qr = (selector) -> document.querySelector selector
 style = (element) -> document.defaultView.getComputedStyle element
 inline = (element,style) -> element.style.cssText += style
+

 planeInitialCss =
   "position:absolute;
@@ -210,64 +211,137 @@
     @elements.textBackground = txtBg
     @elements.main.appendChild txtBg

-  drawLine: (tokenizedLine) ->
+  getOffset: (element, offset) ->
+    {left:0,top:0}
+    if element?
+      if !offset? then offset = {left:0,top:0}
+      offset.left += element.offsetLeft
+      offset.top += element.offsetTop
+      if element.offsetParent?
+        @getOffset element.offsetParent, offset
+      else
+        offset
+
+
+  drawLine: (tokenizedLine,attrs) ->
     line = document.createElement 'div'
-    line.className = 'editor-background-line line'
-    line.innerText = tokenizedLine.text
+    line.className = 'editor-background-line'
+    text = tokenizedLine.buildText().trim()
+    text = text.replace(/[\s]{1}/gi,'');
+    #text = text.replace(/[\t]{1}/gi,'');
+    line.innerHTML = text
+    marginLeft = tokenizedLine.indentLevel * tokenizedLine.tabLength * attrs.charWidth
+    line.style.cssText = "
+      margin-left:#{marginLeft}px;
+    "
     @elements.textBackground.appendChild line

   drawLines: (attrs) ->
     if attrs?
-      if attrs.editorRect? && attrs.screenLines?
+      if attrs.editorElement? && attrs.screenLines?
         @elements.textBackground.innerText = ''
-        editorSettings = atom.config.settings.editor
-        fontFamily = editorSettings.fontFamily
-        fontSize = editorSettings.fontSize
-        @elements.textBackground.style.cssText="
-        top:#{attrs.editorRect.top}px;
-        left:#{attrs.editorRect.left}px;
-        right:#{attrs.editorRect.right}px;
-        bottom:#{attrs.editorRect.bottom}px;
-        position:absolute;
-        z-index:1;
-        font-family:#{fontFamily};
-        font-size:#{fontSize}px;
-        color:gray;
-        pointer-events:none;
-        opacity:0.5;
-        "
-        @drawLine line for line in attrs.screenLines
-
-  drawBackground: (event)->
-    console.log 'event',event
-    activeEditor = atom.workspace.getActiveEditor()
+        editor = attrs.editorElement
+        if editor.constructor.name == 'atom-text-editor'
+          root = editor.shadowRoot
+          scrollView = root.querySelector '.scroll-view'
+          offset = @getOffset scrollView
+          top = offset.top - attrs.offsetTop
+          left = offset.left
+          right = left + scrollView.width
+          bottom = top + scrollView.height
+          opacity = 0.5;
+          activeEditor = attrs.activeEditor
+          displayBuffer = attrs.displayBuffer
+          lineHeight = attrs.lineHeight
+          charWidth = displayBuffer.getDefaultCharWidth()
+          tabWidth = displayBuffer.getTabLength() * charWidth
+          editorSettings = atom.config.settings.editor
+          defaultSettings = atom.config.defaultSettings
+          fontFamily = editorSettings.fontFamily
+          fontSize = editorSettings.fontSize
+          if !fontFamily? then fontFamily = defaultSettings.fontFamily
+          if !fontSize? then fontSize = defaultSettings.fontSize
+          css = @elements.textBackgroundCss
+          css.innerText="
+            .editor-background-line{
+              font-family:'#{fontFamily}';
+              font-size:#{fontSize}px;
+              height:#{lineHeight}px;
+              display:block;
+              color:transparent;
+              background:red;
+              width:auto;
+              transform:translate3d(0,0,0);
+              float:left;
+              clear:both;
+            }
+            .editor-background-white{
+              width:#{charWidth}px;
+              display:inline-block;
+            }
+            .editor-background-tab{
+              width:#{tabWidth}px;
+              display:inline-block;
+            }
+          "
+          @elements.textBackground.style.cssText="
+          top:#{top}px;
+          left:#{left}px;
+          right:#{right}px;
+          bottom:#{bottom}px;
+          position:absolute;
+          z-index:0;
+          pointer-events:none;
+          opacity:#{opacity};
+          transform:translate3d(0,0,0);
+          "
+          attrsForward = {
+            charWidth:charWidth
+          }
+          for line in attrs.screenLines
+            @drawLine line,attrsForward
+
+
+  activeEditor:{}
+
+  drawBackground: (event,editor)->
+    if event?.active?
+      @activeEditor=editor
+      process.nextTick =>@drawBackground.apply @,[]
+      return
+    activeEditor = @activeEditor
     displayBuffer = activeEditor.displayBuffer
     actualLines = displayBuffer.getVisibleRowRange()
     screenLines = displayBuffer.buildScreenLines actualLines[0],actualLines[1]
-    activePane = atom.workspaceView.getActivePane()[0]
-    editorElement = activePane.querySelector 'atom-text-editor'
-    console.log displayBuffer
+    scrollTop = displayBuffer.getScrollTop()
+    lineHeight = displayBuffer.getLineHeightInPixels()
+    offsetTop = scrollTop - Math.floor(scrollTop / lineHeight) * lineHeight
+    editorElement = atom.views.getView(activeEditor)
     if editorElement?
-      editorRect = editorElement.getBoundingClientRect()
-      attrs =
-        {
-          editorElement:editorElement,
-          editorRect:editorRect,
-          screenLines:screenLines.screenLines
-        }
-      @drawLines attrs
-
+      if editorElement.constructor.name == 'atom-text-editor'
+        editorRect = editorElement.getBoundingClientRect()
+        attrs =
+          {
+            editorElement:editorElement
+            activeEditor:activeEditor
+            lineHeight:lineHeight
+            displayBuffer:displayBuffer
+            screenLines:screenLines.screenLines
+            offsetTop:offsetTop
+            scrollTop:scrollTop
+            visibleBuffer: actualLines
+          }
+        @drawLines attrs

   watchEditor:(editor)->
-    console.log 'editor :)',editor
-    editor.onDidChangeScrollTop (scroll)=>@drawBackground.apply @,[{scrollTop:scroll}]
-    editor.onDidChangeScrollLeft (scroll)=>@drawBackground.apply @,[{scrolLeft:scroll}]
-    editor.onDidChange (change)=>@drawBackground.apply @,[{change:change}]
+    editor.onDidChangeScrollTop (scroll)=>@drawBackground.apply @,[{scrollTop:scroll},editor]
+    editor.onDidChangeScrollLeft (scroll)=>@drawBackground.apply @,[{scrolLeft:scroll},editor]
+    editor.onDidChange (change)=>@drawBackground.apply @,[{change:change},editor]

   watchEditors: ->
     atom.workspace.observeTextEditors (editor)=>@watchEditor.apply @,[editor]
-
+    atom.workspace.observeActivePaneItem (editor)=>@drawBackground.apply @,[{active:editor},editor]

   initialize: ->
     @elements.body = qr 'body'
@@ -286,9 +360,7 @@
     if loaded.length == keys.length

       @insertMain()
-      @insertTextBackground()
       @activateMouseMove()
-      @watchEditors()

       conf=atom.config.get('editor-background')

@@ -308,6 +380,9 @@
       @elements.bg = document.createElement 'div'
       @elements.bg.style.cssText="position:absolute;width:100%;height:100%;"
       @elements.main.appendChild @elements.bg
+
+      @insertTextBackground()
+      @watchEditors()

       @colors.workspaceBgColor=style(@elements.editor).backgroundColor
       @colors.treeOriginalRGB=style(@elements.treeView).backgroundColor


youtube to gif converter

youtube to gif converter

--- lib/editor-background.coffee
+++ lib/editor-background.coffee
@@ -2,8 +2,7 @@
 fs = require 'fs'
 blur = require './StackBlur.js'
 youtubedl = require 'youtube-dl'
-gif_ = require './gif.js'
-GIF=gif_.GIF
+GIFEncoder = require 'gifencoder'

 qr = (selector) -> document.querySelector selector
 style = (element) -> document.defaultView.getComputedStyle element
@@ -54,7 +53,7 @@
       type:'string'
       default:''
       order:1
-      description:"paste youtube render video loop url here to have animation"
+      description:"WARNING!!! NOT FINISHED YET - WORK IN PROGRESS"
     textBackground:
       type:"color"
       default:"rgb(0,0,0)"
@@ -250,71 +249,118 @@
         ytid=ytidregres[1]

   timer:{}
-  gif:{}
-
-  capture: ->
-    video = @elements.video
-    console.log "capturing"
-    @gif.addFrame video, {copy: true, delay: 100}
-
-  decodeVideo:->
+  frames:[]
+  videoWidth:0
+  videoHeight:0
+  playing:true
+
+  getFrame:(canvas,ctx,video,w,h)->
+    console.log 'getting frame'
+    ctx.drawImage video,0,0
+    video.pause()
+    @elements.encoder.addFrame ctx
+    video.play()
+    if @playing
+      setTimeout =>
+        @getFrame canvas,ctx,video,w,h
+      , 50
+    #@frames.push canvas.toDataURL 'image/png'
+
+
+
+  getImages: ->
+    @playing=true
+    console.log 'getting images...'
     video = @elements.video
     canvas = @elements.videoCanvas
-
-    @gif = new GIF
-      workers: 4
-      workerScript: @elements.libPath+'gif.worker.js'
-      width: 640
-      height: 480
-      quality: 1
-
-    @gif.on 'progress', (p) ->
-      console.log "rendering: #{ Math.round(p * 100) }%"
-
-    @gif.on 'finished', (blob) =>
-      dataURL = URL.createObjectURL(blob)
-      console.log 'gif finished',dataURL,blob
-      blobToBase64 blob, (base64)=>
-        videoPath = @elements.videoPath
-        ytid = @getYTId atom.config.get('editor-background.youTubeURL')
-        try
-          fs.writeFileSync videoPath+ytid+'.gif',base64,'base64'
-        catch error
-          console.log error
-
-    video.addEventListener 'play', =>
-      console.log 'playing'
-      clearInterval @timer
-      @timer =
-      setInterval =>
-        @capture.apply @,[]
-      , 100
-
-    video.addEventListener 'ended', =>
-      clearInterval @timer
-      @gif.render()
-
-    @gif.abort()
-    @gif.frames = []
-    video.play()
+    context = canvas.getContext("2d")
+    @elements.encoder.start()
+    w = @videoWidth
+    h = @videoHeight
+    @getFrame canvas,context,video,w,h
+    ###
+    @timer = setInterval =>
+      @getFrame.apply @,[canvas,context,video,w,h]
+    , 100
+    ###
+
+
+  getImagesDone:->
+    @playing=false
+    #clearInterval @timer
+    @elements.encoder.finish()
+    console.log 'done',@frames.length
+    ytid = @elements.ytid
+    ###
+    imagesFolder = @elements.videoPath+ytid+'_images/'
+    try
+      fs.mkdirSync imagesFolder,0o777
+    catch error
+      console.log error
+    i = 0
+    for frame in @frames
+      base64 = frame.replace(/^data:image\/png;base64,/, "")
+      try
+        fs.writeFileSync imagesFolder+i+'.png',base64,'base64'
+      catch
+        console.log error
+      i++
+
+    ###
+
+    gifPath = @elements.videoPath+ytid+'.gif'
+    buf = @elements.encoder.out.getData()
+    fs.writeFile gifPath, buf, (err) =>
+      @elements.videoCanvas.remove()
+      @elements.video.remove()
+      delete @elements.encoder
+      @elements.image.src = gifPath
+      atom.config.set('editor-background.blurRadius',0)
+      atom.config.set('editor-background.imageURL',gifPath)
+
+  playVideo:->
+    console.log 'playing video...'
+    @getImages()
+
+  decodeVideo:->
+    console.log 'decoding video',@elements.video
+    @frames = []
+    video = @elements.video
+    ###
+    video.addEventListener 'play',=>
+      @getImages()
+    ###
+    video.addEventListener 'ended',=>
+      @getImagesDone()
+    video.addEventListener 'canplay',=>
+      @playVideo()
+

   insertVideo: (savePath) ->
     data = fs.readFileSync savePath
-    ###
+
     videoCanvas = document.createElement 'canvas'
-    videoCanvas.widht = "100%"
-    videoCanvas.height = "100%"
+    videoWidth = @videoWidth
+    videoHeight = @videoHeight
+    encoder = new GIFEncoder @videoWidth,@videoHeight
+    encoder.setQuality 5
+    encoder.setDelay 100
+    encoder.setRepeat 0
+    @elements.encoder = encoder
+    videoCanvas.width = videoWidth
+    videoCanvas.height = videoHeight
     videoCanvas.id = "editor-background-videoCanvas"
     videoCanvas.style.cssText = "
     position:absolute;
     top:0px;
     left:0px;
-    width:100%;
-    height:100%;
+    display:none;
+    width:#{videoWidth}px;
+    height:#{videoHeight}px;
     "
     @elements.videoCanvas = videoCanvas
     @elements.main.insertBefore videoCanvas,@elements.textBackground
-    ###
+
     video = document.createElement 'video'
     source = document.createElement 'source'
     @elements.video =  video
@@ -322,8 +368,6 @@
     video.appendChild source
     source.type="video/"+@elements.videoFormat
     source.src = savePath
-    video.width="100%"
-    video.height="100%"
     video.style.cssText="
     position:absolute;
     left:0;
@@ -339,6 +383,7 @@
     videoFormat = @elements.videoFormat
     if url != ''
       ytid = @getYTId url
+      @elements.ytid = ytid
       savePath = @elements.videoPath+ytid+videoExt
       alreadyExists = false

@@ -351,9 +396,11 @@
       if not alreadyExists
         video = youtubedl url,['--format='+videoFormat],{ cwd: __dirname }
         size = 0
-        video.on 'info', (info)->
+        video.on 'info', (info)=>
           console.log 'Download started'
           console.log info
+          @videoWidth = info.width
+          @videoHeight = info.height
           size = info.size
         pos=0
         video.on 'data', (data) =>
@@ -363,8 +410,6 @@
             console.log 'downloaded'
             @insertVideo.apply @,[savePath]
         video.pipe fs.createWriteStream(savePath)
-      else
-        @insertVideo savePath
     else
       @removeVideo()


I'm surrounded by idiots

I'm surrounded by idiots

--- scripts/storage.coffee
+++ scripts/storage.coffee
@@ -1,25 +0,0 @@
-# Description:
-#   Inspect the data in redis easily
-#
-# Commands:
-#   hubot show users - Display all users that hubot knows about
-#   hubot show storage - Display the contents that are persisted in the brain
-
-
-Util = require "util"
-
-module.exports = (robot) ->
-  robot.respond /show storage$/i, (msg) ->
-    output = Util.inspect(robot.brain.data, false, 4)
-    msg.send output
-
-  robot.respond /show users$/i, (msg) ->
-    response = ""
-
-    for own key, user of robot.brain.data.users
-      response += "#{user.id} #{user.name}"
-      response += " <#{user.email_address}>" if user.email_address
-      response += "\n"
-
-    msg.send response
-


use alert icon

use alert icon

--- lib/notifications-log.coffee
+++ lib/notifications-log.coffee
@@ -83,4 +83,4 @@
   onDidDestroy: (callback) ->
     @emitter.on 'did-destroy', callback

-  getIconName: -> 'comment'
+  getIconName: -> 'alert'


remove mention of Github-flavored-markdown since that feature was removed in e53a2be

--- src/docco.coffee
+++ src/docco.coffee
@@ -93,8 +93,8 @@
   sections

 # Highlights a single chunk of CoffeeScript code, using **Pygments** over stdio,
-# and runs the text of its corresponding comment through **Markdown**, using the
-# **Github-flavored-Markdown** modification of [Showdown.js](http://attacklab.net/showdown/).
+# and runs the text of its corresponding comment through **Markdown**, using
+# [Showdown.js](http://attacklab.net/showdown/).
 #
 # We process the entire file in a single call to Pygments by inserting little
 # marker comments between each section and then splitting the result string


Optimize bracket matching

Optimize bracket matching

--- src/app/language-mode.coffee
+++ src/app/language-mode.coffee
@@ -22,14 +22,16 @@

       cursorBufferPosition = @editSession.getCursorBufferPosition()
       nextCharachter = @editSession.getTextInBufferRange([cursorBufferPosition, cursorBufferPosition.add([0,1])])
-      bracketAnchorRange = @bracketAnchorRanges.filter((anchorRange) -> anchorRange.getBufferRange().end.isEqual(cursorBufferPosition))[0]

-      skipOverExistingClosingBracket = @isClosingBracket(text) and nextCharachter == text and bracketAnchorRange
       autoCompleteOpeningBracket = @isOpeningBracket(text) and /\W|^$/.test(nextCharachter)
+      skipOverExistingClosingBracket = false
+      if @isClosingBracket(text) and nextCharachter == text
+        if bracketAnchorRange = @bracketAnchorRanges.filter((anchorRange) -> anchorRange.getBufferRange().end.isEqual(cursorBufferPosition))[0]
+          skipOverExistingClosingBracket = true

       if skipOverExistingClosingBracket
         bracketAnchorRange.destroy()
-        @bracketAnchorRanges = _.without(@bracketAnchorRanges, bracketAnchorRange)
+        _.remove(@bracketAnchorRanges, bracketAnchorRange)
         @editSession.moveCursorRight()
         false
       else if autoCompleteOpeningBracket


change getBounds

change getBounds

--- JSKit/03_JSScrollView.coffee
+++ JSKit/03_JSScrollView.coffee
@@ -6,6 +6,7 @@
 class JSScrollView extends JSView
     constructor:(frame)->
         super(frame)
+        @_frame.size.width -= 24
         @_scroll = false

     destructor:->


Correct the jQuery version of ElementWrapper.children()

--- tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
+++ tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
@@ -507,7 +507,7 @@
 #if jquery
       children = @$.children()

-      for i in [0...matches.length]
+      for i in [0...children.length]
         new ElementWrapper children.eq i

 #elseif prototype


Corrige nome do método no percentualGraph

--- app/assets/javascripts/angular_app/directives/percentual_graph.js.coffee
+++ app/assets/javascripts/angular_app/directives/percentual_graph.js.coffee
@@ -1,5 +1,5 @@
 angular.module('InescApp').directive 'percentualGraph', ->
-  buildGraph = (entity, totals) ->
+  calculateYears = (entity, totals) ->
     years = entity.yearly.map (year) ->
       totalsYear = $.grep(totals.years, (element) -> element.year == year.time.year)[0]
       total = totalsYear.autorizado if totalsYear
@@ -56,7 +56,7 @@
     scope.$watch 'entity.yearly + totals + year', ->
       [entity, totals, year] = [scope.entity, scope.totals, scope.year]
       if entity? and entity.yearly? and totals? and year?
-        scope.years = buildGraph(entity, totals)
+        scope.years = calculateYears(entity, totals)
         scope.currentYear = $.grep(scope.years, (thisYear) ->
           parseInt(thisYear.label) == parseInt(year)
         )[0]


`deepBindAll` in underscore.outcasts, without tests

--- app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee
+++ app/assets/javascripts/ultimate/underscore/underscore.outcasts.js.coffee
@@ -4,7 +4,7 @@
   Underscore.outcasts is freely distributable under the terms of the MIT license.
   Documentation: https://github.com/KODerFunk/underscore.outcasts
   Some code is borrowed from outcasts pull requests to Underscore.
-  Version '0.1.3'
+  Version '0.1.4'
 ###

 'use strict'
@@ -15,7 +15,7 @@

 UnderscoreOutcasts =

-  VERSION: '0.1.3'
+  VERSION: '0.1.4'

   delete: (object, key) ->
     value = object[key]
@@ -72,6 +72,16 @@
       object.toArray() or [object]
     else
       [object]
+
+  # http://coderwall.com/p/krcdig
+  deepBindAll: (obj) ->
+    target = _.last(arguments)
+    for key, value of obj
+      if _.isFunction(value)
+        obj[key] = _.bind(value, target)
+      else if _.isObject(value)
+        obj[key] = _.deepBindAll(value, target)
+    obj

   ###
    Split array into slices of <number> elements.


fixed reappending menu items and children problem

--- client/app/MainApp/maincontroller/mainviews/appsettingsmenubutton.coffee
+++ client/app/MainApp/maincontroller/mainviews/appsettingsmenubutton.coffee
@@ -22,10 +22,15 @@

       menu.items.forEach (item, index) =>

-        item.children or= []
+        # if item has parent then add "children" to the parents.
+        if item.parentId
+          parents = _.filter menu.items, (menuItem)-> menuItem.id is item.parentId
+          parents.forEach (parentItem) -> parentItem.children or= []
+
         item.callback = (contextmenu) =>
           view = getVisibleView()
           view?.emit "#{item.eventName}MenuItemClicked", item.eventName, item, contextmenu, @offset
+          return unless item.eventName
           @contextMenu.destroy()

         if (item.title?.indexOf "customView") is 0
@@ -33,7 +38,10 @@
           if customView instanceof KDView
             item.view = customView
           else
-            menu.items = menu.items.concat JContextMenuTreeViewController.convertToArray customView, item.parentId
+            childItems = JContextMenuTreeViewController.convertToArray customView, item.parentId
+            # escaping the items appended before
+            menuWithoutChilds = _.filter menu.items, (menuItem) -> menuItem.parentId isnt item.parentId
+            menu.items = menuWithoutChilds.concat childItems

       @createMenu event, menu.items


#15 scope.mgEnd = null 추가

#15 scope.mgEnd = null 추가

--- src/mg-datepicker.directive.coffee
+++ src/mg-datepicker.directive.coffee
@@ -553,6 +553,7 @@
               scope.mgSelect()
             ), 300
           else # general case
+            scope.mgEnd = null            # 호텔 달력 UX 변경. 체크인 선택후 체크아웃은 null로.
             startLimit = new Date scope.mgEnd
             startLimit.setDate startLimit.getDate() - scope.mgOptions.limitNights
             if !startSelected or (startSelected and date <= scope.mgStart)


category sizing

category sizing

--- @client/edit_proposal.coffee
+++ @client/edit_proposal.coffee
@@ -146,7 +146,8 @@
             id: "category"
             name: "category"
             style:
-              fontSize: 18
+              fontSize: 16
+              maxWidth: '100%'
             defaultValue: category
             onChange: (e) =>
               @local.category = e.target.value


Set the watch:md to only compile markdown

--- .metalsmith/gulpfile.coffee
+++ .metalsmith/gulpfile.coffee
@@ -97,8 +97,8 @@
 #
 # Run a web server exposing the files in the build directory, and recompile
 # the markdown on change.
-gulp.task 'watch:md', ['build'], ->
-  gulp.watch paths.documents, ['build']
+gulp.task 'watch:md', ['metalsmith'], ->
+  gulp.watch paths.documents, ['metalsmith']

 # ## watch:sass


should read @model not @record because its context is Collection not Singleston

--- src/ajax.coffee
+++ src/ajax.coffee
@@ -76,7 +76,7 @@
     error = params.error

     (jqXHR, statusText, error) =>
-      @record.trigger("ajaxError", @record, jqXHR, statusText, error)
+      @model.trigger("ajaxError", null ,jqXHR, statusText, error)
       error?(@model)


Fetch them one by one

Fetch them one by one

--- client/landing/app/CommonViews/comments/commentlistviewcontroller.coffee
+++ client/landing/app/CommonViews/comments/commentlistviewcontroller.coffee
@@ -49,10 +49,10 @@
         listView.emit "BackgroundActivityStarted"
         callback()

-      for xto in [0..repliesCount] by 10
+      for xto in [repliesCount..0]
         do (xto) =>
           stack.push (callback) =>
-            @fetchCommentsByRange xto, xto+10, (err, comments) =>
+            @fetchCommentsByRange xto, xto+1, (err, comments) =>
               if not removedAlready
                 @removeAllItems()
                 removedAlready = yes


else block

else block

--- chuck.coffee
+++ chuck.coffee
@@ -45,13 +45,14 @@
     when "throw"
       cc = calculateExpressionComplexity statement.throws
     when "declaration"
-      cc = if statement.initializer then 1 + calculateExpressionComplexity statement.initializer else 0
+      cc = if statement.initializer? then 1 + calculateExpressionComplexity statement.initializer else 0
     when "assignment", "prefix", "postfix", "methodCall"
       cc = calculateExpressionComplexity statement.expression
     when "if"
-      cc = if statement.elseBlock then 2 else 1
+      cc = if statement.elseBlock? then 2 else 1
       cc += calculateExpressionComplexity statement.condition
       cc += calculateStatementComplexity statement.block
+      cc += calculateStatementComplexity statement.elseBlock if statement.elseBlock?
     when "while", "do_while"
       cc = 1
       cc += calculateExpressionComplexity statement.condition


prompt space

prompt space

--- lib/Repl/ReplClass.coffee
+++ lib/Repl/ReplClass.coffee
@@ -8,7 +8,7 @@
       #console.log('2')
       if(@cmdQueue.length > 0)
         cmd = @cmdQueue.shift()
-        @print += @prompt+cmd
+        @print += @prompt+' '+cmd
         @replProcess.stdin.write(cmd)

     processOutputData:(data) ->
@@ -37,8 +37,8 @@
       @replProcess.stdout.on('data', (data)->self.processOutputData(data))
       @replProcess.stderr.on('data', (data)->self.processErrorData(data))
       @replProcess.on('close', ()->self.closeRepl())
-      console.log(@print)
+      #console.log(@print)

-myrepl = new Repl('ocaml',['-noprompt'],"# ")
+myrepl = new Repl('ocaml', ['-noprompt'], "#")
 myrepl.writeInRepl("let _ = 2*2;;\n")
 myrepl.writeInRepl("let _ = 3*2;;\n")


Node response bug in setting queries for OPTIONS fixed

--- impl/node/response.coffee
+++ impl/node/response.coffee
@@ -95,7 +95,7 @@
 				if isFinite(len)
 					res.setHeader 'Content-Length', len

-				res.end data
+				res.end data+''

 			(req, res, data, callback) ->
 				acceptEncodingHeader = req?.headers['Accept-Encoding']
@@ -150,7 +150,7 @@
 		# write headers
 		setHeaders serverRes, HEADERS, obj
 		if headers = METHOD_HEADERS[serverReq.method]
-			setHeaders serverRes, headers
+			setHeaders serverRes, headers, obj

 		# set status
 		serverRes.statusCode = res.status


Use proper path for upload.

Use proper path for upload.

--- src/blueprints/deploy.coffee
+++ src/blueprints/deploy.coffee
@@ -7,7 +7,7 @@
   "#{endpoint x}/#{x.id ? x.deployId}"

 upload = (x) ->
-  "#{byId x}/#{x.path}"
+  "#{byId x}/files/#{x.path}"

 module.exports = (blueprints) ->
   blueprints.deploy =


Move body test coverage

Move body test coverage

--- src/base.coffee
+++ src/base.coffee
@@ -2,9 +2,10 @@
 global.$game.common = {} if not global.$game.common

 global.$game.common.move = (what, to)->
+  _ = require "underscore"
   what.location = global.$game.$nowhere if not what.location
   if what.location?.contents?
-    what.location.contents = what.location.contents.remove(what)
+    what.location.contents = _(what.location.contents).without(what)
   to.contents = [] if not to.contents
   to.contents.push what
   what.location = to


check "superclass" for coco/ls too

check "superclass" for coco/ls too

--- src/chaplin/lib/utils.coffee
+++ src/chaplin/lib/utils.coffee
@@ -50,7 +50,8 @@
   # Get the whole chain of object prototypes.
   getPrototypeChain: (object) ->
     chain = [object.constructor.prototype]
-    chain.push object while object = object.constructor?.__super__
+    while object = object.constructor?.__super__ ? object.constructor?.superclass
+      chain.push object
     chain

   # Get all property versions from object’s prototype chain.


Don't import twice files with big cozy

Don't import twice files with big cozy

--- backend/remote/watcher.coffee
+++ backend/remote/watcher.coffee
@@ -10,6 +10,7 @@
 # TODO add comments
 class RemoteWatcher
     constructor: (@couch, @merge, @pouch) ->
+        @pending = 0

     # First time replication
     #
@@ -55,8 +56,8 @@
                 callback err

     # Listen to the Couchdb changes feed for files and folders updates
+    # TODO add integration tests
     # TODO use a view instead of a filter
-    # TODO add integration tests
     listenToChanges: (options, callback) =>
         @pouch.getRemoteSeq (err, seq) =>
             return callback err if err
@@ -77,7 +78,14 @@
                     callback err
                 .on 'complete', =>
                     @changes = null
-                    callback()
+                    @whenReady callback
+
+    # TODO comments, tests
+    whenReady: (callback) =>
+        if @pending is 0
+            callback()
+        else
+            setTimeout (-> @whenReady callback), 100

     # Take one change from the changes feed and give it to merge
     #
@@ -105,6 +113,7 @@
     # In PouchDB, the filepath is in the _id.
     # And the _id/_rev from CouchDB are saved in the remote field in PouchDB.
     putDoc: (doc, was, callback) =>
+        # TODO start from {} and add wanted properties instead of deleting somes
         doc = clone doc
         doc.remote =
             _id: doc._id
@@ -121,6 +130,7 @@
         delete doc.name
         delete doc.binary
         delete doc.clearance
+        delete doc.localPath
         if @merge.invalidId doc
             log.error "Invalid id"
             log.error doc
@@ -135,8 +145,11 @@
                 @merge.putDoc doc, callback

     # Keep track of the sequence number and log errors
+    # TODO test pending counts
     changed: (change) =>
+        @pending++
         (err) =>
+            @pending--
             if err
                 log.error err
                 log.debug change


minnor code refactoring;

minnor code refactoring;

--- app/assets/javascripts/chr/core/item.coffee
+++ app/assets/javascripts/chr/core/item.coffee
@@ -2,8 +2,14 @@
 # ITEM
 # -----------------------------------------------------------------------------
 class @Item
+  constructor: (@module, @path, @object, @config) ->
+    @$el =$ """"""
+    @$el.on 'click', (e) => @onClick(e)
+    @render()
+
+
   _is_folder: ->
-    # TODO: update this logic as it's not reliable
+    # update this logic as it's not reliable
     if @object._title then true else false

@@ -31,7 +37,8 @@
   _render_thumbnail: ->
     if @config.itemThumbnail
       imageUrl = @config.itemThumbnail(@object)
-      if imageUrl != '' and not imageUrl.endsWith('_old_') # NOTE: carrierwave fix, check if still required
+      # carrierwave fix, check if still required
+      if imageUrl != '' and not imageUrl.endsWith('_old_')
         @$thumbnail =$ "

"
         @$el.append(@$thumbnail)
         @$el.addClass 'has-thumbnail'
@@ -51,12 +58,6 @@
       if @config.arrayStore and @config.arrayStore.reorderable
         @$el.addClass('reorderable')
         @$el.append $("

")
-
-
-  constructor: (@module, @path, @object, @config) ->
-    @$el =$ """"""
-    @$el.on 'click', (e) => @onClick(e)
-    @render()

   onClick: (e) ->
@@ -90,4 +91,3 @@

-


Updated User voice end point

Updated User voice end point

--- app/assets/javascripts/user_voice.js.coffee
+++ app/assets/javascripts/user_voice.js.coffee
@@ -24,6 +24,6 @@
   UserVoice.push ['autoprompt', {}]

 $ ->
-  $.getScript "//widget.uservoice.com/Tzs6afFRyv54dMJ18t1zg.js", showuservoice
+  $.getScript "//widget.uservoice.com/MiPFKEx8KFMEkeJZzwWQ.js", showuservoice

 $(document).on 'page:change', showuservoice


add cv-hex-color component

add cv-hex-color component

--- src/index.coffee
+++ src/index.coffee
@@ -6,6 +6,7 @@

 app = new Vue
   data: ->
+    color: '#ff0000'
     count: 3
     title: 'color-vue!'
   # TODO: props:
@@ -21,7 +22,8 @@

       {{message}}
-      <hoge></hoge>
+      <cv-hex-color color="{{color}}">
+      </cv-hex-color>

   '''
   replace: true # default: true
@@ -42,10 +44,15 @@
   destroyed: ->
     console.log 'on destroyed'
   components:
-    hoge:
-      data: ->
-        message: 'hoge'
-      template: '

'
+    'cv-hex-color':
+      props: ['color']
+      template: '''
+

+

+      '''
   inherit: false # default: false
   events:
     'hook:created': ->


Update index.coffee

Update index.coffee

--- src/index.coffee
+++ src/index.coffee
@@ -81,7 +81,7 @@

     new Promise (fulfill, reject) =>
       query = ViewQuery.from(designDocumentName, viewName).key(key).stale(ViewQuery.Update.BEFORE)
-      if options query = ViewQuery.from(designDocumentName, viewName).key(key).stale(ViewQuery.Update.BEFORE).options
+      typeof options !== typeof undefined ? query = ViewQuery.from(designDocumentName, viewName).key(key).stale(ViewQuery.Update.BEFORE).options
       @bucket.query query, (error, results) ->
         if error?
           reject error


fix(request): set `Content-Type` header, otherwise the api doesn't parse request body

--- src/index.coffee
+++ src/index.coffee
@@ -20,6 +20,7 @@

     serializedParams = ( "#{encodeURIComponent(key)}=#{encodeURIComponent(val)}" for own key, val of params ).join "&"
     xhr.open "POST", url
+    xhr.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
     xhr.send serializedParams


Added Options Support  As needed in steelbrain/atom-hack#13

--- src/index.coffee
+++ src/index.coffee
@@ -31,12 +31,14 @@
         me.conn.connect me.config
       catch error
         reject error
-  exec:(command)->
+  exec:(command,opts)->
+    if typeof opts is 'undefined' then opts = {}
     me = this
     return new Promise (resolve,reject)->
       if me.status is 0
         reject "Not yet connected to server"
         return
+      if typeof opts.cwd isnt 'undefined' then command = "cd #{opts.cwd}; #{command}"
       me.conn.exec command,(error,stream)->
         if error
           reject error


Fix typo in method call + make work for server rendering

--- src/index.coffee
+++ src/index.coffee
@@ -64,15 +64,16 @@
       newFontSize = ms(scaler, options.modularScales[0]) * baseFont + "px"
       vr.adjustFontSizeTo(newFontSize)
     injectStyles: ->
-      # Replace existing
-      if document.getElementByID('typography.js')?
-        styleNode = document.getElementById('typography.js')
-        styleNode.innerHTML = createStylesString(options)
-      else
-        node = document.createElement('style')
-        node.id = "typography.js"
-        node.innerHTML = createStylesString()
-        document.head.appendChild(node)
+      if document?
+        # Replace existing
+        if document.getElementById('typography.js')?
+          styleNode = document.getElementById('typography.js')
+          styleNode.innerHTML = createStylesString(options)
+        else
+          node = document?.createElement('style')
+          node.id = "typography.js"
+          node.innerHTML = createStylesString()
+          document.head.appendChild(node)
   }

 #console.log test({


fixed example

fixed example

--- src/index.coffee
+++ src/index.coffee
@@ -18,9 +18,11 @@
 app.configure 'production', 'development', 'testing', ->
 	config.setEnvironment app.settings.env

-db_config = "mongodb://#{config.DB_USER}:#{config.DB_PASS}@#{config.DB_HOST}:#{config.DB_PORT}/#{config.DB_NAME}"
+# db_config = "mongodb://#{config.DB_USER}:#{config.DB_PASS}@#{config.DB_HOST}:#{config.DB_PORT}/#{config.DB_NAME}"
+# mongoose.connect db_config

-mongoose.connect db_config
+mongoose.connect 'mongodb://localhost/example'
+

 #### View initialization
 # Add Connect Assets.


Fixed mixin deprecation warning

Fixed mixin deprecation warning

--- src/mixin.coffee
+++ src/mixin.coffee
@@ -1,5 +1,5 @@

-React = require('react/addons')
+React = require('react')

 merge = clone = require('xtend')


ordering errors

ordering errors

--- src/jscov.coffee
+++ src/jscov.coffee
@@ -96,7 +96,7 @@
     return

   if errors.length > 0
-    failures = errors.map((x) -> x.file + ": " + (x.ex.message || 'UNKNOWN')).join('\n')
+    failures = _.sortBy(errors, (x) -> x.file).map((x) -> x.file + ": " + (x.ex.message || 'UNKNOWN')).join('\n')
     callback(new Error(failures))
   else
     callback()


Remove debug output

Remove debug output

--- src/echo.coffee
+++ src/echo.coffee
@@ -119,19 +119,18 @@
             stream.transport.decrypt(data)

           dtls.on 'encrypted', (data) =>
-            console.log 'ENC', data.length
             stream.nice.send(1, data)

           dtls.on 'decrypted', (data) =>
-            console.log 'DEC', data.length
             stream.transport.encrypt(data)

+          dtls.on 'connected', () =>
+            clearInterval stream.timer
+
           nice_stream.on 'stateChanged', (component, state) ->
-            console.log 'STATE', state
             if component == 1 and state == 'ready'
-              console.log 'CON'
               tick = () => stream.transport.tick()
-              @dtls_timer = setInterval tick, 100
+              stream.timer = setInterval tick, 100

           stream.transport = dtls


Reuse existing code.

Reuse existing code.

--- src/game.coffee
+++ src/game.coffee
@@ -216,24 +216,22 @@
 class Flag extends Case
   constructor: () ->
     super
-    size = @size
-
-    rect = @ctx.rect 0, 0, size, size
-    rect.attr fill: '#ddd', stroke: 'gray', strokeWidth: 1
-
-    polygon = @ctx.polygon([size/3, size/2,
+    @g.append Flag.render @ctx, @size
+
+  @render: (ctx, size) ->
+    polygon = ctx.polygon([size/3, size/2,
                             (size/3)*2, size/3,
                             (size/3)*2, (size/3)*2])
     polygon.attr fill: 'red'

-    lines = @ctx.polyline([(size/3)*2, size/3,
+    lines = ctx.polyline([(size/3)*2, size/3,
                            (size/3)*2, size-(size/4),
                            size/2, size-(size/4),
                            size-(size/5), size-(size/4)])
     lines.attr stroke: 'black', strokeWidth: size/20, fill: 'transparent'

-    group = @ctx.g(rect, polygon, lines)
-    @g.append group
+    group = ctx.g polygon, lines
+    return group

 class Mine extends Case
@@ -257,21 +255,7 @@
 class FlaggedMine extends Mine
   constructor: () ->
     super
-    size = @size
-
-    polygon = @ctx.polygon([size/3, size/2,
-                            (size/3)*2, size/3,
-                            (size/3)*2, (size/3)*2])
-    polygon.attr fill: 'red'
-
-    lines = @ctx.polyline([(size/3)*2, size/3,
-                           (size/3)*2, size-(size/4),
-                           size/2, size-(size/4),
-                           size-(size/5), size-(size/4)])
-    lines.attr stroke: 'black', strokeWidth: size/20, fill: 'transparent'
-
-    group = @ctx.g(polygon, lines)
-    @g.append group
+    @g.append Flag.render @ctx, @size

 class BadFlag extends Flag


Fixed wrong imports

Fixed wrong imports

--- src/game.coffee
+++ src/game.coffee
@@ -1,7 +1,5 @@
 #<< tictactoe/ai
 #<< tictactoe/board
-#<< tictactoe/player
-#<< tictactoe/ai
 #<< tictactoe/event
 #<< tictactoe/mark
 #<< tictactoe/player


Fix Projectile#update bug

Fix Projectile#update bug

--- src/game.coffee
+++ src/game.coffee
@@ -90,7 +90,8 @@

   constructor: (@x, @y, @width, @height, @speed, @game, @strength)->
     @game.addProjectile @
-update: ->
+
+  update: ->
     if @y > @game.height || @y < 0 || @x > @game.width || @x < 0
       @game.removeProjectile @

@@ -102,6 +103,7 @@
     super @x, @y, @radius, @radius, @speed, @game, 1

   update: ->
+    super
     @move @x, @y-@speed

   render: ->


added State#redraw method

added State#redraw method

--- lib/assets/javascripts/stark/state.js.coffee
+++ lib/assets/javascripts/stark/state.js.coffee
@@ -184,6 +184,16 @@
     @activate()
     @app.current_state = @
     @app.trigger("state:activate", @)
+
+
+  # Redraw the current state
+  redraw: ->
+    @dispose(@)
+    @render()
+
+  # Redraw the current state
+  refresh: ->
+    @redraw()

   # This is called by Stark when this state becomes active (transitioning TO),
   # after all data has been loaded and views have been rendered.


Rework docs for up.observe

Rework docs for up.observe

--- lib/assets/javascripts/unpoly/form.js.coffee
+++ lib/assets/javascripts/unpoly/form.js.coffee
@@ -165,28 +165,40 @@

   \#\#\# Example

-  The following would submit the form whenever the
-  text field value changes:
-
-      up.observe('input[name=query]', function(value, $input) {
+  The following would print to the console whenever an input field changes:
+
+      up.observe('input.query', function(value, $input) {
         console.log('Query is now ' + value);
       });

-  Instead of a single form field, you can also
-  pass, a `<form>` or any container that contains form fields.
+  Instead of a single form field, you can also pass multiple fields,
+  a `<form>` or any container that contains form fields.
   The callback will be run if any of the given fields change.

   \#\#\# Preventing concurrency

-  Firing asynchronous code after a form field can cause
+  Making network requests whenever a form field changes can cause
   [concurrency issues](https://makandracards.com/makandra/961-concurrency-issues-with-find-as-you-type-boxes).
+  Since `up.observe` can trigger many requests in a short period of time,
+  the responses might not arrive in the same order.

   To mitigate this, `up.observe` will try to never run a callback
   before the previous callback has completed.
-  To take advantage of this, your callback code must return a promise.
-  Note that all asynchronous Unpoly functions return promises.
-
-  \#\#\# Throttling
+  For this your callback code must return a promise that resolves
+  when your request completes.
+
+  The following would submit a form whenever an input field changes,
+  but never make more than one request at a time:
+
+      up.observe('input.query', function(value, $input) {
+        var submitDone = up.submit($input);
+        return submitDone;
+      });
+
+  Note that many Unpoly functions like [`up.submit`](/up.submit) or
+  [`up.replace`](/up.replace) return promises.
+
+  \#\#\# Debouncing

   If you are concerned about fast typists causing too much
   load on your server, you can use a `delay` option to wait


fixed TypeError: Cannot call method 'trim' of null in embedboxviews.coffee

--- client/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -359,7 +359,7 @@
     oembed         = data.link_embed
     @originalTitle = oembed?.title

-    @hide()  if not oembed?.title? or oembed?.title.trim() is ''
+    @hide()  if not oembed?.title?.trim() is ''

     if options.hasConfig is yes
       @setClass 'has-config'
@@ -420,7 +420,7 @@

     oembed = data.link_embed

-    @hide()  unless oembed?.description? or oembed?.description.trim() isnt ''
+    @hide()  unless oembed?.description?.trim() isnt ''

     @originalDescription = oembed?.description


groups controller: code style

groups controller: code style

--- client/landing/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Groups.kdapplication/AppController.coffee
@@ -54,11 +54,10 @@
     return @currentGroupData.data

   openGroupChannel:(group, callback=->)->
-    @groupChannel = KD.remote.subscribe "group.#{group.slug}", {
+    @groupChannel = KD.remote.subscribe "group.#{group.slug}",
       serviceType : 'group'
       group       : group.slug
       isExclusive : yes
-    }
     @groupChannel.once 'setSecretNames', callback

   changeGroup:(groupName='koding', callback=->)->


Prevent drop down menu from being cut off

If the menu was wide enough some of it could be cut off when it was
displayed.

--- app/assets/javascripts/active_admin/lib/dropdown-menu.js.coffee
+++ app/assets/javascripts/active_admin/lib/dropdown-menu.js.coffee
@@ -22,8 +22,7 @@
     @isOpen = true
     @$menuList.fadeIn @options.fadeInDuration

-    @_positionMenuList()
-    @_positionNipple()
+    @_position()
     @

@@ -70,14 +69,24 @@
         if @isOpen then @close() else @open()
       false

-  _positionMenuList: ->
-    button_center = @$menuButton.position().left + @$menuButton.outerWidth() / 2
-    menu_center   = @$menuList.outerWidth() / 2
-    @$menuList.css 'left', button_center - menu_center
+  _position: ->
+    @$menuList.css 'top', @$menuButton.position().top + @$menuButton.outerHeight() + 10

-  _positionNipple: ->
-    @$menuList.css 'top',  @$menuButton.position().top + @$menuButton.outerHeight() + 10
-    @$nipple.css   'left', @$menuList.outerWidth() / 2 - @$nipple.outerWidth() / 2
+    button_left = @$menuButton.position().left
+    button_center =  @$menuButton.outerWidth() / 2
+    menu_center = @$menuList.outerWidth() / 2
+    nipple_center = @$nipple.outerWidth() / 2
+
+    centered_menu_left = button_left + button_center - menu_center
+
+    if centered_menu_left < 0
+      # Left align with button
+      @$menuList.css 'left', button_left
+      @$nipple.css   'left', button_center - nipple_center
+    else
+      # Center align under button
+      @$menuList.css 'left', centered_menu_left
+      @$nipple.css   'left', menu_center - nipple_center

 $.widget.bridge 'aaDropdownMenu', ActiveAdmin.DropdownMenu


Remove old rules controller that is not erb

--- app/assets/javascripts/angular/controllers/rules_ctrl.js.coffee
+++ app/assets/javascripts/angular/controllers/rules_ctrl.js.coffee
@@ -1,4 +0,0 @@
-App.controller 'RulesCtrl', ['$scope', ($scope) ->
-  $scope.ruleTypes = () ->
-   ['INSTRUMENT_SURVEY_LIMIT_RULE']
-]


When the read page is retrieved from the bfcache, immediately refresh feeds.

This should mitigate the problem I was having with wrong feed information being displayed when the user navigated back to the app with the back button, mainly in mobile browsers.

--- app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-FeedsFoldersSvc.js.coffee
@@ -254,6 +254,9 @@
       feedsPaginationSvc.set_busy false
       load_data()
       refresh_feeds()
+      # When the page is retrieved from the bfcache, immediately refresh feeds
+      $(window).on 'pageshow', ->
+        load_feeds()

     #---------------------------------------------
     # Reset the timer that refreshes feeds every minute.


If section is expanded before new search trigger click

--- app/assets/javascripts/species/routes/documents_route.js.coffee
+++ app/assets/javascripts/species/routes/documents_route.js.coffee
@@ -11,6 +11,7 @@
       queryParams.geo_entities_ids = queryParams.geo_entities_ids.split(',')
     @controllerFor('elibrarySearch').setFilters(queryParams)
     $(@spinnerSelector).css("visibility", "visible")
+    $('tr.group i.fa-minus-circle').click()

   model: (params, queryParams, transition) ->
     @resetDocumentsResults()


woops accidental indent

woops accidental indent

--- app/components/confirm-account-page/confirm-account-page.coffee
+++ app/components/confirm-account-page/confirm-account-page.coffee
@@ -26,4 +26,4 @@
               $location.path("/setup_group")
         .catch ->
             Toast.show('Sorry, that confirmation token has expired.')
-          $location.path('/')
+            $location.path('/')


fix typo in periodical descriptions

fix typo in periodical descriptions


Improve filters, better scoring Properly reorder grouped selects – don't duplicate optgroups

--- vendor/assets/javascripts/chosen/parser.coffee
+++ vendor/assets/javascripts/chosen/parser.coffee
@@ -55,8 +55,10 @@

   parse: (selected_options = []) ->
     formatter = @chosen.option_formatter || @default_formatter
-    current_group_label = null
-    group = null
+
+    group       = null
+    group_label = null
+    group_id    = 0

     @all_options = []
     @selected_options = []
@@ -66,14 +68,16 @@

     @chosen.$target.find("option").each (index, option) =>
       if option.parentNode.nodeName is "OPTGROUP"
-        if current_group_label != option.parentNode.label
-          current_group_label = option.parentNode.label
-          group = $("<li />", class: "chosen-group", text: current_group_label)
+        if group_label != option.parentNode.label
+          group_label = option.parentNode.label
+          group = $("<li />", class: "chosen-group", text: group_label)
+          group.group_id = group_id
+          group_id += 1
       else
         group = null

       classes = "chosen-option"
-      classes += " group" if group
+      classes += " group"    if group
       classes += " selected" if option.selected
       classes += " disabled" if option.disabled

@@ -126,7 +130,7 @@
       value:      $option[0].value
       selected:   false
       disabled:   false
-      match_type: if append then null else 0
+      match_type: if append then null else -1

     if append
       @all_options.push chosen_option
@@ -235,7 +239,7 @@

   exact_matches: ->
     $.grep @available_options, (option) ->
-      option.match_type is 0
+      option.match_type is -1

   includes_blank: ->
     not not @blank_option()
@@ -246,66 +250,72 @@

     return null

+  reset_filter: ->
+    option.score = option.index * -1 for option in @all_options
+    return @
+
   apply_filter: (value) ->
     @reset_filter()

     if (value = $.trim(value))
-      scores = [
-        @all_options.length * 12, @all_options.length * 9
-        @all_options.length * 6, @all_options.length * 3
-        @all_options.length
-      ]
-
+      num_options = @all_options.length
       exact_query = value.toLowerCase()
-      query = exact_query.replace(Parser.escape_exp, "\\$&").split(" ")
-
-      expressions_collection = $.map(query, (word, index) ->
-        return [[
-          new RegExp("^#{word}$", "i"),
-          new RegExp("^#{word}", "i")
-          new RegExp("#{word}$", "i"),
-          new RegExp(word, "i")
-        ]]
+      query       = $.grep(exact_query.replace(Parser.escape_exp, "\\$&").split(/\W/), (s) -> s.length)
+
+      expressions_collection = $.map(query, (w, i) ->
+        return [[new RegExp("^#{w}$", "i"), new RegExp("^#{w}", "i"), new RegExp(w, "i")]]
       )

       for option in @all_options
+        option.score = 0
+
         if option.label.toLowerCase() is exact_query
-          option.match_type = 0
+          option.match_type = -1
         else
           option.match_type = null

-        words = option.label.split(" ")
-
-        for word in words
+        words = $.grep(option.label.split(/\W/), (s) -> s.length)
+
+        for word, word_index in words
           for expressions in expressions_collection
-            for expression, index in expressions
+            for expression, expression_index in expressions
               if word.match(expression)
-                if option.match_type is null
-                  option.match_type = index + 1
-
-                option.score += scores[option.match_type]
+                unless option.match_type is -1
+                  option.match_type = expression_index
+
+                option.score += num_options * (expressions.length - option.match_type) / (word_index + 1)
                 break
-              else if index is expressions.length - 1
-                option.score -= 1
+              else if expression_index is expressions.length - 1
+                option.score -= num_options * expressions.length / words.length

     @order()
     return @

-  reset_filter: ->
-    option.score = option.index * -1 for option in @all_options
-    return @
-
+  # Re-order all_options array keeping the optgroup order
   order: ->
-    @all_options = @all_options.sort (a, b) ->
-      if a.score > b.score then -1 else if a.score < b.score then 1 else 0
-
+    groups = {}
+
+    for option in @all_options
+      group_id = if option.$group then option.$group.group_id else -1
+      groups[group_id] ||= []
+      groups[group_id].push(option)
+
+    @all_options       = []
     @available_options = []
+
+    for own group_id, value of groups
+      @all_options = @all_options.concat(@order_group(value))

     for option in @all_options
       if not option.blank
         @available_options.push(option)

     return @
+
+  order_group: (group) ->
+    group
+      .sort (a, b) -> a.index - b.index
+      .sort (a, b) -> b.score - a.score

   default_parser: (attrs) ->
     value: attrs.value


Texture: Add DDS support. Misc changes.

Texture: Add DDS support. Misc changes.


update test case description

update test case description

--- device/test/10-static.coffee
+++ device/test/10-static.coffee
@@ -1,4 +1,4 @@
 describe 'static', ->
-  it 'GET /static/js/app.js', ->
+  it 'GET /static/js/test.js', ->
     fetch '/static/js/test.js'
       .then ok


add X-CSRF-Token to any ajax not GET query

--- app/templates/app/scripts/coffee/app.coffee
+++ app/templates/app/scripts/coffee/app.coffee
@@ -7,12 +7,17 @@
   Modal = require "view/modal"
   Page = require "view/page"
   Widget = require "view/widget"
-
+  cookies = require "cookies"
   #GAConstructor = require "sp-utils-gaconstructor"
   #UserModel = require "model/UserModel"
   #social = require "packages/social"

   $ = Backbone.$
+
+  $(document).ajaxSend (event, jqxhr, settings)->
+   if settings.type != "GET"
+      jqxhr.setRequestHeader 'X-CSRF-Token', cookies.get('CSRF-Token')
+

   class Application
     constructor: (common)->


Implement C++ void main() {} sample code wrapper

--- app/views/play/level/tome/Spell.coco.coffee
+++ app/views/play/level/tome/Spell.coco.coffee
@@ -82,8 +82,20 @@
         }
       """
     if @language is 'cpp' and not @languages[@language]
-      # TODO: do some transforms here to handle most common sample code differences between JavaScript and C++
-      @languages.cpp = @languages.javascript
+      # TODO: do more transforms here to handle most common sample code differences between JavaScript and C++
+      lines = (@languages.javascript ? '').split '\n'
+      lines.push '' if lines[lines.length - 1] isnt ''
+      code = """
+        void main() {
+        #{(lines.map ((line) -> '    ' + line)).join('\n')}
+        }
+      """
+      code = code.replace new RegExp('var i =', 'g'), 'int i ='
+      code = code.replace new RegExp('    var x', 'g'), '    float x'
+      code = code.replace new RegExp('    var y', 'g'), '    float y'
+      code = code.replace new RegExp('    var dist', 'g'), '    float dist'
+      code = code.replace new RegExp('    var ', 'g'), '    auto '
+      @languages.cpp = code
     @originalSource = @languages[@language] ? @languages.javascript
     @originalSource = @addPicoCTFProblem() if window.serverConfig.picoCTF


define person

define person

--- _source/server/lib/user.coffee
+++ _source/server/lib/user.coffee
@@ -27,7 +27,7 @@
 ###
 Apollos.user.translate = (user, platform) ->

-  return Apollos.entityHelpers.translate "person", person, platform
+  return Apollos.entityHelpers.translate "user", user, platform


activity: minor code style update

activity: minor code style update

--- client/activity/lib/flux/actions/realtime/actioncreators.coffee
+++ client/activity/lib/flux/actions/realtime/actioncreators.coffee
@@ -20,11 +20,11 @@
   messageId = message.id
   { initialChannelId: channelId } = message

-  message.on 'LikeAdded', ({ accountId }) ->
-    dispatch actions.LIKE_MESSAGE_SUCCESS, { userId: accountId, messageId }
+  message.on 'LikeAdded', ({ accountId: userId }) ->
+    dispatch actions.LIKE_MESSAGE_SUCCESS, { userId, messageId }

-  message.on 'LikeRemoved', ({ accountId }) ->
-    dispatch actions.UNLIKE_MESSAGE_SUCCESS, { userId: accountId, messageId }
+  message.on 'LikeRemoved', ({ accountId: userId }) ->
+    dispatch actions.UNLIKE_MESSAGE_SUCCESS, { userId, messageId }

   message.on 'AddReply', (comment) ->
     dispatch actions.LOAD_COMMENT_SUCCESS, { messageId, comment }


Workspace: Improved pane resizing for split views.

--- client/landing/app/CommonViews/workspace/workspacelayout.coffee
+++ client/landing/app/CommonViews/workspace/workspacelayout.coffee
@@ -19,8 +19,13 @@
         views.push wrapper

     splitView = new SplitViewWithOlderSiblings { type, sizes, views }
-    splitView.on "ResizeDidStop", =>
-      for pane in @getDelegate().panes
-        pane.emit "PaneResized"
+    splitView.on "ResizeDidStop", => @emitResizedEventToPanes()

-    return splitView+    splitView.on "viewAppended", =>
+      splitView.resizers[0].on "DragInAction", =>
+        @emitResizedEventToPanes()
+
+    return splitView
+
+  emitResizedEventToPanes: ->
+    pane.emit "PaneResized" for pane in @getDelegate().panes


Remove referal id from invite email link

--- services/web/app/coffee/Features/Collaborators/CollaboratorsEmailHandler.coffee
+++ services/web/app/coffee/Features/Collaborators/CollaboratorsEmailHandler.coffee
@@ -9,8 +9,6 @@
 		"#{Settings.siteUrl}/project/#{project._id}/invite/token/#{invite.token}?" + [
 			"project_name=#{encodeURIComponent(project.name)}"
 			"user_first_name=#{encodeURIComponent(project.owner_ref.first_name)}"
-			"r=#{project.owner_ref.referal_id}" # Referal
-			"rs=ci" # referral source = collaborator invite
 		].join("&")

 	notifyUserOfProjectShare: (project_id, email, callback)->


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -44,7 +44,7 @@

   Point:: =
     ###*
-    Constrain the magnitude of a
+    Constrain the magnitude of a vector.

     @name clamp
     @methodOf Point#


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/point.coffee
+++ src/point.coffee
@@ -50,7 +50,7 @@
       @copy().clamp()

     clamp$: (n) ->
-      if @magnitude
+      if @magnitude() >

     ###*
     Creates a copy of this point.


refactor(model): removed all encryption functionality, added containers

--- src/model.coffee
+++ src/model.coffee
@@ -7,13 +7,14 @@
   'use strict'

   unless options.connector?
-    throw new Error 'No connector given for api: ' + options.api
-
-  options.connector.init options.api
-
-  options.connector.add_apis_to options.has_one, options.api if options.has_one?
-
-  options.connector.add_apis_to options.has_many, options.api if options.has_many?
+    throw new Error 'No connector given for API URI: ' + options.api
+
+  sub_resources = []
+
+  sub_resources = object_merge sub_resources, options.has_one if options.has_one?
+  sub_resources = object_merge sub_resources, options.has_many if options.has_many?
+
+  options.connector.init options.api, sub_resources

   connector = options.connector.get options.api

@@ -27,10 +28,8 @@
     #
     self.api = -> connector

-    # Check if de/encryption functions should be included
-    #
-    if options.encrypted_container?
-      Encryptable self, options.encrypted_container, options.encrypted_editable
+    if options.hash_salt?
+      self.hash_salt = options.hash_salt

     # Include sortable functions
     if options.sortable? && options.sortable == true
@@ -60,11 +59,11 @@
       for index, name of options.editable
         Editable self, name, DelayedSave.apply(self, [options, self])

-    # Encrypted editable fields
-    #
-    if options.encrypted_editable?
-      for index, name of options.encrypted_editable
-        Editable self, name, EncryptedDelayedSave.apply(self, [options, self])
+    # Hashed editable fields
+    #
+    if options.hashed_index?
+      for index, name of options.hashed_index
+        Editable self, name, HashedSave.apply(self, [options, self])

     # Set computed fields
     #
@@ -103,9 +102,6 @@
       self.api().create(self.export()).done (data) =>
         self.id = data.id

-        if options.encrypted_container?
-          self.enc_update data
-
         callback()

     # Export all model data as an array
@@ -118,10 +114,6 @@

       if options.editable?
         for index, name of options.editable
-          data[name] = self[name]()
-
-      if options.encrypted_editable?
-        for index, name of options.encrypted_editable
           data[name] = self[name]()

       if options.belongs_to?
@@ -152,6 +144,15 @@
     if typeof params != 'object'
       throw new Error 'Passed params is not an object: ' + typeof params

+    if options.hashed_index? && params?
+      salt = ''
+      if options.hash_salt?
+        salt = options.hash_salt
+
+      for param of params
+        if param in options.hashed_index
+          params[param] = getHash(params[param] + salt)
+
     if params? && params.id?
       connector.read(params.id, params).done callback
     else
@@ -193,33 +194,12 @@
         callbackOrObservable(data)

     if params.length > 0 || typeof params == 'object'
-      if model.encrypted_container?
-        params = model.encrypt_container(params)
-
       connector.create(params).done callback
     else
       connector.create().done callback

     return

-  model.encrypted_container = options.encrypted_container if options.encrypted_container?
-
-  model.encrypted_editable = options.encrypted_editable if options.encrypted_editable?
-
-  # Debug function
-  model.encrypt_container = (data, container) ->
-    data[model.encrypted_container] = {}
-
-    for index, value of data
-      if index in model.encrypted_editable
-        delete data[index]
-        data[model.encrypted_container][index] = value
-
-    encdata = encryptData(sessionStorage.getItem('appKey'), JSON.stringify(data[model.encrypted_container]))
-    data[model.encrypted_container] = encdata
-
-    return data
-
   return model


Use the state function as the prototype

Use the state function as the prototype

This is funky. It's bothersome that you can't seem to really create suclasses
of Function (at least in v8). Using the original function as a prototype
at least gets us some properties (like length) that could be useful.

Anyway, it's symmetric with what I did for List with Arrays and that had
a real motivation...

--- src/monad.coffee
+++ src/monad.coffee
@@ -80,7 +80,7 @@
   concat (map fn, list).valueOf()...

 # Stateful computations!
-State = (sfn) -> Trait.create Function.prototype, (Trait.override (Trait {
+State = (sfn) -> Trait.create sfn, (Trait.override (Trait {
   unit : (u) ->
     State ((s) -> List u, s)
   bind : (fn) ->


Revert the incorrect change in watcher

Revert the incorrect change in watcher

--- library/gearing/watch.coffee
+++ library/gearing/watch.coffee
@@ -165,7 +165,7 @@
     # should be used only by the watcher internals, not directly.
     collectServices: (required) ->
         exports = _.values (required.exports or [])
-        isService = (s) -> s::?process? and s::?matches?
+        isService = (s) -> s::process? and s::matches?
         isAugment = (s) -> s.augment instanceof augment.Augment
         augments = _.filter exports, isAugment
         augments = augments.map (a) -> a.service


minor unit test fix

minor unit test fix

--- test/test-fluent-process.coffee
+++ test/test-fluent-process.coffee
@@ -16,7 +16,7 @@
         childProcess = 'ls'.start_Process_Redirect_Console('.')
         childProcess.on 'exit', ->
             console.log 'process ended'
-            log_Messages.first() .assert_Is('LICENSE\nREADME.md\nindex.js\nnode_modules\npackage.json\nsrc\ntest')
+            log_Messages.first() .assert_Contains('README.md')
             log_Messages.second().assert_Is('process ended')
             console.log = original_log
             done()


Add failing test to show the problem

Add failing test to show the problem

Signed-off-by: Niels van Hoorn <9639b9eee3fe09ce53e8dfede54f918fb4bd39be@nvh.io>

--- test/tests/BaseClassTest.coffee
+++ test/tests/BaseClassTest.coffee
@@ -204,3 +204,23 @@

 		instance.testPropA.should.equal "a"
 		instance.testPropB.should.equal "value"
+	it "should not share properties in subclasses", ->
+
+		class LalaLayer extends Framer.BaseClass
+			@define "blabla",
+				get: -> "hoera"
+				set: -> "sdfsd"
+
+		class TestClassD extends LalaLayer
+			@define "a",
+				get: -> "getClassD"
+				set: -> "setClassD"
+
+
+		class TestClassC extends LalaLayer
+			@define "a",
+				get: -> "getClassC"
+				# set: -> "setClassC"
+
+		expect(TestClassD["_DefinedPropertiesKey"]?.a?.set).to.be.ok
+		expect(TestClassC["_DefinedPropertiesKey"]?.a?.set).to.not.be.ok


CREATE-711 Enable passing in focus and blur handlers as options

--- src/quill.coffee
+++ src/quill.coffee
@@ -26,7 +26,9 @@
     pollInterval: 100
     readOnly: false
     styles: {}
-    theme: 'base'
+    theme: 'base',
+    onFocus: false,
+    onBlur: false

   @events:
     FORMAT_INIT      : 'format-init'
@@ -78,6 +80,8 @@
     _.each(@options.modules, (option, name) =>
       this.addModule(name, option)
     )
+    dom(@root).on('focus', @options.onFocus) if @options.onFocus
+    dom(@root).on('blur', @options.onBlur) if @options.onFocus

   destroy: ->
     html = this.getHTML()


Be sure to return (void 0), not void 0.

Be sure to return (void 0), not void 0.

--- src/nodes.coffee
+++ src/nodes.coffee
@@ -236,7 +236,7 @@
         return '\n' + codes.join('\n\n') + '\n'
       else
         return "#{codes.join '\n'}"
-    code = codes.join(', ') or 'void 0'
+    code = codes.join(', ') or '(void 0)'
     "(do #{code})"

   # If we happen to be the top-level **Block**, wrap everything in


use kernel's display_name as language if none set. Closes #5

--- lib/kernel-manager.coffee
+++ lib/kernel-manager.coffee
@@ -38,7 +38,12 @@
         kernels = @getAvailableKernels()
         console.log "Available kernels:", kernels
         matchingKernels = _.filter kernels, (kernel) ->
-            return kernel? and language.toLowerCase() == kernel.language.toLowerCase()
+            kernelLanguage = kernel.language
+            kernelLanguage ?= kernel.display_name
+
+            return kernel? and
+                   kernel.language? and
+                   language.toLowerCase() == kernel.language.toLowerCase()

         if matchingKernels.length == 0
             return null


KernelManager: use SIGINT

KernelManager: use SIGINT

* Invoke child_process.exec with `killSignal: 'SIGINT'`.

* The default `killSignal: 'SIGTERM'` launches a crash report in Ubuntu
  16.04 when trying to run `ipython kernelspec list --json
  --log-level=CRITICAL`.

--- lib/kernel-manager.coffee
+++ lib/kernel-manager.coffee
@@ -168,7 +168,8 @@
                 err = null
                 message = 'Hydrogen Kernels updated:'
                 options =
-                    detail: (_.map @_kernelSpecs, 'spec.display_name').join('\n')
+                    detail:
+                        (_.map @_kernelSpecs, 'spec.display_name').join('\n')
                 atom.notifications.addInfo message, options

             callback? err, @_kernelSpecs
@@ -191,7 +192,8 @@

     getKernelSpecsFrom: (command, callback) ->
-        child_process.exec command, (err, stdout, stderr) ->
+        options = killSignal: 'SIGINT'
+        child_process.exec command, options, (err, stdout, stderr) ->
             unless err
                 try
                     kernelSpecs = JSON.parse(stdout).kernelspecs


Support old style command documentation

Support old style command documentation

--- src/robot.coffee
+++ src/robot.coffee
@@ -12,6 +12,8 @@
 inspect = require('util').inspect

 HUBOT_DEFAULT_ADAPTERS = [ 'campfire', 'shell' ]
+HUBOT_DOCUMENTATION_SECTIONS = [ 'description', 'dependencies', 'configuration', 'commands', 'notes', 'author' ]
+

 class Robot
   # Robots receive messages from a chat source (Campfire, irc, etc), and
@@ -262,21 +264,33 @@
         cleanedLine = line[2..line.length].replace("\n", "")
         @logger.debug "parseHelp(#{scriptName}): read #{cleanedLine}"

-        # clean line isn't empty, and isn't 'none'
-        if cleanedLine.length isnt 0 and cleanedLine.trim().toLowerCase() isnt 'none'
-
-          # sections shouldn't have any leading whitespace
-          if cleanedLine[0..1] isnt '  '
-            currentSection = cleanedLine.replace(':', '').toLowerCase()
-            scriptDocumentation[currentSection] = []
-            @logger.debug "parseHelp(#{scriptName}): adding #{currentSection} section"
-          # lines in a section _do_ have leading whitespace
-          else
-            if currentSection
-              @logger.debug "parseHelp(#{scriptName}) adding '#{cleanedLine.trim()}' to #{currentSection}"
-              scriptDocumentation[currentSection].push cleanedLine.trim()
-              if currentSection == 'commands'
-                @commands.push cleanedLine.trim()
+        continue if cleanedLine.length is 0
+        continue if cleanedLine.trim().toLowerCase() is 'none'
+
+        # sections shouldn't have any leading whitespace
+        if cleanedLine.trim().toLowerCase().replace(':', '') in HUBOT_DOCUMENTATION_SECTIONS
+          currentSection = cleanedLine.replace(':', '').toLowerCase()
+          scriptDocumentation[currentSection] = []
+          @logger.debug "parseHelp(#{scriptName}): adding #{currentSection} section"
+        # lines in a section _do_ have leading whitespace
+        else
+          if currentSection
+            @logger.debug "parseHelp(#{scriptName}) adding '#{cleanedLine.trim()}' to #{currentSection}"
+            scriptDocumentation[currentSection].push cleanedLine.trim()
+            if currentSection == 'commands'
+              @commands.push cleanedLine.trim()
+      # no current section? probably using old style documentation
+      if currentSection is null
+        @logger.info "#{path} is using deprecated documentation syntax"
+        scriptDocumentation.commands = []
+        for i, line of body.split("\n")
+          break    if not (line[0] == '#' or line.substr(0, 2) == '//')
+          continue if not line.match('-')
+          cleanedLine = line[2..line.length].replace(/^hubot/i, @name).trim()
+
+          @logger.debug "parseHelp(#{scriptName}) adding '#{cleanedLine}' to commands"
+          scriptDocumentation.commands.push cleanedLine
+          @commands.push cleanedLine

   # Public: A helper send function which delegates to the adapter's send
   # function.


But, we aren't linting on the fly.

But, we aren't linting on the fly.

--- lib/linter-crystal.coffee
+++ lib/linter-crystal.coffee
@@ -34,7 +34,7 @@
       scopes: ['source.crystal']
       scope: 'file'
       lint: @lint
-      lintOnFly: true
+      lintOnFly: false
     }

   lint: (TextEditor, TextBuffer) ->


Fix require

Fix require

--- src/robot.coffee
+++ src/robot.coffee
@@ -1,8 +1,8 @@
-Fs           = require 'fs'
-Log          = require 'log'
-Path         = require 'path'
-HttpClient   = require 'scoped-http-client'
-EventEmitter = require('events').EventEmitter;
+Fs             = require 'fs'
+Log            = require 'log'
+Path           = require 'path'
+HttpClient     = require 'scoped-http-client'
+{EventEmitter} = require 'events'

 User                                                    = require './user'
 Brain                                                   = require './brain'


Make sure the undefined user has the correct room set before returning a user object.

--- src/robot.coffee
+++ src/robot.coffee
@@ -276,6 +276,11 @@
     unless user
       user = new User id, options
       @brain.data.users[id] = user
+
+    if options.room and user.room isnt options.room
+      user = new User id, options
+      @brain.data.users[id] = user
+
     user

   # Public: Get a User object given a name.


Convert file line endings to unix.

Convert file line endings to unix.

--- src/main.coffee
+++ src/main.coffee
@@ -1,175 +1,175 @@
-###
-# AJAX call for main info page.
-###
-
-# Set-timeout values: current, min/max limits and adjustment coefficient.
-set_timeout_cur = 100
-set_timeout_max = 1000
-set_timeout_min = 0
-set_timeout_incr = 10
-set_timeout_decr = 10
-
-# The actual AJAX timeout value.
-ajax_timeout = 2000
-
-# Previous image timestamp, used to detect whether
-# a new image is available.
-prev_image_tstamp = null
-
-# Time when image source  changes.
-# Used to compute image load time.
-image_load_begin = Date.now()
-
-# Keep track of latest interval between images.
-image_interval = Date.now()
-
-# Keep counts of requests, earlies and errors.
-request_count = 0
-early_count = 0
-error_count = 0
-
-info = ->
-    ###
-    # Make the AJAX server info request.
-    ###
-
-    # Increment the request count.
-    request_count++
-
-    # Mark the time-of-request.
-    request_begin = Date.now()
-
-    # Mark the beginning of callback entry.
-    callback_begin = null
-
-    $.ajax 'service/info',
-        dataType : 'json'
-        cache    : false
-        timeout  : ajax_timeout
-        success  : (data, status, req) ->
-
-            # Mark the start of callback processing.
-            callback_begin = Date.now()
-
-            # Update the AJAX response time.
-            elapsed = (Date.now() - request_begin) / 1000;
-            $('#response_time').text elapsed.toFixed(3)
-
-            short_tstamp = data.latest_tstamp.slice(0, -7)  # Trim off ms.
-            short_tstamp = short_tstamp.substring(11)  # Trim off date.
-            $('#live_time_hud').text short_tstamp
-            $('#server_time').text data.server_time.slice(0, -3)
-            $('#load_avg').text data.load_avg
-            $('#db_size').text data.db_size
-            $('#latest_tstamp').text data.latest_tstamp.slice(0, -3)
-
-            # If the database is empty, turn OFF live display
-            # and try again.
-            if data.db_size == '0'
-                $('#live_outer').css 'display', 'none'
-                tryInfoAgain()
-
-            # Otherwise, if a new image arrived...
-            else if prev_image_tstamp != data.latest_tstamp
-
-                # Compute the interval between images
-                # as the difference between now and the previous load.
-                image_interval = Date.now() - image_load_begin
-                $('#image_interval').text (image_interval/1000).toFixed(3)
-
-                # Reset load begin time.
-                image_load_begin = Date.now()
-
-                # Set the  src attribute
-                # and make sure live display is ON.
-                $('#latest_image').attr 'src', data.latest_url
-                $('#live_outer').css 'display', 'inline-block'
-                prev_image_tstamp = data.latest_tstamp
-
-            # Otherwise, the database is not empty but there is no
-            # newer image available.
-            # Either we requested too soon (the server's capture
-            # framerate is slower than our request rate)
-            else
-                early_count++
-                tryInfoAgain()
-
-        error : (req, status, err) ->
-
-            # Mark the start of callback processing.
-            callback_begin = Date.now()
-
-            # Increment the error count.
-            error_count++
-
-            # Update the AJAX response time display.
-            elapsed = (Date.now() - request_begin) / 1000;
-            $('#response_time').text elapsed.toFixed(3)
-
-            $('#server_time').text 'Error'
-            $('#load_avg').text ''
-            $('#db_size').text ''
-            $('#latest_tstamp').text ''
-
-            tryInfoAgain()
-
-        complete : (req, satus) ->
-
-            # Update the callback processing time display.
-            elapsed = (Date.now() - callback_begin) / 1000;
-            $('#callback_time').text elapsed.toFixed(3)
-
-            # Update the displays.
-            $('#request_count').text request_count
-            $('#error_count').text error_count
-            $('#set_timeout').text (set_timeout_cur/1000).toFixed(3)
-
-            # Update the early count display.
-            ratio = request_count/early_count
-            $('#early_count').text early_count
-            $('#request_early_ratio').text ratio.toFixed(3)
-
-
-tryInfoAgain = ->
-    ###
-    # Increase the set-timeout delay, and then
-    # set info() as the timeout callback.
-    ###
-
-    # Compute the interval between images
-    # as the difference between now and the previous image load.
-    image_interval = Date.now() - image_load_begin
-    $('#image_interval').text (image_interval/1000).toFixed(3)
-
-    # Set timeout.
-    set_timeout_cur += set_timeout_incr
-    set_timeout_cur = Math.min(set_timeout_cur, set_timeout_max)
-    callback = -> info 0
-    setTimeout callback, set_timeout_cur
-
-
-onImageLoad = ->
-    ###
-    # Decrease the set-timeout delay and call info().
-    ###
-
-    # Update the load-time display.
-    load_time = Date.now() - image_load_begin
-    $('#image_load_time').text (load_time/1000).toFixed(3)
-
-    # Set timeout.
-    set_timeout_cur -= set_timeout_decr
-    set_timeout_cur = Math.max(set_timeout_cur, set_timeout_min)
-    callback = -> info 0
-    setTimeout callback, set_timeout_cur
-
-# Only run when document has loaded
-$ ->
-    info()
-
-# All CoffeeScript output is wrapped in an anonymous function,
-# so in order to use functions as top-level variables (like in HTML)
-# let's attach them as properties on *window*.
-window.wabbitOnImageLoad = onImageLoad
-
-# End of file.
+###
+# AJAX call for main info page.
+###
+
+# Set-timeout values: current, min/max limits and adjustment coefficient.
+set_timeout_cur = 100
+set_timeout_max = 1000
+set_timeout_min = 0
+set_timeout_incr = 10
+set_timeout_decr = 10
+
+# The actual AJAX timeout value.
+ajax_timeout = 2000
+
+# Previous image timestamp, used to detect whether
+# a new image is available.
+prev_image_tstamp = null
+
+# Time when image source  changes.
+# Used to compute image load time.
+image_load_begin = Date.now()
+
+# Keep track of latest interval between images.
+image_interval = Date.now()
+
+# Keep counts of requests, earlies and errors.
+request_count = 0
+early_count = 0
+error_count = 0
+
+info = ->
+    ###
+    # Make the AJAX server info request.
+    ###
+
+    # Increment the request count.
+    request_count++
+
+    # Mark the time-of-request.
+    request_begin = Date.now()
+
+    # Mark the beginning of callback entry.
+    callback_begin = null
+
+    $.ajax 'service/info',
+        dataType : 'json'
+        cache    : false
+        timeout  : ajax_timeout
+        success  : (data, status, req) ->
+
+            # Mark the start of callback processing.
+            callback_begin = Date.now()
+
+            # Update the AJAX response time.
+            elapsed = (Date.now() - request_begin) / 1000;
+            $('#response_time').text elapsed.toFixed(3)
+
+            short_tstamp = data.latest_tstamp.slice(0, -7)  # Trim off ms.
+            short_tstamp = short_tstamp.substring(11)  # Trim off date.
+            $('#live_time_hud').text short_tstamp
+            $('#server_time').text data.server_time.slice(0, -3)
+            $('#load_avg').text data.load_avg
+            $('#db_size').text data.db_size
+            $('#latest_tstamp').text data.latest_tstamp.slice(0, -3)
+
+            # If the database is empty, turn OFF live display
+            # and try again.
+            if data.db_size == '0'
+                $('#live_outer').css 'display', 'none'
+                tryInfoAgain()
+
+            # Otherwise, if a new image arrived...
+            else if prev_image_tstamp != data.latest_tstamp
+
+                # Compute the interval between images
+                # as the difference between now and the previous load.
+                image_interval = Date.now() - image_load_begin
+                $('#image_interval').text (image_interval/1000).toFixed(3)
+
+                # Reset load begin time.
+                image_load_begin = Date.now()
+
+                # Set the  src attribute
+                # and make sure live display is ON.
+                $('#latest_image').attr 'src', data.latest_url
+                $('#live_outer').css 'display', 'inline-block'
+                prev_image_tstamp = data.latest_tstamp
+
+            # Otherwise, the database is not empty but there is no
+            # newer image available.
+            # Either we requested too soon (the server's capture
+            # framerate is slower than our request rate)
+            else
+                early_count++
+                tryInfoAgain()
+
+        error : (req, status, err) ->
+
+            # Mark the start of callback processing.
+            callback_begin = Date.now()
+
+            # Increment the error count.
+            error_count++
+
+            # Update the AJAX response time display.
+            elapsed = (Date.now() - request_begin) / 1000;
+            $('#response_time').text elapsed.toFixed(3)
+
+            $('#server_time').text 'Error'
+            $('#load_avg').text ''
+            $('#db_size').text ''
+            $('#latest_tstamp').text ''
+
+            tryInfoAgain()
+
+        complete : (req, satus) ->
+
+            # Update the callback processing time display.
+            elapsed = (Date.now() - callback_begin) / 1000;
+            $('#callback_time').text elapsed.toFixed(3)
+
+            # Update the displays.
+            $('#request_count').text request_count
+            $('#error_count').text error_count
+            $('#set_timeout').text (set_timeout_cur/1000).toFixed(3)
+
+            # Update the early count display.
+            ratio = request_count/early_count
+            $('#early_count').text early_count
+            $('#request_early_ratio').text ratio.toFixed(3)
+
+
+tryInfoAgain = ->
+    ###
+    # Increase the set-timeout delay, and then
+    # set info() as the timeout callback.
+    ###
+
+    # Compute the interval between images
+    # as the difference between now and the previous image load.
+    image_interval = Date.now() - image_load_begin
+    $('#image_interval').text (image_interval/1000).toFixed(3)
+
+    # Set timeout.
+    set_timeout_cur += set_timeout_incr
+    set_timeout_cur = Math.min(set_timeout_cur, set_timeout_max)
+    callback = -> info 0
+    setTimeout callback, set_timeout_cur
+
+
+onImageLoad = ->
+    ###
+    # Decrease the set-timeout delay and call info().
+    ###
+
+    # Update the load-time display.
+    load_time = Date.now() - image_load_begin
+    $('#image_load_time').text (load_time/1000).toFixed(3)
+
+    # Set timeout.
+    set_timeout_cur -= set_timeout_decr
+    set_timeout_cur = Math.max(set_timeout_cur, set_timeout_min)
+    callback = -> info 0
+    setTimeout callback, set_timeout_cur
+
+# Only run when document has loaded
+$ ->
+    info()
+
+# All CoffeeScript output is wrapped in an anonymous function,
+# so in order to use functions as top-level variables (like in HTML)
+# let's attach them as properties on *window*.
+window.wabbitOnImageLoad = onImageLoad
+
+# End of file.


main.coffee updated

main.coffee updated

--- src/main.coffee
+++ src/main.coffee
@@ -1 +1,3 @@
-requirejs ['sugar']+JasmineSugar = requirejs ['sugar']
+
+JasmineSugar.setup(this)


capture screen if not already cached

capture screen if not already cached

--- src/main.coffee
+++ src/main.coffee
@@ -1,6 +1,11 @@
+fs = require 'fs'
 path = require 'path'
+crypto = require 'crypto'

+_ = require 'lodash'
 electron = require 'electron'
+mkdirp = require 'mkdirp'
+Promise = require 'when'

 Launcher = require './launcher'

@@ -14,6 +19,22 @@

 primaryDisplay = null
 windows = null
+counter = 0
+
+cacheDirPromise = Promise.promise (resolve, reject) ->
+  dirName = path.resolve __dirname, '../cache'
+  mkdirp dirName, (err) ->
+    return reject err if err
+    resolve dirName
+
+getCacheImagePath = (url, width, height) ->
+  cacheDirPromise
+  .then (cacheDir) ->
+    hash = crypto
+      .createHash 'md5'
+      .update String(width) + String(height) + url
+      .digest 'hex'
+    path.join cacheDir, "#{hash}.png"

 closeWindows = ->
   return unless windows?.length > 0
@@ -93,58 +114,45 @@
       height: bounds.height
       enableLargerThanScreen: true
       frame: false
-    window.setBounds(bounds, false);
+    window._id = counter++
+    window.setBounds bounds, false

-    # window.loadURL('http://mucholol.com/');
-    # https://www.youtube.com/watch?v=NeQ0_e7aa8o
+    setTimeout ->
+      win = _.find windows, (w) -> w == window
+      return unless win?
+      getCacheImagePath display.url, bounds.width, bounds.height
+      .then (fileName) ->
+        Promise.promise (resolve, reject) ->
+          fs.exists fileName, (exists) ->
+            if exists
+              resolve false
+            else
+              resolve fileName
+      .then (fileName) ->
+        return unless fileName
+        Promise.promise (resolve, reject) ->
+          window.capturePage (img) ->
+            png = img?.toPng?()
+            resolve png
+        .then (imgBuffer) ->
+          return unless imgBuffer
+          fs.writeFile fileName, imgBuffer, (err) ->
+            return console.log err if err
+    , 5000

-    # window.loadURL('http://www.youtube.com/embed/NeQ0_e7aa8o?autoplay=1&loop=1&controls=0&showinfo=0&rel=0&autohide=1&playlist=NeQ0_e7aa8o');
-    # window.loadURL('http://www.youtube.com/embed/O4UV_SVGSUU?autoplay=1&loop=1&controls=0&showinfo=0&rel=0&autohide=1&playlist=O4UV_SVGSUU');
-    # window.loadURL "file://#{path.resolve __dirname, '..'}/index.html#bounds=#{boundsStr}"
     window.loadURL display.url
-
-    # window.webContents.openDevTools();

     window.on 'closed', ->
       closeWindows()
-      # app.quit()
+      console.log 'windows closed, show launcher', arguments
       Launcher.show primaryDisplay

     windows.push window

-  # and load the index.html of the app.
-
-  # Open the DevTools.
-  # mainWindow.webContents.openDevTools();
-
-  # Emitted when the window is closed.
-  # mainWindow.on('closed', function() {
-  #   # Dereference the window object, usually you would store windows
-  #   # in an array if your app supports multi windows, this is the time
-  #   # when you should delete the corresponding element.
-  #   mainWindow = null;
-  # });
-
 ipcMain.on 'launch', (event, data) ->
   createWindows data.displays

-# This method will be called when Electron has finished
-# initialization and is ready to create browser windows.
 app.on 'ready', ->
   electronScreen = electron.screen
   primaryDisplay = electronScreen.getPrimaryDisplay()
   Launcher.show primaryDisplay
-
-# # Quit when all windows are closed.
-# app.on 'window-all-closed', ->
-#   # On OS X it is common for applications and their menu bar
-#   # to stay active until the user quits explicitly with Cmd + Q
-#   if process.platform != 'darwin'
-#     app.quit()
-
-# app.on 'activate', ->
-#   # On OS X it's common to re-create a window in the app when the
-#   # dock icon is clicked and there are no other windows open.
-#   # if (mainWindow === null) {
-#   #   createWindows();
-#   # }


Show update errors

Show update errors

--- lib/packages-panel.coffee
+++ lib/packages-panel.coffee
@@ -61,6 +61,9 @@

     @subscribe @packageManager, 'package-install-failed', (pack, error) =>
       @searchErrors.append(new ErrorView(error))
+
+    @subscribe @packageManager, 'package-update-failed theme-update-failed', (pack, error) =>
+      @updateErrors.append(new ErrorView(error))

     @loadFeaturedPackages()
     @checkForUpdates()