tb_core 1.4.3.1 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -54
  3. data/app/assets/javascripts/admin/core/application.js +8 -4
  4. data/app/assets/javascripts/admin/core/dashboard.js +2 -3
  5. data/app/assets/javascripts/admin/core/date_picker.js +2 -2
  6. data/app/assets/javascripts/admin/core/editor.js +2 -2
  7. data/app/assets/javascripts/admin/core/modal.js +1 -1
  8. data/app/assets/javascripts/admin/core/users.js +5 -5
  9. data/app/assets/javascripts/tb_core.js +8 -0
  10. data/app/assets/libs/sortable/sortable.js +152 -91
  11. data/app/controllers/admin/application_controller.rb +1 -2
  12. data/app/controllers/admin/dashboard_controller.rb +2 -2
  13. data/app/controllers/admin/settings_controller.rb +2 -2
  14. data/app/controllers/admin/users_controller.rb +4 -7
  15. data/app/controllers/concerns/tb_core/error_handling.rb +16 -7
  16. data/app/controllers/concerns/tb_core/user_authentication.rb +5 -5
  17. data/app/controllers/tb_core/application_controller.rb +1 -1
  18. data/app/controllers/user_sessions_controller.rb +2 -2
  19. data/app/helpers/tb_core/application_helper.rb +4 -36
  20. data/app/mailers/tb_core_mailer.rb +3 -3
  21. data/app/models/concerns/tb_core/user_model.rb +131 -0
  22. data/app/models/spud/spud_user_model.rb +6 -138
  23. data/app/models/spud_permission.rb +4 -4
  24. data/app/models/spud_role.rb +1 -1
  25. data/app/models/spud_user.rb +2 -1
  26. data/app/views/admin/dashboard/index.html.erb +1 -1
  27. data/app/views/admin/settings/_form.html.erb +1 -1
  28. data/app/views/admin/settings/edit.html.erb +1 -1
  29. data/app/views/admin/setup/new.html.erb +1 -1
  30. data/app/views/admin/users/_form.html.erb +1 -1
  31. data/app/views/admin/users/index.html.erb +2 -2
  32. data/app/views/admin/users/show.html.erb +1 -1
  33. data/app/views/layouts/admin/application.html.erb +6 -6
  34. data/app/views/layouts/admin/login.html.erb +1 -1
  35. data/app/views/tb_core_mailer/forgot_password_notification.html.erb +2 -2
  36. data/app/views/tb_core_mailer/user_credentials.html.erb +2 -2
  37. data/app/views/user_sessions/_form.html.erb +1 -1
  38. data/app/views/user_sessions/new.html.erb +1 -1
  39. data/lib/generators/spud/module_generator.rb +7 -7
  40. data/lib/generators/spud/setup_generator.rb +6 -7
  41. data/lib/generators/spud/templates/assets/admin/application.js +1 -1
  42. data/lib/generators/spud/templates/assets/application.js +3 -4
  43. data/lib/generators/spud/templates/controller_spec.rb.erb +5 -5
  44. data/lib/generators/spud/templates/views/admin/_form.html.erb +1 -1
  45. data/lib/generators/spud/templates/views/admin/index.html.erb +1 -1
  46. data/lib/generators/spud/templates/views/admin/show.html.erb +1 -1
  47. data/lib/generators/spud/templates/views/frontend/index.html.erb +1 -1
  48. data/lib/generators/spud/templates/views/frontend/show.html.erb +1 -1
  49. data/lib/tb_core.rb +3 -5
  50. data/lib/tb_core/belongs_to_app.rb +47 -46
  51. data/lib/tb_core/catch_all_route.rb +3 -0
  52. data/lib/tb_core/configuration.rb +28 -0
  53. data/lib/tb_core/engine.rb +61 -0
  54. data/lib/tb_core/errors.rb +50 -0
  55. data/lib/tb_core/responder.rb +0 -26
  56. data/lib/{spud_core → tb_core}/searchable.rb +4 -1
  57. data/lib/tb_core/spud_core.rb +23 -0
  58. data/lib/{spud_core → tb_core}/test_files.rb +0 -0
  59. data/lib/tb_core/test_helper.rb +3 -3
  60. data/lib/tb_core/version.rb +3 -0
  61. data/spec/controllers/admin/application_controller_spec.rb +5 -5
  62. data/spec/controllers/admin/dashboard_controller_spec.rb +5 -5
  63. data/spec/controllers/admin/password_reset_controller_spec.rb +1 -1
  64. data/spec/controllers/admin/settings_controller_spec.rb +1 -1
  65. data/spec/controllers/admin/setup_controller_spec.rb +2 -2
  66. data/spec/controllers/admin/user_sessions_controller_spec.rb +2 -2
  67. data/spec/controllers/admin/users_controller_spec.rb +17 -17
  68. data/spec/dummy/app/assets/javascripts/admin/application.js +1 -1
  69. data/spec/dummy/app/assets/javascripts/application.js +1 -1
  70. data/spec/dummy/config/application.rb +1 -1
  71. data/spec/factories/spud_admin_permission_factories.rb +2 -2
  72. data/spec/factories/spud_role_factories.rb +2 -2
  73. data/spec/factories/spud_user_factories.rb +6 -6
  74. data/spec/helpers/spud/admin/application_helper_spec.rb +1 -1
  75. data/spec/helpers/tb_core/application_helper_spec.rb +0 -35
  76. data/spec/lib/spud_core/configuration_spec.rb +2 -2
  77. data/spec/lib/tb_core/belongs_to_app_spec.rb +4 -3
  78. data/spec/models/spud_role_spec.rb +2 -2
  79. data/spec/models/spud_user_spec.rb +13 -13
  80. data/spec/rails_helper.rb +1 -1
  81. metadata +59 -67
  82. data/app/assets/javascripts/tb_core.js.erb +0 -9
  83. data/app/assets/javascripts/tb_core/configuration.js.erb +0 -9
  84. data/app/assets/javascripts/tb_core/console.js +0 -12
  85. data/app/assets/javascripts/tb_core/remote.js +0 -237
  86. data/app/assets/javascripts/tb_core/utility.js +0 -55
  87. data/app/controllers/concerns/tb_core/sortable_params.rb +0 -80
  88. data/app/controllers/spud/application_controller.rb +0 -10
  89. data/lib/generators/spud/templates/assets/bootstrap.js +0 -14
  90. data/lib/spud_core/catch_all_route.rb +0 -3
  91. data/lib/spud_core/configuration.rb +0 -56
  92. data/lib/spud_core/engine.rb +0 -76
  93. data/lib/spud_core/errors.rb +0 -47
  94. data/lib/spud_core/version.rb +0 -5
  95. data/lib/tb_core/table_header.rb +0 -92
  96. data/spec/controllers/tb_core/sortable_params_spec.rb +0 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ff5711624143515d7f25cae3514af1952fe9bca
4
- data.tar.gz: 6122374aada3014cb4b4cf8a9bd143eab357a15c
3
+ metadata.gz: 2510a27d312d495c3d4a1163f6aace27f801370e
4
+ data.tar.gz: ae0a3e6d44d4c30ea885097bf4219e0717564bd5
5
5
  SHA512:
6
- metadata.gz: ae2b741afa3778b40a80e0e10c9b0bfdbd59dc84a09203aaff6a187908d2bbb2d1026c964de8e162b715123dca1be0c45fc216a6f4c923ff0e109d4d624d1136
7
- data.tar.gz: d9519b8044a3f459b412c115700e223887b515952df7bac9bc68562e51f451a1c423bd5263b619facdced32a3b2b40d741d094a8c59e590b51c2537970a9267d
6
+ metadata.gz: b3a0574fd5d942b756698fb48f5772247335f8d89f2233c943d66089915b8f43520fffcbdcda3919804b316256d3270f1f79e50108da386e4f9e3a526d24f758
7
+ data.tar.gz: 453bada55663e0c324cb2c3f417933d3bf7635f5f075ec5b8a87c435820ddb712973d8f1e09286db956c955f9290a428483aa171aacedebe393662d60bd42287
data/README.md CHANGED
@@ -41,7 +41,7 @@ Configuration
41
41
 
42
42
  The core engine provides various configuration options.
43
43
 
44
- Spud::Core.configure do |config|
44
+ TbCore.configure do |config|
45
45
  config.option = value
46
46
  ...
47
47
  end
@@ -66,9 +66,9 @@ Running the example above would create an ActiveRecord model, migration, admin c
66
66
 
67
67
  Sometimes you want to create a dashboard app without all the magic described above. Start by building your controllers and views under the `admin` namespace. Extend your controller from `Admin::ApplicationController` in order to inherit the default admin behaviors, and look at the core admin controllers for example implementations.
68
68
 
69
- Then, add your module to the `Spud::Core.config.admin_applications` array. We recommend you perform this action in either an initializer or within your `application.rb` file.
69
+ Then, add your module to the `TbCore.config.admin_applications` array. We recommend you perform this action in either an initializer or within your `application.rb` file.
70
70
 
71
- Spud::Core.config.admin_applications += [{
71
+ TbCore.config.admin_applications += [{
72
72
  # Required:
73
73
  :name => "Clients",
74
74
  :thumbnail => "spud/admin/clients.png",
@@ -86,9 +86,10 @@ A common requirement is to add custom fields and functionality to the base `spud
86
86
 
87
87
  ### Adding custom methods and attributes to the SpudUser class
88
88
 
89
- Create a file in your app at `app/models/spud_user.rb`, and build a class called `SpudUser` that extends from `Spud::SpudUserModel`. You do not need any explicit requires - the autoloader will take care of the rest.
89
+ Create a file in your app at `app/models/spud_user.rb`, and build a class called `SpudUser` that includes the `TbCore::UserModel` module.
90
90
 
91
- class SpudUser < Spud::SpudUserModel
91
+ class SpudUser < ApplicationRecord
92
+ include TbCore::UserModel
92
93
  has_attached_file :avatar
93
94
  attr_accessible :avatar
94
95
  def say_hello
@@ -121,9 +122,9 @@ Create a file in your app at `app/views/admin/users/_show_additions.html.erb`.
121
122
  Error Handling
122
123
  ------------
123
124
 
124
- The base `TbCore::ApplicationController` will automatically rescue from any `Spud::NotFoundError` and `Spud::AccessDeniedError` errors by rendering out the `layouts/error_page.html` template. If you ran the `spud:setup` generator, a template of that name will have been created for you automatically.
125
+ The base `TbCore::ApplicationController` will automatically rescue from any `TbCore::NotFoundError` and `TbCore::AccessDeniedError` errors by rendering out the `layouts/error_page.html` template. If you ran the `spud:setup` generator, a template of that name will have been created for you automatically.
125
126
 
126
- When building your own apps you may raise a `Spud::NotFoundError` or `Spud::AccessDeniedError` error at any time to halt further execution and cause the error page to render. For example:
127
+ When building your own apps you may raise a `TbCore::NotFoundError` or `TbCore::AccessDeniedError` error at any time to halt further execution and cause the error page to render. For example:
127
128
 
128
129
  class CarsController
129
130
  before_action :get_record
@@ -175,87 +176,50 @@ A common case is to have a model in your app that `belongs_to :spud_user`. The `
175
176
  <%= f.tb_save_buttons('Widget', widgets_path) %>
176
177
  <% end %>
177
178
 
178
- Table Sorting
179
- -------------
180
-
181
- We provide a few tools to assist with sorting tables of data backed by ActiveRecord queries. In your view, the `tb_table_header` helper method will generate a `<thead>` element with header tags.
182
-
183
- ### Example
184
-
185
- ```erb
186
- <%= tb_table_header :users_path do |t| %>
187
- <%= t.sortable :full_name %>
188
- <%= t.sortable :email %>
189
- <%= t.sortable :birth_date %>
190
- <%= t.header :favorite_color %> # Header without sorting
191
- <th>Plain HTML is fine too</th>
192
- <% end %>
193
- ```
194
-
195
- On the controller side, you can choose to deal with the `:dir` and `:sort` params using your own custom logic, or you can use the `SortableParams` module.
196
-
197
- ```ruby
198
- include TbCore::SortableParams
199
-
200
- sortable_by :email, # A basic sort
201
- full_name: [:last_name, :first_name], # This will sort on two columns
202
- birth_date: 'birth_dates.date :dir', # This will sort on a joined table
203
- default: :email # The sort to use when none is passed
204
-
205
- def index
206
- MyModel.where(...).order(sortable_query)
207
- end
208
- ```
209
-
210
179
  Remote Forms
211
180
  --------------
212
181
 
213
- If you choose to include the `tb_core` JavaScript file in your project, we provide a couple additional features on top of the standard [jquery-ujs](https://github.com/rails/jquery-ujs) adapter. Use the following data attributes on your remote forms.
182
+ Rails-validator is included for easy remote form validation.
214
183
 
215
- - `data-errors`: Set to `inline` for inline form errors, or `alert` for a browser alert window.
216
- - `data-success`: Set to a path you would like to redirect to. For example, redirecting back to an index after a successful post.
217
-
218
- This feature works with `tb_form_for` or standard `form_for` forms.
219
-
220
- ### Example
184
+ **Example**
221
185
 
222
- <%= tb_form_for @widget, :remote => true, :data => {:errors => :inline, :success => widgets_path} do |f| %>
186
+ <%= tb_form_for @widget, remote: true, data: { errors: :inline, success: widgets_path } do |f| %>
223
187
  <!--input fields here -->
224
188
  <% end %>
225
189
 
226
- See `remote.js` for the full details.
190
+ See [rails-validator](https://github.com/moser-inc/rails-validator) for details.
227
191
 
228
192
  Roles & Permissions
229
193
  -------------------
230
194
 
231
195
  A user can be assigned to a role, which itself has one or more permissions. These permissions serve two distinct purposes: Determining what dashboard apps a user has access to (if any), and being used within your application logic to establish fine-grained user abilities.
232
196
 
233
- Checking permissions is easy. Each permission has a `tag` which is a unique, namespaced identifier. Use the `has_permission?` and `has_any_permission?` methods on the user object to check for their existence. Keep in mind that these calls will always return true for users who have the `super_admin` flag set.
197
+ Checking permissions is easy. Each permission has a `tag` which is a unique, namespaced identifier. Use the `permission?` and `any_permission?` methods on the user object to check for their existence. Keep in mind that these calls will always return true for users who have the `super_admin` flag set.
234
198
 
235
- if !@spud_user.has_permission?('my_website.clients.my_clients')
199
+ if !@spud_user.permission?('my_website.clients.my_clients')
236
200
  redirect_to root_path, :notice => 'Permission Denied!'
237
201
  end
238
202
 
239
203
  Permissions are created one of two ways:
240
204
 
241
205
  1. Every dashboard app automatically generates a "Full Access" permission, with a tag of `admin.(app name).full_access`.
242
- 2. You as the developer may append custom permissions to the `Spud::Core.permissions` array.
206
+ 2. You as the developer may append custom permissions to the `TbCore.permissions` array.
243
207
 
244
208
  Create custom permissions whenever you need to permit an action that falls outside of the standard "full access to an app" use case; For example, turning on/off the ability for a user to upload an avatar.
245
209
 
246
210
  // application.rb
247
- Spud::Core.permissions += [
211
+ TbCore.permissions += [
248
212
  {:tag => 'my_website.profile.avatar', :name => 'Upload an avatar to my profile'}
249
213
  ]
250
214
  // some_view.html.erb
251
- <% if current_user.has_permission?('my_website.profile.avatar') %>
215
+ <% if current_user.permission?('my_website.profile.avatar') %>
252
216
  <%= link_to 'Upload Avatar', upload_avatar_path %>
253
217
  <% end %>
254
218
 
255
219
  Finally, custom permissions may optionally be tied to one or more dashboard apps. A user who has the permission shown below would have access to the the Clients and Projects dashboard apps. After that is is up to you to code your view and controller logic in accorance to what permissions the user has.
256
220
 
257
221
  // application.rb
258
- Spud::Core.permissions += [{
222
+ TbCore.permissions += [{
259
223
  :tag => 'my_website.projects.project_management',
260
224
  :name => 'Manage clients and projects, but cannot delete them or view private info',
261
225
  :apps => [:clients, :projects]
@@ -1,14 +1,18 @@
1
+ //= require rails-ujs
2
+ //= require rails-validator
1
3
  //= require jquery
2
4
  //= require bootstrap-sprockets
3
- //= require ../../tb_core
4
5
  //= require_self
5
6
  //= require_tree .
6
7
 
7
- spud = {admin:{}};
8
+ window.tb = {};
8
9
 
9
- $(document).ready(function() {
10
+ // for legacy code
11
+ window.spud = { admin: window.tb };
10
12
 
11
- spud.admin.editor.init();
13
+ document.addEventListener('DOMContentLoaded', function() {
14
+
15
+ tb.editor.init();
12
16
 
13
17
  $("#modal_window .modal-footer .form-submit").bind('click', function() {
14
18
  $("#modal_window .modal-body form").submit();
@@ -4,7 +4,7 @@
4
4
 
5
5
  var badgeInterval;
6
6
 
7
- spud.admin.dashboard = {
7
+ tb.dashboard = {
8
8
  init:function(){
9
9
  badgeInterval = setInterval(updateBadges, 30000);
10
10
  updateBadges();
@@ -13,9 +13,8 @@ spud.admin.dashboard = {
13
13
  };
14
14
 
15
15
  var updateBadges = function() {
16
- var url = tb.util.urlFor('admin/badges');
17
16
  $.ajax({
18
- url: url
17
+ url: '/admin/badges'
19
18
  }).always(function(json) {
20
19
  if(json && json.data) {
21
20
  if(json.data.length === 0){
@@ -1,7 +1,7 @@
1
- spud.admin.date_picker = {};
1
+ tb.date_picker = {};
2
2
 
3
3
  (function() {
4
- var picker = spud.admin.date_picker;
4
+ var picker = tb.date_picker;
5
5
 
6
6
  parsePickerDate = function(value) {
7
7
  var date = new Date();
@@ -1,8 +1,8 @@
1
- spud.admin.editor = {};
1
+ tb.editor = {};
2
2
 
3
3
  (function(){
4
4
 
5
- var editor = spud.admin.editor;
5
+ var editor = tb.editor;
6
6
 
7
7
  editor.tinymce = {loaded:false};
8
8
 
@@ -1,6 +1,6 @@
1
1
  (function(){
2
2
 
3
- spud.admin.modal = {
3
+ tb.modal = {
4
4
 
5
5
  displayWithOptions: function(options){
6
6
  var modal = $('#modal_window');
@@ -2,7 +2,7 @@
2
2
 
3
3
  var $picker;
4
4
 
5
- spud.admin.users = {
5
+ tb.users = {
6
6
  init: function(){
7
7
  var $body = $('body');
8
8
  $body.on('click', '.btn-generate-password', clickedGeneratePassword);
@@ -78,7 +78,7 @@ var clickedPickerEdit = function(e){
78
78
  $.ajax(href, {
79
79
  dataType: 'html',
80
80
  success: function(html){
81
- spud.admin.modal.displayWithOptions({
81
+ tb.modal.displayWithOptions({
82
82
  title: 'Edit User',
83
83
  html: html,
84
84
  hideFooter: true
@@ -96,7 +96,7 @@ var clickedPickerAdd = function(e){
96
96
  $.ajax($(this).attr('href'), {
97
97
  dataType: 'html',
98
98
  success: function(html){
99
- spud.admin.modal.displayWithOptions({
99
+ tb.modal.displayWithOptions({
100
100
  title: 'New User',
101
101
  html: html,
102
102
  hideFooter: true
@@ -122,10 +122,10 @@ var savedModalUser = function(event){
122
122
  $picker.append($option);
123
123
  }
124
124
  $picker.val(json.id);
125
- spud.admin.modal.hide();
125
+ tb.modal.hide();
126
126
  return false;
127
127
  };
128
128
 
129
- $(document).ready(spud.admin.users.init);
129
+ document.addEventListener('DOMContentLoaded', tb.users.init);
130
130
 
131
131
  })();
@@ -0,0 +1,8 @@
1
+ //= require rails-ujs
2
+ //= require rails-validator
3
+
4
+ console.warn("You are including tb_core.js in your application; This file is deprecated and may be removed.\n\
5
+ For similar functionality, replace tb_core in your asset pipeline with the following:\n\
6
+ \n\
7
+ //= require rails-ujs\n\
8
+ //= require rails-validator")
@@ -20,7 +20,7 @@
20
20
  })(function sortableFactory() {
21
21
  "use strict";
22
22
 
23
- if (typeof window == "undefined" || !window.document) {
23
+ if (typeof window === "undefined" || !window.document) {
24
24
  return function sortableError() {
25
25
  throw new Error("Sortable.js requires a window with a document");
26
26
  };
@@ -64,16 +64,18 @@
64
64
  win = window,
65
65
  document = win.document,
66
66
  parseInt = win.parseInt,
67
+ setTimeout = win.setTimeout,
67
68
 
68
69
  $ = win.jQuery || win.Zepto,
69
70
  Polymer = win.Polymer,
70
71
 
71
72
  captureMode = false,
73
+ passiveMode = false,
72
74
 
73
- supportDraggable = !!('draggable' in document.createElement('div')),
75
+ supportDraggable = ('draggable' in document.createElement('div')),
74
76
  supportCssPointerEvents = (function (el) {
75
77
  // false when IE11
76
- if (!!navigator.userAgent.match(/Trident.*rv[ :]?11\./)) {
78
+ if (!!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie)/i)) {
77
79
  return false;
78
80
  }
79
81
  el = document.createElement('x');
@@ -214,6 +216,20 @@
214
216
  }
215
217
  ;
216
218
 
219
+ // Detect support a passive mode
220
+ try {
221
+ window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
222
+ get: function () {
223
+ // `false`, because everything starts to work incorrectly and instead of d'n'd,
224
+ // begins the page has scrolled.
225
+ passiveMode = false;
226
+ captureMode = {
227
+ capture: false,
228
+ passive: passiveMode
229
+ };
230
+ }
231
+ }));
232
+ } catch (err) {}
217
233
 
218
234
  /**
219
235
  * @class Sortable
@@ -261,7 +277,8 @@
261
277
  fallbackClass: 'sortable-fallback',
262
278
  fallbackOnBody: false,
263
279
  fallbackTolerance: 0,
264
- fallbackOffset: {x: 0, y: 0}
280
+ fallbackOffset: {x: 0, y: 0},
281
+ supportPointer: Sortable.supportPointer !== false
265
282
  };
266
283
 
267
284
 
@@ -285,7 +302,7 @@
285
302
  // Bind events
286
303
  _on(el, 'mousedown', this._onTapStart);
287
304
  _on(el, 'touchstart', this._onTapStart);
288
- _on(el, 'pointerdown', this._onTapStart);
305
+ options.supportPointer && _on(el, 'pointerdown', this._onTapStart);
289
306
 
290
307
  if (this.nativeDraggable) {
291
308
  _on(el, 'dragover', this);
@@ -310,7 +327,7 @@
310
327
  type = evt.type,
311
328
  touch = evt.touches && evt.touches[0],
312
329
  target = (touch || evt).target,
313
- originalTarget = evt.target.shadowRoot && evt.path[0] || target,
330
+ originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0]) || target,
314
331
  filter = options.filter,
315
332
  startIndex;
316
333
 
@@ -322,10 +339,14 @@
322
339
  return;
323
340
  }
324
341
 
325
- if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
342
+ if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) {
326
343
  return; // only left button or enabled
327
344
  }
328
345
 
346
+ // cancel dnd if original target is content editable
347
+ if (originalTarget.isContentEditable) {
348
+ return;
349
+ }
329
350
 
330
351
  target = _closest(target, options.draggable, el);
331
352
 
@@ -344,7 +365,7 @@
344
365
  // Check filter
345
366
  if (typeof filter === 'function') {
346
367
  if (filter.call(this, evt, target, this)) {
347
- _dispatchEvent(_this, originalTarget, 'filter', target, el, startIndex);
368
+ _dispatchEvent(_this, originalTarget, 'filter', target, el, el, startIndex);
348
369
  preventOnFilter && evt.preventDefault();
349
370
  return; // cancel dnd
350
371
  }
@@ -354,7 +375,7 @@
354
375
  criteria = _closest(originalTarget, criteria.trim(), el);
355
376
 
356
377
  if (criteria) {
357
- _dispatchEvent(_this, criteria, 'filter', target, el, startIndex);
378
+ _dispatchEvent(_this, criteria, 'filter', target, el, el, startIndex);
358
379
  return true;
359
380
  }
360
381
  });
@@ -394,7 +415,7 @@
394
415
  this._lastX = (touch || evt).clientX;
395
416
  this._lastY = (touch || evt).clientY;
396
417
 
397
- dragEl.style['will-change'] = 'transform';
418
+ dragEl.style['will-change'] = 'all';
398
419
 
399
420
  dragStartFn = function () {
400
421
  // Delayed drag has been triggered
@@ -411,7 +432,7 @@
411
432
  _this._triggerDragStart(evt, touch);
412
433
 
413
434
  // Drag start event
414
- _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex);
435
+ _dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, rootEl, oldIndex);
415
436
  };
416
437
 
417
438
  // Disable "draggable"
@@ -422,8 +443,8 @@
422
443
  _on(ownerDocument, 'mouseup', _this._onDrop);
423
444
  _on(ownerDocument, 'touchend', _this._onDrop);
424
445
  _on(ownerDocument, 'touchcancel', _this._onDrop);
425
- _on(ownerDocument, 'pointercancel', _this._onDrop);
426
446
  _on(ownerDocument, 'selectstart', _this);
447
+ options.supportPointer && _on(ownerDocument, 'pointercancel', _this._onDrop);
427
448
 
428
449
  if (options.delay) {
429
450
  // If the user moves the pointer or let go the click or touch
@@ -434,7 +455,7 @@
434
455
  _on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
435
456
  _on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
436
457
  _on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
437
- _on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
458
+ options.supportPointer && _on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
438
459
 
439
460
  _this._dragStartTimer = setTimeout(dragStartFn, options.delay);
440
461
  } else {
@@ -481,7 +502,7 @@
481
502
  try {
482
503
  if (document.selection) {
483
504
  // Timeout neccessary for IE9
484
- setTimeout(function () {
505
+ _nextTick(function () {
485
506
  document.selection.empty();
486
507
  });
487
508
  } else {
@@ -502,7 +523,7 @@
502
523
  Sortable.active = this;
503
524
 
504
525
  // Drag start event
505
- _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
526
+ _dispatchEvent(this, rootEl, 'start', dragEl, rootEl, rootEl, oldIndex);
506
527
  } else {
507
528
  this._nulling();
508
529
  }
@@ -521,9 +542,14 @@
521
542
  _css(ghostEl, 'display', 'none');
522
543
  }
523
544
 
524
- var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
525
- parent = target,
526
- i = touchDragOverListeners.length;
545
+ var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
546
+ var parent = target;
547
+ var i = touchDragOverListeners.length;
548
+
549
+ if (target && target.shadowRoot) {
550
+ target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY);
551
+ parent = target;
552
+ }
527
553
 
528
554
  if (parent) {
529
555
  do {
@@ -621,22 +647,26 @@
621
647
  },
622
648
 
623
649
  _onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
624
- var dataTransfer = evt.dataTransfer,
625
- options = this.options;
650
+ var _this = this;
651
+ var dataTransfer = evt.dataTransfer;
652
+ var options = _this.options;
626
653
 
627
- this._offUpEvents();
654
+ _this._offUpEvents();
628
655
 
629
- if (activeGroup.checkPull(this, this, dragEl, evt)) {
656
+ if (activeGroup.checkPull(_this, _this, dragEl, evt)) {
630
657
  cloneEl = _clone(dragEl);
631
658
 
632
659
  cloneEl.draggable = false;
633
660
  cloneEl.style['will-change'] = '';
634
661
 
635
662
  _css(cloneEl, 'display', 'none');
636
- _toggleClass(cloneEl, this.options.chosenClass, false);
663
+ _toggleClass(cloneEl, _this.options.chosenClass, false);
637
664
 
638
- rootEl.insertBefore(cloneEl, dragEl);
639
- _dispatchEvent(this, rootEl, 'clone', dragEl);
665
+ // #1143: IFrame support workaround
666
+ _this._cloneId = _nextTick(function () {
667
+ rootEl.insertBefore(cloneEl, dragEl);
668
+ _dispatchEvent(_this, rootEl, 'clone', dragEl);
669
+ });
640
670
  }
641
671
 
642
672
  _toggleClass(dragEl, options.dragClass, true);
@@ -644,27 +674,36 @@
644
674
  if (useFallback) {
645
675
  if (useFallback === 'touch') {
646
676
  // Bind touch events
647
- _on(document, 'touchmove', this._onTouchMove);
648
- _on(document, 'touchend', this._onDrop);
649
- _on(document, 'touchcancel', this._onDrop);
650
- _on(document, 'pointermove', this._onTouchMove);
651
- _on(document, 'pointerup', this._onDrop);
677
+ _on(document, 'touchmove', _this._onTouchMove);
678
+ _on(document, 'touchend', _this._onDrop);
679
+ _on(document, 'touchcancel', _this._onDrop);
680
+
681
+ if (options.supportPointer) {
682
+ _on(document, 'pointermove', _this._onTouchMove);
683
+ _on(document, 'pointerup', _this._onDrop);
684
+ }
652
685
  } else {
653
686
  // Old brwoser
654
- _on(document, 'mousemove', this._onTouchMove);
655
- _on(document, 'mouseup', this._onDrop);
687
+ _on(document, 'mousemove', _this._onTouchMove);
688
+ _on(document, 'mouseup', _this._onDrop);
656
689
  }
657
690
 
658
- this._loopId = setInterval(this._emulateDragOver, 50);
691
+ _this._loopId = setInterval(_this._emulateDragOver, 50);
659
692
  }
660
693
  else {
661
694
  if (dataTransfer) {
662
695
  dataTransfer.effectAllowed = 'move';
663
- options.setData && options.setData.call(this, dataTransfer, dragEl);
696
+ options.setData && options.setData.call(_this, dataTransfer, dragEl);
664
697
  }
665
698
 
666
- _on(document, 'drop', this);
667
- setTimeout(this._dragStarted, 0);
699
+ _on(document, 'drop', _this);
700
+
701
+ // #1143: Бывает элемент с IFrame внутри блокирует `drop`,
702
+ // поэтому если вызвался `mouseover`, значит надо отменять весь d'n'd.
703
+ // Breaking Chrome 62+
704
+ // _on(document, 'mouseover', _this);
705
+
706
+ _this._dragStartId = _nextTick(_this._dragStarted);
668
707
  }
669
708
  },
670
709
 
@@ -736,8 +775,13 @@
736
775
 
737
776
 
738
777
  if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
739
- (el === evt.target) && (target = _ghostIsLast(el, evt))
778
+ (el === evt.target) && (_ghostIsLast(el, evt))
740
779
  ) {
780
+ //assign target only if condition is true
781
+ if (el.children.length !== 0 && el.children[0] !== ghostEl && el === evt.target) {
782
+ target = el.lastElementChild;
783
+ }
784
+
741
785
  if (target) {
742
786
  if (target.animated) {
743
787
  return;
@@ -775,34 +819,36 @@
775
819
  isLong = (target.offsetHeight > dragEl.offsetHeight),
776
820
  halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
777
821
  nextSibling = target.nextElementSibling,
778
- moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt),
779
822
  after = false
780
823
  ;
781
824
 
782
- if (moveVector !== false) {
783
- _silent = true;
784
- setTimeout(_unsilent, 30);
825
+ if (floating) {
826
+ var elTop = dragEl.offsetTop,
827
+ tgTop = target.offsetTop;
785
828
 
786
- _cloneHide(activeSortable, isOwner);
829
+ if (elTop === tgTop) {
830
+ after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
831
+ }
832
+ else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
833
+ after = (evt.clientY - targetRect.top) / height > 0.5;
834
+ } else {
835
+ after = tgTop > elTop;
836
+ }
837
+ } else if (!isMovingBetweenSortable) {
838
+ after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
839
+ }
840
+
841
+ var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after);
787
842
 
843
+ if (moveVector !== false) {
788
844
  if (moveVector === 1 || moveVector === -1) {
789
845
  after = (moveVector === 1);
790
846
  }
791
- else if (floating) {
792
- var elTop = dragEl.offsetTop,
793
- tgTop = target.offsetTop;
794
847
 
795
- if (elTop === tgTop) {
796
- after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
797
- }
798
- else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
799
- after = (evt.clientY - targetRect.top) / height > 0.5;
800
- } else {
801
- after = tgTop > elTop;
802
- }
803
- } else if (!isMovingBetweenSortable) {
804
- after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
805
- }
848
+ _silent = true;
849
+ setTimeout(_unsilent, 30);
850
+
851
+ _cloneHide(activeSortable, isOwner);
806
852
 
807
853
  if (!dragEl.contains(el)) {
808
854
  if (after && !nextSibling) {
@@ -860,6 +906,7 @@
860
906
  _off(ownerDocument, 'touchend', this._onDrop);
861
907
  _off(ownerDocument, 'pointerup', this._onDrop);
862
908
  _off(ownerDocument, 'touchcancel', this._onDrop);
909
+ _off(ownerDocument, 'pointercancel', this._onDrop);
863
910
  _off(ownerDocument, 'selectstart', this);
864
911
  },
865
912
 
@@ -871,7 +918,11 @@
871
918
  clearInterval(autoScroll.pid);
872
919
  clearTimeout(this._dragStartTimer);
873
920
 
921
+ _cancelNextTick(this._cloneId);
922
+ _cancelNextTick(this._dragStartId);
923
+
874
924
  // Unbind events
925
+ _off(document, 'mouseover', this);
875
926
  _off(document, 'mousemove', this._onTouchMove);
876
927
 
877
928
  if (this.nativeDraggable) {
@@ -887,11 +938,11 @@
887
938
  !options.dropBubble && evt.stopPropagation();
888
939
  }
889
940
 
890
- ghostEl && ghostEl.parentNode.removeChild(ghostEl);
941
+ ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl);
891
942
 
892
943
  if (rootEl === parentEl || Sortable.active.lastPullMode !== 'clone') {
893
944
  // Remove clone
894
- cloneEl && cloneEl.parentNode.removeChild(cloneEl);
945
+ cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl);
895
946
  }
896
947
 
897
948
  if (dragEl) {
@@ -906,19 +957,22 @@
906
957
  _toggleClass(dragEl, this.options.ghostClass, false);
907
958
  _toggleClass(dragEl, this.options.chosenClass, false);
908
959
 
960
+ // Drag stop event
961
+ _dispatchEvent(this, rootEl, 'unchoose', dragEl, parentEl, rootEl, oldIndex);
962
+
909
963
  if (rootEl !== parentEl) {
910
964
  newIndex = _index(dragEl, options.draggable);
911
965
 
912
966
  if (newIndex >= 0) {
913
967
  // Add event
914
- _dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
968
+ _dispatchEvent(null, parentEl, 'add', dragEl, parentEl, rootEl, oldIndex, newIndex);
915
969
 
916
970
  // Remove event
917
- _dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
971
+ _dispatchEvent(this, rootEl, 'remove', dragEl, parentEl, rootEl, oldIndex, newIndex);
918
972
 
919
973
  // drag from one list and drop into another
920
- _dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
921
- _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
974
+ _dispatchEvent(null, parentEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
975
+ _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
922
976
  }
923
977
  }
924
978
  else {
@@ -928,8 +982,8 @@
928
982
 
929
983
  if (newIndex >= 0) {
930
984
  // drag & drop within the same list
931
- _dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
932
- _dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
985
+ _dispatchEvent(this, rootEl, 'update', dragEl, parentEl, rootEl, oldIndex, newIndex);
986
+ _dispatchEvent(this, rootEl, 'sort', dragEl, parentEl, rootEl, oldIndex, newIndex);
933
987
  }
934
988
  }
935
989
  }
@@ -940,7 +994,7 @@
940
994
  newIndex = oldIndex;
941
995
  }
942
996
 
943
- _dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
997
+ _dispatchEvent(this, rootEl, 'end', dragEl, parentEl, rootEl, oldIndex, newIndex);
944
998
 
945
999
  // Save sorting
946
1000
  this.save();
@@ -998,6 +1052,10 @@
998
1052
  }
999
1053
  break;
1000
1054
 
1055
+ case 'mouseover':
1056
+ this._onDrop(evt);
1057
+ break;
1058
+
1001
1059
  case 'selectstart':
1002
1060
  evt.preventDefault();
1003
1061
  break;
@@ -1245,7 +1303,7 @@
1245
1303
 
1246
1304
 
1247
1305
 
1248
- function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
1306
+ function _dispatchEvent(sortable, rootEl, name, targetEl, toEl, fromEl, startIndex, newIndex) {
1249
1307
  sortable = (sortable || rootEl[expando]);
1250
1308
 
1251
1309
  var evt = document.createEvent('Event'),
@@ -1254,7 +1312,7 @@
1254
1312
 
1255
1313
  evt.initEvent(name, true, true);
1256
1314
 
1257
- evt.to = rootEl;
1315
+ evt.to = toEl || rootEl;
1258
1316
  evt.from = fromEl || rootEl;
1259
1317
  evt.item = targetEl || rootEl;
1260
1318
  evt.clone = cloneEl;
@@ -1270,7 +1328,7 @@
1270
1328
  }
1271
1329
 
1272
1330
 
1273
- function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt) {
1331
+ function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt, willInsertAfter) {
1274
1332
  var evt,
1275
1333
  sortable = fromEl[expando],
1276
1334
  onMoveFn = sortable.options.onMove,
@@ -1285,6 +1343,7 @@
1285
1343
  evt.draggedRect = dragRect;
1286
1344
  evt.related = targetEl || toEl;
1287
1345
  evt.relatedRect = targetRect || toEl.getBoundingClientRect();
1346
+ evt.willInsertAfter = willInsertAfter;
1288
1347
 
1289
1348
  fromEl.dispatchEvent(evt);
1290
1349
 
@@ -1313,10 +1372,8 @@
1313
1372
 
1314
1373
  // 5 — min delta
1315
1374
  // abs — нельзя добавлять, а то глюки при наведении сверху
1316
- return (
1317
- (evt.clientY - (rect.top + rect.height) > 5) ||
1318
- (evt.clientX - (rect.right + rect.width) > 5)
1319
- ) && lastEl;
1375
+ return (evt.clientY - (rect.top + rect.height) > 5) ||
1376
+ (evt.clientX - (rect.left + rect.width) > 5);
1320
1377
  }
1321
1378
 
1322
1379
 
@@ -1411,15 +1468,20 @@
1411
1468
  }
1412
1469
 
1413
1470
  function _clone(el) {
1414
- return $
1415
- ? $(el).clone(true)[0]
1416
- : (Polymer && Polymer.dom
1417
- ? Polymer.dom(el).cloneNode(true)
1418
- : el.cloneNode(true)
1419
- );
1471
+ if (Polymer && Polymer.dom) {
1472
+ return Polymer.dom(el).cloneNode(true);
1473
+ }
1474
+ else if ($) {
1475
+ return $(el).clone(true)[0];
1476
+ }
1477
+ else {
1478
+ return el.cloneNode(true);
1479
+ }
1420
1480
  }
1421
1481
 
1422
1482
  function _saveInputCheckedState(root) {
1483
+ savedInputChecked.length = 0;
1484
+
1423
1485
  var inputs = root.getElementsByTagName('input');
1424
1486
  var idx = inputs.length;
1425
1487
 
@@ -1429,6 +1491,14 @@
1429
1491
  }
1430
1492
  }
1431
1493
 
1494
+ function _nextTick(fn) {
1495
+ return setTimeout(fn, 0);
1496
+ }
1497
+
1498
+ function _cancelNextTick(id) {
1499
+ return clearTimeout(id);
1500
+ }
1501
+
1432
1502
  // Fixed #973:
1433
1503
  _on(document, 'touchmove', function (evt) {
1434
1504
  if (Sortable.active) {
@@ -1436,17 +1506,6 @@
1436
1506
  }
1437
1507
  });
1438
1508
 
1439
- try {
1440
- window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
1441
- get: function () {
1442
- captureMode = {
1443
- capture: false,
1444
- passive: false
1445
- };
1446
- }
1447
- }));
1448
- } catch (err) {}
1449
-
1450
1509
  // Export utils
1451
1510
  Sortable.utils = {
1452
1511
  on: _on,
@@ -1461,7 +1520,9 @@
1461
1520
  closest: _closest,
1462
1521
  toggleClass: _toggleClass,
1463
1522
  clone: _clone,
1464
- index: _index
1523
+ index: _index,
1524
+ nextTick: _nextTick,
1525
+ cancelNextTick: _cancelNextTick
1465
1526
  };
1466
1527
 
1467
1528
 
@@ -1476,6 +1537,6 @@
1476
1537
 
1477
1538
 
1478
1539
  // Export
1479
- Sortable.version = '1.5.1';
1540
+ Sortable.version = '1.7.0';
1480
1541
  return Sortable;
1481
1542
  });