draggablesilder-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA512:
3
+ data.tar.gz: cf6f40efde064f40250eebcd3fd4bc52f0ebe3671d5fa532e526e9112d382a754e0ac7b2affffe84d8e0e798e4eeb67f7759ae3e95a0a7e233755483be5d7755
4
+ metadata.gz: 06349ffab7de890d11df2e31632129ef8bbd5f66a04b13e46df035c0b130fb70e7d1e151559c0fc8aa04467b87a8244d3a2f83c6b47f1549749c6c5fa433b6af
5
+ SHA1:
6
+ data.tar.gz: d29f7cc335d58da149b1b152169d3bb4c07e61dd
7
+ metadata.gz: 59acf441fb3f8cdde19fd0333245c4551081db30
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in draggablesilder-rails.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 producao02
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # draggablesilder-rails
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
3
+ <svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ x="0px" y="0px" width="128px" height="128px" viewBox="0 0 128 128" xml:space="preserve">
5
+ <circle fill="none" stroke="#ffffff" stroke-width="1" stroke-miterlimit="10" stroke-dasharray="2,2" cx="59.5" cy="34.5" r="14.5"/>
6
+ <g>
7
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="13" y1="32" x2="37" y2="32"/>
8
+ <g>
9
+ <polygon points="19.796,40.18 21.16,38.715 13.937,32.002 21.16,25.287 19.796,23.822 11,32.002" fill="#ffffff"/>
10
+ </g>
11
+ </g>
12
+ <g>
13
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="82" y1="32" x2="106" y2="32"/>
14
+ <g>
15
+ <polygon points="99.204,40.18 97.84,38.715 105.063,32.002 97.84,25.287 99.204,23.822 108,32.002" fill="#ffffff"/>
16
+ </g>
17
+ </g>
18
+ <polygon stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" fill="rgba(255,255,255,0.1)" points="53,31 53,74 40,56 30,63 49,106 98,106
19
+ 98,65 70,65 67,65 67,31 "/>
20
+ </svg>
@@ -0,0 +1,45 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
3
+ <svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ x="0px" y="0px" width="128px" height="128px" viewBox="0 0 128 128" xml:space="preserve">
5
+ <g>
6
+ <g>
7
+ <g>
8
+ <g>
9
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="85" y1="64" x2="100" y2="64"/>
10
+ <g>
11
+ <polygon points="93.763,71.971 92.398,70.506 99.622,63.793 92.398,57.078 93.763,55.613 102.559,63.793" fill="#ffffff"/>
12
+ </g>
13
+ </g>
14
+ <rect x="71" y="42" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" width="45" height="44"/>
15
+ </g>
16
+ <g>
17
+ <g>
18
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="44" y1="64" x2="29" y2="64"/>
19
+ <g>
20
+ <polygon points="35.237,55.611 36.602,57.076 29.378,63.789 36.602,70.504 35.237,71.969 26.441,63.789" fill="#ffffff"/>
21
+ </g>
22
+ </g>
23
+ <rect x="12" y="42" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" width="45" height="44"/>
24
+ </g>
25
+ </g>
26
+ <g>
27
+ <g>
28
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="64" y1="37" x2="64" y2="23"/>
29
+ <g>
30
+ <polygon points="72.471,29.796 71.006,31.16 64.293,23.937 57.578,31.16 56.113,29.796 64.293,21" fill="#ffffff"/>
31
+ </g>
32
+ </g>
33
+ <polyline fill="none" stroke="#ffffff" stroke-width="0.5" stroke-miterlimit="10" points="33,38 33,15 95,15 95,38"/>
34
+ </g>
35
+ <g>
36
+ <g>
37
+ <line fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" x1="64" y1="91" x2="64" y2="105"/>
38
+ <g>
39
+ <polygon points="56.111,98.204 57.576,96.84 64.289,104.063 71.004,96.84 72.469,98.204 64.289,107" fill="#ffffff"/>
40
+ </g>
41
+ </g>
42
+ <polyline fill="none" stroke="#ffffff" stroke-width="0.5" stroke-miterlimit="10" points="33,90 33,113 95,113 95,90"/>
43
+ </g>
44
+ </g>
45
+ </svg>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
3
+ <svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ x="0px" y="0px" width="90px" height="100px" viewBox="0 0 90 100" xml:space="preserve">
5
+ <rect x="12.35" y="22.792" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" width="65.301" height="54.416"/>
6
+ <g>
7
+ <line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" x1="50.166" y1="35" x2="38.75" y2="35"/>
8
+ <g>
9
+ <polygon fill="#FFFFFF" points="46.852,30.91 46.17,31.643 49.781,34.999 46.17,38.356 46.852,39.089 51.25,34.999"/>
10
+ </g>
11
+ </g>
12
+ <g>
13
+ <line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" x1="51.25" y1="66" x2="39.834" y2="66"/>
14
+ <g>
15
+ <polygon fill="#FFFFFF" points="43.148,61.91 43.83,62.643 40.218,65.999 43.83,69.356 43.148,70.089 38.75,65.999"/>
16
+ </g>
17
+ </g>
18
+ <circle fill="none" stroke="#FFFFFF" stroke-width="2" stroke-miterlimit="10" cx="45" cy="50" r="43.5"/>
19
+ </svg>
@@ -0,0 +1,23 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
3
+ <svg version="1.1" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ x="0px" y="0px" width="90px" height="100px" viewBox="0 0 90 100" xml:space="preserve">
5
+ <rect x="25.25" y="33.542" fill="none" stroke-width="2" stroke="#ffffff" stroke-miterlimit="10" width="39.5" height="32.916"/>
6
+ <path fill="none" stroke="#ffffff" stroke-miterlimit="10" d="M85.269,33.542H64.75v32.916h20.519C87.346,61.38,88.5,55.826,88.5,50
7
+ C88.5,44.174,87.346,38.62,85.269,33.542z"/>
8
+ <path fill="none" stroke="#ffffff" stroke-miterlimit="10" d="M25.25,33.542H4.731C2.654,38.62,1.5,44.174,1.5,50
9
+ c0,5.826,1.154,11.38,3.231,16.458H25.25V33.542z"/>
10
+ <g>
11
+ <line fill="none" stroke-width="2" stroke="#ffffff" stroke-miterlimit="10" x1="45" y1="74.584" x2="45" y2="86"/>
12
+ <g>
13
+ <polygon points="40.91,77.898 41.643,78.58 44.999,74.969 48.356,78.58 49.089,77.898 44.999,73.5" fill="#ffffff"/>
14
+ </g>
15
+ </g>
16
+ <g>
17
+ <line fill="none" stroke-width="2" stroke="#ffffff" stroke-miterlimit="10" x1="45" y1="13" x2="45" y2="24.416"/>
18
+ <g>
19
+ <polygon points="40.91,21.102 41.643,20.42 44.999,24.032 48.356,20.42 49.089,21.102 44.999,25.5" fill="#ffffff"/>
20
+ </g>
21
+ </g>
22
+ <circle fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" cx="45" cy="50" r="43.5"/>
23
+ </svg>
@@ -0,0 +1,80 @@
1
+ /*!
2
+ * classie - class helper functions
3
+ * from bonzo https://github.com/ded/bonzo
4
+ *
5
+ * classie.has( elem, 'my-class' ) -> true/false
6
+ * classie.add( elem, 'my-new-class' )
7
+ * classie.remove( elem, 'my-unwanted-class' )
8
+ * classie.toggle( elem, 'my-class' )
9
+ */
10
+
11
+ /*jshint browser: true, strict: true, undef: true */
12
+ /*global define: false */
13
+
14
+ ( function( window ) {
15
+
16
+ 'use strict';
17
+
18
+ // class helper functions from bonzo https://github.com/ded/bonzo
19
+
20
+ function classReg( className ) {
21
+ return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
22
+ }
23
+
24
+ // classList support for class management
25
+ // altho to be fair, the api sucks because it won't accept multiple classes at once
26
+ var hasClass, addClass, removeClass;
27
+
28
+ if ( 'classList' in document.documentElement ) {
29
+ hasClass = function( elem, c ) {
30
+ return elem.classList.contains( c );
31
+ };
32
+ addClass = function( elem, c ) {
33
+ elem.classList.add( c );
34
+ };
35
+ removeClass = function( elem, c ) {
36
+ elem.classList.remove( c );
37
+ };
38
+ }
39
+ else {
40
+ hasClass = function( elem, c ) {
41
+ return classReg( c ).test( elem.className );
42
+ };
43
+ addClass = function( elem, c ) {
44
+ if ( !hasClass( elem, c ) ) {
45
+ elem.className = elem.className + ' ' + c;
46
+ }
47
+ };
48
+ removeClass = function( elem, c ) {
49
+ elem.className = elem.className.replace( classReg( c ), ' ' );
50
+ };
51
+ }
52
+
53
+ function toggleClass( elem, c ) {
54
+ var fn = hasClass( elem, c ) ? removeClass : addClass;
55
+ fn( elem, c );
56
+ }
57
+
58
+ var classie = {
59
+ // full names
60
+ hasClass: hasClass,
61
+ addClass: addClass,
62
+ removeClass: removeClass,
63
+ toggleClass: toggleClass,
64
+ // short names
65
+ has: hasClass,
66
+ add: addClass,
67
+ remove: removeClass,
68
+ toggle: toggleClass
69
+ };
70
+
71
+ // transport
72
+ if ( typeof define === 'function' && define.amd ) {
73
+ // AMD
74
+ define( classie );
75
+ } else {
76
+ // browser global
77
+ window.classie = classie;
78
+ }
79
+
80
+ })( window );
@@ -0,0 +1,850 @@
1
+ /**
2
+ * Dragdealer.js 0.9.7
3
+ * http://github.com/skidding/dragdealer
4
+ *
5
+ * (c) 2010+ Ovidiu Cherecheș
6
+ * http://skidding.mit-license.org
7
+ */
8
+
9
+ /* Edited by Codrops: 3dtranslate instead of translateX and translateY, removed perspective and backface-visibility hidden*/
10
+
11
+ (function (root, factory) {
12
+ if (typeof define === 'function' && define.amd) {
13
+ // AMD. Register as an anonymous module.
14
+ define(factory);
15
+ } else {
16
+ // Browser globals
17
+ root.Dragdealer = factory();
18
+ }
19
+ }(this, function () {
20
+
21
+ var Dragdealer = function(wrapper, options) {
22
+ /**
23
+ * Drag-based component that works around two basic DOM elements.
24
+ *
25
+ * - The wrapper: The top-level element with the .dragdealer class. We
26
+ * create a Dragdealer instance with the wrapper as the
27
+ * first constructor parameter (it can either receive the ID
28
+ * of the wrapper, or the element itself.) The wrapper
29
+ * establishes the dragging bounds.
30
+ *
31
+ * - The handle: A child of the wrapper element, div with a required
32
+ * .handle class (may be overridden in options). This will be
33
+ * the dragged element, constrained by the wrapper's bounds.
34
+ *
35
+ *
36
+ * The handle can be both smaller or bigger than the wrapper.
37
+ *
38
+ * - When the handle is smaller, Dragdealer will act as a regular slider,
39
+ * enabling the handle to be dragged from one side of the wrapper to
40
+ * another.
41
+ *
42
+ * - When the handle is bigger, Dragdealer will act a mask for a draggable
43
+ * surface, where the handle is the draggable surface contrained by the
44
+ * smaller bounds of the wrapper. The drag action in this case is used
45
+ * to reveal and "discover" partial content at a time.
46
+ *
47
+ *
48
+ * Simple usage:
49
+ *
50
+ * // JavaScript
51
+ * new Dragdealer('simple-slider');
52
+ *
53
+ * <!-- HTML -->
54
+ * <div id="simple-slider" class="dragdealer">
55
+ * <div class="handle">drag me</div>
56
+ * </div>
57
+ *
58
+ *
59
+ * The second parameter of the Dragdealer constructor is an object used for
60
+ * specifying any of the supported options. All of them are optional.
61
+ *
62
+ * - bool disabled=false: Init Dragdealer in a disabled state. The handle
63
+ * will have a .disabled class.
64
+ *
65
+ * - bool horizontal=true: Enable horizontal dragging.
66
+ *
67
+ * - bool vertical=false: Enable vertical dragging.
68
+ *
69
+ * - number x=0: Initial horizontal (left) position. Accepts a float number
70
+ * value between 0 and 1. Read below about positioning in
71
+ * Dragdealer.
72
+ *
73
+ * - number y=0: Initial vertical (top) position. Accepts a float number
74
+ * value between 0 and 1. Read below about positoning in
75
+ * Dragdealer.
76
+ *
77
+ * - number steps=0: Limit the positioning of the handle within the bounds
78
+ * of the wrapper, by defining a virtual grid made out of
79
+ * a number of equally-spaced steps. This restricts
80
+ * placing the handle anywhere in-between these steps.
81
+ * E.g. setting 3 steps to a regular slider will only
82
+ * allow you to move it to the left, to the right or
83
+ * exactly in the middle.
84
+ *
85
+ * - bool snap=false: When a number of steps is set, snap the position of
86
+ * the handle to its closest step instantly, even when
87
+ * dragging.
88
+ *
89
+ * - bool slide=true: Slide handle after releasing it, depending on the
90
+ * movement speed before the mouse/touch release. The
91
+ * formula for calculating how much will the handle
92
+ * slide after releasing it is defined by simply
93
+ * extending the movement of the handle in the current
94
+ * direction, with the last movement unit times four (a
95
+ * movement unit is considered the distance crossed
96
+ * since the last animation loop, which is currently
97
+ * 25ms.) So if you were to drag the handle 50px in the
98
+ * blink of an eye, it will slide another 200px in the
99
+ * same direction. Steps interfere with this formula, as
100
+ * the closest step is calculated before the sliding
101
+ * distance.
102
+ *
103
+ * - bool loose=false: Loosen-up wrapper boundaries when dragging. This
104
+ * allows the handle to be *slightly* dragged outside
105
+ * the bounds of the wrapper, but slides it back to the
106
+ * margins of the wrapper upon release. The formula for
107
+ * calculating how much the handle exceeds the wrapper
108
+ * bounds is made out of the actual drag distance
109
+ * divided by 4. E.g. Pulling a slider outside its
110
+ * frame by 100px will only position it 25px outside
111
+ * the frame.
112
+ *
113
+ * - number top=0: Top padding between the wrapper and the handle.
114
+ *
115
+ * - number bottom=0: Bottom padding between the wrapper and the handle.
116
+ *
117
+ * - number left=0: Left padding between the wrapper and the handle.
118
+ *
119
+ * - number right=0: Right padding between the wrapper and the handle.
120
+ *
121
+ * - fn callback(x, y): Called when releasing handle, with the projected
122
+ * x, y position of the handle. Projected value means
123
+ * the value the slider will have after finishing a
124
+ * sliding animation, caused by either a step
125
+ * restriction or drag motion (see steps and slide
126
+ * options.) This implies that the actual position of
127
+ * the handle at the time this callback is called
128
+ * might not yet reflect the x, y values received.
129
+ *
130
+ * - fn animationCallback(x, y): Called every animation loop, as long as
131
+ * the handle is being dragged or in the
132
+ * process of a sliding animation. The x, y
133
+ * positional values received by this
134
+ * callback reflect the exact position of the
135
+ * handle DOM element, which includes
136
+ * exceeding values (even negative values)
137
+ * when the loose option is set true.
138
+ *
139
+ * - string handleClass='handle': Custom class of handle element.
140
+ *
141
+ * - bool css3=true: Use css3 transform in modern browsers instead of
142
+ * absolute positioning.
143
+ *
144
+ * Dragdealer also has a few methods to interact with, post-initialization.
145
+ *
146
+ * - disable: Disable dragging of a Dragdealer instance. Just as with the
147
+ * disabled option, the handle will receive a .disabled class
148
+ *
149
+ * - enable: Enable dragging of a Dragdealer instance. The .disabled class
150
+ * of the handle will be removed.
151
+ *
152
+ * - reflow: Recalculate the wrapper bounds of a Dragdealer instance, used
153
+ * when the wrapper is responsive and its parent container
154
+ * changed its size, or after changing the size of the wrapper
155
+ * directly.
156
+ *
157
+ * - getValue: Get the value of a Dragdealer instance programatically. The
158
+ * value is returned as an [x, y] tuple and is the equivalent
159
+ * of the (projected) value returned by the regular callback,
160
+ * not animationCallback.
161
+ *
162
+ * - getStep: Same as getValue, but the value returned is in step
163
+ * increments (see steps option)
164
+ *
165
+ * - setValue(x, y, snap=false): Set the value of a Dragdealer instance
166
+ * programatically. The 3rd parameter allows
167
+ * to snap the handle directly to the desired
168
+ * value, without any sliding transition.
169
+ *
170
+ * - setStep(x, y, snap=false): Same as setValue, but the value is received
171
+ * in step increments (see steps option)
172
+ *
173
+ *
174
+ * Positioning in Dragdealer:
175
+ *
176
+ * Besides the top, bottom, left and right paddings, which represent a
177
+ * number of pixels, Dragdealer uses a [0, 1]-based positioning. Both
178
+ * horizontal and vertical positions are represented by ratios between 0
179
+ * and 1. This allows the Dragdealer wrapper to have a responsive size and
180
+ * not revolve around a specific number of pixels. This is how the x, y
181
+ * options are set, what the callback args contain and what values the
182
+ * setValue method expects. Once picked up, the ratios can be scaled and
183
+ * mapped to match any real-life system of coordinates or dimensions.
184
+ */
185
+ this.bindMethods();
186
+ this.options = this.applyDefaults(options || {});
187
+ this.wrapper = this.getWrapperElement(wrapper);
188
+ if (!this.wrapper) {
189
+ return;
190
+ }
191
+ this.handle = this.getHandleElement(this.wrapper, this.options.handleClass);
192
+ if (!this.handle) {
193
+ return;
194
+ }
195
+ this.init();
196
+ this.bindEventListeners();
197
+ };
198
+ Dragdealer.prototype = {
199
+ defaults: {
200
+ disabled: false,
201
+ horizontal: true,
202
+ vertical: false,
203
+ slide: true,
204
+ steps: 0,
205
+ snap: false,
206
+ loose: false,
207
+ speed: 0.1,
208
+ xPrecision: 0,
209
+ yPrecision: 0,
210
+ handleClass: 'handle',
211
+ css3: true,
212
+ activeClass: 'active',
213
+ tapping: true
214
+ },
215
+ init: function() {
216
+ this.value = {
217
+ prev: [-1, -1],
218
+ current: [this.options.x || 0, this.options.y || 0],
219
+ target: [this.options.x || 0, this.options.y || 0]
220
+ };
221
+ this.offset = {
222
+ wrapper: [0, 0],
223
+ mouse: [0, 0],
224
+ prev: [-999999, -999999],
225
+ current: [0, 0],
226
+ target: [0, 0]
227
+ };
228
+ this.change = [0, 0];
229
+ this.stepRatios = this.calculateStepRatios();
230
+
231
+ this.activity = false;
232
+ this.dragging = false;
233
+ this.tapping = false;
234
+
235
+ this.reflow();
236
+ if (this.options.disabled) {
237
+ this.disable();
238
+ }
239
+ },
240
+ applyDefaults: function(options) {
241
+ for (var k in this.defaults) {
242
+ if (!options.hasOwnProperty(k)) {
243
+ options[k] = this.defaults[k];
244
+ }
245
+ }
246
+ return options;
247
+ },
248
+ getWrapperElement: function(wrapper) {
249
+ if (typeof(wrapper) == 'string') {
250
+ return document.getElementById(wrapper);
251
+ } else {
252
+ return wrapper;
253
+ }
254
+ },
255
+ getHandleElement: function(wrapper, handleClass) {
256
+ var childElements,
257
+ handleClassMatcher,
258
+ i;
259
+ if (wrapper.getElementsByClassName) {
260
+ childElements = wrapper.getElementsByClassName(handleClass);
261
+ if (childElements.length > 0) {
262
+ return childElements[0];
263
+ }
264
+ } else {
265
+ handleClassMatcher = new RegExp('(^|\\s)' + handleClass + '(\\s|$)');
266
+ childElements = wrapper.getElementsByTagName('*');
267
+ for (i = 0; i < childElements.length; i++) {
268
+ if (handleClassMatcher.test(childElements[i].className)) {
269
+ return childElements[i];
270
+ }
271
+ }
272
+ }
273
+ },
274
+ calculateStepRatios: function() {
275
+ var stepRatios = [];
276
+ if (this.options.steps > 1) {
277
+ for (var i = 0; i <= this.options.steps - 1; i++) {
278
+ stepRatios[i] = i / (this.options.steps - 1);
279
+ }
280
+ }
281
+ return stepRatios;
282
+ },
283
+ setWrapperOffset: function() {
284
+ this.offset.wrapper = Position.get(this.wrapper);
285
+ },
286
+ calculateBounds: function() {
287
+ // Apply top/bottom/left and right padding options to wrapper extremities
288
+ // when calculating its bounds
289
+ var bounds = {
290
+ top: this.options.top || 0,
291
+ bottom: -(this.options.bottom || 0) + this.wrapper.offsetHeight,
292
+ left: this.options.left || 0,
293
+ right: -(this.options.right || 0) + this.wrapper.offsetWidth
294
+ };
295
+ // The available width and height represents the horizontal and vertical
296
+ // space the handle has for moving. It is determined by the width and
297
+ // height of the wrapper, minus the width and height of the handle
298
+ bounds.availWidth = (bounds.right - bounds.left) - this.handle.offsetWidth;
299
+ bounds.availHeight = (bounds.bottom - bounds.top) - this.handle.offsetHeight;
300
+ return bounds;
301
+ },
302
+ calculateValuePrecision: function() {
303
+ // The sliding transition works by dividing itself until it reaches a min
304
+ // value step; because Dragdealer works with [0-1] values, we need this
305
+ // "min value step" to represent a pixel when applied to the real handle
306
+ // position within the DOM. The xPrecision/yPrecision options can be
307
+ // specified to increase the granularity when we're controlling larger
308
+ // objects from one of the callbacks
309
+ var xPrecision = this.options.xPrecision || Math.abs(this.bounds.availWidth),
310
+ yPrecision = this.options.yPrecision || Math.abs(this.bounds.availHeight);
311
+ return [
312
+ xPrecision ? 1 / xPrecision : 0,
313
+ yPrecision ? 1 / yPrecision : 0
314
+ ];
315
+ },
316
+ bindMethods: function() {
317
+ this.onHandleMouseDown = bind(this.onHandleMouseDown, this);
318
+ this.onHandleTouchStart = bind(this.onHandleTouchStart, this);
319
+ this.onDocumentMouseMove = bind(this.onDocumentMouseMove, this);
320
+ this.onWrapperTouchMove = bind(this.onWrapperTouchMove, this);
321
+ this.onWrapperMouseDown = bind(this.onWrapperMouseDown, this);
322
+ this.onWrapperTouchStart = bind(this.onWrapperTouchStart, this);
323
+ this.onDocumentMouseUp = bind(this.onDocumentMouseUp, this);
324
+ this.onDocumentTouchEnd = bind(this.onDocumentTouchEnd, this);
325
+ this.onHandleClick = bind(this.onHandleClick, this);
326
+ this.onWindowResize = bind(this.onWindowResize, this);
327
+ },
328
+ bindEventListeners: function() {
329
+ // Start dragging
330
+ addEventListener(this.handle, 'mousedown', this.onHandleMouseDown);
331
+ addEventListener(this.handle, 'touchstart', this.onHandleTouchStart);
332
+ // While dragging
333
+ addEventListener(document, 'mousemove', this.onDocumentMouseMove);
334
+ addEventListener(this.wrapper, 'touchmove', this.onWrapperTouchMove);
335
+ // Start tapping
336
+ addEventListener(this.wrapper, 'mousedown', this.onWrapperMouseDown);
337
+ addEventListener(this.wrapper, 'touchstart', this.onWrapperTouchStart);
338
+ // Stop dragging/tapping
339
+ addEventListener(document, 'mouseup', this.onDocumentMouseUp);
340
+ addEventListener(document, 'touchend', this.onDocumentTouchEnd);
341
+
342
+ addEventListener(this.handle, 'click', this.onHandleClick);
343
+ addEventListener(window, 'resize', this.onWindowResize);
344
+
345
+ var _this = this;
346
+ this.interval = setInterval(function() {
347
+ _this.animate();
348
+ }, 25);
349
+ this.animate(false, true);
350
+ },
351
+ unbindEventListeners: function() {
352
+ removeEventListener(this.handle, 'mousedown', this.onHandleMouseDown);
353
+ removeEventListener(this.handle, 'touchstart', this.onHandleTouchStart);
354
+ removeEventListener(document, 'mousemove', this.onDocumentMouseMove);
355
+ removeEventListener(this.wrapper, 'touchmove', this.onWrapperTouchMove);
356
+ removeEventListener(this.wrapper, 'mousedown', this.onWrapperMouseDown);
357
+ removeEventListener(this.wrapper, 'touchstart', this.onWrapperTouchStart);
358
+ removeEventListener(document, 'mouseup', this.onDocumentMouseUp);
359
+ removeEventListener(document, 'touchend', this.onDocumentTouchEnd);
360
+ removeEventListener(this.handle, 'click', this.onHandleClick);
361
+ removeEventListener(window, 'resize', this.onWindowResize);
362
+
363
+ clearInterval(this.interval);
364
+ },
365
+ onHandleMouseDown: function(e) {
366
+ Cursor.refresh(e);
367
+ preventEventDefaults(e);
368
+ stopEventPropagation(e);
369
+ this.activity = false;
370
+ this.startDrag();
371
+ },
372
+ onHandleTouchStart: function(e) {
373
+ Cursor.refresh(e);
374
+ // Unlike in the `mousedown` event handler, we don't prevent defaults here,
375
+ // because this would disable the dragging altogether. Instead, we prevent
376
+ // it in the `touchmove` handler. Read more about touch events
377
+ // https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Touch_events#Handling_clicks
378
+ stopEventPropagation(e);
379
+ this.activity = false;
380
+ this.startDrag();
381
+ },
382
+ onDocumentMouseMove: function(e) {
383
+ Cursor.refresh(e);
384
+ if (this.dragging) {
385
+ this.activity = true;
386
+ }
387
+ },
388
+ onWrapperTouchMove: function(e) {
389
+ Cursor.refresh(e);
390
+ // Dragging on a disabled axis (horizontal or vertical) shouldn't prevent
391
+ // defaults on touch devices. !this.activity denotes this is the first move
392
+ // inside a drag action; you can drag in any direction after this point if
393
+ // the dragging wasn't stopped
394
+ if (!this.activity && this.draggingOnDisabledAxis()) {
395
+ if (this.dragging) {
396
+ this.stopDrag();
397
+ }
398
+ return;
399
+ }
400
+ // Read comment in `onHandleTouchStart` above, to understand why we're
401
+ // preventing defaults here and not there
402
+ preventEventDefaults(e);
403
+ this.activity = true;
404
+ },
405
+ onWrapperMouseDown: function(e) {
406
+ Cursor.refresh(e);
407
+ preventEventDefaults(e);
408
+ this.startTap();
409
+ },
410
+ onWrapperTouchStart: function(e) {
411
+ Cursor.refresh(e);
412
+ preventEventDefaults(e);
413
+ this.startTap();
414
+ },
415
+ onDocumentMouseUp: function(e) {
416
+ this.stopDrag();
417
+ this.stopTap();
418
+ },
419
+ onDocumentTouchEnd: function(e) {
420
+ this.stopDrag();
421
+ this.stopTap();
422
+ },
423
+ onHandleClick: function(e) {
424
+ // We keep track if any dragging activity has been made between the
425
+ // mouse/touch down and up events; based on this we allow or cancel a click
426
+ // event from inside the handle. i.e. Click events shouldn't be triggered
427
+ // when dragging, but should be allowed when clicking still
428
+ if (this.activity) {
429
+ preventEventDefaults(e);
430
+ stopEventPropagation(e);
431
+ }
432
+ },
433
+ onWindowResize: function(e) {
434
+ this.reflow();
435
+ },
436
+ enable: function() {
437
+ this.disabled = false;
438
+ this.handle.className = this.handle.className.replace(/\s?disabled/g, '');
439
+ },
440
+ disable: function() {
441
+ this.disabled = true;
442
+ this.handle.className += ' disabled';
443
+ },
444
+ reflow: function() {
445
+ this.setWrapperOffset();
446
+ this.bounds = this.calculateBounds();
447
+ this.valuePrecision = this.calculateValuePrecision();
448
+ this.updateOffsetFromValue();
449
+ },
450
+ getStep: function() {
451
+ return [
452
+ this.getStepNumber(this.value.target[0]),
453
+ this.getStepNumber(this.value.target[1])
454
+ ];
455
+ },
456
+ getValue: function() {
457
+ return this.value.target;
458
+ },
459
+ setStep: function(x, y, snap) {
460
+ this.setValue(
461
+ this.options.steps && x > 1 ? (x - 1) / (this.options.steps - 1) : 0,
462
+ this.options.steps && y > 1 ? (y - 1) / (this.options.steps - 1) : 0,
463
+ snap
464
+ );
465
+ },
466
+ setValue: function(x, y, snap) {
467
+ this.setTargetValue([x, y || 0]);
468
+ if (snap) {
469
+ this.groupCopy(this.value.current, this.value.target);
470
+ // Since the current value will be equal to the target one instantly, the
471
+ // animate function won't get to run so we need to update the positions
472
+ // and call the callbacks manually
473
+ this.updateOffsetFromValue();
474
+ this.callAnimationCallback();
475
+ }
476
+ },
477
+ startTap: function() {
478
+ if (this.disabled || !this.options.tapping) {
479
+ return;
480
+ }
481
+
482
+ this.tapping = true;
483
+ this.setWrapperOffset();
484
+
485
+ this.setTargetValueByOffset([
486
+ Cursor.x - this.offset.wrapper[0] - (this.handle.offsetWidth / 2),
487
+ Cursor.y - this.offset.wrapper[1] - (this.handle.offsetHeight / 2)
488
+ ]);
489
+ },
490
+ stopTap: function() {
491
+ if (this.disabled || !this.tapping) {
492
+ return;
493
+ }
494
+ this.tapping = false;
495
+
496
+ this.setTargetValue(this.value.current);
497
+ },
498
+ startDrag: function() {
499
+ if (this.disabled) {
500
+ return;
501
+ }
502
+ this.dragging = true;
503
+ this.setWrapperOffset();
504
+
505
+ this.offset.mouse = [
506
+ Cursor.x - Position.get(this.handle)[0],
507
+ Cursor.y - Position.get(this.handle)[1]
508
+ ];
509
+ if (!this.wrapper.className.match(this.options.activeClass)) {
510
+ this.wrapper.className += ' ' + this.options.activeClass;
511
+ }
512
+ },
513
+ stopDrag: function() {
514
+ if (this.disabled || !this.dragging) {
515
+ return;
516
+ }
517
+ this.dragging = false;
518
+
519
+ var target = this.groupClone(this.value.current);
520
+ if (this.options.slide) {
521
+ var ratioChange = this.change;
522
+ target[0] += ratioChange[0] * 4;
523
+ target[1] += ratioChange[1] * 4;
524
+ }
525
+ this.setTargetValue(target);
526
+ this.wrapper.className = this.wrapper.className.replace(' ' + this.options.activeClass, '');
527
+ },
528
+ callAnimationCallback: function() {
529
+ var value = this.value.current;
530
+ if (this.options.snap && this.options.steps > 1) {
531
+ value = this.getClosestSteps(value);
532
+ }
533
+ if (!this.groupCompare(value, this.value.prev)) {
534
+ if (typeof(this.options.animationCallback) == 'function') {
535
+ this.options.animationCallback.call(this, value[0], value[1]);
536
+ }
537
+ this.groupCopy(this.value.prev, value);
538
+ }
539
+ },
540
+ callTargetCallback: function() {
541
+ if (typeof(this.options.callback) == 'function') {
542
+ this.options.callback.call(this, this.value.target[0], this.value.target[1]);
543
+ }
544
+ },
545
+ animate: function(direct, first) {
546
+ if (direct && !this.dragging) {
547
+ return;
548
+ }
549
+ if (this.dragging) {
550
+ var prevTarget = this.groupClone(this.value.target);
551
+
552
+ var offset = [
553
+ Cursor.x - this.offset.wrapper[0] - this.offset.mouse[0],
554
+ Cursor.y - this.offset.wrapper[1] - this.offset.mouse[1]
555
+ ];
556
+ this.setTargetValueByOffset(offset, this.options.loose);
557
+
558
+ this.change = [
559
+ this.value.target[0] - prevTarget[0],
560
+ this.value.target[1] - prevTarget[1]
561
+ ];
562
+ }
563
+ if (this.dragging || first) {
564
+ this.groupCopy(this.value.current, this.value.target);
565
+ }
566
+ if (this.dragging || this.glide() || first) {
567
+ this.updateOffsetFromValue();
568
+ this.callAnimationCallback();
569
+ }
570
+ },
571
+ glide: function() {
572
+ var diff = [
573
+ this.value.target[0] - this.value.current[0],
574
+ this.value.target[1] - this.value.current[1]
575
+ ];
576
+ if (!diff[0] && !diff[1]) {
577
+ return false;
578
+ }
579
+ if (Math.abs(diff[0]) > this.valuePrecision[0] ||
580
+ Math.abs(diff[1]) > this.valuePrecision[1]) {
581
+ this.value.current[0] += diff[0] * this.options.speed;
582
+ this.value.current[1] += diff[1] * this.options.speed;
583
+ } else {
584
+ this.groupCopy(this.value.current, this.value.target);
585
+ }
586
+ return true;
587
+ },
588
+ updateOffsetFromValue: function() {
589
+ if (!this.options.snap) {
590
+ this.offset.current = this.getOffsetsByRatios(this.value.current);
591
+ } else {
592
+ this.offset.current = this.getOffsetsByRatios(
593
+ this.getClosestSteps(this.value.current)
594
+ );
595
+ }
596
+ if (!this.groupCompare(this.offset.current, this.offset.prev)) {
597
+ this.renderHandlePosition();
598
+ this.groupCopy(this.offset.prev, this.offset.current);
599
+ }
600
+ },
601
+ renderHandlePosition: function() {
602
+
603
+ var transform = '';
604
+ if (this.options.css3 && StylePrefix.transform) {
605
+ if (this.options.horizontal) {
606
+ transform += 'translate3d(' + this.offset.current[0] + 'px, 0, 0)';
607
+ }
608
+ if (this.options.vertical) {
609
+ transform += ' translate3d(0, ' + this.offset.current[1] + 'px, 0)';
610
+ }
611
+ this.handle.style[StylePrefix.transform] = transform;
612
+ return;
613
+ }
614
+
615
+ if (this.options.horizontal) {
616
+ this.handle.style.left = this.offset.current[0] + 'px';
617
+ }
618
+ if (this.options.vertical) {
619
+ this.handle.style.top = this.offset.current[1] + 'px';
620
+ }
621
+ },
622
+ setTargetValue: function(value, loose) {
623
+ var target = loose ? this.getLooseValue(value) : this.getProperValue(value);
624
+
625
+ this.groupCopy(this.value.target, target);
626
+ this.offset.target = this.getOffsetsByRatios(target);
627
+
628
+ this.callTargetCallback();
629
+ },
630
+ setTargetValueByOffset: function(offset, loose) {
631
+ var value = this.getRatiosByOffsets(offset);
632
+ var target = loose ? this.getLooseValue(value) : this.getProperValue(value);
633
+
634
+ this.groupCopy(this.value.target, target);
635
+ this.offset.target = this.getOffsetsByRatios(target);
636
+ },
637
+ getLooseValue: function(value) {
638
+ var proper = this.getProperValue(value);
639
+ return [
640
+ proper[0] + ((value[0] - proper[0]) / 4),
641
+ proper[1] + ((value[1] - proper[1]) / 4)
642
+ ];
643
+ },
644
+ getProperValue: function(value) {
645
+ var proper = this.groupClone(value);
646
+
647
+ proper[0] = Math.max(proper[0], 0);
648
+ proper[1] = Math.max(proper[1], 0);
649
+ proper[0] = Math.min(proper[0], 1);
650
+ proper[1] = Math.min(proper[1], 1);
651
+
652
+ if ((!this.dragging && !this.tapping) || this.options.snap) {
653
+ if (this.options.steps > 1) {
654
+ proper = this.getClosestSteps(proper);
655
+ }
656
+ }
657
+ return proper;
658
+ },
659
+ getRatiosByOffsets: function(group) {
660
+ return [
661
+ this.getRatioByOffset(group[0], this.bounds.availWidth, this.bounds.left),
662
+ this.getRatioByOffset(group[1], this.bounds.availHeight, this.bounds.top)
663
+ ];
664
+ },
665
+ getRatioByOffset: function(offset, range, padding) {
666
+ return range ? (offset - padding) / range : 0;
667
+ },
668
+ getOffsetsByRatios: function(group) {
669
+ return [
670
+ this.getOffsetByRatio(group[0], this.bounds.availWidth, this.bounds.left),
671
+ this.getOffsetByRatio(group[1], this.bounds.availHeight, this.bounds.top)
672
+ ];
673
+ },
674
+ getOffsetByRatio: function(ratio, range, padding) {
675
+ return Math.round(ratio * range) + padding;
676
+ },
677
+ getStepNumber: function(value) {
678
+ // Translate a [0-1] value into a number from 1 to N steps (set using the
679
+ // "steps" option)
680
+ return this.getClosestStep(value) * (this.options.steps - 1) + 1;
681
+ },
682
+ getClosestSteps: function(group) {
683
+ return [
684
+ this.getClosestStep(group[0]),
685
+ this.getClosestStep(group[1])
686
+ ];
687
+ },
688
+ getClosestStep: function(value) {
689
+ var k = 0;
690
+ var min = 1;
691
+ for (var i = 0; i <= this.options.steps - 1; i++) {
692
+ if (Math.abs(this.stepRatios[i] - value) < min) {
693
+ min = Math.abs(this.stepRatios[i] - value);
694
+ k = i;
695
+ }
696
+ }
697
+ return this.stepRatios[k];
698
+ },
699
+ groupCompare: function(a, b) {
700
+ return a[0] == b[0] && a[1] == b[1];
701
+ },
702
+ groupCopy: function(a, b) {
703
+ a[0] = b[0];
704
+ a[1] = b[1];
705
+ },
706
+ groupClone: function(a) {
707
+ return [a[0], a[1]];
708
+ },
709
+ draggingOnDisabledAxis: function() {
710
+ return (!this.options.horizontal && Cursor.xDiff > Cursor.yDiff) ||
711
+ (!this.options.vertical && Cursor.yDiff > Cursor.xDiff);
712
+ }
713
+ };
714
+
715
+
716
+ var bind = function(fn, context) {
717
+ /**
718
+ * CoffeeScript-like function to bind the scope of a method to an instance,
719
+ * the context of that method, regardless from where it is called
720
+ */
721
+ return function() {
722
+ return fn.apply(context, arguments);
723
+ };
724
+ };
725
+
726
+ // Cross-browser vanilla JS event handling
727
+
728
+ var addEventListener = function(element, type, callback) {
729
+ if (element.addEventListener) {
730
+ element.addEventListener(type, callback, false);
731
+ } else if (element.attachEvent) {
732
+ element.attachEvent('on' + type, callback);
733
+ }
734
+ };
735
+
736
+ var removeEventListener = function(element, type, callback) {
737
+ if (element.removeEventListener) {
738
+ element.removeEventListener(type, callback, false);
739
+ } else if (element.detachEvent) {
740
+ element.detachEvent('on' + type, callback);
741
+ }
742
+ };
743
+
744
+ var preventEventDefaults = function(e) {
745
+ if (!e) {
746
+ e = window.event;
747
+ }
748
+ if (e.preventDefault) {
749
+ e.preventDefault();
750
+ }
751
+ e.returnValue = false;
752
+ };
753
+
754
+ var stopEventPropagation = function(e) {
755
+ if (!e) {
756
+ e = window.event;
757
+ }
758
+ if (e.stopPropagation) {
759
+ e.stopPropagation();
760
+ }
761
+ e.cancelBubble = true;
762
+ };
763
+
764
+
765
+ var Cursor = {
766
+ /**
767
+ * Abstraction for making the combined mouse or touch position available at
768
+ * any time.
769
+ *
770
+ * It picks up the "move" events as an independent component and simply makes
771
+ * the latest x and y mouse/touch position of the user available at any time,
772
+ * which is requested with Cursor.x and Cursor.y respectively.
773
+ *
774
+ * It can receive both mouse and touch events consecutively, extracting the
775
+ * relevant meta data from each type of event.
776
+ *
777
+ * Cursor.refresh(e) is called to update the global x and y values, with a
778
+ * genuine MouseEvent or a TouchEvent from an event listener, e.g.
779
+ * mousedown/up or touchstart/end
780
+ */
781
+ x: 0,
782
+ y: 0,
783
+ xDiff: 0,
784
+ yDiff: 0,
785
+ refresh: function(e) {
786
+ if (!e) {
787
+ e = window.event;
788
+ }
789
+ if (e.type == 'mousemove') {
790
+ this.set(e);
791
+ } else if (e.touches) {
792
+ this.set(e.touches[0]);
793
+ }
794
+ },
795
+ set: function(e) {
796
+ var lastX = this.x,
797
+ lastY = this.y;
798
+ if (e.clientX || e.clientY) {
799
+ this.x = e.clientX;
800
+ this.y = e.clientY;
801
+ } else if (e.pageX || e.pageY) {
802
+ this.x = e.pageX - document.body.scrollLeft - document.documentElement.scrollLeft;
803
+ this.y = e.pageY - document.body.scrollTop - document.documentElement.scrollTop;
804
+ }
805
+ this.xDiff = Math.abs(this.x - lastX);
806
+ this.yDiff = Math.abs(this.y - lastY);
807
+ }
808
+ };
809
+
810
+
811
+ var Position = {
812
+ /**
813
+ * Helper for extracting position of a DOM element, relative to the viewport
814
+ *
815
+ * The get(obj) method accepts a DOM element as the only parameter, and
816
+ * returns the position under a (x, y) tuple, as an array with two elements.
817
+ */
818
+ get: function(obj) {
819
+ // Dragdealer relies on getBoundingClientRect to calculate element offsets,
820
+ // but we want to be sure we don't throw any unhandled exceptions and break
821
+ // other code from the page if running from in very old browser that doesn't
822
+ // support this method
823
+ var rect = {left: 0, top: 0};
824
+ if (obj.getBoundingClientRect !== undefined) {
825
+ rect = obj.getBoundingClientRect();
826
+ }
827
+ return [rect.left, rect.top];
828
+ }
829
+ };
830
+
831
+
832
+ var StylePrefix = {
833
+ transform: getPrefixedStylePropName('transform')
834
+ }
835
+
836
+ function getPrefixedStylePropName(propName) {
837
+ var domPrefixes = 'Webkit Moz ms O'.split(' '),
838
+ elStyle = document.documentElement.style;
839
+ if (elStyle[propName] !== undefined) return propName; // Is supported unprefixed
840
+ propName = propName.charAt(0).toUpperCase() + propName.substr(1);
841
+ for (var i = 0; i < domPrefixes.length; i++) {
842
+ if (elStyle[domPrefixes[i] + propName] !== undefined) {
843
+ return domPrefixes[i] + propName; // Is supported with prefix
844
+ }
845
+ }
846
+ };
847
+
848
+ return Dragdealer;
849
+
850
+ }));