added subscriber

added subscriber

--- src/js/continuum.coffee
+++ src/js/continuum.coffee
@@ -8,6 +8,27 @@
 Continuum.register_collection = (key, value) ->
   Collections[key] = value
   value.bokeh_key = key
+
+Continuum.load_models = (modelspecs)->
+  for model in modelspecs
+    colls = get_collections(model['collections'])
+    attrs = model['attributes']
+    type = model['type']
+    colls[type].add(attrs)
+
+Continuum.submodels = (ws_conn_string, topic) ->
+  try
+    s = new WebSocket(ws_conn_string)
+  catch
+    s = new MozWebSocket(ws_conn_string)
+  s.onopen = () ->
+    s.send(JSON.stringify({msgtype : 'subscribe', topic : topic}))
+  s.onmessage = (msg) ->
+    msgobj = JSON.parse(msg.data)
+    if msgobj['msgtype'] == 'modelpush'
+      Continuum.load_models(msgobj['modelspecs'])
+  return s
+

 window.logger = new Backbone.Model()
 window.logger.on('all',
@@ -415,12 +436,6 @@
   model : Table
   url : "/"

-Continuum.load_models = (models)->
-  for model in models
-    colls = get_collections(model['collections'])
-    attrs = model['attributes']
-    type = model['type']
-    colls[type].create(attrs)

 Continuum.register_collection('Table', new Tables())


fix default login window height

fix default login window height

--- src/api/auth.coffee
+++ src/api/auth.coffee
@@ -174,13 +174,12 @@
           window.__hull_login_status__ = null
           onCompleteAuthentication(hash)
       authHelper: (path, providerName)->
-
         if providerName == 'facebook'
           width = 650
           height = 430
         else
           width = 1030
-          width = 430
+          height = 550

         w = window.open(path, "_auth", "location=0,status=0,width=#{width},height=#{height}")


host.stats fixed 2

host.stats fixed 2

--- src/api/host.coffee
+++ src/api/host.coffee
@@ -272,6 +272,9 @@
   granularity = if granularity then granularity else 0
   # granularity: 0: every 5 sec along last 10 minutes, 1: every minute along last 2 hours, 2: every hour along past week, 3: everyday along past year
   # see http://xenserver.org/partners/developing-products-for-xenserver/18-sdk-development/96-xs-dev-rrds.html
+
+  # select the AVERAGE values
+  granularity = {0:0, 1:1, 2:4, 3:7}[granularity]
   xapi = @getXAPI host

   [response, body] = yield $request {


Require it

Require it

--- server/code/model/box.coffee
+++ server/code/model/box.coffee
@@ -3,6 +3,7 @@
 async = require 'async'
 nibbler = require 'nibbler'
 request = require 'request'
+randtoken = require 'rand-token'

 Schema = mongoose.Schema


Set ignoreInCount when moving message to Trash

--- server/models/message.coffee
+++ server/models/message.coffee
@@ -633,7 +633,9 @@
         mailboxIDs = {}
         mailboxIDs[key] = value for key, value of @mailboxIDs or {}
         mailboxIDs[box.id] = uid
-        @updateAttributes {mailboxIDs}, (err) ->
+        changes = {mailboxIDs}
+        changes.ignoreInCount = true if box.ignoreInCount()
+        @updateAttributes changes, (err) ->
             callback err, {shouldNotif: false, actuallyAdded: true}

     # Public: helper to check if a message is in a box


unbind test and fix

unbind test and fix

--- src/bindable.coffee
+++ src/bindable.coffee
@@ -36,7 +36,7 @@
     eventCallbacks[event] = eventCallbacks[event] || []

     if callback
-      eventCallbacks.remove(callback)
+      eventCallbacks[event].remove(callback)
     else
       eventCallbacks[event] = []


Fix typo.

Fix typo.

--- app/initialize.coffee
+++ app/initialize.coffee
@@ -10,7 +10,7 @@
 ]

 folderOrder.forEach (folder) ->
-  # Go through the prefixes in order and rquire them
+  # Go through the prefixes in order and require them
   window.require.list().filter((module) ->
     new RegExp("^#{folder}/").test(module)
   ).forEach((module) -> require(module))


removed console.log

removed console.log

--- server/routes/reports.coffee
+++ server/routes/reports.coffee
@@ -10,11 +10,10 @@
 exports.show = (req, res) ->
   reportId = req.params.id
   Report.findFatReport(reportId).success((reportData)->
-    console.log reportData
     res.render "reports/show",
       reportData: JSON.stringify reportData
   ).error((error) ->
-    console.log error
+    console.error error
     res.render "404"
   )

@@ -27,10 +26,9 @@
 exports.present = (req, res) ->
   reportId = req.params.id
   Report.findFatReport(reportId).success((reportData)->
-    console.log reportData
     res.render "reports/present",
       reportData: JSON.stringify reportData
   ).error((error) ->
-    console.log error
+    console.error error
     res.render "404"
   )


city needs to be encoded before concat

city needs to be encoded before concat

--- src/weather.coffee
+++ src/weather.coffee
@@ -15,11 +15,11 @@
     value - 273.15

   @getCurrent: (city, callback) ->
-    @_getJSON "http://openweathermap.org/data/2.1/find/city?q=#{city}&cnt=1", (data) =>
+    @_getJSON "http://openweathermap.org/data/2.1/find/city?q=#{encodeURIComponent city}&cnt=1", (data) =>
       callback new Weather.Current(data)

   @getForecast: (city, callback) ->
-    @_getJSON "http://openweathermap.org/data/2.1/forecast/city?q=#{city}&cnt=1", (data) =>
+    @_getJSON "http://openweathermap.org/data/2.1/forecast/city?q=#{encodeURIComponent city}&cnt=1", (data) =>
       callback new Weather.Forecast(data)

   #


Onboarding: Implemented missing appStorage feature for onboarding items.

--- client/Main/onboarding/onboardingcontroller.coffee
+++ client/Main/onboarding/onboardingcontroller.coffee
@@ -4,25 +4,47 @@

     super options, data

-    @items = {}
-    @fetchItems()
+    @onboardings   = {}
+    mainController = KD.getSingleton "mainController"
+    @appStorage    = KD.getSingleton("appStorageController").storage "OnboardingStatus", "1.0.0"

-    mainController = KD.getSingleton "mainController"
+    @fetchItems()  if KD.isLoggedIn()
+
     mainController.on "accountChanged.to.loggedIn", @bound "fetchItems"

+    @on "OnboardingShown", (slug) =>
+      @appStorage.setValue slug, yes
+
   fetchItems: ->
-    return  unless KD.isLoggedIn()
     query = partialType: "ONBOARDING"
-    KD.remote.api.JCustomPartials.some query, {}, (err, onboarding) =>
-      @items[item.name] = item.partial  for item in onboarding
-      @bindOnboardingEvents()
+    KD.remote.api.JCustomPartials.some query, {}, (err, onboardings) =>
+
+      for data in onboardings when data.partial
+        appName = data.partial.app
+        @onboardings[appName] = []  unless @onboardings[appName]
+        @onboardings[appName].push data
+
+      @appStorage.fetchStorage (storage) =>
+        @bindOnboardingEvents()

   bindOnboardingEvents: ->
     appManager = KD.getSingleton "appManager"
     appManager.on "AppCreated", (app) =>
+      appName = app.getOptions().name
+      return unless @onboardings[appName]
+
       KD.utils.wait 3000, =>
-        appName    = app.getOptions().name
-        onboarding = @items[appName]
-        return  unless onboarding?.items.length
+        onboardings = @onboardings[appName]
+        onboarding  = null

-        new OnboardingViewController { app }, onboarding
+        for item in onboardings
+          slug    = KD.utils.slugify KD.utils.curry appName, item.name
+          isShown = @appStorage.getValue slug
+
+          unless isShown
+            onboarding = item
+            break
+
+        return unless onboarding?.partial.items?.length
+
+        new OnboardingViewController { app, slug, delegate: this }, onboarding.partial


Client: Teams: Added getNewInvitations method. It splits newInvitations from allInvites.

--- client/admin/lib/views/invitations/invitesomeoneview.coffee
+++ client/admin/lib/views/invitations/invitesomeoneview.coffee
@@ -220,6 +220,14 @@

         resolve { pendingInvitations }

+
+  getNewInvitations: (allInvites, pendings) ->
+
+    pendingEmails  = pendings.map (item) -> item.email
+    newInvitations = (invite for invite, i in allInvites when invite.email not in pendingEmails)
+
+    return newInvitations
+
     remote.api.JInvitation.create invitations: invites, (err) =>
       if err
         return new KDNotificationView


app-settings-menu: Added app settings menu button support for internal Koding apps like WebTerm and Ace.

--- client/app/MainApp/maincontroller/mainviews/mainview.coffee
+++ client/app/MainApp/maincontroller/mainviews/mainview.coffee
@@ -92,8 +92,8 @@
       cssClass : "kdtabhandlecontainer"
       delegate : @

-    @mainSettingsMenuButton = @getMainSettingsMenuButton()
-    @mainSettingsMenuButton.hide()
+    @appSettingsMenuButton = new AppSettingsMenuButton
+    @appSettingsMenuButton.hide()

     @mainTabView = new MainTabView
       domId              : "main-tab-view"
@@ -104,15 +104,20 @@
     ,null

     @mainTabView.on "PaneDidShow", =>
-      appManifest = getFrontAppManifest()
-      @mainSettingsMenuButton[if appManifest?.menu then "show" else "hide"]()
+      appManifest = KD.getSingleton("appManager").getFrontAppManifest()
+      menu = appManifest?.menu or appManager.get(@mainTabView.getActivePane()?.name)?.getOptions().menu
+      if menu
+        @appSettingsMenuButton.setData menu
+        @appSettingsMenuButton.show()
+      else
+        @appSettingsMenuButton.hide()

     @mainTabView.on "AllPanesClosed", ->
       @getSingleton('router').handleRoute "/Activity"

     @contentPanel.addSubView @mainTabView
     @contentPanel.addSubView @mainTabHandleHolder
-    @contentPanel.addSubView @mainSettingsMenuButton
+    @contentPanel.addSubView @appSettingsMenuButton

   createSideBar:->


Dragging offset fix.

Dragging offset fix.

--- packages/pixelartacademy-pixelboy-drawing/editor/theme/school/references/references.coffee
+++ packages/pixelartacademy-pixelboy-drawing/editor/theme/school/references/references.coffee
@@ -106,3 +106,13 @@

         # Activate hide mode when nearing tray.
         @references.hideActive not @references.opened() and Math.abs(draggingPosition.x) < @trayWidth / 2 and draggingPosition.y + @parentOffset.top / displayScale - halfHeight < @trayHideActiveHeight
+
+    onMouseDown: (event) ->
+      super
+
+      # Parent offset will be relative to PixelBoy viewport so we need to remove it.
+      $pixelBoy = $('.pixelartacademy-pixelboy-os').eq(0)
+      pixelBoyOffset = $pixelBoy.offset()
+
+      @parentOffset.left -= pixelBoyOffset.left
+      @parentOffset.top -= pixelBoyOffset.top


Much better

Much better

--- client/landing/app/MainApp/sidebar/modals/adminmodal.coffee
+++ client/landing/app/MainApp/sidebar/modals/adminmodal.coffee
@@ -44,15 +44,13 @@
                   diameter    : 12
                 callback      : =>
                   form = @modalTabs.forms["Migrate Kodingen Users"]
-                  button = form.buttons.Migrate
-                  status = form.inputs.statusInfo
-                  status.updatePartial 'Working on it...'
+                  form.inputs.statusInfo.updatePartial 'Working on it...'
                   KD.remote.api.JOldUser.__migrateKodingenUsers
                     limit     : parseInt form.inputs.Count.getValue(), 10
                     delay     : parseInt form.inputs.Delay.getValue(), 10
                   , (err, res)->
-                    button.hideLoader()
-                    status.updatePartial res
+                    form.buttons.Migrate.hideLoader()
+                    form.inputs.statusInfo.updatePartial res
                     console.log res, err
               Stop            :
                 title         : "Stop"
@@ -62,14 +60,11 @@
                   diameter    : 12
                 callback      : =>
                   form = @modalTabs.forms["Migrate Kodingen Users"]
-                  buttonM = form.buttons.Migrate
-                  buttonS = form.buttons.Stop
-                  status = form.inputs.statusInfo
-                  status.updatePartial 'Trying to stop...'
+                  form.inputs.statusInfo.updatePartial 'Trying to stop...'
                   KD.remote.api.JOldUser.__stopMigrate (err, res)->
-                    buttonS.hideLoader()
-                    buttonM.hideLoader()
-                    status.updatePartial res
+                    form.buttons.Stop.hideLoader()
+                    form.buttons.Migrate.hideLoader()
+                    form.inputs.statusInfo.updatePartial res
                     console.log res, err
             fields            :
               Information     :


mongo import debug query limit removed

mongo import debug query limit removed

--- data/imports/aiddata.coffee
+++ data/imports/aiddata.coffee
@@ -12,13 +12,14 @@
 pg = require 'pg'
 pgurl = dbconf.postgres

-DEBUG_updateRowsLimit = 10 #null  # set for debugging
+DEBUG_updateRowsLimit =  null  # set for debugging
 OMIT_NULL_VALUE_FIELDS_IN_COMMITMENTS_JSON = true

 PATH = "data/imports/"
 TEMP_DIR = "temp"
 AIDDATA_TEMP_FILE = PATH + TEMP_DIR + "/aiddata.json"
 LOCATIONS_TEMP_FILE = PATH + TEMP_DIR + "/locations.json"
+IMPORT_COMMITMENT_DATES_AS_DATES = true

 USE_MONGOIMPORT = true

@@ -306,18 +307,24 @@
     os.mkdir TEMP_DIR
     fd = fs.openSync(AIDDATA_TEMP_FILE, 'w')

-    epochMillis = (field, q) -> "EXTRACT(EPOCH FROM #{q})*1000 AS #{field}"
-
-    dateFields = [
-      epochMillis("date", "COALESCE(commitment_date, start_date, to_timestamp(to_char(year, '9999'), 'YYYY'))"),
-      epochMillis("start_date", "start_date"),
-      epochMillis("commitment_date", "commitment_date"),
-      epochMillis("end_date", "end_date"),
-    ].join(',')
+    dateFieldQ = (field, q) ->
+      if IMPORT_COMMITMENT_DATES_AS_DATES
+        "EXTRACT(EPOCH FROM #{q})*1000 AS #{field}"
+      else
+        "#{q} AS #{field}"
+
+    dateFields =
+      "date" : "COALESCE(commitment_date, start_date, to_timestamp(to_char(year, '9999'), 'YYYY'))"
+      "start_date" : "start_date"
+      "commitment_date" : "commitment_date"
+      "end_date" : "end_date"

+    dateFieldsSelect = (dateFieldQ(f,q) for f,q of dateFields).join(',')
+
+
     query = pgclient.query "
         SELECT
-          *, #{dateFields}
+          *, #{dateFieldsSelect}
         FROM
           aiddata2
         #{if DEBUG_updateRowsLimit? then 'LIMIT '+DEBUG_updateRowsLimit}
@@ -333,9 +340,10 @@
       row.origin = locationNameToCode[row.donor]
       row.dest = locationNameToCode[row.recipient]

-      for f in ["date_coalesced", "start_date", "commitment_date", "end_date"]
-        if row[f]?
-          row[f] = { "$date" : row[f] }
+      if IMPORT_COMMITMENT_DATES_AS_DATES
+        for f,q of dateFields
+          if row[f]?
+            row[f] = { "$date" : row[f] }

       delete row.donor
       delete row.donorcode
@@ -367,62 +375,6 @@
     console.log "> Upserting commitments in MongoDB"

     importCollectionToMongo  "aiddata", AIDDATA_TEMP_FILE, ["aiddata_id"], callWhenEnded
-
-    ###
-    if USE_MONGOIMPORT   # faster
-
-      os.run "/usr/bin/mongoimport " +
-                  "-d aiddata -c aiddata --upsert --upsertFields aiddata_id " +
-                  "-u #{dbconf.mongodb.user} -p #{dbconf.mongodb.password} "+
-                  "#{file}",
-        (err) ->
-          unless err?
-            fs.unlink(AIDDATA_TEMP_FILE)
-
-          callWhenEnded(err)
-
-    else
-
-      import
-
-      mongodb.collection 'aiddata', (err, coll) =>
-        if err? then callWhenEnded(err)
-        else
-
-          reader = linereader.open AIDDATA_TEMP_FILE
-
-          nextRow = ->
-            if reader.hasNextLine()
-              JSON.parse reader.nextLine()
-            else
-              null
-
-
-          # update synchronously
-          upsert = do ->
-            upsertCount = 0
-            (row) ->
-              unless row?
-                callWhenEnded()
-                console.log "Upserted #{upsertCount} documents"
-              else
-                coll.update(
-                  { aiddata_id:row.aiddata_id }, row, { safe:true, upsert:true },
-                  (err, docs) ->
-                    if err?
-                      console.log "Upserted #{upsertCount} documents"
-                      console.warn "Problem upserting aiddata_id: #{row.aiddata_id}: #{err}"
-                      callWhenEnded(err)
-                    else
-                      upsertCount++
-                      if ((upsertCount % 10000) is 0) then console.log "Upsert count: #{upsertCount}"
-                      upsert nextRow()   # proceed to next row
-                  , true
-                )
-          upsert nextRow()
-    ###
-
-


changed text area overlay to cover the whole terminal screen

--- client/app/Applications/WebTerm.kdapplication/AppView.coffee
+++ client/app/Applications/WebTerm.kdapplication/AppView.coffee
@@ -132,6 +132,7 @@

   click: ->
     @setKeyView()
+    @textarea?.remove()

   keyDown: (event) ->
     @terminal.keyDown event
@@ -156,24 +157,35 @@
         selectedText = document.getSelection()
     else if document.selection
         selectedText = document.selection.createRange().text
-    textarea = $(document.createElement("textarea"))
-    textarea.css
+
+    @textarea?.remove()
+    @textarea = $(document.createElement("textarea"))
+    @textarea.css
       position  : "absolute"
       opacity   : 0
-      width     : "30px"
-      height    : "30px"
-      top       : eventData.offsetY-10
-      left      : eventData.offsetX-10
-    @$().append textarea
+      # width     : "30px"
+      # height    : "30px"
+      # top       : eventData.offsetY-10
+      # left      : eventData.offsetX-10
+      top         : 0
+      left        : 0
+      right       : 0
+      bottom      : 0
+    @$().append @textarea
+
+    # remove on any of these events
+    @textarea.on 'copy cut paste', (event)=>
+      @utils.wait 1000, => @textarea.remove()
+      yes

     if selectedText
-      textarea.val(selectedText.toString())
-      textarea.select()
-    textarea.focus()
-
-    #remove 5sec later
-    @utils.wait 5000, =>
-      textarea.remove()
+      @textarea.val(selectedText.toString())
+      @textarea.select()
+    @textarea.focus()
+
+    #remove 15sec later
+    @utils.wait 15000, =>
+      @textarea?.remove()

   _windowDidResize: (event) ->
     @terminal.windowDidResize()


app: Don't try to fetch integration account

--- client/app/lib/components/profile/profilefetchermixin.coffee
+++ client/app/lib/components/profile/profilefetchermixin.coffee
@@ -3,15 +3,27 @@
 fetchAccount = require 'app/util/fetchAccount'

 module.exports =
+
+
   getInitialState: ->
+
+    account = if @props.origin?.isIntegration
+    then @props.origin
+    else @props.account
+
     return {
-      account: @props.account or helper.defaultAccount()
+      account: account or helper.defaultAccount()
     }

+
   componentDidMount: ->
+
     if @props.origin
-      fetchAccount @props.origin, (err, account) =>
-        @setState { err, account }
+      if @props.origin.isIntegration
+        @setState { account: @props.origin }
+      else
+        fetchAccount @props.origin, (err, account) =>
+          @setState { err, account }

   renderChildren: ->


Client: Activitt: Tests: Added documentation for propTypes

--- client/app/lib/components/sidebarlist/sidebarlistitem.coffee
+++ client/app/lib/components/sidebarlist/sidebarlistitem.coffee
@@ -6,6 +6,8 @@

 module.exports = class SidebarListItem extends React.Component

+  # title prop can be any type. SidebarChannelsListItem pass a string but
+  # SidebarMessagesListItem can pass an object.
   @propTypes =
     title   : React.PropTypes.any
     href    : React.PropTypes.string


MOAR changes

MOAR changes

--- client/landing/site.hackathon/coffee/home/AppViewFull.coffee
+++ client/landing/site.hackathon/coffee/home/AppViewFull.coffee
@@ -372,9 +372,9 @@

How does it work?

First, sign up using the simple form above. Every day (up until we stop accepting
-        applications) between 4-5pm PST, we send you a short questionnaire that will help us
-        understand a little bit more about you and your team’s background. We promise to keep
-        it short and sweet.
+        applications on Dec 3rd, 2014 12 noon PST) between 4-5pm PST, we will send all new
+        applicants a short questionnaire that will help us learn a little bit more about
+        you and your team’s background. We promise to keep it short and sweet.

Applications can come from individuals or teams of up to 5. Only one member per team
@@ -390,11 +390,15 @@
         developer looking for a backend database team member. Once you get a response, jump into
         private chats on Koding itself to discuss your ideas and start recruiting!

+
+

+        Prizes (described below) are in three different categories so it does not matter if you
+        are an expert, a student or a beginner at programming. Everyone can compete and win!

How do I get accepted?

We expect a lot of you to apply but to keep things sane, we will be limiting the
-        final competition to 1000 teams. Our panel of judges will decide who will be part
+        final competition to 1000 2000 teams. Our panel of judges will decide who will be part
         of the competition. Their decision will be based on factors like location, team size,
         project work on github, social presence, etc. Applications are being reviewed on a first
         come first serve basis so apply today and if approved, you will receive an email from
@@ -403,34 +407,80 @@

What is the theme for the Hackathon?

-
We will provide a few themes for the hackathon. Your task
-        will be to use publicly available resources (APIs, data sets, graphics, etc.) and your imagination
-        to create a project that addresses one of the the themes of the event. You can expect the themes
-        to revolve around topics like: Global finance, Education, Healthcare, Climate change, Travel, etc.
-

-
On Nov 21st, the hackathon themes will be announced on the
-        #hackathon channel on Koding.
-        We will also email the themes to all teams that are
-        accepted into the event.

+

+        The following themes have been selected:
+

+
Problems facing our planet, explained using interactive data visualization.
+        	(e.g. climate change, earthquakes, food/water waste, etc.)

+
Introducing software development to a beginner (games!)

+
No one reads the fine print (ie TOS, EULA, legal documents) anymore yet every
+        	site has them. Devise a creative/interactive solution.

+
HTML5 games that are educational and learning oriented. (multiplayer preferred)

+
Challenges associated with real time communication and translation (Star
+        	Trek universal translation anyone?)

+

+        Your task is to use publicly available resources (APIs, data sets, graphics, etc.) and
+        your imagination to create a project that addresses one of the themes of the event.
+

What are the prizes?

Total prizes over $#{prize.toLocaleString()}! Here are the details.

-

-
$10,000 cash prize from Koding, split amongst the top 3 teams ($5k, $3k, $2k each).

-
Winners will be offered credits for services like DigitalOcean, Tutum and many more!

-
Winners will be offered interviews with investors on our judging panel.

-

-

+
Prizes will be awarded in three categories:

+		Top Hacker:
+

+
First prize:  <s>$5,000</s> $8,000

+
Second prize: <s>#3,000</s> $4,000

+
Third prize:  <s>$2,000</s> $2,000

+

+		Student Hacker:
+

+
First prize: $1,500

+
Second prize: $1,000

+
Third prize: $500

+

+		Apprentice Hacker:
+

+
First prize: $1,200

+
Second prize: $800

+
Third prize: $500

+

+

Additional Prizes

+
These will be presented to the #1 winning team in the "Top Hacker" prize category

-
-
Turkcell is contributing $5,000 to the total prize money!

-

-

+
+
Count.ly will award an extra $1,000 to the winning team if they have a Countly account so register today!

+

+

+

+

+
+
nginx is adding an additional $2,000 to the overall prize money for the top winning team if the team uses their web server as part of their final project.

+

+

+

+

+
+
If the winning team has an oDesk.com or Elance.com account, they will receive an extra $2,500. Register today if you don't have an account with them.
+

+

+

+

+

+
+
The top winning team will receive $5,000 in account credit from Digital Ocean. and a one year service account credit from Tutum.

+

+

+

+

+

+
These prizes can be won by any team, even if you don't win the top 3 spots in the "Top Hacker" prize
+		category.

+

@@ -442,26 +492,7 @@

Count.ly will award an extra $1,000 to the winning team if they have a Countly account.

-

-

-

-
-
nginx is adding an additional $2,000 to the overall prize money for teams that use their web server as part of their final project  .

-

-

-

-

-
-
The top winning team will receive $5,000 in account credit from Digital Ocean.

-

-

-

-

-
-
The top-performing team that has an oDesk.com or Elance.com account will receive an extra $2,500.
-              Be sure to add your oDesk or Elance userID to the questionnaire that we send you.

-

-

+

@@ -470,18 +501,12 @@

-
-
PayPal is sponsoring an additional $2,500 in prize money. Details on how to win this additional amount are coming soon.

-

-

-

-

3Scale will offer an additional $1,500 to the team that makes the best usage of any API on thier site.

-
DATADOG is adding $2,500 in grand prize money and an additional $1,000 for the team that makes the best use of their service and their API.

+
DATADOG will award $1,000 to the team that makes the best use of their service and their API.

@@ -491,12 +516,12 @@

-
Sprintly will offer an additional $1,000 to the team that 1) has a Sprintly account, 2) uses their API.

+
Sprintly will offer $1,000 to the team that 1) has a Sprintly account, 2) uses their API.

-
APITools will offer an additional $1,500 to the team that uses APItools in the creation of thier project.

+
APITools will offer $1,500 to the team that uses APItools in the creation of thier project.

@@ -505,8 +530,8 @@

What is the schedule?

Now - Registration open.

-
#{closeDay}, 1200 PST - Applications are closed. Final notifications are sent to teams/individuals who were accepted and the
-          Hackathon Theme is announced (on the #hackathon channel on Koding and via email to the accepted teams).

+
#{closeDay}, 1200 PST - Applications are closed. Final notifications are sent to teams/individuals
+          who are accepted

#{firstDay} - Day 1 Let the hacking begin!

#{secondDay} - Day 2 of the Hackathon

@@ -514,7 +539,7 @@

2330 – 2359 PST Teams submit their projects to hackathon@koding.com (see section below on how to submit)

-
#{emailDay} - Winners are notified via email and winning projects are listed for public viewing.

+
#{emailDay} - Winners are notified and winning projects are listed for public viewing.

@@ -544,6 +569,13 @@

 What are the rules?

+

+
You (or someone from your team) must be approved by Koding and should have our acceptance email.

+
Go to this repo on GitHub and
+			follow the instructions presented there.

+
All team members must have a Koding account.

+
All project developed must be directly aligned with one of the themes of the event.

+

Our goal is to ensure that all teams have a level playing field; therefore it is
         imperative that all code, design, assets, etc… must be created during the duration
         of the event. No exceptions. You can brainstorm ideas prior to the event, however
@@ -571,6 +603,7 @@

Send the following details to hackathon@koding.com

Koding VM URL where the judges can see your project.

+
URL to the team in the hackathon Git repo

A brief description of your project (not more than 250 words).

A brief introduction to your team members (who did what).

How your project addresses the theme of the hackathon.


fix tgLightboxAddMembersWarningMessage directive decorator name

--- coffee/lightbox-add-members-warning-message.directive.coffee
+++ coffee/lightbox-add-members-warning-message.directive.coffee
@@ -29,4 +29,4 @@

     return $delegate

-window.addDecorator("tgLightboxAddMembersWarningMessage", decorator)
+window.addDecorator("tgLightboxAddMembersWarningMessageDirective", decorator)


Allow personal VMs in Koding group.

Allow personal VMs in Koding group.

--- client/app/MainApp/VirtualizationController.coffee
+++ client/app/MainApp/VirtualizationController.coffee
@@ -115,7 +115,7 @@
         style    : "modal-clean-gray"
         callback : => @createGroupVM 'personal', vmCreateCallback

-    if "owner" in KD.config.roles
+    if "owner" in KD.config.roles or "admin" in KD.config.roles
       content = """You can create a Personal or Shared VM for
                    #{group}. If you prefer to create a shared VM, all
                    members in #{group} will be able to use that VM.
@@ -124,7 +124,7 @@
         style      : "modal-clean-gray"
         callback   : => @createGroupVM 'shared', vmCreateCallback

-    else if "member" in KD.config.roles and group isnt 'koding'
+    else if "member" in KD.config.roles
       content = """You can create a Personal VM in #{group}."""

     else


fix initReport bug

fix initReport bug

--- client/views/common/shiftReport/shiftReport.coffee
+++ client/views/common/shiftReport/shiftReport.coffee
@@ -40,7 +40,9 @@
 	shiftId = FlowRouter.getQueryParam('showShiftReport')

 	@autorun ->
-		handle = ShiftSubs.subscribe 'shift', FlowRouter.getQueryParam('showShiftReport')
+		shiftId = FlowRouter.getQueryParam('showShiftReport')
+
+		handle = ShiftSubs.subscribe 'shift', shiftId
 		handle.ready Tracker.afterFlush ->
 			$('#shiftReportModal').modal('show')
 			$('#shiftReportModal').on 'hidden.bs.modal', ->
@@ -50,7 +52,10 @@
 					showShift: shiftId
 				$('.skipping').addClass('animated').removeClass('skipping')

-			Meteor.call 'initReport', FlowRouter.getQueryParam('showShiftReport'), FlowRouter.getQueryParam('reportTeamId')
+			teamId = FlowRouter.getQueryParam('reportTeamId')
+
+			if shiftId && teamId
+				Meteor.call 'initReport', shiftId, teamId

 Template.shiftReport.onRendered ->


Fixed validation

Fixed validation

--- app/assets/javascripts/quick_idea.js.coffee
+++ app/assets/javascripts/quick_idea.js.coffee
@@ -9,7 +9,7 @@
     @updateUI()

   canSubmit: =>
-    _.any(@ui.titleField.val()) && _.any(@ui.subtitleField.val())
+    _.any(@ui.titleField.val())

   updateUI: =>
     @ui.submitButton.toggleClass('disabled', !@canSubmit())


refactored createCalendar method to use date object

--- app/coffee/drm-calendar/drm-calendar.coffee
+++ app/coffee/drm-calendar/drm-calendar.coffee
@@ -16,10 +16,11 @@
             self.body = $ 'body'
             self.daysPerWeek = 7
             self.events = []
-            self.today = new Date()
-            self.currentMonth = self.today.getMonth()
-            self.currentYear = self.today.getFullYear()
-            self.currentDate = self.today.getDate()
+            self.now = new Date()
+            self.today =
+                month: self.now.getMonth()
+                date: self.now.getDate()
+                year: self.now.getFullYear()
             self.calendarInnerClass = "drm-calendar-#{self.view}-view"
             self.calendarNav = $ '.drm-calendar-nav'
             self.calendarSelect = $ '.drm-calendar-select'
@@ -376,7 +377,11 @@
                     self.createEvent value

             if self.calendar.length > 0
-                self.createCalendar self.currentMonth, self.currentDate, self.currentYear
+                currentDate =
+                    month: self.today.month
+                    date: self.today.date
+                    year: self.today.year
+                self.createCalendar currentDate

             self.calendar.on 'click', '.drm-calendar-date-prev, .drm-calendar-date-next', ->
                 # skip date forward or backward
@@ -400,7 +405,11 @@

             self.calendar.on 'click', '.drm-calendar-current', ->
                 # go to today's date
-                self.changeCalendar.call @, self.currentMonth, self.currentDate, self.currentYear
+                currentDate =
+                    month: self.today.month
+                    date: self.today.date
+                    year: self.today.year
+                self.changeCalendar.call @, currentDate

             self.calendar.on 'click', '.drm-calendar-select button[type=submit]', (e) ->
                 # go to a specific date
@@ -420,7 +429,7 @@
                     parseInt value, 10

                 # change calendar view
-                self.changeCalendar.call self, currentDate.month, currentDate.date, currentDate.year
+                self.changeCalendar.call self, currentDate

             self.calendar.on 'click', 'button.drm-show-event-form', ->
                 # show add event form
@@ -452,8 +461,16 @@

                 self.createEvent newEvent

-                newMonth = if newEvent.month? then $.inArray newEvent.month, self.months else self.currentMonth
-                if newMonth isnt currentMonth then self.changeCalendar.call @, newMonth, newEvent.eventDate, self.currentYear
+                newMonth = if newEvent.month? then $.inArray newEvent.month, self.months else self.today.month
+
+                if newMonth isnt currentMonth
+                    currentDate =
+                        month: newMonth
+                        date: newEvent.eventDate
+                        year: self.today.year
+
+                    self.changeCalendar.call @, currentDate
+
                 # reset form
                 fields = self.addEventForm.find(':input').not('button[type=submit]').val ''
                 self.clearForm fields
@@ -823,8 +840,13 @@
                     length = weeks.length
                     $.each weeks, (key, value) ->
                         that = $ value
-                        days = if self.view is 'month' then that.find ".#{self.classes.date}" else that.find ".#{self.classes.date}[data-hour='All Day Event']"
+                        days = if self.view is 'month'
+                                that.find ".#{self.classes.date}"
+                            else
+                                that.find ".#{self.classes.date}[data-hour='All Day Event']"
+
                         weekLength = days.length
+
                         if key is 0 and length isnt 1
                             if dayShift <= day then eventDates.push that.find(".#{self.classes.date}[data-day=#{day}]").data('date')
                         else if key is (length - 1) and length isnt 1
@@ -835,7 +857,11 @@
             eventDates

         _addDailyEvents = (events, eventDates) ->
-            days = if self.view is 'month' then calendarInner.find ".#{self.classes.date}" else calendarInner.find ".#{self.classes.date}[data-hour='All Day Event']"
+            days = if self.view is 'month'
+                    calendarInner.find ".#{self.classes.date}"
+                else
+                    calendarInner.find ".#{self.classes.date}[data-hour='All Day Event']"
+
             days.each ->
                 eventDates.push $(@).data 'date'

@@ -904,144 +930,155 @@

     advanceDate: (direction) =>
         calendarInner = @calendar.find "div.#{@calendarInnerClass}"
-        currentMonth = calendarInner.data 'month'
-        currentYear = calendarInner.data 'year'
-        nextYear = currentYear + 1
-        lastYear = currentYear - 1
-        nextMonth = if currentMonth is 11 then 0 else currentMonth + 1
-        lastMonth = if currentMonth is 0 then 11 else currentMonth - 1
-        currentDate = calendarInner.find(".#{@classes.date}").data 'date'
+        currentDate =
+            month: calendarInner.data 'month'
+            date: calendarInner.find(".#{@classes.date}").data 'date'
+            year: calendarInner.data 'year'
+        nextYear = currentDate.year + 1
+        lastYear = currentDate.year - 1
+        nextMonth = if currentDate.month is 11 then 0 else currentDate.month + 1
+        lastMonth = if currentDate.month is 0 then 11 else currentDate.month - 1

         if direction is 'prev'
-            lastDayOfPrevMonth = @getDaysInMonth lastMonth, currentYear
-
-            if currentDate is 1
-                currentDate = lastDayOfPrevMonth
-                currentYear = if currentMonth is 0 then lastYear else currentYear
-                currentMonth = lastMonth
+            lastDayOfPrevMonth = @getDaysInMonth lastMonth, currentDate.year
+
+            if currentDate.date is 1
+                currentDate.date = lastDayOfPrevMonth
+                currentDate.year = if currentDate.month is 0 then lastYear else currentDate.year
+                currentDate.month = lastMonth
             else
-                currentDate = currentDate - 1
+                currentDate.date = currentDate.date - 1

         else if direction is 'next'
-            lastDayOfMonth = @getDaysInMonth currentMonth, currentYear
-
-            if currentDate is lastDayOfMonth
-                currentDate = 1
-                currentYear = if currentMonth is 11 then nextYear else currentYear
-                currentMonth = nextMonth
+            lastDayOfMonth = @getDaysInMonth currentDate.month, currentDate.year
+
+            if currentDate.date is lastDayOfMonth
+                currentDate.date = 1
+                currentDate.year = if currentDate.month is 11 then nextYear else currentDate.year
+                currentDate.month = nextMonth
             else
-                currentDate = currentDate + 1
-
-        if @view is 'date' then @changeCalendar currentMonth, currentDate, currentYear
+                currentDate.date = currentDate.date + 1
+
+        if @view is 'date' then @changeCalendar currentDate

     advanceWeek: (direction) =>
         calendarInner = @calendar.find "div.#{@calendarInnerClass}"
-        currentMonth = calendarInner.data 'month'
-        currentYear = calendarInner.data 'year'
-        nextYear = currentYear + 1
-        lastYear = currentYear - 1
-        nextMonth = if currentMonth is 11 then 0 else currentMonth + 1
-        lastMonth = if currentMonth is 0 then 11 else currentMonth - 1
+        currentDate =
+            month: calendarInner.data 'month'
+            date: 1
+            year: calendarInner.data 'year'
+        nextYear = currentDate.year + 1
+        lastYear = currentDate.year - 1
+        nextMonth = if currentDate.month is 11 then 0 else currentDate.month + 1
+        lastMonth = if currentDate.month is 0 then 11 else currentDate.month - 1

         if direction is 'prev'
             firstDay = calendarInner.find(".#{@classes.date}").first().data 'date'
-            lastDayOfPrevMonth = @getDaysInMonth lastMonth, currentYear
+            lastDayOfPrevMonth = @getDaysInMonth lastMonth, currentDate.year

             if firstDay is 1 and @view is 'week'
-                currentDate = lastDayOfPrevMonth
-                currentYear = if currentMonth is 0 then lastYear else currentYear
-                currentMonth = lastMonth
+                currentDate.date = lastDayOfPrevMonth
+                currentDate.year = if currentDate.month is 0 then lastYear else currentDate.year
+                currentDate.month = lastMonth
             else if firstDay < 7 and @view is 'week'
-                currentDate = firstDay - 1
+                currentDate.date = firstDay - 1
             else if firstDay is 1 and @view is 'date'
-                currentDate = lastDayOfPrevMonth - 6 # 30 - 6 1 24
-                currentYear = if currentMonth is 0 then lastYear else currentYear
-                currentMonth = lastMonth
+                currentDate.date = lastDayOfPrevMonth - 6 # 30 - 6 1 24
+                currentDate.year = if currentDate.month is 0 then lastYear else currentDate.year
+                currentDate.month = lastMonth
             else if firstDay < 7 and @view is 'date'
-                currentDate = lastDayOfPrevMonth - (7 - firstDay) # 31 - (7 - 3) = 27 27  28 - (7 - 6) = 27 6 27
-                currentYear = if currentMonth is 0 then lastYear else currentYear
-                currentMonth = lastMonth
+                currentDate.date = lastDayOfPrevMonth - (7 - firstDay) # 31 - (7 - 3) = 27 27  28 - (7 - 6) = 27 6 27
+                currentDate.year = if currentDate.month is 0 then lastYear else currentDate.year
+                currentDate.month = lastMonth
             else
-                currentDate = firstDay - 7
+                currentDate.date = firstDay - 7

         else if direction is 'next'
             lastDay = calendarInner.find(".#{@classes.date}").last().data 'date'
-            lastDayOfMonth = @getDaysInMonth currentMonth, currentYear
+            lastDayOfMonth = @getDaysInMonth currentDate.month, currentDate.year

             if lastDay is lastDayOfMonth and @view is 'week'
-                currentDate = 1
-                currentYear = if currentMonth is 11 then nextYear else currentYear
-                currentMonth = nextMonth
+                currentDate.date = 1
+                currentDate.year = if currentDate.month is 11 then nextYear else currentDate.year
+                currentDate.month = nextMonth
             else if (lastDay + 7 > lastDayOfMonth) and @view is 'week'
-                currentDate = lastDayOfMonth
+                currentDate.date = lastDayOfMonth
             else if (lastDay + 7 > lastDayOfMonth) and @view is 'date'
-                currentDate = 7 - (lastDayOfMonth - lastDay) # 31 - 29 = 2 then 7 - 2 = 5
-                currentYear = if currentMonth is 11 then nextYear else currentYear
-                currentMonth = nextMonth
+                currentDate.date = 7 - (lastDayOfMonth - lastDay) # 31 - 29 = 2 then 7 - 2 = 5
+                currentDate.year = if currentDate.month is 11 then nextYear else currentDate.year
+                currentDate.month = nextMonth
             else
-                currentDate = lastDay + 7
-
-        if @view is 'date' or @view is 'week' then @changeCalendar currentMonth, currentDate, currentYear
+                currentDate.date = lastDay + 7
+
+        if @view is 'date' or @view is 'week' then @changeCalendar currentDate

     advanceMonth: (direction) =>
+        self = @
         calendarInner = @calendar.find "div.#{@calendarInnerClass}"
-        currentMonth = calendarInner.data 'month'
-        currentYear = calendarInner.data 'year'
-        currentDate = if currentMonth is self.currentMonth then self.currentDate else 1
+        currentDate =
+            month: calendarInner.data 'month'
+            date: if @month is self.today.month then self.today.date else 1
+            year: calendarInner.data 'year'

         if direction is 'prev'
-            currentMonth = if currentMonth is 0 then 11 else currentMonth - 1
-            currentYear = if currentMonth is 11 then currentYear - 1 else currentYear
+            currentDate.month = if currentDate.month is 0 then 11 else currentDate.month - 1
+            currentDate.year = if currentDate.month is 11 then currentDate.year - 1 else currentDate.year
         else if direction is 'next'
-            currentMonth = if currentMonth is 11 then 0 else currentMonth + 1
-            currentYear = if currentMonth is 0 then currentYear + 1 else currentYear
-        @changeCalendar currentMonth, currentDate, currentYear
+            currentDate.month = if currentDate.month is 11 then 0 else currentDate.month + 1
+            currentDate.year = if currentDate.month is 0 then currentDate.year + 1 else currentDate.year
+        @changeCalendar currentDate

     advanceYear: (direction) =>
+        self = @
         calendarInner = @calendar.find "div.#{@calendarInnerClass}"
-        currentMonth = calendarInner.data 'month'
-        currentYear = calendarInner.data 'year'
-        currentDate = if currentMonth is self.currentMonth then self.currentDate else 1
+        currentDate =
+            month: calendarInner.data 'month'
+            year: calendarInner.data 'year'
+
+        currentDate.date = if currentDate.month is self.today.month then self.today.date else 1
+        currentDate.year = if direction is 'prev' then currentDate.year - 1 else currentDate.year + 1

-        if direction is 'prev' then currentYear = currentYear - 1 else currentYear = currentYear + 1
-
-        @changeCalendar currentMonth, currentDate, currentYear
+        @changeCalendar currentDate

     changeCalendarView: (view) =>
-        calendar = $ ".#{@calendarInnerClass}"
-        currentMonth = calendar.data 'month'
-        currentYear = calendar.data 'year'
-        currentDate = if currentMonth is @currentMonth then @currentDate else 1
+        self = @
+        calendarInner = @calendar.find "div.#{@calendarInnerClass}"
+        currentDate =
+            month: calendarInner.data 'month'
+            year: calendarInner.data 'year'
+
+        currentDate.date = if currentDate.month is self.today.month then self.today.date else 1
+
         # update view
         @view = view
-        @changeCalendar currentMonth, currentDate, currentYear
+        @changeCalendar currentDate
         # change calendar class
         @calendarInnerClass = "drm-calendar-#{view}-view"
         @calendarInnerClass

-    changeCalendar: (month, date, year) =>
+    changeCalendar: (currentDate) =>
         self = @
         calendarInner = self.calendar.find "div.#{self.calendarInnerClass}"
         calendarInner.fadeOut(300).queue (next) ->
-            $.when(calendarInner.remove()).then(self.createCalendar(month, date, year))
+            $.when(calendarInner.remove()).then(self.createCalendar(currentDate))
             next()

-    createCalendar: (currentMonth, currentDate, currentYear) =>
+    createCalendar: (currentDate) =>
         self = @
-        nextYear = currentYear + 1
-        lastYear = currentYear - 1
-        nextMonth = if currentMonth is 11 then 0 else currentMonth + 1
-        lastMonth = if currentMonth is 0 then 11 else currentMonth - 1
-        firstDay = self.getDayOfWeek currentMonth, 1, currentYear
+        nextYear = currentDate.year + 1
+        lastYear = currentDate.year - 1
+        nextMonth = if currentDate.month is 11 then 0 else currentDate.month + 1
+        lastMonth = if currentDate.month is 0 then 11 else currentDate.month - 1
+        firstDay = self.getDayOfWeek currentDate.month, 1, currentDate.year
         dayShift = if firstDay is self.daysPerWeek then 0 else firstDay
-        numberDays = self.getDaysInMonth currentMonth, currentYear
-        numberWeeks = self.getWeeksInMonth currentMonth, currentYear
-        prevMonthNumberDays = self.getDaysInMonth lastMonth, currentYear
-        weekInfo = self.getDatesInWeek currentMonth, currentDate, currentYear
+        numberDays = self.getDaysInMonth currentDate.month, currentDate.year
+        numberWeeks = self.getWeeksInMonth currentDate.month, currentDate.year
+        prevMonthNumberDays = self.getDaysInMonth lastMonth, currentDate.year
+        weekInfo = self.getDatesInWeek currentDate.month, currentDate.date, currentDate.year
         datesInWeek = weekInfo.datesInWeek
-        weekNumber = self.getWeekNumber currentMonth, currentDate, currentYear
+        weekNumber = self.getWeekNumber currentDate.month, currentDate.date, currentDate.year
         weekClass = if weekNumber % 2 is 0 then 'even-week' else 'odd-week'
-        day = self.getDayOfWeek currentMonth, currentDate, currentYear
+        day = self.getDayOfWeek currentDate.month, currentDate.date, currentDate.year

         _highlightWeekends = ->
             calendarInner = self.calendar.find "div.#{self.calendarInnerClass}"
@@ -1051,26 +1088,33 @@
             $.each weeks, ->
                 that = $ @
                 $.each weekends, (key, value) ->
-                    weekend = that.find(".#{self.classes.date}[data-day=#{value}]").not ".#{self.classes.muted}, .#{self.classes.today}, .#{self.classes.holiday}"
+                    weekend = that.find(".#{self.classes.date}[data-day=#{value}]")
+                        .not ".#{self.classes.muted}, .#{self.classes.today}, .#{self.classes.holiday}"
                     weekend.addClass self.classes.weekend

-        _highlightCurrentDay = ->
+        _highlightToday = ->
             calendarInner = self.calendar.find "div.#{self.calendarInnerClass}"
             month = calendarInner.data 'month'
             year = calendarInner.data 'year'

-            if month is self.currentMonth and year is self.currentYear
-                calendarInner.find(".drm-date[data-date=#{self.currentDate}]").addClass self.classes.today
-
-        _createMonthView = (currentMonth, currentYear) ->
+            if month is self.today.month and year is self.today.year
+                calendarInner.find(".drm-date[data-date=#{self.today.date}]")
+                    .addClass self.classes.today
+
+        _createMonthView = (currentDate) ->
             _addWeekNumbers = ->
                 weeks = self.calendar.find("div.#{self.calendarInnerClass}").find '.drm-week'

                 $.each weeks, (key, value) ->
                     that = $ value
                     firstDateInWeek = that.find('.drm-date').first().data 'date'
-                    weekNumber = self.getWeekNumber currentMonth, firstDateInWeek, currentYear
-                    if weekNumber % 2 is 0 then that.addClass('even-week') else that.addClass('odd-week')
+                    weekNumber = self.getWeekNumber currentDate.month, firstDateInWeek, currentDate.year
+
+                    if weekNumber % 2 is 0
+                        that.addClass 'even-week'
+                    else
+                        that.addClass 'odd-week'
+
                     that.attr 'data-week', weekNumber

             # create html
@@ -1081,7 +1125,7 @@

             weeks = "<tbody class='#{self.classes.month}'>"
             i = 1
-            currentDate = 1
+            currentDate.date = 1
             l = 1
             prevDays = (prevMonthNumberDays - dayShift) + 1
             nextDates = 1
@@ -1099,27 +1143,30 @@
                         j += 1
                     # start adding cells for the current month
                     while j <= self.daysPerWeek
-                        weeks += "<td class='#{self.classes.date}' data-month='#{currentMonth}' data-date='#{currentDate}' data-year='#{currentYear}' data-day='#{j - 1}'>#{currentDate}</td>"
+                        weeks += "<td class='#{self.classes.date}' data-month='#{currentDate.month}' data-date='#{currentDate.date}'
+                            data-year='#{currentDate.year}' data-day='#{j - 1}'>#{currentDate.date}</td>"
                         j += 1
-                        currentDate += 1
+                        currentDate.date += 1
                 # if we are in the last week of the month we need to add blank cells for next month
                 else if i is numberWeeks
                     while j <= self.daysPerWeek
                         # finish adding cells for the current month
-                        if currentDate <= numberDays
-                            weeks += "<td class='#{self.classes.date}' data-month='#{currentMonth}' data-date='#{currentDate}' data-year='#{currentYear}' data-day='#{j - 1}'>#{currentDate}</td>"
+                        if currentDate.date <= numberDays
+                            weeks += "<td class='#{self.classes.date}' data-month='#{currentDate.month}' data-date='#{currentDate.date}'
+                                data-year='#{currentDate.year}' data-day='#{j - 1}'>#{currentDate.date}</td>"
                         # start adding cells for next month
                         else
                             weeks += "<td class='#{self.classes.muted}' data-day='#{j}'>#{nextDates}</td>"
                             nextDates += 1
                         j += 1
-                        currentDate += 1
+                        currentDate.date += 1
                 else
                     # if we are in the middle of the month add cells for the current month
                     while j <= self.daysPerWeek
-                        weeks += "<td class='#{self.classes.date}' data-month='#{currentMonth}' data-date='#{currentDate}' data-year='#{currentYear}' data-day='#{j - 1}'>#{currentDate}</td>"
+                        weeks += "<td class='#{self.classes.date}' data-month='#{currentDate.month}' data-date='#{currentDate.date}'
+                            data-year='#{currentDate.year}' data-day='#{j - 1}'>#{currentDate.date}</td>"
                         j += 1
-                        currentDate += 1
+                        currentDate.date += 1
                 weeks += '</tr>'
                 i += 1
             weeks += '</tbody></table>'
@@ -1127,17 +1174,17 @@
             calendar = $ '<div></div>',
                 class: self.calendarInnerClass
                 html: weekdays + weeks
-                'data-month': currentMonth
-                'data-year': currentYear
+                'data-month': currentDate.month
+                'data-year': currentDate.year

             heading = $ '<h1></h1>',
                 class: 'drm-calendar-header'
-                text: "#{self.months[currentMonth]} #{currentYear}"
+                text: "#{self.months[currentDate.month]} #{currentDate.year}"

             calendar.appendTo ".#{self.calendarClass}"
             heading.prependTo ".#{self.calendarInnerClass}"

-            _highlightCurrentDay()
+            _highlightToday()
             _highlightWeekends()
             _addWeekNumbers()

@@ -1153,28 +1200,28 @@
             $('.drm-calendar-week-prev, .drm-calendar-week-next').hide()
             $('.drm-calendar-date-prev, .drm-calendar-date-next').hide()

-        _createWeekView = (currentMonth, currentDate, currentYear) ->
+        _createWeekView = (currentDate) ->
             _getDates = (datesInWeek, key) ->
                 dates = {}
                 # if its the first week of the month
                 if datesInWeek.length < self.daysPerWeek and datesInWeek[0] is 1
                     if key is firstDay
                         dates.date = 1
-                        dates.month = currentMonth
+                        dates.month = currentDate.month
                     else if key > firstDay
                         dates.date = (key - firstDay) + 1
-                        dates.month = currentMonth
+                        dates.month = currentDate.month
                     else
                         dates.date = prevMonthNumberDays - (firstDay - (key + 1))
                         dates.month = lastMonth
                 else if datesInWeek.length is self.daysPerWeek
                     dates.date = datesInWeek[key]
-                    dates.month = currentMonth
+                    dates.month = currentDate.month
                 # if its the last week of the month
                 else if datesInWeek.length < self.daysPerWeek and datesInWeek[0] isnt 1
                     if key < datesInWeek.length
                         dates.date = datesInWeek[key]
-                        dates.month = currentMonth
+                        dates.month = currentDate.month
                     else
                         dates.date = Math.abs (datesInWeek.length - (key + 1))
                         dates.month = nextMonth
@@ -1189,11 +1236,14 @@
             weekHtml = "<tbody class='#{self.classes.week} #{weekClass}' data-week='#{weekNumber}'>"
             $.each self.hours, (key, value) ->
                 hour = value.name
+
                 weekHtml += "<tr><td><span class='hour'>#{hour}</span></td>"
                 $.each self.days, (key, value) ->
                     dates = _getDates datesInWeek, key
-                    weekHtml += "<td class='#{self.classes.date}' data-month='#{dates.month}' data-date='#{dates.date}' data-year='#{currentYear}' data-day='#{key}' data-hour='#{hour}'></td>"
+                    weekHtml += "<td class='#{self.classes.date}' data-month='#{dates.month}' data-date='#{dates.date}'
+                        data-year='#{currentDate.year}' data-day='#{key}' data-hour='#{hour}'></td>"
                 weekHtml += '</tr>'
+
             weekHtml += '</tbody>'

             weekView = $ '<table></table>',
@@ -1202,21 +1252,32 @@
             calendar = $ '<div></div>',
                 class: self.calendarInnerClass
                 html: weekView
-                'data-month': currentMonth
-                'data-year': currentYear
-
-            weekDates = if datesInWeek.length > 1 then "#{datesInWeek[0]} - #{datesInWeek[datesInWeek.length - 1]}" else "#{datesInWeek[0]}"
+                'data-month': currentDate.month
+                'data-year': currentDate.year
+
+            weekDates = if datesInWeek.length > 1
+                    "#{datesInWeek[0]} - #{datesInWeek[datesInWeek.length - 1]}"
+                else
+                    "#{datesInWeek[0]}"

             heading = $ '<h1></h1>',
                 class: 'drm-calendar-header'
-                text: "#{self.months[currentMonth]} #{weekDates}: Week #{weekNumber} of #{currentYear}"
+                text: "#{self.months[currentDate.month]} #{weekDates}: Week #{weekNumber} of #{currentDate.year}"

             calendar.appendTo ".#{self.calendarClass}"
             heading.prependTo "div.#{self.calendarInnerClass}"

-            $("div.#{self.calendarInnerClass}").find("tbody td[data-month=#{lastMonth}]").addClass(self.classes.muted).removeClass 'drm-date'
-            $("div.#{self.calendarInnerClass}").find("tbody td[data-month=#{nextMonth}]").addClass(self.classes.muted).removeClass 'drm-date'
-            _highlightCurrentDay()
+            $("div.#{self.calendarInnerClass}")
+                .find "tbody td[data-month=#{lastMonth}]"
+                .addClass self.classes.muted
+                .removeClass 'drm-date'
+
+            $("div.#{self.calendarInnerClass}")
+                .find "tbody td[data-month=#{nextMonth}]"
+                .addClass self.classes.muted
+                .removeClass 'drm-date'
+
+            _highlightToday()
             _highlightWeekends()

             $.each self.events, (key, value) ->
@@ -1231,24 +1292,25 @@
             $('.drm-calendar-week-prev, .drm-calendar-week-next').show()
             $('.drm-calendar-date-prev, .drm-calendar-date-next').hide()

-        _createDateView = (currentMonth, currentDate, currentYear) ->
+        _createDateView = (currentDate) ->
             dayListHtml = "<ul class='drm-week drm-day #{weekClass}' data-week='#{weekNumber}'>"
             $.each self.hours, (key, value) ->
                 hour = value.name
-                dayListHtml += "<li class='#{self.classes.date}' data-month='#{currentMonth}' data-date='#{currentDate}' data-year='#{currentYear}' data-day='#{day}' data-hour='#{hour}'><span class='hour'>#{hour}</span></li>"
+                dayListHtml += "<li class='#{self.classes.date}' data-month='#{currentDate.month}' data-date='#{currentDate.date}'
+                    data-year='#{currentDate.year}' data-day='#{day}' data-hour='#{hour}'><span class='hour'>#{hour}</span></li>"
             dayListHtml += '</ul>'

             calendar = $ '<div></div>',
                 class: self.calendarInnerClass
-                'data-month': currentMonth
-                'data-year': currentYear
+                'data-month': currentDate.month
+                'data-year': currentDate.year
                 html: dayListHtml

             headingText =
-                if currentMonth is self.currentMonth and currentDate is self.currentDate and currentYear is self.currentYear
-                    "Today, #{self.days[day]}, #{self.months[currentMonth]} #{currentDate} #{currentYear}"
+                if currentDate.month is self.today.month and currentDate is self.today.date and currentDate.year is self.today.year
+                    "Today, #{self.days[day]}, #{self.months[currentDate.month]} #{currentDate.date} #{currentDate.year}"
                 else
-                    "#{self.days[day]}, #{self.months[currentMonth]} #{currentDate} #{currentYear}"
+                    "#{self.days[day]}, #{self.months[currentDate.month]} #{currentDate.date} #{currentDate.year}"

             heading = $ '<h1></h1>',
                 class: 'drm-calendar-header'
@@ -1270,6 +1332,6 @@
             $('.drm-calendar-date-prev, .drm-calendar-date-next').show()

         switch self.view
-            when 'month' then _createMonthView currentMonth, currentYear
-            when 'week' then _createWeekView currentMonth, currentDate, currentYear
-            when 'date' then _createDateView currentMonth, currentDate, currentYear+            when 'month' then _createMonthView currentDate
+            when 'week' then _createWeekView currentDate
+            when 'date' then _createDateView currentDate


clean todolist controller

clean todolist controller

--- app/controllers/todolists_controller.coffee
+++ app/controllers/todolists_controller.coffee
@@ -18,6 +18,7 @@
             try
                 list.path = JSON.parse list.path
             catch error
+                list.path = null

         send length: todoLists.length, rows: todoLists

@@ -32,13 +33,16 @@
         else if todoList is null
             send error: 'TodoList not found', 404
         else
-            todoList.path = JSON.parse todoList.path
+            try
+                todoList.path = JSON.parse todoList.path
+            catch error
+                todoList.path = null
             @todoList = todoList
             next()
 , only: ['destroy', 'show']

-###*
+###
 # Before each todoList list modification current tree is loaded. If it does not
 # exist it is created.
 ###
@@ -50,8 +54,8 @@
         else
             next()

-    Tree.getOrCreate createTreeCb, only: ['update', 'destroy', 'create']
-
+    Tree.getOrCreate createTreeCb
+, only: ['update', 'destroy', 'create']

 ### Actions ###
@@ -60,7 +64,7 @@
 # Return all todoLists
 ###
 action 'all', ->
-    TodoList.all(returnTodoLists)
+    TodoList.all returnTodoLists

 ###
 # Return a todoList
@@ -76,21 +80,24 @@
 #          Other attributes of a todoList are optionnal (content, tags)
 ###
 action 'create', ->
+
     parent_id = body.parent_id
     parent_id = "tree-node-all" if not parent_id?
     path = Tree.getPath parent_id
-    path.push(body.title)
-    body.path = JSON.stringify(path)
+    path.push body.title
+    body.path = JSON.stringify path
+
     TodoList.create body, (err, todoList) ->
         if err
-            send error: 'TodoList can not be created'
+            send error: 'TodoList cannot be created'
         else
             Tree.addNode todoList, parent_id, (err)->
                 if err
-                    send error: 'TodoList can not be created'
+                    send error: 'Tree cannot be updated'
                 else
-                    todoList.path = JSON.parse(todoList.path)
+                    todoList.path = JSON.parse todoList.path
                     send todoList, 201
+
 ###
 # Update the todoList and tree in case of :
 #   change of the title
@@ -105,47 +112,30 @@
             send success: 'TodoList updated', 200

     dataTreeNode = Tree.dataTree.nodes[params.id]
-    isNewTitle   = body.title? and body.title isnt dataTreeNode.data
-    isNewParent  = body.parent_id? and \
-                        body.parent_id isnt dataTreeNode._parent._id
+    parent_id = body.parent_id
+    title = body.title
+    isNewTitle = title? and title isnt dataTreeNode.data
+
+    isNewParent = parent_id? and parent_id isnt dataTreeNode._parent._id

     if isNewTitle or isNewParent
-        # update the path of the todoList in the tree.
-        # This operation updates the todoList and its children paths and the
-        # tree.
-        Tree.moveOrRenameNode params.id, body.title, body.parent_id, (err) ->
-            newData    = {}
-            isToUpdate = false
-            if isNewTitle
-                newData.title = body.title
-                isToUpdate = true
-            if body.tags
-                newData.tags = body.tags
-                isToUpdate = true
-            if isNewParent
-                newData.parent_id = body.parent_id
-                isToUpdate = true
-            if isToUpdate
-                newData.id = params.id
-
-                list = new TodoList newData
-                list.updateAttributes newData, callback
-            else
-                callback null
+        Tree.moveOrRenameNode params.id, title, parent_id, (err) ->
+            newData = id: params.id
+            newData.title = body.title if isNewTitle
+            newData.tags = body.tags if body.tags
+            newData.parent_id = body.parent_id if isNewParent

-    # neither title nor path is changed, the todoList can be updated directly.
-    else
-        newData    = {}
-        isToUpdate = false
-        if body.tags
-            newData.tags = body.tags
-            isToUpdate = true
-        if isToUpdate
-            newData.id = params.id
             list = new TodoList newData
             list.updateAttributes newData, callback
-        else
-            callback null
+
+    else if body.tags?
+        newData = id: params.id
+        newData.tags = body.tags
+
+        list = new TodoList newData
+        list.updateAttributes newData, callback
+    else
+        callback null

 # Destroy todo list and all tasks linked to that list.
 action 'destroy', ->


changed style of async jobs to map to use underscore which is like we use in the rest of the stack. Also removed a few => binds which are not needed

--- app/coffee/DocManager.coffee
+++ app/coffee/DocManager.coffee
@@ -70,18 +70,17 @@
 			else if !docs?
 				return callback new Errors.NotFoundError("No docs for project #{project_id}")

-			jobs = for doc in docs
-				do (doc) =>
-					(cb) =>
-						logger.log project_id: project_id, doc_id: doc._id, "sending doc to s3"
-						options = buildS3Options(doc.lines, project_id+"/"+doc._id)
-						request.put options, (err, res)->
-							if err? || res.statusCode != 200
-								logger.err err:err, res:res, "something went wrong archiving doc in aws"
-								cb(err)
-							MongoManager.markDocAsArchived doc._id, (error) ->
-								return cb(error) if error?
-								cb()
+			jobs = _.map docs, (doc) ->
+				(cb)->
+					logger.log project_id: project_id, doc_id: doc._id, "sending doc to s3"
+					options = buildS3Options(doc.lines, project_id+"/"+doc._id)
+					request.put options, (err, res)->
+						if err? || res.statusCode != 200
+							logger.err err:err, res:res, "something went wrong archiving doc in aws"
+							cb(err)
+						MongoManager.markDocAsArchived doc._id, (error) ->
+							return cb(error) if error?
+							cb()

 			async.series jobs, callback

@@ -92,21 +91,20 @@
 			else if !docs?
 				return callback new Errors.NotFoundError("No docs for project #{project_id}")

-			jobs = for doc in docs
-				do (doc) =>
+			jobs = _.map docs, (doc) ->
+				(cb)->
 					if !doc.inS3?
-						(cb) => cb()
+						return cb()
 					else
-						(cb) =>
-							logger.log project_id: project_id, doc_id: doc._id, "getting doc from s3"
-							options = buildS3Options(true, project_id+"/"+doc._id)
-							request.get options, (err, res, lines)->
-								if err? || res.statusCode != 200
-									logger.err err:err, res:res, "something went wrong unarchiving doc from aws"
-									cb(err)
-								MongoManager.upsertIntoDocCollection project_id, doc._id.toString(), lines, (error) ->
-									return cb(error) if error?
-									cb()
+						logger.log project_id: project_id, doc_id: doc._id, "getting doc from s3"
+						options = buildS3Options(true, project_id+"/"+doc._id)
+						request.get options, (err, res, lines)->
+							if err? || res.statusCode != 200
+								logger.err err:err, res:res, "something went wrong unarchiving doc from aws"
+								cb(err)
+							MongoManager.upsertIntoDocCollection project_id, doc._id.toString(), lines, (error) ->
+								return cb(error) if error?
+								cb()

 			async.series jobs, callback


spelling mistake

spelling mistake

--- app/coffeescripts/map.coffee
+++ app/coffeescripts/map.coffee
@@ -48,7 +48,7 @@
         $("#trip_route").val directions_hash(directionsDisplay)
         # Distance:
         $("#trip_distance").val directionsDisplay.directions.routes[0].legs[0].distance.value
-        # Google Maps duratiorn
+        # Google Maps duration
         $("#trip_gmaps_duration").val directionsDisplay.directions.routes[0].legs[0].duration.value
         # Display Distance
         $("#trip_distance_display").html directionsDisplay.directions.routes[0].legs[0].distance.text if $("#trip_distance_display")


Add missing return keyword in TermBase constructor

--- drivers/javascript/ast.coffee
+++ drivers/javascript/ast.coffee
@@ -57,7 +57,7 @@
             self.__proto__ = @.__proto__
             return self
         else
-            @
+            return @

     run: (connection, options, callback) ->
         # Valid syntaxes are


Changed leading comment

Changed leading comment

--- src/compiler.coffee
+++ src/compiler.coffee
@@ -421,7 +421,7 @@
       program = new JS.Program block
       program.leadingComments = [
         type: 'Line'
-        value: " Generated by CoffeeScript #{pkg.version}"
+        value: " Generated by EmberScript #{pkg.version}"
       ]
       program
     ]


Fix rebase mistake

Fix rebase mistake

--- app/pages/project/index.cjsx
+++ app/pages/project/index.cjsx
@@ -92,7 +92,11 @@

   fetchProjectData: (ownerName, projectName, user) ->
     @listenToPreferences null
-    @setState({ loading: true })
+    @setState({
+      error: null,
+      loading: true,
+      preferences: null,
+    })

     slug = ownerName + '/' + projectName


reverted the last change. problem is that we keep the same color for one
mentor, but if we have a lot of mentors, these colors are similar.

Have to rethink that

--- app/assets/javascripts/components/kid_mentor_schedules.js.cjsx
+++ app/assets/javascripts/components/kid_mentor_schedules.js.cjsx
@@ -18,7 +18,7 @@
     mentors = @props.mentors
     filteredMentors = _.clone @props.mentors
     index = 0
-    total = Math.min MAX_MENTORS_TO_DISPLAY, _.size @props.mentors
+    total = _.size @props.mentors
     for id, mentor of filteredMentors
       mentor.colors = @getColorsOfMentor total, index
       index++


possible fix for sorting explorative annotations

--- app/assets/javascripts/dashboard/models/dashboard_model.coffee
+++ app/assets/javascripts/dashboard/models/dashboard_model.coffee
@@ -65,10 +65,11 @@

     exploratoryAnnotations = new Backbone.Collection()
     # Display newst first.
+    exploratoryAnnotations.add(@get("exploratoryAnnotations"))
     exploratoryAnnotations.comparator = (a,b) ->
+      console.log(a.get("formattedHash")," : ", a.get("created"), " < ", b.get("created"), " : ", b.get("formattedHash"))
       return a.get("created") < b.get("created")
-    exploratoryAnnotations.add(@get("exploratoryAnnotations"))
-
+    exploratoryAnnotations.sort()

     @set("exploratoryAnnotations", exploratoryAnnotations)


article

article

--- app/services/userData.coffee
+++ app/services/userData.coffee
@@ -3,7 +3,7 @@
 app.factory 'UserData', [
   '$http'
   ($http) ->
-    $http.get('https://')
+    $http.get('http://')
     .success((data) ->
       data)
     .error ((err) ->


cooldown finilazation

cooldown finilazation

--- src/cooldown.coffee
+++ src/cooldown.coffee
@@ -13,7 +13,8 @@
     options.approachBy = 1 unless options.approachBy?

     unless I.cooldowns[name]
-      I.cooldowns[name] = options
+      I.cooldowns[name] =
+

       if options.value?
         I[name] = options.value


ampersandjs-ify and es6-ify group and membership models, fix up associated views

--- src/node_modules/round-overview/round-overview-controller.coffee
+++ src/node_modules/round-overview/round-overview-controller.coffee
@@ -36,6 +36,7 @@

     FixedCostsStore.save(fixedCost).then (saved) ->
       debug("saved", saved)
+      round.fixedCosts.set(fixedCost)
     , (err) ->
       # pass error to xeditable as string
       "Oh noes! :( We had a problem saving, please refresh and try again."


Adds paper-header-panel, paper-input-container and

paper-textarea

--- snippets/paper-elements.cson
+++ snippets/paper-elements.cson
@@ -17,6 +17,14 @@

${2:Content goes here...}

     </paper-scroll-header-panel>
     """
+  "paper header panel":
+    "prefix": "paper-header-panel"
+    "body": """
+    <paper-header-panel>
+      <paper-toolbar>${1:Header}</paper-toolbar>
+
${2:Content goes here...}

+    </paper-header-panel>
+    """
   "paper toolbar":
     "prefix": "paper-toolbar"
     "body": """
@@ -40,3 +48,16 @@
     "body": """
     <paper-button ${1:raised}>${2:button}</paper-button>
     """
+  "paper-input-container":
+    "prefix": "paper-input"
+    "body": """
+    <paper-input-container id="$1">
+      ${2:Input label}
+      <input is="iron-input"$3>
+    </paper--container>
+    """
+  "paper-textarea":
+    "prefix": "paper-textarea"
+    "body": """
+    <paper-textarea id="$1" label="${2:Textarea label}"$3></paper-textarea>
+    """


Select All after inserting a new chapter.

--- src/bootstrap-elm.coffee
+++ src/bootstrap-elm.coffee
@@ -78,7 +78,7 @@
         setUpChapter(newChapter).then (editorHeading, editorBody) ->
           scrollToChapterId newChapter.id
           document.getElementById("edit-chapter-heading-#{newChapter.id}").focus()
-          # TODO now select all!
+          document.execCommand "selectall"

 saveHtmlAndLoadDoc = (html) ->
   sync.saveFreshDoc(DocImport.docFromHtml html).then loadAsCurrentDoc


set default value for writable

set default value for writable

--- src/buffer-stream.coffee
+++ src/buffer-stream.coffee
@@ -35,7 +35,8 @@
         @splitters = []
         @__defineGetter__ 'length', () => @buffer.length
         # states
-        @enabled = on
+        @enabled  = on
+        @writable = off
         @writable = on if @size is 'flexible'
         #init
         @reset()


trending sort order now uses the last activity date as the basis for ranking

--- @client/proposal_sort_and_search.coffee
+++ @client/proposal_sort_and_search.coffee
@@ -181,19 +181,45 @@

       opinion_views = fetch 'opinion_views'

+      filters_for_proposals = {}
+
+      # get last opinion to use as our "trending" stopper
+      last_activity = "0"
+      for prop in proposals
+        opinions = prop.opinions or fetch(prop).opinions or []
+
+        filters_for_proposals[prop.key] =
+          opinions: opinions
+          views: compose_opinion_views opinions, prop, opinion_views
+        {weights, salience, groups} = filters_for_proposals[prop.key].views
+        for o in opinions
+          continue if salience[o.user] < 1 || weights[o.user] == 0
+          if o.updated_at > last_activity
+            last_activity = o.updated_at
+
+        if prop.created_at > last_activity
+          last_activity = prop.created_at
+
+
+      latest_timestamp = new Date(last_activity).getTime()
+      date_filter = fetch('opinion-date-filter')
+      if date_filter.end
+        latest_timestamp = Math.min(date_filter.end, latest_timestamp)
+
       val = (proposal) ->
         if proposal.key not of cache
-          opinions = proposal.opinions or fetch(proposal).opinions or []
-          {weights, salience, groups} = compose_opinion_views opinions, proposal, opinion_views
+          opinions = filters_for_proposals[proposal.key].opinions
+          {weights, salience, groups} = filters_for_proposals[proposal.key].views
           sum = 0
           for opinion in opinions
             continue if salience[opinion.user] < 1 # don't count users who aren't fully salient, they're considered backgrounded
             w = weights[opinion.user] # * salience[opinion.user]
             sum += opinion.stance * w

-          n = Date.now()
           pt = new Date(proposal.created_at).getTime()
-          cache[proposal.key] = sum / (1 + (n - pt) / 10000000000)  # decrease this constant to favor newer proposals
+
+
+          cache[proposal.key] = sum / (1 + (latest_timestamp - pt) / 100)  # decrease this constant to favor newer proposals
         cache[proposal.key]

       proposals.sort (a, b) ->


Iterator warn message removed

Iterator warn message removed

--- iterator.coffee
+++ iterator.coffee
@@ -50,7 +50,7 @@

 		# stop if no data found
 		if not isArray(each) and not (each instanceof List)
-			log.warn "Data is not an array nor List in '#{@text}':\n#{each}"
+			# log.warn "Data is not an array nor List in '#{@text}':\n#{each}"
 			return

 		# set as data


ビーコンによるBOXの出入管理システムプロトタイプの完成

ビーコンによるBOXの出入管理システムプロトタイプの完成

--- scripts/line-beacon-listener.coffee
+++ scripts/line-beacon-listener.coffee
@@ -1,5 +1,4 @@
 module.exports = (robot) ->
-
   robot.router.post "/line-becon", (req, res) ->
     if not req.body
       res.end ""
@@ -8,5 +7,5 @@
     unless data.events?
       res.end ""
       return
-    robot.send {room: "C4LEAQHPW"}, "ビーコンに反応あり 反応の種類#{data.events[0].beacon.type}"
+    robot.send {room: "C5AUJGJJE"}, "ビーコンに反応あり 反応の種類#{data.events[0].beacon.type}"
     res.end ""


Inserting a link while not selecting any text does now insert a _new_ link across all browsers (FF, IE 9/10, Chrome). Fixes issue #110

--- src/plugins/link.coffee
+++ src/plugins/link.coffee
@@ -51,22 +51,22 @@
         widget.options.editable.restoreSelection(widget.lastSelection)
         if isEmptyLink link
           # link is empty, remove it. Make sure the link is selected
-          selectionStart = widget.lastSelection.startContainer
-          if widget.lastSelection.collapsed
-            widget.lastSelection.setStartBefore selectionStart
-            widget.lastSelection.setEndAfter selectionStart
-            window.getSelection().addRange(widget.lastSelection)
           document.execCommand "unlink", null, ""
         else
           # link does not have ://, add http:// as default protocol
           if !(/:\/\//.test link) && !(/^mailto:/.test link)
             link = 'http://' + link
           if widget.lastSelection.startContainer.parentNode.href is undefined
-            document.execCommand "createLink", null, link
+            # we need a new link
+            # following check will work around ie and ff bugs when using
+            # "createLink" on an empty selection
+            if widget.lastSelection.collapsed
+              widget.lastSelection.insertNode jQuery("#{link}")[0]
+            else
+              document.execCommand "createLink", null, link
           else
             widget.lastSelection.startContainer.parentNode.href = link
         widget.options.editable.element.trigger('change')
-        widget.options.editable.removeAllSelections()
         return false

       dialog.find("input[type=submit]").click dialogSubmitCb


Wrap some lines

Wrap some lines

--- resources/assets/coffee/react/beatmapset-page/score.coffee
+++ resources/assets/coffee/react/beatmapset-page/score.coffee
@@ -58,4 +58,8 @@
       hits.values

     div className: 'beatmapset-score__stat beatmapset-score__stat--miss hidden-xs',
-      if props.score.statistics.count_miss > 0 then osu.trans('beatmapsets.show.scoreboard.miss_count', count: props.score.statistics.count_miss) else ''
+      if props.score.statistics.count_miss > 0
+        osu.trans 'beatmapsets.show.scoreboard.miss_count',
+          count: props.score.statistics.count_miss
+      else
+        ''


Number formating and i18n'ed units format

--- src/dropzone.coffee
+++ src/dropzone.coffee
@@ -237,6 +237,14 @@
     # You can use {{maxFiles}} here, which will be replaced by the option.
     dictMaxFilesExceeded: "You can not upload any more files."

+    dictFileTiBUnitFormat: "TiB"
+    dictFileGiBUnitFormat: "GiB"
+    dictFileMiBUnitFormat: "MiB"
+    dictFileKiBUnitFormat: "KiB"
+    dictFileBUnitFormat: "b"
+    dictFileThousandsSeparator: ","
+    dictFileDecimalSeparator: "."
+

     # If `done()` is called without argument the file is accepted
     # If you call it with an error message, the file is rejected
@@ -775,22 +783,30 @@

   # Returns a nicely formatted filesize
   filesize: (size) ->
+    formatNumber = (n, dp, thousandsSep, decimalSep) ->
+      s = "" + (Math.floor(n))
+      d = n % 1
+      i = s.length
+      r = ""
+      r = thousandsSep + s.substr(i, 3) + r  while (i -= 3) > 0
+      s.substr(0, i + 3) + r + ((if d then decimalSep + Math.round(d * Math.pow(10, dp)) else ""))
+
     if      size >= 1024 * 1024 * 1024 * 1024 / 10
       size = size / (1024 * 1024 * 1024 * 1024 / 10)
-      string = "TiB"
+      string = @options.dictFileTiBUnitFormat
     else if size >= 1024 * 1024 * 1024 / 10
       size = size / (1024 * 1024 * 1024 / 10)
-      string = "GiB"
+      string = @options.dictFileGiBUnitFormat
     else if size >= 1024 * 1024 / 10
       size = size / (1024 * 1024 / 10)
-      string = "MiB"
+      string = @options.dictFileMiBUnitFormat
     else if size >= 1024 / 10
       size = size / (1024 / 10)
-      string = "KiB"
+      string = @options.dictFileKiBUnitFormat
     else
       size = size * 10
-      string = "b"
-    "#{Math.round(size)/10} #{string}"
+      string = @options.dictFileBUnitFormat
+    "#{formatNumber(size, 2, @options.dictFileThousandsSeparator, @options.dictFileDecimalSeparator)} #{string}"

   # Adds or removes the `dz-max-files-reached` class from the form.


FIX: Project service addTaskDetailsToProject applying to null array

project.tasks was undefined

--- src/app/common/services/project-service.coffee
+++ src/app/common/services/project-service.coffee
@@ -71,6 +71,7 @@

       base = _.filter base, (task) -> ! _.find(project.tasks, (pt) -> pt.task_definition_id == task.task_definition_id)

+      project.tasks = [] unless project.tasks?
       Array.prototype.push.apply project.tasks, base

     project.tasks = project.tasks.map (task) ->


Fix for download links.

Fix for download links.

--- packages/artificialengines/base/router/router-client.coffee
+++ packages/artificialengines/base/router/router-client.coffee
@@ -143,10 +143,10 @@
       # Do not react if modifier keys are present (the user might be trying to open the link in a new tab).
       return if event.metaKey or event.ctrlKey or event.shiftKey

+      link = event.currentTarget
+
       # Do not act on download links.
-      return if $(event.target).hasAttribute 'download'
-
-      link = event.currentTarget
+      return if link.download

       # Only do soft link changes when we're staying within the same host.
       if link.hostname is location.hostname


Fix false positive PodController test

Fix false positive PodController test

--- karma/test-controllers.coffee
+++ karma/test-controllers.coffee
@@ -511,19 +511,20 @@
         PodApi = new class PodApi
           get: ->

-        p = $q.resolve({foo: 'bar'})
-        spyOn(PodApi, 'get').and.returnValue(p)
+        spyOn(PodApi, 'get').and.returnValue($q.resolve({foo: 'bar'}))

         $controller('PodController', {
-            $scope
-            PodApi
-          })
-
-        $scope.init(21)
-          .then(() ->
-            expect(PodApi.get).toHaveBeenCalledWith(21)
-            expect($scope.pod).toEqual({foo: 'bar'}))
-      )
+          $scope
+          PodApi
+        })
+
+        $scope.init(21, {title: 'Baz'})
+        $scope.$apply()
+
+        expect($scope.podId).toEqual(21)
+        expect($scope.podConfig).toEqual({title: 'Baz'})
+        expect(PodApi.get).toHaveBeenCalledWith(21)
+        expect($scope.podData).toEqual({foo: 'bar'}))
     )
   )
 )


Fixed forced type resolution in non-mutually recursive cases which now works

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -129,7 +129,7 @@
   visitExpressions ast, (expression) ->
     expressions.push "#{collapse toHtml expression} :: #{highlightType expression.tea}" if expression.tea
   types: values mapMap (__ highlightType, _type), subtractMaps ctx._scope(), builtInDefinitions()
-  subs: mapSub highlightType, ctx.substitution
+  subs: subToObject mapSub highlightType, ctx.substitution
   ast: expressions
   deferred: ctx.deferredBindings()

@@ -148,6 +148,13 @@
   typeAst = astize tokenize printType type
   syntaxType typeAst
   collapse toHtml typeAst
+
+subToObject = (sub) ->
+  ob = {}
+  for s, i in sub.vars when s
+    ob["#{i}"] = s
+  ob
+

 _type = (declaration) ->
   declaration.type
@@ -374,7 +381,7 @@
     !!(@_declaration name)

   isTyped: (name) ->
-    !!(@_declaration name).type
+    !!(@_declaration name)?.type

   _declaration: (name) ->
     @_declarationInScope @scopes.length - 1, name
@@ -390,7 +397,7 @@
   assignType: (name, type) ->
     # log "TYPE OF #{name}", printType type
     if declaration = (lookupInMap @_scope(), name)
-      if declaration.type
+      if declaration.type and declaration.type not instanceof TempType
         throw new Error "assignType: #{name} already has a type"
       declaration.type = type
     else
@@ -990,16 +997,17 @@
   for {name, id, type} in definedNames
     currentType = substitute ctx.substitution, type
     deps = ctx.deferredNames()
+
+    # TODO: this is because functions might declare arity before being declared
+    if not ctx.isCurrentlyDeclared name
+      ctx.declare name, id: id
     if deps.length > 0
       # log "adding top level lhs to deferred #{name}", deps
       ctx.addToDeferred {name, type, deps: (map (({name}) -> name), deps)}
       for dep in deps
         ctx.addToDeferred {name: dep.name, type: dep.type, deps: [name]}
-      ctx.declare name, type: (new TempType type), id: id
+      ctx.assignType name, (new TempType type)
     else
-      # TODO: this is because functions might declare arity before being declared
-      if not ctx.isCurrentlyDeclared name
-        ctx.declare name, id: id
       # For explicitly typed bindings, we need to check that the inferred type
       #   corresponds to the annotated
       if ctx.isTyped name
@@ -1066,7 +1074,7 @@
     # First get rid of instances of already resolved types
     unresolvedNames = newMap()
     for name, types of values names
-      if canonicalType = ctx.type name
+      if (canonicalType = ctx.type name) and canonicalType not instanceof TempType
         for type in types
           unify ctx, type.type, (freshInstance ctx, canonicalType).type
       else
@@ -1079,9 +1087,6 @@
         #log type.constructor
         unify ctx, canonicalType.type, type.type
         # log "done unifying one"
-    # log "done unifying"
-    for name of values unresolvedNames
-      # All functions must have been declared already
       ctx.assignType name, quantifyAll substitute ctx.substitution, canonicalType

 compileDeferred = (ctx) ->
@@ -4718,6 +4723,19 @@
   """
   "c", 3

+  'compile before typing due to multiple deferred'
+  """
+    a (f 3)
+    f (fn [x] (h g x))
+    g (fn [x] b)
+    h (fn [y z] (y z))
+    b 4
+    c (f 2)
+  """
+  "a", 4
+
+
+
   # The following doesn't work because the Collection type class specifies
   # that the constructor takes only one argument.
   #
@@ -4792,6 +4810,8 @@
   "Finished"
 # end of tests

+
+
 exports.compileTopLevel = compileTopLevel
 exports.compileExpression = compileExpression
 exports.astizeList = astizeList


Added Infinity constants

Added Infinity constants

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -82,7 +82,7 @@
 constantLabeling = (atom) ->
   {symbol} = atom
   labelMapping atom, [
-    ['numerical', -> /^~?\d+/.test symbol]
+    ['numerical', -> /^~?(\d+|Infinity)/.test symbol]
     ['label', -> isLabel atom]
     ['string', -> /^"/.test symbol]
     ['char', -> /^\\/.test symbol]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/color2.coffee
+++ test/color2.coffee
@@ -155,5 +155,11 @@
   raises ->
     Color2("A Fake Color")

+test "#equals", ->
+  color1 = Color2(255, 255, 255, 1)
+  color2 = Color2([255, 255, 255])
+
+  color1.equal(color2)
+
 module()


Fix printing of flattened

Fix printing of flattened

--- src/js/app/compilers/teascript/compiler.coffee
+++ src/js/app/compilers/teascript/compiler.coffee
@@ -5395,23 +5395,34 @@
     kindedToHtml type, type.name
   else if type.TypeApp
     flatenned = flatten type
-    if flatenned.op.name?.match /^\[\d+\]$/
-      htmlDelimited '[', ']', "#{(map printTypeToHtml, flatenned.args).join ' '}"
-    else
-      opName = if flatenned.op.name is 'Fn0' then (opNameToHtml 'Fn') else printTypeToHtml flatenned.op
-      htmlCalled "#{opName} #{(map printTypeToHtml, flatenned.args).join ' '}"
+    printFlattenedToHtml flatenned
   else if type.ClassConstraint
-    htmlCalled "#{opNameToHtml type.className} #{(map printTypeToHtml, type.types.types).join ' '}"
+    htmlCalled type, "#{opNameToHtml type.className} #{(map printTypeToHtml, type.types.types).join ' '}"
   else if type.Constrained
-    htmlCalled ": #{(map printTypeToHtml, join [type.type], type.constraints).join ' '}"
-  else
+    htmlCalled type, ": #{(map printTypeToHtml, join [type.type], type.constraints).join ' '}"
+  else if type.args
+    printFlattenedToHtml type
+  else
+    console.log type
     throw new Error "Unrecognized type in printTypeToHtml"

-htmlCalled = (string) ->
-  htmlDelimited '(', ')', string
-
-htmlDelimited = (open, close, string) ->
-  (themize 'paren', open) + string + (themize 'paren', close)
+printFlattenedToHtml = (type) ->
+  if type.op.name?.match /^\[\d+\]$/
+    htmlDelimited type, '[', ']', "#{(map printTypeToHtml, type.args).join ' '}"
+  else
+    opName = if type.op.name is 'Fn0' then (opNameToHtml 'Fn') else printTypeToHtml type.op
+    htmlCalled type, "#{opName} #{(map printTypeToHtml, type.args).join ' '}"
+
+htmlCalled = (type, string) ->
+  htmlDelimited type, '(', ')', string
+
+htmlDelimited = (type, open, close, string) ->
+  label =
+    if type.error
+      'malformed'
+    else
+      'paren'
+  (themize label, open) + string + (themize label, close)

 flatten = (type) ->
   if type.TypeApp


add test & better test names

add test & better test names

--- test/hinoki.coffee
+++ test/hinoki.coffee
@@ -1,6 +1,20 @@
 q = require 'q'

 hinoki = require '../src/hinoki'
+
+computationExampleFactories =
+    count: (xs) ->
+        xs.length
+    mean: (xs, n) ->
+        reducer = (acc, x) ->
+            acc + x
+        xs.reduce reducer, 0
+    meanOfSquares: (xs, n) ->
+        reducer = (acc, x) ->
+            acc + x * x
+        xs.reduce reducer, 0
+    variance: (mean, meanOfSquares) ->
+        meanOfSquares - mean * mean

 module.exports =

@@ -34,7 +48,7 @@
                 hinoki.parseFunctionArguments (first, second, third) ->
             test.done()

-    'find with single container':
+    'find with 1 container':

         'not found': (test) ->
             result = hinoki.find [{}], 'id'
@@ -171,13 +185,11 @@

             test.done()

-    'inject with one container':
+    'inject with 1 container':

         'missing factory': (test) ->
-            container = {}
-
             try
-                hinoki.inject container, (a) ->
+                hinoki.inject {}, (a) ->
             catch error
                 test.equals error.message, "missing factory for service 'a'"
                 test.done()
@@ -205,7 +217,7 @@
                 test.equals error.message, "exception in factory 'a': Error: b"
                 test.done()

-        'one seed': (test) ->
+        '1 seed': (test) ->
             container =
                 scope:
                     a: 5
@@ -214,7 +226,7 @@
                 test.equals a, 5
                 test.done()

-        'three dependencies': (test) ->
+        '3 dependencies': (test) ->
             container =
                 factories:
                     a: -> 1
@@ -384,3 +396,15 @@
                 test.deepEqual container3.scope,
                     a: 1
                 test.done()
+
+    'examples':
+
+        'computation - ask for count': (test) ->
+            c =
+                factories: computationExampleFactories
+                scope:
+                    xs: [1, 2, 3, 6]
+
+            hinoki.inject c, (count) ->
+                test.equals count, 4
+                test.done()


Bind tick method as it is called by setInterval.

--- app/assets/javascripts/modules/timelet/models/timelet.js.coffee
+++ app/assets/javascripts/modules/timelet/models/timelet.js.coffee
@@ -33,7 +33,7 @@
   #
   # @return [Boolean] `false` if the timer finished, otherwise `true`
   #
-  tick: ->
+  tick: =>
     @set timer: (@get('timer') - 1)
     @stop() if @isFinished()


In smartphone-sized screens, when collapsing the menu, give focus to the toggle-menu-collapse button in the navbar.

Related to 1d5e4d0910442c69bdad11fd73b2b27852f8e8e8. The only change is which element is focused; the reason is that Android browsers give focused elements a weird orange highlight which looks weird around the brand link in the navbar. This hightlight is not straightforward to remove, and it's simpler to give focus to a related button which does not look so bad.

--- app/assets/javascripts/ng-services/ng-MenuCollapseSvc.js.coffee
+++ app/assets/javascripts/ng-services/ng-MenuCollapseSvc.js.coffee
@@ -16,5 +16,5 @@
   #---------------------------------------------
   close: ->
     $('.navbar-collapse').collapse 'hide'
-    $('#feedbunch-brand').focus()
+    $('#toggle-menu-collapse').focus()
 ]


Now `x` XML namespace is not stringified

--- Element/element/tag/stringify.coffee
+++ Element/element/tag/stringify.coffee
@@ -1,12 +1,7 @@
 'use strict'

-HIDDEN_TAGS =
-	'x:unit': true
-	'x:source': true
-
-HIDDEN_ATTRS =
-	'x:each': true
-	'x:if': true
+isPublic = (name) ->
+	name.indexOf('x:') isnt 0

 getInnerHTML = (elem) ->
 	if elem.visible and elem.children
@@ -21,13 +16,13 @@
 	if elem._text isnt undefined
 		return elem._text

-	if not elem.name or HIDDEN_TAGS[elem.name]
+	if not elem.name or not isPublic elem.name
 		return getInnerHTML elem

 	ret = "<" + elem.name
 	if elem.attrsNames
 		for attrName, i in elem.attrsKeys
-			continue if HIDDEN_ATTRS[attrName]
+			continue unless isPublic attrName
 			attrValue = elem.attrsValues[i]
 			continue if attrValue is undefined