├── .gitignore ├── pics ├── blue.png ├── gray.png ├── .DS_Store ├── blue-v.png ├── gray-v.png ├── runner.png ├── point-center.png ├── point-left.png ├── point-right.png ├── runner-left.png ├── runner-right.png ├── runner-top.png ├── point_range_in.png ├── runner-bottom.png └── runner-freeze.png ├── package.json ├── demo ├── page.css ├── simple.html ├── style.css ├── script.js └── index.html ├── rader.css ├── LICENSE ├── test ├── invalidate.js ├── rader.auto.html ├── events.js └── dom.js ├── .jscs.json ├── Gruntfile.js ├── css └── style.css ├── tasks └── mocha-phantomjs.js ├── readme.md ├── rader.min.js ├── js ├── qwery.js ├── bean.js └── bonzo.js ├── rader.min.js.map └── rader.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.idea 3 | .DS_Store -------------------------------------------------------------------------------- /pics/blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/blue.png -------------------------------------------------------------------------------- /pics/gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/gray.png -------------------------------------------------------------------------------- /pics/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/.DS_Store -------------------------------------------------------------------------------- /pics/blue-v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/blue-v.png -------------------------------------------------------------------------------- /pics/gray-v.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/gray-v.png -------------------------------------------------------------------------------- /pics/runner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner.png -------------------------------------------------------------------------------- /pics/point-center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/point-center.png -------------------------------------------------------------------------------- /pics/point-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/point-left.png -------------------------------------------------------------------------------- /pics/point-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/point-right.png -------------------------------------------------------------------------------- /pics/runner-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner-left.png -------------------------------------------------------------------------------- /pics/runner-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner-right.png -------------------------------------------------------------------------------- /pics/runner-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner-top.png -------------------------------------------------------------------------------- /pics/point_range_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/point_range_in.png -------------------------------------------------------------------------------- /pics/runner-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner-bottom.png -------------------------------------------------------------------------------- /pics/runner-freeze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2gis/rader/HEAD/pics/runner-freeze.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rader", 3 | "description": "Range slider", 4 | "version": "0.3.0", 5 | "homepage": "https://github.com/Diokuz/rader", 6 | "authors": [ 7 | "Dmitry Kuznecov <@Diokuz>" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/Diokuz/rader.git" 12 | }, 13 | "keywords": [ 14 | "slider", 15 | "custom", 16 | "crossbrowser" 17 | ], 18 | "main": "./rader.js", 19 | "devDependencies": { 20 | "chai": "^3.5.0", 21 | "google-closure-compiler": "^20160315.2.0", 22 | "grunt": "^1.0.1", 23 | "grunt-contrib-jshint": "^1.0.0", 24 | "grunt-jscs": "^2.8.0", 25 | "jscs": "^3.0.3", 26 | "mocha": "^2.4.5", 27 | "mocha-phantomjs": "^4.0.2", 28 | "phantomjs": "^2.1.7" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/page.css: -------------------------------------------------------------------------------- 1 | .page { 2 | margin: 0; 3 | height: 100%; 4 | font: 14px/18px Helvetica, Arial; 5 | } 6 | .page__body { 7 | background: #f9f9f9; 8 | margin: 0; 9 | height: 100%; 10 | display: flex; 11 | padding: 0px; 12 | align-items: stretch; 13 | box-sizing: border-box; 14 | } 15 | 16 | .page__main { 17 | position: absolute; 18 | top: 10px; 19 | left: 12px; 20 | bottom: 60px; 21 | right: 60px; 22 | } 23 | .page__col1 { 24 | position: absolute; 25 | left: 20%; 26 | width: 46%; 27 | padding: 0; 28 | height: 100%; 29 | } 30 | .page__colInner { 31 | position: absolute; 32 | top: -61px; 33 | bottom: -15px; 34 | width: 100%; 35 | display: flex; 36 | justify-content: space-between; 37 | flex-direction: column; 38 | } 39 | -------------------------------------------------------------------------------- /demo/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | Simple rader demo 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /rader.css: -------------------------------------------------------------------------------- 1 | .rader { 2 | position: relative; 3 | margin: 0 auto; 4 | width: 100%; 5 | } 6 | 7 | .rader__track { 8 | position: relative; 9 | height: 6px; 10 | border-radius: 3px; 11 | background: #8ee0f1; 12 | box-shadow: 13 | 1px 1px 1px rgba(255, 255, 255, .9), 14 | 1px 1px 2px rgba(0, 0, 0, .4) inset; 15 | } 16 | .rader__runner { 17 | position: absolute; 18 | width: 0; 19 | height: 0; 20 | z-index: 1; 21 | cursor: pointer; 22 | } 23 | .rader__runner:active { 24 | z-index: 2; 25 | } 26 | .rader__runner:before { 27 | content: ''; 28 | position: absolute; 29 | top: -13px; 30 | left: -7px; 31 | width: 14px; 32 | height: 32px; 33 | border-radius: 3px; 34 | background: #ff5252; 35 | background-image: linear-gradient(to bottom, #ff5252, #ef4242); 36 | box-shadow: 37 | 1px 1px 4px rgba(0, 0, 0, .1), 38 | 0 0 3px rgba(130, 220, 240, .9) inset; 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015, 2GIS (http://2gis.ru) 2 | Copyright (c) 2014-2015, Dmitry Kuznetsov 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /test/invalidate.js: -------------------------------------------------------------------------------- 1 | 2 | describe("Инвалидация радера", function() { 3 | 4 | var sliderElem, 5 | rader; 6 | 7 | before(function() { 8 | sliderElem = $('#invalidate_slider'); 9 | sliderElem.css('display', 'none'); 10 | 11 | window.invalidateRader = rader = sliderElem.rader({ 12 | trackActive: sliderElem.find('.rader__track-active'), 13 | points: sliderElem.find('.rader__point'), 14 | runners: sliderElem.find('.rader__runner'), 15 | pointInRangeCls: 'rader__point_range_in', 16 | pointsPos: [1, 1.5, 2, 3, 4.3, 5], 17 | runnersPos: [2, 3, 5], 18 | stickingRadius: 20, 19 | bumpRadius: 44, 20 | transCls: 'rader__track_transition_on', 21 | change: function(e) { 22 | $('.out__min').text(e.minVal); 23 | $('.out__max').text(e.maxVal); 24 | }, 25 | move: function(e) { 26 | $('.out__min-move').text(e.minVal); 27 | $('.out__max-move').text(e.maxVal); 28 | } 29 | }); 30 | 31 | sliderElem.css('display', ''); 32 | 33 | rader.invalidate(); 34 | }); 35 | 36 | it("инвалидация не сбрасывает значения", function() { 37 | assert.ok(rader); 38 | 39 | rader.val(0, 1); 40 | rader.invalidate(); 41 | assert.equal(rader.val(0), 1); 42 | }); 43 | 44 | }); -------------------------------------------------------------------------------- /.jscs.json: -------------------------------------------------------------------------------- 1 | { 2 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], 3 | "requireSpacesInFunctionExpression": { 4 | "beforeOpeningCurlyBrace": true 5 | }, 6 | "disallowSpacesInFunctionExpression": { 7 | "beforeOpeningRoundBrace": true 8 | }, 9 | "disallowSpaceAfterObjectKeys": true, 10 | "requireCommaBeforeLineBreak": true, 11 | "requireOperatorBeforeLineBreak": [ 12 | "?", 13 | "+", 14 | "-", 15 | "/", 16 | "*", 17 | "=", 18 | "==", 19 | "===", 20 | "!=", 21 | "!==", 22 | ">", 23 | ">=", 24 | "<", 25 | "<=" 26 | ], 27 | "requireSpaceBeforeBinaryOperators": [ 28 | "?", 29 | "/", 30 | "*", 31 | "=", 32 | "==", 33 | "===", 34 | "!=", 35 | "!==", 36 | ">", 37 | ">=", 38 | "<", 39 | "<=" 40 | ], 41 | "disallowSpaceAfterBinaryOperators": ["!"], 42 | "requireSpaceAfterBinaryOperators": [ 43 | "?", 44 | "/", 45 | "*", 46 | ":", 47 | "=", 48 | "==", 49 | "===", 50 | "!=", 51 | "!==", 52 | ">", 53 | ">=", 54 | "<", 55 | "<=" 56 | ], 57 | "requireSpaceBeforeBinaryOperators": [ 58 | "+", 59 | "-", 60 | "/", 61 | "*", 62 | "=", 63 | "==", 64 | "===", 65 | "!=", 66 | "!==" 67 | ], 68 | "requireSpaceAfterBinaryOperators": [ 69 | "+", 70 | "-", 71 | "/", 72 | "*", 73 | "=", 74 | "==", 75 | "===", 76 | "!=", 77 | "!==" 78 | ], 79 | "disallowMixedSpacesAndTabs": true, 80 | "disallowTrailingWhitespace": true, 81 | "disallowKeywordsOnNewLine": ["else"] 82 | } -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | var compilerPackage = require('google-closure-compiler'); 3 | compilerPackage.grunt(grunt); 4 | 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | jshint: { 8 | all: ['Gruntfile.js', 'rader.js', 'test/**/*.js'], 9 | options: { 10 | "indent": 4, 11 | "node": true, 12 | "browser": true, 13 | "jquery": true, 14 | "eqnull": true, 15 | "eqeqeq": false, 16 | "devel": false, 17 | "boss": true, 18 | "trailing": true, 19 | "loopfunc": true, 20 | "-W041": true, 21 | "-W015": true 22 | } 23 | }, 24 | 'closure-compiler': { 25 | my_target: { 26 | files: { 27 | 'rader.min.js': ['rader.js'] 28 | }, 29 | options: { 30 | compilation_level: 'ADVANCED', 31 | language_in: 'ECMASCRIPT5_STRICT', 32 | language_out: 'ECMASCRIPT5_STRICT', 33 | create_source_map: 'rader.min.js.map', 34 | output_wrapper: '%output%\n//# sourceMappingURL=rader.min.js.map' 35 | } 36 | } 37 | }, 38 | jscs: { 39 | src: ['<%= pkg.name %>.js'], 40 | options: { 41 | config: ".jscs.json" 42 | } 43 | }, 44 | 'mocha-phantomjs': { 45 | options: { 46 | view: '1024x768' 47 | }, 48 | all: ['test/*.auto.html'] 49 | } 50 | }); 51 | 52 | grunt.loadTasks('tasks'); // Для grunt-mocha-phantomjs 53 | grunt.loadNpmTasks('grunt-contrib-jshint'); 54 | grunt.loadNpmTasks("grunt-jscs"); 55 | 56 | grunt.registerTask('build', ['closure-compiler']); 57 | 58 | grunt.registerTask('default', 'build'); 59 | grunt.registerTask('test', ['build', 'mocha-phantomjs']); 60 | grunt.registerTask('t', ['jscs', 'jshint', 'mocha-phantomjs']); 61 | }; -------------------------------------------------------------------------------- /test/rader.auto.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 24 | 25 | Rader auto-test page 26 | 27 | 28 | 29 |
30 | 31 | 32 | 33 | 39 | 40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | .page { 2 | margin: 0 3 | } 4 | .page__body { 5 | 6 | } 7 | .rader { 8 | position: relative; 9 | margin: 0 auto; 10 | } 11 | .rader__track { 12 | position: relative; 13 | height: 6px; 14 | /*margin: 10px;*/ 15 | border-radius: 3px; 16 | background: url(../pics/gray.png) repeat-x; 17 | } 18 | .rader__track-active { 19 | position: absolute; 20 | top: -1px; 21 | left: 0; 22 | height: 8px; 23 | border-radius: 4px; 24 | background: url(../pics/blue.png) repeat-x; 25 | } 26 | .rader__point { 27 | position: absolute; 28 | width: 0; 29 | height: 0; 30 | } 31 | .rader__point:before { 32 | content: ''; 33 | position: absolute; 34 | left: -5px; 35 | top: -2px; 36 | width: 10px; 37 | height: 10px; 38 | background-position: center; 39 | background-repeat: no-repeat; 40 | } 41 | .rader__point_type_l:before { 42 | background-image: url(../pics/point-left.png); 43 | } 44 | .rader__point_type_lr:before { 45 | left: -5px; 46 | background-image: url(../pics/point-center.png); 47 | } 48 | .rader__point_type_r:before { 49 | background-image: url(../pics/point-right.png); 50 | } 51 | .rader__point_range_in { 52 | z-index: 1; 53 | } 54 | .rader__point:after { 55 | content: ''; 56 | opacity: 0; 57 | position: absolute; 58 | top: -3px; 59 | left: -6px; 60 | width: 12px; 61 | height: 12px; 62 | background: url(../pics/point_range_in.png) center no-repeat; 63 | -webkit-transition: all .3s; 64 | transition: all .3s; 65 | } 66 | .rader__point_range_in:after { 67 | opacity: 1; 68 | } 69 | 70 | .rader__runner { 71 | position: absolute; 72 | width: 0; 73 | height: 0; 74 | z-index: 1; 75 | } 76 | .rader__runner:active { 77 | z-index: 2; 78 | } 79 | .rader__runner:before { 80 | content: ''; 81 | position: absolute; 82 | top: -8px; 83 | left: -11px; 84 | width: 22px; 85 | height: 22px; 86 | border-radius: 11px; 87 | background: url(../pics/runner.png); 88 | } 89 | .rader__runner_pos_left:before { 90 | background: url(../pics/runner-left.png); 91 | } 92 | .rader__runner_pos_right:before { 93 | background: url(../pics/runner-right.png); 94 | } 95 | .rader__track_transition_on .rader__runner { 96 | -webkit-transition: all .1s ease-in; 97 | transition: all .1s ease-in; 98 | } 99 | .rader__track_transition_on .rader__track-active { 100 | -webkit-transition: all .1s ease-in; 101 | transition: all .1s ease-in; 102 | } -------------------------------------------------------------------------------- /test/events.js: -------------------------------------------------------------------------------- 1 | // Возвращает jQueryEvent событие типа mousedown для радера с координатами x, y 2 | function getMouseDown(x, y) { 3 | var mousedown = new jQuery.Event('mousedown'); 4 | 5 | mousedown.clientX = x; 6 | mousedown.clientY = y; 7 | 8 | return mousedown; 9 | } 10 | 11 | // Возвращает jQueryEvent событие типа mousemove для радера с координатами x, y 12 | function getMouseMove(x, y) { 13 | var mousemove = new jQuery.Event('mousemove'); 14 | 15 | mousemove.clientX = x; 16 | mousemove.clientY = y; 17 | 18 | return mousemove; 19 | } 20 | 21 | // Возвращает jQueryEvent событие типа mouseup для радера с координатами x, y 22 | function getMouseUp(x, y) { 23 | var mouseup = new jQuery.Event('mouseup'); 24 | 25 | mouseup.clientX = x; 26 | mouseup.clientY = y; 27 | 28 | return mouseup; 29 | } 30 | 31 | // Возвращает jQueryEvent событие типа touchstart для радера с координатами x, y 32 | function getTouchStart(x, y) { 33 | var touchstart = new jQuery.Event('touchstart'); 34 | 35 | touchstart.clientX = x; 36 | touchstart.clientY = y; 37 | 38 | return touchstart; 39 | } 40 | 41 | // Возвращает jQueryEvent событие типа touchend для радера с координатами x, y 42 | function getTouchEnd(x, y) { 43 | var touchend = new jQuery.Event('touchend'); 44 | 45 | touchend.clientX = x; 46 | touchend.clientY = y; 47 | 48 | return touchend; 49 | } 50 | 51 | // Возвращает jQueryEvent событие типа touchmove для радера с координатами x, y 52 | function getTouchMove(x, y) { 53 | var touchmove = new jQuery.Event('touchmove'); 54 | 55 | touchmove.clientX = x; 56 | touchmove.clientY = y; 57 | 58 | return touchmove; 59 | } 60 | 61 | function getClick(x) { 62 | var click = new jQuery.Event('click'); 63 | 64 | click.clientX = x; 65 | 66 | return click; 67 | } 68 | 69 | // Вырабатывает и триггерит события, эмулирующие драг рунера 70 | function dragRunner(root, num, pc) { 71 | var track = $(root).find('.rader__track'); 72 | var runner = $(root).find('.rader__runner').eq(num); 73 | var x1 = runner.offset().left; 74 | var x2 = track.offset().left + track.width() / 100 * pc; 75 | 76 | var md = getMouseDown(x1, 0); 77 | runner.trigger(md); 78 | 79 | var mm = getMouseMove(x2, 0); 80 | runner.trigger(mm); 81 | 82 | var mu = getMouseUp(x2, 0); 83 | runner.trigger(mu); 84 | } 85 | 86 | // Вырабатывает и триггерит события, эмулирующие драг рунера на touch-устройствах 87 | function dragRunnerTouch(root, num, pc) { 88 | var track = $(root).find('.rader__track'); 89 | var runner = $(root).find('.rader__runner').eq(num); 90 | var x1 = runner.offset().left; 91 | var x2 = track.offset().left + track.width() / 100 * pc; 92 | 93 | var md = getTouchStart(x1, 0); 94 | runner.trigger(md); 95 | 96 | var mm = getTouchMove(x2, 0); 97 | runner.trigger(mm); 98 | 99 | var mu = getTouchEnd(x2, 0); 100 | runner.trigger(mu); 101 | } 102 | 103 | function clickTrack(root, pc) { 104 | var track = $(root).find('.rader__track'); 105 | var x = track.offset().left + track.width() / 100 * pc; 106 | 107 | var c = getClick(x, 0); 108 | track.trigger(c); 109 | } -------------------------------------------------------------------------------- /tasks/mocha-phantomjs.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-mocha-phantomjs 3 | * https://github.com/jdcataldo/grunt-mocha-phantomjs 4 | * 5 | * Copyright (c) 2013 Justin Cataldo 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | var util = grunt.util, 13 | // Alias for Lo-Dash 14 | _ = util._, 15 | path = require("path"), 16 | exists = grunt.file.exists; 17 | 18 | grunt.registerMultiTask('mocha-phantomjs', 'Run client-side mocha test with phantomjs.', function() { 19 | // Merge options 20 | var options = this.options({ 21 | reporter: 'spec', 22 | // Non file urls to test 23 | urls: [] 24 | }), 25 | args = [], 26 | binPath = '.bin/mocha-phantomjs' + (process.platform === 'win32' ? '.cmd' : ''), 27 | phantomjs_path = path.join(__dirname, '..', '/node_modules/', binPath), 28 | urls = options.urls.concat(this.filesSrc), 29 | done = this.async(), 30 | errors = 0, 31 | results = '', 32 | output = options.output || false; 33 | 34 | // Check for a local install of mocha-phantomjs to use 35 | if (!exists(phantomjs_path)) { 36 | var i = module.paths.length, 37 | bin; 38 | while(i--) { 39 | bin = path.join(module.paths[i], binPath); 40 | if (exists(bin)) { 41 | phantomjs_path = bin; 42 | break; 43 | } 44 | } 45 | } 46 | 47 | if(!exists(phantomjs_path)) { 48 | grunt.fail.warn('Unable to find mocha-phantomjs.'); 49 | } 50 | 51 | // Loop through the options and add them to args 52 | // Omit urls from the options to be passed through 53 | _.each(_.omit(options, 'urls', 'output'), function(value, key) { 54 | // Convert to the key to a switch 55 | var sw = (key.length > 1 ? '--' : '-') + key; 56 | // Add the switch and its value 57 | // If the value is an array, add all array elements to the array. 58 | if(!_.isArray(value)) { 59 | value = [value]; 60 | } 61 | 62 | _.each(value, function(value) { 63 | args.push([sw, value.toString()]); 64 | }); 65 | }); 66 | 67 | util.async.forEachSeries(urls, function(f, next) { 68 | var phantomjs = grunt.util.spawn({ 69 | cmd: phantomjs_path, 70 | args: _.flatten([f].concat(args)) 71 | }, function(error, result, code) { 72 | next(); 73 | }); 74 | 75 | phantomjs.stdout.pipe(process.stdout); 76 | phantomjs.stderr.pipe(process.stderr); 77 | 78 | // Append output to be written to a file 79 | if(output) { 80 | phantomjs.stdout.on('data', function(data){ 81 | results += String(data.toString()); 82 | }); 83 | } 84 | 85 | phantomjs.on('exit', function(code){ 86 | if (code === 127) { 87 | grunt.fail.warn("Phantomjs isn't installed"); 88 | } 89 | errors += code; 90 | }); 91 | 92 | }, function(){ 93 | // Fail if errors are reported and we aren't outputing to a file 94 | if(!output && errors > 0) { 95 | grunt.fail.warn(errors + " tests failed"); 96 | } else if(output) { 97 | grunt.file.write(output, results); 98 | } 99 | done(); 100 | }); 101 | }); 102 | 103 | }; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Rader - range slider. 2 | 3 | ## Demo 4 | 5 | http://diokuz.github.io/rader/simple.html 6 | 7 | http://diokuz.github.io/rader/ 8 | 9 | ## How to use rader 10 | 11 | Make sure you have jQuery on your page. 12 | 13 | Pick some js 14 | 15 | ```js 16 | 17 | ``` 18 | 19 | Then add some html and css 20 | 21 | ```html 22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 | ``` 32 | 33 | or make your own. If so, make sure you have a proper css. 34 | 35 | Then just call rader: 36 | 37 | ```js 38 | $('.rader').rader({ 39 | runners: '.rader__runner' 40 | }); 41 | ``` 42 | 43 | Thats it! 44 | 45 | For advanced usage see API below. 46 | 47 | ## Features 48 | 49 | - 2, 3, 4... 10000000 runners per one slider 50 | - Bumping, sticking, transitioning... 51 | - Non-linear and exponent intervals support 52 | - Flexible markup support 53 | - (new!) 1 runner support 54 | - (new!) Vertical mode 55 | 56 | ## API 57 | 58 | Attention! the context for all input selectors is document, not 'root' param! Remember that and precisely define selectors for all your dom-elements. 59 | 60 | Document is chosed as default context because in that case you have more power: you may, for example, define one track inside root and two additional tracks outside root dom-element. 61 | 62 | Also, you can pass jQuery-objects instead if selectors, like $('.track') instead of '.track'. 63 | 64 | ```js 65 | { 66 | // Root dom node (most parent) for slider, also - statical part of track 67 | root: '.slider', 68 | 69 | // Whole track. 70 | track: '.track', 71 | 72 | // Active (between runners) part of track 73 | trackActive: '.track-active', 74 | 75 | // Dom elements of points 76 | points: '.point', 77 | 78 | // Dom elements responsible for dragging (runners) 79 | runners: '.runner', 80 | 81 | // CSS modifier for points in range for now 82 | pointInRangeCls: '.point_active' 83 | 84 | // Relative linear point positions 85 | pointsPos: [ 0, 12, 33 ], 86 | 87 | // Values which will be linked to points 88 | values: [1, 10, 77, 10000], 89 | 90 | // Values on which runners will point on slider init 91 | runnersVal: [ 2, 600 ], 92 | 93 | // Runners with 1 will be freezed (user will not be able to move them) 94 | runnersFreeze: [ 1, 0 ], 95 | 96 | // Sticky radius for runner (around each point) in px 97 | stickingRadius: 40, 98 | 99 | // Minimal distance between two runners in px 100 | bumpRadius: 22, 101 | 102 | // Use 'log' value if you want logarithmic scale 103 | scale: 'log', 104 | 105 | // Which direction will be used (horizontal '-' by default or vertical '|') for slider 106 | direction: '-', 107 | 108 | // If true, click to track will move the closest non-freezed runner to click point 109 | click: false, 110 | 111 | // CSS class on root element when dragged runners goes to stick (with transition) on point 112 | transCls: '.rader_trans', 113 | 114 | // Event on drag end (mouseup, touchend, generating only by real user) 115 | change: function(e) {}, 116 | 117 | // Event on drag (mousemove, touchmove) 118 | move: function(e) {}, 119 | 120 | // Event on drag end (generating by real user or by methods like setters .pos and .val) 121 | onUpdate: function(e) {} 122 | } 123 | ``` 124 | 125 | ## Methods 126 | 127 | ```js 128 | var rader = $('.rader').rader(params); 129 | 130 | /** 131 | * Emulation of drag of runner number num (0, 1, 2...) to defined position pos (px) 132 | * @return current position (px) of runner number num (0, 1, 2...) in getter mode, and rader instance in setter mode 133 | */ 134 | rader.pos(num, pos); 135 | 136 | /** 137 | * Emulation of drag of runner number num (0, 1, 2...) to defined position of value val (user-defined dimension) 138 | * @return current value (user-defined dimension) of runner number num (0, 1, 2...) in getter mode, and rader instance in setter mode 139 | */ 140 | rader.val(num, val); 141 | 142 | /** 143 | * Invalidating all positions of runners and track 144 | */ 145 | rader.invalidate(); 146 | 147 | /** 148 | * Killing the rader instance, all its event listeners 149 | */ 150 | rader.dispose(); 151 | 152 | ``` 153 | 154 | ## License 155 | 156 | MIT. -------------------------------------------------------------------------------- /demo/style.css: -------------------------------------------------------------------------------- 1 | .page { 2 | margin: 0; 3 | height: 100%; 4 | font: 14px/18px Helvetica, Arial; 5 | } 6 | .page__body { 7 | margin: 0; 8 | height: 100%; 9 | display: flex; 10 | padding: 0px; 11 | align-items: stretch; 12 | box-sizing: border-box; 13 | } 14 | 15 | .page__main { 16 | position: absolute; 17 | top: 10px; 18 | left: 12px; 19 | bottom: 60px; 20 | right: 60px; 21 | } 22 | .page__col1 { 23 | position: absolute; 24 | left: 20%; 25 | width: 46%; 26 | padding: 0; 27 | height: 100%; 28 | } 29 | .page__colInner { 30 | position: absolute; 31 | top: -61px; 32 | bottom: -15px; 33 | width: 100%; 34 | display: flex; 35 | justify-content: space-between; 36 | flex-direction: column; 37 | } 38 | 39 | .rader { 40 | position: relative; 41 | margin: 0 auto; 42 | width: 100%; 43 | padding: 12px 0; 44 | } 45 | .rader._vertical { 46 | position: absolute; 47 | top: 10px; 48 | bottom: 60px; 49 | right: 10px; 50 | width: 20px; 51 | padding: 0; 52 | } 53 | .rader._horizontal { 54 | position: absolute; 55 | left: 12px; 56 | right: 60px; 57 | bottom: 10px; 58 | width: auto; 59 | } 60 | 61 | .rader__track { 62 | position: relative; 63 | height: 6px; 64 | border-radius: 3px; 65 | background: url(../pics/gray.png) repeat-x; 66 | } 67 | .rader._vertical .rader__track { 68 | height: 100%; 69 | width: 6px; 70 | background: url(../pics/gray-v.png) repeat-y; 71 | } 72 | .rader__track-active { 73 | position: absolute; 74 | top: -1px; 75 | left: 0; 76 | height: 8px; 77 | border-radius: 4px; 78 | background: url(../pics/blue.png) repeat-x; 79 | } 80 | .rader._vertical .rader__track-active { 81 | top: 0; 82 | left: -1px; 83 | height: 100%; 84 | width: 8px; 85 | background: url(../pics/blue-v.png) repeat-y; 86 | } 87 | .rader__point { 88 | position: absolute; 89 | width: 0; 90 | height: 0; 91 | } 92 | .rader__point:before { 93 | content: ''; 94 | position: absolute; 95 | left: -5px; 96 | top: -2px; 97 | width: 10px; 98 | height: 10px; 99 | background-position: center; 100 | background-repeat: no-repeat; 101 | } 102 | .rader__point_type_l:before { 103 | background-image: url(../pics/point-left.png); 104 | } 105 | .rader__point_type_lr:before { 106 | left: -5px; 107 | background-image: url(../pics/point-center.png); 108 | } 109 | .rader__point_type_r:before { 110 | background-image: url(../pics/point-right.png); 111 | } 112 | .rader__point_range_in { 113 | z-index: 1; 114 | } 115 | .rader__point:after { 116 | position: absolute; 117 | top: -3px; 118 | left: -6px; 119 | width: 12px; 120 | height: 12px; 121 | background: url(../pics/point_range_in.png) center no-repeat; 122 | content: ''; 123 | opacity: 0; 124 | -webkit-transition: opacity .3s linear; 125 | transition: opacity .3s linear; 126 | } 127 | .rader__point_range_in:after { 128 | opacity: 1; 129 | } 130 | 131 | .rader__runner { 132 | position: absolute; 133 | width: 0; 134 | height: 0; 135 | z-index: 1; 136 | } 137 | .rader__runner:active { 138 | z-index: 2; 139 | } 140 | .rader__runner:before { 141 | content: ''; 142 | position: absolute; 143 | top: -8px; 144 | left: -11px; 145 | width: 22px; 146 | height: 22px; 147 | border-radius: 11px; 148 | background: url(../pics/runner.png); 149 | } 150 | .rader._vertical .rader__runner:before { 151 | top: -11px; 152 | left: -8px; 153 | } 154 | .rader__runner_pos_left:before { 155 | background: url(../pics/runner-left.png); 156 | } 157 | .rader__runner_pos_right:before { 158 | background: url(../pics/runner-right.png); 159 | } 160 | .rader__runner_pos_top:before { 161 | background: url(../pics/runner-top.png); 162 | } 163 | .rader__runner_pos_bottom:before { 164 | background: url(../pics/runner-bottom.png); 165 | } 166 | .rader__runner._freeze:before { 167 | background: url(../pics/runner-freeze.png); 168 | } 169 | .rader__track_transition_on .rader__runner { 170 | -webkit-transition: all .1s ease-in; 171 | transition: all .1s ease-in; 172 | } 173 | .rader__track_transition_on .rader__track-active { 174 | -webkit-transition: all .1s ease-in; 175 | transition: all .1s ease-in; 176 | } 177 | 178 | .out { 179 | position: fixed; 180 | left: 0; 181 | top: 0; 182 | } 183 | .out__input { 184 | display: inline-block; 185 | padding: 3px 10px; 186 | border: 1px solid #eee; 187 | } -------------------------------------------------------------------------------- /rader.min.js: -------------------------------------------------------------------------------- 1 | 'use strict';(function(M,n,N){function O(g){function F(a,c){var b=a[h.clientX]||(((a.originalEvent||a).touches||[])[0]||{})[h.pageX];if(c)var d=c.getBoundingClientRect(),b=b-d[h.start];return b}function X(a){if(a)n(document).on("selectstart.rader",function(){return!1});else n(document).off("selectstart.rader")}function A(a){return Math.min.apply(Math,a)}function G(a){100a&&(a=0);return a}function p(a){null==H[a]&&(H[a]=G((a-x)/P*100));return H[a]}function y(a){null==I[a]&&(I[a]=a/100* 2 | P+x);return I[a]}function B(a){var c=k[0],b=k[k.length-1];if(!k)return a;for(var d=0;l[d+1]&&!(a>=l[d]&&a<=l[d+1]);)d++;var e=l[d],f=l[d+1]||e,C=k[d],d=k[d+1]||C,h;0>=f-e&&(f=J,e=x);"log"==g.scale?h=Math.exp((a-e)/(f-e)*(Math.log(d)-Math.log(C))+Math.log(C)):h=(a-e)/(f-e)*(d-C)+C;return hb?b:h}function Y(a){if(!l)return a;for(var c=l[0],b=l[l.length-1],d=0;k[d+1]&&!(a>=k[d]&&a<=k[d+1]);)d++;d==k.length-1&&d--;var e=k[d],f=k[d+1],h=l[d],d=l[d+1];a="log"==g.scale?(Math.log(a)-Math.log(e))/(Math.log(f)- 3 | Math.log(e))*(d-h)+h:(a-e)/(f-e)*(d-h)+h;return ab?b:a}function Q(a,c,b){var d=a,e,f,h=-1,k=1/0;c=c||l;for(var m=0;mk)return a;for(var m=0;ma*c&&d=k)return a=G(a-e*c+k*c),Math.abs(h-a)d)d=1;else if(bd)&&(a=D(a,c+1*d,b+d*f),b*d>(a-f*d)*d&&(b=Z(a-f*d,-d)));e[c]=b;a={};a[h.start]= 5 | e[c]+"%";q(E[c]).css(a);return b}function K(a){if(g.pointInRangeCls){var c=z.slice(),b;for(b=0;b=e[0]&&p(l[b])<=e[e.length-1]?z[b]=1:z[b]=0;for(b=0;b 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Rader demo page 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 | 27 |
28 |

Все необязательные параметры по-умолчанию.

29 |
30 | 31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 | 40 | 41 | 60 | 61 | 62 | 63 |
64 |

Очень большой радиус прилипания, перескок левого бегунка, без jQuery

65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 | 77 |
78 | 79 | 80 |
81 |
82 |
83 |
84 |
85 |
86 | 87 | 88 | 89 |
90 |

Нелинейные интервалы

91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | 99 |
100 | 101 | 102 |
103 |
104 |
105 |
106 | 107 |
108 |

Блокировка и клик в трек

109 |
110 | 111 |
112 | 113 | 114 |
115 |
116 |
117 |
118 |
119 | 120 |
121 | 122 |
123 | 124 |
125 | 126 |
127 |
128 | 129 |
130 | 131 | 132 |
133 |
134 |
135 |
136 | 137 |
138 |
139 | 140 |
141 | 142 | 143 |
144 |
145 |
146 |
147 | 148 |
149 |
150 |

151 | 152 |
153 |
154 |
155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /js/qwery.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @preserve Qwery - A Blazing Fast query selector engine 3 | * https://github.com/ded/qwery 4 | * copyright Dustin Diaz 2012 5 | * MIT License 6 | */ 7 | 8 | (function (name, context, definition) { 9 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 10 | else if (typeof define == 'function' && define.amd) define(definition) 11 | else context[name] = definition() 12 | })('qwery', this, function () { 13 | var doc = document 14 | , html = doc.documentElement 15 | , byClass = 'getElementsByClassName' 16 | , byTag = 'getElementsByTagName' 17 | , qSA = 'querySelectorAll' 18 | , useNativeQSA = 'useNativeQSA' 19 | , tagName = 'tagName' 20 | , nodeType = 'nodeType' 21 | , select // main select() method, assign later 22 | 23 | , id = /#([\w\-]+)/ 24 | , clas = /\.[\w\-]+/g 25 | , idOnly = /^#([\w\-]+)$/ 26 | , classOnly = /^\.([\w\-]+)$/ 27 | , tagOnly = /^([\w\-]+)$/ 28 | , tagAndOrClass = /^([\w]+)?\.([\w\-]+)$/ 29 | , splittable = /(^|,)\s*[>~+]/ 30 | , normalizr = /^\s+|\s*([,\s\+\~>]|$)\s*/g 31 | , splitters = /[\s\>\+\~]/ 32 | , splittersMore = /(?![\s\w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^'"]*\]|[\s\w\+\-]*\))/ 33 | , specialChars = /([.*+?\^=!:${}()|\[\]\/\\])/g 34 | , simple = /^(\*|[a-z0-9]+)?(?:([\.\#]+[\w\-\.#]+)?)/ 35 | , attr = /\[([\w\-]+)(?:([\|\^\$\*\~]?\=)['"]?([ \w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^]+)["']?)?\]/ 36 | , pseudo = /:([\w\-]+)(\(['"]?([^()]+)['"]?\))?/ 37 | , easy = new RegExp(idOnly.source + '|' + tagOnly.source + '|' + classOnly.source) 38 | , dividers = new RegExp('(' + splitters.source + ')' + splittersMore.source, 'g') 39 | , tokenizr = new RegExp(splitters.source + splittersMore.source) 40 | , chunker = new RegExp(simple.source + '(' + attr.source + ')?' + '(' + pseudo.source + ')?') 41 | 42 | var walker = { 43 | ' ': function (node) { 44 | return node && node !== html && node.parentNode 45 | } 46 | , '>': function (node, contestant) { 47 | return node && node.parentNode == contestant.parentNode && node.parentNode 48 | } 49 | , '~': function (node) { 50 | return node && node.previousSibling 51 | } 52 | , '+': function (node, contestant, p1, p2) { 53 | if (!node) return false 54 | return (p1 = previous(node)) && (p2 = previous(contestant)) && p1 == p2 && p1 55 | } 56 | } 57 | 58 | function cache() { 59 | this.c = {} 60 | } 61 | cache.prototype = { 62 | g: function (k) { 63 | return this.c[k] || undefined 64 | } 65 | , s: function (k, v, r) { 66 | v = r ? new RegExp(v) : v 67 | return (this.c[k] = v) 68 | } 69 | } 70 | 71 | var classCache = new cache() 72 | , cleanCache = new cache() 73 | , attrCache = new cache() 74 | , tokenCache = new cache() 75 | 76 | function classRegex(c) { 77 | return classCache.g(c) || classCache.s(c, '(^|\\s+)' + c + '(\\s+|$)', 1) 78 | } 79 | 80 | // not quite as fast as inline loops in older browsers so don't use liberally 81 | function each(a, fn) { 82 | var i = 0, l = a.length 83 | for (; i < l; i++) fn(a[i]) 84 | } 85 | 86 | function flatten(ar) { 87 | for (var r = [], i = 0, l = ar.length; i < l; ++i) arrayLike(ar[i]) ? (r = r.concat(ar[i])) : (r[r.length] = ar[i]) 88 | return r 89 | } 90 | 91 | function arrayify(ar) { 92 | var i = 0, l = ar.length, r = [] 93 | for (; i < l; i++) r[i] = ar[i] 94 | return r 95 | } 96 | 97 | function previous(n) { 98 | while (n = n.previousSibling) if (n[nodeType] == 1) break; 99 | return n 100 | } 101 | 102 | function q(query) { 103 | return query.match(chunker) 104 | } 105 | 106 | // called using `this` as element and arguments from regex group results. 107 | // given => div.hello[title="world"]:foo('bar') 108 | // div.hello[title="world"]:foo('bar'), div, .hello, [title="world"], title, =, world, :foo('bar'), foo, ('bar'), bar] 109 | function interpret(whole, tag, idsAndClasses, wholeAttribute, attribute, qualifier, value, wholePseudo, pseudo, wholePseudoVal, pseudoVal) { 110 | var i, m, k, o, classes 111 | if (this[nodeType] !== 1) return false 112 | if (tag && tag !== '*' && this[tagName] && this[tagName].toLowerCase() !== tag) return false 113 | if (idsAndClasses && (m = idsAndClasses.match(id)) && m[1] !== this.id) return false 114 | if (idsAndClasses && (classes = idsAndClasses.match(clas))) { 115 | for (i = classes.length; i--;) if (!classRegex(classes[i].slice(1)).test(this.className)) return false 116 | } 117 | if (pseudo && qwery.pseudos[pseudo] && !qwery.pseudos[pseudo](this, pseudoVal)) return false 118 | if (wholeAttribute && !value) { // select is just for existance of attrib 119 | o = this.attributes 120 | for (k in o) { 121 | if (Object.prototype.hasOwnProperty.call(o, k) && (o[k].name || k) == attribute) { 122 | return this 123 | } 124 | } 125 | } 126 | if (wholeAttribute && !checkAttr(qualifier, getAttr(this, attribute) || '', value)) { 127 | // select is for attrib equality 128 | return false 129 | } 130 | return this 131 | } 132 | 133 | function clean(s) { 134 | return cleanCache.g(s) || cleanCache.s(s, s.replace(specialChars, '\\$1')) 135 | } 136 | 137 | function checkAttr(qualify, actual, val) { 138 | switch (qualify) { 139 | case '=': 140 | return actual == val 141 | case '^=': 142 | return actual.match(attrCache.g('^=' + val) || attrCache.s('^=' + val, '^' + clean(val), 1)) 143 | case '$=': 144 | return actual.match(attrCache.g('$=' + val) || attrCache.s('$=' + val, clean(val) + '$', 1)) 145 | case '*=': 146 | return actual.match(attrCache.g(val) || attrCache.s(val, clean(val), 1)) 147 | case '~=': 148 | return actual.match(attrCache.g('~=' + val) || attrCache.s('~=' + val, '(?:^|\\s+)' + clean(val) + '(?:\\s+|$)', 1)) 149 | case '|=': 150 | return actual.match(attrCache.g('|=' + val) || attrCache.s('|=' + val, '^' + clean(val) + '(-|$)', 1)) 151 | } 152 | return 0 153 | } 154 | 155 | // given a selector, first check for simple cases then collect all base candidate matches and filter 156 | function _qwery(selector, _root) { 157 | var r = [], ret = [], i, l, m, token, tag, els, intr, item, root = _root 158 | , tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr)) 159 | , dividedTokens = selector.match(dividers) 160 | 161 | if (!tokens.length) return r 162 | 163 | token = (tokens = tokens.slice(0)).pop() // copy cached tokens, take the last one 164 | if (tokens.length && (m = tokens[tokens.length - 1].match(idOnly))) root = byId(_root, m[1]) 165 | if (!root) return r 166 | 167 | intr = q(token) 168 | // collect base candidates to filter 169 | els = root !== _root && root[nodeType] !== 9 && dividedTokens && /^[+~]$/.test(dividedTokens[dividedTokens.length - 1]) ? 170 | function (r) { 171 | while (root = root.nextSibling) { 172 | root[nodeType] == 1 && (intr[1] ? intr[1] == root[tagName].toLowerCase() : 1) && (r[r.length] = root) 173 | } 174 | return r 175 | }([]) : 176 | root[byTag](intr[1] || '*') 177 | // filter elements according to the right-most part of the selector 178 | for (i = 0, l = els.length; i < l; i++) { 179 | if (item = interpret.apply(els[i], intr)) r[r.length] = item 180 | } 181 | if (!tokens.length) return r 182 | 183 | // filter further according to the rest of the selector (the left side) 184 | each(r, function (e) { if (ancestorMatch(e, tokens, dividedTokens)) ret[ret.length] = e }) 185 | return ret 186 | } 187 | 188 | // compare element to a selector 189 | function is(el, selector, root) { 190 | if (isNode(selector)) return el == selector 191 | if (arrayLike(selector)) return !!~flatten(selector).indexOf(el) // if selector is an array, is el a member? 192 | 193 | var selectors = selector.split(','), tokens, dividedTokens 194 | while (selector = selectors.pop()) { 195 | tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr)) 196 | dividedTokens = selector.match(dividers) 197 | tokens = tokens.slice(0) // copy array 198 | if (interpret.apply(el, q(tokens.pop())) && (!tokens.length || ancestorMatch(el, tokens, dividedTokens, root))) { 199 | return true 200 | } 201 | } 202 | return false 203 | } 204 | 205 | // given elements matching the right-most part of a selector, filter out any that don't match the rest 206 | function ancestorMatch(el, tokens, dividedTokens, root) { 207 | var cand 208 | // recursively work backwards through the tokens and up the dom, covering all options 209 | function crawl(e, i, p) { 210 | while (p = walker[dividedTokens[i]](p, e)) { 211 | if (isNode(p) && (interpret.apply(p, q(tokens[i])))) { 212 | if (i) { 213 | if (cand = crawl(p, i - 1, p)) return cand 214 | } else return p 215 | } 216 | } 217 | } 218 | return (cand = crawl(el, tokens.length - 1, el)) && (!root || isAncestor(cand, root)) 219 | } 220 | 221 | function isNode(el, t) { 222 | return el && typeof el === 'object' && (t = el[nodeType]) && (t == 1 || t == 9) 223 | } 224 | 225 | function uniq(ar) { 226 | var a = [], i, j; 227 | o: 228 | for (i = 0; i < ar.length; ++i) { 229 | for (j = 0; j < a.length; ++j) if (a[j] == ar[i]) continue o 230 | a[a.length] = ar[i] 231 | } 232 | return a 233 | } 234 | 235 | function arrayLike(o) { 236 | return (typeof o === 'object' && isFinite(o.length)) 237 | } 238 | 239 | function normalizeRoot(root) { 240 | if (!root) return doc 241 | if (typeof root == 'string') return qwery(root)[0] 242 | if (!root[nodeType] && arrayLike(root)) return root[0] 243 | return root 244 | } 245 | 246 | function byId(root, id, el) { 247 | // if doc, query on it, else query the parent doc or if a detached fragment rewrite the query and run on the fragment 248 | return root[nodeType] === 9 ? root.getElementById(id) : 249 | root.ownerDocument && 250 | (((el = root.ownerDocument.getElementById(id)) && isAncestor(el, root) && el) || 251 | (!isAncestor(root, root.ownerDocument) && select('[id="' + id + '"]', root)[0])) 252 | } 253 | 254 | function qwery(selector, _root) { 255 | var m, el, root = normalizeRoot(_root) 256 | 257 | // easy, fast cases that we can dispatch with simple DOM calls 258 | if (!root || !selector) return [] 259 | if (selector === window || isNode(selector)) { 260 | return !_root || (selector !== window && isNode(root) && isAncestor(selector, root)) ? [selector] : [] 261 | } 262 | if (selector && arrayLike(selector)) return flatten(selector) 263 | if (m = selector.match(easy)) { 264 | if (m[1]) return (el = byId(root, m[1])) ? [el] : [] 265 | if (m[2]) return arrayify(root[byTag](m[2])) 266 | if (hasByClass && m[3]) return arrayify(root[byClass](m[3])) 267 | } 268 | 269 | return select(selector, root) 270 | } 271 | 272 | // where the root is not document and a relationship selector is first we have to 273 | // do some awkward adjustments to get it to work, even with qSA 274 | function collectSelector(root, collector) { 275 | return function (s) { 276 | var oid, nid 277 | if (splittable.test(s)) { 278 | if (root[nodeType] !== 9) { 279 | // make sure the el has an id, rewrite the query, set root to doc and run it 280 | if (!(nid = oid = root.getAttribute('id'))) root.setAttribute('id', nid = '__qwerymeupscotty') 281 | s = '[id="' + nid + '"]' + s // avoid byId and allow us to match context element 282 | collector(root.parentNode || root, s, true) 283 | oid || root.removeAttribute('id') 284 | } 285 | return; 286 | } 287 | s.length && collector(root, s, false) 288 | } 289 | } 290 | 291 | var isAncestor = 'compareDocumentPosition' in html ? 292 | function (element, container) { 293 | return (container.compareDocumentPosition(element) & 16) == 16 294 | } : 'contains' in html ? 295 | function (element, container) { 296 | container = container[nodeType] === 9 || container == window ? html : container 297 | return container !== element && container.contains(element) 298 | } : 299 | function (element, container) { 300 | while (element = element.parentNode) if (element === container) return 1 301 | return 0 302 | } 303 | , getAttr = function () { 304 | // detect buggy IE src/href getAttribute() call 305 | var e = doc.createElement('p') 306 | return ((e.innerHTML = 'x') && e.firstChild.getAttribute('href') != '#x') ? 307 | function (e, a) { 308 | return a === 'class' ? e.className : (a === 'href' || a === 'src') ? 309 | e.getAttribute(a, 2) : e.getAttribute(a) 310 | } : 311 | function (e, a) { return e.getAttribute(a) } 312 | }() 313 | , hasByClass = !!doc[byClass] 314 | // has native qSA support 315 | , hasQSA = doc.querySelector && doc[qSA] 316 | // use native qSA 317 | , selectQSA = function (selector, root) { 318 | var result = [], ss, e 319 | try { 320 | if (root[nodeType] === 9 || !splittable.test(selector)) { 321 | // most work is done right here, defer to qSA 322 | return arrayify(root[qSA](selector)) 323 | } 324 | // special case where we need the services of `collectSelector()` 325 | each(ss = selector.split(','), collectSelector(root, function (ctx, s) { 326 | e = ctx[qSA](s) 327 | if (e.length == 1) result[result.length] = e.item(0) 328 | else if (e.length) result = result.concat(arrayify(e)) 329 | })) 330 | return ss.length > 1 && result.length > 1 ? uniq(result) : result 331 | } catch (ex) { } 332 | return selectNonNative(selector, root) 333 | } 334 | // no native selector support 335 | , selectNonNative = function (selector, root) { 336 | var result = [], items, m, i, l, r, ss 337 | selector = selector.replace(normalizr, '$1') 338 | if (m = selector.match(tagAndOrClass)) { 339 | r = classRegex(m[2]) 340 | items = root[byTag](m[1] || '*') 341 | for (i = 0, l = items.length; i < l; i++) { 342 | if (r.test(items[i].className)) result[result.length] = items[i] 343 | } 344 | return result 345 | } 346 | // more complex selector, get `_qwery()` to do the work for us 347 | each(ss = selector.split(','), collectSelector(root, function (ctx, s, rewrite) { 348 | r = _qwery(s, ctx) 349 | for (i = 0, l = r.length; i < l; i++) { 350 | if (ctx[nodeType] === 9 || rewrite || isAncestor(r[i], root)) result[result.length] = r[i] 351 | } 352 | })) 353 | return ss.length > 1 && result.length > 1 ? uniq(result) : result 354 | } 355 | , configure = function (options) { 356 | // configNativeQSA: use fully-internal selector or native qSA where present 357 | if (typeof options[useNativeQSA] !== 'undefined') 358 | select = !options[useNativeQSA] ? selectNonNative : hasQSA ? selectQSA : selectNonNative 359 | } 360 | 361 | configure({ useNativeQSA: true }) 362 | 363 | qwery.configure = configure 364 | qwery.uniq = uniq 365 | qwery.is = is 366 | qwery.pseudos = {} 367 | 368 | return qwery 369 | }); -------------------------------------------------------------------------------- /rader.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"rader.min.js", 4 | "lineCount":12, 5 | "mappings":"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", 6 | "sources":["/rader.js"], 7 | "names":["window","$","undefined","init","params","getClientX","event","relativeTo","x","dir","clientX","pageX","rect","start","selection","enable","document","getMin","arr","Math","min","apply","limitPos","posToPc","posToPcMem","delta","pcToPos","px","pcToPosMem","pos2val","minVal","values","maxVal","length","i","pointsPos","x1","x2","v1","v2","val","end","exp","log","val2pos","minX","maxX","val1","val2","getClosest","pc","dimension","pcret","runnerPc","index","minDelta","abs","getNextStableX","sign","dx","dxCl","dxmin","xret","xofClosestPoint","stick","deltaPx","tryMoveRunner","drag","num","runnersCurrentPc","stage","x0","xSticky","stickTimeout","dom","root","setTimeout","bump","xofNextRunner","pos","runners","updatePositions","force","pointsWasInRange","pointsInRange","slice","points","size","max","trackActive","update","x0drag","runnersInitialPos","runnersPrevPos","getEvent","minPos","maxPos","onChange","onMove","updateSizes","client","updateInitialRunnersPos","len","self","direction","directions","defaultParams","elements","offset","key","track","runnersPos","runnersVal","k","e","isRunner","target","parentNode","closest","n","button","setup","setAttribute","offsetStart","rader"] 8 | } 9 | -------------------------------------------------------------------------------- /rader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * There several dimensions in rader: 3 | * - x - pixels dimension 4 | * - pos - user defined linear scale, 0..10 by default 5 | * - % - percent linear scale, 0..100 all the time 6 | * - Value - user defined scale, can be linear, exponentional and linear on intervals, any values allowed 7 | * pixels are used only in the first place at the begining of the event, then px converted to % 8 | */ 9 | 10 | var count = 0; 11 | 12 | /* jshint -W069 */ 13 | (function(window, $, undefined) { 14 | var DEBUG = false, 15 | stage; 16 | 17 | var rader = function(params) { 18 | params = params || {}; 19 | params['root'] = params['root'] || this; 20 | 21 | return new init(params); 22 | }; 23 | 24 | var directions = { 25 | '-': { // Direction: left-to-right 26 | start: 'left', 27 | offsetStart: 'offsetLeft', 28 | client: 'clientWidth', 29 | offset: 'offsetWidth', 30 | size: 'width', 31 | clientX: 'clientX', 32 | pageX: 'pageX' 33 | }, 34 | 35 | '|': { // top-to-bottom 36 | start: 'top', 37 | offsetStart: 'offsetTop', 38 | client: 'clientHeight', 39 | offset: 'offsetHeight', 40 | size: 'height', 41 | clientX: 'clientY', 42 | pageX: 'pageY' 43 | } 44 | }; 45 | 46 | if (DEBUG) { 47 | var error = function(msg) { 48 | throw new Error(msg); 49 | }; 50 | } 51 | 52 | /** 53 | * rader object constructor 54 | * 55 | * @param [params['root']] - dom element of rader makeup 56 | * @param params.dom - dom utility, jQuery or bonzo for example 57 | * @param params.selector - css selector engine - native, sizzle or qwery 58 | */ 59 | var init = function(params) { 60 | var self = this, 61 | elements = {}, 62 | delta, 63 | deltaPx, // Ширина всего трека в пикселях 64 | direction = params.direction || '-', 65 | dir = directions[direction], 66 | defaultParams, 67 | dom, 68 | selector, 69 | x0drag, 70 | i, 71 | drag = -1, // Runner number dragger right now 72 | runnersInitialPos = [], // Current absolute runners position (before and after drag) in offsets! 73 | runnersCurrentPc = [], // Current (in drag mode) absolute (%) runners position 74 | runnersPrevPos = [], // Before tryMove current runners position 75 | pointsInRange = []; // Bool array 76 | 77 | stage = 'init'; 78 | this.elements = elements; 79 | 80 | // DOM utility 81 | dom = params['dom'] || $; 82 | 83 | // Selector engine 84 | selector = params['selector'] || $; 85 | 86 | // Dom initialization 87 | var root = params['root'][0]; 88 | var runners = $(params['runners']); 89 | 90 | // Params initialization 91 | defaultParams = { // Default input parameters 92 | 'stickingRadius': 0, 93 | 'bumpRadius': runners[0][dir.offset], // firstRunner.offsetWidth | Height 94 | 'points': [], // Ссылки на дом-элементы, которые будут привязаны к значениям pointsPos 95 | 'pointsPos': [0, 10], // Позиция виртуальных точек, линейно связанная с пикселями (например мы разбиваем равномерно интервал на N частей) 96 | 'runnersPos': [] // Начальная позиция бегунков 97 | }; 98 | 99 | for (var key in defaultParams) { 100 | if (params[key] == null) { 101 | params[key] = defaultParams[key]; 102 | } 103 | } 104 | 105 | var track = params['track'], 106 | trackActive = params['trackActive'], 107 | points = params['points'], 108 | pointsPos = params['pointsPos'], 109 | runnersPos = params['runnersPos'], 110 | runnersVal = params['runnersVal'], 111 | start, 112 | end, 113 | values = params['values']; 114 | 115 | start = pointsPos[0]; 116 | end = pointsPos[pointsPos.length - 1]; 117 | if (!values) { 118 | values = pointsPos; 119 | } 120 | for (i = 0 ; i < values.length ; i++) { 121 | values[i] = +values[i]; // str to float 122 | } 123 | // Когда позиций точек много, а значения заданы только крайние, добавляем промежуточные значения 124 | if (values.length == 2 && pointsPos.length > 2) { 125 | var k = (values[1] - values[0]) / (pointsPos[pointsPos.length - 1] - pointsPos[0]); 126 | 127 | for (i = 1 ; i < pointsPos.length ; i++) { 128 | values[i] = values[0] + (pointsPos[i] - pointsPos[0]) * k; 129 | } 130 | } 131 | if (runnersVal && !runnersPos.length) { // Задали положение бегунков по значениям шкалы, но не задали runnersPos 132 | for (i = 0 ; i < runnersVal.length ; i++) { 133 | runnersPos[i] = val2pos(runnersVal[i]); 134 | } 135 | } 136 | if (!runnersPos.length) { 137 | runnersPos = [start, end]; 138 | } 139 | 140 | delta = Math.abs(end - start); 141 | 142 | // Validation (dev mode) 143 | if (DEBUG) { 144 | // if (pointsPos && pointsPos.length !== points.length) { 145 | // console.error('pointsPos.length !== points.length'); 146 | // } 147 | if (runners.length != runners.length) { 148 | error('runners.length !== runners.length'); 149 | } 150 | if (!selector) { 151 | error('No selector engine found'); 152 | } 153 | if (!dom) { 154 | error('No dom utility found'); 155 | } 156 | } 157 | 158 | /** 159 | * @param {Object} event - native or jquery click-event object 160 | * @param {HTMLElement} relativeTo - element, top left corner of wich will be the coordinates origin (window by default) 161 | * @return {Number} - position in px of click, absolute by default 162 | */ 163 | function getClientX(event, relativeTo) { 164 | var x = event[dir.clientX] || (((event['originalEvent'] || event)['touches'] || [])[0] || {})[dir.pageX]; // iOs support 165 | 166 | if (relativeTo) { 167 | var rect = relativeTo['getBoundingClientRect'](); 168 | x -= rect[dir.start]; 169 | } 170 | 171 | return x; 172 | } 173 | 174 | // Text selection preventing on drag 175 | function selection(enable) { 176 | if (enable) { 177 | $(document)['on']('selectstart.rader', function() { 178 | return false; 179 | }); 180 | } else { 181 | $(document)['off']('selectstart.rader'); 182 | } 183 | } 184 | 185 | function getMax(arr) { 186 | return Math.max.apply(Math, arr); 187 | } 188 | 189 | function getMin(arr) { 190 | return Math.min.apply(Math, arr); 191 | } 192 | 193 | // Returns true if two arrays is equal 194 | function isEqual(arr1, arr2) { 195 | if (arr1.length != arr2.length) { 196 | return false; 197 | } 198 | 199 | for (var i = 0 ; i < arr1.length ; i++) { 200 | if (arr1[i] !== arr2[i]) { 201 | return false; 202 | } 203 | } 204 | 205 | return true; 206 | } 207 | 208 | // Limiting the % position of x by [0..100] 209 | function limitPos(x) { 210 | if (x > 100) { 211 | x = 100; 212 | } 213 | 214 | if (x < 0) { 215 | x = 0; 216 | } 217 | 218 | return x; 219 | } 220 | 221 | // Converting pos dimension to % dimension 222 | var posToPcMem = []; 223 | function posToPc(x) { 224 | if (posToPcMem[x] == null) { 225 | posToPcMem[x] = limitPos(((x - start) / delta) * 100); 226 | } 227 | 228 | return posToPcMem[x]; 229 | } 230 | 231 | // Converting % dimension to pos dimension 232 | var pcToPosMem = []; 233 | function pcToPos(px) { 234 | if (pcToPosMem[px] == null) { 235 | pcToPosMem[px] = px / 100 * delta + start; 236 | } 237 | 238 | return pcToPosMem[px]; 239 | } 240 | 241 | // Converting pos dimension to Value dimension 242 | function pos2val(x) { 243 | var minVal = values[0], 244 | maxVal = values[values.length - 1]; 245 | 246 | if (!values) return x; 247 | 248 | // Ищем индекс i где в интервале i, i + 1 находится x 249 | var i = 0; 250 | while (pointsPos[i + 1] && !(x >= pointsPos[i] && x <= pointsPos[i + 1])) { 251 | i++; 252 | } 253 | 254 | var x1 = pointsPos[i], 255 | x2 = pointsPos[i + 1] || x1, 256 | v1 = values[i], 257 | v2 = values[i + 1] || v1, 258 | val; 259 | 260 | if (x2 - x1 <= 0) { 261 | x2 = end; 262 | x1 = start; 263 | } 264 | 265 | if (params['scale'] == 'log') { 266 | val = Math.exp((x - x1) / (x2 - x1) * (Math.log(v2) - Math.log(v1)) + Math.log(v1)); 267 | } else { // linear 268 | val = (x - x1) / (x2 - x1) * (v2 - v1) + v1; 269 | } 270 | 271 | // Fallbacks when not in range (299.99999995 instead of 300) 272 | if (val < minVal) return minVal; 273 | if (val > maxVal) return maxVal; 274 | 275 | return val; 276 | } 277 | 278 | // Converting Value dimension to Pos dimension 279 | function val2pos(val) { 280 | if (!pointsPos) return val; 281 | 282 | var minX = pointsPos[0], 283 | maxX = pointsPos[pointsPos.length - 1]; 284 | 285 | // Ищем индекс i где в интервале i, i + 1 находится val 286 | var i = 0; 287 | while (values[i + 1] && !(val >= values[i] && val <= values[i + 1])) { 288 | i++; 289 | } 290 | 291 | if (i == values.length - 1) { 292 | i--; 293 | } 294 | 295 | var val1 = values[i], 296 | val2 = values[i + 1], 297 | x1 = pointsPos[i], 298 | x2 = pointsPos[i + 1], 299 | x; 300 | 301 | if (params['scale'] == 'log') { 302 | //val = Math.exp((x - x1) / (x2 - x1) * (Math.log(v2) - Math.log(v1)) + Math.log(v1)); 303 | x = (Math.log(val) - Math.log(val1)) / (Math.log(val2) - Math.log(val1)) * (x2 - x1) + x1; 304 | } else { // linear 305 | x = (val - val1) / (val2 - val1) * (x2 - x1) + x1; 306 | } 307 | 308 | // Fallbacks when not in range (299.99999995 instead of 300) 309 | if (x < minX) return minX; 310 | if (x > maxX) return maxX; 311 | 312 | return x; 313 | } 314 | 315 | /** 316 | * Getting coordinate of closest to pc point 317 | * 318 | * @param {Number} pc - coordinate in % 319 | * @optional {Array} arr - array of X-coordinates, pointsPos by default 320 | * @optional {String} dimension - supported value: 'pc', otherwise it 'pos' 321 | * @return {Number} - closest static point coordinate in % 322 | */ 323 | function getClosest(pc, arr, dimension) { 324 | var pcret = pc, 325 | delta, 326 | runnerPc, 327 | index = -1, 328 | minDelta = 1 / 0; // +Infinity 329 | 330 | arr = arr || pointsPos; 331 | for (var i = 0 ; i < arr.length ; i++) { 332 | runnerPc = dimension == 'pc' ? arr[i] : posToPc(arr[i]); 333 | 334 | delta = Math.abs(runnerPc - pc); 335 | 336 | if (delta < minDelta && (!params['runnersFreeze'] || !params['runnersFreeze'][i])) { 337 | pcret = runnerPc; 338 | minDelta = delta; 339 | index = i; 340 | } 341 | } 342 | 343 | return { 344 | index: index, 345 | pc: pcret 346 | }; 347 | } 348 | 349 | // Getting coordinate of closest to @x point from (sign > 0) right / bottom or (sign < 0) left / top 350 | function getNextStableX(x, sign) { 351 | var px, dx, dxCl, dxmin = 1 / 0, xret = x, xofClosestPoint = getClosest(x).pc; 352 | var stick = params['stickingRadius'] * 100 / deltaPx; // actual stick in px 353 | 354 | dxCl = Math.abs(xofClosestPoint - x); 355 | 356 | // No x correction needed (because x is outside of points gravity) 357 | if (dxCl > stick) { // 1 358 | return x; 359 | } 360 | 361 | for (var i = 0 ; i < pointsPos.length ; i++) { // 2 362 | px = posToPc(pointsPos[i]); 363 | dx = Math.abs(px - x); 364 | 365 | if ((px * sign > x * sign) && dx < dxmin) { 366 | dxmin = dx; 367 | xret = px; 368 | } 369 | } 370 | 371 | if (dxmin < stick) { // 2, 4 (partially) 372 | return xret; 373 | } 374 | 375 | if (dxmin >= stick) { // 3, 4 376 | x = limitPos(x - dxCl * sign + stick * sign); 377 | 378 | if (Math.abs(xret - x) < stick) { 379 | return xret; 380 | } else { 381 | return x; 382 | } 383 | } 384 | } 385 | 386 | var stickTimeout; 387 | /** 388 | * @param {Number} drag - index of initially moving runner 389 | * @param {Number} num - index of currently moving runner 390 | * @param {Number} x - %-coordinate of point, to where runner is pulled 391 | * @return {Number} - new %-coordinate of current (num) runner 392 | */ 393 | function tryMoveRunner(drag, num, x) { 394 | var sign; 395 | 396 | if (params['runnersFreeze'] && params['runnersFreeze'][num]) return runnersCurrentPc[num]; 397 | 398 | function next(num) { 399 | return num + 1 * sign; 400 | } 401 | 402 | // Moving direction 403 | if (stage == 'init') { 404 | sign = +1; 405 | } else { 406 | var x0 = runnersCurrentPc[drag] || 0; 407 | if (x > x0) { 408 | sign = +1; 409 | } else if (x < x0) { 410 | sign = -1; 411 | } else { 412 | return false; // No main coordinate change 413 | } 414 | 415 | x = limitPos(x); 416 | 417 | if (drag != num) { // Bumping runner 418 | x = getNextStableX(x, sign); 419 | } 420 | } 421 | 422 | // Sticking runner 423 | var xSticky = getClosest(x).pc; 424 | var stick = params['stickingRadius'] * 100 / deltaPx; // actual stick in px 425 | if (xSticky !== undefined && xSticky !== x && Math.abs(xSticky - x) < stick) { 426 | if (!stickTimeout && params['transCls']) { 427 | dom(root)['addClass'](params['transCls']); 428 | stickTimeout = setTimeout(function() { 429 | dom(root)['removeClass'](params['transCls']); 430 | stickTimeout = undefined; 431 | }, 500); 432 | } 433 | x = xSticky; 434 | } 435 | 436 | // Bumping neighbour runners 437 | var bump = params['bumpRadius'] * 100 / deltaPx; // actual bump in pc 438 | if (runnersCurrentPc[next(num)] !== undefined && 439 | (((runnersCurrentPc[next(num)] - x) < bump && sign > 0) || 440 | ((x - runnersCurrentPc[next(num)]) < bump && sign < 0))) { 441 | var xofNextRunner = tryMoveRunner(drag, next(num), x + sign * bump); 442 | 443 | if (x * sign > (xofNextRunner - bump * sign) * sign) { 444 | x = getNextStableX(xofNextRunner - bump * sign, -sign); 445 | } 446 | } 447 | 448 | // Positioning elements runner 449 | runnersCurrentPc[num] = x; 450 | var pos = {}; 451 | pos[dir.start] = runnersCurrentPc[num] + '%'; 452 | dom(runners[num])['css'](pos); 453 | 454 | return x; 455 | } 456 | 457 | // Updating points look (in range | not in range) 458 | function updatePoints(force) { 459 | if (params['pointInRangeCls']) { 460 | // Cloning pointsInRange to pointsWasInRange 461 | var pointsWasInRange = pointsInRange.slice(), 462 | i; 463 | 464 | for (i = 0 ; i < points.length ; i++) { 465 | if (posToPc(pointsPos[i]) >= runnersCurrentPc[0] && posToPc(pointsPos[i]) <= runnersCurrentPc[runnersCurrentPc.length - 1]) { 466 | pointsInRange[i] = 1; 467 | } else { 468 | pointsInRange[i] = 0; 469 | } 470 | } 471 | 472 | for (i = 0 ; i < pointsInRange.length ; i++) { 473 | if (pointsInRange[i] != pointsWasInRange[i] || force) { // Mega profit (+few ms per point change) 474 | if (pointsInRange[i]) { 475 | dom(points[i])['addClass'](params['pointInRangeCls']); 476 | } else { 477 | dom(points[i])['removeClass'](params['pointInRangeCls']); 478 | } 479 | } 480 | } 481 | } 482 | } 483 | 484 | function updatePositions(force) { 485 | // Updating activation state of all points 486 | updatePoints(force); 487 | 488 | // Positioning active track 489 | var pos = {}; 490 | pos[dir.start] = getMin(runnersCurrentPc) + '%'; 491 | pos[dir.size] = (getMax(runnersCurrentPc) - getMin(runnersCurrentPc)) + '%'; 492 | dom(trackActive)['css'](pos); 493 | 494 | if (params['onUpdate']) { 495 | params['onUpdate']({ 496 | 'minPos': getMin(runnersCurrentPc), 497 | 'maxPos': getMax(runnersCurrentPc), 498 | 'minVal': pos2val(pcToPos(getMin(runnersCurrentPc))), 499 | 'maxVal': pos2val(pcToPos(getMax(runnersCurrentPc))) 500 | }); 501 | } 502 | } 503 | 504 | function update(x, force) { 505 | if (x != null) { 506 | var pxperpc = 100 / deltaPx, 507 | dx = (x - x0drag) * pxperpc; 508 | 509 | x = posToPc(runnersInitialPos[drag]) + dx; 510 | 511 | for (var i = 0 ; i < runnersCurrentPc.length ; i++) { 512 | runnersPrevPos[i] = runnersCurrentPc[i]; 513 | } 514 | 515 | tryMoveRunner(drag, drag, limitPos(x)); 516 | } 517 | 518 | if (!isEqual(runnersCurrentPc, runnersPrevPos) || force) { 519 | updatePositions(force); 520 | } 521 | } 522 | 523 | // Возвращает объект с текущими параметрами для юзерских колбеков на события 524 | function getEvent() { 525 | var minPos = getMin(runnersCurrentPc), // pc 526 | maxPos = getMax(runnersCurrentPc), 527 | minVal = pos2val(pcToPos(minPos)), // val 528 | maxVal = pos2val(pcToPos(maxPos)); 529 | 530 | // Слипание значений соседних бегунков 531 | if (params['collapseVals']) { 532 | var bump = params['bumpRadius'] * 100 / deltaPx; // actual bump in pc 533 | if (maxPos - minPos < bump + 1.e-5) { // Самые дальние бегунки на расстоянии слипания 534 | // var stickX = getClosest(minPos); // Ближайшая точка прилипания 535 | 536 | // Сначала проверяем на попадание одной из точек на край диапазона 537 | // console.log('end', end, maxVal); 538 | if (maxPos == 100) { 539 | minVal = maxVal; 540 | } else if (minPos == 0) { 541 | maxVal = minVal; 542 | } else { // Если не на краю, выставляет оба значения активного бегунка 543 | if (runnersCurrentPc[drag] === minPos) { 544 | maxVal = minVal; 545 | } else { 546 | minVal = maxVal; 547 | } 548 | } 549 | } 550 | } 551 | 552 | return { 553 | 'minPos': minPos, 554 | 'maxPos': maxPos, 555 | 'minVal': minVal, 556 | 'maxVal': maxVal 557 | }; 558 | } 559 | 560 | // Вызывается по moveEnd 561 | function onChange() { 562 | if (params['change']) { 563 | params['change'](getEvent()); 564 | } 565 | } 566 | 567 | // Вызывается на событии move 568 | function onMove() { 569 | if (params['move']) { 570 | params['move'](getEvent()); 571 | } 572 | } 573 | 574 | // подготавливает внутренние переменные для работы слайдера 575 | function setup() { 576 | deltaPx = root[dir.client]; // Размер трека нужен уже сейчас 577 | runnersInitialPos = runnersPos.slice(); 578 | 579 | for (var i = 0 ; i < runnersPos.length ; i++) { 580 | runnersCurrentPc[i] = posToPc(runnersInitialPos[i]); 581 | } 582 | for (i = 0 ; i < runnersPos.length ; i++) { 583 | self['pos'](i, runnersInitialPos[i]); // Эмулируем действия юзера для бампинга 584 | runnersCurrentPc[i] = posToPc(runnersInitialPos[i]); 585 | } 586 | } 587 | 588 | // Обновляет все размеры при ресайзе контейнера 589 | function updateSizes() { 590 | deltaPx = root[dir.client]; 591 | posToPcMem = []; 592 | pcToPosMem = []; 593 | 594 | var pos, 595 | i; 596 | // Coordinates initialization 597 | for (i = 0 ; i < pointsPos.length ; i++) { 598 | pos = {}; 599 | pos[dir.start] = posToPc(pointsPos[i]) + '%'; 600 | dom(points[i])['css'](pos); 601 | } 602 | 603 | // Runners position & drag initialization 604 | for (i = 0 ; i < runnersInitialPos.length ; i++) { 605 | pos = {}; 606 | runnersCurrentPc[i] = posToPc(runnersInitialPos[i]); 607 | pos[dir.start] = runnersCurrentPc[i] + '%'; 608 | dom(runners[i])['css'](pos); 609 | } 610 | 611 | // Active track position initialization 612 | var x1 = posToPc(runnersInitialPos[0]), 613 | x2 = posToPc(runnersInitialPos[runnersInitialPos.length - 1]); 614 | 615 | pos = {}; 616 | pos[dir.start] = x1 + '%'; 617 | pos[dir.size] = Math.abs(x2 - x1) + '%'; 618 | dom(trackActive)['css'](pos); 619 | } 620 | 621 | function updateInitialRunnersPos() { 622 | for (var i = 0, len = runnersInitialPos.length; i < len; i++) { 623 | runnersInitialPos[i] = pcToPos(runnersCurrentPc[i]); // Updating initial pos at dragend 624 | } 625 | } 626 | 627 | // Dragend 628 | $(document)['on']('mouseup.rader blur.rader touchend.rader', function() { 629 | if (drag != -1) { 630 | updateInitialRunnersPos(); 631 | 632 | onChange(); 633 | 634 | selection(1); // Enable text selection 635 | 636 | drag = -1; 637 | } 638 | }); 639 | 640 | // Dragstart 641 | $(document)['on']('mousedown.rader touchstart.rader', function(e) { // document, not window, for ie8 642 | deltaPx = root[dir.client]; // recalc for resize 643 | x0drag = getClientX(e); 644 | }); 645 | 646 | $(document)['on']('mousemove.rader touchmove.rader', function(e) { // document, not window, for ie8 647 | if (drag != -1) { 648 | var clientX = getClientX(e); 649 | update(clientX, 0, 1); 650 | onMove(); 651 | } 652 | }); 653 | 654 | if (params['click']) { 655 | $(track || root)['on']('click.rader', function(e) { 656 | var isRunner; 657 | 658 | for (var i = 0 ; i < params['runners'].length && !isRunner; i++) { 659 | // Checking parentNode because target can be a child of runner 660 | // https://github.com/2gis/rader/issues/20 661 | isRunner = e.target == params['runners'][i] || e.target.parentNode == params['runners'][i]; 662 | } 663 | 664 | if (!isRunner) { // if click was not inside one of the runners 665 | var pc = getClientX(e, this) / this[dir.client] * 100; // to % 666 | var closest = getClosest(pc, runnersCurrentPc, 'pc'); 667 | 668 | tryMoveRunner(closest.index, closest.index, pc); 669 | var clientX = getClientX(e); 670 | update(clientX, 1); 671 | updateInitialRunnersPos(); 672 | onMove(); 673 | onChange(); 674 | } 675 | }); 676 | } 677 | 678 | this['pos'] = function(num, pos) { // Emulating drag and drop 679 | if (pos != null) { // setter mode 680 | var x = posToPc(pos); 681 | 682 | tryMoveRunner(num, num, x); 683 | updateInitialRunnersPos(); 684 | updatePositions(1); 685 | 686 | return this; 687 | } 688 | 689 | return runnersInitialPos[num]; // Getter mode 690 | }; 691 | 692 | this['val'] = function(num, val) { // Emulating drag and drop 693 | if (val != null) { // setter mode 694 | var pos = val2pos(val), 695 | x = posToPc(pos); 696 | 697 | tryMoveRunner(num, num, x); 698 | for (var i = 0 ; i < runnersCurrentPc.length ; i++) { 699 | runnersInitialPos[i] = pcToPos(runnersCurrentPc[i]); 700 | } 701 | updatePositions(1); 702 | 703 | return this; 704 | } 705 | 706 | return pos2val(runnersInitialPos[num]); 707 | }; 708 | 709 | this['invalidate'] = function() { 710 | updateSizes(); 711 | updatePositions(1); 712 | onMove(); 713 | }; 714 | 715 | this['dispose'] = function() { 716 | $(document)['off']('rader'); 717 | $(runners)['off']('rader'); 718 | $(track)['off']('rader'); 719 | $(root)['off']('rader'); 720 | }; 721 | 722 | // Методы выше уже нужны, поэтому код здесь 723 | for (i = 0 ; i < runnersPos.length ; i++) { 724 | $(runners[i])['on']('mousedown.rader touchstart.rader', (function(n) { 725 | return function(e) { 726 | if (e.button != 2) { 727 | e['preventDefault'](); // Text selection disabling in Opera... and all other browsers? 728 | selection(); // Disable text selection in ie8 729 | drag = n; // Runner number to be dragged 730 | } 731 | }; 732 | })(i)); 733 | } 734 | 735 | setup(); 736 | updateSizes(); 737 | 738 | update(null, 1); 739 | 740 | stage = 'ready'; 741 | 742 | $(root)[0].setAttribute('data-rader-inited', direction); 743 | 744 | return this; 745 | }; 746 | 747 | window['$']['fn']['rader'] = rader; 748 | })(window, $); 749 | -------------------------------------------------------------------------------- /test/dom.js: -------------------------------------------------------------------------------- 1 | function initTests(rader, params) { 2 | assert.ok(rader); 3 | assert(typeof rader.pos == 'function', 'Есть метод pos'); 4 | assert(typeof rader.val == 'function', 'Есть метод val'); 5 | assert(typeof rader.invalidate == 'function', 'Есть метод invalidate'); 6 | } 7 | 8 | // Тесты на установку позиции 9 | function posTests() { 10 | 11 | } 12 | 13 | // Тесты на изменение ширины контейнера, содержащего радер 14 | function varWidth(rader, params) { 15 | $('.wrapper').css({width: '400px'}); 16 | rader.invalidate(); 17 | assert.equal(rader.elements.track.offsetWidth, 400); 18 | assert.equal(rader.elements.trackActive.offsetWidth, 400); 19 | } 20 | 21 | paramsList = [{ 22 | 23 | }]; 24 | 25 | describe('Два бегунка.', function() { 26 | var rader, 27 | params, 28 | twoRunners = '
', 29 | oneRunner = '
'; 30 | 31 | function init() { 32 | $('.wrapper').html(twoRunners); 33 | 34 | params = { 35 | trackActive: $('.rader_2 .rader__track-active'), 36 | points: $('.rader_2 .rader__point'), 37 | runners: $('.rader_2 .rader__runner'), 38 | pointInRangeCls: 'rader__point_range_in', 39 | change: function(e) { 40 | $('.out__min').text(e.minVal); 41 | $('.out__max').text(e.maxVal); 42 | }, 43 | move: function(e) { 44 | $('.out__min-move').text(e.minVal); 45 | $('.out__max-move').text(e.maxVal); 46 | } 47 | }; 48 | 49 | rader = $('.rader_2').rader(params); 50 | } 51 | 52 | // for (var i = 0 ; i < 12 ; i++) { 53 | it('Тесты инициализации', function() { 54 | init(); 55 | initTests(rader, params); 56 | }); 57 | 58 | it('Тесты pos', function() { 59 | init(); 60 | posTests(rader, params); 61 | }); 62 | 63 | /*it('Изменение ширины', function() { 64 | varWidth(rader, params); 65 | });*/ 66 | // } 67 | 68 | it('Установка начальных значений', function() { 69 | $('.wrapper').html(twoRunners); 70 | params = { 71 | trackActive: $('.rader_2 .rader__track-active'), 72 | points: $('.rader_2 .rader__point'), 73 | runners: $('.rader_2 .rader__runner'), 74 | pointInRangeCls: 'rader__point_range_in', 75 | values: [1, 1000], 76 | runnersVal: [100, 500] 77 | }; 78 | 79 | rader = $('.rader_2').rader(params); 80 | 81 | var val0 = rader.val(0), 82 | val1 = rader.val(1); 83 | 84 | assert.equal(val0, 100, 'Левое значение выставлено правильно'); 85 | assert.equal(val1, 500, 'Правое значение выставлено правильно'); 86 | 87 | rader.invalidate(); 88 | }); 89 | 90 | it('Доразбиение values', function() { 91 | $('.wrapper').html(twoRunners); 92 | params = { 93 | trackActive: $('.rader_2 .rader__track-active'), 94 | points: $('.rader_2 .rader__point'), 95 | runners: $('.rader_2 .rader__runner'), 96 | pointInRangeCls: 'rader__point_range_in', 97 | pointsPos: [0, 100, 200, 300, 400], 98 | values: [0, 400], 99 | runnersVal: [0, 200] 100 | }; 101 | 102 | rader = $('.rader_2').rader(params); 103 | 104 | var pos1 = params.runners[0].offsetLeft / $('.rader_2 .rader__track').width() * 100, 105 | pos2 = params.runners[1].offsetLeft / $('.rader_2 .rader__track').width() * 100; 106 | 107 | assert.equal(pos1, 0, 'Левое значение выставлено правильно'); 108 | assert(Math.abs(pos2 - 50) < 0.1, 'Правое значение выставлено правильно ' + pos2); 109 | 110 | rader.invalidate(); 111 | }); 112 | 113 | it('Работает на touch-устройствах', function() { 114 | var changeEvent, 115 | moveEvent; 116 | 117 | $('.wrapper').html(twoRunners); 118 | 119 | params = { 120 | track: $('.rader_2 .rader__track'), 121 | trackActive: $('.rader_2 .rader__track-active'), 122 | points: $('.rader_2 .rader__point'), 123 | runners: $('.rader_2 .rader__runner'), 124 | runnersVal: [5, 9], 125 | move: function(e) { 126 | moveEvent = e; 127 | }, 128 | change: function(e) { 129 | changeEvent = e; 130 | } 131 | }; 132 | 133 | rader = $('.rader_2').rader(params); 134 | 135 | dragRunnerTouch($('.rader_2'), 1, 80); 136 | rader.invalidate(); 137 | 138 | assert(Math.abs(moveEvent.minVal - 5) < 0.01, 'Начальное значение не должно было поменяться т.кдвигался втроой раннер'); 139 | assert(Math.abs(moveEvent.maxVal - 8) < 0.01, 'Не вызывается событие move на touch-устройствах'); 140 | 141 | assert(Math.abs(changeEvent.minVal - 5) < 0.01, 'Начальное значение не должно было поменяться т.кдвигался втроой раннер'); 142 | assert(Math.abs(changeEvent.maxVal - 8) < 0.01, 'Не вызывается событие change на touch-устройствах'); 143 | }); 144 | 145 | 146 | 147 | it('Выставление значений только через values', function() { 148 | var maxVal; 149 | $('.wrapper').html(twoRunners); 150 | params = { 151 | trackActive: $('.rader_2 .rader__track-active'), 152 | points: $('.rader_2 .rader__point'), 153 | runners: $('.rader_2 .rader__runner'), 154 | values: [1, 4], 155 | onUpdate: function(e) { 156 | maxVal = e.maxVal; 157 | } 158 | }; 159 | 160 | rader = $('.rader_2').rader(params); 161 | 162 | assert.equal(maxVal, 4, 'Значение совпадает с values[1]'); 163 | }); 164 | 165 | describe('Слипание бегунков', function() { 166 | function reset() { 167 | $('.wrapper').html(twoRunners); 168 | } 169 | 170 | function init() { 171 | reset(); 172 | 173 | params = { 174 | trackActive: $('.rader_2 .rader__track-active'), 175 | points: $('.rader_2 .rader__point'), 176 | runners: $('.rader_2 .rader__runner'), 177 | pointInRangeCls: 'rader__point_range_in', 178 | bumpRadius: '22', 179 | collapseVals: true, 180 | change: function(e) { 181 | $('.out__min').text(e.minVal); 182 | $('.out__max').text(e.maxVal); 183 | }, 184 | move: function(e) { 185 | $('.out__min-move').text(e.minVal); 186 | $('.out__max-move').text(e.maxVal); 187 | } 188 | }; 189 | 190 | rader = $('.rader_2').rader(params); 191 | } 192 | 193 | // it.only('Перемещение бегунка в позицию на bump от второго приводит к слипанию значений', function() { 194 | // rader.pos(1, 5); 195 | // rader.pos(0, 5); 196 | 197 | // rader.invalidate(); 198 | 199 | // var val0 = rader.val(0), 200 | // val1 = rader.val(1); 201 | 202 | // console.log('val0, val1', val0, val1); 203 | // }); 204 | 205 | it('Перемещение левого бегунка в позицию правого приводит к смещению правого', function() { 206 | init(); 207 | 208 | rader.pos(1, 5); 209 | rader.pos(0, 5); 210 | rader.invalidate(); 211 | 212 | var val0 = rader.val(0), 213 | val1 = rader.val(1), 214 | pos0 = rader.pos(0), 215 | pos1 = rader.pos(1); 216 | 217 | assert(val0 == 5, 'Значение для левого выставилось: ' + val0); 218 | assert(val0 != val1, 'Значение правого поменялось: ' + val1); 219 | assert(pos0 == 5, 'Позиция для левого выставилась: ' + pos0); 220 | assert(pos0 != pos1, 'Позиция правого поменялась: ' + pos1); 221 | }); 222 | 223 | it('Перемещение левого бегунка в крайне правую позицию приводит к правильному смещению обоих', function() { 224 | init(); 225 | 226 | rader.pos(0, 10); 227 | rader.invalidate(); 228 | 229 | var val0 = rader.val(0), 230 | val1 = rader.val(1), 231 | pos0 = rader.pos(0), 232 | pos1 = rader.pos(1); 233 | 234 | assert(val1 == 10, 'Значение правого бегунка - крайне правое'); 235 | assert(val0 < val1, 'Значение левого бегунка левее крайне правого'); 236 | assert(pos1 == 10, 'Позиция правого бегунка - крайне правоая'); 237 | assert(pos0 < pos1, 'Позиция левого бегунка левее крайне правой'); 238 | }); 239 | 240 | it('Перемещение правого бегунка в крайне левую позицию приводит к правильному смещению обоих', function() { 241 | init(); 242 | 243 | rader.pos(1, 0); 244 | rader.invalidate(); 245 | 246 | var val0 = rader.val(0), 247 | val1 = rader.val(1), 248 | pos0 = rader.pos(0), 249 | pos1 = rader.pos(1); 250 | 251 | assert(val0 == 0, 'Значение левого бегунка - крайне левое'); 252 | assert(val1 > val0, 'Значение правого бегунка правее крайне левого'); 253 | assert(pos0 == 0, 'Позиция левого бегунка - крайне левая'); 254 | assert(pos1 > pos0, 'Позиция правого бегунка правее крайне левой'); 255 | }); 256 | 257 | describe('pos', function() { 258 | it('При помещении левого бегунка на правый, их значения в методе move слипаются по левому, но значения из val различаются', function(done) { 259 | reset(); 260 | 261 | params = { 262 | trackActive: $('.rader_2 .rader__track-active'), 263 | points: $('.rader_2 .rader__point'), 264 | runners: $('.rader_2 .rader__runner'), 265 | pointInRangeCls: 'rader__point_range_in', 266 | bumpRadius: '22', 267 | collapseVals: true, 268 | move: function(e) { 269 | assert(e.minVal == e.maxVal, 'Значения слиплись'); 270 | assert(e.minVal == 5, 'Значения в событии равны выставленному'); 271 | 272 | var val0 = rader.val(0), 273 | val1 = rader.val(1); 274 | 275 | assert(val0 == 5, 'Левое значение равно выставленному'); 276 | assert(val1 > 5, 'Правое значение больше левого'); 277 | 278 | $(document).trigger('blur'); 279 | 280 | done(); 281 | } 282 | }; 283 | 284 | rader = $('.rader_2').rader(params); 285 | 286 | rader.pos(1, 5); 287 | rader.pos(0, 5); 288 | $('.rader_2 .rader__runner_pos_left').trigger('mousedown'); 289 | rader.invalidate(); 290 | }); 291 | 292 | it('Аналогично, при помещении правого бегунка на левый, их значения в методе move слипаются по правому, но значения из val различаются', function(done) { 293 | reset(); 294 | 295 | params = { 296 | trackActive: $('.rader_2 .rader__track-active'), 297 | points: $('.rader_2 .rader__point'), 298 | runners: $('.rader_2 .rader__runner'), 299 | pointInRangeCls: 'rader__point_range_in', 300 | bumpRadius: '22', 301 | collapseVals: true, 302 | move: function(e) { 303 | assert(e.maxVal == e.minVal, 'Значения слиплись'); 304 | assert(Math.abs(e.maxVal - 4.01) < 0.0001, 'Значения в событии равны выставленному'); 305 | 306 | var val0 = rader.val(0), 307 | val1 = rader.val(1); 308 | 309 | assert(Math.abs(val1 - 4.01) < 0.0001, 'Правое значение равно выставленному'); 310 | assert(val0 < 4.01, 'Левое значение меньше правого'); 311 | 312 | $(document).trigger('blur'); 313 | 314 | done(); 315 | } 316 | }; 317 | 318 | rader = $('.rader_2').rader(params); 319 | 320 | rader.pos(0, 4); 321 | rader.pos(1, 4.01); 322 | $('.rader_2 .rader__runner_pos_right').trigger('mousedown'); 323 | rader.invalidate(); 324 | }); 325 | 326 | it('При угоне правого в крайне левое положение, значения слипаются в минимум диапазона (а не по правому)', function(done) { 327 | reset(); 328 | 329 | params = { 330 | trackActive: $('.rader_2 .rader__track-active'), 331 | points: $('.rader_2 .rader__point'), 332 | runners: $('.rader_2 .rader__runner'), 333 | pointInRangeCls: 'rader__point_range_in', 334 | bumpRadius: '22', 335 | collapseVals: true, 336 | move: function(e) { 337 | assert(e.maxVal == e.minVal, 'Значения слиплись'); 338 | assert(e.maxVal == 0, 'Значения в событии на крайне левой границе'); 339 | 340 | var val0 = rader.val(0), 341 | val1 = rader.val(1); 342 | 343 | assert(val0 == 0, 'Левое значение на левой границе'); 344 | assert(val1 > 0, 'Правое значение больше левого'); 345 | 346 | $(document).trigger('blur'); 347 | 348 | done(); 349 | } 350 | }; 351 | 352 | rader = $('.rader_2').rader(params); 353 | 354 | rader.pos(0, 4); 355 | rader.pos(1, 0); 356 | $('.rader_2 .rader__runner_pos_right').trigger('mousedown'); 357 | rader.invalidate(); 358 | }); 359 | 360 | it('При угоне левого в крайне правое положение, значения слипаются в максимуме диапазона (а не по левому)', function(done) { 361 | reset(); 362 | 363 | params = { 364 | trackActive: $('.rader_2 .rader__track-active'), 365 | points: $('.rader_2 .rader__point'), 366 | runners: $('.rader_2 .rader__runner'), 367 | pointInRangeCls: 'rader__point_range_in', 368 | bumpRadius: '22', 369 | collapseVals: true, 370 | move: function(e) { 371 | assert(e.maxVal == e.minVal, 'Значения слиплись'); 372 | assert(e.minVal == 10, 'Значения в событии на крайне правой границе'); 373 | 374 | var val0 = rader.val(0), 375 | val1 = rader.val(1); 376 | 377 | assert(val1 == 10, 'Правое значение на правой границе'); 378 | assert(val0 < 10, 'Левое значение меньше правого'); 379 | 380 | // $(document).trigger('blur'); 381 | 382 | done(); 383 | } 384 | }; 385 | 386 | rader = $('.rader_2').rader(params); 387 | 388 | rader.pos(1, 4); 389 | rader.pos(0, 10); 390 | done(); 391 | // $('.rader_2 .rader__runner_pos_left').trigger('mousedown'); 392 | // rader.invalidate(); 393 | }); 394 | 395 | it('Выставление двух бегунков в одну позицию в момент инициализации не приводит к визуальному слипанию', function() { 396 | reset(); 397 | 398 | params = { 399 | trackActive: $('.rader_2 .rader__track-active'), 400 | points: $('.rader_2 .rader__point'), 401 | runners: $('.rader_2 .rader__runner'), 402 | pointInRangeCls: 'rader__point_range_in', 403 | bumpRadius: '22', 404 | collapseVals: true, 405 | runnersVal: [5, 5] 406 | }; 407 | 408 | rader = $('.rader_2').rader(params); 409 | 410 | var pos1 = $('.rader_2 .rader__runner_pos_left').offset().left, 411 | pos2 = $('.rader_2 .rader__runner_pos_right').offset().left; 412 | 413 | assert(Math.abs(pos2 - pos1) >= 22, 'Позиции должны быть разлеплены: ' + pos1 + ' ' + pos2); 414 | }); 415 | 416 | it('Выставление трёх бегунков в одну позицию в момент инициализации не приводит к визуальному слипанию', function() { 417 | reset(); 418 | 419 | params = { 420 | trackActive: $('#invalidate_slider .rader__track-active'), 421 | points: $('#invalidate_slider .rader__point'), 422 | runners: $('#invalidate_slider .rader__runner'), 423 | pointInRangeCls: 'rader__point_range_in', 424 | bumpRadius: '22', 425 | collapseVals: true, 426 | runnersVal: [5, 5, 5] 427 | }; 428 | 429 | rader = $('#invalidate_slider').rader(params); 430 | 431 | var pos1 = $('#invalidate_slider .rader__runner_pos_left').offset().left, 432 | pos2 = $('#invalidate_slider .rader__runner').eq(1).offset().left, 433 | pos3 = $('#invalidate_slider .rader__runner_pos_right').offset().left; 434 | 435 | var dx = Math.max(Math.abs(pos2 - pos1), Math.abs(pos3 - pos1), Math.abs(pos2 - pos3)); 436 | assert(dx >= 22, 'Позиции должны быть разлеплены: ' + pos1 + ' ' + pos2 + ' ' + pos3); 437 | }); 438 | }); 439 | 440 | 441 | describe('val', function() { 442 | it('При помещении левого бегунка на правый, их значения в методе move слипаются по левому, но значения из val различаются', function(done) { 443 | reset(); 444 | 445 | params = { 446 | trackActive: $('.rader_2 .rader__track-active'), 447 | points: $('.rader_2 .rader__point'), 448 | runners: $('.rader_2 .rader__runner'), 449 | pointInRangeCls: 'rader__point_range_in', 450 | bumpRadius: '22', 451 | collapseVals: true, 452 | move: function(e) { 453 | assert(e.minVal == e.maxVal, 'Значения слиплись'); 454 | assert(e.minVal == 5, 'Значения в событии равны выставленному'); 455 | 456 | var val0 = rader.val(0), 457 | val1 = rader.val(1); 458 | 459 | assert(val0 == 5, 'Левое значение равно выставленному'); 460 | assert(val1 > 5, 'Правое значение больше левого'); 461 | 462 | $(document).trigger('blur'); 463 | 464 | done(); 465 | } 466 | }; 467 | 468 | rader = $('.rader_2').rader(params); 469 | 470 | rader.val(1, 5); 471 | rader.val(0, 5); 472 | $('.rader_2 .rader__runner_pos_left').trigger('mousedown'); 473 | rader.invalidate(); 474 | }); 475 | }); 476 | 477 | describe('onUpdate', function() { 478 | it('Аргумент-объект имеет правильный формат', function() { 479 | var event; 480 | 481 | reset(); 482 | 483 | params = { 484 | trackActive: $('.rader_2 .rader__track-active'), 485 | points: $('.rader_2 .rader__point'), 486 | runners: $('.rader_2 .rader__runner'), 487 | pointInRangeCls: 'rader__point_range_in', 488 | bumpRadius: '22', 489 | collapseVals: true, 490 | onUpdate: function(e) { 491 | event = e; 492 | } 493 | }; 494 | 495 | rader = $('.rader_2').rader(params); 496 | 497 | event = {}; 498 | rader.val(0, 3); 499 | 500 | assert.equal(event.minVal, 3, 'Значение выставилось'); 501 | assert.equal(event.maxVal, 10); 502 | assert(event.minPos, 'Поле minPos undefinded'); 503 | assert(event.maxPos, 'Поле maxPos undefinded'); 504 | }); 505 | }); 506 | }); 507 | 508 | describe('Числа передаются в строках', function() { 509 | function reset() { 510 | $('.wrapper').html(twoRunners); 511 | } 512 | 513 | it('values', function() { 514 | var event; 515 | 516 | reset(); 517 | 518 | params = { 519 | trackActive: $('.rader_2 .rader__track-active'), 520 | points: $('.rader_2 .rader__point'), 521 | runners: $('.rader_2 .rader__runner'), 522 | values: ['33', '67'], 523 | move: function(e) { 524 | event = e; 525 | } 526 | }; 527 | 528 | rader = $('.rader_2').rader(params); 529 | rader.invalidate(); 530 | 531 | assert.equal(event.minVal, 33, 'Начальное значение должно выставиться числом'); 532 | assert.equal(event.maxVal, 67, 'Конечное значение должно выставиться числом'); 533 | }); 534 | 535 | it('pointsPos', function() { 536 | var event; 537 | 538 | reset(); 539 | 540 | params = { 541 | trackActive: $('.rader_2 .rader__track-active'), 542 | points: $('.rader_2 .rader__point'), 543 | runners: $('.rader_2 .rader__runner'), 544 | pointsPos: ['123', '456'], 545 | move: function(e) { 546 | event = e; 547 | } 548 | }; 549 | 550 | rader = $('.rader_2').rader(params); 551 | rader.invalidate(); 552 | 553 | assert.equal(event.minVal, 123, 'Начальное значение должно выставиться числом'); 554 | assert.equal(event.maxVal, 456, 'Конечное значение должно выставиться числом'); 555 | }); 556 | }); 557 | 558 | describe('Параметр click', function() { 559 | function reset() { 560 | $('.wrapper').html(twoRunners); 561 | } 562 | 563 | it('false', function() { 564 | var event; 565 | 566 | reset(); 567 | 568 | params = { 569 | track: $('.rader_2 .rader__track'), 570 | trackActive: $('.rader_2 .rader__track-active'), 571 | points: $('.rader_2 .rader__point'), 572 | runners: $('.rader_2 .rader__runner'), 573 | click: false, 574 | move: function(e) { 575 | event = e; 576 | } 577 | }; 578 | 579 | rader = $('.rader_2').rader(params); 580 | 581 | var e = new jQuery.Event("click"); 582 | e.clientX = 400; 583 | $(params.trackActive).trigger(e); 584 | 585 | rader.invalidate(); 586 | 587 | assert.equal(event.minVal, 0, 'Начальное значение не должно измениться'); 588 | assert.equal(event.maxVal, 10, 'Конечное значение не должно измениться'); 589 | }); 590 | 591 | it('true', function() { 592 | var event; 593 | 594 | reset(); 595 | 596 | params = { 597 | track: $('.rader_2 .rader__track'), 598 | trackActive: $('.rader_2 .rader__track-active'), 599 | points: $('.rader_2 .rader__point'), 600 | runners: $('.rader_2 .rader__runner'), 601 | click: true, 602 | move: function(e) { 603 | event = e; 604 | } 605 | }; 606 | 607 | rader = $('.rader_2').rader(params); 608 | 609 | var e = new jQuery.Event("click"); 610 | e.clientX = 400; 611 | $(params.trackActive).trigger(e); 612 | 613 | rader.invalidate(); 614 | 615 | assert.notEqual(event.minVal, 0, 'Начальное значение должно измениться'); 616 | assert.equal(event.maxVal, 10, 'Конечное значение не должно измениться'); 617 | }); 618 | 619 | it('Клик в runner не должен приводить к его перемещению', function() { 620 | var event; 621 | 622 | reset(); 623 | 624 | params = { 625 | track: $('.rader_2 .rader__track'), 626 | trackActive: $('.rader_2 .rader__track-active'), 627 | points: $('.rader_2 .rader__point'), 628 | runners: $('.rader_2 .rader__runner'), 629 | click: true, 630 | move: function(e) { 631 | event = e; 632 | } 633 | }; 634 | 635 | rader = $('.rader_2').rader(params); 636 | 637 | var e = new jQuery.Event("click"); 638 | e.clientX = 400; 639 | $(params.runners).eq(1).trigger(e); 640 | 641 | rader.invalidate(); 642 | 643 | assert.equal(event.minVal, 0, 'Начальное значение не должно измениться'); 644 | assert.equal(event.maxVal, 10, 'Конечное значение не должно измениться'); 645 | }); 646 | }); 647 | 648 | describe('Параметр runnersFreeze', function() { 649 | function reset() { 650 | $('.wrapper').html(twoRunners); 651 | } 652 | 653 | it('Runner не двигается драгом ни бампингом', function() { 654 | var event; 655 | 656 | reset(); 657 | 658 | params = { 659 | track: $('.rader_2 .rader__track'), 660 | trackActive: $('.rader_2 .rader__track-active'), 661 | points: $('.rader_2 .rader__point'), 662 | runners: $('.rader_2 .rader__runner'), 663 | runnersVal: [5, 9], 664 | runnersFreeze: [1, 0], 665 | move: function(e) { 666 | event = e; 667 | } 668 | }; 669 | 670 | rader = $('.rader_2').rader(params); 671 | 672 | dragRunner($('.rader_2'), 0, 20); 673 | rader.invalidate(); 674 | 675 | assert.equal(event.minVal, 5, 'Начальное значение не должно измениться'); 676 | assert.equal(event.maxVal, 9, 'Конечное значение не должно измениться'); 677 | 678 | dragRunner($('.rader_2'), 1, 80); 679 | rader.invalidate(); 680 | 681 | assert(Math.abs(event.minVal - 5) < 0.01, 'Начальное значение не должно измениться'); 682 | assert(Math.abs(event.maxVal - 8) < 0.01, 'Конечное значение должно измениться, ведь оно не заморожено'); 683 | 684 | 685 | 686 | dragRunner($('.rader_2'), 1, 20); 687 | rader.invalidate(); 688 | 689 | assert(Math.abs(event.minVal - 5) < 0.01, 'Начальное значение не должно измениться'); 690 | assert(Math.abs(event.maxVal - 5) < 0.01, 'Конечное значение должно измениться, но должно блокироваться замороженным ранером'); 691 | }); 692 | 693 | it('Клик около замороженного ранера приводит к смещению соседнего, хоть и более дальнего', function() { 694 | var event; 695 | 696 | reset(); 697 | 698 | params = { 699 | track: $('.rader_2 .rader__track'), 700 | trackActive: $('.rader_2 .rader__track-active'), 701 | points: $('.rader_2 .rader__point'), 702 | runners: $('.rader_2 .rader__runner'), 703 | runnersVal: [5, 9], 704 | runnersFreeze: [1, 0], 705 | click: true, 706 | move: function(e) { 707 | event = e; 708 | } 709 | }; 710 | 711 | rader = $('.rader_2').rader(params); 712 | 713 | clickTrack($('.rader_2'), 60); 714 | rader.invalidate(); 715 | 716 | assert.equal(event.minVal, 5, 'Начальное значение не должно измениться'); 717 | assert(Math.abs(event.maxVal - 6) < 0.01, 'Конечное значение должно измениться ' + event.maxVal); 718 | }); 719 | 720 | it('Клик в трек вызывает change', function() { 721 | var changeEvent, 722 | moveEvent; 723 | 724 | reset(); 725 | 726 | params = { 727 | track: $('.rader_2 .rader__track'), 728 | trackActive: $('.rader_2 .rader__track-active'), 729 | points: $('.rader_2 .rader__point'), 730 | runners: $('.rader_2 .rader__runner'), 731 | runnersVal: [5, 9], 732 | runnersFreeze: [1, 0], 733 | click: true, 734 | move: function(e) { 735 | moveEvent = e; 736 | }, 737 | change: function(e) { 738 | changeEvent = e; 739 | } 740 | }; 741 | 742 | rader = $('.rader_2').rader(params); 743 | 744 | clickTrack($('.rader_2'), 60); 745 | rader.invalidate(); 746 | 747 | assert(Math.abs(moveEvent.minPos - 50) < 0.01); 748 | assert(Math.abs(moveEvent.maxPos - 60) < 0.01); 749 | 750 | assert(Math.abs(changeEvent.minPos - 50) < 0.01); 751 | assert(Math.abs(changeEvent.maxPos - 60) < 0.01); 752 | }); 753 | }); 754 | 755 | describe('Одиночка', function() { 756 | function reset() { 757 | $('.wrapper').html(oneRunner); 758 | } 759 | 760 | it('Работает', function() { 761 | var event; 762 | 763 | reset(); 764 | 765 | params = { 766 | runners: $('.rader_2 .rader__runner'), 767 | runnersVal: [5], 768 | values: [0, 1439], 769 | move: function(e) { 770 | event = e; 771 | } 772 | }; 773 | 774 | rader = $('.rader_2').rader(params); 775 | 776 | dragRunner($('.rader_2'), 0, 20); 777 | rader.invalidate(); 778 | 779 | assert(Math.abs(event.minPos - 20) < 0.01, 'Начальное значение не должно измениться ' + event.minPos); 780 | // assert.equal(event.maxVal, 9, 'Конечное значение не должно измениться'); 781 | assert(true); 782 | }); 783 | }); 784 | }); -------------------------------------------------------------------------------- /js/bean.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bean - copyright (c) Jacob Thornton 2011-2012 3 | * https://github.com/fat/bean 4 | * MIT license 5 | */ 6 | (function (name, context, definition) { 7 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 8 | else if (typeof define == 'function' && define.amd) define(definition) 9 | else context[name] = definition() 10 | })('bean', this, function (name, context) { 11 | name = name || 'bean' 12 | context = context || this 13 | 14 | var win = window 15 | , old = context[name] 16 | , namespaceRegex = /[^\.]*(?=\..*)\.|.*/ 17 | , nameRegex = /\..*/ 18 | , addEvent = 'addEventListener' 19 | , removeEvent = 'removeEventListener' 20 | , doc = document || {} 21 | , root = doc.documentElement || {} 22 | , W3C_MODEL = root[addEvent] 23 | , eventSupport = W3C_MODEL ? addEvent : 'attachEvent' 24 | , ONE = {} // singleton for quick matching making add() do one() 25 | 26 | , slice = Array.prototype.slice 27 | , str2arr = function (s, d) { return s.split(d || ' ') } 28 | , isString = function (o) { return typeof o == 'string' } 29 | , isFunction = function (o) { return typeof o == 'function' } 30 | 31 | // events that we consider to be 'native', anything not in this list will 32 | // be treated as a custom event 33 | , standardNativeEvents = 34 | 'click dblclick mouseup mousedown contextmenu ' + // mouse buttons 35 | 'mousewheel mousemultiwheel DOMMouseScroll ' + // mouse wheel 36 | 'mouseover mouseout mousemove selectstart selectend ' + // mouse movement 37 | 'keydown keypress keyup ' + // keyboard 38 | 'orientationchange ' + // mobile 39 | 'focus blur change reset select submit ' + // form elements 40 | 'load unload beforeunload resize move DOMContentLoaded ' + // window 41 | 'readystatechange message ' + // window 42 | 'error abort scroll ' // misc 43 | // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event 44 | // that doesn't actually exist, so make sure we only do these on newer browsers 45 | , w3cNativeEvents = 46 | 'show ' + // mouse buttons 47 | 'input invalid ' + // form elements 48 | 'touchstart touchmove touchend touchcancel ' + // touch 49 | 'gesturestart gesturechange gestureend ' + // gesture 50 | 'textinput' + // TextEvent 51 | 'readystatechange pageshow pagehide popstate ' + // window 52 | 'hashchange offline online ' + // window 53 | 'afterprint beforeprint ' + // printing 54 | 'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd 55 | 'loadstart progress suspend emptied stalled loadmetadata ' + // media 56 | 'loadeddata canplay canplaythrough playing waiting seeking ' + // media 57 | 'seeked ended durationchange timeupdate play pause ratechange ' + // media 58 | 'volumechange cuechange ' + // media 59 | 'checking noupdate downloading cached updateready obsolete ' // appcache 60 | 61 | // convert to a hash for quick lookups 62 | , nativeEvents = (function (hash, events, i) { 63 | for (i = 0; i < events.length; i++) events[i] && (hash[events[i]] = 1) 64 | return hash 65 | }({}, str2arr(standardNativeEvents + (W3C_MODEL ? w3cNativeEvents : '')))) 66 | 67 | // custom events are events that we *fake*, they are not provided natively but 68 | // we can use native events to generate them 69 | , customEvents = (function () { 70 | var isAncestor = 'compareDocumentPosition' in root 71 | ? function (element, container) { 72 | return container.compareDocumentPosition && (container.compareDocumentPosition(element) & 16) === 16 73 | } 74 | : 'contains' in root 75 | ? function (element, container) { 76 | container = container.nodeType === 9 || container === window ? root : container 77 | return container !== element && container.contains(element) 78 | } 79 | : function (element, container) { 80 | while (element = element.parentNode) if (element === container) return 1 81 | return 0 82 | } 83 | , check = function (event) { 84 | var related = event.relatedTarget 85 | return !related 86 | ? related == null 87 | : (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) 88 | && !isAncestor(related, this)) 89 | } 90 | 91 | return { 92 | mouseenter: { base: 'mouseover', condition: check } 93 | , mouseleave: { base: 'mouseout', condition: check } 94 | , mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' } 95 | } 96 | }()) 97 | 98 | // we provide a consistent Event object across browsers by taking the actual DOM 99 | // event object and generating a new one from its properties. 100 | , Event = (function () { 101 | // a whitelist of properties (for different event types) tells us what to check for and copy 102 | var commonProps = str2arr('altKey attrChange attrName bubbles cancelable ctrlKey currentTarget ' + 103 | 'detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey ' + 104 | 'srcElement target timeStamp type view which propertyName') 105 | , mouseProps = commonProps.concat(str2arr('button buttons clientX clientY dataTransfer ' + 106 | 'fromElement offsetX offsetY pageX pageY screenX screenY toElement')) 107 | , mouseWheelProps = mouseProps.concat(str2arr('wheelDelta wheelDeltaX wheelDeltaY wheelDeltaZ ' + 108 | 'axis')) // 'axis' is FF specific 109 | , keyProps = commonProps.concat(str2arr('char charCode key keyCode keyIdentifier ' + 110 | 'keyLocation location')) 111 | , textProps = commonProps.concat(str2arr('data')) 112 | , touchProps = commonProps.concat(str2arr('touches targetTouches changedTouches scale rotation')) 113 | , messageProps = commonProps.concat(str2arr('data origin source')) 114 | , stateProps = commonProps.concat(str2arr('state')) 115 | , overOutRegex = /over|out/ 116 | // some event types need special handling and some need special properties, do that all here 117 | , typeFixers = [ 118 | { // key events 119 | reg: /key/i 120 | , fix: function (event, newEvent) { 121 | newEvent.keyCode = event.keyCode || event.which 122 | return keyProps 123 | } 124 | } 125 | , { // mouse events 126 | reg: /click|mouse(?!(.*wheel|scroll))|menu|drag|drop/i 127 | , fix: function (event, newEvent, type) { 128 | newEvent.rightClick = event.which === 3 || event.button === 2 129 | newEvent.pos = { x: 0, y: 0 } 130 | if (event.pageX || event.pageY) { 131 | newEvent.clientX = event.pageX 132 | newEvent.clientY = event.pageY 133 | } else if (event.clientX || event.clientY) { 134 | newEvent.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft 135 | newEvent.clientY = event.clientY + doc.body.scrollTop + root.scrollTop 136 | } 137 | if (overOutRegex.test(type)) { 138 | newEvent.relatedTarget = event.relatedTarget 139 | || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'] 140 | } 141 | return mouseProps 142 | } 143 | } 144 | , { // mouse wheel events 145 | reg: /mouse.*(wheel|scroll)/i 146 | , fix: function () { return mouseWheelProps } 147 | } 148 | , { // TextEvent 149 | reg: /^text/i 150 | , fix: function () { return textProps } 151 | } 152 | , { // touch and gesture events 153 | reg: /^touch|^gesture/i 154 | , fix: function () { return touchProps } 155 | } 156 | , { // message events 157 | reg: /^message$/i 158 | , fix: function () { return messageProps } 159 | } 160 | , { // popstate events 161 | reg: /^popstate$/i 162 | , fix: function () { return stateProps } 163 | } 164 | , { // everything else 165 | reg: /.*/ 166 | , fix: function () { return commonProps } 167 | } 168 | ] 169 | , typeFixerMap = {} // used to map event types to fixer functions (above), a basic cache mechanism 170 | 171 | , Event = function (event, element, isNative) { 172 | if (!arguments.length) return 173 | event = event || ((element.ownerDocument || element.document || element).parentWindow || win).event 174 | this.originalEvent = event 175 | this.isNative = isNative 176 | this.isBean = true 177 | 178 | if (!event) return 179 | 180 | var type = event.type 181 | , target = event.target || event.srcElement 182 | , i, l, p, props, fixer 183 | 184 | this.target = target && target.nodeType === 3 ? target.parentNode : target 185 | 186 | if (isNative) { // we only need basic augmentation on custom events, the rest expensive & pointless 187 | fixer = typeFixerMap[type] 188 | if (!fixer) { // haven't encountered this event type before, map a fixer function for it 189 | for (i = 0, l = typeFixers.length; i < l; i++) { 190 | if (typeFixers[i].reg.test(type)) { // guaranteed to match at least one, last is .* 191 | typeFixerMap[type] = fixer = typeFixers[i].fix 192 | break 193 | } 194 | } 195 | } 196 | 197 | props = fixer(event, this, type) 198 | for (i = props.length; i--;) { 199 | if (!((p = props[i]) in this) && p in event) this[p] = event[p] 200 | } 201 | } 202 | } 203 | 204 | // preventDefault() and stopPropagation() are a consistent interface to those functions 205 | // on the DOM, stop() is an alias for both of them together 206 | Event.prototype.preventDefault = function () { 207 | if (this.originalEvent.preventDefault) this.originalEvent.preventDefault() 208 | else this.originalEvent.returnValue = false 209 | } 210 | Event.prototype.stopPropagation = function () { 211 | if (this.originalEvent.stopPropagation) this.originalEvent.stopPropagation() 212 | else this.originalEvent.cancelBubble = true 213 | } 214 | Event.prototype.stop = function () { 215 | this.preventDefault() 216 | this.stopPropagation() 217 | this.stopped = true 218 | } 219 | // stopImmediatePropagation() has to be handled internally because we manage the event list for 220 | // each element 221 | // note that originalElement may be a Bean#Event object in some situations 222 | Event.prototype.stopImmediatePropagation = function () { 223 | if (this.originalEvent.stopImmediatePropagation) this.originalEvent.stopImmediatePropagation() 224 | this.isImmediatePropagationStopped = function () { return true } 225 | } 226 | Event.prototype.isImmediatePropagationStopped = function () { 227 | return this.originalEvent.isImmediatePropagationStopped && this.originalEvent.isImmediatePropagationStopped() 228 | } 229 | Event.prototype.clone = function (currentTarget) { 230 | //TODO: this is ripe for optimisation, new events are *expensive* 231 | // improving this will speed up delegated events 232 | var ne = new Event(this, this.element, this.isNative) 233 | ne.currentTarget = currentTarget 234 | return ne 235 | } 236 | 237 | return Event 238 | }()) 239 | 240 | // if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both 241 | , targetElement = function (element, isNative) { 242 | return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element 243 | } 244 | 245 | /** 246 | * Bean maintains an internal registry for event listeners. We don't touch elements, objects 247 | * or functions to identify them, instead we store everything in the registry. 248 | * Each event listener has a RegEntry object, we have one 'registry' for the whole instance. 249 | */ 250 | , RegEntry = (function () { 251 | // each handler is wrapped so we can handle delegation and custom events 252 | var wrappedHandler = function (element, fn, condition, args) { 253 | var call = function (event, eargs) { 254 | return fn.apply(element, args ? slice.call(eargs, event ? 0 : 1).concat(args) : eargs) 255 | } 256 | , findTarget = function (event, eventElement) { 257 | return fn.__beanDel ? fn.__beanDel.ft(event.target, element) : eventElement 258 | } 259 | , handler = condition 260 | ? function (event) { 261 | var target = findTarget(event, this) // deleated event 262 | if (condition.apply(target, arguments)) { 263 | if (event) event.currentTarget = target 264 | return call(event, arguments) 265 | } 266 | } 267 | : function (event) { 268 | if (fn.__beanDel) event = event.clone(findTarget(event)) // delegated event, fix the fix 269 | return call(event, arguments) 270 | } 271 | handler.__beanDel = fn.__beanDel 272 | return handler 273 | } 274 | 275 | , RegEntry = function (element, type, handler, original, namespaces, args, root) { 276 | var customType = customEvents[type] 277 | , isNative 278 | 279 | if (type == 'unload') { 280 | // self clean-up 281 | handler = once(removeListener, element, type, handler, original) 282 | } 283 | 284 | if (customType) { 285 | if (customType.condition) { 286 | handler = wrappedHandler(element, handler, customType.condition, args) 287 | } 288 | type = customType.base || type 289 | } 290 | 291 | this.isNative = isNative = nativeEvents[type] && !!element[eventSupport] 292 | this.customType = !W3C_MODEL && !isNative && type 293 | this.element = element 294 | this.type = type 295 | this.original = original 296 | this.namespaces = namespaces 297 | this.eventType = W3C_MODEL || isNative ? type : 'propertychange' 298 | this.target = targetElement(element, isNative) 299 | this[eventSupport] = !!this.target[eventSupport] 300 | this.root = root 301 | this.handler = wrappedHandler(element, handler, null, args) 302 | } 303 | 304 | // given a list of namespaces, is our entry in any of them? 305 | RegEntry.prototype.inNamespaces = function (checkNamespaces) { 306 | var i, j, c = 0 307 | if (!checkNamespaces) return true 308 | if (!this.namespaces) return false 309 | for (i = checkNamespaces.length; i--;) { 310 | for (j = this.namespaces.length; j--;) { 311 | if (checkNamespaces[i] == this.namespaces[j]) c++ 312 | } 313 | } 314 | return checkNamespaces.length === c 315 | } 316 | 317 | // match by element, original fn (opt), handler fn (opt) 318 | RegEntry.prototype.matches = function (checkElement, checkOriginal, checkHandler) { 319 | return this.element === checkElement && 320 | (!checkOriginal || this.original === checkOriginal) && 321 | (!checkHandler || this.handler === checkHandler) 322 | } 323 | 324 | return RegEntry 325 | }()) 326 | 327 | , registry = (function () { 328 | // our map stores arrays by event type, just because it's better than storing 329 | // everything in a single array. 330 | // uses '$' as a prefix for the keys for safety and 'r' as a special prefix for 331 | // rootListeners so we can look them up fast 332 | var map = {} 333 | 334 | // generic functional search of our registry for matching listeners, 335 | // `fn` returns false to break out of the loop 336 | , forAll = function (element, type, original, handler, root, fn) { 337 | var pfx = root ? 'r' : '$' 338 | if (!type || type == '*') { 339 | // search the whole registry 340 | for (var t in map) { 341 | if (t.charAt(0) == pfx) { 342 | forAll(element, t.substr(1), original, handler, root, fn) 343 | } 344 | } 345 | } else { 346 | var i = 0, l, list = map[pfx + type], all = element == '*' 347 | if (!list) return 348 | for (l = list.length; i < l; i++) { 349 | if ((all || list[i].matches(element, original, handler)) && !fn(list[i], list, i, type)) return 350 | } 351 | } 352 | } 353 | 354 | , has = function (element, type, original, root) { 355 | // we're not using forAll here simply because it's a bit slower and this 356 | // needs to be fast 357 | var i, list = map[(root ? 'r' : '$') + type] 358 | if (list) { 359 | for (i = list.length; i--;) { 360 | if (!list[i].root && list[i].matches(element, original, null)) return true 361 | } 362 | } 363 | return false 364 | } 365 | 366 | , get = function (element, type, original, root) { 367 | var entries = [] 368 | forAll(element, type, original, null, root, function (entry) { 369 | return entries.push(entry) 370 | }) 371 | return entries 372 | } 373 | 374 | , put = function (entry) { 375 | var has = !entry.root && !this.has(entry.element, entry.type, null, false) 376 | , key = (entry.root ? 'r' : '$') + entry.type 377 | ;(map[key] || (map[key] = [])).push(entry) 378 | return has 379 | } 380 | 381 | , del = function (entry) { 382 | forAll(entry.element, entry.type, null, entry.handler, entry.root, function (entry, list, i) { 383 | list.splice(i, 1) 384 | entry.removed = true 385 | if (list.length === 0) delete map[(entry.root ? 'r' : '$') + entry.type] 386 | return false 387 | }) 388 | } 389 | 390 | // dump all entries, used for onunload 391 | , entries = function () { 392 | var t, entries = [] 393 | for (t in map) { 394 | if (t.charAt(0) == '$') entries = entries.concat(map[t]) 395 | } 396 | return entries 397 | } 398 | 399 | return { has: has, get: get, put: put, del: del, entries: entries } 400 | }()) 401 | 402 | // we need a selector engine for delegated events, use querySelectorAll if it exists 403 | // but for older browsers we need Qwery, Sizzle or similar 404 | , selectorEngine 405 | , setSelectorEngine = function (e) { 406 | if (!arguments.length) { 407 | selectorEngine = doc.querySelectorAll 408 | ? function (s, r) { 409 | return r.querySelectorAll(s) 410 | } 411 | : function () { 412 | throw new Error('Bean: No selector engine installed') // eeek 413 | } 414 | } else { 415 | selectorEngine = e 416 | } 417 | } 418 | 419 | // we attach this listener to each DOM event that we need to listen to, only once 420 | // per event type per DOM element 421 | , rootListener = function (event, type) { 422 | if (!W3C_MODEL && type && event && event.propertyName != '_on' + type) return 423 | 424 | var listeners = registry.get(this, type || event.type, null, false) 425 | , l = listeners.length 426 | , i = 0 427 | 428 | event = new Event(event, this, true) 429 | if (type) event.type = type 430 | 431 | // iterate through all handlers registered for this type, calling them unless they have 432 | // been removed by a previous handler or stopImmediatePropagation() has been called 433 | for (; i < l && !event.isImmediatePropagationStopped(); i++) { 434 | if (!listeners[i].removed) listeners[i].handler.call(this, event) 435 | } 436 | } 437 | 438 | // add and remove listeners to DOM elements 439 | , listener = W3C_MODEL 440 | ? function (element, type, add) { 441 | // new browsers 442 | element[add ? addEvent : removeEvent](type, rootListener, false) 443 | } 444 | : function (element, type, add, custom) { 445 | // IE8 and below, use attachEvent/detachEvent and we have to piggy-back propertychange events 446 | // to simulate event bubbling etc. 447 | var entry 448 | if (add) { 449 | registry.put(entry = new RegEntry( 450 | element 451 | , custom || type 452 | , function (event) { // handler 453 | rootListener.call(element, event, custom) 454 | } 455 | , rootListener 456 | , null 457 | , null 458 | , true // is root 459 | )) 460 | if (custom && element['_on' + custom] == null) element['_on' + custom] = 0 461 | entry.target.attachEvent('on' + entry.eventType, entry.handler) 462 | } else { 463 | entry = registry.get(element, custom || type, rootListener, true)[0] 464 | if (entry) { 465 | entry.target.detachEvent('on' + entry.eventType, entry.handler) 466 | registry.del(entry) 467 | } 468 | } 469 | } 470 | 471 | , once = function (rm, element, type, fn, originalFn) { 472 | // wrap the handler in a handler that does a remove as well 473 | return function () { 474 | fn.apply(this, arguments) 475 | rm(element, type, originalFn) 476 | } 477 | } 478 | 479 | , removeListener = function (element, orgType, handler, namespaces) { 480 | var type = orgType && orgType.replace(nameRegex, '') 481 | , handlers = registry.get(element, type, null, false) 482 | , removed = {} 483 | , i, l 484 | 485 | for (i = 0, l = handlers.length; i < l; i++) { 486 | if ((!handler || handlers[i].original === handler) && handlers[i].inNamespaces(namespaces)) { 487 | // TODO: this is problematic, we have a registry.get() and registry.del() that 488 | // both do registry searches so we waste cycles doing this. Needs to be rolled into 489 | // a single registry.forAll(fn) that removes while finding, but the catch is that 490 | // we'll be splicing the arrays that we're iterating over. Needs extra tests to 491 | // make sure we don't screw it up. @rvagg 492 | registry.del(handlers[i]) 493 | if (!removed[handlers[i].eventType] && handlers[i][eventSupport]) 494 | removed[handlers[i].eventType] = { t: handlers[i].eventType, c: handlers[i].type } 495 | } 496 | } 497 | // check each type/element for removed listeners and remove the rootListener where it's no longer needed 498 | for (i in removed) { 499 | if (!registry.has(element, removed[i].t, null, false)) { 500 | // last listener of this type, remove the rootListener 501 | listener(element, removed[i].t, false, removed[i].c) 502 | } 503 | } 504 | } 505 | 506 | // set up a delegate helper using the given selector, wrap the handler function 507 | , delegate = function (selector, fn) { 508 | //TODO: findTarget (therefore $) is called twice, once for match and once for 509 | // setting e.currentTarget, fix this so it's only needed once 510 | var findTarget = function (target, root) { 511 | var i, array = isString(selector) ? selectorEngine(selector, root) : selector 512 | for (; target && target !== root; target = target.parentNode) { 513 | for (i = array.length; i--;) { 514 | if (array[i] === target) return target 515 | } 516 | } 517 | } 518 | , handler = function (e) { 519 | var match = findTarget(e.target, this) 520 | if (match) fn.apply(match, arguments) 521 | } 522 | 523 | // __beanDel isn't pleasant but it's a private function, not exposed outside of Bean 524 | handler.__beanDel = { 525 | ft : findTarget // attach it here for customEvents to use too 526 | , selector : selector 527 | } 528 | return handler 529 | } 530 | 531 | , fireListener = W3C_MODEL ? function (isNative, type, element) { 532 | // modern browsers, do a proper dispatchEvent() 533 | var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents') 534 | evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1) 535 | element.dispatchEvent(evt) 536 | } : function (isNative, type, element) { 537 | // old browser use onpropertychange, just increment a custom property to trigger the event 538 | element = targetElement(element, isNative) 539 | isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++ 540 | } 541 | 542 | /** 543 | * Public API: off(), on(), add(), (remove()), one(), fire(), clone() 544 | */ 545 | 546 | /** 547 | * off(element[, eventType(s)[, handler ]]) 548 | */ 549 | , off = function (element, typeSpec, fn) { 550 | var isTypeStr = isString(typeSpec) 551 | , k, type, namespaces, i 552 | 553 | if (isTypeStr && typeSpec.indexOf(' ') > 0) { 554 | // off(el, 't1 t2 t3', fn) or off(el, 't1 t2 t3') 555 | typeSpec = str2arr(typeSpec) 556 | for (i = typeSpec.length; i--;) 557 | off(element, typeSpec[i], fn) 558 | return element 559 | } 560 | 561 | type = isTypeStr && typeSpec.replace(nameRegex, '') 562 | if (type && customEvents[type]) type = customEvents[type].base 563 | 564 | if (!typeSpec || isTypeStr) { 565 | // off(el) or off(el, t1.ns) or off(el, .ns) or off(el, .ns1.ns2.ns3) 566 | if (namespaces = isTypeStr && typeSpec.replace(namespaceRegex, '')) namespaces = str2arr(namespaces, '.') 567 | removeListener(element, type, fn, namespaces) 568 | } else if (isFunction(typeSpec)) { 569 | // off(el, fn) 570 | removeListener(element, null, typeSpec) 571 | } else { 572 | // off(el, { t1: fn1, t2, fn2 }) 573 | for (k in typeSpec) { 574 | if (typeSpec.hasOwnProperty(k)) off(element, k, typeSpec[k]) 575 | } 576 | } 577 | 578 | return element 579 | } 580 | 581 | /** 582 | * on(element, eventType(s)[, selector], handler[, args ]) 583 | */ 584 | , on = function(element, events, selector, fn) { 585 | var originalFn, type, types, i, args, entry, first 586 | 587 | //TODO: the undefined check means you can't pass an 'args' argument, fix this perhaps? 588 | if (selector === undefined && typeof events == 'object') { 589 | //TODO: this can't handle delegated events 590 | for (type in events) { 591 | if (events.hasOwnProperty(type)) { 592 | on.call(this, element, type, events[type]) 593 | } 594 | } 595 | return 596 | } 597 | 598 | if (!isFunction(selector)) { 599 | // delegated event 600 | originalFn = fn 601 | args = slice.call(arguments, 4) 602 | fn = delegate(selector, originalFn, selectorEngine) 603 | } else { 604 | args = slice.call(arguments, 3) 605 | fn = originalFn = selector 606 | } 607 | 608 | types = str2arr(events) 609 | 610 | // special case for one(), wrap in a self-removing handler 611 | if (this === ONE) { 612 | fn = once(off, element, events, fn, originalFn) 613 | } 614 | 615 | for (i = types.length; i--;) { 616 | // add new handler to the registry and check if it's the first for this element/type 617 | first = registry.put(entry = new RegEntry( 618 | element 619 | , types[i].replace(nameRegex, '') // event type 620 | , fn 621 | , originalFn 622 | , str2arr(types[i].replace(namespaceRegex, ''), '.') // namespaces 623 | , args 624 | , false // not root 625 | )) 626 | if (entry[eventSupport] && first) { 627 | // first event of this type on this element, add root listener 628 | listener(element, entry.eventType, true, entry.customType) 629 | } 630 | } 631 | 632 | return element 633 | } 634 | 635 | /** 636 | * add(element[, selector], eventType(s), handler[, args ]) 637 | * 638 | * Deprecated: kept (for now) for backward-compatibility 639 | */ 640 | , add = function (element, events, fn, delfn) { 641 | return on.apply( 642 | null 643 | , !isString(fn) 644 | ? slice.call(arguments) 645 | : [ element, fn, events, delfn ].concat(arguments.length > 3 ? slice.call(arguments, 5) : []) 646 | ) 647 | } 648 | 649 | /** 650 | * one(element, eventType(s)[, selector], handler[, args ]) 651 | */ 652 | , one = function () { 653 | return on.apply(ONE, arguments) 654 | } 655 | 656 | /** 657 | * fire(element, eventType(s)[, args ]) 658 | * 659 | * The optional 'args' argument must be an array, if no 'args' argument is provided 660 | * then we can use the browser's DOM event system, otherwise we trigger handlers manually 661 | */ 662 | , fire = function (element, type, args) { 663 | var types = str2arr(type) 664 | , i, j, l, names, handlers 665 | 666 | for (i = types.length; i--;) { 667 | type = types[i].replace(nameRegex, '') 668 | if (names = types[i].replace(namespaceRegex, '')) names = str2arr(names, '.') 669 | if (!names && !args && element[eventSupport]) { 670 | fireListener(nativeEvents[type], type, element) 671 | } else { 672 | // non-native event, either because of a namespace, arguments or a non DOM element 673 | // iterate over all listeners and manually 'fire' 674 | handlers = registry.get(element, type, null, false) 675 | args = [false].concat(args) 676 | for (j = 0, l = handlers.length; j < l; j++) { 677 | if (handlers[j].inNamespaces(names)) { 678 | handlers[j].handler.apply(element, args) 679 | } 680 | } 681 | } 682 | } 683 | return element 684 | } 685 | 686 | /** 687 | * clone(dstElement, srcElement[, eventType ]) 688 | * 689 | * TODO: perhaps for consistency we should allow the same flexibility in type specifiers? 690 | */ 691 | , clone = function (element, from, type) { 692 | var handlers = registry.get(from, type, null, false) 693 | , l = handlers.length 694 | , i = 0 695 | , args, beanDel 696 | 697 | for (; i < l; i++) { 698 | if (handlers[i].original) { 699 | args = [ element, handlers[i].type ] 700 | if (beanDel = handlers[i].handler.__beanDel) args.push(beanDel.selector) 701 | args.push(handlers[i].original) 702 | on.apply(null, args) 703 | } 704 | } 705 | return element 706 | } 707 | 708 | , bean = { 709 | on : on 710 | , add : add 711 | , one : one 712 | , off : off 713 | , remove : off 714 | , clone : clone 715 | , fire : fire 716 | , setSelectorEngine : setSelectorEngine 717 | , noConflict : function () { 718 | context[name] = old 719 | return this 720 | } 721 | } 722 | 723 | // for IE, clean up on unload to avoid leaks 724 | if (win.attachEvent) { 725 | var cleanup = function () { 726 | var i, entries = registry.entries() 727 | for (i in entries) { 728 | if (entries[i].type && entries[i].type !== 'unload') off(entries[i].element, entries[i].type) 729 | } 730 | win.detachEvent('onunload', cleanup) 731 | win.CollectGarbage && win.CollectGarbage() 732 | } 733 | win.attachEvent('onunload', cleanup) 734 | } 735 | 736 | // initialize selector engine to internal default (qSA or throw Error) 737 | setSelectorEngine() 738 | 739 | return bean 740 | }); -------------------------------------------------------------------------------- /js/bonzo.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bonzo: DOM Utility (c) Dustin Diaz 2012 3 | * https://github.com/ded/bonzo 4 | * License MIT 5 | */ 6 | (function (name, context, definition) { 7 | if (typeof module != 'undefined' && module.exports) module.exports = definition() 8 | else if (typeof define == 'function' && define.amd) define(definition) 9 | else context[name] = definition() 10 | })('bonzo', this, function() { 11 | var win = window 12 | , doc = win.document 13 | , html = doc.documentElement 14 | , parentNode = 'parentNode' 15 | , specialAttributes = /^(checked|value|selected|disabled)$/i 16 | , specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i // tags that we have trouble inserting *into* 17 | , simpleScriptTagRe = /\s*