visual_condition_builder 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +155 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/visual_condition_builder/Sortable.js +1385 -0
- data/app/assets/javascripts/visual_condition_builder/autoNumeric-2.0-BETA.js +2156 -0
- data/app/assets/javascripts/visual_condition_builder/autonumeric_ujs.js +94 -0
- data/app/assets/javascripts/visual_condition_builder/condition_builder.js +633 -0
- data/app/assets/javascripts/visual_condition_builder/diacritics.js +116 -0
- data/app/assets/javascripts/visual_condition_builder/select2.full.js +6436 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ar.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/az.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/bg.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ca.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/cs.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/da.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/de.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/el.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/en.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/es.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/et.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/eu.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/fa.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/fi.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/fr.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/gl.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/he.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/hi.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/hr.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/hu.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/id.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/is.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/it.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ja.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/km.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ko.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/lt.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/lv.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/mk.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ms.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/nb.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/nl.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/pl.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/pt-BR.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/pt.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ro.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/ru.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/sk.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/sr-Cyrl.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/sr.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/sv.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/th.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/tr.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/uk.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/vi.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/zh-CN.js +3 -0
- data/app/assets/javascripts/visual_condition_builder/select2_i18n/zh-TW.js +3 -0
- data/app/assets/javascripts/visual_condition_builder.js +6 -0
- data/app/assets/stylesheets/visual_condition_builder/condition_builder.css +87 -0
- data/app/assets/stylesheets/visual_condition_builder/select2-bootstrap.css +722 -0
- data/app/assets/stylesheets/visual_condition_builder/select2.css +484 -0
- data/app/assets/stylesheets/visual_condition_builder.css +5 -0
- data/app/controllers/application_widget.rb +56 -0
- data/app/controllers/visual_condition_builder/application_controller.rb +5 -0
- data/app/controllers/visual_condition_builder/widgets_controller.rb +45 -0
- data/app/helpers/visual_condition_builder/application_helper.rb +74 -0
- data/app/models/visual_condition_builder/user.rb +10 -0
- data/app/views/visual_condition_builder/widgets/_widgets_list.html.erb +24 -0
- data/app/views/visual_condition_builder/widgets/index.html.erb +25 -0
- data/config/initializers/assets.rb +1 -0
- data/config/initializers/visual_condition_builder.rb +7 -0
- data/config/routes.rb +19 -0
- data/lib/generators/templates/create_taxweb_widgets_users.rb +18 -0
- data/lib/generators/templates/generic_widget.erb +12 -0
- data/lib/generators/templates/generic_widget.html.erb +2 -0
- data/lib/generators/visual_condition_builder/install_generator.rb +26 -0
- data/lib/generators/visual_condition_builder/view_generator.rb +11 -0
- data/lib/generators/visual_condition_builder/widget_generator.rb +25 -0
- data/lib/visual_condition_builder/converter.rb +22 -0
- data/lib/visual_condition_builder/dictionary.rb +166 -0
- data/lib/visual_condition_builder/engine.rb +6 -0
- data/lib/visual_condition_builder/helper.rb +19 -0
- data/lib/visual_condition_builder/version.rb +3 -0
- data/lib/visual_condition_builder.rb +9 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 97cee479630d485dd9d7c81df722e6a07bfbb701
|
4
|
+
data.tar.gz: 5355a6541eead0031d7d0add3bf1be7f3f1278bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f9681137a3b7c9efccc7006fb8cc5c69a7441a43ed753265813629f0d6686d3347b8c588719545414311b749902564ca0c33c2e187b7c80f6e2486b639aaa5c6
|
7
|
+
data.tar.gz: 65b2df904ab1e9ab8cf7fd54910abe039d813f489cf47930b0f0a8138775b530baef2000abe792ad82ae7b2644f9c80bf7da1746a4119869a7f692021aedea51
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 Daniel de Carvalho Passarini
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Search Builder
|
2
|
+
|
3
|
+
A great and easy search builder to your rails project
|
4
|
+
|
5
|
+
### Instalação
|
6
|
+
|
7
|
+
Add it to your **Gemfile**:
|
8
|
+
```ruby
|
9
|
+
gem 'visual_condition_builder'
|
10
|
+
```
|
11
|
+
|
12
|
+
Rubn the following command to install it:
|
13
|
+
```sh
|
14
|
+
$ bundle install
|
15
|
+
```
|
16
|
+
|
17
|
+
Add it to your **app/assets/stylesheets/application.css**
|
18
|
+
```js
|
19
|
+
*= require visual_condition_builder
|
20
|
+
```
|
21
|
+
|
22
|
+
Add it to your **app/assets/javascripts/application.js**
|
23
|
+
```js
|
24
|
+
//= require visual_condition_builder
|
25
|
+
```
|
26
|
+
### Dependencies
|
27
|
+
|
28
|
+
Antes de iniciar o plugin você precisa ter o jQuery adicionado no seu application.js
|
29
|
+
|
30
|
+
Caso você tenha os seguintes plugins jQuery indicados no seu application.js, você deve remover:
|
31
|
+
|
32
|
+
- select2
|
33
|
+
- autoNumeric
|
34
|
+
|
35
|
+
|
36
|
+
### Select2 Languages
|
37
|
+
|
38
|
+
Select2 supports multiple languages by simply including the right language JS file (visual_condition_builder/select2_i18n/it, visual_condition_builder/select2_i18n/pt-BR, etc.) after visual_condition_builder in your **application.js**
|
39
|
+
```js
|
40
|
+
//= require visual_condition_builder
|
41
|
+
//= require visual_condition_builder/select2_i18n/pt-BR
|
42
|
+
```
|
43
|
+
|
44
|
+
## Como usar
|
45
|
+
|
46
|
+
Instancie em um elemento jQuery passando os parametros necessários:
|
47
|
+
|
48
|
+
```javascript
|
49
|
+
var builder = $('#div-exemplo-regras').ruleBuilder({
|
50
|
+
dicionario: [],
|
51
|
+
regras: [],
|
52
|
+
texto_instrucao: {
|
53
|
+
campos: 'Selecione um campo',
|
54
|
+
operadores: 'Selecione um operador',
|
55
|
+
valores: 'Informe um valor'
|
56
|
+
},
|
57
|
+
permite_brancos: true,
|
58
|
+
resultado: function(dados) {
|
59
|
+
//RETORNO DO RESULTADO EM JSON
|
60
|
+
}
|
61
|
+
});
|
62
|
+
```
|
63
|
+
|
64
|
+
Nos parâmetros dicionário e regras você pode informar diretamente um objeto com os dados ou informar uma url para carregar os dados. Atualmente o carregamento dessa fonte de dados é feita de forma síncrona);
|
65
|
+
```javascript
|
66
|
+
var dicionario = [{
|
67
|
+
"variavel": "nome_do_campo", //
|
68
|
+
"tipo": "STRING", //
|
69
|
+
"descricao": "descricao_do_campo", //
|
70
|
+
"operadores": [
|
71
|
+
{"operador": "simbolo_do_operador", "descricao": "texto_do_operador"},
|
72
|
+
{"operador": "=", "descricao": "Igual"},
|
73
|
+
{"operador": "EMPTY", "descricao": "Vazio", "sem_valor": "true"},
|
74
|
+
{"operador": "IN", "descricao": "Presente em", "multiplo": "true"},
|
75
|
+
{"operador": "BETWEEN", "descricao": "Entre", "multiplo": "2" }
|
76
|
+
],
|
77
|
+
"valores": [
|
78
|
+
{"id": "1", "label": "Um"},
|
79
|
+
{"id": "65465", "label": "José da Silva"},
|
80
|
+
{"id": "2016-06-15", "label": "Data de Hoje"},
|
81
|
+
{"id": "50.8", "label": "Valor Total"}
|
82
|
+
]
|
83
|
+
}];
|
84
|
+
```
|
85
|
+
### Parâmetros
|
86
|
+
|
87
|
+
#### **dicionario**
|
88
|
+
|
89
|
+
> Array de Hash: `[{},{},{}, ...]`
|
90
|
+
|
91
|
+
- *variavel*: Indica qual será o VALUE utilizado no `<option>` do `<select>` na coluna CAMPO
|
92
|
+
- *tipo*: Qual o tipo do valor do campo, esse tipo influência nos valores. Ex: Date mostra um datepicker no `<input>` do valor.
|
93
|
+
- STRING, DECIMAL(n), INTEGER, DATE, TIME
|
94
|
+
- *descricao*: Indica qual será o TEXT utilizado no `<option>` do `<select>` na coluna CAMPO
|
95
|
+
- *operadores*: Array de Hash com os operadores que esse campo possibilita selecionar.
|
96
|
+
- *operador*: Indica qual será o VALUE utilizado no `<option>` do `<select>` na coluna OPERADOR
|
97
|
+
- *descricao*: Indica qual será o TEXT utilizado no `<option>` do `<select>` na coluna OPERADOR
|
98
|
+
- *sem_valor*: Indica que esse operador não requer um valor, logo ele não será exibido.
|
99
|
+
- *multiplo*: Indica que o campo possibilita informar multiplos valores. Caso você passe um número para esse campo, esse número indicará quantas caixas de valores serão exibidas para esse campo.
|
100
|
+
- *valores*: É o Array dos valores que será exibidos para o usuário durante a seleção desse campo. Caso você não informe valores, será exibido um campo em aberto de acordo com o tipo selecionado. Também pode ser informado uma URL para carregar os dados diretamente dessa fonte.
|
101
|
+
- *id*: Indica qual será o VALUE utilizado no `<option>` do `<select>` na coluna VALOR
|
102
|
+
- *label*: Indica qual será o TEXT utilizado no `<option>` do `<select>` na coluna VALOR
|
103
|
+
- *Exemplo*: `"valores": [{"id": "S", "label": "Sim" }, {"id": "N", "label": "Não" }]`
|
104
|
+
|
105
|
+
#### **regras**
|
106
|
+
|
107
|
+
> Array de Array: `[[],[], ...]`
|
108
|
+
|
109
|
+
Esse parâmetro permite informar os dados que serão pré-selecionados ao carregar o plugin, é o estado inicial do plugin.
|
110
|
+
O array interno das regras sempre deverá ser informado com 3 elementos: `["CAMPO], "OPERADOR", "VALOR"]`, onde o valor poderá ser uma outra array caso esse campo permita seleção de múltiplos valores.
|
111
|
+
|
112
|
+
Exemplo:
|
113
|
+
|
114
|
+
```javascript
|
115
|
+
var regras = [
|
116
|
+
["nome_do_campo", "simbolo_do_operador", "id_do_valor"],
|
117
|
+
["campo_sim_ou_nao", "=", "S"],
|
118
|
+
["campo_decimal", "=", "88884.55"],
|
119
|
+
["campo_multiplo_2", "BETWEEN", ["2015-07-21","2015-07-30"]],
|
120
|
+
["campo_multiplo_true", "IN", ["A","B","C","D"]]
|
121
|
+
];
|
122
|
+
```
|
123
|
+
|
124
|
+
#### **permite_brancos**
|
125
|
+
|
126
|
+
> Boleano: true/false
|
127
|
+
|
128
|
+
Esse campo permite informar se as caixas de seleção irão ou não iniciar com valor "vazio" para que o usuário possa selecionar. Caso *false* seja informado o primeiro valor de cada coluna será pré-selecionado.
|
129
|
+
|
130
|
+
#### **resultado**
|
131
|
+
|
132
|
+
> Função: `function(dados){}`
|
133
|
+
|
134
|
+
Callback de retorno dos dados em JSON (type object), o retorno segue o mesmo padrão do parâmetro regra, logo o mesmo formato que você informa para pré-popular o plugin será o formato que o plugin retornará apóas as modificações do usuário.
|
135
|
+
|
136
|
+
Exemplo:
|
137
|
+
```javascript
|
138
|
+
var resultado = function(objJSON) {
|
139
|
+
//CONVERTO JSON EM STRING COMO EXEMPLO...
|
140
|
+
var stringJSON = JSON.stringify(objJSON);
|
141
|
+
}
|
142
|
+
```
|
143
|
+
|
144
|
+
Esse callback é chamado toda vez que o usuário faz uma modificação no plugin. Caso você queira forçar a recuperação desses dados através de um botão, você pode fazer:
|
145
|
+
```javascript
|
146
|
+
var builder = $('#div-exemplo-regras').ruleBuilder({
|
147
|
+
//...
|
148
|
+
});
|
149
|
+
|
150
|
+
$('#meu-botao').click(function(){
|
151
|
+
var objJSON = builder.getResultado();
|
152
|
+
//CONVERTO JSON EM STRING COMO EXEMPLO...
|
153
|
+
var stringJSON = JSON.stringify(objJSON);
|
154
|
+
});
|
155
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
# require 'rdoc/task'
|
8
|
+
# RDoc::Task.new(:rdoc) do |rdoc|
|
9
|
+
# rdoc.rdoc_dir = 'rdoc'
|
10
|
+
# rdoc.title = 'VisualConditionBuilder'
|
11
|
+
# rdoc.options << '--line-numbers'
|
12
|
+
# rdoc.rdoc_files.include('README.rdoc')
|
13
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
# end
|
15
|
+
|
16
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
17
|
+
load 'rails/tasks/engine.rake'
|
18
|
+
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
# require 'rake/testtask'
|
25
|
+
#
|
26
|
+
# Rake::TestTask.new(:test) do |t|
|
27
|
+
# t.libs << 'lib'
|
28
|
+
# t.libs << 'test'
|
29
|
+
# t.pattern = 'test/**/*_test.rb'
|
30
|
+
# t.verbose = false
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
#
|
34
|
+
# task default: :test
|
@@ -0,0 +1,1385 @@
|
|
1
|
+
/**!
|
2
|
+
* Sortable
|
3
|
+
* @author RubaXa <trash@rubaxa.org>
|
4
|
+
* @license MIT
|
5
|
+
* https://github.com/RubaXa/Sortable
|
6
|
+
*/
|
7
|
+
|
8
|
+
(function sortableModule(factory) {
|
9
|
+
"use strict";
|
10
|
+
|
11
|
+
if (typeof define === "function" && define.amd) {
|
12
|
+
define(factory);
|
13
|
+
}
|
14
|
+
else if (typeof module != "undefined" && typeof module.exports != "undefined") {
|
15
|
+
module.exports = factory();
|
16
|
+
}
|
17
|
+
else if (typeof Package !== "undefined") {
|
18
|
+
//noinspection JSUnresolvedVariable
|
19
|
+
Sortable = factory(); // export for Meteor.js
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
/* jshint sub:true */
|
23
|
+
window["Sortable"] = factory();
|
24
|
+
}
|
25
|
+
})(function sortableFactory() {
|
26
|
+
"use strict";
|
27
|
+
|
28
|
+
if (typeof window == "undefined" || !window.document) {
|
29
|
+
return function sortableError() {
|
30
|
+
throw new Error("Sortable.js requires a window with a document");
|
31
|
+
};
|
32
|
+
}
|
33
|
+
|
34
|
+
var dragEl,
|
35
|
+
parentEl,
|
36
|
+
ghostEl,
|
37
|
+
cloneEl,
|
38
|
+
rootEl,
|
39
|
+
nextEl,
|
40
|
+
|
41
|
+
scrollEl,
|
42
|
+
scrollParentEl,
|
43
|
+
scrollCustomFn,
|
44
|
+
|
45
|
+
lastEl,
|
46
|
+
lastCSS,
|
47
|
+
lastParentCSS,
|
48
|
+
|
49
|
+
oldIndex,
|
50
|
+
newIndex,
|
51
|
+
|
52
|
+
activeGroup,
|
53
|
+
putSortable,
|
54
|
+
|
55
|
+
autoScroll = {},
|
56
|
+
|
57
|
+
tapEvt,
|
58
|
+
touchEvt,
|
59
|
+
|
60
|
+
moved,
|
61
|
+
|
62
|
+
/** @const */
|
63
|
+
RSPACE = /\s+/g,
|
64
|
+
|
65
|
+
expando = 'Sortable' + (new Date).getTime(),
|
66
|
+
|
67
|
+
win = window,
|
68
|
+
document = win.document,
|
69
|
+
parseInt = win.parseInt,
|
70
|
+
|
71
|
+
$ = win.jQuery || win.Zepto,
|
72
|
+
Polymer = win.Polymer,
|
73
|
+
|
74
|
+
supportDraggable = !!('draggable' in document.createElement('div')),
|
75
|
+
supportCssPointerEvents = (function (el) {
|
76
|
+
// false when IE11
|
77
|
+
if (!!navigator.userAgent.match(/Trident.*rv[ :]?11\./)) {
|
78
|
+
return false;
|
79
|
+
}
|
80
|
+
el = document.createElement('x');
|
81
|
+
el.style.cssText = 'pointer-events:auto';
|
82
|
+
return el.style.pointerEvents === 'auto';
|
83
|
+
})(),
|
84
|
+
|
85
|
+
_silent = false,
|
86
|
+
|
87
|
+
abs = Math.abs,
|
88
|
+
min = Math.min,
|
89
|
+
slice = [].slice,
|
90
|
+
|
91
|
+
touchDragOverListeners = [],
|
92
|
+
|
93
|
+
_autoScroll = _throttle(function (/**Event*/evt, /**Object*/options, /**HTMLElement*/rootEl) {
|
94
|
+
// Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521
|
95
|
+
if (rootEl && options.scroll) {
|
96
|
+
var el,
|
97
|
+
rect,
|
98
|
+
sens = options.scrollSensitivity,
|
99
|
+
speed = options.scrollSpeed,
|
100
|
+
|
101
|
+
x = evt.clientX,
|
102
|
+
y = evt.clientY,
|
103
|
+
|
104
|
+
winWidth = window.innerWidth,
|
105
|
+
winHeight = window.innerHeight,
|
106
|
+
|
107
|
+
vx,
|
108
|
+
vy,
|
109
|
+
|
110
|
+
scrollOffsetX,
|
111
|
+
scrollOffsetY
|
112
|
+
;
|
113
|
+
|
114
|
+
// Delect scrollEl
|
115
|
+
if (scrollParentEl !== rootEl) {
|
116
|
+
scrollEl = options.scroll;
|
117
|
+
scrollParentEl = rootEl;
|
118
|
+
scrollCustomFn = options.scrollFn;
|
119
|
+
|
120
|
+
if (scrollEl === true) {
|
121
|
+
scrollEl = rootEl;
|
122
|
+
|
123
|
+
do {
|
124
|
+
if ((scrollEl.offsetWidth < scrollEl.scrollWidth) ||
|
125
|
+
(scrollEl.offsetHeight < scrollEl.scrollHeight)
|
126
|
+
) {
|
127
|
+
break;
|
128
|
+
}
|
129
|
+
/* jshint boss:true */
|
130
|
+
} while (scrollEl = scrollEl.parentNode);
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
if (scrollEl) {
|
135
|
+
el = scrollEl;
|
136
|
+
rect = scrollEl.getBoundingClientRect();
|
137
|
+
vx = (abs(rect.right - x) <= sens) - (abs(rect.left - x) <= sens);
|
138
|
+
vy = (abs(rect.bottom - y) <= sens) - (abs(rect.top - y) <= sens);
|
139
|
+
}
|
140
|
+
|
141
|
+
|
142
|
+
if (!(vx || vy)) {
|
143
|
+
vx = (winWidth - x <= sens) - (x <= sens);
|
144
|
+
vy = (winHeight - y <= sens) - (y <= sens);
|
145
|
+
|
146
|
+
/* jshint expr:true */
|
147
|
+
(vx || vy) && (el = win);
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
if (autoScroll.vx !== vx || autoScroll.vy !== vy || autoScroll.el !== el) {
|
152
|
+
autoScroll.el = el;
|
153
|
+
autoScroll.vx = vx;
|
154
|
+
autoScroll.vy = vy;
|
155
|
+
|
156
|
+
clearInterval(autoScroll.pid);
|
157
|
+
|
158
|
+
if (el) {
|
159
|
+
autoScroll.pid = setInterval(function () {
|
160
|
+
scrollOffsetY = vy ? vy * speed : 0;
|
161
|
+
scrollOffsetX = vx ? vx * speed : 0;
|
162
|
+
|
163
|
+
if ('function' === typeof(scrollCustomFn)) {
|
164
|
+
return scrollCustomFn.call(_this, scrollOffsetX, scrollOffsetY, evt);
|
165
|
+
}
|
166
|
+
|
167
|
+
if (el === win) {
|
168
|
+
win.scrollTo(win.pageXOffset + scrollOffsetX, win.pageYOffset + scrollOffsetY);
|
169
|
+
} else {
|
170
|
+
el.scrollTop += scrollOffsetY;
|
171
|
+
el.scrollLeft += scrollOffsetX;
|
172
|
+
}
|
173
|
+
}, 24);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}, 30),
|
178
|
+
|
179
|
+
_prepareGroup = function (options) {
|
180
|
+
function toFn(value, pull) {
|
181
|
+
if (value === void 0 || value === true) {
|
182
|
+
value = group.name;
|
183
|
+
}
|
184
|
+
|
185
|
+
if (typeof value === 'function') {
|
186
|
+
return value;
|
187
|
+
} else {
|
188
|
+
return function (to, from) {
|
189
|
+
var fromGroup = from.options.group.name;
|
190
|
+
|
191
|
+
return pull
|
192
|
+
? value
|
193
|
+
: value && (value.join
|
194
|
+
? value.indexOf(fromGroup) > -1
|
195
|
+
: (fromGroup == value)
|
196
|
+
);
|
197
|
+
};
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
var group = {};
|
202
|
+
var originalGroup = options.group;
|
203
|
+
|
204
|
+
if (!originalGroup || typeof originalGroup != 'object') {
|
205
|
+
originalGroup = {name: originalGroup};
|
206
|
+
}
|
207
|
+
|
208
|
+
group.name = originalGroup.name;
|
209
|
+
group.checkPull = toFn(originalGroup.pull, true);
|
210
|
+
group.checkPut = toFn(originalGroup.put);
|
211
|
+
|
212
|
+
options.group = group;
|
213
|
+
}
|
214
|
+
;
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
/**
|
219
|
+
* @class Sortable
|
220
|
+
* @param {HTMLElement} el
|
221
|
+
* @param {Object} [options]
|
222
|
+
*/
|
223
|
+
function Sortable(el, options) {
|
224
|
+
if (!(el && el.nodeType && el.nodeType === 1)) {
|
225
|
+
throw 'Sortable: `el` must be HTMLElement, and not ' + {}.toString.call(el);
|
226
|
+
}
|
227
|
+
|
228
|
+
this.el = el; // root element
|
229
|
+
this.options = options = _extend({}, options);
|
230
|
+
|
231
|
+
|
232
|
+
// Export instance
|
233
|
+
el[expando] = this;
|
234
|
+
|
235
|
+
|
236
|
+
// Default options
|
237
|
+
var defaults = {
|
238
|
+
group: Math.random(),
|
239
|
+
sort: true,
|
240
|
+
disabled: false,
|
241
|
+
store: null,
|
242
|
+
handle: null,
|
243
|
+
scroll: true,
|
244
|
+
scrollSensitivity: 30,
|
245
|
+
scrollSpeed: 10,
|
246
|
+
draggable: /[uo]l/i.test(el.nodeName) ? 'li' : '>*',
|
247
|
+
ghostClass: 'sortable-ghost',
|
248
|
+
chosenClass: 'sortable-chosen',
|
249
|
+
dragClass: 'sortable-drag',
|
250
|
+
ignore: 'a, img',
|
251
|
+
filter: null,
|
252
|
+
animation: 0,
|
253
|
+
setData: function (dataTransfer, dragEl) {
|
254
|
+
dataTransfer.setData('Text', dragEl.textContent);
|
255
|
+
},
|
256
|
+
dropBubble: false,
|
257
|
+
dragoverBubble: false,
|
258
|
+
dataIdAttr: 'data-id',
|
259
|
+
delay: 0,
|
260
|
+
forceFallback: false,
|
261
|
+
fallbackClass: 'sortable-fallback',
|
262
|
+
fallbackOnBody: false,
|
263
|
+
fallbackTolerance: 0,
|
264
|
+
fallbackOffset: {x: 0, y: 0}
|
265
|
+
};
|
266
|
+
|
267
|
+
|
268
|
+
// Set default options
|
269
|
+
for (var name in defaults) {
|
270
|
+
!(name in options) && (options[name] = defaults[name]);
|
271
|
+
}
|
272
|
+
|
273
|
+
_prepareGroup(options);
|
274
|
+
|
275
|
+
// Bind all private methods
|
276
|
+
for (var fn in this) {
|
277
|
+
if (fn.charAt(0) === '_' && typeof this[fn] === 'function') {
|
278
|
+
this[fn] = this[fn].bind(this);
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
// Setup drag mode
|
283
|
+
this.nativeDraggable = options.forceFallback ? false : supportDraggable;
|
284
|
+
|
285
|
+
// Bind events
|
286
|
+
_on(el, 'mousedown', this._onTapStart);
|
287
|
+
_on(el, 'touchstart', this._onTapStart);
|
288
|
+
_on(el, 'pointerdown', this._onTapStart);
|
289
|
+
|
290
|
+
if (this.nativeDraggable) {
|
291
|
+
_on(el, 'dragover', this);
|
292
|
+
_on(el, 'dragenter', this);
|
293
|
+
}
|
294
|
+
|
295
|
+
touchDragOverListeners.push(this._onDragOver);
|
296
|
+
|
297
|
+
// Restore sorting
|
298
|
+
options.store && this.sort(options.store.get(this));
|
299
|
+
}
|
300
|
+
|
301
|
+
|
302
|
+
Sortable.prototype = /** @lends Sortable.prototype */ {
|
303
|
+
constructor: Sortable,
|
304
|
+
|
305
|
+
_onTapStart: function (/** Event|TouchEvent */evt) {
|
306
|
+
var _this = this,
|
307
|
+
el = this.el,
|
308
|
+
options = this.options,
|
309
|
+
type = evt.type,
|
310
|
+
touch = evt.touches && evt.touches[0],
|
311
|
+
target = (touch || evt).target,
|
312
|
+
originalTarget = evt.target.shadowRoot && evt.path[0] || target,
|
313
|
+
filter = options.filter,
|
314
|
+
startIndex;
|
315
|
+
|
316
|
+
// Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group.
|
317
|
+
if (dragEl) {
|
318
|
+
return;
|
319
|
+
}
|
320
|
+
|
321
|
+
if (type === 'mousedown' && evt.button !== 0 || options.disabled) {
|
322
|
+
return; // only left button or enabled
|
323
|
+
}
|
324
|
+
|
325
|
+
if (options.handle && !_closest(originalTarget, options.handle, el)) {
|
326
|
+
return;
|
327
|
+
}
|
328
|
+
|
329
|
+
target = _closest(target, options.draggable, el);
|
330
|
+
|
331
|
+
if (!target) {
|
332
|
+
return;
|
333
|
+
}
|
334
|
+
|
335
|
+
// Get the index of the dragged element within its parent
|
336
|
+
startIndex = _index(target, options.draggable);
|
337
|
+
|
338
|
+
// Check filter
|
339
|
+
if (typeof filter === 'function') {
|
340
|
+
if (filter.call(this, evt, target, this)) {
|
341
|
+
_dispatchEvent(_this, originalTarget, 'filter', target, el, startIndex);
|
342
|
+
evt.preventDefault();
|
343
|
+
return; // cancel dnd
|
344
|
+
}
|
345
|
+
}
|
346
|
+
else if (filter) {
|
347
|
+
filter = filter.split(',').some(function (criteria) {
|
348
|
+
criteria = _closest(originalTarget, criteria.trim(), el);
|
349
|
+
|
350
|
+
if (criteria) {
|
351
|
+
_dispatchEvent(_this, criteria, 'filter', target, el, startIndex);
|
352
|
+
return true;
|
353
|
+
}
|
354
|
+
});
|
355
|
+
|
356
|
+
if (filter) {
|
357
|
+
evt.preventDefault();
|
358
|
+
return; // cancel dnd
|
359
|
+
}
|
360
|
+
}
|
361
|
+
|
362
|
+
// Prepare `dragstart`
|
363
|
+
this._prepareDragStart(evt, touch, target, startIndex);
|
364
|
+
},
|
365
|
+
|
366
|
+
_prepareDragStart: function (/** Event */evt, /** Touch */touch, /** HTMLElement */target, /** Number */startIndex) {
|
367
|
+
var _this = this,
|
368
|
+
el = _this.el,
|
369
|
+
options = _this.options,
|
370
|
+
ownerDocument = el.ownerDocument,
|
371
|
+
dragStartFn;
|
372
|
+
|
373
|
+
if (target && !dragEl && (target.parentNode === el)) {
|
374
|
+
tapEvt = evt;
|
375
|
+
|
376
|
+
rootEl = el;
|
377
|
+
dragEl = target;
|
378
|
+
parentEl = dragEl.parentNode;
|
379
|
+
nextEl = dragEl.nextSibling;
|
380
|
+
activeGroup = options.group;
|
381
|
+
oldIndex = startIndex;
|
382
|
+
|
383
|
+
this._lastX = (touch || evt).clientX;
|
384
|
+
this._lastY = (touch || evt).clientY;
|
385
|
+
|
386
|
+
dragEl.style['will-change'] = 'transform';
|
387
|
+
|
388
|
+
dragStartFn = function () {
|
389
|
+
// Delayed drag has been triggered
|
390
|
+
// we can re-enable the events: touchmove/mousemove
|
391
|
+
_this._disableDelayedDrag();
|
392
|
+
|
393
|
+
// Make the element draggable
|
394
|
+
dragEl.draggable = _this.nativeDraggable;
|
395
|
+
|
396
|
+
// Chosen item
|
397
|
+
_toggleClass(dragEl, options.chosenClass, true);
|
398
|
+
|
399
|
+
// Bind the events: dragstart/dragend
|
400
|
+
_this._triggerDragStart(evt, touch);
|
401
|
+
|
402
|
+
// Drag start event
|
403
|
+
_dispatchEvent(_this, rootEl, 'choose', dragEl, rootEl, oldIndex);
|
404
|
+
};
|
405
|
+
|
406
|
+
// Disable "draggable"
|
407
|
+
options.ignore.split(',').forEach(function (criteria) {
|
408
|
+
_find(dragEl, criteria.trim(), _disableDraggable);
|
409
|
+
});
|
410
|
+
|
411
|
+
_on(ownerDocument, 'mouseup', _this._onDrop);
|
412
|
+
_on(ownerDocument, 'touchend', _this._onDrop);
|
413
|
+
_on(ownerDocument, 'touchcancel', _this._onDrop);
|
414
|
+
_on(ownerDocument, 'pointercancel', _this._onDrop);
|
415
|
+
|
416
|
+
if (options.delay) {
|
417
|
+
// If the user moves the pointer or let go the click or touch
|
418
|
+
// before the delay has been reached:
|
419
|
+
// disable the delayed drag
|
420
|
+
_on(ownerDocument, 'mouseup', _this._disableDelayedDrag);
|
421
|
+
_on(ownerDocument, 'touchend', _this._disableDelayedDrag);
|
422
|
+
_on(ownerDocument, 'touchcancel', _this._disableDelayedDrag);
|
423
|
+
_on(ownerDocument, 'mousemove', _this._disableDelayedDrag);
|
424
|
+
_on(ownerDocument, 'touchmove', _this._disableDelayedDrag);
|
425
|
+
_on(ownerDocument, 'pointermove', _this._disableDelayedDrag);
|
426
|
+
|
427
|
+
_this._dragStartTimer = setTimeout(dragStartFn, options.delay);
|
428
|
+
} else {
|
429
|
+
dragStartFn();
|
430
|
+
}
|
431
|
+
}
|
432
|
+
},
|
433
|
+
|
434
|
+
_disableDelayedDrag: function () {
|
435
|
+
var ownerDocument = this.el.ownerDocument;
|
436
|
+
|
437
|
+
clearTimeout(this._dragStartTimer);
|
438
|
+
_off(ownerDocument, 'mouseup', this._disableDelayedDrag);
|
439
|
+
_off(ownerDocument, 'touchend', this._disableDelayedDrag);
|
440
|
+
_off(ownerDocument, 'touchcancel', this._disableDelayedDrag);
|
441
|
+
_off(ownerDocument, 'mousemove', this._disableDelayedDrag);
|
442
|
+
_off(ownerDocument, 'touchmove', this._disableDelayedDrag);
|
443
|
+
_off(ownerDocument, 'pointermove', this._disableDelayedDrag);
|
444
|
+
},
|
445
|
+
|
446
|
+
_triggerDragStart: function (/** Event */evt, /** Touch */touch) {
|
447
|
+
touch = touch || (evt.pointerType == 'touch' ? evt : null);
|
448
|
+
if (touch) {
|
449
|
+
// Touch device support
|
450
|
+
tapEvt = {
|
451
|
+
target: dragEl,
|
452
|
+
clientX: touch.clientX,
|
453
|
+
clientY: touch.clientY
|
454
|
+
};
|
455
|
+
|
456
|
+
this._onDragStart(tapEvt, 'touch');
|
457
|
+
}
|
458
|
+
else if (!this.nativeDraggable) {
|
459
|
+
this._onDragStart(tapEvt, true);
|
460
|
+
}
|
461
|
+
else {
|
462
|
+
_on(dragEl, 'dragend', this);
|
463
|
+
_on(rootEl, 'dragstart', this._onDragStart);
|
464
|
+
}
|
465
|
+
|
466
|
+
try {
|
467
|
+
if (document.selection) {
|
468
|
+
// Timeout neccessary for IE9
|
469
|
+
setTimeout(function () {
|
470
|
+
document.selection.empty();
|
471
|
+
});
|
472
|
+
} else {
|
473
|
+
window.getSelection().removeAllRanges();
|
474
|
+
}
|
475
|
+
} catch (err) {
|
476
|
+
}
|
477
|
+
},
|
478
|
+
|
479
|
+
_dragStarted: function () {
|
480
|
+
if (rootEl && dragEl) {
|
481
|
+
var options = this.options;
|
482
|
+
|
483
|
+
// Apply effect
|
484
|
+
_toggleClass(dragEl, options.ghostClass, true);
|
485
|
+
_toggleClass(dragEl, options.dragClass, false);
|
486
|
+
|
487
|
+
Sortable.active = this;
|
488
|
+
|
489
|
+
// Drag start event
|
490
|
+
_dispatchEvent(this, rootEl, 'start', dragEl, rootEl, oldIndex);
|
491
|
+
}
|
492
|
+
},
|
493
|
+
|
494
|
+
_emulateDragOver: function () {
|
495
|
+
if (touchEvt) {
|
496
|
+
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY) {
|
497
|
+
return;
|
498
|
+
}
|
499
|
+
|
500
|
+
this._lastX = touchEvt.clientX;
|
501
|
+
this._lastY = touchEvt.clientY;
|
502
|
+
|
503
|
+
if (!supportCssPointerEvents) {
|
504
|
+
_css(ghostEl, 'display', 'none');
|
505
|
+
}
|
506
|
+
|
507
|
+
var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY),
|
508
|
+
parent = target,
|
509
|
+
i = touchDragOverListeners.length;
|
510
|
+
|
511
|
+
if (parent) {
|
512
|
+
do {
|
513
|
+
if (parent[expando]) {
|
514
|
+
while (i--) {
|
515
|
+
touchDragOverListeners[i]({
|
516
|
+
clientX: touchEvt.clientX,
|
517
|
+
clientY: touchEvt.clientY,
|
518
|
+
target: target,
|
519
|
+
rootEl: parent
|
520
|
+
});
|
521
|
+
}
|
522
|
+
|
523
|
+
break;
|
524
|
+
}
|
525
|
+
|
526
|
+
target = parent; // store last element
|
527
|
+
}
|
528
|
+
/* jshint boss:true */
|
529
|
+
while (parent = parent.parentNode);
|
530
|
+
}
|
531
|
+
|
532
|
+
if (!supportCssPointerEvents) {
|
533
|
+
_css(ghostEl, 'display', '');
|
534
|
+
}
|
535
|
+
}
|
536
|
+
},
|
537
|
+
|
538
|
+
|
539
|
+
_onTouchMove: function (/**TouchEvent*/evt) {
|
540
|
+
if (tapEvt) {
|
541
|
+
var options = this.options,
|
542
|
+
fallbackTolerance = options.fallbackTolerance,
|
543
|
+
fallbackOffset = options.fallbackOffset,
|
544
|
+
touch = evt.touches ? evt.touches[0] : evt,
|
545
|
+
dx = (touch.clientX - tapEvt.clientX) + fallbackOffset.x,
|
546
|
+
dy = (touch.clientY - tapEvt.clientY) + fallbackOffset.y,
|
547
|
+
translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
|
548
|
+
|
549
|
+
// only set the status to dragging, when we are actually dragging
|
550
|
+
if (!Sortable.active) {
|
551
|
+
if (fallbackTolerance &&
|
552
|
+
min(abs(touch.clientX - this._lastX), abs(touch.clientY - this._lastY)) < fallbackTolerance
|
553
|
+
) {
|
554
|
+
return;
|
555
|
+
}
|
556
|
+
|
557
|
+
this._dragStarted();
|
558
|
+
}
|
559
|
+
|
560
|
+
// as well as creating the ghost element on the document body
|
561
|
+
this._appendGhost();
|
562
|
+
|
563
|
+
moved = true;
|
564
|
+
touchEvt = touch;
|
565
|
+
|
566
|
+
_css(ghostEl, 'webkitTransform', translate3d);
|
567
|
+
_css(ghostEl, 'mozTransform', translate3d);
|
568
|
+
_css(ghostEl, 'msTransform', translate3d);
|
569
|
+
_css(ghostEl, 'transform', translate3d);
|
570
|
+
|
571
|
+
evt.preventDefault();
|
572
|
+
}
|
573
|
+
},
|
574
|
+
|
575
|
+
_appendGhost: function () {
|
576
|
+
if (!ghostEl) {
|
577
|
+
var rect = dragEl.getBoundingClientRect(),
|
578
|
+
css = _css(dragEl),
|
579
|
+
options = this.options,
|
580
|
+
ghostRect;
|
581
|
+
|
582
|
+
ghostEl = dragEl.cloneNode(true);
|
583
|
+
|
584
|
+
_toggleClass(ghostEl, options.ghostClass, false);
|
585
|
+
_toggleClass(ghostEl, options.fallbackClass, true);
|
586
|
+
_toggleClass(ghostEl, options.dragClass, true);
|
587
|
+
|
588
|
+
_css(ghostEl, 'top', rect.top - parseInt(css.marginTop, 10));
|
589
|
+
_css(ghostEl, 'left', rect.left - parseInt(css.marginLeft, 10));
|
590
|
+
_css(ghostEl, 'width', rect.width);
|
591
|
+
_css(ghostEl, 'height', rect.height);
|
592
|
+
_css(ghostEl, 'opacity', '0.8');
|
593
|
+
_css(ghostEl, 'position', 'fixed');
|
594
|
+
_css(ghostEl, 'zIndex', '100000');
|
595
|
+
_css(ghostEl, 'pointerEvents', 'none');
|
596
|
+
|
597
|
+
options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
|
598
|
+
|
599
|
+
// Fixing dimensions.
|
600
|
+
ghostRect = ghostEl.getBoundingClientRect();
|
601
|
+
_css(ghostEl, 'width', rect.width * 2 - ghostRect.width);
|
602
|
+
_css(ghostEl, 'height', rect.height * 2 - ghostRect.height);
|
603
|
+
}
|
604
|
+
},
|
605
|
+
|
606
|
+
_onDragStart: function (/**Event*/evt, /**boolean*/useFallback) {
|
607
|
+
var dataTransfer = evt.dataTransfer,
|
608
|
+
options = this.options;
|
609
|
+
|
610
|
+
this._offUpEvents();
|
611
|
+
|
612
|
+
if (activeGroup.checkPull(this, this, dragEl, evt) == 'clone') {
|
613
|
+
cloneEl = _clone(dragEl);
|
614
|
+
_css(cloneEl, 'display', 'none');
|
615
|
+
rootEl.insertBefore(cloneEl, dragEl);
|
616
|
+
_dispatchEvent(this, rootEl, 'clone', dragEl);
|
617
|
+
}
|
618
|
+
|
619
|
+
_toggleClass(dragEl, options.dragClass, true);
|
620
|
+
|
621
|
+
if (useFallback) {
|
622
|
+
if (useFallback === 'touch') {
|
623
|
+
// Bind touch events
|
624
|
+
_on(document, 'touchmove', this._onTouchMove);
|
625
|
+
_on(document, 'touchend', this._onDrop);
|
626
|
+
_on(document, 'touchcancel', this._onDrop);
|
627
|
+
_on(document, 'pointermove', this._onTouchMove);
|
628
|
+
_on(document, 'pointerup', this._onDrop);
|
629
|
+
} else {
|
630
|
+
// Old brwoser
|
631
|
+
_on(document, 'mousemove', this._onTouchMove);
|
632
|
+
_on(document, 'mouseup', this._onDrop);
|
633
|
+
}
|
634
|
+
|
635
|
+
this._loopId = setInterval(this._emulateDragOver, 50);
|
636
|
+
}
|
637
|
+
else {
|
638
|
+
if (dataTransfer) {
|
639
|
+
dataTransfer.effectAllowed = 'move';
|
640
|
+
options.setData && options.setData.call(this, dataTransfer, dragEl);
|
641
|
+
}
|
642
|
+
|
643
|
+
_on(document, 'drop', this);
|
644
|
+
setTimeout(this._dragStarted, 0);
|
645
|
+
}
|
646
|
+
},
|
647
|
+
|
648
|
+
_onDragOver: function (/**Event*/evt) {
|
649
|
+
var el = this.el,
|
650
|
+
target,
|
651
|
+
dragRect,
|
652
|
+
targetRect,
|
653
|
+
revert,
|
654
|
+
options = this.options,
|
655
|
+
group = options.group,
|
656
|
+
activeSortable = Sortable.active,
|
657
|
+
isOwner = (activeGroup === group),
|
658
|
+
canSort = options.sort;
|
659
|
+
|
660
|
+
if (evt.preventDefault !== void 0) {
|
661
|
+
evt.preventDefault();
|
662
|
+
!options.dragoverBubble && evt.stopPropagation();
|
663
|
+
}
|
664
|
+
|
665
|
+
moved = true;
|
666
|
+
|
667
|
+
if (activeGroup && !options.disabled &&
|
668
|
+
(isOwner
|
669
|
+
? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list
|
670
|
+
: (
|
671
|
+
putSortable === this ||
|
672
|
+
activeGroup.checkPull(this, activeSortable, dragEl, evt) && group.checkPut(this, activeSortable, dragEl, evt)
|
673
|
+
)
|
674
|
+
) &&
|
675
|
+
(evt.rootEl === void 0 || evt.rootEl === this.el) // touch fallback
|
676
|
+
) {
|
677
|
+
// Smart auto-scrolling
|
678
|
+
_autoScroll(evt, options, this.el);
|
679
|
+
|
680
|
+
if (_silent) {
|
681
|
+
return;
|
682
|
+
}
|
683
|
+
|
684
|
+
target = _closest(evt.target, options.draggable, el);
|
685
|
+
dragRect = dragEl.getBoundingClientRect();
|
686
|
+
putSortable = this;
|
687
|
+
|
688
|
+
if (revert) {
|
689
|
+
_cloneHide(true);
|
690
|
+
parentEl = rootEl; // actualization
|
691
|
+
|
692
|
+
if (cloneEl || nextEl) {
|
693
|
+
rootEl.insertBefore(dragEl, cloneEl || nextEl);
|
694
|
+
}
|
695
|
+
else if (!canSort) {
|
696
|
+
rootEl.appendChild(dragEl);
|
697
|
+
}
|
698
|
+
|
699
|
+
return;
|
700
|
+
}
|
701
|
+
|
702
|
+
|
703
|
+
if ((el.children.length === 0) || (el.children[0] === ghostEl) ||
|
704
|
+
(el === evt.target) && (target = _ghostIsLast(el, evt))
|
705
|
+
) {
|
706
|
+
if (target) {
|
707
|
+
if (target.animated) {
|
708
|
+
return;
|
709
|
+
}
|
710
|
+
|
711
|
+
targetRect = target.getBoundingClientRect();
|
712
|
+
}
|
713
|
+
|
714
|
+
_cloneHide(isOwner);
|
715
|
+
|
716
|
+
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt) !== false) {
|
717
|
+
if (!dragEl.contains(el)) {
|
718
|
+
el.appendChild(dragEl);
|
719
|
+
parentEl = el; // actualization
|
720
|
+
}
|
721
|
+
|
722
|
+
this._animate(dragRect, dragEl);
|
723
|
+
target && this._animate(targetRect, target);
|
724
|
+
}
|
725
|
+
}
|
726
|
+
else if (target && !target.animated && target !== dragEl && (target.parentNode[expando] !== void 0)) {
|
727
|
+
if (lastEl !== target) {
|
728
|
+
lastEl = target;
|
729
|
+
lastCSS = _css(target);
|
730
|
+
lastParentCSS = _css(target.parentNode);
|
731
|
+
}
|
732
|
+
|
733
|
+
targetRect = target.getBoundingClientRect();
|
734
|
+
|
735
|
+
var width = targetRect.right - targetRect.left,
|
736
|
+
height = targetRect.bottom - targetRect.top,
|
737
|
+
floating = /left|right|inline/.test(lastCSS.cssFloat + lastCSS.display)
|
738
|
+
|| (lastParentCSS.display == 'flex' && lastParentCSS['flex-direction'].indexOf('row') === 0),
|
739
|
+
isWide = (target.offsetWidth > dragEl.offsetWidth),
|
740
|
+
isLong = (target.offsetHeight > dragEl.offsetHeight),
|
741
|
+
halfway = (floating ? (evt.clientX - targetRect.left) / width : (evt.clientY - targetRect.top) / height) > 0.5,
|
742
|
+
nextSibling = target.nextElementSibling,
|
743
|
+
moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt),
|
744
|
+
after
|
745
|
+
;
|
746
|
+
|
747
|
+
if (moveVector !== false) {
|
748
|
+
_silent = true;
|
749
|
+
setTimeout(_unsilent, 30);
|
750
|
+
|
751
|
+
_cloneHide(isOwner);
|
752
|
+
|
753
|
+
if (moveVector === 1 || moveVector === -1) {
|
754
|
+
after = (moveVector === 1);
|
755
|
+
}
|
756
|
+
else if (floating) {
|
757
|
+
var elTop = dragEl.offsetTop,
|
758
|
+
tgTop = target.offsetTop;
|
759
|
+
|
760
|
+
if (elTop === tgTop) {
|
761
|
+
after = (target.previousElementSibling === dragEl) && !isWide || halfway && isWide;
|
762
|
+
}
|
763
|
+
else if (target.previousElementSibling === dragEl || dragEl.previousElementSibling === target) {
|
764
|
+
after = (evt.clientY - targetRect.top) / height > 0.5;
|
765
|
+
} else {
|
766
|
+
after = tgTop > elTop;
|
767
|
+
}
|
768
|
+
} else {
|
769
|
+
after = (nextSibling !== dragEl) && !isLong || halfway && isLong;
|
770
|
+
}
|
771
|
+
|
772
|
+
if (!dragEl.contains(el)) {
|
773
|
+
if (after && !nextSibling) {
|
774
|
+
el.appendChild(dragEl);
|
775
|
+
} else {
|
776
|
+
target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
|
777
|
+
}
|
778
|
+
}
|
779
|
+
|
780
|
+
parentEl = dragEl.parentNode; // actualization
|
781
|
+
|
782
|
+
this._animate(dragRect, dragEl);
|
783
|
+
this._animate(targetRect, target);
|
784
|
+
}
|
785
|
+
}
|
786
|
+
}
|
787
|
+
},
|
788
|
+
|
789
|
+
_animate: function (prevRect, target) {
|
790
|
+
var ms = this.options.animation;
|
791
|
+
|
792
|
+
if (ms) {
|
793
|
+
var currentRect = target.getBoundingClientRect();
|
794
|
+
|
795
|
+
_css(target, 'transition', 'none');
|
796
|
+
_css(target, 'transform', 'translate3d('
|
797
|
+
+ (prevRect.left - currentRect.left) + 'px,'
|
798
|
+
+ (prevRect.top - currentRect.top) + 'px,0)'
|
799
|
+
);
|
800
|
+
|
801
|
+
target.offsetWidth; // repaint
|
802
|
+
|
803
|
+
_css(target, 'transition', 'all ' + ms + 'ms');
|
804
|
+
_css(target, 'transform', 'translate3d(0,0,0)');
|
805
|
+
|
806
|
+
clearTimeout(target.animated);
|
807
|
+
target.animated = setTimeout(function () {
|
808
|
+
_css(target, 'transition', '');
|
809
|
+
_css(target, 'transform', '');
|
810
|
+
target.animated = false;
|
811
|
+
}, ms);
|
812
|
+
}
|
813
|
+
},
|
814
|
+
|
815
|
+
_offUpEvents: function () {
|
816
|
+
var ownerDocument = this.el.ownerDocument;
|
817
|
+
|
818
|
+
_off(document, 'touchmove', this._onTouchMove);
|
819
|
+
_off(document, 'pointermove', this._onTouchMove);
|
820
|
+
_off(ownerDocument, 'mouseup', this._onDrop);
|
821
|
+
_off(ownerDocument, 'touchend', this._onDrop);
|
822
|
+
_off(ownerDocument, 'pointerup', this._onDrop);
|
823
|
+
_off(ownerDocument, 'touchcancel', this._onDrop);
|
824
|
+
},
|
825
|
+
|
826
|
+
_onDrop: function (/**Event*/evt) {
|
827
|
+
var el = this.el,
|
828
|
+
options = this.options;
|
829
|
+
|
830
|
+
clearInterval(this._loopId);
|
831
|
+
clearInterval(autoScroll.pid);
|
832
|
+
clearTimeout(this._dragStartTimer);
|
833
|
+
|
834
|
+
// Unbind events
|
835
|
+
_off(document, 'mousemove', this._onTouchMove);
|
836
|
+
|
837
|
+
if (this.nativeDraggable) {
|
838
|
+
_off(document, 'drop', this);
|
839
|
+
_off(el, 'dragstart', this._onDragStart);
|
840
|
+
}
|
841
|
+
|
842
|
+
this._offUpEvents();
|
843
|
+
|
844
|
+
if (evt) {
|
845
|
+
if (moved) {
|
846
|
+
evt.preventDefault();
|
847
|
+
!options.dropBubble && evt.stopPropagation();
|
848
|
+
}
|
849
|
+
|
850
|
+
ghostEl && ghostEl.parentNode.removeChild(ghostEl);
|
851
|
+
|
852
|
+
if (dragEl) {
|
853
|
+
if (this.nativeDraggable) {
|
854
|
+
_off(dragEl, 'dragend', this);
|
855
|
+
}
|
856
|
+
|
857
|
+
_disableDraggable(dragEl);
|
858
|
+
dragEl.style['will-change'] = '';
|
859
|
+
|
860
|
+
// Remove class's
|
861
|
+
_toggleClass(dragEl, this.options.ghostClass, false);
|
862
|
+
_toggleClass(dragEl, this.options.chosenClass, false);
|
863
|
+
|
864
|
+
if (rootEl !== parentEl) {
|
865
|
+
newIndex = _index(dragEl, options.draggable);
|
866
|
+
|
867
|
+
if (newIndex >= 0) {
|
868
|
+
|
869
|
+
// Add event
|
870
|
+
_dispatchEvent(null, parentEl, 'add', dragEl, rootEl, oldIndex, newIndex);
|
871
|
+
|
872
|
+
// Remove event
|
873
|
+
_dispatchEvent(this, rootEl, 'remove', dragEl, rootEl, oldIndex, newIndex);
|
874
|
+
|
875
|
+
// drag from one list and drop into another
|
876
|
+
_dispatchEvent(null, parentEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
|
877
|
+
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
|
878
|
+
}
|
879
|
+
}
|
880
|
+
else {
|
881
|
+
// Remove clone
|
882
|
+
cloneEl && cloneEl.parentNode.removeChild(cloneEl);
|
883
|
+
|
884
|
+
if (dragEl.nextSibling !== nextEl) {
|
885
|
+
// Get the index of the dragged element within its parent
|
886
|
+
newIndex = _index(dragEl, options.draggable);
|
887
|
+
|
888
|
+
if (newIndex >= 0) {
|
889
|
+
// drag & drop within the same list
|
890
|
+
_dispatchEvent(this, rootEl, 'update', dragEl, rootEl, oldIndex, newIndex);
|
891
|
+
_dispatchEvent(this, rootEl, 'sort', dragEl, rootEl, oldIndex, newIndex);
|
892
|
+
}
|
893
|
+
}
|
894
|
+
}
|
895
|
+
|
896
|
+
if (Sortable.active) {
|
897
|
+
/* jshint eqnull:true */
|
898
|
+
if (newIndex == null || newIndex === -1) {
|
899
|
+
newIndex = oldIndex;
|
900
|
+
}
|
901
|
+
|
902
|
+
_dispatchEvent(this, rootEl, 'end', dragEl, rootEl, oldIndex, newIndex);
|
903
|
+
|
904
|
+
// Save sorting
|
905
|
+
this.save();
|
906
|
+
}
|
907
|
+
}
|
908
|
+
|
909
|
+
}
|
910
|
+
|
911
|
+
this._nulling();
|
912
|
+
},
|
913
|
+
|
914
|
+
_nulling: function() {
|
915
|
+
rootEl =
|
916
|
+
dragEl =
|
917
|
+
parentEl =
|
918
|
+
ghostEl =
|
919
|
+
nextEl =
|
920
|
+
cloneEl =
|
921
|
+
|
922
|
+
scrollEl =
|
923
|
+
scrollParentEl =
|
924
|
+
|
925
|
+
tapEvt =
|
926
|
+
touchEvt =
|
927
|
+
|
928
|
+
moved =
|
929
|
+
newIndex =
|
930
|
+
|
931
|
+
lastEl =
|
932
|
+
lastCSS =
|
933
|
+
|
934
|
+
putSortable =
|
935
|
+
activeGroup =
|
936
|
+
Sortable.active = null;
|
937
|
+
},
|
938
|
+
|
939
|
+
handleEvent: function (/**Event*/evt) {
|
940
|
+
var type = evt.type;
|
941
|
+
|
942
|
+
if (type === 'dragover' || type === 'dragenter') {
|
943
|
+
if (dragEl) {
|
944
|
+
this._onDragOver(evt);
|
945
|
+
_globalDragOver(evt);
|
946
|
+
}
|
947
|
+
}
|
948
|
+
else if (type === 'drop' || type === 'dragend') {
|
949
|
+
this._onDrop(evt);
|
950
|
+
}
|
951
|
+
},
|
952
|
+
|
953
|
+
|
954
|
+
/**
|
955
|
+
* Serializes the item into an array of string.
|
956
|
+
* @returns {String[]}
|
957
|
+
*/
|
958
|
+
toArray: function () {
|
959
|
+
var order = [],
|
960
|
+
el,
|
961
|
+
children = this.el.children,
|
962
|
+
i = 0,
|
963
|
+
n = children.length,
|
964
|
+
options = this.options;
|
965
|
+
|
966
|
+
for (; i < n; i++) {
|
967
|
+
el = children[i];
|
968
|
+
if (_closest(el, options.draggable, this.el)) {
|
969
|
+
order.push(el.getAttribute(options.dataIdAttr) || _generateId(el));
|
970
|
+
}
|
971
|
+
}
|
972
|
+
|
973
|
+
return order;
|
974
|
+
},
|
975
|
+
|
976
|
+
|
977
|
+
/**
|
978
|
+
* Sorts the elements according to the array.
|
979
|
+
* @param {String[]} order order of the items
|
980
|
+
*/
|
981
|
+
sort: function (order) {
|
982
|
+
var items = {}, rootEl = this.el;
|
983
|
+
|
984
|
+
this.toArray().forEach(function (id, i) {
|
985
|
+
var el = rootEl.children[i];
|
986
|
+
|
987
|
+
if (_closest(el, this.options.draggable, rootEl)) {
|
988
|
+
items[id] = el;
|
989
|
+
}
|
990
|
+
}, this);
|
991
|
+
|
992
|
+
order.forEach(function (id) {
|
993
|
+
if (items[id]) {
|
994
|
+
rootEl.removeChild(items[id]);
|
995
|
+
rootEl.appendChild(items[id]);
|
996
|
+
}
|
997
|
+
});
|
998
|
+
},
|
999
|
+
|
1000
|
+
|
1001
|
+
/**
|
1002
|
+
* Save the current sorting
|
1003
|
+
*/
|
1004
|
+
save: function () {
|
1005
|
+
var store = this.options.store;
|
1006
|
+
store && store.set(this);
|
1007
|
+
},
|
1008
|
+
|
1009
|
+
|
1010
|
+
/**
|
1011
|
+
* For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
|
1012
|
+
* @param {HTMLElement} el
|
1013
|
+
* @param {String} [selector] default: `options.draggable`
|
1014
|
+
* @returns {HTMLElement|null}
|
1015
|
+
*/
|
1016
|
+
closest: function (el, selector) {
|
1017
|
+
return _closest(el, selector || this.options.draggable, this.el);
|
1018
|
+
},
|
1019
|
+
|
1020
|
+
|
1021
|
+
/**
|
1022
|
+
* Set/get option
|
1023
|
+
* @param {string} name
|
1024
|
+
* @param {*} [value]
|
1025
|
+
* @returns {*}
|
1026
|
+
*/
|
1027
|
+
option: function (name, value) {
|
1028
|
+
var options = this.options;
|
1029
|
+
|
1030
|
+
if (value === void 0) {
|
1031
|
+
return options[name];
|
1032
|
+
} else {
|
1033
|
+
options[name] = value;
|
1034
|
+
|
1035
|
+
if (name === 'group') {
|
1036
|
+
_prepareGroup(options);
|
1037
|
+
}
|
1038
|
+
}
|
1039
|
+
},
|
1040
|
+
|
1041
|
+
|
1042
|
+
/**
|
1043
|
+
* Destroy
|
1044
|
+
*/
|
1045
|
+
destroy: function () {
|
1046
|
+
var el = this.el;
|
1047
|
+
|
1048
|
+
el[expando] = null;
|
1049
|
+
|
1050
|
+
_off(el, 'mousedown', this._onTapStart);
|
1051
|
+
_off(el, 'touchstart', this._onTapStart);
|
1052
|
+
_off(el, 'pointerdown', this._onTapStart);
|
1053
|
+
|
1054
|
+
if (this.nativeDraggable) {
|
1055
|
+
_off(el, 'dragover', this);
|
1056
|
+
_off(el, 'dragenter', this);
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
// Remove draggable attributes
|
1060
|
+
Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) {
|
1061
|
+
el.removeAttribute('draggable');
|
1062
|
+
});
|
1063
|
+
|
1064
|
+
touchDragOverListeners.splice(touchDragOverListeners.indexOf(this._onDragOver), 1);
|
1065
|
+
|
1066
|
+
this._onDrop();
|
1067
|
+
|
1068
|
+
this.el = el = null;
|
1069
|
+
}
|
1070
|
+
};
|
1071
|
+
|
1072
|
+
|
1073
|
+
function _cloneHide(state) {
|
1074
|
+
if (cloneEl && (cloneEl.state !== state)) {
|
1075
|
+
_css(cloneEl, 'display', state ? 'none' : '');
|
1076
|
+
!state && cloneEl.state && rootEl.insertBefore(cloneEl, dragEl);
|
1077
|
+
cloneEl.state = state;
|
1078
|
+
}
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
|
1082
|
+
function _closest(/**HTMLElement*/el, /**String*/selector, /**HTMLElement*/ctx) {
|
1083
|
+
if (el) {
|
1084
|
+
ctx = ctx || document;
|
1085
|
+
|
1086
|
+
do {
|
1087
|
+
if ((selector === '>*' && el.parentNode === ctx) || _matches(el, selector)) {
|
1088
|
+
return el;
|
1089
|
+
}
|
1090
|
+
/* jshint boss:true */
|
1091
|
+
} while (el = _getParentOrHost(el));
|
1092
|
+
}
|
1093
|
+
|
1094
|
+
return null;
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
|
1098
|
+
function _getParentOrHost(el) {
|
1099
|
+
var parent = el.host;
|
1100
|
+
|
1101
|
+
return (parent && parent.nodeType) ? parent : el.parentNode;
|
1102
|
+
}
|
1103
|
+
|
1104
|
+
|
1105
|
+
function _globalDragOver(/**Event*/evt) {
|
1106
|
+
if (evt.dataTransfer) {
|
1107
|
+
evt.dataTransfer.dropEffect = 'move';
|
1108
|
+
}
|
1109
|
+
evt.preventDefault();
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
|
1113
|
+
function _on(el, event, fn) {
|
1114
|
+
el.addEventListener(event, fn, false);
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
|
1118
|
+
function _off(el, event, fn) {
|
1119
|
+
el.removeEventListener(event, fn, false);
|
1120
|
+
}
|
1121
|
+
|
1122
|
+
|
1123
|
+
function _toggleClass(el, name, state) {
|
1124
|
+
if (el) {
|
1125
|
+
if (el.classList) {
|
1126
|
+
el.classList[state ? 'add' : 'remove'](name);
|
1127
|
+
}
|
1128
|
+
else {
|
1129
|
+
var className = (' ' + el.className + ' ').replace(RSPACE, ' ').replace(' ' + name + ' ', ' ');
|
1130
|
+
el.className = (className + (state ? ' ' + name : '')).replace(RSPACE, ' ');
|
1131
|
+
}
|
1132
|
+
}
|
1133
|
+
}
|
1134
|
+
|
1135
|
+
|
1136
|
+
function _css(el, prop, val) {
|
1137
|
+
var style = el && el.style;
|
1138
|
+
|
1139
|
+
if (style) {
|
1140
|
+
if (val === void 0) {
|
1141
|
+
if (document.defaultView && document.defaultView.getComputedStyle) {
|
1142
|
+
val = document.defaultView.getComputedStyle(el, '');
|
1143
|
+
}
|
1144
|
+
else if (el.currentStyle) {
|
1145
|
+
val = el.currentStyle;
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
return prop === void 0 ? val : val[prop];
|
1149
|
+
}
|
1150
|
+
else {
|
1151
|
+
if (!(prop in style)) {
|
1152
|
+
prop = '-webkit-' + prop;
|
1153
|
+
}
|
1154
|
+
|
1155
|
+
style[prop] = val + (typeof val === 'string' ? '' : 'px');
|
1156
|
+
}
|
1157
|
+
}
|
1158
|
+
}
|
1159
|
+
|
1160
|
+
|
1161
|
+
function _find(ctx, tagName, iterator) {
|
1162
|
+
if (ctx) {
|
1163
|
+
var list = ctx.getElementsByTagName(tagName), i = 0, n = list.length;
|
1164
|
+
|
1165
|
+
if (iterator) {
|
1166
|
+
for (; i < n; i++) {
|
1167
|
+
iterator(list[i], i);
|
1168
|
+
}
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
return list;
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
return [];
|
1175
|
+
}
|
1176
|
+
|
1177
|
+
|
1178
|
+
|
1179
|
+
function _dispatchEvent(sortable, rootEl, name, targetEl, fromEl, startIndex, newIndex) {
|
1180
|
+
sortable = (sortable || rootEl[expando]);
|
1181
|
+
|
1182
|
+
var evt = document.createEvent('Event'),
|
1183
|
+
options = sortable.options,
|
1184
|
+
onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1);
|
1185
|
+
|
1186
|
+
evt.initEvent(name, true, true);
|
1187
|
+
|
1188
|
+
evt.to = rootEl;
|
1189
|
+
evt.from = fromEl || rootEl;
|
1190
|
+
evt.item = targetEl || rootEl;
|
1191
|
+
evt.clone = cloneEl;
|
1192
|
+
|
1193
|
+
evt.oldIndex = startIndex;
|
1194
|
+
evt.newIndex = newIndex;
|
1195
|
+
|
1196
|
+
rootEl.dispatchEvent(evt);
|
1197
|
+
|
1198
|
+
if (options[onName]) {
|
1199
|
+
options[onName].call(sortable, evt);
|
1200
|
+
}
|
1201
|
+
}
|
1202
|
+
|
1203
|
+
|
1204
|
+
function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvt) {
|
1205
|
+
var evt,
|
1206
|
+
sortable = fromEl[expando],
|
1207
|
+
onMoveFn = sortable.options.onMove,
|
1208
|
+
retVal;
|
1209
|
+
|
1210
|
+
evt = document.createEvent('Event');
|
1211
|
+
evt.initEvent('move', true, true);
|
1212
|
+
|
1213
|
+
evt.to = toEl;
|
1214
|
+
evt.from = fromEl;
|
1215
|
+
evt.dragged = dragEl;
|
1216
|
+
evt.draggedRect = dragRect;
|
1217
|
+
evt.related = targetEl || toEl;
|
1218
|
+
evt.relatedRect = targetRect || toEl.getBoundingClientRect();
|
1219
|
+
|
1220
|
+
fromEl.dispatchEvent(evt);
|
1221
|
+
|
1222
|
+
if (onMoveFn) {
|
1223
|
+
retVal = onMoveFn.call(sortable, evt, originalEvt);
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
return retVal;
|
1227
|
+
}
|
1228
|
+
|
1229
|
+
|
1230
|
+
function _disableDraggable(el) {
|
1231
|
+
el.draggable = false;
|
1232
|
+
}
|
1233
|
+
|
1234
|
+
|
1235
|
+
function _unsilent() {
|
1236
|
+
_silent = false;
|
1237
|
+
}
|
1238
|
+
|
1239
|
+
|
1240
|
+
/** @returns {HTMLElement|false} */
|
1241
|
+
function _ghostIsLast(el, evt) {
|
1242
|
+
var lastEl = el.lastElementChild,
|
1243
|
+
rect = lastEl.getBoundingClientRect();
|
1244
|
+
|
1245
|
+
// 5 — min delta
|
1246
|
+
// abs — нельзя добавлять, а то глюки при наведении сверху
|
1247
|
+
return (
|
1248
|
+
(evt.clientY - (rect.top + rect.height) > 5) ||
|
1249
|
+
(evt.clientX - (rect.right + rect.width) > 5)
|
1250
|
+
) && lastEl;
|
1251
|
+
}
|
1252
|
+
|
1253
|
+
|
1254
|
+
/**
|
1255
|
+
* Generate id
|
1256
|
+
* @param {HTMLElement} el
|
1257
|
+
* @returns {String}
|
1258
|
+
* @private
|
1259
|
+
*/
|
1260
|
+
function _generateId(el) {
|
1261
|
+
var str = el.tagName + el.className + el.src + el.href + el.textContent,
|
1262
|
+
i = str.length,
|
1263
|
+
sum = 0;
|
1264
|
+
|
1265
|
+
while (i--) {
|
1266
|
+
sum += str.charCodeAt(i);
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
return sum.toString(36);
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
/**
|
1273
|
+
* Returns the index of an element within its parent for a selected set of
|
1274
|
+
* elements
|
1275
|
+
* @param {HTMLElement} el
|
1276
|
+
* @param {selector} selector
|
1277
|
+
* @return {number}
|
1278
|
+
*/
|
1279
|
+
function _index(el, selector) {
|
1280
|
+
var index = 0;
|
1281
|
+
|
1282
|
+
if (!el || !el.parentNode) {
|
1283
|
+
return -1;
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
while (el && (el = el.previousElementSibling)) {
|
1287
|
+
if ((el.nodeName.toUpperCase() !== 'TEMPLATE') && (selector === '>*' || _matches(el, selector))) {
|
1288
|
+
index++;
|
1289
|
+
}
|
1290
|
+
}
|
1291
|
+
|
1292
|
+
return index;
|
1293
|
+
}
|
1294
|
+
|
1295
|
+
function _matches(/**HTMLElement*/el, /**String*/selector) {
|
1296
|
+
if (el) {
|
1297
|
+
selector = selector.split('.');
|
1298
|
+
|
1299
|
+
var tag = selector.shift().toUpperCase(),
|
1300
|
+
re = new RegExp('\\s(' + selector.join('|') + ')(?=\\s)', 'g');
|
1301
|
+
|
1302
|
+
return (
|
1303
|
+
(tag === '' || el.nodeName.toUpperCase() == tag) &&
|
1304
|
+
(!selector.length || ((' ' + el.className + ' ').match(re) || []).length == selector.length)
|
1305
|
+
);
|
1306
|
+
}
|
1307
|
+
|
1308
|
+
return false;
|
1309
|
+
}
|
1310
|
+
|
1311
|
+
function _throttle(callback, ms) {
|
1312
|
+
var args, _this;
|
1313
|
+
|
1314
|
+
return function () {
|
1315
|
+
if (args === void 0) {
|
1316
|
+
args = arguments;
|
1317
|
+
_this = this;
|
1318
|
+
|
1319
|
+
setTimeout(function () {
|
1320
|
+
if (args.length === 1) {
|
1321
|
+
callback.call(_this, args[0]);
|
1322
|
+
} else {
|
1323
|
+
callback.apply(_this, args);
|
1324
|
+
}
|
1325
|
+
|
1326
|
+
args = void 0;
|
1327
|
+
}, ms);
|
1328
|
+
}
|
1329
|
+
};
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
function _extend(dst, src) {
|
1333
|
+
if (dst && src) {
|
1334
|
+
for (var key in src) {
|
1335
|
+
if (src.hasOwnProperty(key)) {
|
1336
|
+
dst[key] = src[key];
|
1337
|
+
}
|
1338
|
+
}
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
return dst;
|
1342
|
+
}
|
1343
|
+
|
1344
|
+
function _clone(el) {
|
1345
|
+
return $
|
1346
|
+
? $(el).clone(true)[0]
|
1347
|
+
: (Polymer && Polymer.dom
|
1348
|
+
? Polymer.dom(el).cloneNode(true)
|
1349
|
+
: el.cloneNode(true)
|
1350
|
+
);
|
1351
|
+
}
|
1352
|
+
|
1353
|
+
|
1354
|
+
// Export utils
|
1355
|
+
Sortable.utils = {
|
1356
|
+
on: _on,
|
1357
|
+
off: _off,
|
1358
|
+
css: _css,
|
1359
|
+
find: _find,
|
1360
|
+
is: function (el, selector) {
|
1361
|
+
return !!_closest(el, selector, el);
|
1362
|
+
},
|
1363
|
+
extend: _extend,
|
1364
|
+
throttle: _throttle,
|
1365
|
+
closest: _closest,
|
1366
|
+
toggleClass: _toggleClass,
|
1367
|
+
clone: _clone,
|
1368
|
+
index: _index
|
1369
|
+
};
|
1370
|
+
|
1371
|
+
|
1372
|
+
/**
|
1373
|
+
* Create sortable instance
|
1374
|
+
* @param {HTMLElement} el
|
1375
|
+
* @param {Object} [options]
|
1376
|
+
*/
|
1377
|
+
Sortable.create = function (el, options) {
|
1378
|
+
return new Sortable(el, options);
|
1379
|
+
};
|
1380
|
+
|
1381
|
+
|
1382
|
+
// Export
|
1383
|
+
Sortable.version = '1.5.0-rc1';
|
1384
|
+
return Sortable;
|
1385
|
+
});
|