(function($, window, document, undefined) {
  var renderTemplateData = function(data) {
    var view = {}

    for(var key in data) {
      if (!data.hasOwnProperty(key)) continue

      if ($.isArray(data[key])) {
        view[key] = $.map(data[key], function(value) {
          var new_value = $.extend(!!'deep_copy', {}, value)

          if (value['_template_key']) {
            new_value['to_s'] = new_value['to_html'] = renderTemplateData(value)
          }

          return new_value
        })

        view[key].toString = function() { return $.map(this, function(value) { return value.to_html }).join('') }
      } else if (data[key] && typeof data[key] === 'object' && data[key]['_template_key']) {
        view[key] = renderTemplateData(data[key])
      } else {
        view[key] = data[key]
      }
    }

    view['to_s'] = function() { toString() }

    return LP[data._template_key](view)
  }

  // pretty much ganked from pjax...
  var locationReplace = function(url) {
    window.history.replaceState(null, "", "#")
    window.location.replace(url)
  }

  var perspectivesVersion = function() {
    return $('meta').filter(function() {
      var name = $(this).attr('http-equiv')
      return name && name.toUpperCase() === 'X-PERSPECTIVES-VERSION'
    }).attr('content')
  }

  var renderPerspectivesResponse = function(options) {
    var $container = $(options.container)
    console.time('perspectives rendering')

    var version = perspectivesVersion() || ''
    if (version.length && version !== xhr.getResponseHeader('X-Perspectives-Version')) {
      locationReplace(options.href)
      return false
    }

    var $rendered = $(renderTemplateData(options.json))

    $container.html($rendered)

    $(document).trigger('perspectives:load', options.xhr)

    console.timeEnd('perspectives rendering')
  }

  var handlePerspectivesClick = function(container) {
    var $this = $(this)
    var href = this.href
    var replaceContainer = $($this.attr('data-perspectives-container')).length ? $this.attr('data-perspectives-container') : container

    $.getJSON(href, function(json, status, xhr) {
      $this.trigger('perspectives:response', [renderPerspectivesResponse, {
        json: json,
        status: status,
        xhr: xhr,
        href: href,
        container: replaceContainer
      }])

      window.history.pushState({container: container}, href, href)
    })

    return false
  }

  $(document).on('perspectives:response', function(e, defaultRender, options) { defaultRender(options) })

  $(window).on('popstate.perspectives', function(event) {
    var originalEvent = event.originalEvent
    if(originalEvent && originalEvent.state && originalEvent.state.container) {
      $.ajax({
        method: 'GET',
        url: window.location.href,
        dataType: 'json',
        headers: { 'x-perspectives-full-page': true }
      }).success(function(json, status, xhr) {
        renderPerspectivesResponse({
          json: json,
          status: status,
          xhr: xhr,
          href: window.location.href,
          container: originalEvent.state.container
        })
      })
    }
  })

  $.fn.perspectives = function(selector, container) {
    $(this).on('click', selector, function() {
      return handlePerspectivesClick.bind(this)(container)
    })
  }

  LP.renderTemplateData = LP.render = renderTemplateData
})(jQuery, window, document)

@@ -1824,24 +1824,224 @@
 ring')%0A  %7D%0A%0A
+  var perspectivesContainer = function($element, defaultContainer) %7B%0A    return $($element.attr('data-perspectives-target')).length ? $element.attr('data-perspectives-target') : defaultContainer%0A  %7D%0A%0A
   var handle
@@ -2162,27 +2162,8 @@
 r = 
-$($this.attr('data-
 pers
@@ -2170,26 +2170,25 @@
 pectives
--c
+C
 ontainer
 ')).leng
@@ -2183,74 +2183,26 @@
 iner
-')).length ? $this.attr('data-perspectives-container') : container
+($this, container)
 %0A%0A  
@@ -2658,16 +2658,651 @@
 ns) %7D)%0A%0A
+  $(document).on('ajax:success', function(event, data, status, xhr) %7B%0A    if (!xhr.getResponseHeader('Content-Type').match(/json/i)) return%0A%0A    var $form = $(event.target),%0A        $globalContainer = $('%5Bdata-global-perspectives-target%5D'),%0A        href = $form.attr('action'),%0A        container = perspectivesContainer($form, $globalContainer)%0A%0A    $form.trigger('perspectives:response', %5BrenderPerspectivesResponse, %7B%0A      json: data,%0A      status: status,%0A      xhr: xhr,%0A      href: href,%0A      container: container%0A    %7D%5D)%0A%0A    window.history.pushState(%7Bcontainer: $globalContainer.selector%7D, href, href)%0A%0A    return false%0A  %7D)%0A%0A
   $(wind
@@ -3951,24 +3951,88 @@
 ontainer) %7B%0A
+    $(container).attr('data-global-perspectives-target', true)%0A%0A
     $(this).

Handle ajax:success for form saves