Refile upload with jquery-ui and progress

Ran into a few gotschas while implementing this, so I wanted to share 🙂

– buttons need to be disabled via the button method
– e.originalEvent.detail holds progress information


$(document).on("upload:start", "form", function(e) {
  $(e.target).prev().prev('img').hide(); // hide old image we are replacing
  $(this).find("input[type=submit]").
    button({disabled: true}). // do not let user press submit until image is uploaded
    after('<img src="/images/loading.gif" />') // indicate we are waiting
});

$(document).on("upload:progress", "form", function(e) {
  // http://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable
  function humanFileSize(bytes) {
    var thresh = 1024;
    if(bytes < thresh) return bytes + ' B';
    var units = ['kB','MB','GB','TB','PB','EB','ZB','YB'];
    var u = -1;
    do {
      bytes /= thresh;
      ++u;
    } while(bytes >= thresh);
    return bytes.toFixed(1)+' '+units[u];
  };

  var detail = e.originalEvent.detail;
  var percentage = Math.round((detail.loaded / detail.total) * 100);
  $(e.target).next().text(percentage + "% of " + humanFileSize(detail.total))
});

$(document).on("upload:complete", "form", function(e) {
  if(!$(this).find("input.uploading").length) {
    $(this).find("input[type=submit]").
      button({disabled: false}). // all images uploaded, user can submit the form
      next().remove(); // remove loading
  }
});

jQuery extension to ajax-ify selected links to stay inside a container

Make your links stay in a container, great for will_paginate / sorting headers and friends 🙂

Usage
// ajaxify all links that have “tab=my_container” in their href
$(“#my_container”).ajaxifyContainer(‘a[href*=”tab=my_container”]’)

Code

  // make selected links in a container replace the container
  // https://grosser.it/2012/06/21/jquery-pjaxcontainer-extensions
  $.fn.ajaxifyContainer = function(selector){
    selector = selector || 'a';
    var $container = $(this);
    $(selector, $container).live('click', function(){
      $container.html("Loading ...").load($(this).attr("href"));
      return false;
    });
  };

jQuery .load extension to indicate loading and errors

  // show loading animation and errors inside the container that is being replaced
  // https://grosser.it/2012/06/21/jquery-load-extension-to-indicate-loading-and-errors
  $.fn.responsiveLoad = function(url, callback){
    var loading = 'Loading';
    var $container = $(this);
    $container.html(loading).load(url, function(response, status, xhr){
      if (status == "error") {
        $container.html("Error:" + xhr.status + " " + xhr.statusText);
      } else {
        if(callback) callback(response, status, xhr);
      }
    });
  };

A chosen.js select filled via Ember.js

We have a lot of chosen in our ember app, and extracted this superclass to make interaction with them easier, it takes care of refreshing/filling/selecting stuff.

// fix chosen picks up ember script tags -> weird output when search for t or i
// this breaks the bindings within the options tags
// works around https://github.com/harvesthq/chosen/issues/581
App.UnboundSelectOption = Ember.SelectOption.extend({
  template: Ember.Handlebars.compile('{{unbound label}}')
});

App.ChosenSelect = Ember.Select.extend({
  chosenOptions: {},

  template: Ember.Handlebars.compile(
    '{{#if prompt}}{{unbound prompt}}{{/if}}' +
    '{{#each content}}{{view App.UnboundSelectOption contentBinding="this"}}{{/each}}'
  ),

  didInsertElement: function(){
    this._super();
    this.$().chosen(this.chosenOptions());
  },

  _closeChosen: function(){
    // trigger escape to close chosen
    this.$().next('.chzn-container-active').find('input').trigger({type:'keyup', which:27});
  },

  rerender: function(){
    if(this.get('state') == 'inDOM'){
     // remove now disconnected html
      this.$().next('.chzn-container').remove();
    }
    this._super();
  },

  rerenderChosen: function() {
    this.$().trigger('liszt:updated');
  }
});

Displaying collections and sorting them with ember-data

We had a lot of pain sorting our collections, since they are not there when the page is opened you need to continually re-sort…

App.Controller.Events = Ember.ArrayController.extend({
    init: function() {
      this._super();
      Helper.assignSortedCollection(this, 'content', Oceans.store.findAll(Oceans.Model.Event), function(item){
        return -1 * item.get('created_at').getTime();
      });
    }
  });

Helper = {
  assignSortedCollection: function(to, path, items, sorter){
    items.addObserver('length', function(){
      var temp = Ember.A();
      this.forEach(function(item) { temp.pushObject(item); });
      to.set(path, _(temp).sortBy(sorter));
    });
  }
}