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.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +155 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/visual_condition_builder/Sortable.js +1385 -0
  6. data/app/assets/javascripts/visual_condition_builder/autoNumeric-2.0-BETA.js +2156 -0
  7. data/app/assets/javascripts/visual_condition_builder/autonumeric_ujs.js +94 -0
  8. data/app/assets/javascripts/visual_condition_builder/condition_builder.js +633 -0
  9. data/app/assets/javascripts/visual_condition_builder/diacritics.js +116 -0
  10. data/app/assets/javascripts/visual_condition_builder/select2.full.js +6436 -0
  11. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ar.js +3 -0
  12. data/app/assets/javascripts/visual_condition_builder/select2_i18n/az.js +3 -0
  13. data/app/assets/javascripts/visual_condition_builder/select2_i18n/bg.js +3 -0
  14. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ca.js +3 -0
  15. data/app/assets/javascripts/visual_condition_builder/select2_i18n/cs.js +3 -0
  16. data/app/assets/javascripts/visual_condition_builder/select2_i18n/da.js +3 -0
  17. data/app/assets/javascripts/visual_condition_builder/select2_i18n/de.js +3 -0
  18. data/app/assets/javascripts/visual_condition_builder/select2_i18n/el.js +3 -0
  19. data/app/assets/javascripts/visual_condition_builder/select2_i18n/en.js +3 -0
  20. data/app/assets/javascripts/visual_condition_builder/select2_i18n/es.js +3 -0
  21. data/app/assets/javascripts/visual_condition_builder/select2_i18n/et.js +3 -0
  22. data/app/assets/javascripts/visual_condition_builder/select2_i18n/eu.js +3 -0
  23. data/app/assets/javascripts/visual_condition_builder/select2_i18n/fa.js +3 -0
  24. data/app/assets/javascripts/visual_condition_builder/select2_i18n/fi.js +3 -0
  25. data/app/assets/javascripts/visual_condition_builder/select2_i18n/fr.js +3 -0
  26. data/app/assets/javascripts/visual_condition_builder/select2_i18n/gl.js +3 -0
  27. data/app/assets/javascripts/visual_condition_builder/select2_i18n/he.js +3 -0
  28. data/app/assets/javascripts/visual_condition_builder/select2_i18n/hi.js +3 -0
  29. data/app/assets/javascripts/visual_condition_builder/select2_i18n/hr.js +3 -0
  30. data/app/assets/javascripts/visual_condition_builder/select2_i18n/hu.js +3 -0
  31. data/app/assets/javascripts/visual_condition_builder/select2_i18n/id.js +3 -0
  32. data/app/assets/javascripts/visual_condition_builder/select2_i18n/is.js +3 -0
  33. data/app/assets/javascripts/visual_condition_builder/select2_i18n/it.js +3 -0
  34. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ja.js +3 -0
  35. data/app/assets/javascripts/visual_condition_builder/select2_i18n/km.js +3 -0
  36. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ko.js +3 -0
  37. data/app/assets/javascripts/visual_condition_builder/select2_i18n/lt.js +3 -0
  38. data/app/assets/javascripts/visual_condition_builder/select2_i18n/lv.js +3 -0
  39. data/app/assets/javascripts/visual_condition_builder/select2_i18n/mk.js +3 -0
  40. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ms.js +3 -0
  41. data/app/assets/javascripts/visual_condition_builder/select2_i18n/nb.js +3 -0
  42. data/app/assets/javascripts/visual_condition_builder/select2_i18n/nl.js +3 -0
  43. data/app/assets/javascripts/visual_condition_builder/select2_i18n/pl.js +3 -0
  44. data/app/assets/javascripts/visual_condition_builder/select2_i18n/pt-BR.js +3 -0
  45. data/app/assets/javascripts/visual_condition_builder/select2_i18n/pt.js +3 -0
  46. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ro.js +3 -0
  47. data/app/assets/javascripts/visual_condition_builder/select2_i18n/ru.js +3 -0
  48. data/app/assets/javascripts/visual_condition_builder/select2_i18n/sk.js +3 -0
  49. data/app/assets/javascripts/visual_condition_builder/select2_i18n/sr-Cyrl.js +3 -0
  50. data/app/assets/javascripts/visual_condition_builder/select2_i18n/sr.js +3 -0
  51. data/app/assets/javascripts/visual_condition_builder/select2_i18n/sv.js +3 -0
  52. data/app/assets/javascripts/visual_condition_builder/select2_i18n/th.js +3 -0
  53. data/app/assets/javascripts/visual_condition_builder/select2_i18n/tr.js +3 -0
  54. data/app/assets/javascripts/visual_condition_builder/select2_i18n/uk.js +3 -0
  55. data/app/assets/javascripts/visual_condition_builder/select2_i18n/vi.js +3 -0
  56. data/app/assets/javascripts/visual_condition_builder/select2_i18n/zh-CN.js +3 -0
  57. data/app/assets/javascripts/visual_condition_builder/select2_i18n/zh-TW.js +3 -0
  58. data/app/assets/javascripts/visual_condition_builder.js +6 -0
  59. data/app/assets/stylesheets/visual_condition_builder/condition_builder.css +87 -0
  60. data/app/assets/stylesheets/visual_condition_builder/select2-bootstrap.css +722 -0
  61. data/app/assets/stylesheets/visual_condition_builder/select2.css +484 -0
  62. data/app/assets/stylesheets/visual_condition_builder.css +5 -0
  63. data/app/controllers/application_widget.rb +56 -0
  64. data/app/controllers/visual_condition_builder/application_controller.rb +5 -0
  65. data/app/controllers/visual_condition_builder/widgets_controller.rb +45 -0
  66. data/app/helpers/visual_condition_builder/application_helper.rb +74 -0
  67. data/app/models/visual_condition_builder/user.rb +10 -0
  68. data/app/views/visual_condition_builder/widgets/_widgets_list.html.erb +24 -0
  69. data/app/views/visual_condition_builder/widgets/index.html.erb +25 -0
  70. data/config/initializers/assets.rb +1 -0
  71. data/config/initializers/visual_condition_builder.rb +7 -0
  72. data/config/routes.rb +19 -0
  73. data/lib/generators/templates/create_taxweb_widgets_users.rb +18 -0
  74. data/lib/generators/templates/generic_widget.erb +12 -0
  75. data/lib/generators/templates/generic_widget.html.erb +2 -0
  76. data/lib/generators/visual_condition_builder/install_generator.rb +26 -0
  77. data/lib/generators/visual_condition_builder/view_generator.rb +11 -0
  78. data/lib/generators/visual_condition_builder/widget_generator.rb +25 -0
  79. data/lib/visual_condition_builder/converter.rb +22 -0
  80. data/lib/visual_condition_builder/dictionary.rb +166 -0
  81. data/lib/visual_condition_builder/engine.rb +6 -0
  82. data/lib/visual_condition_builder/helper.rb +19 -0
  83. data/lib/visual_condition_builder/version.rb +3 -0
  84. data/lib/visual_condition_builder.rb +9 -0
  85. 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
+ });