best_in_place 2.1.0 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -5
  3. data/.rspec +1 -0
  4. data/.travis.yml +12 -5
  5. data/Appraisals +17 -0
  6. data/CHANGELOG.md +51 -30
  7. data/Gemfile +15 -2
  8. data/README.md +52 -105
  9. data/best_in_place.gemspec +13 -11
  10. data/config.ru +7 -0
  11. data/gemfiles/rails_3.2.gemfile +24 -0
  12. data/gemfiles/rails_4.0.gemfile +23 -0
  13. data/gemfiles/rails_4.1.gemfile +23 -0
  14. data/gemfiles/rails_edge.gemfile +25 -0
  15. data/lib/assets/javascripts/best_in_place.jquery-ui.js +57 -0
  16. data/lib/assets/javascripts/best_in_place.js +551 -650
  17. data/lib/assets/javascripts/best_in_place.purr.js +16 -6
  18. data/lib/best_in_place.rb +29 -9
  19. data/lib/best_in_place/controller_extensions.rb +10 -13
  20. data/lib/best_in_place/display_methods.rb +26 -21
  21. data/lib/best_in_place/engine.rb +2 -2
  22. data/lib/best_in_place/helper.rb +145 -87
  23. data/lib/best_in_place/railtie.rb +5 -2
  24. data/lib/best_in_place/test_helpers.rb +0 -1
  25. data/lib/best_in_place/utils.rb +20 -12
  26. data/lib/best_in_place/version.rb +1 -1
  27. data/spec/{helpers/best_in_place_spec.rb → helper_spec.rb} +134 -99
  28. data/spec/integration/double_init_spec.rb +3 -5
  29. data/spec/integration/js_spec.rb +193 -123
  30. data/spec/integration/live_spec.rb +3 -4
  31. data/spec/integration/text_area_spec.rb +4 -4
  32. data/spec/internal/app/assets/images/info.png +0 -0
  33. data/{test_app → spec/internal}/app/assets/images/no.png +0 -0
  34. data/spec/internal/app/assets/images/purrBottom.png +0 -0
  35. data/spec/internal/app/assets/images/purrClose.png +0 -0
  36. data/spec/internal/app/assets/images/purrTop.png +0 -0
  37. data/{test_app → spec/internal}/app/assets/images/red_pen.png +0 -0
  38. data/{test_app → spec/internal}/app/assets/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  39. data/{test_app → spec/internal}/app/assets/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  40. data/{test_app → spec/internal}/app/assets/images/ui-bg_flat_10_000000_40x100.png +0 -0
  41. data/{test_app → spec/internal}/app/assets/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/{test_app → spec/internal}/app/assets/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/{test_app → spec/internal}/app/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/{test_app → spec/internal}/app/assets/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/{test_app → spec/internal}/app/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/{test_app → spec/internal}/app/assets/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/{test_app → spec/internal}/app/assets/images/ui-icons_222222_256x240.png +0 -0
  48. data/{test_app → spec/internal}/app/assets/images/ui-icons_228ef1_256x240.png +0 -0
  49. data/{test_app → spec/internal}/app/assets/images/ui-icons_ef8c08_256x240.png +0 -0
  50. data/{test_app → spec/internal}/app/assets/images/ui-icons_ffd27a_256x240.png +0 -0
  51. data/{test_app → spec/internal}/app/assets/images/ui-icons_ffffff_256x240.png +0 -0
  52. data/{test_app → spec/internal}/app/assets/images/yes.png +0 -0
  53. data/spec/internal/app/assets/javascripts/application.js +37 -0
  54. data/{test_app → spec/internal}/app/assets/stylesheets/.gitkeep +0 -0
  55. data/{test_app → spec/internal}/app/assets/stylesheets/jquery-ui-1.8.16.custom.css.erb +1 -2
  56. data/{test_app → spec/internal}/app/assets/stylesheets/scaffold.css +1 -1
  57. data/{test_app → spec/internal}/app/assets/stylesheets/style.css.erb +2 -4
  58. data/{test_app → spec/internal}/app/controllers/admin/users_controller.rb +8 -3
  59. data/{test_app → spec/internal}/app/controllers/application_controller.rb +0 -0
  60. data/{test_app → spec/internal}/app/controllers/cuca/cars_controller.rb +0 -0
  61. data/{test_app → spec/internal}/app/controllers/users_controller.rb +8 -40
  62. data/{test_app → spec/internal}/app/helpers/application_helper.rb +0 -0
  63. data/spec/internal/app/helpers/users_helper.rb +29 -0
  64. data/{test_app → spec/internal}/app/models/cuca/car.rb +0 -0
  65. data/{test_app → spec/internal}/app/models/user.rb +5 -1
  66. data/{test_app → spec/internal}/app/views/admin/users/show.html.erb +2 -2
  67. data/{test_app → spec/internal}/app/views/cuca/cars/show.html.erb +0 -0
  68. data/{test_app → spec/internal}/app/views/layouts/application.html.erb +1 -1
  69. data/{test_app → spec/internal}/app/views/users/_form.html.erb +2 -2
  70. data/{test_app → spec/internal}/app/views/users/double_init.html.erb +4 -10
  71. data/spec/internal/app/views/users/edit.html.erb +5 -0
  72. data/{test_app → spec/internal}/app/views/users/email_field.html.erb +0 -0
  73. data/{test_app → spec/internal}/app/views/users/index.html.erb +0 -0
  74. data/{test_app → spec/internal}/app/views/users/new.html.erb +0 -0
  75. data/{test_app → spec/internal}/app/views/users/show.html.erb +32 -24
  76. data/{test_app → spec/internal}/app/views/users/show_ajax.html.erb +0 -0
  77. data/spec/internal/config/database.yml +5 -0
  78. data/{test_app → spec/internal}/config/initializers/countries.rb +0 -0
  79. data/{test_app → spec/internal}/config/initializers/default_date_format.rb +0 -0
  80. data/spec/internal/config/initializers/development.rb +8 -0
  81. data/{test_app → spec/internal}/config/routes.rb +1 -2
  82. data/spec/internal/db/schema.rb +26 -0
  83. data/{test_app → spec/internal}/public/favicon.ico +0 -0
  84. data/spec/rails_helper.rb +21 -0
  85. data/spec/support/retry_on_timeout.rb +4 -7
  86. data/spec/utils_spec.rb +21 -0
  87. data/vendor/assets/javascripts/jquery.autosize.js +272 -0
  88. data/{lib → vendor}/assets/javascripts/jquery.purr.js +1 -1
  89. metadata +92 -175
  90. data/lib/best_in_place/check_version.rb +0 -8
  91. data/spec/spec_helper.rb +0 -23
  92. data/test_app/Gemfile +0 -16
  93. data/test_app/README +0 -256
  94. data/test_app/Rakefile +0 -7
  95. data/test_app/app/assets/javascripts/application.js +0 -35
  96. data/test_app/app/helpers/users_helper.rb +0 -29
  97. data/test_app/config.ru +0 -4
  98. data/test_app/config/application.rb +0 -51
  99. data/test_app/config/boot.rb +0 -13
  100. data/test_app/config/database.yml +0 -22
  101. data/test_app/config/environment.rb +0 -5
  102. data/test_app/config/environments/development.rb +0 -25
  103. data/test_app/config/environments/production.rb +0 -49
  104. data/test_app/config/environments/test.rb +0 -35
  105. data/test_app/config/initializers/backtrace_silencers.rb +0 -7
  106. data/test_app/config/initializers/inflections.rb +0 -10
  107. data/test_app/config/initializers/mime_types.rb +0 -5
  108. data/test_app/config/initializers/secret_token.rb +0 -7
  109. data/test_app/config/initializers/session_store.rb +0 -8
  110. data/test_app/config/locales/en.yml +0 -5
  111. data/test_app/db/migrate/20101206205922_create_users.rb +0 -18
  112. data/test_app/db/migrate/20101212170114_add_receive_email_to_user.rb +0 -9
  113. data/test_app/db/migrate/20110115204441_add_description_to_user.rb +0 -9
  114. data/test_app/db/migrate/20111210084202_add_favorite_color_to_users.rb +0 -5
  115. data/test_app/db/migrate/20111210084251_add_favorite_books_to_users.rb +0 -5
  116. data/test_app/db/migrate/20111217215935_add_birth_date_to_users.rb +0 -5
  117. data/test_app/db/migrate/20111224181356_add_money_to_user.rb +0 -5
  118. data/test_app/db/migrate/20120513003308_create_cars.rb +0 -11
  119. data/test_app/db/migrate/20120607172609_add_favorite_movie_to_users.rb +0 -5
  120. data/test_app/db/migrate/20120616170454_add_money_proc_to_users.rb +0 -6
  121. data/test_app/db/migrate/20120620165212_add_height_to_user.rb +0 -5
  122. data/test_app/db/migrate/20130213224102_add_favorite_locale_to_users.rb +0 -5
  123. data/test_app/db/schema.rb +0 -41
  124. data/test_app/db/seeds.rb +0 -19
  125. data/test_app/doc/README_FOR_APP +0 -2
  126. data/test_app/lib/tasks/.gitkeep +0 -0
  127. data/test_app/lib/tasks/cron.rake +0 -7
  128. data/test_app/public/404.html +0 -26
  129. data/test_app/public/422.html +0 -26
  130. data/test_app/public/500.html +0 -26
  131. data/test_app/public/robots.txt +0 -5
  132. data/test_app/script/rails +0 -6
  133. data/test_app/test/fixtures/users.yml +0 -17
  134. data/test_app/test/functional/users_controller_test.rb +0 -49
  135. data/test_app/test/performance/browsing_test.rb +0 -9
  136. data/test_app/test/test_helper.rb +0 -13
  137. data/test_app/test/unit/helpers/users_helper_test.rb +0 -4
  138. data/test_app/test/unit/user_test.rb +0 -8
  139. data/test_app/vendor/plugins/.gitkeep +0 -0
@@ -0,0 +1,5 @@
1
+ <h1>Edit user</h1>
2
+
3
+ <%= render 'form' %>
4
+
5
+ <%= link_to 'Back', users_path %>
@@ -7,19 +7,19 @@
7
7
  <tr>
8
8
  <td>Name <a id="editme" href="#">(edit me)</a></td>
9
9
  <td id="name">
10
- <%= best_in_place @user, :name, :type => :input, :activator => "#editme" %>
10
+ <%= best_in_place @user, :name, as: :input, :activator => "#editme" %>
11
11
  </td>
12
12
  </tr>
13
13
  <tr>
14
14
  <td>Last Name</td>
15
15
  <td id="last_name">
16
- <%= best_in_place @user, :last_name, :nil => "Nothing to show" %>
16
+ <%= best_in_place @user, :last_name, place_holder: 'Nothing to show' %>
17
17
  </td>
18
18
  </tr>
19
19
  <tr>
20
20
  <td>Height</td>
21
21
  <td id="height">
22
- <%= best_in_place @user, :height, :type => :select, :collection => height_collection.zip(height_collection), :sanitize => false %>
22
+ <%= best_in_place @user, :height, as: :select, :collection => height_collection, :raw => true %>
23
23
  </td>
24
24
  </tr>
25
25
  <tr>
@@ -31,7 +31,7 @@
31
31
  <tr>
32
32
  <td>Birth date</td>
33
33
  <td id="birth_date">
34
- <%= best_in_place @user, :birth_date, :type => :date %>
34
+ <%= best_in_place @user, :birth_date, as: :date %>
35
35
  </td>
36
36
  </tr>
37
37
  <tr>
@@ -43,46 +43,46 @@
43
43
  <tr>
44
44
  <td>ZIP</td>
45
45
  <td id="zip">
46
- <%= best_in_place @user, :zip %>
46
+ <%= best_in_place @user, :zip, :display_as => :zip_format %>
47
47
  </td>
48
48
  </tr>
49
49
  <tr>
50
50
  <td>Country</td>
51
51
  <td id="country">
52
- <%= best_in_place @user, :country, :type => :select, :collection => @countries, :inner_class => :some_class %>
52
+ <%= best_in_place @user, :country, as: :select, :collection => @countries, :inner_class => :some_class %>
53
53
  </td>
54
54
  </tr>
55
55
  <tr>
56
56
  <td>Receive newsletter?</td>
57
57
  <td id="receive_email">
58
- <%= best_in_place @user, :receive_email, :type => :checkbox, :collection => ["No thanks", "Yes of course"] %>
58
+ <%= best_in_place @user, :receive_email, as: :checkbox, collection: {'false' => "No thanks", 'true' => "Yes of course"} %>
59
59
  </td>
60
60
  </tr>
61
61
  <tr>
62
62
  <td>Receive newsletter (image)?</td>
63
63
  <td id="receive_email_image">
64
- <%= best_in_place @user, :receive_email, :type => :checkbox, :collection => [image_tag('no.png'), image_tag('yes.png')] %>
64
+ <%= best_in_place @user, :receive_email, as: :checkbox, :collection => {'false' => image_tag('no.png'), 'true' => image_tag('yes.png')} %>
65
65
  </td>
66
66
  </tr>
67
67
  <tr>
68
68
  <td>Favorite color</td>
69
69
  <td id="favorite_color">
70
- <%- opts = { :ok_button => 'Do it!', :cancel_button => 'Nope', :ok_button_class => 'custom-submit other-custom-submit', :cancel_button_class => 'custom-cancel other-custom-cancel' } %>
70
+ <%- opts = {:ok_button => 'Do it!', :cancel_button => 'Nope', :ok_button_class => 'custom-submit other-custom-submit', :cancel_button_class => 'custom-cancel other-custom-cancel'} %>
71
71
  <%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
72
- <%- opts[:nil] = "<span class='nil'>Click to add your favorite color</span>" %>
72
+ <%- opts[:place_holder] = "<span class='placeholder'>Click to add your favorite color</span>" %>
73
73
  <%= best_in_place @user, :favorite_color, opts %>
74
74
  </td>
75
75
  </tr>
76
76
  <tr>
77
77
  <td>Favorite locale</td>
78
78
  <td id="favorite_locale">
79
- <%= best_in_place @user, :favorite_locale, :nil => "N/A" %>
79
+ <%= best_in_place @user, :favorite_locale, place_holder: 'N/A' %>
80
80
  </td>
81
81
  </tr>
82
82
  <tr>
83
83
  <td>Favorite books</td>
84
84
  <td id="favorite_books">
85
- <%- opts = { :type => :textarea, :ok_button => 'Save', :cancel_button => 'Cancel' } %>
85
+ <%- opts = {as: :textarea, :ok_button => 'Save', :cancel_button => 'Cancel'} %>
86
86
  <%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
87
87
  <%= best_in_place @user, :favorite_books, opts %>
88
88
  </td>
@@ -90,13 +90,13 @@
90
90
  <tr>
91
91
  <td>User description</td>
92
92
  <td id="description">
93
- <%= best_in_place @user, :description, :display_as => :markdown_desc, :type => :textarea, :sanitize => false %>
93
+ <%= best_in_place @user, :description, :display_as => :markdown_desc, as: :textarea, :raw => true %>
94
94
  </td>
95
95
  </tr>
96
96
  <tr>
97
97
  <td>Simple-formatted user description</td>
98
98
  <td id="dw_description">
99
- <%= best_in_place @user, :description, :display_with => :simple_format, :type => :textarea %>
99
+ <%= best_in_place @user, :description, :display_with => :simple_format, as: :textarea %>
100
100
  </td>
101
101
  </tr>
102
102
  <tr>
@@ -108,7 +108,7 @@
108
108
  <tr>
109
109
  <td>Money with proc</td>
110
110
  <td id="money_proc">
111
- <%= best_in_place @user, :money_proc, :display_with => lambda{ |v| v.blank? ? "No money" : number_to_currency(v) } %>
111
+ <%= best_in_place @user, :money_proc, :display_with => lambda { |v| v.blank? ? "No money" : number_to_currency(v) } %>
112
112
  </td>
113
113
  </tr>
114
114
  <tr>
@@ -117,25 +117,33 @@
117
117
  <%= best_in_place @user, :money_custom, :display_with => lambda { |x| bb(x) } %>
118
118
  </td>
119
119
  </tr>
120
+ <tr>
121
+ <td>Money with value</td>
122
+ <td id="money_value">
123
+ <%= best_in_place @user, :money, :display_with => :number_to_currency, value: 'Custom Value' %>
124
+ </td>
125
+ </tr>
120
126
  <tr>
121
127
  <td>Favorite Movie</td>
122
128
  <td id="favorite_movie">
123
- <%- opts = { :ok_button => 'Do it!', :cancel_button => 'Nope', :use_confirm => false } %>
129
+ <%- opts = {:ok_button => 'Do it!', :cancel_button => 'Nope', :use_confirm => false} %>
124
130
  <%- opts.delete(:ok_button) if params[:suppress_ok_button] %>
125
131
  <%= best_in_place @user, :favorite_movie, opts %>
126
132
  </td>
127
133
  </tr>
128
134
  </table>
129
- <br />
130
- <hr />
135
+ <br/>
136
+ <hr/>
131
137
  <p>Try the <b>features of Best In Place</b>:</p>
132
138
  <ul>
133
- <li>Try giving wrong email values or too short address, inputs to see server errors.</li>
134
- <li>Click on newsletter to change a boolean value</li>
135
- <li>Click on country to change the value in a collection of values</li>
136
- <li>Use the external handler to change the value of the name</li>
137
- <li>Try making changes inside inputs or textareas and then press the ESC key to recover the old value</li>
139
+ <li>Try giving wrong email values or too short address, inputs to see server errors.</li>
140
+ <li>Click on newsletter to change a boolean value</li>
141
+ <li>Click on country to change the value in a collection of values</li>
142
+ <li>Use the external handler to change the value of the name</li>
143
+ <li>Try making changes inside inputs or textareas and then press the ESC key to recover the old value</li>
138
144
  </ul>
139
- <p>More information on <a href="http://github.com/bernat/best_in_place">github</a> or <a href="http://blog.bernatfarrero.com/in-place-editing-with-javascript-jquery-and-rails-3/">bernatfarrero.com</a>.</p>
145
+ <p>More information on <a href="http://github.com/bernat/best_in_place">github</a> or
146
+ <a href="http://blog.bernatfarrero.com/in-place-editing-with-javascript-jquery-and-rails-3/">bernatfarrero.com</a>.
147
+ </p>
140
148
 
141
149
  </div>
@@ -0,0 +1,5 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/test.sqlite3
4
+ pool: 5
5
+ timeout: 5000
@@ -0,0 +1,8 @@
1
+ if ENV['dev']
2
+ Rails.application.configure do
3
+ config.cache_classes = false
4
+ config.eager_load = false
5
+ config.assets.debug = true
6
+ end
7
+ end
8
+ I18n.enforce_available_locales = true
@@ -1,7 +1,6 @@
1
- BipApp::Application.routes.draw do
1
+ Rails.application.routes.draw do
2
2
  resources :users do
3
3
  member do
4
- put :test_respond_with
5
4
  get :double_init
6
5
  get :show_ajax
7
6
  get :email_field
@@ -0,0 +1,26 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table "cars", :force => true do |t|
3
+ t.string "model"
4
+ end
5
+
6
+ create_table "users", :force => true do |t|
7
+ t.string "name"
8
+ t.string "last_name"
9
+ t.string "address"
10
+ t.string "email", :null => false
11
+ t.string "zip"
12
+ t.string "country"
13
+ t.datetime "created_at", :null => false
14
+ t.datetime "updated_at", :null => false
15
+ t.boolean "receive_email"
16
+ t.text "description"
17
+ t.string "favorite_color"
18
+ t.text "favorite_books"
19
+ t.datetime "birth_date"
20
+ t.float "money"
21
+ t.float "money_proc"
22
+ t.string "height"
23
+ t.string "favorite_movie"
24
+ t.string "favorite_locale"
25
+ end
26
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
3
+ require 'combustion'
4
+ require 'capybara/rspec'
5
+ require 'best_in_place'
6
+
7
+ Combustion.initialize! :active_record, :action_controller,
8
+ :action_view, :sprockets
9
+
10
+ require 'rspec/rails'
11
+ require 'capybara/rails'
12
+
13
+ require 'best_in_place/test_helpers'
14
+ require_relative 'support/retry_on_timeout'
15
+
16
+
17
+ RSpec.configure do |config|
18
+ config.include BestInPlace::TestHelpers
19
+ config.use_transactional_fixtures = false
20
+ config.raise_errors_for_deprecations!
21
+ end
@@ -1,10 +1,7 @@
1
1
  def retry_on_timeout(n = 3, &block)
2
2
  block.call
3
- rescue Capybara::TimeoutError, Capybara::ElementNotFound => e
4
- if n > 0
5
- puts "Catched error: #{e.message}. #{n-1} more attempts."
6
- retry_on_timeout(n - 1, &block)
7
- else
8
- raise
9
- end
3
+ rescue => e
4
+ fail if n.zero?
5
+ puts "Catched error: #{e.message}. #{n-1} more attempts."
6
+ retry_on_timeout(n - 1, &block)
10
7
  end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ describe BestInPlace::Utils do
4
+ include BestInPlace::Utils
5
+ describe '#build_best_in_place_id' do
6
+ it 'build id from symbol' do
7
+ expect(build_best_in_place_id(:user, :login)).to eq('best_in_place_user_login')
8
+ end
9
+
10
+ it 'build id from record' do
11
+ car = Cuca::Car.create
12
+ expect(build_best_in_place_id(car, :model)).to eq("best_in_place_cuca_car_#{car.id}_model")
13
+ end
14
+
15
+
16
+ end
17
+
18
+ describe '#object_to_key' do
19
+
20
+ end
21
+ end
@@ -0,0 +1,272 @@
1
+ /*!
2
+ Autosize v1.18.9 - 2014-05-27
3
+ Automatically adjust textarea height based on user input.
4
+ (c) 2014 Jack Moore - http://www.jacklmoore.com/autosize
5
+ license: http://www.opensource.org/licenses/mit-license.php
6
+ */
7
+ (function ($) {
8
+ 'use strict';
9
+ var defaults = {
10
+ className: 'autosizejs',
11
+ id: 'autosizejs',
12
+ append: '\n',
13
+ callback: false,
14
+ resizeDelay: 10,
15
+ placeholder: true
16
+ },
17
+
18
+ // border:0 is unnecessary, but avoids a bug in Firefox on OSX
19
+ copy = '<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',
20
+
21
+ // line-height is conditionally included because IE7/IE8/old Opera do not return the correct value.
22
+ typographyStyles = [
23
+ 'fontFamily',
24
+ 'fontSize',
25
+ 'fontWeight',
26
+ 'fontStyle',
27
+ 'letterSpacing',
28
+ 'textTransform',
29
+ 'wordSpacing',
30
+ 'textIndent'
31
+ ],
32
+
33
+ // to keep track which textarea is being mirrored when adjust() is called.
34
+ mirrored,
35
+
36
+ // the mirror element, which is used to calculate what size the mirrored element should be.
37
+ mirror = $(copy).data('autosize', true)[0];
38
+
39
+ // test that line-height can be accurately copied.
40
+ mirror.style.lineHeight = '99px';
41
+ if ($(mirror).css('lineHeight') === '99px') {
42
+ typographyStyles.push('lineHeight');
43
+ }
44
+ mirror.style.lineHeight = '';
45
+
46
+ $.fn.autosize = function (options) {
47
+ if (!this.length) {
48
+ return this;
49
+ }
50
+
51
+ options = $.extend({}, defaults, options || {});
52
+
53
+ if (mirror.parentNode !== document.body) {
54
+ $(document.body).append(mirror);
55
+ }
56
+
57
+ return this.each(function () {
58
+ var
59
+ ta = this,
60
+ $ta = $(ta),
61
+ maxHeight,
62
+ minHeight,
63
+ boxOffset = 0,
64
+ callback = $.isFunction(options.callback),
65
+ originalStyles = {
66
+ height: ta.style.height,
67
+ overflow: ta.style.overflow,
68
+ overflowY: ta.style.overflowY,
69
+ wordWrap: ta.style.wordWrap,
70
+ resize: ta.style.resize
71
+ },
72
+ timeout,
73
+ width = $ta.width(),
74
+ taResize = $ta.css('resize');
75
+
76
+ if ($ta.data('autosize')) {
77
+ // exit if autosize has already been applied, or if the textarea is the mirror element.
78
+ return;
79
+ }
80
+ $ta.data('autosize', true);
81
+
82
+ if ($ta.css('box-sizing') === 'border-box' || $ta.css('-moz-box-sizing') === 'border-box' || $ta.css('-webkit-box-sizing') === 'border-box'){
83
+ boxOffset = $ta.outerHeight() - $ta.height();
84
+ }
85
+
86
+ // IE8 and lower return 'auto', which parses to NaN, if no min-height is set.
87
+ minHeight = Math.max(parseInt($ta.css('minHeight'), 10) - boxOffset || 0, $ta.height());
88
+
89
+ $ta.css({
90
+ overflow: 'hidden',
91
+ overflowY: 'hidden',
92
+ wordWrap: 'break-word' // horizontal overflow is hidden, so break-word is necessary for handling words longer than the textarea width
93
+ });
94
+
95
+ if (taResize === 'vertical') {
96
+ $ta.css('resize','none');
97
+ } else if (taResize === 'both') {
98
+ $ta.css('resize', 'horizontal');
99
+ }
100
+
101
+ // The mirror width must exactly match the textarea width, so using getBoundingClientRect because it doesn't round the sub-pixel value.
102
+ // window.getComputedStyle, getBoundingClientRect returning a width are unsupported, but also unneeded in IE8 and lower.
103
+ function setWidth() {
104
+ var width;
105
+ var style = window.getComputedStyle ? window.getComputedStyle(ta, null) : false;
106
+
107
+ if (style) {
108
+
109
+ width = ta.getBoundingClientRect().width;
110
+
111
+ if (width === 0 || typeof width !== 'number') {
112
+ width = parseInt(style.width,10);
113
+ }
114
+
115
+ $.each(['paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'], function(i,val){
116
+ width -= parseInt(style[val],10);
117
+ });
118
+ } else {
119
+ width = $ta.width();
120
+ }
121
+
122
+ mirror.style.width = Math.max(width,0) + 'px';
123
+ }
124
+
125
+ function initMirror() {
126
+ var styles = {};
127
+
128
+ mirrored = ta;
129
+ mirror.className = options.className;
130
+ mirror.id = options.id;
131
+ maxHeight = parseInt($ta.css('maxHeight'), 10);
132
+
133
+ // mirror is a duplicate textarea located off-screen that
134
+ // is automatically updated to contain the same text as the
135
+ // original textarea. mirror always has a height of 0.
136
+ // This gives a cross-browser supported way getting the actual
137
+ // height of the text, through the scrollTop property.
138
+ $.each(typographyStyles, function(i,val){
139
+ styles[val] = $ta.css(val);
140
+ });
141
+
142
+ $(mirror).css(styles).attr('wrap', $ta.attr('wrap'));
143
+
144
+ setWidth();
145
+
146
+ // Chrome-specific fix:
147
+ // When the textarea y-overflow is hidden, Chrome doesn't reflow the text to account for the space
148
+ // made available by removing the scrollbar. This workaround triggers the reflow for Chrome.
149
+ if (window.chrome) {
150
+ var width = ta.style.width;
151
+ ta.style.width = '0px';
152
+ var ignore = ta.offsetWidth;
153
+ ta.style.width = width;
154
+ }
155
+ }
156
+
157
+ // Using mainly bare JS in this function because it is going
158
+ // to fire very often while typing, and needs to very efficient.
159
+ function adjust() {
160
+ var height, original;
161
+
162
+ if (mirrored !== ta) {
163
+ initMirror();
164
+ } else {
165
+ setWidth();
166
+ }
167
+
168
+ if (!ta.value && options.placeholder) {
169
+ // If the textarea is empty, copy the placeholder text into
170
+ // the mirror control and use that for sizing so that we
171
+ // don't end up with placeholder getting trimmed.
172
+ mirror.value = ($ta.attr("placeholder") || '') + options.append;
173
+ } else {
174
+ mirror.value = ta.value + options.append;
175
+ }
176
+
177
+ mirror.style.overflowY = ta.style.overflowY;
178
+ original = parseInt(ta.style.height,10);
179
+
180
+ // Setting scrollTop to zero is needed in IE8 and lower for the next step to be accurately applied
181
+ mirror.scrollTop = 0;
182
+
183
+ mirror.scrollTop = 9e4;
184
+
185
+ // Using scrollTop rather than scrollHeight because scrollHeight is non-standard and includes padding.
186
+ height = mirror.scrollTop;
187
+
188
+ if (maxHeight && height > maxHeight) {
189
+ ta.style.overflowY = 'scroll';
190
+ height = maxHeight;
191
+ } else {
192
+ ta.style.overflowY = 'hidden';
193
+ if (height < minHeight) {
194
+ height = minHeight;
195
+ }
196
+ }
197
+
198
+ height += boxOffset;
199
+
200
+ if (original !== height) {
201
+ ta.style.height = height + 'px';
202
+ if (callback) {
203
+ options.callback.call(ta,ta);
204
+ }
205
+ }
206
+ }
207
+
208
+ function resize () {
209
+ clearTimeout(timeout);
210
+ timeout = setTimeout(function(){
211
+ var newWidth = $ta.width();
212
+
213
+ if (newWidth !== width) {
214
+ width = newWidth;
215
+ adjust();
216
+ }
217
+ }, parseInt(options.resizeDelay,10));
218
+ }
219
+
220
+ if ('onpropertychange' in ta) {
221
+ if ('oninput' in ta) {
222
+ // Detects IE9. IE9 does not fire onpropertychange or oninput for deletions,
223
+ // so binding to onkeyup to catch most of those occasions. There is no way that I
224
+ // know of to detect something like 'cut' in IE9.
225
+ $ta.on('input.autosize keyup.autosize', adjust);
226
+ } else {
227
+ // IE7 / IE8
228
+ $ta.on('propertychange.autosize', function(){
229
+ if(event.propertyName === 'value'){
230
+ adjust();
231
+ }
232
+ });
233
+ }
234
+ } else {
235
+ // Modern Browsers
236
+ $ta.on('input.autosize', adjust);
237
+ }
238
+
239
+ // Set options.resizeDelay to false if using fixed-width textarea elements.
240
+ // Uses a timeout and width check to reduce the amount of times adjust needs to be called after window resize.
241
+
242
+ if (options.resizeDelay !== false) {
243
+ $(window).on('resize.autosize', resize);
244
+ }
245
+
246
+ // Event for manual triggering if needed.
247
+ // Should only be needed when the value of the textarea is changed through JavaScript rather than user input.
248
+ $ta.on('autosize.resize', adjust);
249
+
250
+ // Event for manual triggering that also forces the styles to update as well.
251
+ // Should only be needed if one of typography styles of the textarea change, and the textarea is already the target of the adjust method.
252
+ $ta.on('autosize.resizeIncludeStyle', function() {
253
+ mirrored = null;
254
+ adjust();
255
+ });
256
+
257
+ $ta.on('autosize.destroy', function(){
258
+ mirrored = null;
259
+ clearTimeout(timeout);
260
+ $(window).off('resize', resize);
261
+ $ta
262
+ .off('autosize')
263
+ .off('.autosize')
264
+ .css(originalStyles)
265
+ .removeData('autosize');
266
+ });
267
+
268
+ // Call adjust in case the textarea already contains text.
269
+ adjust();
270
+ });
271
+ };
272
+ }(window.jQuery || window.$)); // jQuery or jQuery-like library, such as Zepto