Social: use a generic function for sending requests

--- workers/social/lib/social/models/socialapi/channel.coffee
+++ workers/social/lib/social/models/socialapi/channel.coffee
@@ -53,34 +53,45 @@
   {fetchGroup} = require "./helper"

   @fetchActivities = secure (client, options = {}, callback)->
-    fetchGroup client, (err, group)->
-      return callback err if err
-      {connection:{delegate}} = client
-      delegate.createSocialApiId (err, socialApiId)->
-        return callback err if err
-
-        options.channelId = options.id
-        options.accountId = socialApiId
-        options.groupName = group.slug
-
-        {fetchChannelActivities} = require './requests'
-        fetchChannelActivities options, callback
+    options.channelId = options.id
+    @doRequest 'fetchChannelActivities', client, options, callback

   @fetchPopularTopics = secure (client, options = {}, callback)->
-    fetchGroup client, (err, group)->
-      return callback err if err
-      options.groupName = group.slug
-
-      {fetchPopularTopics} = require './requests'
-      fetchPopularTopics options, callback
+    @doRequest 'fetchPopularTopics', client, options, callback

   @fetchChannels = secure (client, options = {}, callback)->
-    @fetchChannelReqeust 'fetchGroupChannels', client, options, callback
+    @doRequest 'fetchGroupChannels', client, options, callback

   @fetchFollowedChannels = secure (client, options = {}, callback)->
-    @fetchChannelReqeust 'fetchFollowedChannels', client, options, callback
+    @doRequest 'fetchFollowedChannels', client, options, callback

-  @fetchChannelReqeust = (funcName, client, options, callback)->
+  @listPinnedMessages = permit 'pin posts',
+    success: (client, options, callback)->
+      @doRequest 'listPinnedMessages', client, options, callback
+
+  @pinMessage = permit 'pin posts',
+    success:  (client, options, callback)->
+      unless options.messageId
+        return callback {message: "Message id is not set for pinning "}
+      @doRequest 'pinMessage', client, options, callback
+
+  @unpinMessage = permit 'like posts',
+    success:  (client, options, callback)->
+      unless options.messageId
+        return callback {message: "Message id is not set for un-pinning "}
+      @doRequest 'unpinMessage', client, options, callback
+
+  @follow = secure (client, options, callback)->
+    unless options.channelId
+      return callback {message: "Channel id is not set for following a topic"}
+    @doRequest 'followTopic', client, options, callback
+
+  @unfollow = secure (client, options, callback)->
+    unless options.channelId
+      return callback {message: "Channel id is not set for topic unfollowing"}
+    @doRequest 'unfollowTopic', client, options, callback
+
+  @doRequest = (funcName, client, options, callback)->
     fetchGroup client, (err, group)->
       return callback err if err
       {connection:{delegate}} = client
@@ -92,63 +103,3 @@

         requests = require './requests'
         requests[funcName] options, callback
-
-  @listPinnedMessages = permit 'pin posts',
-    success:  (client, options, callback)->
-      {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)->
-        return callback err if err
-        options.accountId = socialApiId
-        options.groupName = context.group
-        {listPinnedMessages} = require './requests'
-        listPinnedMessages options, callback
-
-  @pinMessage = permit 'pin posts',
-    success:  (client, data, callback)->
-      {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)->
-        return callback err if err
-        unless data.messageId
-          return callback {message: "Message id is not set for pinning "}
-
-        data.groupName = context.group
-        data.accountId = socialApiId
-        {pinMessage} = require './requests'
-        pinMessage data, callback
-
-  @unpinMessage = permit 'like posts',
-    success:  (client, data, callback)->
-      {connection:{delegate}, context} = client
-      delegate.createSocialApiId (err, socialApiId)->
-        return callback err if err
-        unless data.messageId
-          return callback {message: "Message id is not set for un-pinning "}
-
-        data.groupName = context.group
-        data.accountId = socialApiId
-        {unpinMessage} = require './requests'
-        unpinMessage data, callback
-
-  @follow = secure (client, data, callback)->
-    {connection:{delegate}, context} = client
-    delegate.createSocialApiId (err, socialApiId)->
-      return callback err if err
-      unless data.channelId
-        return callback {message: "Channel id is not set for following a topic"}
-
-      data.groupName = context.group
-      data.accountId = socialApiId
-      {followTopic} = require './requests'
-      followTopic data, callback
-
-  @unfollow = secure (client, data, callback)->
-    {connection:{delegate}, context} = client
-    delegate.createSocialApiId (err, socialApiId)->
-      return callback err if err
-      unless data.channelId
-        return callback {message: "Channel id is not set for topic unfollowing"}
-
-      data.groupName = context.group
-      data.accountId = socialApiId
-      {unfollowTopic} = require './requests'
-      unfollowTopic data, callback


[master] Restart ability

[master] Restart ability

--- get_comment_count.coffee
+++ get_comment_count.coffee
@@ -91,11 +91,16 @@

 executionDate = moment()

-parseSingleSymbol = (symbol, callback) ->
-  parseSinglePage symbol, 1, executionDate, executionDate, callback
+parseSingleSymbol = (symbol, start, callback) ->
+  debug "symbol #{symbol} starting from page #{start}"
+  parseSinglePage symbol, start, executionDate, executionDate, callback

-_.map symbolList, (i) ->
-  parseSingleSymbol parseInt(i), ->
-    if ++finished == symbolList.length
-      redis.quit()
+redis.hgetall 'progress', (err, obj) ->
+  if err
+    throw err
+  _.map symbolList, (i) ->
+    start = parseInt obj?[i] ? 1
+    parseSingleSymbol parseInt(i), start, ->
+      if ++finished == symbolList.length
+        redis.quit()


Correctly set the time the last update was run in the frontend

--- views/map.coffee
+++ views/map.coffee
@@ -25,7 +25,8 @@
     markerClusterGroup.removeLayers(expiredMarkers)
     map.addLayer(markerClusterGroup)

-    setTimeout((-> loadSessions(map, markers, markerClusterGroup, $.now())), 30000)
+    lastUpdatedAt = $.now()
+    setTimeout((-> loadSessions(map, markers, markerClusterGroup, lastUpdatedAt)), 30000)

 # Markers of sessions that were posted more than 24 hours ago
 @filterExpiredMarkers = (markers) ->


Slightly adjust map zoom and controls

Slightly adjust map zoom and controls

--- views/map.coffee
+++ views/map.coffee
@@ -4,4 +4,6 @@
       lat: 28.3
       lng: 20.8
     zoom: 3
+    disableDefaultUI: true,
+    zoomControl: true
   )


debug log

debug log

--- tests/no-regression.coffee
+++ tests/no-regression.coffee
@@ -561,6 +561,7 @@
         it "And I send a request to search the notes containing dragons", \
                 (done) ->
             Note.search "dragons", (err, notes) =>
+                return done err if err
                 @notes = notes
                 @indexer.close()
                 done()


fix bug where toolbar discuss button overwrites all submitted task keys

--- src/app/projects/partials/directives/project-contexts.coffee
+++ src/app/projects/partials/directives/project-contexts.coffee
@@ -126,7 +126,11 @@
       Project.update({ id: $scope.project.project_id, trigger: "trigger_week_end" }).$promise.then (
         (project) ->
           oldId = $scope.activeTask.id
-          angular.extend $scope.submittedTasks, project.tasks
+
+          # go through each task and update the status only to the new project task's status
+          _.each $scope.submittedTasks, (task) ->
+            task.status = (_.find project.tasks, (t) -> task.id == t.id).status
+
           $scope.activeTask = _.find $scope.submittedTasks, (task) -> task.id == oldId
           alertService.add("success", "Status updated.", 2000)
       )


Fixed a colon's alignment

Fixed a colon's alignment

--- client/Social/Activity/views/activityitemchild.coffee
+++ client/Social/Activity/views/activityitemchild.coffee
@@ -56,14 +56,14 @@
     account = KD.whoami()
     if (data.originId is KD.whoami().getId()) or KD.checkFlag 'super-admin'
       @settingsButton = new KDButtonViewWithMenu
-        cssClass    : 'transparent activity-settings-menu'
-        itemChildClass: ActivityItemMenuItem
-        title       : ''
-        icon        : yes
-        delegate    : @
-        iconClass   : "arrow"
-        menu        : @settingsMenu data
-        callback    : (event)=> @settingsButton.contextMenu event
+        cssClass       : 'transparent activity-settings-menu'
+        itemChildClass : ActivityItemMenuItem
+        title          : ''
+        icon           : yes
+        delegate       : @
+        iconClass      : "arrow"
+        menu           : @settingsMenu data
+        callback       : (event)=> @settingsButton.contextMenu event
     else
       @settingsButton = new KDCustomHTMLView tagName : 'span', cssClass : 'hidden'


Cleaned code to better match psr2

Cleaned code to better match psr2

--- snippets/php.cson
+++ snippets/php.cson
@@ -58,7 +58,8 @@
      * CMS Fields
      * @return FieldList
      */
-    function getCMSFields() {
+    function getCMSFields()
+    {
     \t\\$fields = parent::getCMSFields();
     \t\\$fields->$0
     \treturn \\$fields;
@@ -71,7 +72,8 @@
      * Update Fields
      * @return FieldList
      */
-    function updateCMSFields(FieldList \\$fields) {
+    function updateCMSFields(FieldList \\$fields)
+    {
     \t\\$fields->$0
     \treturn \\$fields;
     }
@@ -183,7 +185,8 @@
      * Viewing Permissions
      * @return boolean
      */
-    public function canView($member = null) {
+    public function canView($member = null)
+    {
     \treturn ${1};
     }
     """
@@ -194,7 +197,8 @@
      * Editing Permissions
      * @return boolean
      */
-    public function canEdit($member = null) {
+    public function canEdit($member = null)
+    {
     \treturn ${1};
     }
     """
@@ -205,7 +209,8 @@
      * Deleting Permissions
      * @return boolean
      */
-    public function canDelete($member = null) {
+    public function canDelete($member = null)
+    {
     \treturn ${1};
     }
     """
@@ -216,7 +221,8 @@
      * Creating Permissions
      * @return boolean
      */
-    public function canCreate($member = null) {
-    \treturn ${1};
-    }
-    """
+    public function canCreate($member = null)
+    {
+    \treturn ${1};
+    }
+    """


Fix build

Fix build

--- app/locale/id.coffee
+++ app/locale/id.coffee
@@ -49,7 +49,7 @@
     not_tedious: "tidak membosankan."
     gaming_is_good: "Studi menunjukkan permainan itu baik untuk otak anak-anak. (itu benar!)"
     game_based: "Ketika sistem pembelajaran berbasis permainan"
-   compared: "dibandingkan"
+    compared: "dibandingkan"
     conventional: "terhadap metode penilaian konvensional, perbedaannya jelas: permainan merupakan pilihan yang lebih baik untuk membantu siswa mempertahankan pengetahuan, berkonsentrasi dan"
     perform_at_higher_level: "tampil di tingkat yang lebih tinggi dari pencapaian"
     feedback: "Permainan juga memberikan umpan balik dalam waktu nyata yang memungkinkan siswa untuk menyesuaikan jalan solusi mereka dan memahami konsep-konsep yang lebih holistik, daripada hanya terbatas dengan jawaban "benar" atau "salah" saja."


Update id.coffee (#4930)

Update id.coffee (#4930)

--- app/locale/id.coffee
+++ app/locale/id.coffee
@@ -93,14 +93,14 @@
     get_involved: "Ambil Andil"
     open_source: "Open source (GitHub)"
     support: "Bantuan"
-#    faqs: "FAQs"
+    faqs: "Tanya Jawab"
     copyright_prefix: "Hak Cipta"
     copyright_suffix: "Seluruh Hak Cipta"
     help_pref: "Butuh bantuan? Email"
     help_suff: "dan kita akan menghubungi!"
     resource_hub: "Pusat Sumber Daya"
     apcsp: "Fundamental AP CS"
-#    parent: "Parents"
+    parent: "Orang Tua"

   modal:
     close: "Tutup"
@@ -138,7 +138,7 @@
     poll: "Poll" # Tooltip on poll button from /play
     next: "Lanjut" # Go from choose hero to choose inventory before playing a level
     change_hero: "Ganti Jagoan" # Go back from choose inventory to choose hero
-#    change_hero_or_language: "Change Hero or Language"
+    change_hero_or_language: "Ganti Jagoan atau Bahasa"
     buy_gems: "Beli Permata"
     subscribers_only: "Hanya untuk yang pelanggan!"
     subscribe_unlock: "Berlangganan untuk membuka!"


Update sr.coffee (#3673)

Update sr.coffee (#3673)

began translation of skill docs and clans

--- app/locale/sr.coffee
+++ app/locale/sr.coffee
@@ -297,7 +297,7 @@
     save: "Сачувај"
     publish: "Објави"
     create: "Направи"
-#    fork: "Fork"
+    fork: "Форкуј"
     play: "Играј" # When used as an action verb, like "Play next level"
     retry: "Покушај поново"
     actions: "Радње"
@@ -676,16 +676,16 @@
     restricted_to_certain_heroes: "Само одређени хероји могу да играју овај ниво."

 #  skill_docs:
-#    function: "function" # skill types
-#    method: "method"
-#    snippet: "snippet"
-#    number: "number"
-#    array: "array"
-#    object: "object"
-#    string: "string"
+    function: "функција" # skill types
+    method: "метод"
+    snippet: "исечак"
+    number: "број"
+    array: "низ"
+    object: "објекат"
+    string: "стринг"
 #    writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
 #    read_only: "read-only"
-#    action: "Action"
+    action: "Aкција"
 #    spell: "Spell"
 #    action_name: "name"
 #    action_cooldown: "Takes"
@@ -939,14 +939,14 @@
     password_repeat: "Понови своју шифру."

 #  keyboard_shortcuts:
-#    keyboard_shortcuts: "Keyboard Shortcuts"
-#    space: "Space"
-#    enter: "Enter"
-#    press_enter: "press enter"
-#    escape: "Escape"
-#    shift: "Shift"
-#    run_code: "Run current code."
-#    run_real_time: "Run in real time."
+    keyboard_shortcuts: "Пречице за тастатуру"
+    space: "Space"
+    enter: "Enter"
+    press_enter: "притисни enter"
+    escape: "Escape"
+    shift: "Shift"
+    run_code: "Покрени тренутни код."
+    run_real_time: "Покрени у реалном времену."
 #    continue_script: "Continue past current script."
 #    skip_scripts: "Skip past all skippable scripts."
 #    toggle_playback: "Toggle play/pause."
@@ -960,7 +960,7 @@
 #    maximize_editor: "Maximize/minimize code editor."

 #  community:
-#    main_title: "CodeCombat Community"
+    main_title: "CodeCombat Заједница"
 #    introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!"
 #    level_editor_prefix: "Use the CodeCombat"
 #    level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!"
@@ -968,53 +968,53 @@
 #    thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites."
 #    article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the"
 #    article_editor_suffix: "and help CodeCombat players get the most out of their playtime."
-#    find_us: "Find us on these sites"
-#    social_github: "Check out all our code on GitHub"
-#    social_blog: "Read the CodeCombat blog on Sett"
-#    social_discource: "Join the discussion on our Discourse forum"
-#    social_facebook: "Like CodeCombat on Facebook"
-#    social_twitter: "Follow CodeCombat on Twitter"
-#    social_gplus: "Join CodeCombat on Google+"
-#    social_slack: "Chat with us in the public CodeCombat Slack channel"
-#    contribute_to_the_project: "Contribute to the project"
-
-#  clans:
-#    clan: "Clan"
-#    clans: "Clans"
-#    new_name: "New clan name"
-#    new_description: "New clan description"
-#    make_private: "Make clan private"
-#    subs_only: "subscribers only"
-#    create_clan: "Create New Clan"
-#    private_preview: "Preview"
-#    private_clans: "Private Clans"
-#    public_clans: "Public Clans"
-#    my_clans: "My Clans"
-#    clan_name: "Clan Name"
-#    name: "Name"
+    find_us: "Нађи нас на овим сајтовима"
+    social_github: "Погледај цео наш код на GitHub-у"
+    social_blog: "Читај CodeCombat блог на Sett-у"
+    social_discource: "Придружи се дискусији на нашем Discourse форуму"
+    social_facebook: "Лајкуј CodeCombat на Фејсбуку"
+    social_twitter: "Запрати CodeCombat на Твитеру"
+    social_gplus: "Придружи се CodeCombat-у на Гугл+"
+    social_slack: "Ћаскај са нама на јавном CodeCombat Slack каналу"
+    contribute_to_the_project: "Допринеси пројекту"
+
+  clans:
+    clan: "Клан"
+    clans: "Кланови"
+    new_name: "Име новог клана"
+    new_description: "Опис новог клана"
+    make_private: "Направи клан приватним"
+    subs_only: "само за претплатнике"
+    create_clan: "Направи нови клан"
+    private_preview: "Preview"
+    private_clans: "Приватни кланови"
+    public_clans: "Јавни кланови"
+    my_clans: "Моји кланови"
+    clan_name: "Име клана"
+    name: "Име"
 #    chieftain: "Chieftain"
 #    type: "Type"
-#    edit_clan_name: "Edit Clan Name"
-#    edit_clan_description: "Edit Clan Description"
-#    edit_name: "edit name"
-#    edit_description: "edit description"
-#    private: "(private)"
+    edit_clan_name: "Измени име клана"
+    edit_clan_description: "Измени опис клана"
+    edit_name: "измени име"
+    edit_description: "измени опис"
+    private: "(приватан)"
 #    summary: "Summary"
 #    average_level: "Average Level"
 #    average_achievements: "Average Achievements"
-#    delete_clan: "Delete Clan"
-#    leave_clan: "Leave Clan"
-#    join_clan: "Join Clan"
-#    invite_1: "Invite:"
+    delete_clan: "Избриши клан"
+    leave_clan: "Напусти клан"
+    join_clan: "Придружи се клану"
+    invite_1: "Позови:"
 #    invite_2: "*Invite players to this Clan by sending them this link."
-#    members: "Members"
-#    progress: "Progress"
+    members: "Чланови"
+    progress: "Напредак"
 #    not_started_1: "not started"
 #    started_1: "started"
 #    complete_1: "complete"
 #    exp_levels: "Expand levels"
 #    rem_hero: "Remove Hero"
-#    status: "Status"
+    status: "Статус"
 #    complete_2: "Complete"
 #    started_2: "Started"
 #    not_started_2: "Not Started"
@@ -1873,15 +1873,15 @@
     year: "Година"
     day: "Дан"
     month: "Месец"
-    january: "Јануар"
-    february: "Фебруар"
-    march: "Март"
-    april: "Април"
-    may: "Мај"
-    june: "Јун"
-    july: "Јул"
-    august: "Август"
-    september: "Септембар"
-    october: "Октобар"
-    november: "Новембар"
-    december: "Децембар"
+    january: "јануар"
+    february: "фебруар"
+    march: "март"
+    april: "април"
+    may: "мај"
+    june: "јун"
+    july: "јул"
+    august: "август"
+    september: "септембар"
+    october: "октобар"
+    november: "новембар"
+    december: "децембар"


quiet down the console logs.

quiet down the console logs.

--- assets/front.coffee
+++ assets/front.coffee
@@ -233,8 +233,8 @@

 	getTrackInfo = ->
 		$.getJSON "all.json", (segments) ->
-			console.log("getTrackInfo")
-			console.log(segments)
+			#console.log("getTrackInfo")
+			#console.log(segments)
 			trackids = []
 			for segment in segments
 				if segment.tracks?
@@ -248,10 +248,10 @@
 					# Signed: Chris Angelico (Rosuav).
 					now = window.serverTime()/1000
 					if now > segment.time+segment.duration
-						console.log("Segment ["+id+"] is in the past")
+						#console.log("Segment ["+id+"] is in the past")
 						continue # Voorbij is nu voorbij :)
 					else
-						console.log("Segment ["+id+"] is in the future")
+						#console.log("Segment ["+id+"] is in the future")
 					isnew = 1
 					for t in trackids
 						if t == id
@@ -264,7 +264,7 @@
 						seconds = Math.floor(length%60)
 						if seconds < 10
 							seconds = "0" + seconds
-						console.log("Recording artist"+tag+" as "+segment.tracks[0].metadata.artist)
+						#console.log("Recording artist"+tag+" as "+segment.tracks[0].metadata.artist)
 						artist = document.getElementById('artist'+tag)
 						if artist
 							# Eventually we'll run out of objects to stash info into - that's fine.
@@ -302,10 +302,10 @@
 			if typeof data is "string"
 				data = JSON.parse(data)
 				if data.segment.send_next_track
-					console.log("Send next track!")
-					console.log(Date())
-					console.log(data.segment.prev_track)
-					console.log(data.segment.next_track)
+					#console.log("Send next track!")
+					#console.log(Date())
+					#console.log(data.segment.prev_track)
+					#console.log(data.segment.next_track)
 					# Temporarily tagging the artist so we know it came from send_next_track
 					document.getElementById('artist').innerHTML = "[snt] " + data.segment.next_track.artist
 					document.getElementById('artist_next').innerHTML = "Up next: (unknown)"
@@ -322,9 +322,9 @@
 					document.getElementById('artist').innerHTML = window._next_artist
 					window._next_artist = data.segment.tracks[0].metadata.artist
 					document.getElementById('artist_next').innerHTML = "Up next: " + window._next_artist
-				console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
-				console.log(data.segment.tracks[0].metadata.artist)
-				console.log(data.segment.tracks[0].metadata.id)
+				#console.log("GetTrackDets Data " + window._count_TrackDeets + ": ")
+				#console.log(data.segment.tracks[0].metadata.artist)
+				#console.log(data.segment.tracks[0].metadata.id)
 				window._count_TrackDeets++
 			if data.listener_count?
 				window._listeners = data.listener_count


small copy updates

small copy updates

--- static/embed/oab_embed.coffee
+++ static/embed/oab_embed.coffee
@@ -983,7 +983,7 @@

The library has checked and the journal encourages you to freely share your paper so colleagues and the public can freely read and cite it.

✓ Find the manuscript the journal accepted. It\'s not a PDF from the journal site

-
This is the only version you\'re able to share legally. The accepted manuscript is the word file or Latex export you sent the publisher after peer-review and before formatting (publisher proofs).

+
This is the only version you\'re able to share under copyright. The accepted manuscript is the word file or Latex export you sent the publisher after peer-review and before formatting (publisher proofs).

✓ Check there aren\'t publisher logos or formatting

It\'s normal to share accepted manuscripts as the research is the same. It\'s fine to save your file as a pdf, make small edits to formatting, fix typos, remove comments, and arrange figures.

@@ -1018,9 +1018,9 @@

-
Hmmm, something looks wrong

-
You\'re nearly done. It looks like what you uploaded is a publisher\'s PDF which your journal prohibits legally sharing.

-    We just need the version accepted by the journal to make your work available to everyone.

+
We need an earlier version

+
It looks like what you uploaded is a publisher\'s PDF which your journal prohibits legally sharing.

+    You\'re nearly done. We need the accepted version, not the PDF from the journal site.

Try uploading again

My upload was an accepted manuscript


fix ALL the problems

fix ALL the problems

--- client/main.coffee
+++ client/main.coffee
@@ -7,7 +7,7 @@
 Meteor.subscribe( "assigned_posts" )

 #Template variables/functions
-min_posts = 0
+min_posts = 999

 graduated = (tag, post) ->
   return post.tags[tag].users.length >= 2
@@ -22,19 +22,16 @@
     placed = 0
     for tag,info of post.tags
       if graduated(tag, post)
-        addGroupName(post, tag)
         if groups[tag]?
           groups[tag].posts.push(post)
         else
           groups[tag] = {'posts': [post]}
         placed++
       else if placed == 0
-        addGroupName(post, 'Incubator')
         groups['Incubator'].posts.push(post)
         placed++
       count++
     if count == 0
-      addGroupName(post, 'Incubator')
       groups['Incubator'].posts.push(post)
   groupList = []
   for name,info of groups
@@ -65,55 +62,50 @@

 _.extend( Template.stream,
   posts: ->
-    return Posts.find( 'parent_id': undefined )
+    return (Posts.find( 'parent_id': undefined ).map (post) -> {'post':post, 'group':""})
   new: true
 )

 _.extend( Template.post,
   content: ->
     showdownConverter = new Showdown.converter()
-    postContentHtml = showdownConverter.makeHtml(@content)
+    postContentHtml = showdownConverter.makeHtml(@post.content)
     return postContentHtml
   groups: ->
-    children = Posts.find( parent_id: @_id )
+    children = Posts.find( parent_id: @post._id )
     numChildren = children.count()
     if numChildren == 0
       return []
     else if numChildren < min_posts
-      return [{name: "All Replies", posts: children}]
+      return [{'name': "All Replies", 'posts': children.fetch()}]
     else
       return makeGroups(children)
-  identifier: -> @_id
-  groupname: ->
-    if !@group_name?
-      @group_name = ""
-    return @group_name
+  identifier: -> @post._id
+  groupname: -> @group
   events: {
     "click button[name='replySubmit']": (event) ->
-
-      replyTextBox = document.getElementById("replyText-#{ @_id }-#{ @group_name }")#Bryan thinks there's a way to do this without traversing the DOM.
-
-      replyContent = replyTextBox.value
-      console.log("ID of Post you're replying to: #{ @_id }")
-      console.log("Reply content: #{replyContent}")
-      console.log("ID of new post: "
-        Posts.insert(
+      if !event.isImmediatePropagationStopped()
+        replyTextBox = document.getElementById("replyText-#{ @post._id }-#{ @group }")#Bryan thinks there's a way to do this without traversing the DOM.
+        event.stopImmediatePropagation()
+        replyContent = replyTextBox.value
+        console.log("ID of Post you're replying to: #{ @post._id }-#{ @group }")
+        console.log("Reply content: #{replyContent}")
+        replyID = Posts.insert(
           {
             content: replyContent,
-            parent_id: @_id,
-            instance_id: @instance_id
+            parent_id: @post._id,
+            instance_id: @post.instance_id
           }
         )
-      )
-      replyTextBox.value = '' #clear the textbox for giggles -- should probably do this only if the post succeeds.
-      event.stopImmediatePropagation()
+        console.log("ID of new post: "+replyID)
+        replyTextBox.value = '' #clear the textbox for giggles -- should probably do this only if the post succeeds.
   }

 )

 _.extend( Template.group,
   name: -> @name
-  posts: -> @posts
+  posts: -> {'post':post, 'group':@name} for post in @posts
 )

 _.extend( Template.notifications,


Put the code to add a PostView from a response into the StreamView addOne function

--- client/main.coffee
+++ client/main.coffee
@@ -8,6 +8,7 @@
     respond: (response) =>

       p = new Post(
+        parentID: @id
         id: 1.1
         editing: false
         content: response.posttext
@@ -17,8 +18,7 @@
         parents: ''
         responses: ''
       )
-      pv = new PostView(model: p)
-      $('#response' + @id).append(pv.render())
+      App.addOne(p)
       #@save(
       #  responses:
       #    @get( 'responses' ).push( p.cid )
@@ -68,8 +68,9 @@
       postCollection.fetch()
     addOne: (item) ->
       post = new PostView(model: item)
-      #parents = ()
-      $(post.render()).appendTo()
+      $('#response' + item.get('parentID')).append(post.render())
+      # parents = ()
+      # $(post.render()).appendTo()
     addAll: ->
       postCollection.each(@addOne)


Allow save mentions to 'all'

Allow save mentions to 'all'

--- packages/rocketchat-mentions/server.coffee
+++ packages/rocketchat-mentions/server.coffee
@@ -13,7 +13,13 @@
 			mentions = _.unique mentions
 			verifiedMentions = []
 			mentions.forEach (mention) ->
-				verifiedMention = Meteor.users.findOne({username: mention}, {fields: {_id: 1, username: 1}})
+				if mention is 'all'
+					verifiedMention =
+						_id: mention
+						username: mention
+				else
+					verifiedMention = Meteor.users.findOne({username: mention}, {fields: {_id: 1, username: 1}})
+
 				verifiedMentions.push verifiedMention if verifiedMention?
 			if verifiedMentions.length isnt 0
 				message.mentions = verifiedMentions


Display BPE update message correctly

Display BPE update message correctly

--- app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -275,7 +275,7 @@
       ).success((data) ->
         if angular.toJson($scope.productsWithoutDerivedAttributes()) == angular.toJson(data)
           $scope.resetProducts data
-          $scope.displaySuccess()
+          $timeout -> $scope.displaySuccess()
         else
           $scope.displayFailure "Product lists do not match."
       ).error (data, status) ->


Force to load the user.

Force to load the user.

--- app/assets/javascripts/app/controllers/user_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/user_zoom.js.coffee
@@ -11,7 +11,7 @@
       @user = user
       @render()

-    App.User.retrieve( @user_id, start )
+    App.User.retrieve( @user_id, start, true )

   meta: =>
     meta =


add new options to cli

add new options to cli

--- src/command.coffee
+++ src/command.coffee
@@ -13,12 +13,17 @@
   Usage: gerber2svg [options] path/to/gerbers
   Output:
     `gerber2svg path/to/gerber.gbr` will write the svg to stdout
-    `gerber2svg path/to/gerber.gbr -o some/dir` will create some/dir/gerber.svg
+    `gerber2svg -o some/dir path/to/gerber.gbr` will create some/dir/gerber.svg
+    `gerber2svg -d drill.drl -o out gerb/*` will process drill.drl as a drill
+      file, everything in gerb as a Gerber file, and output to out
 '''

 OPTIONS = [
   [ 'o', 'out', '     specify an output directory' ]
-  [ 'q', 'quiet', '   run quietly (does not include svg output)' ]
+  [ 'q', 'quiet', '   do not print warnings and messages' ]
+  [ 'p', 'pretty', '  align SVG output prettily' ]
+  [ 'd', 'drill', '   process following file as an NC (Excellon) drill file' ]
+  [ 'a', 'append', '  append .svg rather than replace the old file extension' ]
   [ 'v', 'version', ' display version information' ]
   [ 'h', 'help', '    display this help text' ]
 ]
@@ -44,8 +49,9 @@
   }
   fileList = argv._

-  return version() if argv.version
-  return help() if argv.help or argv._.length is 0
+  if argv.version then return version()
+  if argv.help or (argv._.length is 0 and typeof argv.drill isnt 'string')
+    return help()

   # console
   warn = (string) -> console.error chalk.bold.yellow string unless argv.quiet
@@ -55,21 +61,26 @@
   write = (string, filename) ->
     unless argv.out then process.stdout.write string
     else
-      newName = path.basename(filename, path.extname filename) + '.svg'
-      newName = path.join argv.out, newName
+      if argv.append then newName = path.basename filename
+      else newName = path.basename filename, path.extname filename
+      newName = path.join argv.out, newName + '.svg'
       fs.writeFile newName, string, (error) ->
         unless error then print "#{filename} converted to #{newName}"
         else warn "Error writing to #{newName}: #{error.code}"

+  # add drill file if it was included
+  if argv.drill? and not argv.drill in fileList then fileList.push argv.drill
   # loop through files
   for file in fileList
     do (file) ->
       fs.readFile file, 'utf-8', (error, data) ->
         unless error
           try
-            write gerberToSvg(data), file
+            opts = { pretty: argv.pretty, drill: (file is argv.drill) }
+            write gerberToSvg(data, opts), file
           catch e
             warn "could not process #{file}: #{e.message}"
-        else warn "Error reading file #{file}: #{error.code}"
+        else
+          warn "Error reading file #{file}: #{error.code}"

 module.exports = run


renderable list builders

renderable list builders

--- src/context.coffee
+++ src/context.coffee
@@ -98,11 +98,19 @@
       handle_any_object
     ]

+    delayed_renderable_list_builder = ($item) ->
+      nested_renderables = []
+      add_renderable: (renderable) -> nested_renderables.push renderable
+      _lead_render: ->
+        children = _.map nested_renderables, (i) -> i._lead_render()
+        $item.append children
+        $item
+

     run_context = _.extend {}, extra_contexts...,
       $el: $el
       current_options: {}
-      renderables: []
+      renderable_list_builder: delayed_renderable_list_builder $el
       active_context: ->
         console.warn 'no active running context. did you call an async function without keeping the context?' unless running_context_binding?
         run_context.current_context
@@ -142,13 +150,16 @@
           restoring_context fn, arguments

       add_renderable: (renderable) ->
-        run_context.current_context.renderables.push renderable
+        run_context.current_context.renderable_list_builder.add_renderable renderable
+
+      add_rendered: (rendered) ->
+        run_context.add_renderable _lead_render: -> rendered

       output: (output) ->
         $item = $ '<div class="item"/>'
         if output?
           $item.append output
-        run_context.add_renderable _lead_render: -> $item
+        run_context.add_rendered $item
         $item

       render: (o) ->
@@ -164,14 +175,10 @@
         @nested_item $item, fn, args...

       nested_item: ($item, fn, args...) ->
-        nested_renderables = []
-        run_context.add_renderable _lead_render: ->
-          children = _.map nested_renderables, (i) -> i._lead_render()
-          $item.append children
-          $item
-
+        nested_renderable = delayed_renderable_list_builder $item
+        run_context.add_renderable nested_renderable
         nested_context = _.extend {}, run_context,
-          renderables: nested_renderables
+          renderable_list_builder: nested_renderable
         run_context.in_context nested_context, fn, args

       handle_exception: (e, compiled) ->
@@ -247,8 +254,8 @@
       result = (-> eval string).call run_context
       `}`
       run_context.$el.removeClass 'clean'
-      run_context.$el.append _.map run_context.renderables, (r) -> r._lead_render()
       run_context.display_object result
+      run_context.renderable_list_builder._lead_render()
     catch e
       run_context.handle_exception e, string
     finally


Nicer syntax

Nicer syntax

--- core/app/backbone/collections/favourite_topics.coffee
+++ core/app/backbone/collections/favourite_topics.coffee
@@ -13,17 +13,14 @@
     @_activeId = model.id
     @getActive()?.trigger 'activate'

-  isActive: (model) ->
-    model.id == @_activeId
+  isActive: (model) -> model.id == @_activeId

   getActive: -> @get(@_activeId)

-
 class window.FavouriteTopics extends Backbone.Collection
+  _.extend @prototype, ActivatableCollection

   model: Topic

   url: -> "/#{currentUser.get('username')}/favourite_topics"
-
-_.extend window.FavouriteTopics.prototype, ActivatableCollection


Emit a signal when user subscribes.

Emit a signal when user subscribes.

--- client/app/CommonViews/paymentwidgets.coffee
+++ client/app/CommonViews/paymentwidgets.coffee
@@ -42,6 +42,7 @@
         @checkSubscription (status)=>
           @loader.hide()
           if status
+            @emit "subscribed"
             @widgetContent.show()
           else
             @buttonSubscribe.show()


client: make solo product lite only for prod

--- client/app/lib/util/issoloproductlite.coffee
+++ client/app/lib/util/issoloproductlite.coffee
@@ -1,4 +1,8 @@
+globals = require 'globals'
+
 module.exports = isSoloProductLite =  ->
+
+  return no  if globals.config.environment in [ 'dev', 'sandbox' ]

   cutOffDate = new Date 2016, 2, 1  # March 1st, 2016


use braces

use braces

--- scripts/feed_checker.coffee
+++ scripts/feed_checker.coffee
@@ -36,7 +36,7 @@
       is_room = usr.room?
       continue if _.contains sent, (if is_room then usr.room else usr.id)

-      func = if is_room then robot.messageRoom else robot.send
+      func = (if is_room then robot.messageRoom else robot.send)
       func (if is_room then usr.room else usr), msg
       sent.push if is_room then usr.room else usr.id


type() fix

type() fix

--- turingtype.coffee
+++ turingtype.coffee
@@ -31,7 +31,7 @@

   type: =>
-    return @callback?() if @i is @len
+    return @callback?() if @i is @len + 1
     if rand() > @accuracy
       @el[@attr] = @text.slice(0, @i) + @keys[floor rand() * @keys.length]
       @timer     = setTimeout =>


Removes specs that were duplicated

Removes specs that were duplicated

--- spec/index.coffee
+++ spec/index.coffee
@@ -68,11 +68,6 @@
     it 'should only accept functions as arguments', ->
       @maypop.after.bind(undefined).should.throw()
       @maypop.after.bind(undefined, ->).should.not.throw()
-    it 'should run the function passed to "before" before the original function', ->
-      spy = sinon.spy()
-      @maypop.before spy
-      @maypop()
-      spy.should.have.been.calledBefore(@original)
     it 'should run the function passed after the original function', ->
       spy = sinon.spy()
       @maypop.after spy
@@ -85,7 +80,7 @@
         @maypop {}, true, "123"
         afterSpy.should.have.been.calledWith @returnValue
     describe 'multiple calls to aspects', ->
-      it 'should be possible to call "before" and "after" multiple times', ->
+      it 'should be possible to call "after" multiple times', ->
         @maypop.after(->)
         @maypop.after.bind(undefined, ->).should.not.throw
       it 'should execute the functions in the order they are added', ->


scroll bug fix

scroll bug fix

--- src/desktop/components/view_in_room/view.coffee
+++ src/desktop/components/view_in_room/view.coffee
@@ -33,6 +33,7 @@
   render: ->
     @__render__()
     @scrollbar.disable()
+    document.body.style.overflowY = 'hidden'
     @cacheSelectors()
     @injectImage()

@@ -148,7 +149,7 @@
     scaling = factor / @$placeholder.width()
     Math.round(scaling * 100) / 100

-  measurementMargin: ->
+  measurementMargin: ->
     @$el.height() / 1.79 - 27

   measurementWidth: ->
@@ -167,5 +168,6 @@
         if @$sourceImage
           @$sourceImage.css visibility: 'visible'
         @scrollbar.reenable()
+        document.body.style.overflowY = 'visible'
         ViewInRoom.__super__.remove.apply this, arguments
         @trigger 'removed'


anota o método de setTimeOutDefaults

anota o método de setTimeOutDefaults

--- src/script/vtex-message.coffee
+++ src/script/vtex-message.coffee
@@ -109,6 +109,11 @@
     @show()
     return

+   ###
+   # Configura o timeout da mensagem de acordo com o 'type' da mesma
+   # @method setTimeoutDefaults
+   # @return
+   ###
   setTimeoutDefaults: (options) ->
     ONE_SECOND = 1000
     if options.timeout?


remove code duplication

remove code duplication

--- src/scripts/jira-issues.coffee
+++ src/scripts/jira-issues.coffee
@@ -47,13 +47,14 @@
               .auth(auth)
               .get() (err, res, body) ->
                 try
-                  key = JSON.parse(body).key
-                  msg.send "[" + key + "] " + JSON.parse(body).fields.summary
+                  json = JSON.parse(body)
+                  key = json.key
+                  msg.send "[" + key + "] " + json.fields.summary
                   urlRegex = new RegExp(jiraUrl + "[^\\s]*" + key)
                   if not msg.message.text.match(urlRegex)
                     msg.send jiraUrl + "/browse/" + key
                 catch error
                   try
-                    msg.send "[*ERROR*] " + JSON.parse(body).errorMessages[0]
+                    msg.send "[*ERROR*] " + json.errorMessages[0]
                   catch reallyError
                     msg.send "[*ERROR*] " + reallyError


Fix #1682. Removed --no-patch flag from phpcbf beautifier version 3 on other platforms

--- src/beautifiers/phpcbf.coffee
+++ src/beautifiers/phpcbf.coffee
@@ -76,7 +76,7 @@
       )
     else
       @run("phpcbf", [
-        "--no-patch"
+        "--no-patch" unless options.phpcbf_version is 3
         "--standard=#{options.standard}" if options.standard
         tempFile = @tempFile("temp", text)
         ], {


When invited add them to the invitees list too.

--- workers/social/lib/social/models/chatconversation/index.coffee
+++ workers/social/lib/social/models/chatconversation/index.coffee
@@ -140,7 +140,7 @@
     unless nickname in @invitees
       return callback? new KodingError "Access denied!"

-    @update {$addToSet: participants: invitee}, (err)=>
+    @update $addToSet: {participants: invitee, invitees: invitee}, (err)=>
       return console.error err  if err?

       @emit 'notification', {


Wake up/take confusion bug.

Wake up/take confusion bug.

--- packages/pixelartacademy-season1-episode0/chapter1/chapter1.coffee
+++ packages/pixelartacademy-season1-episode0/chapter1/chapter1.coffee
@@ -83,7 +83,7 @@
   onCommand: (commandResponse) ->
     return unless LOI.adventure.currentTimelineId() is PAA.TimelineIds.DareToDream

-    commandResponse.onPhrase
+    commandResponse.onExactPhrase
       form: [Vocabulary.Keys.Verbs.WakeUp]
       action: =>
         # End intro section.


remove unneeded dep

remove unneeded dep

--- html-app/app/scripts/directives/topic.coffee
+++ html-app/app/scripts/directives/topic.coffee
@@ -4,8 +4,7 @@
   restrict: 'EA'
   scope:
     topic: '=model'
-    peek: '='
-  controller: ['$scope', 'Authorize', 'Post', 'Topic', 'User', 'Like', 'CurrentSession', 'Support', ($scope, Authorize, Post, Topic, User, Like, CurrentSession, Support)->
+  controller: ['$scope', 'Authorize', 'Post', 'Topic', 'User', 'Like', 'CurrentSession',  ($scope, Authorize, Post, Topic, User, Like, CurrentSession)->
     $scope.post = $scope.topic['first-post']
     $scope.formatPostCount = (postCount)->
       switch postCount


新しい絵文字が追加された時に通知

新しい絵文字が追加された時に通知

--- scripts/notification.coffee
+++ scripts/notification.coffee
@@ -1,21 +1,18 @@
 # Description:
-#   新しいチャンネルの完成を教えてくれるbot
+#   様々な通知を行うbot
 # Author:
 #   raoka0000

 module.exports = (robot) ->
-  ###robot.hear /test/i, (msg) ->
-    userId = robot.adapter.client.rtm.dataStore.getUserByName('raoka0000').id
-    roomId = msg.envelope.room
-    tRoom = robot.adapter.client.rtm.dataStore.getChannelById(roomId)
-    #console.log  tRoom
-    robot.send {room: roomId}, "テスト中だよ #{userId} | #{roomId}\n(##{tRoom.name})\n: #{tRoom.purpose.value}"###
-
   robot.adapter.client.rtm.on 'raw_message', (msg) ->
     #console.log msg
     message = JSON.parse msg
-    return unless message.type is 'channel_created'
-    creator = robot.adapter.client.rtm.dataStore.getUserById(message.channel.creator)
-    #console.log creator
-    robot.send {room: "C0P4MNTT8"}, "新しいチャンネルができたよ！ (##{message.channel.name}) By #{creator.real_name}"
+    switch message.type
+      when 'channel_created'
+        creator = robot.adapter.client.rtm.dataStore.getUserById(message.channel.creator)
+        robot.send {room: "C0P4MNTT8"}, "新しいチャンネルができたよ！ (##{message.channel.name}) By #{creator.real_name}"
+      when 'emoji_changed'
+        break unless message.subtype is 'add'
+        robot.send {room: "C0P4MNTT8"}, "新しい絵文字か追加されたよ!\n どんなの? -> :#{message.name}: [#{message.name}]"

+


Increased user limits for VMs.

Increased user limits for VMs.

--- client/app/Applications/Groups.kdapplication/AppController.coffee
+++ client/app/Applications/Groups.kdapplication/AppController.coffee
@@ -639,39 +639,39 @@
                 name                 : "member-vm"
                 defaultValue         : yes
               "Member CPU Limit"     :
-                label                : "CPU Limit per Member"
+                label                : "CPU Limit"
                 itemClass            : KDSelectBox
                 type                 : "select"
                 name                 : "vm-cpu-member"
-                defaultValue         : "1"
+                defaultValue         : "10"
                 selectOptions        : [
-                  { title : "1 CPU unit",    value : "1" }
-                  { title : "2 CPU units",    value : "2" }
-                  { title : "3 CPU units",    value : "3" }
-                  { title : "4 CPU units",    value : "4" }
-                  { title : "5 CPU units",    value : "5" }
+                  { title : "10 CPU units",    value : "10" }
+                  { title : "20 CPU units",    value : "20" }
+                  { title : "30 CPU units",    value : "30" }
+                  { title : "40 CPU units",    value : "40" }
+                  { title : "50 CPU units",    value : "50" }
                 ]
               "Member RAM Limit"     :
-                label                : "RAM Limit per Member"
+                label                : "RAM Limit"
                 itemClass            : KDSelectBox
                 type                 : "select"
                 name                 : "vm-ram-member"
-                defaultValue         : "1"
+                defaultValue         : "10"
                 selectOptions        : [
-                  { title : "1 GB",     value : "1" }
-                  { title : "2 GBs",    value : "2" }
-                  { title : "5 GBs",    value : "5" }
+                  { title : "10 GBs",    value : "10" }
+                  { title : "20 GBs",    value : "20" }
+                  { title : "30 GBs",    value : "30" }
                 ]
               "Member Disk Limit"    :
-                label                : "Disk Limit per Member"
+                label                : "Disk Limit"
                 itemClass            : KDSelectBox
                 type                 : "select"
                 name                 : "vm-disk-member"
-                defaultValue         : "1"
+                defaultValue         : "10"
                 selectOptions        : [
-                  { title : "1 GB",     value : "1" }
-                  { title : "2 GBs",    value : "2" }
-                  { title : "5 GBs",    value : "5" }
+                  { title : "10 GBs",    value : "10" }
+                  { title : "20 GBs",    value : "20" }
+                  { title : "30 GBs",    value : "30" }
                 ]

     modal = new KDModalViewWithForms modalOptions


activity: remove unused function and its call

--- client/activity/lib/components/publicchatpane/index.coffee
+++ client/activity/lib/components/publicchatpane/index.coffee
@@ -26,15 +26,6 @@

     ActivityFlux.actions.message.createMessage @channel('id'), body

-    @moveScrollToBottom()
-
-
-  moveScrollToBottom: ->
-
-    ChatPaneWrapper = @refs.ChatPaneWrapper.getDOMNode()
-    InfiniteScroll = ChatPaneWrapper.querySelector '.InfiniteScroll'
-    InfiniteScroll.scrollTop = InfiniteScroll.scrollHeight
-

   onLoadMore: ->


Client: Sidebar: Feed: Deleted showPopularMessages prop and check this state by resultStates

--- client/activity/lib/components/publicfeedpane/index.coffee
+++ client/activity/lib/components/publicfeedpane/index.coffee
@@ -5,13 +5,13 @@
 FeedPane       = require './feedpane'
 ActivityFlux   = require 'activity/flux'
 ScrollerMixin  = require 'app/components/scroller/scrollermixin'
+ResultState    = require 'activity/util/resultStates'

 module.exports = class PublicFeedPane extends React.Component

   @defaultProps =
-    thread              : immutable.Map()
-    messages            : immutable.List()
-    showPopularMessages : no
+    thread   : immutable.Map()
+    messages : immutable.List()

   channel: (keyPath...) -> @props.thread?.getIn ['channel'].concat keyPath
@@ -32,12 +32,13 @@

     return  unless (messages = @props.thread.get 'messages').size

-    if @props.showPopularMessages
-      from = messages.last().get 'createdAt'
-      ActivityFlux.actions.message.loadPopularMessages @channel('id'), { from }
-    else
-      from = messages.first().get 'createdAt'
-      ActivityFlux.actions.message.loadMessages @channel('id'), { from }
+    switch @props.thread.getIn ['flags', 'resultListState']
+      when ResultState.LIKED
+        from = messages.last().get 'createdAt'
+        ActivityFlux.actions.channel.loadPopularMessages @channel('id'), { from }
+      when ResultState.RECENT
+        from = messages.first().get 'createdAt'
+        ActivityFlux.actions.message.loadMessages @channel('id'), { from }

   onThresholdReached: (event) ->
@@ -62,6 +63,6 @@
       key={@props.thread.get 'channelId'}
       thread={@props.thread}
       onLoadMore={@bound 'onLoadMore'}
-      showPopularMessages={@props.showPopularMessages}
     />

+


virtualization controller: close the modal on cancel

--- client/landing/app/MainApp/VirtualizationController.coffee
+++ client/landing/app/MainApp/VirtualizationController.coffee
@@ -371,6 +371,7 @@
       overlay : yes

     workflow.on 'DataCollected', -> debugger
+    workflow.on 'Cancel', -> modal.destroy()

     workflow.enter()


Clarified nesting

Clarified nesting

--- core/app/assets/javascripts/lib/prevent_scroll_propagation.coffee
+++ core/app/assets/javascripts/lib/prevent_scroll_propagation.coffee
@@ -20,7 +20,9 @@
       $el = $(event.delegateTarget)
       return if shouldUseNativeBehaviour($el)

-      event.stopPropagation() # allow nesting of scrollable containers
+      # Allow nesting of scrollable containers
+      # This does mean that all children should also have preventScrollPropagation!
+      event.stopPropagation()

       if shouldPreventScroll($el, delta)
         event.preventDefault()


Extract method to add common params

Extract method to add common params

--- services/web/app/coffee/Features/Analytics/AnalyticsManager.coffee
+++ services/web/app/coffee/Features/Analytics/AnalyticsManager.coffee
@@ -4,6 +4,20 @@
 request = require "requestretry"
 Errors = require '../Errors/Errors'

+
+makeFaultTolerantRequest = (userId, options, callback) ->
+	if userId+"" == settings.smokeTest?.userId+""
+		return callback()
+
+	options = Object.assign(options, {
+		delayStrategy: exponentialBackoffStrategy()
+		timeout: 1000
+	})
+
+	if settings.overleaf?
+		options.qs = Object.assign({}, options.qs, { fromV2: 1 })
+
+	makeRequest(options, callback)

 makeRequest = (opts, callback)->
 	if settings.apis?.analytics?.url?
@@ -49,40 +63,33 @@
 		makeRequest opts, callback

 	recordEvent: (user_id, event, segmentation = {}, callback = (error) ->) ->
-		if user_id+"" == settings.smokeTest?.userId+""
-			return callback()
 		opts =
 			body:
 				event:event
 				segmentation:segmentation
 			json:true
 			method:"POST"
-			timeout:1000
 			url: "/user/#{user_id}/event"
-			delayStrategy: exponentialBackoffStrategy()
 			maxAttempts: 7 # Give up after ~ 8min
-		if settings.overleaf?
-			opts.qs = {fromV2: 1}
-		makeRequest opts, callback
+
+		makeFaultTolerantRequest user_id, opts, callback
+

 	updateEditingSession: (userId, projectId, countryCode, callback = (error) ->) ->
-		if userId+"" == settings.smokeTest?.userId+""
-			return callback()
 		query =
 			userId: userId
 			projectId: projectId
+
 		if countryCode
 			query.countryCode = countryCode
+
 		opts =
 			method: "PUT"
-			timeout: 1000
 			url: "/editingSession"
 			qs: query
 			maxAttempts: 6 # Give up after ~ 4min
-			delayStrategy: exponentialBackoffStrategy()
-		if settings.overleaf?
-			opts.qs.fromV2 = 1
-		makeRequest opts, callback
+
+		makeFaultTolerantRequest userId, opts, callback

 	getLastOccurrence: (user_id, event, callback = (error) ->) ->


insert the pdf text and annotations after the page has been rendered

--- services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
+++ services/web/public/coffee/ide/pdfng/directives/pdfRenderer.coffee
@@ -151,28 +151,27 @@
 					textLayerDiv: element.text[0]
 					viewport: viewport
 				})
-				page.getTextContent().then (textContent) ->
-					console.log 'text content is', textContent
-					window.RENDER_DELAY = 0
-					textLayer.setTextContent textContent

 				annotationsLayer = new pdfAnnotations({
 					annotations: element.annotations[0]
 					viewport: viewport
 					navigateFn: @navigateFn
 				})
-				page.getAnnotations().then (annotations) ->
-					console.log 'annotations are', annotations
-					window.RENDER_DELAY = 0
-					annotationsLayer.setAnnotations annotations
+
+				element.canvas.replaceWith(canvas)
+				canvas.removeClass('pdf-canvas-new')

 				return @renderTask = page.render {
 					canvasContext: ctx
 					viewport: viewport
 				}
 				.then () ->
-					element.canvas.replaceWith(canvas)
-					canvas.removeClass('pdf-canvas-new')
+					page.getTextContent().then (textContent) ->
+						console.log 'text content is', textContent
+						textLayer.setTextContent textContent
+					page.getAnnotations().then (annotations) ->
+						console.log 'annotations are', annotations
+						annotationsLayer.setAnnotations annotations

 			addSpinner: (element) ->
 				h = element.height()


Fix GPS extract, refs #177

Fix GPS extract, refs #177

--- server/helpers/thumb.coffee
+++ server/helpers/thumb.coffee
@@ -15,15 +15,17 @@
 gpsDegToDec = (pos, posRef) -> # String to int

     split = pos.match /(\d+)\/(\d+), (\d+)\/(\d+), (\d+)\/(\d+)/
-    if split[6]
+    if split?[6]
         coord    =  split[1] / split[2] + \
                    (split[3] / split[4]) / 60 + \
                    (split[5] / split[6])/3600 # lat and long format
     else
         splitAlt = pos.match /(\d+)\/(\d+)/   # altitude format
-        coord    = splitAlt[1] / splitAlt[2]
+        if splitAlt?
+            coord    = splitAlt[1] / splitAlt[2]
     ref = if (posRef == 'S' or posRef == 'W') then -1 else 1
-    return ref * coord
+    if coord?
+        return ref * coord

 module.exports = thumb =

@@ -53,7 +55,6 @@
                     GPS.lat  = gpsDegToDec data.Properties[lat] , data.Properties[lat  + 'Ref']
                 if data.Properties[ long ]
                     GPS.long = gpsDegToDec data.Properties[long], data.Properties[long + 'Ref']
-                #console.log data.Properties

                 if not(orientation?) or data.Orientation is 'Undefined'
                     orientation = 1


Require under benv.setup

Require under benv.setup

--- components/split_test/test/middleware.coffee
+++ components/split_test/test/middleware.coffee
@@ -4,17 +4,21 @@

 describe 'splitTestMiddleware', ->
   before ->
-    @splitTestMiddleware = rewire '../middleware'
-    @splitTestMiddleware.__set__ 'runningTests',
-      header_design:
-        key: 'header_design'
-        outcomes: old: 0, new: 1
-        edge: 'old'
+    benv.setup (done) =>
+      @splitTestMiddleware = rewire '../middleware'
+      @splitTestMiddleware.__set__ 'runningTests',
+        header_design:
+          key: 'header_design'
+          outcomes: old: 0, new: 1
+          edge: 'old'

-      some_other_test:
-        key: 'some_other_test'
-        outcomes: old: 1, new: 0
-        edge: 'new'
+        some_other_test:
+          key: 'some_other_test'
+          outcomes: old: 1, new: 0
+          edge: 'new'
+
+  after ->
+    benv.teardown()

   beforeEach ->
     @req = cookies: {}


Use camelCase for options and improve the syntax using _.defaults()

--- src/chaplin/dispatcher.coffee
+++ src/chaplin/dispatcher.coffee
@@ -28,9 +28,9 @@

     initialize: (options = {}) ->
       # Merge the options
-      defaults =
-        controller_path: 'controllers/'
-        controller_suffix: '_controller'
+      @settings = _(options).defaults
+        controllerPath: 'controllers/'
+        controllerSuffix: '_controller'

       @settings = _.extend(defaults, options)

@@ -89,8 +89,8 @@
     # The default implementation uses require() from a AMD module loader
     # like RequireJS to fetch the constructor.
     loadController: (controllerName, handler) ->
-      controllerFileName = utils.underscorize(controllerName) + @settings.controller_suffix
-      require [@settings.controller_path + controllerFileName], handler
+      controllerFileName = utils.underscorize(controllerName) + @settings.controllerSuffix
+      require [@settings.controllerPath + controllerFileName], handler

     # Handler for the controller lazy-loading
     controllerLoaded: (controllerName, action, params, ControllerConstructor) ->


url_encode & url_decode improved.

url_encode & url_decode improved.

--- coffee_mate.coffee
+++ coffee_mate.coffee
@@ -82,14 +82,14 @@

 ######################### url helpers ############################

-url_encode = (obj) ->
-	("#{encodeURIComponent(k)}=#{encodeURIComponent(v)}" for k, v of obj).join('&')
-
-url_decode = (url) ->
+url_encode = (obj, packer = ((o) -> str(o))) ->
+	("#{encodeURIComponent(k)}=#{encodeURIComponent packer v}" for k, v of obj).join('&')
+
+url_decode = (url, unpacker = ((s) -> s)) ->
 	d = {}
 	for s in url.match /[^?=&]+=[^&]*/g
 		[..., k, v] = s.match /([^=]+)=(.*)/
-		d[decodeURIComponent(k)] = decodeURIComponent(v)
+		d[decodeURIComponent(k)] = (unpacker decodeURIComponent v)
 	d

 ###################### simple pseudo-random ######################


Removed commented code

Removed commented code

--- core/app/assets/javascripts/base/registration_forms.coffee
+++ core/app/assets/javascripts/base/registration_forms.coffee
@@ -2,7 +2,6 @@
   _.forEach fieldErrors, (errorMessage, field) ->
     el = $("#" + formId + "_user_" + field)

-    # el.css border: "1px solid rgb(206, 0, 0)"
     el.addClass "error"
     showErrorPopover el, "#{field} #{errorMessage}", formId


monkeytails take up less chars ;)

monkeytails take up less chars ;)

--- core/app/backbone/views/auto_completed_channel_view.coffee
+++ core/app/backbone/views/auto_completed_channel_view.coffee
@@ -8,20 +8,20 @@
   template: "channels/_auto_completed_channel"

   initialize: ->
-    this.queryRegex = new RegExp(this.options.query, "gi")
+    @queryRegex = new RegExp(@options.query, "gi")

   templateHelpers: ->
     view = this
-    return { highlightedTitle: -> this.title.replace(view.queryRegex, "$&")}
+    return { highlightedTitle: -> @title.replace(view.queryRegex, "$&")}

   onRender: ->
-    if this.model.get('user_channel' )
-      this.$el.addClass('user-channel')
+    if @model.get('user_channel' )
+      @$el.addClass('user-channel')

   activate: ->
-    this.$el.addClass 'active'
+    @$el.addClass 'active'

   deActivate: ->
-    this.$el.removeClass 'active'
+    @$el.removeClass 'active'

 )


a common fetch, for now

a common fetch, for now

--- bin/precious.coffee
+++ bin/precious.coffee
@@ -61,11 +61,13 @@
   input

-# This is the call.
-# Note the extra parse / stringify just for the sake of easy defaults.
-# It isn't the most efficient way to do it, but it does offer convenience.
+# This is the call.  Not too efficient for the sake of easy cli
+# defaults / convenience.  For something better use precious as a module lib.
+# Waiting for Node's new child_process streams interface, as well as
+# a second implementation method that will not spawn a process at all.
+# Will revist / refactor `fetch` then.
 fetch = (what) ->
-  stream = ephemeris convenient json.parse what
+  stream = ephemeris convenient what
   stream.stderr.on 'data', (error) ->
     console.error '\nSpawned child_process error.\n'.red + error
     process.exit(1)
@@ -86,12 +88,11 @@
     parser = JSONStream.parse /./
     process.stdin.pipe parser

-    parser.on "data", (data) ->
-      (ephemeris convenient data).stdout.pipe process.stdout
-
     parser.on "error", (err) ->
       console.error "Bad JSON, parser error: ".red + err.message
       process.exit(1)
+
+    parser.on "data", (data) -> fetch data

   # The rest of the options are just for convenience / variety.
@@ -106,10 +107,10 @@
 An error has ocurred.  Please double-check the file & path.".red
         console.error err
         process.exit(1)
-      else fetch data
+      else fetch json.parse data

   when '-o', '--object'
-    if args[1]? then fetch args[1]
+    if args[1]? then fetch json.parse args[1]
     else
       console.error "\nNo JSON provided, format instructions follow...".red
       man "precious-json", 1, ->


Fixed API url.

Fixed API url.

--- app/assets/javascripts/app/controllers/reset_password.js.coffee
+++ app/assets/javascripts/app/controllers/reset_password.js.coffee
@@ -42,7 +42,7 @@
     App.Com.ajax(
       id:   'password_reset',
       type: 'POST',
-      url:  '/users/password_reset',
+      url:  '/api/users/password_reset',
       data: JSON.stringify(params),
       processData: true,
       success: @success,
@@ -83,7 +83,7 @@
     App.Com.ajax(
       id:   'passwort_reset_verify',
       type: 'POST',
-      url:  '/users/password_reset_verify',
+      url:  '/api/users/password_reset_verify',
       data: JSON.stringify(params),
       processData: true,
       success: @render_success
@@ -118,7 +118,7 @@
     App.Com.ajax(
       id:   'password_reset_verify',
       type: 'POST',
-      url:  '/users/password_reset_verify',
+      url:  '/api/users/password_reset_verify',
       data: JSON.stringify(params),
       processData: true,
       success: @render_changed_success


Disable because of missing server event modules.

--- app/assets/javascripts/app/lib/app_post/_collection_base.coffee
+++ app/assets/javascripts/app/lib/app_post/_collection_base.coffee
@@ -49,9 +49,9 @@
     delete @callbacks[counter]

   fetch: =>
-    if App.WebSocket.support() && App.WebSocket.channel()
-      App.WebSocket.send(event: @event)
-      return
+    #if App.WebSocket.support() && App.WebSocket.channel()
+    #  App.WebSocket.send(event: @event)
+    #  return

     return if @fetchActive
     @fetchActive = true


Debug suppressed error logs lab80/hellomoney#1967

--- error_logger_server.coffee
+++ error_logger_server.coffee
@@ -1,4 +1,4 @@
-SUPPRESSED_ERRORS = _.map(Meteor.settings?.errors?.suppressedErrors or {}, (e) -> e.subject)
+SUPPRESSED_ERRORS = _.map(Meteor.settings?.errors?.suppressedErrors or [], (e) -> e.subject)
 LOG_ROUTE = '/errorlog'
 ERROR_FIELDS = ['errorType', 'ipAddress', 'location', 'browser', 'details']
 ERROR_TYPE_TO_SUBJECT_GETTER =
@@ -7,7 +7,7 @@
   AJAX: (error) -> error.details.xhrStatusText
   METEOR: (error) -> error.details.message
 _isSuppressedError = (error) ->
-  _.has(SUPPRESSED_ERRORS, error.details.message.trim())
+  _.contains(SUPPRESSED_ERRORS, error.details.message.trim())

 ErrorLogger =
   _collection: new Mongo.Collection('errorlogs')


Update minipanebody.cjsx

Update minipanebody.cjsx

--- views/components/main/parts/miniship-parts/minipanebody.cjsx
+++ views/components/main/parts/miniship-parts/minipanebody.cjsx
@@ -41,7 +41,7 @@
   # retreat status
   # if shipId == escapeId || shipId == towId
     # return status = 0
-  # reparing
+  # repairing
   if shipId in _ndocks
     return status = 1
   # supply
@@ -56,7 +56,7 @@
   # special 3 locked phase 3
   else if  _ships[shipId].api_sally_area == 3
     return status = 4
-  # special 3 locked phase 3
+  # special 4 locked phase 4
   else if _ships[shipId].api_sally_area == 4
     return status = 5
   return status


<debug> Deleting now useless logs.

<debug> Deleting now useless logs.

--- server/models/driver.coffee
+++ server/models/driver.coffee
@@ -166,8 +166,11 @@

 				# Initialize the driver:
 				initializeDriver = () ->
-					unless driverModule = require dirName + file.name
+					try
+						driverModule = require(dirName + file.name)
+					catch
 						callback 'Couldn\'t find module', null
+
 					unless isActuator = driverModule.isActuator or isSensor = driverModule.isSensor # or (!isActuator and !isSensor)
 						callback 'Driver not defining if for Sensors and/or Actuators', null


The URL for rendering a single dashboard was changed between 2.0.2 and newer

--- src/grafana.coffee
+++ src/grafana.coffee
@@ -54,9 +54,12 @@
       if dashboard.dashboard
         # 2.0.2+: Changed in https://github.com/grafana/grafana/commit/e5c11691203fe68958e66693e429f6f5a3c77200
         data = dashboard.dashboard
+        # The URL was changed in https://github.com/grafana/grafana/commit/35cc0a1cc0bca453ce789056f6fbd2fcb13f74cb
+        apiEndpoint = "dashboard-solo"
       else
         # 2.0.2 and older
         data = dashboard.model
+        apiEndpoint = "dashboard/solo"

       # Support for templated dashboards
       robot.logger.debug data.templating.list
@@ -78,7 +81,7 @@
           from = from.trim()
           to = to.trim()

-          imageUrl = "#{grafana_host}/render/dashboard/solo/db/#{slug}/?panelId=#{panel.id}&width=1000&height=500&from=#{from}&to=#{to}"
+          imageUrl = "#{grafana_host}/render/#{apiEndpoint}/db/#{slug}/?panelId=#{panel.id}&width=1000&height=500&from=#{from}&to=#{to}"
           link = "#{grafana_host}/dashboard/db/#{slug}/?panelId=#{panel.id}&fullscreen&from=#{from}&to=#{to}"
           msg.send "#{formatTitleWithTemplate(panel.title, template_map)}: #{imageUrl} - #{link}"


feat(vm.import): throw if `type` is `xva` and `data` is provided (#506)

--- src/api/vm.coffee
+++ src/api/vm.coffee
@@ -1022,6 +1022,9 @@

 # TODO: "sr_id" can be passed in URL to target a specific SR
 import_ = $coroutine ({ data, host, sr, type }) ->
+  if data and type is 'xva'
+    throw invalidParameters('unsupported field data for the file type xva')
+
   if not sr
     if not host
       throw invalidParameters('you must provide either host or SR')


Logs.test: disable papertrail log tests

Logs.test: disable papertrail log tests

--- servers/lib/server/handlers/api/logs.test.coffee
+++ servers/lib/server/handlers/api/logs.test.coffee
@@ -180,7 +180,7 @@
       done()

-  it 'should send HTTP 200 and the result in data if session is valid', (done) ->
+  it.skip 'should send HTTP 200 and the result in data if session is valid', (done) ->

     { client, group } = TESTUSERS.admin

@@ -202,7 +202,7 @@
       done()

-  it 'should send HTTP 200 and the result in data if apiToken is valid', (done) ->
+  it.skip 'should send HTTP 200 and the result in data if apiToken is valid', (done) ->

     { apiToken, group } = TESTUSERS.team


Don't repeat modifier keys in keystrokeForKeyboardEvent

I didn't add specs for this because it would have been completely
mocked.

Closes atom/keybinding-resolver#1

--- src/helpers.coffee
+++ src/helpers.coffee
@@ -38,6 +38,9 @@
     key = key.toUpperCase() if /^[a-z]$/.test(key)
   else
     key = key.toLowerCase() if /^[A-Z]$/.test(key)
+
+  # If only a modifier was pressed, null out the key
+  key = null if key in ["meta", "shift", "control", "alt"]

   keystroke.push 'cmd' if event.metaKey
   keystroke.push(key) if key?


[#98] Updated tooltip with new mac-tooltip-disabled option.

--- src/directives/tooltip.coffee
+++ src/directives/tooltip.coffee
@@ -9,6 +9,7 @@
 @param {String}  mac-tooltip-direction Direction of tooltip (default 'top')
 @param {String}  mac-tooltip-trigger   How tooltip is triggered (default 'hover')
 @param {Boolean} mac-tooltip-inside    Should the tooltip be appended inside element (default false)
+@param {Expr}    mac-tooltip-disabled  Disable and enable tooltip
 ###

 angular.module("Mac").directive "macTooltip", [
@@ -17,9 +18,10 @@
     restrict: "A"

     link: (scope, element, attrs) ->
-      tooltip = null
-      text    = ""
-      enabled = false
+      tooltip  = null
+      text     = ""
+      enabled  = false
+      disabled = false

       defaults =
         direction: "top"
@@ -29,6 +31,8 @@
       opts = util.extendAttributes "macTooltip", defaults, attrs

       showTip = (event) ->
+        return if disabled
+
         tip       = if opts.inside then element else $(document.body)
         tooltip   = $("""

#{text}

""")
         tip.append tooltip
@@ -90,6 +94,9 @@

             enabled = true

+      scope.$watch attrs.macTooltipDisabled, (value) ->
+        disabled = value
+
       scope.$on "$destroy", ->
         removeTip() if tooltip?
 ]


Removed useless comments from station.coffee

--- Stations/static/js/station.coffee
+++ Stations/static/js/station.coffee
@@ -25,28 +25,4 @@
     $.getJSON url, (opts) ->
         chart = new Highcharts.Chart(opts)

-  graph sensor for sensor in window.sensorList
-
-###
-var curStation = '{{selStation.slug}}';
-var sensorList = JSON.parse('{{default_sensor_list}}');
-$(function() {
-    update();
-    $('#update-btn').click(update);
-});
-function update() {
-    var start = $("#start-date").datepicker("getDate");
-    var end = $("#end-date").datepicker("getDate");
-    for (var i in sensorList) {
-        $("#graphs").append($("

"));
-        $.getJSON("{% url 'graph-view' '888' '999' %}"
-            .replace('999', sensorList[i])
-            .replace('888', curStation) +
-            "?start=" + (start.getMonth()+1)+"-"+start.getDate()+'-'+start.getFullYear() +
-            "&end=" + (end.getMonth()+1)+"-"+end.getDate()+'-'+end.getFullYear(),
-            function(opts) {
-                var chart = new Highcharts.Chart(opts);
-            });
-    }
-}
-###+  graph sensor for sensor in window.sensorList


added min/max for histogram aggregators

added min/max for histogram aggregators

--- src/driver/druidDriver.coffee
+++ src/driver/druidDriver.coffee
@@ -380,46 +380,65 @@
               return

           when 'count', 'sum', 'min', 'max'
-            if apply.filter
-              { jsFilter, context } = @filterToJS(apply.filter)
-              fieldNames = []
-              varNames = []
-              for fieldName, varName of context
-                fieldNames.push(fieldName)
-                varNames.push(varName)
-
-              [zero, jsAgg] = aggregateToJS[apply.aggregate]
-
-              if apply.aggregate is 'count'
-                jsIf = "(#{jsFilter}?1:#{zero})"
+            if @approximate and apply.aggregate in ['min', 'max'] and /_hist$/.test(apply.attribute)
+              # A hacky way to determine that this is a histogram aggregated column (it ends with _hist)
+
+              histogramAggregationName = @addAggregation {
+                type: "approxHistogramFold"
+                fieldName: apply.attribute
+              }
+              postAggregation = {
+                type: apply.aggregate
+                fieldName: histogramAggregationName
+              }
+
+              if returnPostAggregation
+                return postAggregation
               else
-                fieldNames.push(apply.attribute)
-                varNames.push('a')
-                jsIf = "(#{jsFilter}?a:#{zero})"
-
-              aggregation = {
-                type: "javascript"
-                name: applyName
-                fieldNames: fieldNames
-                fnAggregate: "function(cur,#{varNames.join(',')}){return #{jsAgg('cur', jsIf)};}"
-                fnCombine: "function(pa,pb){return #{jsAgg('pa', 'pb')};}"
-                fnReset: "function(){return #{zero};}"
-              }
+                postAggregation.name = applyName
+                @addPostAggregation(postAggregation)
+                return
             else
-              aggregation = {
-                type: if apply.aggregate is 'sum' then 'doubleSum' else apply.aggregate
-                name: applyName
-              }
-
-              if apply.aggregate isnt 'count'
-                throw new Error("#{apply.aggregate} must have an attribute") unless apply.attribute
-                aggregation.fieldName = apply.attribute
-
-            aggregationName = @addAggregation(aggregation)
-            if returnPostAggregation
-              return { type: "fieldAccess", fieldName: aggregationName }
-            else
-              return
+              if apply.filter
+                { jsFilter, context } = @filterToJS(apply.filter)
+                fieldNames = []
+                varNames = []
+                for fieldName, varName of context
+                  fieldNames.push(fieldName)
+                  varNames.push(varName)
+
+                [zero, jsAgg] = aggregateToJS[apply.aggregate]
+
+                if apply.aggregate is 'count'
+                  jsIf = "(#{jsFilter}?1:#{zero})"
+                else
+                  fieldNames.push(apply.attribute)
+                  varNames.push('a')
+                  jsIf = "(#{jsFilter}?a:#{zero})"
+
+                aggregation = {
+                  type: "javascript"
+                  name: applyName
+                  fieldNames: fieldNames
+                  fnAggregate: "function(cur,#{varNames.join(',')}){return #{jsAgg('cur', jsIf)};}"
+                  fnCombine: "function(pa,pb){return #{jsAgg('pa', 'pb')};}"
+                  fnReset: "function(){return #{zero};}"
+                }
+              else
+                aggregation = {
+                  type: if apply.aggregate is 'sum' then 'doubleSum' else apply.aggregate
+                  name: applyName
+                }
+
+                if apply.aggregate isnt 'count'
+                  throw new Error("#{apply.aggregate} must have an attribute") unless apply.attribute
+                  aggregation.fieldName = apply.attribute
+
+              aggregationName = @addAggregation(aggregation)
+              if returnPostAggregation
+                return { type: "fieldAccess", fieldName: aggregationName }
+              else
+                return

           when 'uniqueCount'
             throw new Error("approximate queries not allowed") unless @approximate


added checks on error and response status code for smoke tests to make them easier to follow in logs

--- services/web/test/smoke/coffee/SmokeTests.coffee
+++ services/web/test/smoke/coffee/SmokeTests.coffee
@@ -37,9 +37,11 @@
 			headers:
 				"X-Forwarded-Proto": "https"
 		}, (error, response, body) ->
+			expect(error, "smoke test: error in getting project").to.not.exist
+			expect(response.statusCode, "smoke test: response code is not 200 getting project").to.equal(200)
 			# Check that the project id is present in the javascript that loads up the project
 			match = !!body.match("\"project_id\":\"#{Settings.smokeTest.projectId}\"")
-			expect(match).to.equal true
+			expect(match, "smoke test: project page html does not have project_id").to.equal true
 			done()

 	it "the project list", (done) ->
@@ -49,7 +51,9 @@
 			headers:
 				"X-Forwarded-Proto": "https"
 		}, (error, response, body) ->
-			expect(!!body.match("<title>Your Projects - Online LaTeX Editor ShareLaTeX</title>")).to.equal true
-			expect(!!body.match("
Projects
")).to.equal true
+			expect(error, "smoke test: error returned in getting project list").to.not.exist
+			expect(response.statusCode, "smoke test: response code is not 200 getting project life").to.equal(200)
+			expect(!!body.match("<title>Your Projects - Online LaTeX Editor ShareLaTeX</title>"), "smoke test: body does not have correct title").to.equal true
+			expect(!!body.match("
Projects
"), "smoke test: body does not have correct h1").to.equal true
 			done()


prep for async http requests

prep for async http requests

--- chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
+++ chrome/content/zotero-better-bibtex/zotero-better-bibtex.coffee
@@ -447,6 +447,19 @@
   @loadTranslators()
   @extensionConflicts()

+  # monkey-patch Zotero.Server.DataListener.prototype._headerFinished for async handling of web-translation requests
+  Zotero.Server.DataListener::_headerFinished = ((original) ->
+    return ->
+      header = @header.split(/\r?\n/)
+
+      req = header[0]?.exec(/^([A-Z]+) (.*)/)
+
+      return original.apply(@, arguments) unless req?[1]?.indexOf('/better-bibtex/') == 0
+
+      return original.apply(@, arguments)
+    )(Zotero.Server.DataListener::_headerFinished)
+
+
   # monkey-patch Zotero.Search.prototype.save to trigger auto-exports
   Zotero.Search::save = ((original) ->
     return (fixGaps) ->


Work on RequireJS Jasmine test.

Work on RequireJS Jasmine test.

--- lms/static/coffee/spec/requirejs_spec.coffee
+++ lms/static/coffee/spec/requirejs_spec.coffee
@@ -1,51 +1,68 @@
-describe "RequireJS namespacing", ->
-  beforeEach ->
-    @addMatchers
-      requirejsTobeUndefined: ->
-        typeof requirejs is "undefined"
+describe('RequireJS namespacing', function() {
+  beforeEach(function() {
+    this.addMatchers({
+      requirejsTobeUndefined: function() {
+        return (typeof requirejs === 'undefined');
+      },
+      requireTobeUndefined: function() {
+        return (typeof require === 'undefined');
+      },
+      defineTobeUndefined: function() {
+        return (typeof define === 'undefined');
+      },
+    });
+  });

-      requireTobeUndefined: ->
-        typeof require is "undefined"
+    it('check that the RequireJS object is present in the global namespace', function() {
+        expect(RequireJS).toEqual(jasmine.any(Object));
+        expect(window.RequireJS).toEqual(jasmine.any(Object));
+    });

-      defineTobeUndefined: ->
-        typeof define is "undefined"
+    it('check that requirejs(), require(), and define() are not in the global namespace', function () {
+        expect({}).requirejsTobeUndefined();
+        expect({}).requireTobeUndefined();
+        expect({}).defineTobeUndefined();

+        expect(window.requirejs).not.toBeDefined();
+        expect(window.require).not.toBeDefined();
+        expect(window.define).not.toBeDefined();
+    });
+});

-  it "check that the RequireJS object is present in the global namespace", ->
-    expect(RequireJS).toEqual jasmine.any(Object)
-    expect(window.RequireJS).toEqual jasmine.any(Object)
+describe('RequireJS module creation', function() {
+    var inDefineCallback, inRequireCallback;

-  it "check that requirejs(), require(), and define() are not in the global namespace", ->
-    expect({}).requirejsTobeUndefined()
-    expect({}).requireTobeUndefined()
-    expect({}).defineTobeUndefined()
-    expect(window.requirejs).not.toBeDefined()
-    expect(window.require).not.toBeDefined()
-    expect(window.define).not.toBeDefined()
+    it('check that we can use RequireJS define() and require() a module', function() {
+        runs(function () {
+            inDefineCallback = false;
+            inRequireCallback = false;

+            RequireJS.define('test_module', [], function () {
+                inDefineCallback = true;

-describe "RequireJS module creation", ->
-  inDefineCallback = undefined
-  inRequireCallback = undefined
-  it "check that we can use RequireJS define() and require() a module", ->
-    runs ->
-      inDefineCallback = false
-      inRequireCallback = false
-      RequireJS.define "test_module", [], ->
-        inDefineCallback = true
-        module_status: "OK"
+                return {
+                    'module_status': 'OK'
+                };
+            });

-      RequireJS.require ["test_module"], (test_module) ->
-        inRequireCallback = true
-        expects(test_module.module_status).toBe "OK"
+            RequireJS.require(['test_module'], function (test_module) {
+                inRequireCallback = true;

+                expect(test_module.module_status).toBe('OK');
+            });
+        });

-    waitsFor (->
-      return false  if (inDefineCallback isnt true) or (inRequireCallback isnt true)
-      true
-    ), "We should eventually end up in the defined callback", 1000
-    runs ->
-      expects(inDefineCallback).toBeTruthy()
-      expects(inRequireCallback).toBeTruthy()
+        waitsFor(function () {
+            if ((inDefineCallback !== true) || (inRequireCallback !== true)) {
+                return false;
+            }

+            return true
+        }, 'We should eventually end up in the defined callback', 1000);

+        runs(function () {
+            expect(inDefineCallback).toBeTruthy();
+            expect(inRequireCallback).toBeTruthy();
+        });
+    });
+});


Activity: unused classes from sidebar removed

--- client/Social/Activity/ContentDisplays/activitysideviews.coffee
+++ client/Social/Activity/ContentDisplays/activitysideviews.coffee
@@ -41,226 +41,3 @@
     <h3>#{@getOption 'title'}{{> @showAllLink}}</h3>
     {{> @listView}}
     """
-
-class UserGroupList extends ActivitySideView
-
-  constructor: (options = {}, data) ->
-
-    options.cssClass  = KD.utils.curry "user-group-list hidden", options.cssClass
-    options.title     = "Your Groups"
-    options.viewTagName = "ul"
-    options.itemType = "activity-ticker-item"
-
-    @itemClass = PopupGroupListItem
-
-    super options, data
-
-
-  viewAppended:->
-
-    super
-
-    KD.whoami().fetchGroups (err, items) =>
-      @renderItems null, (item for item in items when item.group.slug isnt "koding")
-      if items.length - 1
-        @parent.emit 'TopOffsetShouldBeFixed'
-        @show()
-
-class DummyUsers extends ActivitySideView
-
-  skip = -3
-
-  constructor:(options={}, data)->
-
-    @itemClass = MembersListItemView
-
-    super options, data
-
-    KD.singletons.groupsController.ready =>
-      skip += 3
-      KD.getGroup().fetchNewestMembers {}, {limit : 3, skip}, @bound 'renderItems'
-
-
-class DummyTopics extends ActivitySideView
-
-  skip = -3
-
-  constructor:(options={}, data)->
-
-    @itemClass = ActiveTopicItemView
-
-    super options, data
-
-    skip += 3
-
-    KD.remote.api.JTag.some {group : 'koding'},
-      limit  : 3
-      skip   : skip
-      sort   : "counts.followers" : -1
-    , (err, topics)=>
-      if err or topics.length is 0
-        @hide()
-      else
-        @renderItems err, topics
-
-
-
-class ActiveUsers extends ActivitySideView
-
-  constructor:(options={}, data)->
-    {entryPoint} = KD.config
-    if entryPoint?.type is "group" then group = entryPoint.slug else group = "koding"
-
-    @itemClass       = MembersListItemView
-    options.title    = if group is "koding" then "Active users" else "New Members"
-    options.cssClass = "active-users"
-
-    super options, data
-
-    @showAllLink?.destroy()
-    @showAllLink = new KDCustomHTMLView
-      tagName : "a"
-      partial : "show all"
-      cssClass: "show-all-link hidden"
-      click   : (event) ->
-        KD.singletons.router.handleRoute "/Members"
-        KD.mixpanel "Show all members, click"
-    , data
-
-    if group is "koding"
-      KD.remote.api.ActiveItems.fetchUsers {}, @bound 'renderItems'
-    else
-      KD.singletons.groupsController.ready =>
-        currentGroup = KD.singletons.groupsController.getCurrentGroup()
-        currentGroup.fetchNewestMembers {}, "limit" : 10, @bound 'renderItems'
-
-
-class ActiveTopics extends ActivitySideView
-
-  constructor:(options={}, data)->
-
-    @itemClass       = ActiveTopicItemView
-    options.title    = "Popular topics"
-    options.cssClass = "active-topics"
-
-    super options, data
-
-    {entryPoint} = KD.config
-    if entryPoint?.type is "group" then group = entryPoint.slug else group = "koding"
-
-    @showAllLink?.destroy()
-    @showAllLink = new KDCustomHTMLView
-      tagName : "a"
-      partial : "show all"
-      cssClass: "show-all-link"
-      click   : (event) ->
-        if group is "koding" then route = "/Topics" else route = "/#{group}/Topics"
-        KD.singletons.router.handleRoute route
-        KD.mixpanel "Show all topics, click"
-    , data
-
-    if group is "koding"
-      KD.remote.api.ActiveItems.fetchTopics {group}, @bound 'renderItems'
-    else
-      KD.remote.api.JTag.some {group},
-        limit  : 16
-        sort   : "counts.followers" : -1
-      , (err, topics)=>
-        if err or topics.length is 0
-          @hide()
-        else
-          @renderItems err, topics
-
-
-class GroupDescription extends KDView
-
-  constructor:(options={}, data)->
-    super options, data
-
-    {groupsController} = KD.singletons
-    groupsController.ready =>
-      group = groupsController.getCurrentGroup()
-
-      @innerContaner = new KDCustomHTMLView cssClass : "right-block-box"
-
-      {body}  = group
-      body   ?= ""
-      hasBody = Boolean body.trim().length
-      isAdmin = "admin" in KD.config.roles
-
-      edit = new CustomLinkView
-        title    : "Group settings"
-        cssClass : if isAdmin then "show-all-link" else "show-all-link hidden"
-        click    : (event)->
-          KD.utils.stopDOMEvent event
-          KD.singletons.router.handleRoute "/Dashboard"  if isAdmin
-
-      @titleView = new KDCustomHTMLView
-        tagName         : "h3"
-        pistachioParams : { edit }
-        pistachio       : "{{ #(title)}} {{> edit}}"
-      , group
-
-      @bodyView = new KDCustomHTMLView
-        tagName   : "p"
-        pistachio : "{{ #(body) or ''}}"
-        cssClass  : "group-description"
-      , group
-
-
-      @innerContaner.addSubView @titleView
-      @innerContaner.addSubView @bodyView
-      @addSubView @innerContaner
-
-      if "admin" in KD.config.roles
-        @bodyView.setPartial "You can have a short description for your group here"  unless hasBody
-
-
-class GroupMembers extends ActivitySideView
-
-  constructor:(options={}, data)->
-    @itemClass       = GroupMembersListItemView
-    options.title    = "Group members"
-    options.cssClass = "group-members"
-
-    super options, data
-
-    {entryPoint} = KD.config
-    if entryPoint?.type is "group" then groupSlug = entryPoint.slug else groupSlug = "koding"
-
-    @showAllLink?.destroy()
-    @showAllLink = new KDCustomHTMLView
-      tagName    : "a"
-      partial    : "See All"
-      cssClass   : "show-all-link"
-      click      : (event) ->
-        KD.singletons.router.handleRoute "/#{groupSlug}/Members"
-        KD.mixpanel "Show all members, click"
-    , @getData()
-
-    {groupsController} = KD.singletons
-    groupsController.ready =>
-      group = groupsController.getCurrentGroup()
-      group.fetchMembers {}, limit : 12, (err, members) =>
-        @renderItems err, members
-        if members.length < 12
-          groupsController.on "MemberJoinedGroup", (data) =>
-            {constructorName, id} = data.member
-            KD.remote.cacheable constructorName, id, (err, account)=>
-              return console.error "account is not found", err if err or not account
-              @listController.addItem account
-
-
-
-
-class GroupMembersListItemView extends KDListItemView
-
-  constructor: (options = {}, data) ->
-    super options, data
-    @avatar      = new AvatarView
-      size       : width : 60, height : 60
-      showStatus : yes
-    , @getData()
-    @addSubView @avatar
-
-  viewAppended:JView::viewAppended


Activity: Fix wrong comment ordering on pageLoad bug

--- client/Social/Activity/views/comments/listviewcontroller.coffee
+++ client/Social/Activity/views/comments/listviewcontroller.coffee
@@ -21,8 +21,7 @@

   instantiateListItems: (items) ->
-
-    super items.sort (a, b) -> a.createdAt - b.createdAt
+    super items.sort (a, b) -> new Date(a.createdAt) - new Date(b.createdAt)

   addItem: (item, index) ->


Client: Activity: Deleted unnecessary event dispatcher from action creators

--- client/activity/lib/flux/actions/realtime/actioncreators.coffee
+++ client/activity/lib/flux/actions/realtime/actioncreators.coffee
@@ -80,8 +80,6 @@
     unreadCount : unreadCount
     channelId   : channel.id

-  _reactor.dispatch actions.UNSET_LOADED_WITH_SCROLL, channelId: channel.id
-

 module.exports = {
   bindChannelEvents


rollbar: notify shared hosting failures

rollbar: notify shared hosting failures

--- client/app/MainApp/filetree/controllers/findercontroller.coffee
+++ client/app/MainApp/filetree/controllers/findercontroller.coffee
@@ -101,6 +101,8 @@

       kiteFailureTimer = @utils.wait 5000, =>
         unless @treeLoaded
+          KD.logToExternal "SharedHosting fetching files failed"
+
           msg = "Couldn't fetch files! Click to retry"
           @treeController.notify msg, 'clickable', "Sorry, a problem occured while communicating with servers, please try again later.", yes
           log msg+" fired from initial kiteFailureTimer"


Add read only functionnality on published node

--- BackofficeBundle/Resources/public/coffee/page/nodeView.coffee
+++ BackofficeBundle/Resources/public/coffee/page/nodeView.coffee
@@ -66,8 +66,12 @@
       displayClass: (if @node.get("bo_direction") is "v" then "inline" else "block")
     )
     @$el.find('ul.ui-model-areas').first().append  areaView.render().el
-    $("ul.ui-model-areas, ul.ui-model-blocks", @$el).each ->
-      refreshUl $(this)
+    if @node.attributes.status.published
+        $('.ui-model *', @el).unbind()
+        $('.js-widget-panel').hide()
+    else
+      $("ul.ui-model-areas, ul.ui-model-blocks", @$el).each ->
+        refreshUl $(this)
     return
   addVersionToView: ->
     viewContext = this


point and triangle events fix

point and triangle events fix

These events were never fired by Morpher: point:add, point:remove,
triangle:add, triangle:remove

--- source/javascripts/morpher/lib/morpher.js.coffee
+++ source/javascripts/morpher/lib/morpher.js.coffee
@@ -109,17 +109,21 @@

   addPointHandler: (image, point, pointParams = null) =>
     position = pointParams || image.getRelativePositionOf(point)
-    for img in @images.concat @mesh
+    for img in @images
       if img.points.length < image.points.length
         img.addPoint position
         return
-    @trigger 'point:add', this
+    if @mesh.points.length < image.points.length
+      @mesh.addPoint position
+      @trigger 'point:add', this

   removePointHandler: (image, point, index) =>
-    for img in @images.concat @mesh
+    for img in @images
       if img.points.length > image.points.length
         img.removePoint index
         return
+    if @mesh.points.length > image.points.length
+      @mesh.removePoint index
     for triangle in @triangles
       for v, k in triangle
         triangle[k] -= 1 if v >= index
@@ -141,19 +145,23 @@
   addTriangleHandler: (image, i1, i2, i3, triangle) =>
     if image.triangles.length > @triangles.length && !@triangleExists(i1, i2, i3)
       @triangles.push [i1, i2, i3]
-    for img in @images.concat @mesh
+    for img in @images
       if img.triangles.length < @triangles.length
         img.addTriangle i1, i2, i3
         return
+    if @mesh.triangles.length < @triangles.length
+      @mesh.addTriangle i1, i2, i3
     @trigger 'triangle:add', this

   removeTriangleHandler: (image, triangle, index) =>
     if image.triangles.length < @triangles.length
       delete @triangles.splice index, 1
-    for img in @images.concat @mesh
+    for img in @images
       if img.triangles.length > @triangles.length
         img.removeTriangle index
         return
+    if @mesh.triangles.length > @triangles.length
+      @mesh.removeTriangle index
     @trigger 'triangle:remove', this


fixed misalign axis labels

fixed misalign axis labels

--- src/viz/types/helpers/graph/includes/svg.coffee
+++ src/viz/types/helpers/graph/includes/svg.coffee
@@ -152,8 +152,7 @@
         .style "text-anchor", if rotated then "end" else "middle"
         .call tickFont, "x"
         .each "end", (d) ->
-          if vars.x.ticks.wrap and !vars.x.ticks.hidden and
-             vars.x.ticks.visible.indexOf(d) >= 0
+          if !vars.x.ticks.hidden and vars.x.ticks.visible.indexOf(d) >= 0
             textwrap()
               .container(d3.select(this))
               .height(vars.x.ticks.maxHeight)


#1233 Fix Bounding box creation

#1233 Fix Bounding box creation

--- app/assets/javascripts/oxalis/view/settings/category_views/bounding_box_category_view.coffee
+++ app/assets/javascripts/oxalis/view/settings/category_views/bounding_box_category_view.coffee
@@ -30,7 +30,7 @@

     [minX, minY, minZ, maxX, maxY, maxZ] = Utils.stringToNumberArray(value)

-    if isValid = minX > maxX or minY > maxY or minZ > maxZ
+    if isInvalid = minX > maxX or minY > maxY or minZ > maxZ

       # Unfortunately we cannot use HTML5 form validation here since the text box
       # is not part of a form and a submit event is missing :-(
@@ -42,6 +42,6 @@
       # reset error state
       @ui.text[0].setCustomValidity("")

-    return isValid
+    return not isInvalid

 module.exports = BoundingBoxCategory


fix the accidental replacement of @usernames in status update bodies

--- client/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
+++ client/app/Applications/Activity.kdapplication/ListItems/ActivityListItemStatusUpdate.coffee
@@ -21,7 +21,7 @@

   attachTooltipAndEmbedInteractivity:=>
     @$("p.status-body a").each (i,element)=>
-      href = $(element).attr("data-original-url") or ""
+      href = $(element).attr("data-original-url") or $(element).attr("href") or ""

       twOptions = (title) ->
          title : title, placement : "above", offset : 3, delayIn : 300, html : yes, animate : yes, className : "link-expander"
@@ -30,7 +30,8 @@

       # unless $(element).text() is href.replace(/^(((ht|f)tp(s)?\:)?\/\/)/,"").replace(/\/$/,"") or\
       #        "http://"+$(element).text().replace("","") is href.replace(/\/$/,"")
-      $(element).twipsy twOptions("External Link : "+href+"")
+      unless /^(#!)/.test href
+        $(element).twipsy twOptions("External Link : "+href+"")
         # linkIsShortened = yes

       element


Update app.coffee

Update app.coffee

--- source/assets/js/app.coffee
+++ source/assets/js/app.coffee
@@ -44,11 +44,11 @@
     phone: "",
     awesome: "No Opinion",
     people: [
-      { name: "George Clooney", like: false },
-      { name: "Bill Murray", like: false },
+      { name: "George Clooney", like: true },
+      { name: "Bill Murray", like: true },
       { name: "Paris Hilton", like: false },
       { name: "Snooki", like: false },
-      { name: "Ghandi", like: false }
+      { name: "Ghandi", like: true }
     ],
     randomtext: ""
   }


fix arg passing for forEach on helper item

--- test-integration/helpers/ui/test-element.coffee
+++ test-integration/helpers/ui/test-element.coffee
@@ -142,9 +142,12 @@
   @param {Function} forEachFunction The last parameter, if provided and is a function, will be run on each WebElement
   @param {Function} [forEachFunction2] If an additional function is available, this will run once before.
   ###
-  forEach: (args..., forEachFunction, forEachFunction2) =>
-    locator = @getLocator(args...)
-    @test.utils.forEach(locator, forEachFunction, forEachFunction2)
+  forEach: (args...) =>
+    [argsForEach, argsForLocator] = _.partition(args, _.isFunction)
+    locator = @getLocator(argsForLocator...)
+    argsForEach.unshift(locator)
+
+    @test.utils.forEach(argsForEach...)

   ###
   Checks if element matching locator is currently present, returns Selenium.Promise that resolves to a boolean


Function for fetching the channels that have unreads in them

--- src/client.coffee
+++ src/client.coffee
@@ -292,6 +292,19 @@
       if group.unread_count? then count += group.unread_count

     count
+
+  getChannelsWithUnreads: ->
+    unreads = []
+    for id, channel of @channels
+      if channel.unread_count? then unreads.push channel
+
+    for id, dm of @ims
+      if dm.unread_count? then unreads.push dm
+
+    for id, group of @groups
+      if group.unread_count? then unreads.push group
+
+    unreads

   #
   # Message handler callback and dispatch


working

working

--- src/client.coffee
+++ src/client.coffee
@@ -19,31 +19,34 @@

     # TODO: Handle denials, in the case of denial, just fail

+handleData = (data) ->
+    {type, content} = data
+
+    switch type
+        when "url"
+            a = $("")
+            a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100
+        when "youtube"
+            time = if data.time then data.time else ""
+            a = $("")
+            a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100
+
+        when "image"
+            $("body").append $("")
+        when "spotify"
+            document.location = "spotify:track:#{content}"
+        else
+            alert("Unknown")
+            return
+    a.hide()
+    a.appendTo('.content')
+    a.fadeIn('slow')
+
 socket.on 'frisbee', (data) ->
-    console.log data
-    {type, content} = data
     console.log data

     throwFrisbee ->
-        switch type
-            when "url"
-                a = $("")
-                a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100
-            when "youtube"
-                a = $("")
-                a.css width: percentOfWidth(100) - 100, height: percentOfHeight(100) - 100
-
-            when "image"
-                $("body").append $("")
-            when "spotify"
-                document.location = "spotify:track:#{content}"
-            else
-                alert("Unknown")
-                return
-        a.hide()
-        a.appendTo('.content')
-        a.fadeIn('slow')
-
+        handleData data

 throwFrisbee = window.throwFrisbee = (cb) ->
@@ -72,7 +75,9 @@
         frisbee.animate {left: percentOfWidth(-25), bottom: percentOfHeight(-25)}, 1250, 'easeInOutQuad', ->
                 cb()

-    if oldFrisbee.hasClass('frisbeeContent')
+    if oldFrisbee.length is 0
+        animateFrisbee()
+    else if oldFrisbee.hasClass('frisbeeContent')
         oldFrisbee.fadeOut animateFrisbee
     else
         oldFrisbee.animate left: -300, 1000, 'easeInOutQuad', animateFrisbee
@@ -114,6 +119,17 @@
     cloud.animate {left: -250}, 60000, 'linear', ->
         cloud.remove()

+performYTSearch = (s, cb) ->
+    $.ajax
+      dataType: 'jsonp'
+      type: 'GET'
+      url: "http://gdata.youtube.com/feeds/api/videos?q=#{ encodeURIComponent(s) }" +
+           "&format=5&v=2&alt=jsonc" + # Force embeddable vids (format=5)
+           "&max-results=1"
+      success: (responseData, textStatus, XMLHttpRequest) =>
+          if videoId = responseData?.data?.items?[0].id
+              cb (videoId)
+
 startAnimation = ->
     createCloud()
     window.setInterval ->
@@ -136,4 +152,26 @@
         , 200

 $ ->
-    startAnimation()+    search = window.location.search
+    if search
+        YT_SEARCH = ///youtube=([^&]*)///
+        result = YT_SEARCH.exec search
+        s = result[1]
+        s = s.replace(',', ' ')
+
+        YT_TIME = ///time=([^&]*)///
+        result = YT_TIME.exec search
+        time = result[1]
+
+        console.log 'hey'
+
+        performYTSearch s, (videoId) ->
+            throwFrisbee ->
+                handleData type: 'youtube', content: videoId, time: time
+    else
+        startAnimation()
+
+
+
+
+