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.
- checksums.yaml +4 -4
- data/README.md +18 -54
- data/app/assets/javascripts/admin/core/application.js +8 -4
- data/app/assets/javascripts/admin/core/dashboard.js +2 -3
- data/app/assets/javascripts/admin/core/date_picker.js +2 -2
- data/app/assets/javascripts/admin/core/editor.js +2 -2
- data/app/assets/javascripts/admin/core/modal.js +1 -1
- data/app/assets/javascripts/admin/core/users.js +5 -5
- data/app/assets/javascripts/tb_core.js +8 -0
- data/app/assets/libs/sortable/sortable.js +152 -91
- data/app/controllers/admin/application_controller.rb +1 -2
- data/app/controllers/admin/dashboard_controller.rb +2 -2
- data/app/controllers/admin/settings_controller.rb +2 -2
- data/app/controllers/admin/users_controller.rb +4 -7
- data/app/controllers/concerns/tb_core/error_handling.rb +16 -7
- data/app/controllers/concerns/tb_core/user_authentication.rb +5 -5
- data/app/controllers/tb_core/application_controller.rb +1 -1
- data/app/controllers/user_sessions_controller.rb +2 -2
- data/app/helpers/tb_core/application_helper.rb +4 -36
- data/app/mailers/tb_core_mailer.rb +3 -3
- data/app/models/concerns/tb_core/user_model.rb +131 -0
- data/app/models/spud/spud_user_model.rb +6 -138
- data/app/models/spud_permission.rb +4 -4
- data/app/models/spud_role.rb +1 -1
- data/app/models/spud_user.rb +2 -1
- data/app/views/admin/dashboard/index.html.erb +1 -1
- data/app/views/admin/settings/_form.html.erb +1 -1
- data/app/views/admin/settings/edit.html.erb +1 -1
- data/app/views/admin/setup/new.html.erb +1 -1
- data/app/views/admin/users/_form.html.erb +1 -1
- data/app/views/admin/users/index.html.erb +2 -2
- data/app/views/admin/users/show.html.erb +1 -1
- data/app/views/layouts/admin/application.html.erb +6 -6
- data/app/views/layouts/admin/login.html.erb +1 -1
- data/app/views/tb_core_mailer/forgot_password_notification.html.erb +2 -2
- data/app/views/tb_core_mailer/user_credentials.html.erb +2 -2
- data/app/views/user_sessions/_form.html.erb +1 -1
- data/app/views/user_sessions/new.html.erb +1 -1
- data/lib/generators/spud/module_generator.rb +7 -7
- data/lib/generators/spud/setup_generator.rb +6 -7
- data/lib/generators/spud/templates/assets/admin/application.js +1 -1
- data/lib/generators/spud/templates/assets/application.js +3 -4
- data/lib/generators/spud/templates/controller_spec.rb.erb +5 -5
- data/lib/generators/spud/templates/views/admin/_form.html.erb +1 -1
- data/lib/generators/spud/templates/views/admin/index.html.erb +1 -1
- data/lib/generators/spud/templates/views/admin/show.html.erb +1 -1
- data/lib/generators/spud/templates/views/frontend/index.html.erb +1 -1
- data/lib/generators/spud/templates/views/frontend/show.html.erb +1 -1
- data/lib/tb_core.rb +3 -5
- data/lib/tb_core/belongs_to_app.rb +47 -46
- data/lib/tb_core/catch_all_route.rb +3 -0
- data/lib/tb_core/configuration.rb +28 -0
- data/lib/tb_core/engine.rb +61 -0
- data/lib/tb_core/errors.rb +50 -0
- data/lib/tb_core/responder.rb +0 -26
- data/lib/{spud_core → tb_core}/searchable.rb +4 -1
- data/lib/tb_core/spud_core.rb +23 -0
- data/lib/{spud_core → tb_core}/test_files.rb +0 -0
- data/lib/tb_core/test_helper.rb +3 -3
- data/lib/tb_core/version.rb +3 -0
- data/spec/controllers/admin/application_controller_spec.rb +5 -5
- data/spec/controllers/admin/dashboard_controller_spec.rb +5 -5
- data/spec/controllers/admin/password_reset_controller_spec.rb +1 -1
- data/spec/controllers/admin/settings_controller_spec.rb +1 -1
- data/spec/controllers/admin/setup_controller_spec.rb +2 -2
- data/spec/controllers/admin/user_sessions_controller_spec.rb +2 -2
- data/spec/controllers/admin/users_controller_spec.rb +17 -17
- data/spec/dummy/app/assets/javascripts/admin/application.js +1 -1
- data/spec/dummy/app/assets/javascripts/application.js +1 -1
- data/spec/dummy/config/application.rb +1 -1
- data/spec/factories/spud_admin_permission_factories.rb +2 -2
- data/spec/factories/spud_role_factories.rb +2 -2
- data/spec/factories/spud_user_factories.rb +6 -6
- data/spec/helpers/spud/admin/application_helper_spec.rb +1 -1
- data/spec/helpers/tb_core/application_helper_spec.rb +0 -35
- data/spec/lib/spud_core/configuration_spec.rb +2 -2
- data/spec/lib/tb_core/belongs_to_app_spec.rb +4 -3
- data/spec/models/spud_role_spec.rb +2 -2
- data/spec/models/spud_user_spec.rb +13 -13
- data/spec/rails_helper.rb +1 -1
- metadata +59 -67
- data/app/assets/javascripts/tb_core.js.erb +0 -9
- data/app/assets/javascripts/tb_core/configuration.js.erb +0 -9
- data/app/assets/javascripts/tb_core/console.js +0 -12
- data/app/assets/javascripts/tb_core/remote.js +0 -237
- data/app/assets/javascripts/tb_core/utility.js +0 -55
- data/app/controllers/concerns/tb_core/sortable_params.rb +0 -80
- data/app/controllers/spud/application_controller.rb +0 -10
- data/lib/generators/spud/templates/assets/bootstrap.js +0 -14
- data/lib/spud_core/catch_all_route.rb +0 -3
- data/lib/spud_core/configuration.rb +0 -56
- data/lib/spud_core/engine.rb +0 -76
- data/lib/spud_core/errors.rb +0 -47
- data/lib/spud_core/version.rb +0 -5
- data/lib/tb_core/table_header.rb +0 -92
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2510a27d312d495c3d4a1163f6aace27f801370e
|
4
|
+
data.tar.gz: ae0a3e6d44d4c30ea885097bf4219e0717564bd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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 `
|
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
|
-
|
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
|
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 <
|
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 `
|
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 `
|
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
|
-
|
182
|
+
Rails-validator is included for easy remote form validation.
|
214
183
|
|
215
|
-
|
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, :
|
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
|
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 `
|
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.
|
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 `
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
8
|
+
window.tb = {};
|
8
9
|
|
9
|
-
|
10
|
+
// for legacy code
|
11
|
+
window.spud = { admin: window.tb };
|
10
12
|
|
11
|
-
|
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
|
-
|
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:
|
17
|
+
url: '/admin/badges'
|
19
18
|
}).always(function(json) {
|
20
19
|
if(json && json.data) {
|
21
20
|
if(json.data.length === 0){
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
var $picker;
|
4
4
|
|
5
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
125
|
+
tb.modal.hide();
|
126
126
|
return false;
|
127
127
|
};
|
128
128
|
|
129
|
-
|
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
|
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 =
|
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
|
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'] = '
|
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
|
-
|
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
|
-
|
526
|
-
|
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
|
625
|
-
|
650
|
+
var _this = this;
|
651
|
+
var dataTransfer = evt.dataTransfer;
|
652
|
+
var options = _this.options;
|
626
653
|
|
627
|
-
|
654
|
+
_this._offUpEvents();
|
628
655
|
|
629
|
-
if (activeGroup.checkPull(
|
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,
|
663
|
+
_toggleClass(cloneEl, _this.options.chosenClass, false);
|
637
664
|
|
638
|
-
|
639
|
-
|
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',
|
648
|
-
_on(document, 'touchend',
|
649
|
-
_on(document, 'touchcancel',
|
650
|
-
|
651
|
-
|
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',
|
655
|
-
_on(document, 'mouseup',
|
687
|
+
_on(document, 'mousemove', _this._onTouchMove);
|
688
|
+
_on(document, 'mouseup', _this._onDrop);
|
656
689
|
}
|
657
690
|
|
658
|
-
|
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(
|
696
|
+
options.setData && options.setData.call(_this, dataTransfer, dragEl);
|
664
697
|
}
|
665
698
|
|
666
|
-
_on(document, 'drop',
|
667
|
-
|
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) && (
|
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 (
|
783
|
-
|
784
|
-
|
825
|
+
if (floating) {
|
826
|
+
var elTop = dragEl.offsetTop,
|
827
|
+
tgTop = target.offsetTop;
|
785
828
|
|
786
|
-
|
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
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
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.
|
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
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
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.
|
1540
|
+
Sortable.version = '1.7.0';
|
1480
1541
|
return Sortable;
|
1481
1542
|
});
|