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 +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +1 -0
- data/Rakefile +2 -0
- data/app/assets/images/1.jpg +0 -0
- data/app/assets/images/2.jpg +0 -0
- data/app/assets/images/3.jpg +0 -0
- data/app/assets/images/4.jpg +0 -0
- data/app/assets/images/5.jpg +0 -0
- data/app/assets/images/6.jpg +0 -0
- data/app/assets/images/7.jpg +0 -0
- data/app/assets/images/8.jpg +0 -0
- data/app/assets/images/drag.svg +20 -0
- data/app/assets/images/keys.svg +45 -0
- data/app/assets/images/switchmax.svg +19 -0
- data/app/assets/images/switchmin.svg +23 -0
- data/app/assets/javascripts/classie.js +80 -0
- data/app/assets/javascripts/dragdealer.js +850 -0
- data/app/assets/javascripts/dragslideshow.js +349 -0
- data/app/assets/javascripts/modernizr.custom.js +4 -0
- data/app/assets/stylesheets/component.css +458 -0
- data/app/assets/stylesheets/demo.css +211 -0
- data/app/assets/stylesheets/dragdealer.css +29 -0
- data/app/assets/stylesheets/normalize.css +1 -0
- data/draggablesilder-rails.gemspec +23 -0
- data/lib/draggablesilder-rails.rb +8 -0
- data/lib/draggablesilder-rails/version.rb +5 -0
- metadata +98 -0
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
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
|
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
|
+
}));
|