Update fr.coffee

Update fr.coffee

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -24,7 +24,7 @@
     blog: "Blog"
     forum: "Forum"
     account: "Compte"
-#    profile: "Profile"
+    profile: "Profil"
 #    stats: "Stats"
 #    code: "Code"
     admin: "Admin" # Only shows up when you are an admin
@@ -34,7 +34,7 @@
     about: "À propos"
     contact: "Contact"
     twitter_follow: "Suivre"
-#    teachers: "Teachers"
+    teachers: "Enseignants"

   modal:
     close: "Fermer"
@@ -181,7 +181,7 @@
   play_level:
     done: "Fait"
     home: "Accueil"
-#    skip: "Skip"
+    skip: "Passer"
 #    game_menu: "Game Menu"
     guide: "Guide"
     restart: "Relancer"
@@ -219,13 +219,13 @@
 #    tome_cast_button_run: "Run"
 #    tome_cast_button_running: "Running"
 #    tome_cast_button_ran: "Ran"
-#    tome_submit_button: "Submit"
+    tome_submit_button: "Envoyer"
 #    tome_reload_method: "Reload original code for this method" # Title text for individual method reload button.
 #    tome_select_method: "Select a Method"
 #    tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos).
     tome_select_a_thang: "Sélectionnez une unité pour"
     tome_available_spells: "Sorts diponibles"
-#    tome_your_skills: "Your Skills"
+    tome_your_skills: "Vos compétences"
     hud_continue: "Continuer (appuie sur shift ou espace)"
 #    code_saved: "Code Saved"
     skip_tutorial: "Passer (esc)"
@@ -270,7 +270,7 @@
     customize_wizard: "Personnaliser le magicien"

   game_menu:
-#    inventory_tab: "Inventory"
+    inventory_tab: "Inventaire"
 #    choose_hero_tab: "Restart Level"
 #    save_load_tab: "Save/Load"
 #    options_tab: "Options"
@@ -287,11 +287,11 @@
 #    choose_inventory: "Equip Items"

 #  choose_hero:
-#    choose_hero: "Choose Your Hero"
+    choose_hero: "Choisir votre héro"
 #    programming_language: "Programming Language"
 #    programming_language_description: "Which programming language do you want to use?"
 #    status: "Status"
-#    weapons: "Weapons"
+    weapons: "Armes"
 #    health: "Health"
 #    speed: "Speed"

@@ -473,7 +473,7 @@
     revert: "Annuler"
     revert_models: "Annuler les modèles"
 #    pick_a_terrain: "Pick A Terrain"
-#    small: "Small"
+    small: "Petit"
 #    grassy: "Grassy"
     fork_title: "Fork une nouvelle version"
     fork_creating: "Créer un Fork..."
@@ -668,8 +668,8 @@
 #    tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details"
 #    tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details"
     tournament_blurb_blog: "Sur notre blog"
-#    rules: "Rules"
-#    winners: "Winners"
+    rules: "Règles"
+    winners: "Gagnants"

 #  user:
 #    stats: "Stats"
@@ -683,7 +683,7 @@
 #    no_singleplayer: "No Singleplayer games played yet."
 #    no_multiplayer: "No Multiplayer games played yet."
 #    no_achievements: "No Achievements earned yet."
-#    favorite_prefix: "Favorite language is "
+    favorite_prefix: "Le langage favori est "
 #    favorite_postfix: "."

 #  achievements:
@@ -736,9 +736,9 @@
 #    patched_model: "Source Document"
 #    model: "Model"
     system: "Système"
-#    systems: "Systems"
-#    component: "Component"
-#    components: "Components"
+    systems: "Systèmes"
+    component: "Composant"
+    components: "Composants"
 #    thang: "Thang"
 #    thangs: "Thangs"
 #    level_session: "Your Session"
@@ -752,14 +752,14 @@
     document: "Document"
 #    sprite_sheet: "Sprite Sheet"
 #    employers: "Employers"
-#    candidates: "Candidates"
+    candidates: "Candidats"
 #    candidate_sessions: "Candidate Sessions"
 #    user_remark: "User Remark"
 #    user_remarks: "User Remarks"
 #    versions: "Versions"
-#    items: "Items"
-#    heroes: "Heroes"
-#    wizard: "Wizard"
+    items: "Objets"
+    heroes: "Héros"
+    wizard: "Magicien"
 #    achievement: "Achievement"
 #    clas: "CLAs"
 #    play_counts: "Play Counts"
@@ -1000,7 +1000,7 @@
 #    filter_visa_yes: "US Authorized"
 #    filter_visa_no: "Not Authorized"
 #    filter_education_top: "Top School"
-#    filter_education_other: "Other"
+    filter_education_other: "Autre"
 #    filter_role_web_developer: "Web Developer"
 #    filter_role_software_developer: "Software Developer"
 #    filter_role_mobile_developer: "Mobile Developer"
@@ -1009,7 +1009,7 @@
 #    filter_experience_junior: "Junior"
 #    filter_experience_recent_grad: "Recent Grad"
 #    filter_experience_student: "College Student"
-#    filter_results: "results"
+    filter_results: "Résultat"
 #    start_hiring: "Start hiring."
 #    reasons: "Three reasons you should hire through us:"
 #    everyone_looking: "Everyone here is looking for their next opportunity."
@@ -1032,21 +1032,21 @@
     candidate_last_updated: "Dernière mise à jour"
 #    candidate_who: "Who"
 #    featured_developers: "Featured Developers"
-#    other_developers: "Other Developers"
+    other_developers: "Autres développeur"
 #    inactive_developers: "Inactive Developers"

   admin:
 #    av_espionage: "Espionage" # Really not important to translate /admin controls.
-#    av_espionage_placeholder: "Email or username"
-#    av_usersearch: "User Search"
+    av_espionage_placeholder: "Email ou nom d'utilisateur"
+    av_usersearch: "Recherche d'utilisateurs"
 #    av_usersearch_placeholder: "Email, username, name, whatever"
-#    av_usersearch_search: "Search"
+    av_usersearch_search: "Recherche"
     av_title: "Vues d'administrateurs"
     av_entities_sub_title: "Entités"
     av_entities_users_url: "Utilisateurs"
     av_entities_active_instances_url: "Instances actives"
     av_entities_employer_list_url: "Liste des employés"
-#    av_entities_candidates_list_url: "Candidate List"
+    av_entities_candidates_list_url: "Liste des candidats"
     av_other_sub_title: "Autre"
     av_other_debug_base_url: "Base (pour debugger base.jade)"
     u_title: "Liste des utilisateurs"


try to fix regular expression interpolation

--- app/locale/fr.coffee
+++ app/locale/fr.coffee
@@ -1086,7 +1086,7 @@
     conversion_warning: "ATTENTION: Votre compte actuel est un  Compte Étudiant. Une fois que vous avez soumis ce formulaire, votre compte sera mis à jour en compte Professeur."
     learn_more_modal: "Les comptes professeurs sur CodeCombat ont la capacité de suivre la progression des étudiants, leur attribuer des licences et gérer les classes. Les comptes professeurs ne peuvent pas faire partie d'une classe. - si vous faites partie d'une classe en utilisant ce compte, vous ne pourrez plus y accéder quand le compte sera mis à jour en compte Professeur."
     create_account: "Créer votre compte professeur"
-    create_account_subtitle: '''Ayez accès aux outils réservés aux professeurs permettant d'utiiser CodeCombat dans la classe.  Définissez une classe , ajoutez vos étudiants, et suivez leur progression  !'''
+    create_account_subtitle: "Ayez accès aux outils réservés aux professeurs permettant d'utiiser CodeCombat dans la classe. Définissez une classe, ajoutez vos étudiants, et suivez leur progression  !"
     convert_account_title: "Mettez à jour votre compte de Professeur"
     not: "Non"


Mages' Might button polish translation

Mages' Might button polish translation

--- app/locale/pl.coffee
+++ app/locale/pl.coffee
@@ -3213,7 +3213,7 @@
 #    license_type_full: "Full Courses"
 #    license_type_customize: "Customize Courses"

-#  league:
+ league:
 #    student_register_1: "Become the next AI Champion!"
 #    student_register_2: "Sign up, create your own team, or join other teams to start competing."
 #    student_register_3: "Provide the information below to be eligible for prizes."
@@ -3221,7 +3221,7 @@
 #    general_news: "Get emails on the latest news and updates regarding our AI Leagues and tournaments."
 #    team: "team"
 #    how_it_works1: "Join a __team__"
-#    seasonal_arena_tooltip: "Battle against your teammates and others as you use your best programming skills to earn points and rank up the AI League leaderboard before taking on the Championship arena at the end of the season."
+     seasonal_arena_tooltip: "Stań do walki ze swoimi znajomymi oraz innymi użytkownikami, używając całej zebranej programistycznej wiedzy, by zdobywać punkty i piąć się w górę tabeli wyników Zawodów Sztucznej Inteligencji, przed mistrzostwami na końcu sezonu."
 #    summary: "The CodeCombat AI League is uniquely both a competitive AI battle simulator and game engine for learning real Python and JavaScript code."
 #    join_now: "Join Now"
 #    tagline: "The CodeCombat AI League combines our project-based standards-aligned curriculum, engaging adventure-based coding game, and our annual AI coding global tournament into an organized academic competition unlike any other."
@@ -3258,14 +3258,14 @@
 #    download_flyer: "Download Flyer"
 #    championship_summary: "The __championshipArena__ championship arena is now open! Battle it out for the month of __championshipMonth__ to win prizes in the __championshipArena__ __championshipType__."
 #    play_arena_full: "Play __arenaName__ __arenaType__"
-#    play_arena_short: "Play __arenaName__"
+     play_arena_short: "Zagraj w __arenaName__"
 #    view_arena_winners: "View __arenaName__ __arenaType__ winners"
 #    arena_type_championship: "Championship Arena"
 #    arena_type_regular: "Multiplayer Arena"
 #    blazing_battle: "Blazing Battle"
 #    infinite_inferno: "Infinite Inferno"
-#    mages_might: "Mages' Might"
-#    sorcerers: "Sorcerers"
+     mages_might: "Igrzyska Magów"
+     sorcerers: "Czarodzieje"
 #    giants_gate: "Giants' Gate"
 #    colossus: "Colossus"
 #    cup: "Cup"


Remove extra at-signs

Remove extra at-signs

--- scripts/controllers/Marker.coffee
+++ scripts/controllers/Marker.coffee
@@ -8,12 +8,12 @@
 class Marker extends Spine.Controller
 	marking: null
 	paper: null
-	@label: null
-	@labelText: null
-	@deleteButton: null
-	@labelRect: null
+	label: null
+	labelText: null
+	deleteButton: null
+	labelRect: null

-	@centerPoint: null
+	centerPoint: null

 	selected: false


Ensure latest version is fetched before updating product prices

--- src/coffee/pricesync.coffee
+++ src/coffee/pricesync.coffee
@@ -109,7 +109,6 @@
               v.sku is masterSku
             if match?
               data =
-                productVersion: product.version
                 productId: product.id
                 isPublished: product.published is true
                 variant: match
@@ -136,12 +135,14 @@
       @logger?.debug prices, "No available update actions for prices in product #{variantDataInMaster.productId}"
       Promise.resolve()
     else
-      data =
-        version: variantDataInMaster.productVersion
-        variantId: variantDataInMaster.productId
-        actions: actions
-      @logger?.debug data, "About to update product #{variantDataInMaster.productId} in master"
-      @masterClient.products.byId(variantDataInMaster.productId).update(data)
+      @masterClient.productProjections.staged(true).byId(variantDataInMaster.productId).fetch()
+      .then (result) =>
+        data =
+          version: result.body.version
+          variantId: variantDataInMaster.productId
+          actions: actions
+        @logger?.debug data, "About to update product #{variantDataInMaster.productId} in master"
+        @masterClient.products.byId(variantDataInMaster.productId).update(data)
       .then =>
         @summary.synced++
         Promise.resolve()


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- test/engine_camera.coffee
+++ test/engine_camera.coffee
@@ -1,6 +1,9 @@
 module "Engine"

 test "Camera", ->
+  App.width = 800
+  App.height = 640
+
   engine = Engine
     includedModules: ["Camera"]


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/metered.coffee
+++ src/metered.coffee
@@ -169,7 +169,7 @@

   player.include Metered

-
+  # creates a health meter but di
   player.meter 'health'
     show: false


Rename Meter module

Rename Meter module


Fix #75 property value just returning value instead of value="value"

--- lib/helpers/view_helper.coffee
+++ lib/helpers/view_helper.coffee
@@ -209,6 +209,8 @@
           return "value='" + propValue + "'"
         if dataType is "number"
           return "value='" + numeral(propValue).value() + "'"
+        if dataType is "textarea"
+          return _.escape(propValue)
         else return "value='#{_.escape(propValue)}'"
       ""
     #Get the value of the translated label.


Get hipchat working with no error

Get hipchat working with no error

--- plugins/hipchat/index.coffee
+++ plugins/hipchat/index.coffee
@@ -5,9 +5,9 @@

   @receiveEvent: (config, event) ->
     # Build the message
-    message =  "#{event.trigger.message} from #{event.project.name} in #{event.error.context} (details)"
-    message += "
   #{event.error.exceptionClass}" + (if event.error.message then ": #{event.error.message}" else "")
-    message += "
   #{@firstStacktraceLine(event.error.stacktrace)}" if event.error.stacktrace
+    message =  "#{event.trigger.message} from #{event.project.name}" + ( if event.error then " in #{event.error.context} (details)" else "")
+    message += "
   #{event.error.exceptionClass}" + (if event.error.message then ": #{event.error.message}" else "") if event.error
+    message += "
   #{@firstStacktraceLine(event.error.stacktrace)}" if event.error && event.error.stacktrace

     # Build the request
     params =


Update example

Update example

--- examples/examples.cjsx
+++ examples/examples.cjsx
@@ -41,6 +41,7 @@
       <MicroBarChart
         width=200
         height=50
+        xAxis
         fillColor="steelblue" />
         """}
       </code></pre>


Make sure push data exists before trying to use it.

--- app/assets/javascripts/_site.js.coffee
+++ app/assets/javascripts/_site.js.coffee
@@ -706,15 +706,16 @@

     showPushOptions: ->
       if "safari" of window and "pushNotification" of window.safari
-        $('.push-options').removeClass('hide')
-        websiteId = $('#push-data').data('website-id')
-        permissionData = window.safari.pushNotification.permission(websiteId)
-        if (permissionData.permission == 'default')
-          $('.push-enable').removeClass('hide')
-        else if (permissionData.permission == 'granted')
-          $('.push-disable').removeClass('hide')
-        else if (permissionData.permission == 'denied')
-          $('.push-disabled').removeClass('hide')
+        if $('#push-data').length > 0
+          $('.push-options').removeClass('hide')
+          websiteId = $('#push-data').data('website-id')
+          permissionData = window.safari.pushNotification.permission(websiteId)
+          if (permissionData.permission == 'default')
+            $('.push-enable').removeClass('hide')
+          else if (permissionData.permission == 'granted')
+            $('.push-disable').removeClass('hide')
+          else if (permissionData.permission == 'denied')
+            $('.push-disabled').removeClass('hide')

     enablePush: ->
       $(document).on 'click', '[data-behavior~=enable_push]', ->


Restructure code to match conventions elsewhere

--- source/javascripts/threejs_scene.js.coffee
+++ source/javascripts/threejs_scene.js.coffee
@@ -22,16 +22,49 @@

 class @ThreejsScene
   constructor: (@elem_root, @animate_camera = true, @lights = true, @animate_lights) ->
-    @stats = new Stats()
-    @init_root()
-    @create_basic_scene()
+    @_stats = new Stats()
+    @_init_root()
+    @_create_basic_scene()

-    $(@elem_root).append(@stats.domElement)
+    $(@elem_root).append(@_stats.domElement)

     @on_validation_error = @_default_error_handler
     @on_validation_success = @_default_error_handler

-  init_root: () ->
+  get_vertex_source: () ->
+    return @shader_material.vertexShader
+
+  get_fragment_source: () ->
+    return @shader_material.fragmentShader
+
+  update_shader: (shader_parameters) ->
+    if @_validate_shader_parameters(shader_parameters)
+      new_material = new THREE.ShaderMaterial(shader_parameters)
+      @mesh.material = new_material
+      @shader_material = new_material
+
+  render: (timestamp) =>
+    @_stats.begin()
+
+    # Work out our time step. No
+    # smoothing or max step size for
+    # the moment
+    if @last_timestamp?
+        time_step = (timestamp - @last_timestamp) / 1000
+    else
+        time_step = 1/60
+
+    @last_timestamp = timestamp
+
+    # Do some animation
+    requestAnimationFrame(@render);
+    @mesh.rotation.x += 2 * time_step;
+    @mesh.rotation.y += 2 * time_step;
+    @renderer.render(@scene, @camera);
+
+    @_stats.end()
+
+  _init_root: () ->
     width = $(@elem_root).width()
     height = $(@elem_root).height()
     console.log "Init ThreejsScene. Size: #{width}x#{height}"
@@ -44,21 +77,15 @@

     @validator = new GLSLValidator(@renderer.getContext())

-    @resize_debounce = new EventDebounce($(window), 'resize', @handle_resize, 250)
+    @resize_debounce = new EventDebounce($(window), 'resize', @_handle_resize, 250)

-  handle_resize: (e) =>
+  _handle_resize: (e) =>
     width = $(@elem_root).width()
     height = $(@elem_root).height()

     @camera.aspect = width / height
     @camera.updateProjectionMatrix()
     @renderer.setSize( width, height )
-
-  update_shader: (shader_parameters) ->
-    if @_validate_shader_parameters(shader_parameters)
-      new_material = new THREE.ShaderMaterial(shader_parameters)
-      @mesh.material = new_material
-      @shader_material = new_material

   _default_error_handler: (e) =>
     console.error 'threejs_scene error', e
@@ -121,13 +148,7 @@

     return success

-  get_vertex_source: () ->
-    return @shader_material.vertexShader
-
-  get_fragment_source: () ->
-    return @shader_material.fragmentShader
-
-  create_basic_scene: () ->
+  _create_basic_scene: () ->
     shader_parameters =
       vertexShader: default_vertex_source
       fragmentShader: default_fragment_source
@@ -139,24 +160,3 @@
     @scene.add( @mesh )

     @camera.position.z = 1.5
-
-  render: (timestamp) =>
-    @stats.begin()
-
-    # Work out our time step. No
-    # smoothing or max step size for
-    # the moment
-    if @last_timestamp?
-        time_step = (timestamp - @last_timestamp) / 1000
-    else
-        time_step = 1/60
-
-    @last_timestamp = timestamp
-
-    # Do some animation
-    requestAnimationFrame(@render);
-    @mesh.rotation.x += 2 * time_step;
-    @mesh.rotation.y += 2 * time_step;
-    @renderer.render(@scene, @camera);
-
-    @stats.end()


Cleaned it up further by moving it all onto one line. (and it fits within 80 chars!)

--- test/api/tests.coffee
+++ test/api/tests.coffee
@@ -12,12 +12,8 @@

 request = (options={}, callback) ->
-  if process.env.C9_USER?
-    #options.host ?= "#{process.env.C9_PROJECT}.#{process.env.C9_USER}.c9.io"
-    options.port ?= process.env.C9_PORT
-  else
-    options.port ?= 3003
   options.host ?= 'localhost'
+  options.port ?= if not process.env.C9_PORT? then 3003 else process.env.C9_PORT
   options.path ?= '/'
   options.method ?= 'GET'
   options.headers ?= {}


only route events if they include a correlation id

--- source/server/infrastructure/router.coffee
+++ source/server/infrastructure/router.coffee
@@ -60,8 +60,10 @@
     @repository.save aggregate.handle(command)

   _genericEventHandler: (event) =>
-    @log "#{this}: Handling command #{event.typeName()} for
-          #{@aggregate}<#{event.sourceId}>\n", event
-    aggregate = @repository.find @aggregate, event[this.eventCorrelationProperty]
+    id = event[this.eventCorrelationProperty]
+    return if not id?
+    @log "#{this}: Handling event #{event.typeName()} for
+          #{@aggregate}<#{id}>\n", event
+    aggregate = @repository.find @aggregate, id
     throw Router.ERRORS.noAggregateFoundToHandleMessage(event) if !aggregate?
     @repository.save aggregate.handle(event)


clampable

clampable

--- test/clampable.coffee
+++ test/clampable.coffee
@@ -5,6 +5,7 @@

   o.clamp
     x:
+      min: 0

   # Test for equality of two objects


gravity method change of Collada Object

gravity method change of Collada Object

--- lib/_stationary.coffee
+++ lib/_stationary.coffee
@@ -244,7 +244,7 @@
                     if (@scaleZ != @sprite.scaleZ)
                         @sprite.scaleZ = @scaleZ

-                    @ys += @gravity
+                    @ys -= @gravity
                     @x += @xs
                     @y += @ys
                     @z += @zs


forgot header on error

forgot header on error

--- app/assets/javascripts/products.coffee
+++ app/assets/javascripts/products.coffee
@@ -29,7 +29,7 @@
           <button class="close pull-right" data-dismiss="modal">×</button>
         """
         if @status != 200
-          $("#{id} .product-cli-body").html 'Error'
+          $("#{id} .product-cli-body").html "#{header}A fatal error has occurred"
       xhr.open 'PATCH', target
       xhr.setRequestHeader 'X-CSRF-Token', $('meta[name="csrf-token"]').attr('content')
       xhr.setRequestHeader 'X-Requested-With', 'XMLHttpRequest'


add check that token request succeeded before parsing response as json

--- routes/pocketOAuth.coffee
+++ routes/pocketOAuth.coffee
@@ -67,7 +67,9 @@
             str += chunk
             return
         response.on 'end', () ->
-            exports.accessToken = JSON.parse(str).access_token
+            console.log response.statusCode
+            if response.statusCode is 200
+                exports.accessToken = JSON.parse(str).access_token
             res.redirect CALLBACK_URI
             return
         return


Fix coffeescript keyup

Fix coffeescript keyup

--- app/assets/javascripts/staff.js.coffee
+++ app/assets/javascripts/staff.js.coffee
@@ -1,17 +1,18 @@
 # Place all the behaviors and hooks related to the matching controller here.
 # All this logic will automatically be available in application.js.
 # You can use CoffeeScript in this file: http://coffeescript.org/
-$("textarea").keyup ->
-  if $("#tweet_tipe").is(":checked")
-    count = 140
-  else
-    count = 99
-  length = count - $(this).val().length
-  if length <= 0 and $("#count").attr("class") is "text-success"
-    $("#count").toggleClass "text-success text-danger"
-    $(".subm").prop "disabled", true
-  else if length >= 0 and $("#count").attr("class") is "text-danger"
-    $("#count").toggleClass "text-danger text-success"
-    $(".subm").prop "disabled", false
-  $("#count").text length
-  return
+jQuery ->
+  $("textarea").keyup ->
+    if $("#tweet_tipe").is(":checked")
+      count = 140
+    else
+      count = 99
+    length = count - $(this).val().length
+    if length <= 0 and $("#count").attr("class") is "text-success"
+      $("#count").toggleClass "text-success text-danger"
+      $(".subm").prop "disabled", true
+    else if length >= 0 and $("#count").attr("class") is "text-danger"
+      $("#count").toggleClass "text-danger text-success"
+      $(".subm").prop "disabled", false
+    $("#count").text length
+    return


hide image view if no image is in embedly

--- client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
+++ client/landing/app/Applications/Activity.kdapplication/views/embedboxviews.coffee
@@ -87,10 +87,13 @@
   constructor:(options={}, data)->
     super options, data

-    @embedImage = new EmbedBoxLinkViewImage
-      cssClass : 'preview_image'
-      delegate : this
-    ,data
+    if data.link_embed?.images?[0]?
+      @embedImage = new EmbedBoxLinkViewImage
+        cssClass : 'preview_image'
+        delegate : this
+      ,data
+    else
+      @embedImage = new KDCustomHTMLView 'hidden'

     @embedText = new EmbedBoxLinkViewText
       cssClass  : 'preview_text'


Prevent sign in screen during first-run auth

--- www/js/views/app.coffee
+++ www/js/views/app.coffee
@@ -30,7 +30,9 @@
     # screen so the user can sign in with Foursquare.
     _checkForSelfUser: ->
       # If we've already checked and @selfUser exists, skip the checks.
-      return if @selfUser
+      # HACK: Also skip the check if we're in the sign in process. This
+      # prevents the "please sign in screen" from appearing during sign-in.
+      return if @selfUser or window.location.hash.match 'access_token='

       self = this


Made Bezier curves named functions

Made Bezier curves named functions

--- framer/Animators/Curves.coffee
+++ framer/Animators/Curves.coffee
@@ -8,12 +8,15 @@
 		options.values = values
 		new BezierCurveAnimator(options)

+# We define each in a seperate function here
+# so they become named functions
+# and Framer.Curves.Bezier.ease.name is set
 BezierDefaults =
-	linear: Bezier(0, 0, 1, 1)
-	ease: Bezier(.25, .1, .25, 1)
-	easeIn: Bezier(.42, 0, 1, 1)
-	easeOut: Bezier(0, 0, .58, 1)
-	easeInOut: Bezier(.42, 0, .58, 1)
+	linear: (options = {}) -> Bezier(0, 0, 1, 1)(options)
+	ease: (options = {}) -> Bezier(.25, .1, .25, 1)(options)
+	easeIn: (options = {}) -> Bezier(.42, 0, 1, 1)(options)
+	easeOut: (options = {}) -> Bezier(0, 0, .58, 1)(options)
+	easeInOut: (options = {}) -> Bezier(.42, 0, .58, 1)(options)

 Spring = (dampingRatio, mass, velocity) ->
 	curveOptions = {}


Add doc reference to api doc

Add doc reference to api doc

--- library/exposure/apidoc.coffee
+++ library/exposure/apidoc.coffee
@@ -84,6 +84,7 @@
     # class and its module implementation for more information on it.
     @GET (method, service, kernel) ->
         @relevant "ts33kr.github.io/granite/exposure/apidoc.html"
+        @relevant "ts33kr.github.io/granite/membrane/document.html"
         @github "ts33kr", "granite", "library/exposure/apidoc.coffee"
         @synopsis "Get inventory of all APIs available in the system"
         @outputs "An array of objects, each describes a service"


:memo:

:memo:

--- src/application-delegate.coffee
+++ src/application-delegate.coffee
@@ -67,6 +67,9 @@

   openWindowDevTools: ->
     new Promise (resolve) ->
+      # Defer DevTools interaction to the next tick, because using them during
+      # event handling causes some wrong input events to be triggered on
+      # `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
       process.nextTick ->
         if remote.getCurrentWindow().isDevToolsOpened()
           resolve()
@@ -76,6 +79,9 @@

   closeWindowDevTools: ->
     new Promise (resolve) ->
+      # Defer DevTools interaction to the next tick, because using them during
+      # event handling causes some wrong input events to be triggered on
+      # `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
       process.nextTick ->
         unless remote.getCurrentWindow().isDevToolsOpened()
           resolve()
@@ -85,6 +91,9 @@

   toggleWindowDevTools: ->
     new Promise (resolve) =>
+      # Defer DevTools interaction to the next tick, because using them during
+      # event handling causes some wrong input events to be triggered on
+      # `TextEditorComponent` (Ref.: https://github.com/atom/atom/issues/9697).
       process.nextTick =>
         if remote.getCurrentWindow().isDevToolsOpened()
           @closeWindowDevTools().then(resolve)


bump to 200

bump to 200

--- public/js/wireroom.coffee
+++ public/js/wireroom.coffee
@@ -185,7 +185,7 @@
         room: room
         ident: self.Identifier
         nickname: messageInput.getNickname()
-      @socket.emit "backlog",{type: "says", room: room, limit: 100}
+      @socket.emit "backlog",{type: "says", room: room, limit: 200}
       @socket.emit "backlog",{type: "notification.git", room: room, limit: 5}
       @socket.emit "backlog",{type: "notification.github", room: room, limit: 5}
       @socket.emit "backlog",{type: "notification.jenkins", room: room, limit: 5}


Add organizations

Add organizations

--- src/js/github-friction.coffee
+++ src/js/github-friction.coffee
@@ -141,7 +141,7 @@
   branch = $("##{repo_id} > .branch").text()
   link.attr('href',"#{repo_url}/blob/#{branch}/#{file_name}")
   link.attr('target','_blank')
-  link.text(data_cell.text().replace('\u2610 ',''))
+  link.text(data_cell.text().replace(/[\u2610\u2611] /,''))
   data_cell.text('')
   data_cell.append('\u2611 ')
   data_cell.append(link)
@@ -174,6 +174,27 @@
       console.log("no script directory for #{repo_id}") if github_friction_debug
       mark_missing(repo_id)

+add_repos = (github) ->
+  (err, repos) ->
+    repos.map (repo) ->
+      console.log(repo) if github_friction_debug
+      repo_row = $('<tr>').attr('id',repo.id)
+      repo_link = $('<a>').attr('href',repo.html_url).text(repo.full_name).attr('target','_blank')
+      repo_div = $('<td>').addClass('name').append(repo_link)
+      repo_row.append(repo_div)
+      repo_row.append($('<td>').attr('class','active text-center').text('master').addClass('branch'))
+      for name, friction_check of friction_checks
+        repo_row.append($('<td>').attr('class','info text-center').text('\u2610 ' + friction_check.name).addClass(name))
+      $('#repo_list').append(repo_row)
+      github.getRepo(repo.owner.login, repo.name).listBranches (err, branches) =>
+        console.log(repo.name) if github_friction_debug
+        console.log(branches) if github_friction_debug
+        if 'master' in branches
+          check_friction(repo.id, github.getRepo(repo.owner.login, repo.name), 'master')
+        else
+          $("##{repo.id} > .branch").text(branches[0])
+          check_friction(repo.id, github.getRepo(repo.owner.login, repo.name), branches[0])
+
 build_github_friction = ->
   console.log('build') if github_friction_debug
   if get_cookie 'access_token'
@@ -192,25 +213,10 @@
       auth: 'oauth'
     )
     user = github.getUser()
-    user.repos((err, repos) ->
-      repos.map (repo) ->
-        console.log(repo) if github_friction_debug
-        repo_row = $('<tr>').attr('id',repo.id)
-        repo_link = $('<a>').attr('href',repo.html_url).text(repo.full_name).attr('target','_blank')
-        repo_div = $('<td>').addClass('name').append(repo_link)
-        repo_row.append(repo_div)
-        repo_row.append($('<td>').attr('class','active text-center').text('master').addClass('branch'))
-        for name, friction_check of friction_checks
-          repo_row.append($('<td>').attr('class','info text-center').text('\u2610 ' + friction_check.name).addClass(name))
-        $('#repo_list').append(repo_row)
-        github.getRepo(repo.owner.login, repo.name).listBranches (err, branches) =>
-          console.log(repo.name) if github_friction_debug
-          console.log(branches) if github_friction_debug
-          if 'master' in branches
-            check_friction(repo.id, github.getRepo(repo.owner.login, repo.name), 'master')
-          else
-            $("##{repo.id} > .branch").text(branches[0])
-            check_friction(repo.id, github.getRepo(repo.owner.login, repo.name), branches[0])
+    user.repos(add_repos(github))
+    user.orgs((err, orgs) ->
+      orgs.map (org) ->
+        user.orgRepos(org.login, add_repos(github))
     )
   else
     console.log('redirecting to oauth') if github_friction_debug


Change window.location to use turbolinks

--- app/assets/javascripts/todos.js.coffee
+++ app/assets/javascripts/todos.js.coffee
@@ -56,6 +56,5 @@
     $('.todos-pending .badge, .todos-pending-count').text data.count
     $('.todos-done .badge').text data.done_count

-  goToTodoUrl: (e) ->
-    $this = $(e.currentTarget)
-    window.location.href = $this.data('url')
+  goToTodoUrl: ->
+    Turbolinks.visit($(this).data('url'))


set autoFocus props for first task in subTask

--- app/classifier/drawing-tools/root.cjsx
+++ app/classifier/drawing-tools/root.cjsx
@@ -66,7 +66,7 @@
           {for detailTask, i in toolProps.details
             detailTask._key ?= Math.random()
             TaskComponent = tasks[detailTask.type]
-            <TaskComponent key={detailTask._key} task={detailTask} annotation={toolProps.mark.details[i]} onChange={@handleDetailsChange.bind this, i} />}
+            <TaskComponent autoFocus={true if i is 0} key={detailTask._key} task={detailTask} annotation={toolProps.mark.details[i]} onChange={@handleDetailsChange.bind this, i} />}

             <button type="submit" className="standard-button" disabled={not detailsAreComplete}>OK</button>


TEST: added failing tests for two varients of while-expression (one returning a value, one not)

--- test/tests/CaffeineScript/Parser/Working/ControlStructures.coffee
+++ test/tests/CaffeineScript/Parser/Working/ControlStructures.coffee
@@ -103,6 +103,19 @@
   while:
     "while foo\n  bar": "while (foo) {bar;};"

+    """
+    a = while foo
+      bar
+    """: "let a; a = (() => {let result; while (foo) {result = bar}; return result})();"
+
+    """
+    a = if true
+      while false
+        1
+      2
+    else 3
+    """: "let a; a = true ? ((() => {while (false) {1;}})(), 2) : 3;"
+
   until:
     "until foo\n  bar": "while (!foo) {bar;};"


Client side memory optimizations.

Client side memory optimizations.

--- frontend/script.coffee
+++ frontend/script.coffee
@@ -81,15 +81,15 @@
     if _.keys(@ongoingPreloads).length != 0
       return
     song = @nextPreloads.shift()
-    encodedPath = encodeURIComponent song.filename()
+    encodedPath = encodeURIComponent songFilename song
     preloadSource = "preload/#{encodedPath}?type=#{@playbackType.request}"
-    @ongoingPreloads[song.filename()] = new Date()
+    @ongoingPreloads[songFilename song] = new Date()
     request = $.ajax preloadSource,
       type: "GET"
       timeout: PRELOAD_WAIT_TIMEOUT
     preloadCallback = =>
-      @preloaded[song.filename()] = new Date()
-      delete @ongoingPreloads[song.filename()]
+      @preloaded[songFilename song] = new Date()
+      delete @ongoingPreloads[songFilename song]
       if @nextPreloads.length == 0
         return
       nextSong = @nextPreloads.shift()
@@ -105,7 +105,7 @@
     for song in preloadSongs
       if totalPreloads == @preloadCount
         break
-      if song.filename() in @preloaded
+      if songFilename(song) in @preloaded
         continue
       totalPreloads++
       @nextPreloads.push song
@@ -282,14 +282,14 @@

   play: (song) =>
     @playerElement.empty()
-    encodedPath = encodeURIComponent song.filename()
+    encodedPath = encodeURIComponent songFilename song
     songSource = "file/#{encodedPath}?type=#{@playbackType.request}"
     @playerElement.append "<source src=\"#{songSource}\" type='#{@playbackType.mime}' />"
     @player.load()
     @lastSource = songSource
     # console.log "last source: #{@lastSource}"
     @currentSong = song
-    @storage.currentSong = JSON.stringify @currentSong
+    @storage.currentSong = JSON.stringify songToJson @currentSong
     # Preload the currently playing song to handle cases where we
     # fail to play the requested song. As audio element does not
     # send any events on failed playback, we need to use another
@@ -300,17 +300,15 @@
   preload: (song, react=true) =>
     if not song
       return
-    # console.log "player#preload #{song.title} #{react}"
-    if song.filename() of @preloads
-      # console.log "player#preload #{song.title} return"
+    if songFilename(song) of @preloads
       return
-    @preloads[song.filename()] = new Date()
-    encodedPath = encodeURIComponent song.filename()
+    @preloads[songFilename song] = new Date()
+    encodedPath = encodeURIComponent songFilename song
     songPath = "file/#{encodedPath}?type=#{@playbackType.request}"
     @trigger "preloadStart", song, react

     successCallback = (song) =>
-      delete @preloads[song.filename()]
+      delete @preloads[songFilename song]
       @timeouter.reset()

       @preloadElement.empty()
@@ -326,7 +324,7 @@
     failCallback = (song) =>
       @trigger "preloadFailed", song, react
     errorCallback = (song) =>
-      delete @preloads[song.filename()]
+      delete @preloads[songFilename song]
       setTimeout (=> failCallback song), @timeouter.increaseTimeout()
     request.error (xhr, textStatus, errorThrown) =>
       # 302 redirect in Opera
@@ -421,30 +419,24 @@
       return
     lastPreloadSong = song
     newsong = $("✘")
-    newsong.attr "title", "#{song.title} / #{song.album} / #{song.artist} / #{song.filename()}"
+    newsong.attr "title", "#{songTitle(song)} / #{songAlbum(song)} / #{songArtist(song)} / #{songFilename song}"
     nextSongStatusElement.html newsong

   player.on "preloadFailed", (song, react) ->
-    # console.log "player.preloadFailed #{react} #{song.filename}"
     songQueue.clearNext()
     newSong = songQueue.peek()
-    # if react
-      # console.log "player.preloadFailed -> react"
     player.preload newSong, react

   player.on "preloadOk", (song, react) ->
-    # console.log "player.preloadOk #{react} #{song.filename}"
     if not react
       return
     if song == lastPreloadSong
-      # console.log "player.preloadOk #{song.title} -> lastPreloadSong"
       newsong = $(".notloaded", nextSongStatusElement)
       newsong.removeClass "notloaded"
       newsong.addClass "loaded"
       newsong.text "✓"

     if not player.isPlaying()
-      # console.log "player.preloadOk #{song.title} -> notPlaying"
       player.play song

   preloadOnQueueChange = ->
@@ -492,10 +484,10 @@
   albumElement = $("#album", currentSongStatusElement)
   titleElement = $("#title", currentSongStatusElement)
   player.on "preparePlay", (song) ->
-    currentSongStatusElement.attr "title", song.filename()
-    artistElement.text song.artist
-    albumElement.text song.album
-    titleElement.text song.title
+    currentSongStatusElement.attr "title", songFilename(song)
+    artistElement.text songArtist(song)
+    albumElement.text songAlbum(song)
+    titleElement.text songTitle(song)

   artistElement.click ->
     searchValue = "artist:#{simpleNormalizeName artistElement.text()}-"
@@ -541,7 +533,7 @@
     durationElement.text viewTimeString 0

 songToQueueRow = (song) ->
-  return [song.title, song.album, song.artist]
+  return [songTitle(song), songAlbum(song), songArtist(song)]

 initialPlayerComponentsHeight = (document) ->

@@ -565,6 +557,9 @@
     bDeferRender: true
     aaData: queueData
     aoColumns: columns
+    bFilter: false
+    bSort: false
+    aaSorting: []

   queueWrapper = $("#queue_wrapper")
   queueWrapper.hide()
@@ -606,11 +601,8 @@
     columns.push column

   tableData = []
-  for song, index in songData
-    artist = song.artist or 'UNKNOWN'
-    album = song.album or 'UNKNOWN'
-    title = song.title or 'UNKNOWN'
-    tableData.push([index, title, album, artist])
+  for song in songData
+    tableData.push(song.data)

   table = playlistElement.dataTable
     sScrollY: "#{viewport.playlistHeight}px"
@@ -622,6 +614,9 @@
     bDeferRender: true
     aaData: tableData
     aoColumns: columns
+    bFilter: false
+    bSort: false
+    aaSorting: []

   table.on "filter", (event, settings) =>
     visible = []
@@ -639,7 +634,7 @@

     # Front page for the first time:
     if not player.startedPlaying
-      if player.currentSong and (not songName? or player.currentSong.filename() == songName)
+      if player.currentSong and (not songName? or songFilename(player.currentSong) == songName)
         # console.log "resume playing"
         player.resumePlaying()
       else
@@ -650,7 +645,7 @@
     # Hash change while player has already started playing
     # something.
     if player.startedPlaying
-      if songName == player.currentSong.filename()
+      if songName == songFilename(player.currentSong)
         # console.log "current song"
         return

@@ -658,7 +653,7 @@
     # change.
     $(songData).each (index, element) =>
       song = songData[index]
-      if song.filename() == songName
+      if songFilename(song) == songName
         # console.log "playing"
         player.play song
         queue.clearNext()
@@ -678,14 +673,14 @@
   lastSong = null

   player.on "preparePlay", (song) ->
-    if lastHashChange != song.filename()
-      router.navigate "/" + song.filename()
+    if lastHashChange != songFilename(song)
+      router.navigate "/" + songFilename(song)

   previousRow = null
   focusRow = (song) ->
     if previousRow
       $(previousRow).removeClass "playing"
-    row = table.fnGetNodes song.index
+    row = table.fnGetNodes songIndex(song)
     $(row).addClass "playing"
     previousRow = row

@@ -854,42 +849,62 @@
       directoryName = "#{basename}/#{directoryName}"
   return "/#{directoryName}"

+UNKNOWN_STRING = "UNKNOWN"

 class SongDisplay
-  constructor: (@_directories, fileObject) ->
-    @index = fileObject.index
-    @_filename = fileObject.filename
-    @title = fileObject.title
-    @album = fileObject.album
-    @artist = fileObject.artist
-
-  filename: =>
-    directoryName = get_directory @_directories, @_filename
-    [directory, basename] = @_filename.split "/"
-    if not basename
-      basename = @_filename
-    return "#{directoryName}#{basename}"
-
-  toJSON: =>
-    directoryName = ""
-    basename = @_filename
-    if @_filename.indexOf("/") != -1
-      [directory, basename] = @_filename.split "/"
-    directoryName = get_directory @_directories, @_filename
-    data =
-      index: 0
-      _directories: [directoryName]
-      _filename: "0/#{basename}"
-      title: @title
-      album: @album
-      artist: @artist
-    return data
+  constructor: (@directories, fileObject) ->
+    # This data can be shared with datatables.
+    @data = [
+      fileObject.index,
+      fileObject.title or UNKNOWN_STRING,
+      fileObject.album or UNKNOWN_STRING,
+      fileObject.artist or UNKNOWN_STRING]
+    @filename = fileObject.filename
+
+
+songIndex = (song) ->
+  return song.data[0]
+
+
+songTitle = (song) ->
+  return song.data[1]
+
+
+songAlbum = (song) ->
+  return song.data[2]
+
+
+songArtist = (song) ->
+  return song.data[3]
+
+
+songFilename = (song) ->
+  directoryName = get_directory song.directories, song.filename
+  [directory, basename] = song.filename.split "/"
+  if not basename
+    basename = song.filename
+  return "#{directoryName}#{basename}"
+

 jsonToSong = (json_data) ->
   song = new SongDisplay [], {}
   for key, value in JSON.parse json_data
     song[key] = value
   return song
+
+
+songToJson = (song) ->
+  directoryName = ""
+  basename = song.filename
+  if song.filename.indexOf("/") != -1
+    [directory, basename] = song.filename.split "/"
+    directoryName = get_directory song.directories, song.filename
+  data =
+    data: [0, songTitle(song), songAlbum(song), songArtist(song)]
+    directories: [directoryName]
+    filename: "0/#{basename}"
+  return data
+

 class Viewport
   constructor: (@document) ->
@@ -943,9 +958,9 @@

   if localStorage.currentSong
     currentSong = jsonToSong localStorage.currentSong
-    $("#title").text currentSong.title
-    $("#album").text currentSong.album
-    $("#artist").text currentSong.artist
+    $("#title").text songTitle(currentSong)
+    $("#album").text songAlbum(currentSong)
+    $("#artist").text songArtist(currentSong)

   HotkeysView playerInstance, songQueue, $("#play-control"), $("#next"), $("#toggle-queue"), $("#search")


change name from word to entry

change name from word to entry

--- cf/spell.coffee
+++ cf/spell.coffee
@@ -24,12 +24,12 @@
     return data['words'][choice]

   update_question = (data) ->
-    word = random_question(data)
+    entry = random_question(data)
     update_keyHandler()
-    size = word.name.length
+    size = entry.name.length
     $("dd#hint_stat").empty().append("This word is " + size + " long.")
-    $("dd#hint_define").empty().append(word['hint'])
-    $("dd#hint_sentence").empty().append(word['example'])
+    $("dd#hint_define").empty().append(entry['hint'])
+    $("dd#hint_sentence").empty().append(entry['example'])

   emptyInput = () ->
     $("input#input_answer").val("")


Added collection binding

Added collection binding

--- vendor/assets/javascripts/coffee_middleware/system/base.js.coffee
+++ vendor/assets/javascripts/coffee_middleware/system/base.js.coffee
@@ -1,5 +1,6 @@
 class CoffeeMiddleware.System.Base

+  # Bind many elements, each to its own class
   @bindMany: (selector) ->
     collection = []
     instance = @
@@ -10,9 +11,16 @@

     collection

+  # Force only single element
   @bindOne: (selector) ->
     if $(selector).length > 0
       new @($(selector))
+
+  # Bind a collection of elements
+  @bindCollection: (selector) ->
+    if $(selector).length > 0
+      new @($(selector))
+

   reBind: =>
     @.constructor(@.container)


case-insensitive

case-insensitive

--- vendor/assets/javascripts/fustrate/components/autocomplete.coffee
+++ vendor/assets/javascripts/fustrate/components/autocomplete.coffee
@@ -3,7 +3,9 @@
     plain:
       displayKey: 'value'
       item: (object, userInput) -> "<li>#{@highlight object.value}</li>"
-      filter: (object, userInput) -> object.value.indexOf(userInput) >= 0
+      filter: (object, userInput) ->
+        search = userInput.trim().toLowerCase()
+        object.value.toLowerCase().indexOf(search) >= 0

   @initialize: ->
     # Override the default sort


beaglebone.coffee: Change the getting started guide links.

Signed-off-by: Florin Sarbu <3a837a37a2ac9072a088641ec330e0e5d5b86f4e@resin.io>

--- beaglebone.coffee
+++ beaglebone.coffee
@@ -29,9 +29,9 @@
 	].concat(postProvisioningInstructions)

 	gettingStartedLink:
-		windows: 'http://docs.resin.io/#/pages/installing/gettingStarted-BBB.md#windows'
-		osx: 'http://docs.resin.io/#/pages/installing/gettingStarted-BBB.md#on-mac-and-linux'
-		linux: 'http://docs.resin.io/#/pages/installing/gettingStarted-BBB.md#on-mac-and-linux'
+		windows: 'https://docs.resin.io/beaglebone/nodejs/getting-started/#adding-your-first-device'
+		osx: 'https://docs.resin.io/beaglebone/nodejs/getting-started/#adding-your-first-device'
+		linux: 'https://docs.resin.io/beaglebone/nodejs/getting-started/#adding-your-first-device'

 	supportsBlink: true


Builder: Include the project.title for compiled files caches which prevents all conflicts between files which has same name like Intro/main.coffe and landing/main.coffee

--- Builder.coffee
+++ Builder.coffee
@@ -208,7 +208,7 @@
         changed |= project.changed
         for includePath in CoffeeScript.eval(fs.readFileSync(project.includes, "utf-8"))

-          cachePath = ".build/#{includePath.replace /\//g, "_" }"
+          cachePath = ".build/#{project.title}_#{includePath.replace /\//g, "_" }"

           file =
             sourceMapPath : cachePath + ".map"


do not specifiy maxWidth for context.fillText; for CJK chracters

--- src/nvim/ui.coffee
+++ src/nvim/ui.coffee
@@ -215,7 +215,7 @@
     # line up as expected
     char_x = x
     for char in chars
-      @ctx.fillText char, char_x, y, @canvas_char_width
+      @ctx.fillText char, char_x, y
       char_x += @canvas_char_width

     if @attrs.underline


oops -- I left in a log message.

oops -- I left in a log message.

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -3367,14 +3367,14 @@
         )

     _init_doc: (cb) =>
-        console.log("_init_doc: connecting to sync session")
+        #console.log("_init_doc: connecting to sync session")
         @status("connecting to sync session")
         @doc = syncdoc.synchronized_string
             project_id : @editor.project_id
             filename   : @syncdoc_filename
             sync_interval : @opts.sync_interval
             cb         : (err) =>
-                console.log("_init_doc returned: err=#{err}")
+                #console.log("_init_doc returned: err=#{err}")
                 @status()
                 if err
                     cb?("Unable to connect to synchronized document server -- #{err}")


Specify interleaved on the URL

Specify interleaved on the URL

--- src/vbo.coffee
+++ src/vbo.coffee
@@ -47,7 +47,8 @@
   constructor: (drawMode, count, options = {}) ->
     @drawMode = webglmc.engine.gl[drawMode]
     @count = count
-    @interleaved = options.interleaved ? false
+    @interleaved = options.interleaved ?
+      'interleaved=1' in window.location.search.substr(1).split('&')
     @buffers = {}
     @uploaded = false


Fix error when item is undefined

Fix error when item is undefined

--- views/components/ship-parts/minishipitem.cjsx
+++ views/components/ship-parts/minishipitem.cjsx
@@ -21,7 +21,7 @@

     {
       for item, i in @props.data
-        continue if item.id == -1
+        continue if !item? || item.id == -1
         itemId = item.id

           <SlotitemIcon key={itemId} className='slotitem-img' slotitemId={item.slotitemId} />


ipython2: remove help about ipython --existing, since I don't understand the IPython docs on this anymore... and that's where this help should be anyways.

--- salvus/page/editor.coffee
+++ salvus/page/editor.coffee
@@ -3911,8 +3911,8 @@
         t += "You are editing this document using the IPython Notebook enhanced with realtime synchronization."
         t += "
Sage mode by pasting this into a cell
"
         t += "
%load_ext sage
"
-        t += "
Connect to this IPython kernel in a terminal
"
-        t += "
ipython console --existing #{@kernel_id}
"
+        #t += "
Connect to this IPython kernel in a terminal
"
+        #t += "
ipython console --existing #{@kernel_id}
"
         t += "
Pure IPython notebooks
"
         t += "You can also directly use an unmodified version of the IPython Notebook server (this link works for all project collaborators).  "
         t += "

To start your own unmodified IPython Notebook server that is securely accessible to collaborators, type in a terminal

ipython-notebook run
"


Use disposables for keymap and menu deactivation

--- src/package.coffee
+++ src/package.coffee
@@ -162,9 +162,10 @@
     @stylesheetsActivated = true

   activateResources: ->
-    atom.keymaps.add(keymapPath, map) for [keymapPath, map] in @keymaps
-    atom.contextMenu.add(map['context-menu']) for [menuPath, map] in @menus
-    atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu
+    @activationDisposables = new CompositeDisposable
+    @activationDisposables.add(atom.keymaps.add(keymapPath, map)) for [keymapPath, map] in @keymaps
+    @activationDisposables.add(atom.contextMenu.add(map['context-menu'])) for [menuPath, map] in @menus
+    @activationDisposables.add(atom.menu.add(map.menu)) for [menuPath, map] in @menus when map.menu

     unless @grammarsActivated
       grammar.activate() for grammar in @grammars
@@ -294,8 +295,8 @@
   deactivateResources: ->
     grammar.deactivate() for grammar in @grammars
     scopedProperties.deactivate() for scopedProperties in @scopedProperties
-    atom.keymaps.remove(keymapPath) for [keymapPath] in @keymaps
     atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
+    @activationDisposables?.dispose()
     @stylesheetsActivated = false
     @grammarsActivated = false
     @scopedPropertiesActivated = false


:fire: remove unused variable

:fire: remove unused variable

--- src/package.coffee
+++ src/package.coffee
@@ -523,7 +523,7 @@
     if @path.indexOf(path.join(atom.packages.resourcePath, 'node_modules') + path.sep) is 0
       # Bundled packages are always considered compatible
       @compatible = true
-    else if packageMain = @getMainModulePath()
+    else if @getMainModulePath()
       @incompatibleModules = @getIncompatibleNativeModules()
       @compatible = @incompatibleModules.length is 0
     else


Use zrevrangebyscore to get prev

Use zrevrangebyscore to get prev

--- index.coffee
+++ index.coffee
@@ -132,7 +132,7 @@
     # the arguments have to be floats :rage4:
     redis.zrangebyscore [@key(), "#{@time + 1}", '+inf', 'WITHSCORES', 'LIMIT', 0, 1], finish (res) =>
         @next = res[1] || null
-    redis.zrangebyscore [@key(), '-inf', "#{@time - 1}", 'WITHSCORES', 'LIMIT', 0, 1], finish (res) =>
+    redis.zrevrangebyscore [@key(), "#{@time - 1}", '-inf', 'WITHSCORES', 'LIMIT', 0, 1], finish (res) =>
         @prev = res[1] || null

   # Return a comic stamped at `stamp` to caller by


expand timestamp parse

expand timestamp parse

Signed-off-by: Adam Stokes <0a364f4bf549cc82d725fa7fd7ed34404be64079@ubuntu.com>

--- index.coffee
+++ index.coffee
@@ -18,18 +18,24 @@
       '\\s' +
       '(?<series>\\w+);\\surgency=(?<priority>\\w+)' +
       '\\s+\\*\\s(?<body>.*)' +
-      '\\s*--\\s(?<ts>.*)', 'img')
+      '\\s*--\\s(?<firstname>\\w+)' +
+      '\\s' +
+      '(?<lastname>\\w+)' +
+      '\\s' +
+      '(?<email><.*>)' +
+      '\\s+' +
+      '(?<timestamp>.*)$', 'img')

     matches = []
-    xre.forEach @blob, entryRe, (matchVer, i) ->
+    xre.forEach @blob, entryRe, (match, i) ->
       model =
-        pkgname: matchVer.pkgname
-        major: parseInt(matchVer.major, 10)
-        minor: parseInt(matchVer.minor, 10)
-        patchLevel: parseInt(matchVer.patchLevel, 10) or undefined
-        versionExtra: matchVer.versionExtra
-        series: matchVer.series
-        priority: matchVer.priority
+        pkgname: match.pkgname
+        major: parseInt(match.major, 10)
+        minor: parseInt(match.minor, 10)
+        patchLevel: parseInt(match.patchLevel, 10) or undefined
+        versionExtra: match.versionExtra
+        series: match.series
+        priority: match.priority
       model.debVersion = "#{model.major}.#{model.minor}"
       if model.patchLevel?
         model.debVersion = "#{model.debVersion}.#{model.patchLevel}"
@@ -37,8 +43,11 @@
       if xre.exec("#{model.major}.#{model.minor}.#{model.patchLevel}",
         /^(\d+\.\d+\.\d+)/)
         model.semVer = true
-      model.body = matchVer.body
-      model.timestamp = matchVer.ts
+      model.body = match.body
+      model.firstname = match.firstname
+      model.lastname = match.lastname
+      model.email = match.email
+      model.timestamp = match.timestamp
       matches.push model
     return Promise.all(matches)


Bug fix when entering History from image replaced by PDF file

--- client/messageImage.coffee
+++ client/messageImage.coffee
@@ -28,7 +28,7 @@

 @imageTransform = (image, rotate) ->
   unless image.width  ## wait for load
-    image.onload = -> imageTransform image, rotate
+    image.onload = -> imageTransform image, rotate if image.style? # still shown
     return
   if rotate
     ## `rotate` is in clockwise degrees


make MiniCourse extend BaseController

make MiniCourse extend BaseController

--- app/lib/mini-course.coffee
+++ app/lib/mini-course.coffee
@@ -1,7 +1,8 @@
-User = require 'zooniverse/models/user'
+User           = require 'zooniverse/models/user'
+BaseController = require 'zooniverse/controllers/base-controller'
 $ = window.jQuery

-class MiniCourse
+class MiniCourse extends BaseController

 	constructor: (el) ->
 		console.log 'MiniCourse.constructor()'


 Merge branch 'master.groups.deleteStatusUpdates' into 'master.groups'

Groups: Added group content deletion.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -1244,6 +1244,7 @@
   remove_ = @::remove
   remove: secure (client, callback)->
     JName = require '../name'
+    JNewStatusUpdate = require '../messages/newstatusupdate'

     @fetchOwner (err, owner)=>
       return callback err if err
@@ -1286,10 +1287,6 @@
           JInvitation = require '../invitation'
           removeHelperMany JInvitation, requests, err, callback, queue

-        => @fetchVocabularies (err, vocabularies)->
-          JVocabulary = require '../vocabulary'
-          removeHelperMany JVocabulary, vocabularies, err, callback, queue
-
         => @fetchTags (err, tags)->
           JTag = require '../tag'
           removeHelperMany JTag, tags, err, callback, queue
@@ -1298,14 +1295,20 @@
           JNewApp = require '../app'
           removeHelperMany JNewApp, apps, err, callback, queue

-        # needs to be tested once subgroups are supported
-        # => @fetchSubgroups (err, groups)=>
-        #   return callback err if err
-        #   return queue.next() unless groups
-        #   ids = (model._id for model in groups)
-        #   JGroup.remove client, (_id: $in: ids), (err)->
-        #     return callback err if err
-        #     queue.next()
+        => JNewStatusUpdate.count group:@slug, (err, count)=>
+          numberOfNamePages = Math.ceil(count / 50)
+
+          deleteQueue = [1..numberOfNamePages].map (pageNumber)=>=>
+            skip = (pageNumber - 1) * 50
+            option = {
+              limit : 50,
+              skip  : skip
+            }
+            JNewStatusUpdate.some group:@slug, option, (err, statusUpdates)=>
+              removeHelperMany JNewStatusUpdate, statusUpdates, err, callback, deleteQueue
+
+          deleteQueue.push => queue.next()
+          daisy deleteQueue

         =>
           @constructor.emit 'GroupDestroyed', this


Always create bundle.

Always create bundle.

--- workers/social/lib/social/models/group/index.coffee
+++ workers/social/lib/social/models/group/index.coffee
@@ -331,6 +331,17 @@
             else
               console.log 'roles are added'
               queue.next()
+        -> group.createBundle
+            users           : { quota: 1000 }
+            cpu             : { quota: 1000 }
+            ram             : { quota: 1000 }
+            disk            : { quota: 1000 }
+            'cpu per user'  : { quota: 1000 }
+            'ram per user'  : { quota: 1000 }
+            'disk per user' : { quota: 1000 }
+          , ->
+            console.log "bundle is created"
+            queue.next()
       ]

       if 'private' is group.privacy
@@ -1293,14 +1304,16 @@
       return callback err  if err
       plan.subscribeGroup @, data, (err, subs)=>
         return callback err  if err
-        @createBundle
-          users           : { quota: 1000 }
-          cpu             : { quota: 1000 }
-          ram             : { quota: 1000 }
-          disk            : { quota: 1000 }
-          'cpu per user'  : { quota: 1000 }
-          'ram per user'  : { quota: 1000 }
-          'disk per user' : { quota: 1000 }
+        @updateBundle
+          overagePolicy     : "not allowed"
+          quota             :
+            users           : { quota: 1000 }
+            cpu             : { quota: 1000 }
+            ram             : { quota: 1000 }
+            disk            : { quota: 1000 }
+            'cpu per user'  : { quota: 1000 }
+            'ram per user'  : { quota: 1000 }
+            'disk per user' : { quota: 1000 }
         , (err)->
           callback no, subs


JProxyFilter: Added update method.

JProxyFilter: Added update method.

--- workers/social/lib/social/models/proxy/index.coffee
+++ workers/social/lib/social/models/proxy/index.coffee
@@ -33,6 +33,7 @@
         some          : (signature Object, Object, Function)
       instance        :
         remove        : (signature Function)
+        update        : (signature Object, Function)

   @create: secure (client, data, callback = noop) ->
     {delegate}    = client.connection
@@ -80,3 +81,6 @@
         JProxyRestriction = require "./restriction"
         JProxyRestriction.clear client, @getId(), (err) ->
           callback err
+
+  update$: secure (client, data, callback = noop) ->
+    @update { $set: data }, callback


Change node title input to not update on each keypress [#160668583]

Now the node title only updates after the input is blurred, either by clicking away from the input or hitting the enter key.

The escape key now cancels the node title change, restoring it to the starting title value before the input is shown.

--- src/code/views/node-view.coffee
+++ src/code/views/node-view.coffee
@@ -17,29 +17,32 @@
   mixins: [require '../mixins/node-title']

   getInitialState: ->
-    isUniqueTitle: @isUniqueTitle @props.node.title
-
-  isUniqueTitle: (title) ->
-    @props.graphStore.isUniqueTitle title, @props.node
+    @getStateFromProps @props
+
+  isUniqueTitle: (title, props) ->
+    @props.graphStore.isUniqueTitle title, (props or @props).node

   componentWillUnmount: ->
     if @props.isEditing
       @inputElm().off()

   componentWillUpdate: (nextProps) ->
-    # mark the title as updated even if no change was made when it leaves edit mode
-    @titleUpdated = true if @props.isEditing and not nextProps.isEditing
+    if @props.isEditing and not nextProps.isEditing
+      # mark the title as updated even if no change was made when it leaves edit mode
+      @titleUpdated = true
+    else if not @props.isEditing and nextProps.isEditing
+      # reset title state based on node title
+      @setState @getStateFromProps nextProps

   componentDidUpdate: ->
     if @props.isEditing
       $elem = @inputElm()
       $elem.focus()

-      $elem.off()
-      enterKey = 13
-      $elem.on "keyup", (e)=>
-        if e.which is enterKey
-          @finishEditing()
+  getStateFromProps: (props) ->
+    title: props.node.title
+    isUniqueTitle: @isUniqueTitle props.node.title, props
+    isCancelled: false

   inputElm: ->
     $(@refs.input)
@@ -47,20 +50,32 @@
   inputValue: ->
     @inputElm().val()

-  detectDeleteWhenEmpty: (e) ->
+  handleKeyUp: (e) ->
     # 8 is backspace, 46 is delete
     if e.which in [8, 46] and not @titleUpdated
-      @props.graphStore.removeNode @props.nodeKey
-
-  updateTitle: (isComplete) ->
+      canDeleteWhenEmpty = @props.node.addedThisSession and not @titleUpdated
+      if canDeleteWhenEmpty
+        @props.graphStore.removeNode @props.nodeKey
+    # 13 is enter
+    else if e.which is 13
+      @finishEditing()
+    # 27 is escape
+    else if e.which is 27
+      @setState isCancelled: true, => @finishEditing()
+
+  updateTitle: (isComplete, callback) ->
     @titleUpdated = true
-    newTitle = @cleanupTitle(@inputValue(), isComplete)
-    @setState isUniqueTitle: @isUniqueTitle newTitle
-    @props.onChange(newTitle, isComplete)
+    title = if @state.isCancelled then @props.node.title else @inputValue()
+    newTitle = @cleanupTitle(title, isComplete)
+    newState =
+      title: newTitle,
+      isUniqueTitle: @isUniqueTitle newTitle
+    @setState newState, callback

   finishEditing: ->
-    @updateTitle(true)
-    @props.onStopEditing()
+    @updateTitle true, =>
+      @props.onChange(@state.title, true) unless @state.isCancelled
+      @props.onStopEditing()

   renderTitle: ->
     (div {
@@ -71,8 +86,7 @@
     }, @props.title)

   renderTitleInput: ->
-    displayTitle = @displayTitleForInput(@props.title)
-    canDeleteWhenEmpty = @props.node.addedThisSession and not @titleUpdated
+    displayTitle = @displayTitleForInput(@state.title)
     className = "node-title#{if not @state.isUniqueTitle then ' non-unique-title' else ''}"
     (input {
       type: "text"
@@ -80,7 +94,7 @@
       key: "edit"
       style: { display: if @props.isEditing then "block" else "none" }
       className: className
-      onKeyUp: if canDeleteWhenEmpty then @detectDeleteWhenEmpty else null
+      onKeyUp: if @props.isEditing then @handleKeyUp else null
       onChange: => @updateTitle()
       value: displayTitle
       maxLength: @maxTitleLength()


add events to show/hide new todo view

add events to show/hide new todo view

--- app/assets/javascripts/dashit/views/todos/list.js.coffee
+++ app/assets/javascripts/dashit/views/todos/list.js.coffee
@@ -1,3 +1,9 @@
 DashIt.ProjectTodosView = Ember.View.extend
   templateName: 'dashit/templates/todos/list'
   todosBinding: 'DashIt.todosController'
+
+  showNewTodo:
+    -> @set "isNewTodoVisible", true
+
+  hideNewTodo:
+    -> @set "isNewTodoVisible", false


+ updating store on succesful ajax

+ updating store on succesful ajax

--- app/assets/javascripts/mixins/model_ajax_mixin.js.coffee
+++ app/assets/javascripts/mixins/model_ajax_mixin.js.coffee
@@ -3,10 +3,15 @@
  _ajax: (url, method, options) ->
     id = @get 'id'
     type    = @get 'constructor'
-    adapter = @get('store').adapterFor type
+    store   = @get 'store'
+    adapter = store.adapterFor type
     url     = '%@/%@/%@'.fmt adapter.buildURL(type.typeKey), id, url

-    adapter.ajax url, method, options
+    adapter.ajax(url, method, options).then (response) =>
+      store.pushPayload(type.typeKey,response)
+      console.info 'inserted'
+
+

   post: (url, options) ->


explicit ObjectFile.__startUpload flow

explicit ObjectFile.__startUpload flow

--- app/assets/javascripts/uploadcare/files/object.js.coffee
+++ app/assets/javascripts/uploadcare/files/object.js.coffee
@@ -33,33 +33,34 @@
     __startUpload: ->
       @apiDeferred.always =>
         @__file = null
+
       if @__file.size >= @MP_MIN_SIZE and utils.abilities.blob
         @setFile()
-        @multipartUpload()
-      else
-        ios = utils.abilities.iOSVersion
-        if @settings.imageShrink and (not ios or ios >= 8)
-          df = $.Deferred()
-          resizeShare = .4
-
-          utils.imageProcessor.shrinkFile(@__file, @settings.imageShrink)
+        return @multipartUpload()
+
+      ios = utils.abilities.iOSVersion
+      if not @settings.imageShrink or (ios and ios < 8)
+        @setFile()
+        return @directUpload()
+
+      # if @settings.imageShrink
+      df = $.Deferred()
+      resizeShare = .4
+      utils.imageProcessor.shrinkFile(@__file, @settings.imageShrink)
+        .progress (progress) ->
+          df.notify(progress * resizeShare)
+        .done(@setFile)
+        .fail =>
+          @setFile()
+          resizeShare = resizeShare * .1
+        .always =>
+          df.notify(resizeShare)
+          @directUpload()
+            .done(df.resolve)
+            .fail(df.reject)
             .progress (progress) ->
-              df.notify(progress * resizeShare)
-            .done(@setFile)
-            .fail =>
-              @setFile()
-              resizeShare = resizeShare * .1
-            .always =>
-              df.notify(resizeShare)
-              @directUpload()
-                .done(df.resolve)
-                .fail(df.reject)
-                .progress (progress) ->
-                  df.notify(resizeShare + progress * (1 - resizeShare))
-          df
-        else
-          @setFile()
-          @directUpload()
+              df.notify(resizeShare + progress * (1 - resizeShare))
+      return df

     __autoAbort: (xhr) ->
       @apiDeferred.fail(xhr.abort)


Gimme some numbers.

Gimme some numbers.

--- lib/git-service.coffee
+++ lib/git-service.coffee
@@ -318,10 +318,10 @@
     return false unless headerParts

     data =
-      oldStart: headerParts[1]
-      oldCount: headerParts[2]
-      newStart: headerParts[3]
-      newCount: headerParts[4]
+      oldStart: parseInt(headerParts[1], 10)
+      oldCount: parseInt(headerParts[2], 10)
+      newStart: parseInt(headerParts[3], 10)
+      newCount: parseInt(headerParts[4], 10)
       context:  headerParts[5]

   calculatePatchText: (hunk, selectedLines, stage) ->


Remove try/catch now that Electron 0.36.8 is on stable

--- lib/github-file.coffee
+++ lib/github-file.coffee
@@ -1,5 +1,4 @@
-# TODO: Remove the catch once Atom 1.7.0 is released
-try {shell} = require 'electron' catch then shell = require 'shell'
+{shell} = require 'electron'
 {Range} = require 'atom'
 parseUrl = require('url').parse
 path = require('path')


fixed PagingScrollElement flick gesture - yay!

--- src/art/engine/events/pointer_event.coffee
+++ src/art/engine/events/pointer_event.coffee
@@ -26,6 +26,7 @@
         options.pointer || @pointer
         options.time || @time
       )
+      e.timeStamp = @timeStamp
       e.target = options.target || @target
       e


OPCE6: getWord returns a Word object, not a string

--- lib/y-rte.coffee
+++ lib/y-rte.coffee
@@ -18,13 +18,30 @@
   # @param [String] word The initial string value
   # @return [Word] a word instance
   constructor: (@word) ->
-
-  # Remove the selection from the selections bound to the word
-  # @param [Selection] selToRemove the selection to remove from selections list
-  removeSel: (selToRemove) ->
-    @selections.forEach sel, index, array ->
-      if sel.equals(selToRemove)
-        array.splice(index, 1)
+    # Selections that have this word as left bound
+    @left = []
+    # Selections that have this word as right bound
+    @right = []
+
+  # Remove a selection from left or right array
+  #
+  # @param [Selection] selection the selection to remove
+  # @param [Option] side the side where to remove the selection
+  # @option side [String] left left side
+  # @option side [String] right right side
+  removeSel: (selection, side)->
+    if side == "left"
+      array = @left
+    else if side == "right"
+      array = @right
+    else
+      throw new Error "Invalid argument #{side}, expected 'left' or 'right'"
+    index = 0
+    for element in array
+      if element.equals selection
+        array.pop(index)
+        break
+      index += 1

 # A class describing a selection with a style (bold, italic, …)
 class Selection
@@ -39,6 +56,7 @@
   #   @param [Rte] rte a rich-text editor (Rte) instance
   #   @option options [Object] style the style of the selection
   constructor: (start, end, rte, style)->
+
     if not _.isUndefined(start) and not _.isUndefined(end) and not _.isUndefined(rte)
       if !( _.isNumber(start) and
             _.isNumber(end))
@@ -124,6 +142,16 @@
   overlaps: (selection) ->
     @startPos.lt(selection.endPos) or @endPos.gt(selection.startPos)

+  # Returns true if the given selection and this selection are contiguous
+  # and this selection is at left of the given one
+  #
+  # @param [Selection] selection the selection to compare to this
+  atLeftOf: (selection) ->
+    (@endPos.word == selection.startPos.word and
+     @endPos.pos == selection.startPos.pos)
+
+  # Merge the given selection with this selection if they are contiguous
+
   #TODO
   setStyle: (@style) ->

@@ -133,36 +161,39 @@
     @startPos.lt(@endPos)

-  # Try to merge this selection with the one given in argument
-  #
-  # @param s [Selection] The selection to merge with
-  # @param rte [Rte] The RTE instance
+  # Try to merge the given selection with this selection
+  #
+  # @param [Selection] selection the selection to merge to
+  # @param [Rte] rte a Rich text editor instance
+  #
+  # @example
+  #   1                 2                   3
+  #   [  left selection ][  right selection ]
+  #    becomes
+  #   [           right selection           ]
   merge: (selection, rte) ->
-    # Check if they're contiguous
-    if selection.endPos.word == @startPos.word or
-       (selection.endPos.word == @startPos.word -1 and
-        selection.endPos.loc == rte.getWord(selection.endPos.word).length() and
-        @startPos.loc == 0)
-      # boundaries of new selection
-      start = selection.startPos
-      end = @endPos
-      # remove link of middle words to selection
-      rte.getWord(selection.endPos.word).removeSel(selection)
-      rte.getWord(@startPos.word).removeSel(@)
-    else if @endPos.word == selection.startPos.word or
-       (@endPos.word == selection.startPos.word -1 and
-        @endPos.loc == rte.getWord(@endPos.word).length() and
-        selection.startPos.loc == 0)
-      start = @startPos
-      end = selection.endPos
-      rte.getWord(@endPos.word).removeSel(@)
-      rte.getWord(selection.endPos.word).removeSel(selection)
-    else
-      return
-
-    nSelec = new Selection start, end, selection.style
-    rte.getWord()
-
+    if @.style != selection.style
+      return false
+    if @atLeftOf selection
+      leftSel = @
+      rightSel = selection
+    else
+      leftSel = selection
+      rightSel = @
+
+    # unbind words from left selection, remove it from selection list
+    # expand the selection at right to the left end of previous left selection
+    leftSel.unbind()
+    rightSel.left.removeSel rightSel, "left"
+    rightSel.left = leftSel.left
+
+    rightSel.startPos = leftSel.startPos
+
+    rte.removeSel leftSel
+
+  unbind: ->
+    @left.removeSel(@, "left")
+    @right.removeSel(@, "right")

 # Class describing the Rich Text Editor type
 #
@@ -190,7 +221,7 @@
   # unobserve:

   # @overload val()
-  #   Return the value of the Rte instance as a string
+  #   Return the value of the Rte instance as a non formatted string
   #
   # @overload val(content)
   #   Set the content of the Rte instance
@@ -205,15 +236,15 @@
       # TODO: support breaks (br, new paragraph, …)
       (e.word for e in @_rte.words).join('')

-  # Returns the **string representation** of a word.
+  # Returns the word object of a word.
   # @param index [Integer] the index of the word to return
   getWord: (index) ->
     if @_rte.words.length == 0 or index == @_rte.words.length
-      return ""
+      return new Word ""

     if not (0 <= index < @_rte.words.length)
       throw new Error "Index out of bounds #{index}"
-    @_rte.words[index].word
+    @_rte.words[index]

   # Returns the *word objects* within boundaries
   # @param begin [Integer] the first word the return
@@ -293,34 +324,33 @@

   # Merge two words at position
   #
-  # @param [Integer] n position of word where to perform merge. The merge will
-  #  be done with the word at right (if any)
+  # @param [Integer] n position of word where to perform merge. The merge will be done with the word at right (if any)
   #
   merge: (index) ->
     if 0 <= index < @_rte.words.length
-      word = @getWord(index).trimRight()
+      word = @getWord(index).word.trimRight()
       @deleteWords index
       @insert {startPos: {word: index, pos:0}}, word
     else
       throw new Error "Impossible to merge"

-  # Delete a selection
+  # Delete text under selection
   #
   # @param [Selection] sel the selection to delete
   #
   deleteSel: (selection) ->
     if not selection.isValid()
       throw new Error "Invalid selection"
-    if not 0 <= selection.startPos.loc <= @getWord(selection.startPos.word).length
+    if not 0 <= selection.startPos.loc <= @getWord(selection.startPos.word).word.length
       throw new Error "Invalid selection"
-    if not 0 <= selection.endPos.loc <= @getWord(selection.endPos.word).length
+    if not 0 <= selection.endPos.loc <= @getWord(selection.endPos.word).word.length
       throw new Error "Invalid selection"

     start = selection.startPos.word
     end = selection.endPos.word

-    newLeft = @getWord(start).substring(0, selection.startPos.pos)
-    newRight = @getWord(end).substring(selection.endPos.pos)
+    newLeft = @getWord(start).word.substring(0, selection.startPos.pos)
+    newRight = @getWord(end).word.substring(selection.endPos.pos)

     if start == end
       @setWord(start, newLeft + newRight)
@@ -338,7 +368,7 @@

   # Insert text at position
   #
-  # @param [Option] position The position where to insert text
+  # @param [Selection] position The position where to insert text
   # @param [String] content the content to insert
   #
   insert: (selection, content)->
@@ -353,7 +383,7 @@
     pos = selection.startPos.pos

     preSpaces = content.match PreSpacesRegExp
-    currWord = @getWord index
+    currWord = @getWord(index).word

     # move the spaces to the previous word if a pos == 0
     if preSpaces isnt null
@@ -361,7 +391,7 @@
         if index == 0
           index += 1
           @insertWord 0, (new Word '')
-        prevWord = @getWord (index-1)
+        prevWord = @getWord(index-1).word
         prevWord += preSpaces
         content = content.substring(preSpaces.length)
         @setWord (index-1), prevWord
@@ -389,7 +419,7 @@
         if pos < jump
           word -= 1
           jump -= pos
-          pos = @getWord(word).length - 1
+          pos = @getWord(word).word.length - 1

           if word < 0
             return null
@@ -419,6 +449,32 @@
     if not selection.isValid()
       throw new Error "Invalid selection"

+    # In case the selection already has a style which is different
+    if selection.style != style
+      selection = new Selection(selection.start, selection.end, style)
+
+    # If no style add it
+    if _.isUndefined selection.style
+      selection.style = style
+
+    # Link the boundary words to selection
+    leftWord = @getWord selection.startPos.word
+    rightWord = @getWord selection.endPos.word
+
+    selection.left = leftWord
+    selection.right = rightWord
+
+    leftWord.left.push(selection)
+    rightWord.right.push(selection)
+
+    # Merge left…
+    for tmpSelection in leftWord.right
+      tmpSelection.merge(selection, @)
+    # …and right (only happens when selections are contiguous or overlapping
+    # and have same style)
+    for tmpSelection in rightWord.left
+      tmpSelection.merge(selection, @)
+
     # Try to merge with previous / next contiguous selection
     {prevWord, position} = @_jump(selection.startPos, -1)
     {nextWord, position} = @_jump(selection.endPos,   +1)
@@ -455,6 +511,18 @@
         for attr in delta.attributes
           @setAttr(selection.clone().style = attr)

+  # Remove a selection from selection list
+  #
+  # @param [Selection] selection the selection to remove
+  removeSel: (selection) ->
+    index = 0
+    array = @_rte.selections
+    for element in array
+      if (element.equals(selection) and (element.style == selection.style))
+        array.splice index, 1
+        break
+      index += 1
+
 if window?
   window.Rte = Rte
   window.Selection = Selection


No arrows if panel items is less than per_page value.

--- app/assets/javascripts/ample_assets/classes/ample_assets.js.coffee
+++ app/assets/javascripts/ample_assets/classes/ample_assets.js.coffee
@@ -315,6 +315,7 @@

   controls: (display=true) ->
     @log "controls(#{display})"
+    display = false if $(@active_panel).find('li').length < @options.pages_options.per_page
     switch display
       when true
         $('nav.controls').show()


FIXED conversation page bug

FIXED conversation page bug

--- app/assets/javascripts/angular/controllers/ConversationCtrl.coffee
+++ app/assets/javascripts/angular/controllers/ConversationCtrl.coffee
@@ -46,13 +46,14 @@
     $location.url '/view-profile'

   $scope.submitMessage = (conversation, messageText) ->
-    User.createMessageTo($scope.currentUser.id, $scope.selectedConvo.targetUserId, messageText, $scope.selectedConvo.id).success (response) ->
-      $scope.messageText = ''
-      loadConvo()
-      reloadMessages(conversation)
-    .error (response) ->
-      Conversation.notyErrorForMessage()
-    return
+    User.loadOne($scope.selectedConvo.targetUserId).then (data) ->
+      User.createMessageTo($scope.currentUser, data.users[0] , messageText, $scope.selectedConvo.id).success (response) ->
+        $scope.messageText = ''
+        loadConvo()
+        reloadMessages(conversation)
+      .error (response) ->
+        Conversation.notyErrorForMessage()
+      return

   reloadUser()


Improved see more feature.

Improved see more feature.

--- app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
+++ app/assets/javascripts/app/controllers/agent_ticket_zoom.js.coffee
@@ -73,7 +73,7 @@
         # cleanup body
         article['html'] = article.body.trim()
         article['html'].replace(/\n\r/g, "\n");
-        article['html'].replace(/\n\n/mg, "\n");
+        article['html'].replace(/\n\n\n/g, "\n\n");

         # if body has more then x lines / else search for signature
         preview       = 15
@@ -81,7 +81,10 @@
         article_lines = article['html'].split(/\n/)
         if article_lines.length > preview
           preview_mode = true
-          article_lines.splice( preview, 0, "----SEEMORE----" )
+          if article_lines[preview] is ''
+            article_lines.splice( preview-1, 0, "----SEEMORE----" )
+          else
+            article_lines.splice( preview, 0, "----SEEMORE----" )
           article['html'] = article_lines.join("\n")
         article['html'] = window.linkify( article['html'] )
         notify = "" + T('See more') + ""


891: Use https URL for MarkerClusterer images

Avoids mixed content warnings under HTTPS

--- app/assets/javascripts/backbone/views/dashboard_map_view.js.coffee
+++ app/assets/javascripts/backbone/views/dashboard_map_view.js.coffee
@@ -16,7 +16,9 @@
     })

     # create the marker clusterer
-    mc = new MarkerClusterer(@map)
+    mc = new MarkerClusterer(@map, [], {
+      imagePath: 'https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m'
+    })

     # add the markers and keep expanding the bounding rectangle
     bounds = new google.maps.LatLngBounds()


CoffeeScript optimization

CoffeeScript optimization

--- src/components/Page/Page.coffee
+++ src/components/Page/Page.coffee
@@ -74,12 +74,13 @@
   sections = []
   currentSection = {wrap: false, children: []} # First section isn't a card

-  children.forEach (descriptor) ->
+  children.forEach (descriptor) ->
     if descriptor.type.displayName is 'h2' # Caveat: This isn't a documented React API
       # start new section
       sections.push currentSection
       currentSection = {wrap: true, children: []}
     currentSection.children.push(descriptor)
+    null

   sections.map (s) ->
     if s.wrap


Start working on specs

Start working on specs

--- spec/markdown-preview-spec.coffee
+++ spec/markdown-preview-spec.coffee
@@ -154,6 +154,22 @@
             expect(editorPane.isActive()).toBe true
             expect(previewPane.getActiveItem()).toBe preview

+        describe "when the preview is split", ->
+          it "renders another preview in the new split pane", ->
+            markdownEditor = atom.workspace.getActiveTextEditor()
+            previewPane = atom.workspace.getPanes()[1]
+            previewPane.activate()
+            splitPane = previewPane.splitRight({copyActiveItem: true})
+
+            expect(atom.workspace.getPanes()).toHaveLength 3
+
+            waitsFor "split markdown preview to be created", ->
+              preview = atom.workspace.getPanes()[2].getActiveItem()
+
+            runs ->
+              expect(preview).toBeInstanceOf(MarkdownPreviewView)
+              expect(preview.getPath()).toBe atom.workspace.getActivePaneItem().getPath()
+
       describe "when the liveUpdate config is set to false", ->
         it "only re-renders the markdown when the editor is saved, not when the contents are modified", ->
           atom.config.set 'markdown-preview.liveUpdate', false


Check GitHub API for tag before notifying atom.io

This is the first attempt to fix these intermittent errors:

```
apm publish minor
Preparing and tagging a new version ✓
Pushing v0.26.0 tag ✓
Publishing dev-live-reload@v0.26.0 ✗
Creating new version failed: Git tag not found
```

Closes #51

--- src/publish.coffee
+++ src/publish.coffee
@@ -78,6 +78,43 @@
       else
         process.stdout.write '\u2717\n'.red
         callback("#{stdout}\n#{stderr}")
+
+  # Check for the tag being available from the GitHub API before notifying
+  # atom.io about the new version.
+  #
+  # The tag is checked for 5 times at 1 second intervals.
+  #
+  # pack - The package metadata.
+  # tag - The tag that was pushed.
+  # callback - The callback function to invoke when either the tag is available
+  #            or the maximum numbers of requests for the tag have been made.
+  #            No arguments are passed to the callback when it is invoked.
+  waitForTagToBeAvailable: (pack, tag, callback) ->
+    @getToken (error, token) =>
+      if error?
+        callback(error)
+        return
+
+      retryCount = 5
+      interval = 1000
+      requestSettings =
+        url: "https://api.github.com/repos/#{@getRepository(pack)}/tags"
+        json: true
+        proxy: process.env.http_proxy || process.env.https_proxy
+        headers:
+          'User-Agent': "AtomApm/#{require('../package.json').version}"
+          authorization: "token #{token}"
+
+      requestTags = ->
+        request.get requestSettings, (error, response, tags=[]) ->
+          if response.statusCode is 200
+            for {name}, index in tags when name is tag
+              return callback()
+          if --retryCount <= 0
+            callback()
+          else
+            setTimeout(requestTags, interval)
+      requestTags()

   # Does the given package already exist in the registry?
   #
@@ -257,10 +294,12 @@
           @pushVersion tag, (error) =>
             return callback(error) if error?

-            @publishPackage pack, tag, (error) =>
-              if firstTimePublishing and not error?
-                @logFirstTimePublishMessage(pack)
-              callback(error)
+            @waitForTagToBeAvailable pack, tag, =>
+
+              @publishPackage pack, tag, (error) =>
+                if firstTimePublishing and not error?
+                  @logFirstTimePublishMessage(pack)
+                callback(error)
     else if tag?.length > 0
       @registerPackage pack, (error, firstTimePublishing) =>
         return callback(error) if error?


updated nutchStatus API to return human readable status string instead of numeric value

--- routes/nutchStatus.coffee
+++ routes/nutchStatus.coffee
@@ -1,4 +1,10 @@
 db = require '../repositories/db.coffee'
+
+jobStatusMapping = {}
+jobStatusMapping[0] =  "IN_PROGRESS"
+jobStatusMapping[1] = "SUCCESS"
+jobStatusMapping[-1] = "FAILURE"
+

 find = (req, res, next) ->
 	identifier = req.query.identifier
@@ -15,7 +21,7 @@
 				jobStatus = {}
 				jobStatus.identifier = doc.identifier
 				jobStatus.jobName = doc.jobName
-				jobStatus.status  = doc.status
+				jobStatus.status  = jobStatusMapping[doc.status]
 				jobStatus.date = doc.date
 				jobStatuses.push jobStatus
 			res.status 200


missing import

missing import

--- resources/assets/lib/components/comment.coffee
+++ resources/assets/lib/components/comment.coffee
@@ -10,7 +10,7 @@
 import { classWithModifiers } from 'utils/css'
 import { estimateMinLines } from 'utils/estimate-min-lines'
 import { createClickCallback, formatNumberSuffixed } from 'utils/html'
-import { trans } from 'utils/lang'
+import { trans, transChoice } from 'utils/lang'
 import ClickToCopy from './click-to-copy'
 import { CommentEditor } from './comment-editor'
 import { CommentShowMore } from './comment-show-more'


Fixing tests

Fixing tests

--- math/expression_manipulation_spec.coffee
+++ math/expression_manipulation_spec.coffee
@@ -13,12 +13,21 @@
       unless results instanceof ttm.lib.math.ExpressionPosition
         throw "The return value was not an instance of expression position"

-it_inserts_component_into_the_last_nested_open_expression = (specifics)->
-  it "adds #{specifics.name} to the deepest final open expression", ->
-    @exp = @exp_builder(open_expression: {open_expression: []})
-    exp = specifics.manipulator.call(@).perform(@exp)
-    expected = specifics.should_equal_after(@exp_builder)
-    expect(exp.expression).toBeAnEqualExpressionTo expected
+it_inserts_components_where_pointed_to = (specifics)->
+  describe "#{specifics.name} inserts correctly when position is", ->
+    it "works with a base case", ->
+      start =
+        if specifics.basic_start_expression
+          specifics.basic_start_expression.call(@)
+        else
+          @exp_pos_builder()
+      subject = specifics.subject.call(@)
+      new_exp = subject.perform(start)
+      expect(new_exp.expression()).toBeAnEqualExpressionTo(
+       specifics.basic_should_equal_after.call(@)
+      )
+
+

 describe "expression manipulations", ->
   beforeEach ->
@@ -59,6 +68,13 @@
       expression_for_performance: ->
         @exp_pos_builder(10)

+    it_inserts_components_where_pointed_to(
+      name: 'multiplication'
+      subject: -> @manip.build_append_multiplication()
+      basic_start_expression: -> @exp_pos_builder 1
+      basic_should_equal_after: -> @exp_builder 1, '*'
+    )
+
     it "does nothing if the expression is empty", ->
       exp_pos = @exp_pos_builder()
       exp_pos = @manip.build_append_multiplication().perform(exp_pos)
@@ -76,10 +92,7 @@
       expected = @exp_builder({'^': [1, 2]}, '*')
       expect(new_exp.expression()).toBeAnEqualExpressionTo expected

-    it_inserts_component_into_the_last_nested_open_expression(
-      name: 'multiplication'
-      manipulator: -> @manip.build_append_multiplication()
-      should_equal_after: (builder)-> builder(open_expression: {open_expression: ['*']}))
+

   describe "exponentiate last element", ->
     describe "on a single number-only expression", ->
@@ -106,7 +119,7 @@
         expected = @exp_builder('^': [10, {open_expression: null}])
         expect(new_exp).toBeAnEqualExpressionTo expected

-  describe "append number", ->
+  describe "appending a number", ->
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_number(value: 8)
@@ -128,7 +141,7 @@
         expected = @exp_builder([1], '*', 11)
         expect(exp.expression()).toBeAnEqualExpressionTo expected

-  describe "append exponentiation", ->
+  describe "appending exponentiation", ->
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_exponentiation()
@@ -204,15 +217,22 @@
       expression_for_performance: ->
         @exp_pos_builder(10)

+    it_inserts_components_where_pointed_to(
+      name: 'appending a sub expression'
+      subject: -> @manip.build_append_open_sub_expression()
+      basic_start_expression: -> @exp_pos_builder()
+      basic_should_equal_after: -> @exp_builder {open_expression: []}
+    )
+
     it "adds a sub-expression to the expression", ->
-      @exp = @exp_builder(1, '+')
-      new_exp = @manip.build_append_open_sub_expression().perform(@exp)
+      @exp = @exp_pos_builder(1, '+')
+      new_exp = @manip.build_append_open_sub_expression().perform(@exp).expression()
       expected = @exp_builder(1, '+', {open_expression: []})
       expect(new_exp).toBeAnEqualExpressionTo expected

     it "adds an expression that isOpen", ->
-      @exp = @exp_builder(1, '+')
-      new_exp = @manip.build_append_open_sub_expression().perform(@exp)
+      @exp = @exp_pos_builder(1, '+')
+      new_exp = @manip.build_append_open_sub_expression().perform(@exp).expression()

       exp = new_exp.nth(2)
       expect(exp).toBeInstanceOf @components.classes.expression
@@ -235,30 +255,50 @@

         expect(exp).toBeAnEqualExpressionTo expected

-  describe "closing a sub expression ", ->
+  describe "closing a sub expression", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_close_sub_expression()
+      expression_for_performance: ->
+        ret = @exp_pos_builder({open_expression: []})
+        # we need to set the pointed position to the id of the open expression
+        ret.clone(position: ret.expression().first().id())
+
+
+    it_inserts_components_where_pointed_to(
+      name: 'multiplication'
+      subject: -> @manip.build_append_multiplication()
+      basic_start_expression: -> @exp_pos_builder 1
+      basic_should_equal_after: -> @exp_builder 1, '*'
+    )
+
     it "takes an open subexpression and closes it", ->
-      exp = @exp_builder({open_expression: []})
+      exp = @exp_pos_builder({open_expression: []})
+      exp = exp.clone position: exp.expression().first().id()
+
       new_exp = @manip.build_close_sub_expression().perform(exp)
-      expect(new_exp).toBeAnEqualExpressionTo @exp_builder([])
-
-    it "correctly handles nested open subexpressions", ->
-      exp = @exp_builder(open_expression: {open_expression: null})
+      expect(new_exp.expression()).toBeAnEqualExpressionTo @exp_builder([])
+
+    it "correctly handles closing a nested open subexpression that is pointed at", ->
+      exp = @exp_pos_builder(open_expression: {open_expression: null})
+      exp = exp.clone position: exp.expression().first().first().id()
       new_exp = @manip.build_close_sub_expression().perform(exp)
       expected = @exp_builder(open_expression: [[]])
-      expect(new_exp).toBeAnEqualExpressionTo expected
+      expect(new_exp.expression()).toBeAnEqualExpressionTo expected

   describe "appending division", ->
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_division()
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "adds a division to the expression", ->
       exp = @exp_builder(1)

       new_exp = @manip.build_append_division().perform(exp)
       expect(new_exp.last() instanceof @components.classes.division).toBeTruthy()
-

     it_inserts_component_into_the_last_nested_open_expression(
       name: 'division'
@@ -270,23 +310,28 @@
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_addition()
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    it_inserts_components_where_pointed_to(
+      name: 'addition'
+      subject: -> @manip.build_append_addition()
+      basic_start_expression: -> @exp_pos_builder 1
+      basic_should_equal_after: -> @exp_builder 1, '+'
+    )

     it "adds addition to the end of the expression", ->
-      exp = @exp_builder(1)
+      exp = @exp_pos_builder(1)
       new_exp = @manip.build_append_addition().perform(exp)
-      expect(new_exp.last() instanceof @components.classes.addition).toEqual true
-
-    it_inserts_component_into_the_last_nested_open_expression(
-      name: 'addition'
-      manipulator: -> @manip.build_append_addition()
-      should_equal_after: (builder)-> builder(open_expression: {open_expression: ['+']}))
-
+      expect(new_exp.expression().last() instanceof @components.classes.addition).toEqual true

   describe "appending subtraction", ->
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_subtraction()
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "adds subtraction to the end of the expression", ->
       exp = @exp_builder(1)
@@ -302,6 +347,8 @@
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_decimal(value: 5)
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "correctly adds a decimal to the value", ->
       exp = @components.build_expression()
@@ -320,6 +367,8 @@
     it_plays_manipulation_role
       subject: ->
         @manip.build_square()
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "returns a squared expression", ->
       exp = @exp_builder(10)
@@ -330,6 +379,8 @@
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_root()
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "adds a mulitplication and root to the expression", ->
       exp = @exp_builder([1])
@@ -351,6 +402,8 @@
     it_plays_manipulation_role
       subject: ->
         @manip.build_append_variable()
+      expression_for_performance: ->
+        @exp_pos_builder(10)

     it "adds itself to an empty expression", ->
       exp = @exp_builder()
@@ -387,39 +440,46 @@
         false

-  describe "(moved over from other test file)", ->
-
-    describe "NegationManipulation", ->
-      it_plays_manipulation_role
-        subject: ->
-          @manip.build_negate_last()
-
-      it "will negate the last element", ->
-        exp = @exp_builder(1)
-        new_exp = @manip.build_negate_last().perform(exp)
-        expect(new_exp.last().value()).toEqual -1
-
-
-    describe "AppendPi", ->
-      it_plays_manipulation_role
-        subject: ->
-          @manip.build_append_pi()
-
-      it "adds a mulitplication and pi to the expression", ->
-        exp = @exp_builder(1)
-        new_exp = @manip.build_append_pi().perform(exp)
-        expect(new_exp.last() instanceof @components.classes.pi).toBeTruthy()
-        expect(new_exp.last(1) instanceof @components.classes.multiplication).toBeTruthy()
-
-
-    describe "SquareRootManipulation", ->
-      it_plays_manipulation_role
-        subject: ->
-          @manip.build_square_root()
-
-      it "finds the square root of an expression", ->
-        exp = @exp_builder([4])
-        new_exp = @manip.build_square_root().perform(exp)
-        expect(new_exp.last().value()).toEqual '2'
-
-
+  describe "NegationManipulation", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_negate_last()
+      expression_for_performance: ->
+        @exp_pos_builder(10)
+
+    it "will negate the last element", ->
+      exp = @exp_builder(1)
+      new_exp = @manip.build_negate_last().perform(exp)
+      expect(new_exp.last().value()).toEqual -1
+
+  describe "AppendPi", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_append_pi()
+
+    it "adds a mulitplication and pi to the expression", ->
+      exp = @exp_builder(1)
+      new_exp = @manip.build_append_pi().perform(exp)
+      expect(new_exp.last() instanceof @components.classes.pi).toBeTruthy()
+      expect(new_exp.last(1) instanceof @components.classes.multiplication).toBeTruthy()
+
+
+  describe "SquareRootManipulation", ->
+    it_plays_manipulation_role
+      subject: ->
+        @manip.build_square_root()
+
+    it "finds the square root of an expression", ->
+      exp = @exp_pos_builder([4])
+      new_exp = @manip.build_square_root().perform(exp)
+      expect(new_exp.expression().last().value()).toEqual '2'
+
+  describe "utilities", ->
+    describe "implicit multiplication", ->
+      it "should have tests that go here", ->
+
+    describe "expression manipulator", ->
+      it "", ->
+
+
+