draggablesilder-rails 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
+ }));