diff --git a/app/lib/deltas.coffee b/app/lib/deltas.coffee
index 954af00ba..0782231eb 100644
--- a/app/lib/deltas.coffee
+++ b/app/lib/deltas.coffee
@@ -1,75 +1,158 @@
-# path: an array of indexes to navigate into a JSON object
-# left: 
+### 
+  Good-to-knows:
+    dataPath: an array of keys that walks you up a JSON object that's being patched
+      ex: ['scripts', 0, 'description']
+    deltaPath: an array of keys that walks you up a JSON Diff Patch object.
+      ex: ['scripts', '_0', 'description']
+###
+  
+module.exports.expandDelta = (delta, left, schema) ->
+  flattenedDeltas = flattenDelta(delta)
+  (expandFlattenedDelta(fd, left, schema) for fd in flattenedDeltas)
+  
 
-module.exports.interpretDelta = (delta, path, left, schema) ->
-  # takes a single delta and converts into an object that can be
+flattenDelta = (delta, dataPath=null, deltaPath=null) ->
+  # takes a single jsondiffpatch delta and returns an array of objects with
+  return [] unless delta
+  dataPath ?= []
+  deltaPath ?= []
+  return [{dataPath:dataPath, deltaPath: deltaPath, o:delta}] if _.isArray delta
+
+  results = []
+  affectingArray = delta._t is 'a'
+  for deltaIndex, childDelta of delta
+    continue if deltaIndex is '_t'
+    dataIndex = if affectingArray then parseInt(deltaIndex.replace('_', '')) else deltaIndex
+    results = results.concat flattenDelta(
+      childDelta, dataPath.concat([dataIndex]), deltaPath.concat([deltaIndex]))
+  results
+  
+
+expandFlattenedDelta = (delta, left, schema) ->
+  # takes a single flattened delta and converts into an object that can be
   # easily formatted into something human readable.
+  
+  delta.action = '???'
+  o = delta.o # the raw jsondiffpatch delta
 
-  betterDelta = { action:'???', delta: delta }
+  if _.isArray(o) and o.length is 1
+    delta.action = 'added'
+    delta.newValue = o[0]
 
-  if _.isArray(delta) and delta.length is 1
-    betterDelta.action = 'added'
-    betterDelta.newValue = delta[0]
+  if _.isArray(o) and o.length is 2
+    delta.action = 'modified'
+    delta.oldValue = o[0]
+    delta.newValue = o[1]
 
-  if _.isArray(delta) and delta.length is 2
-    betterDelta.action = 'modified'
-    betterDelta.oldValue = delta[0]
-    betterDelta.newValue = delta[1]
+  if _.isArray(o) and o.length is 3 and o[1] is 0 and o[2] is 0
+    delta.action = 'deleted'
+    delta.oldValue = o[0]
 
-  if _.isArray(delta) and delta.length is 3 and delta[1] is 0 and delta[2] is 0
-    betterDelta.action = 'deleted'
-    betterDelta.oldValue = delta[0]
+  if _.isPlainObject(o) and o._t is 'a'
+    delta.action = 'modified-array'
 
-  if _.isPlainObject(delta) and delta._t is 'a'
-    betterDelta.action = 'modified-array'
+  if _.isPlainObject(o) and o._t isnt 'a'
+    delta.action = 'modified-object'
 
-  if _.isPlainObject(delta) and delta._t isnt 'a'
-    betterDelta.action = 'modified-object'
+  if _.isArray(o) and o.length is 3 and o[1] is 0 and o[2] is 3
+    delta.action = 'moved-index'
+    delta.destinationIndex = o[1]
+    delta.originalIndex = delta.dataPath[delta.dataPath.length-1]
 
-  if _.isArray(delta) and delta.length is 3 and delta[1] is 0 and delta[2] is 3
-    betterDelta.action = 'moved-index'
-    betterDelta.destinationIndex = delta[1]
+  if _.isArray(o) and o.length is 3 and o[1] is 0 and o[2] is 2
+    delta.action = 'text-diff'
+    delta.unidiff = o[0]
 
-  if _.isArray(delta) and delta.length is 3 and delta[1] is 0 and delta[2] is 2
-    betterDelta.action = 'text-diff'
-    betterDelta.unidiff = delta[0]
-
-  betterPath = []
+  humanPath = []
   parentLeft = left
   parentSchema = schema
-  for key, i in path
-    # TODO: A smarter way of getting child schemas
+  for key, i in delta.dataPath
+    # TODO: A more comprehensive way of getting child schemas
     childSchema = parentSchema?.items or parentSchema?.properties?[key] or {}
     childLeft = parentLeft?[key]
-    betterKey = null
-    childData = if i is path.length-1 and betterDelta.action is 'added' then delta[0] else childLeft
-    betterKey ?= childData.name or childData.id if childData
-    betterKey ?= "#{childSchema.title} ##{key+1}" if childSchema.title and _.isNumber(key)
-    betterKey ?= "#{childSchema.title}" if childSchema.title
-    betterKey ?= _.string.titleize key
-    betterPath.push betterKey
+    humanKey = null
+    childData = if i is delta.dataPath.length-1 and delta.action is 'added' then o[0] else childLeft
+    humanKey ?= childData.name or childData.id if childData
+    humanKey ?= "#{childSchema.title} ##{key+1}" if childSchema.title and _.isNumber(key)
+    humanKey ?= "#{childSchema.title}" if childSchema.title
+    humanKey ?= _.string.titleize key
+    humanPath.push humanKey
     parentLeft = childLeft
     parentSchema = childSchema
     
-  betterDelta.path = betterPath.join(' :: ')
-  betterDelta.schema = childSchema
-  betterDelta.left = childLeft
-  betterDelta.right = jsondiffpatch.patch childLeft, delta unless betterDelta.action is 'moved-index'
+  delta.humanPath = humanPath.join(' :: ')
+  delta.schema = childSchema
+  delta.left = childLeft
+  delta.right = jsondiffpatch.patch childLeft, delta.o unless delta.action is 'moved-index'
   
-  betterDelta
+  delta
   
-module.exports.flattenDelta = flattenDelta = (delta, path=null) ->
-  # takes a single delta and returns an array of deltas
-  return [] unless delta
+module.exports.makeJSONDiffer = ->
+  hasher = (obj) -> obj.name || obj.id || obj._id || JSON.stringify(_.keys(obj))
+  jsondiffpatch.create({objectHash:hasher})
+    
+module.exports.getConflicts = (headDeltas, pendingDeltas) ->
+  # headDeltas and pendingDeltas should be lists of deltas returned by interpretDelta
+  # Returns a list of conflict objects with properties:
+  #   headDelta
+  #   pendingDelta
+  # The deltas that have conflicts also have conflict properties pointing to one another.
   
-  path ?= []
+  headPathMap = groupDeltasByAffectingPaths(headDeltas)
+  pendingPathMap = groupDeltasByAffectingPaths(pendingDeltas)
+  paths = _.keys(headPathMap).concat(_.keys(pendingPathMap))
   
-  return [{path:path, delta:delta}] if _.isArray delta
+  # Here's my thinking:
+  # A) Conflicts happen when one delta path is a substring of another delta path
+  # B) A delta from one self-consistent group cannot conflict with another
+  # So, sort the paths, which will naturally make conflicts adjacent,
+  # and if one is identified, one path is from the headDeltas, the other is from pendingDeltas
+  # This is all to avoid an O(nm) brute force search.
   
-  results = []
-  affectingArray = delta._t is 'a'
-  for index, childDelta of delta
-    continue if index is '_t'
-    index = parseInt(index.replace('_', '')) if affectingArray
-    results = results.concat flattenDelta(childDelta, path.concat([index]))
-  results 
\ No newline at end of file
+  conflicts = []
+  paths.sort()
+  for path, i in paths
+    continue if i + 1 is paths.length
+    nextPath = paths[i+1]
+    if nextPath.startsWith path
+      headDelta = (headPathMap[path] or headPathMap[nextPath])[0].delta
+      pendingDelta = (pendingPathMap[path] or pendingPathMap[nextPath])[0].delta
+      conflicts.push({headDelta:headDelta, pendingDelta:pendingDelta})
+      pendingDelta.conflict = headDelta
+      headDelta.conflict = pendingDelta
+
+  return conflicts if conflicts.length
+  
+groupDeltasByAffectingPaths = (deltas) ->
+  metaDeltas = []
+  for delta in deltas
+    conflictPaths = []
+    if delta.action is 'moved-index'
+      # every other action affects just the data path, but moved indexes affect a swath
+      indices = [delta.originalIndex, delta.destinationIndex]
+      indices.sort()
+      for index in _.range(indices[0], indices[1]+1)
+        conflictPaths.push delta.dataPath.slice(0, delta.dataPath.length-1).concat(index)
+    else
+      conflictPaths.push delta.dataPath
+    for path in conflictPaths
+      metaDeltas.push {
+        delta: delta
+        path: (item.toString() for item in path).join('/')
+      }
+  _.groupBy metaDeltas, 'path' 
+  
+module.exports.pruneConflictsFromDelta = (delta, conflicts) ->
+  # the jsondiffpatch delta mustn't include any dangling nodes,
+  # or else things will get removed which shouldn't be, or errors will occur
+  for conflict in conflicts
+    prunePath delta, conflict.pendingDelta.deltaPath
+  if _.isEmpty delta then undefined else delta
+    
+prunePath = (delta, path) ->
+  if path.length is 1
+    delete delta[path]
+  else
+    prunePath delta[path[0]], path.slice(1)
+    keys = (k for k in _.keys(delta[path[0]]) when k isnt '_t')
+    delete delta[path[0]] if keys.length is 0
\ No newline at end of file
diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee
index de1695490..59820f703 100644
--- a/app/models/CocoModel.coffee
+++ b/app/models/CocoModel.coffee
@@ -31,6 +31,12 @@ class CocoModel extends Backbone.Model
 
   type: ->
     @constructor.className
+    
+  clone: (withChanges=true) ->
+    # Backbone does not support nested documents
+    clone = super()
+    clone.set($.extend(true, {}, if withChanges then @attributes else @_revertAttributes))
+    clone
 
   onLoaded: ->
     @loaded = true
@@ -223,14 +229,16 @@ class CocoModel extends Backbone.Model
     return false
     
   getDelta: ->
-    jsd = jsondiffpatch.create({
-      objectHash: (obj) -> obj.name || obj.id || obj._id || JSON.stringify(_.keys(obj))
-    })
-    jsd.diff @_revertAttributes, @attributes
+    differ = deltasLib.makeJSONDiffer()
+    differ.diff @_revertAttributes, @attributes
+    
+  applyDelta: (delta) ->
+    newAttributes = $.extend(true, {}, @attributes)
+    jsondiffpatch.patch newAttributes, delta
+    @set newAttributes
     
   getExpandedDelta: ->
     delta = @getDelta()
-    deltas = deltasLib.flattenDelta(delta)
-    (deltasLib.interpretDelta(d.delta, d.path, @_revertAttributes, @schema().attributes) for d in deltas)
+    deltasLib.expandDelta(delta, @_revertAttributes, @schema().attributes)
 
 module.exports = CocoModel
diff --git a/app/styles/editor/delta.sass b/app/styles/editor/delta.sass
index f41da3667..013478efb 100644
--- a/app/styles/editor/delta.sass
+++ b/app/styles/editor/delta.sass
@@ -1,4 +1,4 @@
-.delta-list-view
+.delta-view
   .panel-heading
     font-size: 13px
     padding: 4px
@@ -7,37 +7,37 @@
   
   .delta-added
     border-color: green
-    strong
-      color: green
-    .panel-heading
+    > .panel-heading
       background-color: lighten(green, 70%)
+      strong
+        color: green
 
   .delta-modified
     border-color: darkgoldenrod
-    strong
-      color: darkgoldenrod
-    .panel-heading
+    > .panel-heading
       background-color: lighten(darkgoldenrod, 40%)
+      strong
+        color: darkgoldenrod
       
   .delta-text-diff
     border-color: blue
-    strong
-      color: blue
-    .panel-heading
+    > .panel-heading
       background-color: lighten(blue, 45%)
+      strong
+        color: blue
     table
       width: 100%
 
   .delta-deleted
     border-color: red
-    strong
-      color: red
-    .panel-heading
+    > .panel-heading
       background-color: lighten(red, 42%)
+      strong
+        color: red
 
   .delta-moved-index
     border-color: darkslategray
-    strong
-      color: darkslategray
-    .panel-heading
-      background-color: lighten(darkslategray, 60%)
\ No newline at end of file
+    > .panel-heading
+      background-color: lighten(darkslategray, 60%)
+      strong
+        color: darkslategray
diff --git a/app/styles/editor/patch.sass b/app/styles/editor/patch.sass
new file mode 100644
index 000000000..3296d946c
--- /dev/null
+++ b/app/styles/editor/patch.sass
@@ -0,0 +1,3 @@
+#patch-modal
+  .modal-body
+    padding: 10px
\ No newline at end of file
diff --git a/app/styles/modal/save_version.sass b/app/styles/modal/save_version.sass
index e7ab79751..66de28a29 100644
--- a/app/styles/modal/save_version.sass
+++ b/app/styles/modal/save_version.sass
@@ -33,7 +33,7 @@
     font-size: 0.9em
     font-style: italic
 
-  .delta-list-view
+  .delta-view
     overflow-y: auto
     padding: 10px
     border: 1px solid black
diff --git a/app/templates/editor/delta.jade b/app/templates/editor/delta.jade
index 961483324..480e4ef01 100644
--- a/app/templates/editor/delta.jade
+++ b/app/templates/editor/delta.jade
@@ -1,36 +1,46 @@
 - var i = 0
+
+mixin deltaPanel(delta, conflict)
+  - delta.index = i++
+  .delta.panel.panel-default(class='delta-'+delta.action, data-index=i)
+    .panel-heading
+      if delta.action === 'added'
+        strong(data-i18n="delta.added") Added
+      if delta.action === 'modified'
+        strong(data-i18n="delta.modified") Modified
+      if delta.action === 'deleted'
+        strong(data-i18n="delta.deleted") Deleted
+      if delta.action === 'moved-index'
+        strong(data-i18n="delta.modified_array") Moved Index
+      if delta.action === 'text-diff'
+        strong(data-i18n="delta.text_diff") Text Diff
+      span  
+      a(data-toggle="collapse" data-parent="#delta-accordion"+(counter) href="#collapse-"+(i+counter))
+        span= delta.humanPath
+
+    .panel-collapse.collapse(id="collapse-"+(i+counter))
+      .panel-body.row(class=conflict ? "conflict-details" : "details")
+        if delta.action === 'added'
+          .new-value.col-md-12= delta.right
+        if delta.action === 'modified'
+          .old-value.col-md-6= delta.left
+          .new-value.col-md-6= delta.right
+        if delta.action === 'deleted'
+          .col-md-12
+            div.old-value= delta.left
+        if delta.action === 'text-diff'
+          .col-md-12
+            div.text-diff
+        if delta.action === 'moved-index'
+          .col-md-12
+            span Moved array value #{JSON.stringify(delta.left)} to index #{delta.destinationIndex}
+  
+    if delta.conflict && !conflict
+      .panel-body
+        strong MERGE CONFLICT WITH
+        +deltaPanel(delta.conflict, true)
+
 .panel-group(id='delta-accordion-'+(counter))
   for delta in deltas
-    .delta.panel.panel-default(class='delta-'+delta.action)
-      .panel-heading
-        if delta.action === 'added'
-          strong(data-i18n="delta.added") Added
-        if delta.action === 'modified'
-          strong(data-i18n="delta.modified") Modified
-        if delta.action === 'deleted'
-          strong(data-i18n="delta.deleted") Deleted
-        if delta.action === 'moved-index'
-          strong(data-i18n="delta.modified_array") Moved Index
-        if delta.action === 'text-diff'
-          strong(data-i18n="delta.text_diff") Text Diff
-        span  
-        a(data-toggle="collapse" data-parent="#delta-accordion"+(counter) href="#collapse-"+(i+counter))
-          span= delta.path
-          
-      .panel-collapse.collapse(id="collapse-"+(i+counter))
-        .panel-body.row
-          if delta.action === 'added'
-            .new-value.col-md-12= delta.right
-          if delta.action === 'modified'
-            .old-value.col-md-6= delta.left
-            .new-value.col-md-6= delta.right
-          if delta.action === 'deleted'
-            .col-md-12
-              div.old-value= delta.left
-          if delta.action === 'text-diff'
-            .col-md-12
-              div.text-diff
-          if delta.action === 'moved-index'
-            .col-md-12
-              span Moved array value #{JSON.stringify(delta.left)} to index #{delta.destinationIndex}
-    - i += 1
\ No newline at end of file
+    +deltaPanel(delta)
+    
\ No newline at end of file
diff --git a/app/templates/editor/patch_modal.jade b/app/templates/editor/patch_modal.jade
new file mode 100644
index 000000000..4b094cd81
--- /dev/null
+++ b/app/templates/editor/patch_modal.jade
@@ -0,0 +1,20 @@
+extends /templates/modal/modal_base
+
+block modal-header-content
+  .modal-header-content
+    h3 Patch
+
+block modal-body-content
+  .modal-body
+    .changes-stub
+
+
+block modal-footer
+  .modal-footer
+    button(data-dismiss="modal", data-i18n="common.cancel").btn Cancel
+    if canReject
+      button.btn.btn-danger Reject
+    if canWithdraw
+      button.btn.btn-danger Withdraw
+    if canAccept
+      button.btn.btn-primary Accept
\ No newline at end of file
diff --git a/app/templates/editor/patches.jade b/app/templates/editor/patches.jade
index ce3b1af84..872788e7d 100644
--- a/app/templates/editor/patches.jade
+++ b/app/templates/editor/patches.jade
@@ -20,8 +20,11 @@ else
       th Submitter
       th Submitted
       th Commit Message
+      th Review
     for patch in patches
       tr
         td= patch.userName
         td= moment(patch.get('created')).format('llll')
         td= patch.get('commitMessage')
+        td
+          span.glyphicon.glyphicon-wrench(data-patch-id=patch.id).patch-icon
diff --git a/app/templates/editor/thang/edit.jade b/app/templates/editor/thang/edit.jade
index 04486aba1..b751d6de8 100644
--- a/app/templates/editor/thang/edit.jade
+++ b/app/templates/editor/thang/edit.jade
@@ -19,7 +19,7 @@ block content
   h3 Edit Thang Type: "#{thangType.attributes.name}"
 
   ul.nav.nav-tabs
-    li.active
+    li
       a(href="#editor-thang-main-tab-view", data-toggle="tab") Main
     li
       a(href="#editor-thang-components-tab-view", data-toggle="tab") Components
@@ -27,13 +27,13 @@ block content
       a(href="#editor-thang-spritesheets-view", data-toggle="tab") Spritesheets
     li
       a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors
-    li
+    li.active
       a(href="#editor-thang-patches-view", data-toggle="tab")#patches-tab Patches
 
   div.tab-content
     div.tab-pane#editor-thang-colors-tab-view
     
-    div.tab-pane.active#editor-thang-main-tab-view
+    div.tab-pane#editor-thang-main-tab-view
 
       div.main-area.well
         div.file-controls
@@ -86,7 +86,7 @@ block content
       
       div#spritesheets
   
-    div.tab-pane#editor-thang-patches-view
+    div.tab-pane#editor-thang-patches-view.active
   
       div.patches-view
 
diff --git a/app/views/editor/delta.coffee b/app/views/editor/delta.coffee
index 4d4635ebf..09c0981a6 100644
--- a/app/views/editor/delta.coffee
+++ b/app/views/editor/delta.coffee
@@ -1,57 +1,70 @@
 CocoView = require 'views/kinds/CocoView'
 template = require 'templates/editor/delta'
-deltaLib = require 'lib/deltas'
+deltasLib = require 'lib/deltas'
 
-module.exports = class DeltaListView extends CocoView
+TEXTDIFF_OPTIONS =
+  baseTextName: "Old"
+  newTextName: "New"
+  contextSize: 5
+  viewType: 1
+  
+module.exports = class DeltaView extends CocoView
   @deltaCounter: 0
-  className: "delta-list-view"
+  className: "delta-view"
   template: template
 
   constructor: (options) ->
     super(options)
     @model = options.model
+    @headModel = options.headModel
+    @expandedDeltas = @model.getExpandedDelta()
+    if @headModel
+      @headDeltas = @headModel.getExpandedDelta()
+      @conflicts = deltasLib.getConflicts(@headDeltas, @expandedDeltas)
+    DeltaView.deltaCounter += @expandedDeltas.length
 
   getRenderData: ->
     c = super()
-    c.deltas = @processedDeltas = @model.getExpandedDelta()
-    c.counter = DeltaListView.deltaCounter
-    DeltaListView.deltaCounter += c.deltas.length
+    c.deltas = @expandedDeltas
+    c.counter = DeltaView.deltaCounter
     c
     
   afterRender: ->
-    deltas = @$el.find('.delta')
+    deltas = @$el.find('.details')
     for delta, i in deltas
       deltaEl = $(delta)
-      deltaData = @processedDeltas[i]
-      if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
-        options =
-          data: deltaData.left
-          schema: deltaData.schema
-          readOnly: true
-        treema = TreemaNode.make(leftEl, options)
-        treema.build()
+      deltaData = @expandedDeltas[i]
+      @expandDetails(deltaEl, deltaData)
+      
+    conflictDeltas = @$el.find('.conflict-details')
+    conflicts = (delta.conflict for delta in @expandedDeltas when delta.conflict)
+    for delta, i in conflictDeltas
+      deltaEl = $(delta)
+      deltaData = conflicts[i]
+      @expandDetails(deltaEl, deltaData)
+      
+  expandDetails: (deltaEl, deltaData) ->
+    treemaOptions = { schema: deltaData.schema, readOnly: true }
+    
+    if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
+      options = _.defaults {data: deltaData.left}, treemaOptions
+      TreemaNode.make(leftEl, options).build()
+      
+    if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
+      options = _.defaults {data: deltaData.right}, treemaOptions
+      TreemaNode.make(rightEl, options).build()
+      
+    if deltaData.action is 'text-diff'
+      left = difflib.stringAsLines deltaData.left
+      right = difflib.stringAsLines deltaData.right
+      sm = new difflib.SequenceMatcher(left, right)
+      opcodes = sm.get_opcodes()
+      el = deltaEl.find('.text-diff')
+      options = {baseTextLines: left, newTextLines: right, opcodes: opcodes}
+      args = _.defaults options, TEXTDIFF_OPTIONS
+      el.append(diffview.buildView(args))
 
-      if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
-        options =
-          data: deltaData.right
-          schema: deltaData.schema
-          readOnly: true
-        treema = TreemaNode.make(rightEl, options)
-        treema.build()
-        
-      if deltaData.action is 'text-diff'
-        left = difflib.stringAsLines deltaData.left
-        right = difflib.stringAsLines deltaData.right
-        sm = new difflib.SequenceMatcher(left, right)
-        opcodes = sm.get_opcodes()
-        el = deltaEl.find('.text-diff')
-        args = {
-          baseTextLines: left
-          newTextLines: right
-          opcodes: opcodes
-          baseTextName: "Old"
-          newTextName: "New"
-          contextSize: 5
-          viewType: 1
-        }
-        el.append(diffview.buildView(args))
+  getApplicableDelta: ->
+    delta = @model.getDelta()
+    delta = deltasLib.pruneConflictsFromDelta delta, @conflicts if @conflicts 
+    delta
\ No newline at end of file
diff --git a/app/views/editor/patch_modal.coffee b/app/views/editor/patch_modal.coffee
new file mode 100644
index 000000000..2eecab361
--- /dev/null
+++ b/app/views/editor/patch_modal.coffee
@@ -0,0 +1,42 @@
+ModalView = require 'views/kinds/ModalView'
+template = require 'templates/editor/patch_modal'
+DeltaView = require 'views/editor/delta'
+
+module.exports = class PatchModal extends ModalView
+  id: "patch-modal"
+  template: template
+  plain: true
+
+  constructor: (@patch, @targetModel, options) ->
+    super(options)
+    targetID = @patch.get('target').id
+    if false
+      @originalSource = targetModel.clone(false)
+      @onOriginalLoaded()
+    else
+      @originalSource = new targetModel.constructor({_id:targetID})
+      @originalSource.fetch()
+      @listenToOnce @originalSource, 'sync', @onOriginalLoaded
+      @addResourceToLoad(@originalSource)
+      
+  getRenderData: ->
+    c = super()
+    c
+    
+  afterRender: ->
+    return if @originalSource.loading
+    headModel = @originalSource.clone(false)
+    headModel.set(@targetModel.attributes)
+    
+    pendingModel = @originalSource.clone(false)
+    pendingModel.applyDelta(@patch.get('delta'))
+
+    @deltaView = new DeltaView({model:pendingModel, headModel:headModel})
+    changeEl = @$el.find('.changes-stub')
+    @insertSubView(@deltaView, changeEl)
+    super()
+    
+  acceptPatch: ->
+    delta = @deltaView.getApplicableDelta()
+    pendingModel = @originalSource.clone(false)
+    pendingModel.applyDelta(delta)
\ No newline at end of file
diff --git a/app/views/editor/patches_view.coffee b/app/views/editor/patches_view.coffee
index abba96997..f8dd4fa15 100644
--- a/app/views/editor/patches_view.coffee
+++ b/app/views/editor/patches_view.coffee
@@ -2,6 +2,7 @@ CocoView = require 'views/kinds/CocoView'
 template = require 'templates/editor/patches'
 PatchesCollection = require 'collections/PatchesCollection'
 nameLoader = require 'lib/NameLoader'
+PatchModal = require './patch_modal'
 
 module.exports = class PatchesView extends CocoView
   template: template
@@ -10,6 +11,7 @@ module.exports = class PatchesView extends CocoView
   
   events:
     'change .status-buttons': 'onStatusButtonsChanged'
+    'click .patch-icon': 'openPatchModal'
 
   constructor: (@model, options) ->
     super(options)
@@ -47,3 +49,8 @@ module.exports = class PatchesView extends CocoView
     @initPatches()
     @load()
     @render()
+
+  openPatchModal: (e) ->
+    patch = _.find @patches.models, {id:$(e.target).data('patch-id')}
+    modal = new PatchModal(patch, @model)
+    @openModalView(modal)
\ No newline at end of file
diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee
index b73685eb1..c3fd12228 100644
--- a/app/views/kinds/CocoView.coffee
+++ b/app/views/kinds/CocoView.coffee
@@ -10,7 +10,7 @@ classCount = 0
 makeScopeName = -> "view-scope-#{classCount++}"
 doNothing = ->
 
-module.exports = class CocoView extends Backbone.View
+class CocoView extends Backbone.View
   startsLoading: false
   cache: false # signals to the router to keep this view around
   template: -> ''
@@ -348,6 +348,7 @@ module.exports = class CocoView extends Backbone.View
     slider
 
 
-
-mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
+  mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
 mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i
+
+module.exports = CocoView
diff --git a/server/patches/Patch.coffee b/server/patches/Patch.coffee
index a6c5da41f..df621f2a4 100644
--- a/server/patches/Patch.coffee
+++ b/server/patches/Patch.coffee
@@ -40,7 +40,6 @@ PatchSchema.pre 'save', (next) ->
     
     patches = document.get('patches') or []
     patches.push @_id
-    console.log 'PATCH PUSHED', @_id
     document.set 'patches', patches
     document.save (err) -> next(err)