diff --git a/CHANGELOG b/CHANGELOG
index a1ff098cbcbb303e74fa4721753808199c91ced8..fbc73589bd12896f1a63a5ef4acb178b59d00f56 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,7 @@ v 8.5.0 (unreleased)
   - Ensure rake tasks that don't need a DB connection can be run without one
   - Add "visibility" flag to GET /projects api endpoint
   - Ignore binary files in code search to prevent Error 500 (Stan Hu)
+  - Render sanitized SVG images (Stan Hu)
   - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
   - New UI for pagination
   - Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
@@ -14,7 +15,9 @@ v 8.5.0 (unreleased)
   - Display 404 error on group not found
   - Track project import failure
   - Fix visibility level text in admin area (Zeger-Jan van de Weg)
+  - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
   - Update the ExternalIssue regex pattern (Blake Hitchcock)
+  - Optimized performance of finding issues to be closed by a merge request
   - Revert "Add IP check against DNSBLs at account sign-up"
   - Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
   - Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
@@ -47,7 +50,6 @@ v 8.4.1
     and Nokogiri (1.6.7.2)
   - Fix redirect loop during import
   - Fix diff highlighting for all syntax themes
-  - Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
   - Delete project and associations in a background worker
 
 v 8.4.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e7659b06c7124f0e829e1eff96787be1d958e66f..7235ab4a83d69b7ae5a748a9d1845974c9f0de76 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -177,6 +177,26 @@ is probably 1, adding a new Git Hook maybe 4 or 5, big features 7-9.
 issues or chunks. You can simply not set the weight of a parent issue and set
 weights to children issues.
 
+### Regression issues
+
+Every monthly release has a corresponding issue on the CE issue tracker to keep
+track of functionality broken by that release and any fixes that need to be
+included in a patch release (see [8.3 Regressions] as an example).
+
+As outlined in the issue description, the intended workflow is to post one note
+with a reference to an issue describing the regression, and then to update that
+note with a reference to the merge request that fixes it as it becomes available.
+
+If you're a contributor who doesn't have the required permissions to update
+other users' notes, please post a new note with a reference to both the issue
+and the merge request.
+
+The release manager will [update the notes] in the regression issue as fixes are
+addressed.
+
+[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
+[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
+
 ## Merge requests
 
 We welcome merge requests with fixes and improvements to GitLab code, tests,
diff --git a/Gemfile b/Gemfile
index a09d44f8bfdc41d61501dbfcfe1f872832555213..c9d428a1798b868ba6a230be75067d086babc615 100644
--- a/Gemfile
+++ b/Gemfile
@@ -179,6 +179,9 @@ gem "underscore-rails", "~> 1.8.0"
 gem "sanitize", '~> 2.0'
 gem 'babosa', '~> 1.0.2'
 
+# Sanitizes SVG input
+gem "loofah", "~> 2.0.3"
+
 # Protect against bruteforcing
 gem "rack-attack", '~> 4.3.1'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index ec92964df25cf4e114f0476eb2dad28e712f68f5..b4f7587c4195568964c93a01bd394acdf3f0c1df 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -953,6 +953,7 @@ DEPENDENCIES
   jquery-ui-rails (~> 5.0.0)
   kaminari (~> 0.16.3)
   letter_opener (~> 1.1.2)
+  loofah (~> 2.0.3)
   mail_room (~> 0.6.1)
   method_source (~> 0.8)
   minitest (~> 5.7.0)
diff --git a/app/assets/javascripts/behaviors/autosize.js.coffee b/app/assets/javascripts/behaviors/autosize.js.coffee
index b32072e61ee6af8449be753b88f60e034a214bf1..a072fe48a9843480226d4cbef1fd748fe481c89d 100644
--- a/app/assets/javascripts/behaviors/autosize.js.coffee
+++ b/app/assets/javascripts/behaviors/autosize.js.coffee
@@ -1,4 +1,22 @@
+#= require jquery.ba-resize
 #= require autosize
 
 $ ->
-  autosize($('.js-autosize'))
+  $fields = $('.js-autosize')
+
+  $fields.on 'autosize:resized', ->
+    $field = $(@)
+    $field.data('height', $field.outerHeight())
+
+  $fields.on 'resize.autosize', ->
+    $field = $(@)
+
+    if $field.data('height') != $field.outerHeight()
+      $field.data('height', $field.outerHeight())
+      autosize.destroy($field)
+      $field.css('max-height', window.outerHeight)
+
+  autosize($fields)
+  autosize.update($fields)
+
+  $fields.css('resize', 'vertical')
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 6732343802a5b4f4693270e75068a4dc23a201c3..1d8611b04dcdc4d5b59d6d44784b5942e32a60cf 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -83,7 +83,7 @@
   background: #FFF;
   border: 1px solid #ddd;
   min-height: 140px;
-  max-height: 430px;
+  max-height: 500px;
   padding: 5px;
   box-shadow: none;
   width: 100%;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 32ba1676333c217146028b70d6d7f46952cea275..158c2a478625ad8c6370f564c8c2aff11620d4da 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -147,7 +147,7 @@
 .edit_note {
   .markdown-area {
     min-height: 140px;
-    max-height: 430px;
+    max-height: 500px;
   }
   .note-form-actions {
     background: transparent;
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 694c03206bd6c8e452b66fb9f20b73fa0b5cde95..169679279226266f7617eae4698034fa7b68d70b 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -126,4 +126,16 @@ module BlobHelper
       blob.size
     end
   end
+
+  def blob_svg?(blob)
+    blob.language && blob.language.name == 'SVG'
+  end
+
+  # SVGs can contain malicious JavaScript; only include whitelisted
+  # elements and attributes. Note that this whitelist is by no means complete
+  # and may omit some elements.
+  def sanitize_svg(blob)
+    blob.data = Loofah.scrub_fragment(blob.data, :strip).to_xml
+    blob
+  end
 end
diff --git a/app/mailers/email_rejection_mailer.rb b/app/mailers/email_rejection_mailer.rb
index 883f1c73ad43a5751d1688b2f03d17ca91519363..76db31a4c4546e1f75c8739afd49f91e4b2b92c0 100644
--- a/app/mailers/email_rejection_mailer.rb
+++ b/app/mailers/email_rejection_mailer.rb
@@ -10,7 +10,7 @@ class EmailRejectionMailer < BaseMailer
       subject: "[Rejected] #{@original_message.subject}"
     }
 
-    headers['Message-ID'] = SecureRandom.hex
+    headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>"
     headers['In-Reply-To'] = @original_message.message_id
     headers['References'] = @original_message.message_id
 
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 0af606455455891d2fced43c44278be0510df5bb..89b6c49b36238f92e9b7fabdf8e63034f57ecb38 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -346,10 +346,10 @@ class MergeRequest < ActiveRecord::Base
   # Return the set of issues that will be closed if this merge request is accepted.
   def closes_issues(current_user = self.author)
     if target_branch == project.default_branch
-      issues = commits.flat_map { |c| c.closes_issues(current_user) }
-      issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user).
-                  closed_by_message(description))
-      issues.uniq(&:id)
+      messages = commits.map(&:safe_message) << description
+
+      Gitlab::ClosingIssueExtractor.new(project, current_user).
+        closed_by_message(messages.join("\n"))
     else
       []
     end
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 3d8d88834e2a278288f515fd64fdf1e1fa02ad12..2c5b8dc4356abc8e57c9735abf8a2db638e2277f 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -35,7 +35,10 @@
     - if blob.lfs_pointer?
       = render "download", blob: blob
     - elsif blob.text?
-      = render "text", blob: blob
+      - if blob_svg?(blob)
+        = render "image", blob: sanitize_svg(blob)
+      - else
+        = render "text", blob: blob
     - elsif blob.image?
       = render "image", blob: blob
     - else
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 0945b93ed5d94331e8c3d464d10b0c29e676fee2..3e1deb8d30645d5ed3e48640023e3ed0b2f41020 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -49,12 +49,14 @@ if Gitlab::Metrics.enabled?
     config.instrument_instance_methods(Gitlab::Shell)
 
     config.instrument_methods(Gitlab::Git)
-    config.instrument_instance_methods(Gitlab::Git::Repository)
 
     Gitlab::Git.constants.each do |name|
       const = Gitlab::Git.const_get(name)
 
-      config.instrument_methods(const) if const.is_a?(Module)
+      next unless const.is_a?(Module)
+
+      config.instrument_methods(const)
+      config.instrument_instance_methods(const)
     end
 
     Dir[Rails.root.join('app', 'finders', '*.rb')].each do |path|
@@ -62,6 +64,16 @@ if Gitlab::Metrics.enabled?
 
       config.instrument_instance_methods(const)
     end
+
+    [
+      :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository,
+      :Tag, :TagCollection, :Tree
+    ].each do |name|
+      const = Rugged.const_get(name)
+
+      config.instrument_methods(const)
+      config.instrument_instance_methods(const)
+    end
   end
 
   GC::Profiler.enable
diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md
index 74a4c64d0003a39d0d64d0a4d036c31c36d9c8be..fe6b1c01352b35216e7d09f63321231e8a3cc5b7 100644
--- a/doc/ci/api/projects.md
+++ b/doc/ci/api/projects.md
@@ -18,7 +18,7 @@ GET /ci/projects
 Returns:
 
 ```json
-    [
+[
   {
     "id" : 271,
     "name" : "gitlabhq",
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index a8c276b949ea51f9b928c72acdf6d5b4fd414e0b..1e09dbc4c8ffa7ec48079653b3979c51c9b6c0e1 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -320,3 +320,13 @@ Feature: Project Source Browse Files
     Then I should see download link and object size
     And I should not see lfs pointer details
     And I should see buttons for allowed commands
+
+  @javascript
+  Scenario: I preview an SVG file
+    Given I click on "Upload file" link in repo
+    And I upload a new SVG file
+    And I fill the upload file commit message
+    And I fill the new branch name
+    And I click on "Upload file"
+    Given I visit the SVG file
+    Then I can see the new rendered SVG image
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index d08935aa1010a9f10be706ba03f8e87edba1d53d..13caddc44a49b9f0626ec73bd733246f5cc3bce5 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -351,6 +351,19 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     expect(page).to have_content "You're not allowed to make changes to this project directly. A fork of this project has been created that you can make changes in, so you can submit a merge request."
   end
 
+  # SVG files
+  step 'I upload a new SVG file' do
+    drop_in_dropzone test_svg_file
+  end
+
+  step 'I visit the SVG file' do
+    visit namespace_project_blob_path(@project.namespace, @project, 'new_branch_name/logo_sample.svg')
+  end
+
+  step 'I can see the new rendered SVG image' do
+    expect(find('.file-content')).to have_css('img')
+  end
+
   private
 
   def set_new_content
@@ -410,4 +423,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   def test_image_file
     File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
   end
+
+  def test_svg_file
+    File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg')
+  end
 end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 3331bd123b2d86c593b445ffd2185530243af521..ace906a6f59dec443b5bdb0a574fd7c587994fcd 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -44,19 +44,19 @@ module Gitlab
 
 
     def file_name_regex
-      @file_name_regex ||= /\A[a-zA-Z0-9_\-\.]*\z/.freeze
+      @file_name_regex ||= /\A[a-zA-Z0-9_\-\.\@]*\z/.freeze
     end
 
     def file_name_regex_message
-      "can contain only letters, digits, '_', '-' and '.'. "
+      "can contain only letters, digits, '_', '-', '@' and '.'. "
     end
 
     def file_path_regex
-      @file_path_regex ||= /\A[a-zA-Z0-9_\-\.\/]*\z/.freeze
+      @file_path_regex ||= /\A[a-zA-Z0-9_\-\.\/\@]*\z/.freeze
     end
 
     def file_path_regex_message
-      "can contain only letters, digits, '_', '-' and '.'. Separate directories with a '/'. "
+      "can contain only letters, digits, '_', '-', '@' and '.'. Separate directories with a '/'. "
     end
 
 
diff --git a/spec/fixtures/logo_sample.svg b/spec/fixtures/logo_sample.svg
new file mode 100644
index 0000000000000000000000000000000000000000..883e7e6cf92549cbf9ecc56c006cc41e16dbecc9
--- /dev/null
+++ b/spec/fixtures/logo_sample.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="210px" height="210px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <!-- Generator: Sketch 3.3.2 (12043) - http://www.bohemiancoding.com/sketch -->
+    <title>Slice 1</title>
+    <desc>Created with Sketch.</desc>
+    <script>alert('FAIL')</script>
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
+            <g id="Page-1" sketch:type="MSShapeGroup">
+                <g id="Fill-1-+-Group-24">
+                    <g id="Group-24">
+                        <g id="Group">
+                            <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path>
+                            <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path>
+                            <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>
diff --git a/spec/javascripts/behaviors/autosize_spec.js.coffee b/spec/javascripts/behaviors/autosize_spec.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..7fc1d19c35fa964d199ef1240e7d1a2d11456f9f
--- /dev/null
+++ b/spec/javascripts/behaviors/autosize_spec.js.coffee
@@ -0,0 +1,11 @@
+#= require behaviors/autosize
+
+describe 'Autosize behavior', ->
+  beforeEach ->
+    fixture.set('<textarea class="js-autosize" style="resize: vertical"></textarea>')
+
+  it 'does not overwrite the resize property', ->
+    load()
+    expect($('textarea')).toHaveCss(resize: 'vertical')
+
+  load = -> $(document).trigger('page:load')
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index d67ee423b9baf7ad0e350758a19c93b44c0119f8..c51b10bdc69b5870673b06cd998938dc6149ea6a 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -21,4 +21,12 @@ describe Gitlab::Regex, lib: true do
     it { expect('Dash – is this').to match(Gitlab::Regex.project_name_regex) }
     it { expect('?gitlab').not_to match(Gitlab::Regex.project_name_regex) }
   end
+
+  describe 'file name regex' do
+    it { expect('foo@bar').to match(Gitlab::Regex.file_name_regex) }
+  end
+
+  describe 'file path regex' do
+    it { expect('foo@/bar').to match(Gitlab::Regex.file_path_regex) }
+  end
 end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 291e6200a5bcd1ff6f6080cdaff97bcbac059b9b..46f2f20b98667c74c1d0ed554101330123cf7c49 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -137,9 +137,10 @@ describe MergeRequest, models: true do
   describe 'detection of issues to be closed' do
     let(:issue0) { create :issue, project: subject.project }
     let(:issue1) { create :issue, project: subject.project }
-    let(:commit0) { double('commit0', closes_issues: [issue0]) }
-    let(:commit1) { double('commit1', closes_issues: [issue0]) }
-    let(:commit2) { double('commit2', closes_issues: [issue1]) }
+
+    let(:commit0) { double('commit0', safe_message: "Fixes #{issue0.to_reference}") }
+    let(:commit1) { double('commit1', safe_message: "Fixes #{issue0.to_reference}") }
+    let(:commit2) { double('commit2', safe_message: "Fixes #{issue1.to_reference}") }
 
     before do
       allow(subject).to receive(:commits).and_return([commit0, commit1, commit2])
@@ -149,7 +150,9 @@ describe MergeRequest, models: true do
       allow(subject.project).to receive(:default_branch).
         and_return(subject.target_branch)
 
-      expect(subject.closes_issues).to eq([issue0, issue1].sort_by(&:id))
+      closed = subject.closes_issues
+
+      expect(closed).to include(issue0, issue1)
     end
 
     it 'only lists issues as to be closed if it targets the default branch' do
@@ -167,17 +170,6 @@ describe MergeRequest, models: true do
 
       expect(subject.closes_issues).to include(issue2)
     end
-
-    context 'for a project with JIRA integration' do
-      let(:issue0) { JiraIssue.new('JIRA-123', subject.project) }
-      let(:issue1) { JiraIssue.new('FOOBAR-4567', subject.project) }
-
-      it 'returns sorted JiraIssues' do
-        allow(subject.project).to receive_messages(default_branch: subject.target_branch)
-
-        expect(subject.closes_issues).to eq([issue0, issue1])
-      end
-    end
   end
 
   describe "#work_in_progress?" do
diff --git a/vendor/assets/javascripts/jquery.ba-resize.js b/vendor/assets/javascripts/jquery.ba-resize.js
new file mode 100644
index 0000000000000000000000000000000000000000..1f41d3791539f48bed836a5e1e8ffc158cf35d0a
--- /dev/null
+++ b/vendor/assets/javascripts/jquery.ba-resize.js
@@ -0,0 +1,246 @@
+/*!
+ * jQuery resize event - v1.1 - 3/14/2010
+ * http://benalman.com/projects/jquery-resize-plugin/
+ * 
+ * Copyright (c) 2010 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ */
+
+// Script: jQuery resize event
+//
+// *Version: 1.1, Last updated: 3/14/2010*
+// 
+// Project Home - http://benalman.com/projects/jquery-resize-plugin/
+// GitHub       - http://github.com/cowboy/jquery-resize/
+// Source       - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.js
+// (Minified)   - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.min.js (1.0kb)
+// 
+// About: License
+// 
+// Copyright (c) 2010 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+// 
+// About: Examples
+// 
+// This working example, complete with fully commented code, illustrates a few
+// ways in which this plugin can be used.
+// 
+// resize event - http://benalman.com/code/projects/jquery-resize/examples/resize/
+// 
+// About: Support and Testing
+// 
+// Information about what version or versions of jQuery this plugin has been
+// tested with, what browsers it has been tested in, and where the unit tests
+// reside (so you can test it yourself).
+// 
+// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
+// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1.
+// Unit Tests      - http://benalman.com/code/projects/jquery-resize/unit/
+// 
+// About: Release History
+// 
+// 1.1 - (3/14/2010) Fixed a minor bug that was causing the event to trigger
+//       immediately after bind in some circumstances. Also changed $.fn.data
+//       to $.data to improve performance.
+// 1.0 - (2/10/2010) Initial release
+
+(function($,window,undefined){
+  '$:nomunge'; // Used by YUI compressor.
+  
+  // A jQuery object containing all non-window elements to which the resize
+  // event is bound.
+  var elems = $([]),
+    
+    // Extend $.resize if it already exists, otherwise create it.
+    jq_resize = $.resize = $.extend( $.resize, {} ),
+    
+    timeout_id,
+    
+    // Reused strings.
+    str_setTimeout = 'setTimeout',
+    str_resize = 'resize',
+    str_data = str_resize + '-special-event',
+    str_delay = 'delay',
+    str_throttle = 'throttleWindow';
+  
+  // Property: jQuery.resize.delay
+  // 
+  // The numeric interval (in milliseconds) at which the resize event polling
+  // loop executes. Defaults to 250.
+  
+  jq_resize[ str_delay ] = 250;
+  
+  // Property: jQuery.resize.throttleWindow
+  // 
+  // Throttle the native window object resize event to fire no more than once
+  // every <jQuery.resize.delay> milliseconds. Defaults to true.
+  // 
+  // Because the window object has its own resize event, it doesn't need to be
+  // provided by this plugin, and its execution can be left entirely up to the
+  // browser. However, since certain browsers fire the resize event continuously
+  // while others do not, enabling this will throttle the window resize event,
+  // making event behavior consistent across all elements in all browsers.
+  // 
+  // While setting this property to false will disable window object resize
+  // event throttling, please note that this property must be changed before any
+  // window object resize event callbacks are bound.
+  
+  jq_resize[ str_throttle ] = true;
+  
+  // Event: resize event
+  // 
+  // Fired when an element's width or height changes. Because browsers only
+  // provide this event for the window element, for other elements a polling
+  // loop is initialized, running every <jQuery.resize.delay> milliseconds
+  // to see if elements' dimensions have changed. You may bind with either
+  // .resize( fn ) or .bind( "resize", fn ), and unbind with .unbind( "resize" ).
+  // 
+  // Usage:
+  // 
+  // > jQuery('selector').bind( 'resize', function(e) {
+  // >   // element's width or height has changed!
+  // >   ...
+  // > });
+  // 
+  // Additional Notes:
+  // 
+  // * The polling loop is not created until at least one callback is actually
+  //   bound to the 'resize' event, and this single polling loop is shared
+  //   across all elements.
+  // 
+  // Double firing issue in jQuery 1.3.2:
+  // 
+  // While this plugin works in jQuery 1.3.2, if an element's event callbacks
+  // are manually triggered via .trigger( 'resize' ) or .resize() those
+  // callbacks may double-fire, due to limitations in the jQuery 1.3.2 special
+  // events system. This is not an issue when using jQuery 1.4+.
+  // 
+  // > // While this works in jQuery 1.4+
+  // > $(elem).css({ width: new_w, height: new_h }).resize();
+  // > 
+  // > // In jQuery 1.3.2, you need to do this:
+  // > var elem = $(elem);
+  // > elem.css({ width: new_w, height: new_h });
+  // > elem.data( 'resize-special-event', { width: elem.width(), height: elem.height() } );
+  // > elem.resize();
+      
+  $.event.special[ str_resize ] = {
+    
+    // Called only when the first 'resize' event callback is bound per element.
+    setup: function() {
+      // Since window has its own native 'resize' event, return false so that
+      // jQuery will bind the event using DOM methods. Since only 'window'
+      // objects have a .setTimeout method, this should be a sufficient test.
+      // Unless, of course, we're throttling the 'resize' event for window.
+      if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
+      
+      var elem = $(this);
+      
+      // Add this element to the list of internal elements to monitor.
+      elems = elems.add( elem );
+      
+      // Initialize data store on the element.
+      $.data( this, str_data, { w: elem.width(), h: elem.height() } );
+      
+      // If this is the first element added, start the polling loop.
+      if ( elems.length === 1 ) {
+        loopy();
+      }
+    },
+    
+    // Called only when the last 'resize' event callback is unbound per element.
+    teardown: function() {
+      // Since window has its own native 'resize' event, return false so that
+      // jQuery will unbind the event using DOM methods. Since only 'window'
+      // objects have a .setTimeout method, this should be a sufficient test.
+      // Unless, of course, we're throttling the 'resize' event for window.
+      if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
+      
+      var elem = $(this);
+      
+      // Remove this element from the list of internal elements to monitor.
+      elems = elems.not( elem );
+      
+      // Remove any data stored on the element.
+      elem.removeData( str_data );
+      
+      // If this is the last element removed, stop the polling loop.
+      if ( !elems.length ) {
+        clearTimeout( timeout_id );
+      }
+    },
+    
+    // Called every time a 'resize' event callback is bound per element (new in
+    // jQuery 1.4).
+    add: function( handleObj ) {
+      // Since window has its own native 'resize' event, return false so that
+      // jQuery doesn't modify the event object. Unless, of course, we're
+      // throttling the 'resize' event for window.
+      if ( !jq_resize[ str_throttle ] && this[ str_setTimeout ] ) { return false; }
+      
+      var old_handler;
+      
+      // The new_handler function is executed every time the event is triggered.
+      // This is used to update the internal element data store with the width
+      // and height when the event is triggered manually, to avoid double-firing
+      // of the event callback. See the "Double firing issue in jQuery 1.3.2"
+      // comments above for more information.
+      
+      function new_handler( e, w, h ) {
+        var elem = $(this),
+          data = $.data( this, str_data );
+        
+        // If called from the polling loop, w and h will be passed in as
+        // arguments. If called manually, via .trigger( 'resize' ) or .resize(),
+        // those values will need to be computed.
+        data.w = w !== undefined ? w : elem.width();
+        data.h = h !== undefined ? h : elem.height();
+        
+        old_handler.apply( this, arguments );
+      };
+      
+      // This may seem a little complicated, but it normalizes the special event
+      // .add method between jQuery 1.4/1.4.1 and 1.4.2+
+      if ( $.isFunction( handleObj ) ) {
+        // 1.4, 1.4.1
+        old_handler = handleObj;
+        return new_handler;
+      } else {
+        // 1.4.2+
+        old_handler = handleObj.handler;
+        handleObj.handler = new_handler;
+      }
+    }
+    
+  };
+  
+  function loopy() {
+    
+    // Start the polling loop, asynchronously.
+    timeout_id = window[ str_setTimeout ](function(){
+      
+      // Iterate over all elements to which the 'resize' event is bound.
+      elems.each(function(){
+        var elem = $(this),
+          width = elem.width(),
+          height = elem.height(),
+          data = $.data( this, str_data );
+        
+        // If element size has changed since the last time, update the element
+        // data store and trigger the 'resize' event.
+        if ( width !== data.w || height !== data.h ) {
+          elem.trigger( str_resize, [ data.w = width, data.h = height ] );
+        }
+        
+      });
+      
+      // Loop.
+      loopy();
+      
+    }, jq_resize[ str_delay ] );
+    
+  };
+  
+})(jQuery,this);