├── demo ├── p.gif ├── s.gif ├── arch.png ├── pathbuilder.png ├── pathslider.png ├── colorpicker │ ├── blank.gif │ ├── slider.png │ ├── select2.png │ ├── custom_hex.png │ ├── custom_hsb_b.png │ ├── custom_hsb_h.png │ ├── custom_hsb_s.png │ ├── custom_indic.gif │ ├── custom_rgb_b.png │ ├── custom_rgb_g.png │ ├── custom_rgb_r.png │ ├── custom_submit.png │ ├── colorpicker_overlay.png │ ├── colorpicker_select.gif │ ├── custom_background.png │ ├── colorpicker.css │ └── colorpicker.js ├── wiki │ ├── coordinate-system.png │ └── pathslider-points.png ├── demo.css └── demo.js ├── images ├── chrome1.png └── chrome2.png ├── .gitattributes ├── LICENSE ├── package.json ├── Gruntfile.js ├── .gitignore ├── index.html ├── README.md ├── dist ├── pathslider.min.css ├── jquery.pathslider.min.js ├── pathslider.css └── jquery.pathslider.js ├── css └── pathslider.css └── js ├── jquery.pathslider.builder.js └── jquery.pathslider.js /demo/p.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/p.gif -------------------------------------------------------------------------------- /demo/s.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/s.gif -------------------------------------------------------------------------------- /demo/arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/arch.png -------------------------------------------------------------------------------- /demo/pathbuilder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/pathbuilder.png -------------------------------------------------------------------------------- /demo/pathslider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/pathslider.png -------------------------------------------------------------------------------- /images/chrome1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/images/chrome1.png -------------------------------------------------------------------------------- /images/chrome2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/images/chrome2.png -------------------------------------------------------------------------------- /demo/colorpicker/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/blank.gif -------------------------------------------------------------------------------- /demo/colorpicker/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/slider.png -------------------------------------------------------------------------------- /demo/colorpicker/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/select2.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_hex.png -------------------------------------------------------------------------------- /demo/wiki/coordinate-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/wiki/coordinate-system.png -------------------------------------------------------------------------------- /demo/wiki/pathslider-points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/wiki/pathslider-points.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_hsb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_hsb_b.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_hsb_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_hsb_h.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_hsb_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_hsb_s.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_indic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_indic.gif -------------------------------------------------------------------------------- /demo/colorpicker/custom_rgb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_rgb_b.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_rgb_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_rgb_g.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_rgb_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_rgb_r.png -------------------------------------------------------------------------------- /demo/colorpicker/custom_submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_submit.png -------------------------------------------------------------------------------- /demo/colorpicker/colorpicker_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/colorpicker_overlay.png -------------------------------------------------------------------------------- /demo/colorpicker/colorpicker_select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/colorpicker_select.gif -------------------------------------------------------------------------------- /demo/colorpicker/custom_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mottie/Pathslider/HEAD/demo/colorpicker/custom_background.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | *.md diss=astextplain -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Rob Garrison 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 9 | Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pathslider", 3 | "title": "Pathslider", 4 | "description": "A jQuery numerical slider that follows a bezier path", 5 | "version": "1.0.0-beta", 6 | "author": { 7 | "name": "Rob Garrison", 8 | "url": "https://github.com/Mottie", 9 | "email": "wowmotty@gmail.com" 10 | }, 11 | "license": "MIT", 12 | "dependencies": { 13 | "jquery": ">=1.4.4" 14 | }, 15 | "keywords": [ 16 | "slider", 17 | "number", 18 | "bezier", 19 | "path", 20 | "jquery-plugin" 21 | ], 22 | "homepage": "https://github.com/Mottie/Pathslider", 23 | "demo": "http://mottie.github.io/Pathslider/", 24 | "docs": "https://github.com/Mottie/Pathslider/wiki", 25 | "bugs": "https://github.com/Mottie/Pathslider/issues", 26 | "main": "dist/jquery.pathslider.js", 27 | "files": [ 28 | "dist/*" 29 | ], 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/Mottie/pathslider.git" 33 | }, 34 | "devDependencies": { 35 | "grunt": "^1.0.1", 36 | "grunt-cli": "^1.1.0", 37 | "grunt-contrib-clean": "^1.0.0", 38 | "grunt-contrib-copy": "^1.0.0", 39 | "grunt-contrib-cssmin": "^1.0.1", 40 | "grunt-contrib-jshint": "^1.0.0", 41 | "grunt-contrib-uglify": "^2.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false */ 2 | module.exports = function(grunt) { 3 | 'use strict'; 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | pkg: grunt.file.readJSON('package.json'), 8 | 9 | clean: { 10 | dist: { 11 | src: ['dist/*', 'dist/**/*'] 12 | } 13 | }, 14 | 15 | copy: { 16 | dist: { 17 | files : [{ 18 | expand: true, 19 | dot: true, 20 | flatten: true, 21 | src: [ 'css/pathslider.css', 'js/jquery.pathslider.js' ], 22 | dest: 'dist/' 23 | }] 24 | } 25 | }, 26 | 27 | jshint: { 28 | core: { 29 | options: { 30 | "jquery": true, 31 | "browser": true 32 | }, 33 | src: [ 'js/jquery.*.js' ] 34 | } 35 | }, 36 | 37 | cssmin: { 38 | target: { 39 | files: [{ 40 | expand: true, 41 | flatten: true, 42 | src: ['css/*.css'], 43 | dest: 'dist/', 44 | ext: '.min.css' 45 | }] 46 | } 47 | }, 48 | 49 | uglify: { 50 | options: { 51 | preserveComments: function( node, comment ) { 52 | return /^!/.test( comment.value ); 53 | }, 54 | report: 'gzip' 55 | }, 56 | dist: { 57 | files: [{ 58 | expand: true, 59 | cwd: '', 60 | src: [ 'js/jquery*.js', '!js/*builder.js' ], 61 | dest: 'dist/', 62 | ext: '.min.js', 63 | extDot: 'last', 64 | flatten: true 65 | }] 66 | } 67 | }, 68 | 69 | watch: { 70 | scripts: { 71 | files: ['js/*.js'], 72 | tasks: ['build'] 73 | } 74 | } 75 | 76 | }); 77 | 78 | grunt.loadNpmTasks('grunt-contrib-clean'); 79 | grunt.loadNpmTasks('grunt-contrib-copy'); 80 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 81 | grunt.loadNpmTasks('grunt-contrib-jshint'); 82 | grunt.loadNpmTasks('grunt-contrib-uglify'); 83 | 84 | // Default task. 85 | grunt.registerTask('default', [ 86 | 'clean', 87 | 'jshint', 88 | 'copy', 89 | 'cssmin', 90 | 'uglify' 91 | ]); 92 | 93 | }; 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *_test*.* 11 | *_idea*.* 12 | *.tmp 13 | *.bak 14 | *.swp 15 | *~.nib 16 | local.properties 17 | .classpath 18 | .settings/ 19 | .loadpath 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # CDT-specific 28 | .cproject 29 | 30 | # PDT-specific 31 | .buildpath 32 | 33 | 34 | ################# 35 | ## Visual Studio 36 | ################# 37 | 38 | ## Ignore Visual Studio temporary files, build results, and 39 | ## files generated by popular Visual Studio add-ons. 40 | 41 | # User-specific files 42 | *.suo 43 | *.user 44 | *.sln.docstates 45 | 46 | # Build results 47 | [Dd]ebug/ 48 | [Rr]elease/ 49 | node_modules/ 50 | *_i.c 51 | *_p.c 52 | *.ilk 53 | *.log 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.vspscc 67 | .builds 68 | *.dotCover 69 | 70 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 71 | #packages/ 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | 80 | # Visual Studio profiler 81 | *.psess 82 | *.vsp 83 | 84 | # ReSharper is a .NET coding add-in 85 | _ReSharper* 86 | 87 | # Installshield output folder 88 | [Ee]xpress 89 | 90 | # DocProject is a documentation generator add-in 91 | DocProject/buildhelp/ 92 | DocProject/Help/*.HxT 93 | DocProject/Help/*.HxC 94 | DocProject/Help/*.hhc 95 | DocProject/Help/*.hhk 96 | DocProject/Help/*.hhp 97 | DocProject/Help/Html2 98 | DocProject/Help/html 99 | 100 | # Click-Once directory 101 | publish 102 | 103 | # Others 104 | [Bb]in 105 | [Oo]bj 106 | sql 107 | TestResults 108 | *.Cache 109 | ClientBin 110 | stylecop.* 111 | ~$* 112 | *.dbmdl 113 | Generated_Code #added for RIA/Silverlight projects 114 | 115 | # Backup & report files from converting an old project file to a newer 116 | # Visual Studio version. Backup files are not needed, because we have git ;-) 117 | _UpgradeReport_Files/ 118 | Backup*/ 119 | UpgradeLog*.XML 120 | 121 | 122 | 123 | ############ 124 | ## Windows 125 | ############ 126 | 127 | # Windows image file caches 128 | Thumbs.db 129 | 130 | # Folder config file 131 | Desktop.ini 132 | 133 | 134 | ############# 135 | ## Python 136 | ############# 137 | 138 | *.py[co] 139 | 140 | # Packages 141 | *.egg 142 | *.egg-info 143 | build 144 | eggs 145 | parts 146 | bin 147 | var 148 | sdist 149 | develop-eggs 150 | .installed.cfg 151 | 152 | # Installer logs 153 | pip-log.txt 154 | 155 | # Unit test / coverage reports 156 | .coverage 157 | .tox 158 | 159 | #Translations 160 | *.mo 161 | 162 | #Mr Developer 163 | .mr.developer.cfg 164 | 165 | # Mac crap 166 | .DS_Store 167 | -------------------------------------------------------------------------------- /demo/colorpicker/colorpicker.css: -------------------------------------------------------------------------------- 1 | .colorpicker { 2 | width: 356px; 3 | height: 176px; 4 | overflow: hidden; 5 | position: absolute; 6 | background: url(custom_background.png); 7 | font-family: Arial, Helvetica, sans-serif; 8 | display: none; 9 | } 10 | .colorpicker_color { 11 | width: 150px; 12 | height: 150px; 13 | left: 14px; 14 | top: 13px; 15 | position: absolute; 16 | background: #f00; 17 | overflow: hidden; 18 | cursor: crosshair; 19 | } 20 | .colorpicker_color div { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | width: 150px; 25 | height: 150px; 26 | background: url(colorpicker_overlay.png); 27 | } 28 | .colorpicker_color div div { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | width: 11px; 33 | height: 11px; 34 | overflow: hidden; 35 | background: url(colorpicker_select.gif); 36 | margin: -5px 0 0 -5px; 37 | } 38 | .colorpicker_hue { 39 | position: absolute; 40 | top: 13px; 41 | left: 171px; 42 | width: 35px; 43 | height: 150px; 44 | cursor: n-resize; 45 | } 46 | .colorpicker_hue div { 47 | position: absolute; 48 | width: 35px; 49 | height: 9px; 50 | overflow: hidden; 51 | background: url(custom_indic.gif) left top; 52 | margin: -4px 0 0 0; 53 | left: 0px; 54 | } 55 | .colorpicker_new_color { 56 | position: absolute; 57 | width: 60px; 58 | height: 30px; 59 | left: 213px; 60 | top: 13px; 61 | background: #f00; 62 | } 63 | .colorpicker_current_color { 64 | position: absolute; 65 | width: 60px; 66 | height: 30px; 67 | left: 283px; 68 | top: 13px; 69 | background: #f00; 70 | } 71 | .colorpicker input { 72 | background-color: transparent; 73 | border: 1px solid transparent; 74 | position: absolute; 75 | font-size: 10px; 76 | font-family: Arial, Helvetica, sans-serif; 77 | color: #898989; 78 | top: 4px; 79 | right: 11px; 80 | text-align: right; 81 | margin: 0; 82 | padding: 0; 83 | height: 11px; 84 | } 85 | .colorpicker_hex { 86 | position: absolute; 87 | width: 72px; 88 | height: 22px; 89 | background: url(custom_hex.png) top; 90 | left: 212px; 91 | top: 142px; 92 | } 93 | .colorpicker_hex input { 94 | right: 6px; 95 | } 96 | .colorpicker_field { 97 | height: 22px; 98 | width: 62px; 99 | background-position: top; 100 | position: absolute; 101 | } 102 | .colorpicker_field span { 103 | position: absolute; 104 | width: 12px; 105 | height: 22px; 106 | overflow: hidden; 107 | top: 0; 108 | right: 0; 109 | cursor: n-resize; 110 | } 111 | .colorpicker_rgb_r { 112 | background-image: url(custom_rgb_r.png); 113 | top: 52px; 114 | left: 212px; 115 | } 116 | .colorpicker_rgb_g { 117 | background-image: url(custom_rgb_g.png); 118 | top: 82px; 119 | left: 212px; 120 | } 121 | .colorpicker_rgb_b { 122 | background-image: url(custom_rgb_b.png); 123 | top: 112px; 124 | left: 212px; 125 | } 126 | .colorpicker_hsb_h { 127 | background-image: url(custom_hsb_h.png); 128 | top: 52px; 129 | left: 282px; 130 | } 131 | .colorpicker_hsb_s { 132 | background-image: url(custom_hsb_s.png); 133 | top: 82px; 134 | left: 282px; 135 | } 136 | .colorpicker_hsb_b { 137 | background-image: url(custom_hsb_b.png); 138 | top: 112px; 139 | left: 282px; 140 | } 141 | .colorpicker_submit { 142 | position: absolute; 143 | width: 22px; 144 | height: 22px; 145 | background: url(custom_submit.png) top; 146 | left: 322px; 147 | top: 142px; 148 | overflow: hidden; 149 | } 150 | .colorpicker_focus { 151 | background-position: center; 152 | } 153 | .colorpicker_hex.colorpicker_focus { 154 | background-position: bottom; 155 | } 156 | .colorpicker_submit.colorpicker_focus { 157 | background-position: bottom; 158 | } 159 | .colorpicker_slider { 160 | background-position: bottom; 161 | } 162 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jQuery Pathslider 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 41 | 95 | 96 | 97 | 98 |
99 | 100 |
101 |

jQuery Pathslider

102 | Home 103 | Builder 104 |
105 | 106 |
107 | 108 |
109 |
Basic Curve
110 |
111 |
112 | 113 |
114 |
"P" Curve
115 |
116 |
117 | 118 |
119 |
"S" Curve
120 |
121 |
122 | 123 |
124 | 125 |
126 | 127 |
128 | 129 | 141 | 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A jQuery numerical slider that follows a bezier path. 2 | 3 | [![devDependency Status][david-dev-image]][david-dev-url] [![MIT][license-image]][license-url] 4 | 5 | ## Features 6 | 7 | * Numerical slider similar to the [jQuery UI Slider](http://jqueryui.com/demos/slider/) - currently it only works with percentages (0-100%) 8 | * This slider follows a bezier path. The parameters are set using the pathslider builder. 9 | * Designed to work in older browsers (no canvas support), just add the path as a background image. 10 | * Get and set the slider position dynamically. 11 | * Slider handle (grip) can be set to rotate along with the angle of the curve (uses css3). 12 | * Callback events are available: create, update, start, slide, change, and stop. 13 | * The slider itself works in all browsers: IE6+, Opera, Chrome, Firefox and Safari (known issues below) 14 | * [Pathslider demo](http://mottie.github.com/Pathslider/index.html). 15 | * [Pathslider Builder](http://mottie.github.com/Pathslider/builder.html). 16 | 17 | ## Documentation 18 | 19 | Wiki: 20 | [Home](https://github.com/Mottie/Pathslider/wiki/Home) | 21 | [FAQ](https://github.com/Mottie/Pathslider/wiki/FAQ) | 22 | [Setup](https://github.com/Mottie/Pathslider/wiki/Setup) | 23 | [Options](https://github.com/Mottie/Pathslider/wiki/Options) | 24 | [Events](https://github.com/Mottie/Pathslider/wiki/Events) | 25 | [Theme](https://github.com/Mottie/Pathslider/wiki/Theme) | 26 | [Change](https://github.com/Mottie/Pathslider/wiki/Change) 27 | 28 | ## To Do: 29 | 30 | * Add min, max and step options to use values, other than zero to one hundred percent, with the slider. 31 | * Add enable, disable, destroy methods. 32 | * Smooth out sliding by improving grip position calculation 33 | * The grip jumps around quite a bit with some settings 34 | * This can be minimized by adjusting the tolerance & range settings, but it needs a better method. 35 | * Add more default handle (grip) styles in the css. 36 | * Add ability to make a gradient/patterned stroke style of the curve; I'm not sure it will follow the path of the curve or just be a static background. Needs more testing! 37 | * Add keyboard control to the slider when it has focus - arrows, page up/down, home and end. 38 | * Add a circular path 39 | * I don't plan on making it go continuously around in a circle. It will have a starting and stopping point. 40 | * If you need a continuous circle slider, check out [this one](http://www.eleqtriq.com/2009/12/javascriptdialcontrol/), and [this one](http://www.baijs.nl/tinycircleslider/). 41 | * Combine multiple beziers to make extended shapes - my math skills are a bit lacking so this may take a while, unless someone out there is willing to help ;) 42 | * Consider switching from using canvas to svg. 43 | 44 | ## Dependencies 45 | 46 | * jQuery 1.4.4+ 47 | * A browser that supports canvas (needed for the builder; optional for the pathslider plugin itself). 48 | * A browser that supports css3 transforms (necessary to rotate the grip). 49 | 50 | ## Known Bugs 51 | 52 | * The grip/handle will work if the loop folds back on itself - try these points to see an example: 75,200,200,-125,-200,0,225,200 53 | * The S-Curve demo seems to get stuck near 100% but only in Firefox and on the demo page. It works fine when isolated. The grip isn't staying centered under the cursor like it does in Chrome. 54 | * Safari for Windows likes to start with the grip in the upper left corner of the slider box... most of the time when I hover over it, it jumps to where it should be... wow that is so weird. Also, the "S" curve demo doesn't want to work at all... 55 | 56 | [david-dev-url]: https://david-dm.org/Mottie/pathslider?type=dev 57 | [david-dev-image]: https://img.shields.io/david/dev/Mottie/pathslider.svg 58 | [license-url]: https://github.com/Mottie/Pathslider/blob/master/LICENSE 59 | [license-image]: https://img.shields.io/badge/license-MIT-blue.svg 60 | 61 | ## Change Log 62 | 63 | ### Version 1.0.0-alpha (8/19/2016) (mis-released as beta) 64 | 65 | * Core: 66 | * Use window load event for jQuery v3.0+ compatibility. 67 | * Add `drawCanvas` callback & update curve color options. Fixes [issue #8](https://github.com/Mottie/Pathslider/issues/8). 68 | * Add `finishCurve` function. For use inside of the `drawCanvas` callback. 69 | * Add `redraw` method. See [issue #8](https://github.com/Mottie/Pathslider/issues/8). 70 | * Clean up CSS & use image URI. 71 | * Builder: 72 | * Switch to using `drawCanvas` callback. 73 | * Use `finishCurve` function. 74 | * Add curve shift controls. 75 | * Docs & Readme: 76 | * Update libraries & colors. 77 | * Make MIT license more prominent. 78 | * Optimize png images. 79 | * Extras: 80 | * Add `.git` files. 81 | * Remove BOM from all files. 82 | * Add grunt build script & dist folder. 83 | 84 | ### Version 0.9.1 alpha (12/7/2011) 85 | 86 | * Added touch device compatibility. 87 | * Changed the grip "data-degree" attribute to "data-angle". This attribute contains the angle of transformation to rotate the grip. 88 | * Changed the grip "data-position" attribute to "data-percent". This attribute contains the current percentage distance along the curve of the grip. 89 | * Added some images for the wiki documentation. 90 | 91 | ### Version 0.9 alpha (12/5/2011) 92 | 93 | * Initial commit 94 | -------------------------------------------------------------------------------- /dist/pathslider.min.css: -------------------------------------------------------------------------------- 1 | #slider{width:200px;height:200px}.pathslider-grip{width:30px;height:15px;position:absolute;top:0;left:0;background:#ddd;font-size:1px;z-index:10;cursor:move;border:1px solid;border-radius:7px;-webkit-box-shadow:0 1px 3px rgba(000,000,000,.5),inset 0 0 1px rgba(255,255,255,.6);box-shadow:0 1px 3px rgba(000,000,000,.5),inset 0 0 1px rgba(255,255,255,.6)}.pathslider-grip.sliding,.pathslider-grip:hover{border-color:#8ce;-webkit-box-shadow:0 0 15px #8ce;box-shadow:0 0 15px #8ce}.pathslider{position:relative}.silver{border-color:#949494;background-color:#ebebeb;background-image:-webkit-linear-gradient(top,#fff 0,#ebebeb 50%,#dbdbdb 50%,#b5b5b5);background-image:linear-gradient(top,#fff 0,#ebebeb 50%,#dbdbdb 50%,#b5b5b5)}.black{border-color:#000 2 | background-color: #3b3b3b;background-image:-webkit-linear-gradient(top,#a3a3a3 0,#3b3b3b 50%,#242424 50%,#000);background-image:linear-gradient(top,#a3a3a3 0,#3b3b3b 50%,#242424 50%,#000)}.pathslider-grip.chrome1{width:18px;height:18px;border:0;border-radius:0;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAABI1BMVEUAAADHx8fNzc/29vbCwsOVlpfFxsbb29vp6enm5eX19vb6+vr///+vr6/Dw8K4uLjGxsbQ0NDR0dHh4eHl5uaqq6qlpaWoqqy+vr+9vb3e4OGTk5TIyMiSkpO9vb2kpabDw8SampzDw8O9vb62tratra7Ly8vW1tbk5OTKysrv8fLt7e3c3Nzi4uLd3d309PTv7+/o6Ojm5ufy8vPu7+/r6+zX2NnT09XS0tTk5Obh4eHP0NLKy83DxMX5+fr19fXz8/Xl5ube3+Dd3d/c3N7Z2tvW19jU1de+vr/3+Pjp6ura29zNztG7vL65uruztLWvsLGtra329vbw8PDh4uPLzc/HyMqysrLs7e3o6Orj4+PV1dfQ0dO4uLmoqKn7+/ujo6WIZbNwAAAAMXRSTlMA/v4F/v79kzcuFgkC9vHmsI9+ZRL99fHw7+7u7Ovj4+Hg19bNzMCooJ+Yi4p5SSYjiIrjEAAAASVJREFUGNMVz0OCxAAUANGfpG2MbSu27aTtmfufYtK1fLuCJgBg72g831bO3hoYQAuKvl+PPGkznWXHL/Um7KvfxVIUoZw9cZGLj0YhX93YZTkx4kJ9rArIZwvqDykTMi4nshZOKr9cZwiDtkQIhs2KrED5ikIlPehuDZzgDUdc4yNfG5NoBzKPx+mAsDhT9SlC4BkEcs80CHzJO0QgMBtxbVcgT6coy7qMaVqcNJuJjgz5LollRHJ0fcVPQsZyEEj/0kSObBxXF2ONpEi2BDe7uTy1ljRBajRN+mqpBNUsl0KexnVKIYOFap9UofZ0mBhBQfRI0ShBrhaTtV7bc80VQY20CVN5/oEibHBaLmceIs/LV8ODApotwGr9x9vL8+v7/h7gH5fyPZ6I/Z3zAAAAAElFTkSuQmCC) center center no-repeat;-webkit-box-shadow:0 0 0;box-shadow:0 0 0}.pathslider-grip.chrome2{width:57px;height:35px;border:0;border-radius:0;background:url(data:image/png;base64,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) center center no-repeat;-webkit-box-shadow:0 0 0;box-shadow:0 0 0} -------------------------------------------------------------------------------- /dist/jquery.pathslider.min.js: -------------------------------------------------------------------------------- 1 | !function(a){a.pathslider=function(b,c){var d,e=this;e.$el=a(b).addClass("pathslider"),e.el=b,e.$el.data("pathslider",e),e.init=function(){var b;e.options=d=a.extend(!0,{},a.pathslider.defaults,c),b=document.createElement("canvas"),e.hasCanvas=!(!b.getContext||!b.getContext("2d")),e.hasTouch=document.hasOwnProperty("ontouchend"),e.$grip=a("
").appendTo(e.$el),e.points=[],e.pointsxy=[],e.arrayX=[],e.arrayY=[],e.arrayP=[],e.rad2deg=180/Math.PI,e.sliding=!1,e.lastPercent=e.percent=d.value,a.each("create update start slide change stop".split(" "),function(b,c){a.isFunction(d[c])&&e.$el.bind(c+".pathslider",d[c])}),a(document).bind(e.hasTouch?"touchend.pathslider touchcancel.pathslider":"mouseup.pathslider mouseleave.pathslider",function(a){e.sliding&&(e.$el.trigger("stop.pathslider",[e]),e.lastPercent!==e.percent&&(e.lastPercent=e.percent,e.$el.trigger("change.pathslider",[e]))),e.$grip.removeClass("sliding"),e.sliding=!1}).bind((e.hasTouch?"touchmove":"mousemove")+".pathslider",function(a){e.sliding&&e.setSlider(e.findPos(a),null,!0)}),a(window).bind("resize.pathslider",function(){e.update()}).bind("load",function(){e.sliderDim[0]=e.$el.offset().left,e.sliderDim[1]=e.$el.offset().top}),e.$grip.bind((e.hasTouch?"touchstart":"mousedown")+".pathslider",function(b){return e.sliding=!0,a(this).addClass("sliding"),e.$el.trigger("start.pathslider",[e]),!1}).bind("click",function(){return!1}),e.redraw(),e.$el.trigger("create.pathslider",[e])},e.update=function(){e.$grip.attr("class","pathslider-grip "+d.gripClass),e.ctx&&e.ctx.clearRect(0,0,e.sliderDim[2],e.sliderDim[3]),e.sliderDim=[e.$el.offset().left,e.$el.offset().top,e.$el.width(),e.$el.height()];var b=window.getComputedStyle(e.$grip[0]);e.gripCenter=[parseInt(b.width,10)/2,parseInt(b.height,10)/2],e.dataPoints=d.dataPoints,e.makeArray();var c=a.inArray(e.percent,e.arrayP);e.position=c===-1?Math.round(e.percent/100*e.dataPoints):c,e.setSlider(e.percent,null,!0),e.hasCanvas&&d.useCanvas&&e.drawCurve()},e.setSlider=function(a,b,c){if(!isNaN(a)){a=parseFloat(a,10),a=a>100?100:a<0?0:a;var f,g,h=e.calcBezier(a/100,e.pointsxy),i=a-2>0?e.calcBezier((a-2)/100,e.pointsxy):h,j=a+2<100?e.calcBezier((a+2)/100,e.pointsxy):h,k=j[0]-i[0]===0?90:(j[1]-i[1])/(j[0]-i[0]);e.angle=parseInt(Math.atan(k)*e.rad2deg,10),g="rotate("+e.angle+"deg)",f=d.rotateGrip?{"-webkit-transform":g,transform:g}:{},f.left=h[0]-e.gripCenter[0],f.top=h[1]-e.gripCenter[1],e.$grip.attr({"data-angle":e.angle,"data-percent":a}).css(f),e.percent=a,(a===e.lastPercent||e.sliding)&&c||e.$el.trigger("change.pathslider",[e])}"function"==typeof b&&b(e)},e.mousePos=function(a){return[(a.originalEvent.touches?a.originalEvent.touches[0].pageX:a.pageX)-e.sliderDim[0],(a.originalEvent.touches?a.originalEvent.touches[0].pageY:a.pageY)-e.sliderDim[1]]},e.findPos=function(a){var b,c,f,g,h=[],i=[],j=e.position,k=parseInt(d.tolerance+1,10)||2,l=parseInt(d.range,10)||e.gripCenter[0],m=e.mousePos(a);for(b=0;b1)return e.returnPos(h[0]);if(1===i.length&&h.length>1)return e.returnPos(i[0])}return e.returnPos(j)},e.returnPos=function(a){var b=e.position===a;return e.percent=e.arrayP[a],e.position=a,b||(e.hasCanvas&&d.useCanvas&&e.drawCurve(),e.$el.trigger("slide.pathslider",[e])),e.percent},e.makeArray=function(){var a,b,c=e.pointsxy,d=e.dataPoints;for(a=0;a').appendTo(e.$el),e.$canvas=e.$el.find("canvas").attr({width:e.sliderDim[2],height:e.sliderDim[3]}),e.canvas=e.$canvas[0],e.ctx=e.canvas.getContext("2d")),b=e.ctx,b.clearRect(0,0,e.sliderDim[2],e.sliderDim[3]),b.lineCap=d.curve.cap,b.lineJoin=d.curve.cap,b.lineWidth=d.curve.width,a.isArray(d.curve.color)?(c=b.createLinearGradient(g[0],g[1],g[6],g[7]),f=e.percent/100,c.addColorStop(0,d.curve.color[0]),c.addColorStop(f,d.curve.color[0]),f+.01<=1&&(f+=.01),c.addColorStop(f,d.curve.color[1]),c.addColorStop(1,d.curve.color[1]),b.strokeStyle=c):b.strokeStyle=d.curve.color,f=!0,"function"==typeof d.drawCanvas&&(f=d.drawCanvas(e,b,g)!==!1,b=e.ctx),f===!0&&e.finishCurve(b,g)},e.finishCurve=function(a,b){a=a||e.ctx,b=b||e.pointsxy,a.beginPath(),a.moveTo(b[0],b[1]),a.bezierCurveTo(b[2],b[3],b[4],b[5],b[6],b[7]),a.stroke()},e.init()},a.pathslider.defaults={gripClass:"",rotateGrip:!0,useCanvas:!0,curve:{width:4,color:"#333",cap:"round"},points:[0,50,50,-50,-50,-50,250,50],value:50,dataPoints:100,tolerance:3,range:30},a.fn.pathslider=function(b,c){return this.each(function(){var d,e=a(this).data("pathslider");if((typeof b).match("object|undefined")){if(e)return e.redraw();new a.pathslider(this,b)}else/\d/.test(b)&&!isNaN(b)&&e&&(d="number"==typeof b?b:parseInt(a.trim(b),10),d>=0&&d<=100&&e.setSlider(d,c))})},a.fn.getpathslider=function(){return this.data("pathslider")}}(jQuery); -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | html,body { margin: 0; height: 100%; } 2 | 3 | body { background: #ddd; color: #333; } 4 | a:link { color: #ddd; text-decoration: none; } 5 | a:hover { color: #fff; } 6 | a:visited, a:active { color: #ccc; } 7 | 8 | h1.hide { display: none; } 9 | .oldie h1.hide { display: block; margin: 50px; color: #000; } 10 | h1.hide, .oldie .builder .wrapper, .oldie .builder .footer { display: none; } 11 | 12 | header, .header { 13 | min-height: 100px; 14 | background: #789; 15 | background: -moz-radial-gradient(center top, ellipse farthest-corner, #789, #677 40%); 16 | background: -webkit-radial-gradient(center top, ellipse farthest-corner, #789, #677 40%); 17 | background: -o-radial-gradient(center top, ellipse farthest-corner, #789, #677 40%); 18 | background: -ms-radial-gradient(center top, ellipse farthest-corner, #789, #677 40%); 19 | box-shadow: inset 0 0 10px #567; 20 | } 21 | 22 | h1 { 23 | color: #ddd; 24 | margin: 0; 25 | text-align: center; 26 | font-size: 3.5em; 27 | font-family: 'Paytone One', sans-serif; 28 | text-shadow: #555 1px 1px, #555 2px 2px; 29 | } 30 | 31 | header a, .header a { 32 | position: absolute; 33 | top: 25px; 34 | width: 50px; 35 | height: 50px; 36 | display: block; 37 | text-indent: -999em; 38 | } 39 | header a.current, .header a.current { 40 | -webkit-box-shadow: inset 0 0 20px #59b; 41 | -moz-box-shadow: inset 0 0 20px #59b; 42 | box-shadow: inset 0 0 20px #59b; 43 | border-radius: 15px; 44 | -moz-border-radius: 15px; 45 | -webkit-border-radius: 15px; 46 | } 47 | 48 | a.home { 49 | left: 20px; 50 | background: url(../demo/pathslider.png) center center no-repeat; 51 | } 52 | a.builder { 53 | left: 75px; 54 | background: url(../demo/pathbuilder.png) center center no-repeat; 55 | } 56 | 57 | .wrapper { 58 | min-height: 100%; 59 | height: auto !important; 60 | height: 100%; 61 | margin: 0 auto -100px; /* the bottom margin is the negative value of the footer's height */ 62 | } 63 | 64 | .content { 65 | width: 50%; 66 | margin: 10px auto; 67 | } 68 | 69 | .builder .content { 70 | width: 80%; 71 | 72 | } 73 | 74 | #slider { 75 | margin: 0 auto; 76 | background: #eee; 77 | width: 300px; 78 | height: 300px; 79 | } 80 | 81 | .builder canvas.pathslider-canvas { 82 | border: #555 1px solid; 83 | } 84 | 85 | /* index.html */ 86 | .demo { 87 | color: #777; 88 | margin: 0; 89 | padding: 20px; 90 | font-size: 2.5em; 91 | font-family: 'Paytone One', sans-serif; 92 | white-space: nowrap; 93 | text-align: center; 94 | text-shadow: #555 1px 1px, #555 2px 2px; 95 | clear: both; 96 | } 97 | 98 | .demo .slider { 99 | margin: 0 auto; 100 | } 101 | 102 | /* builder.html */ 103 | #tabs, footer, div.footer { 104 | background: #677; 105 | margin-top: 10px; 106 | min-width: 565px; /*** make this dynamic in the next version ***/ 107 | } 108 | 109 | #tabs .tab { 110 | color: #eee; 111 | } 112 | 113 | p { 114 | line-height: 2em; 115 | } 116 | 117 | blockquote { 118 | margin: 5px 40px; 119 | } 120 | 121 | h4, .message { 122 | margin: 5px; 123 | } 124 | 125 | h4, strong { 126 | color: #fff; 127 | } 128 | 129 | .message { 130 | min-height: 50px; 131 | } 132 | 133 | img#save { 134 | border: #ff0 1px solid; 135 | width: 15px; 136 | height: 15px; 137 | } 138 | 139 | .moving { 140 | cursor: move; 141 | } 142 | 143 | .corner { 144 | background: url(data:image/gif;base64,R0lGODlhCgAKAJEAAOvr64CAgP///wAAACH5BAEAAAIALAAAAAAKAAoAAAIWlG+BK8CqWJsHioicTrfjuQHWaIRgAQA7) no-repeat; 145 | width: 10px; 146 | height: 10px; 147 | position: absolute; 148 | bottom: 0; 149 | right: 0; 150 | cursor: nw-resize; 151 | } 152 | 153 | .events { 154 | width: 30%; 155 | position: relative; 156 | margin-bottom: 10px; 157 | float: right; 158 | } 159 | .move-controls { 160 | display: inline-block; 161 | height: 70px; 162 | margin-left: 40px; 163 | } 164 | .move-controls input { 165 | width: 40px; 166 | } 167 | 168 | textarea.code { 169 | background: #ddd; 170 | font-family: 'courier'; 171 | width: 100%; 172 | } 173 | 174 | /* builder inputs */ 175 | .points { width: 20em; } 176 | .position, .angle, .tolerance.text, .range.text, .thickness.text, .grid.text { width: 35px; } 177 | .gripColor, .curveColor { width: 100px; } 178 | .bkgd { width: 90%; } 179 | 180 | /* readme colors */ 181 | .sxy { color: hsl(120,60%,85%); } 182 | .scxy { color: hsl(120,80%,75%); } 183 | .ecxy { color: hsl(0,40%,85%); } 184 | .exy { color: hsl(0,55%,75%); } 185 | 186 | footer, div.footer, .push { 187 | height: 100px; 188 | font-size: 20px; 189 | text-align: center; 190 | clear: both; 191 | padding: 10px 0 30px 0; 192 | } 193 | 194 | footer, div.footer { 195 | background: #789; 196 | background: -moz-radial-gradient(center bottom, ellipse farthest-corner, #789, #677 40%); 197 | background: -webkit-radial-gradient(center bottom, ellipse farthest-corner, #789, #677 40%); 198 | background: -o-radial-gradient(center bottom, ellipse farthest-corner, #789, #677 40%); 199 | background: -ms-radial-gradient(center bottom, ellipse farthest-corner, #789, #677 40%); 200 | box-shadow: inset 0 0 10px #567; 201 | } 202 | 203 | .blog { 204 | font-size: 0.8em; 205 | color: #ccc; 206 | text-shadow: #aaa inset 0px 3px 3px; 207 | } 208 | 209 | #tooltip { 210 | max-width: 400px; 211 | font-size: 1.1em; 212 | color: #222; 213 | background: #ddd; 214 | border: 1px solid #aaa; 215 | padding: 10px; 216 | display: none; 217 | opacity: 0.95; 218 | filter: alpha(opacity=95); 219 | text-align: left; 220 | border-radius: 8px; 221 | -moz-border-radius: 8px; 222 | -webkit-border-radius: 8px; 223 | } 224 | 225 | #tooltip .close { 226 | display: none; 227 | } -------------------------------------------------------------------------------- /css/pathslider.css: -------------------------------------------------------------------------------- 1 | /* set slider dimension here */ 2 | #slider { 3 | width: 200px; 4 | height: 200px; 5 | } 6 | 7 | /* Basic solid color slider grip */ 8 | .pathslider-grip { 9 | width: 30px; 10 | height: 15px; 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | background: #ddd; 15 | font-size: 1px; 16 | z-index: 10; 17 | cursor: move; 18 | border: 1px solid; 19 | border-radius: 7px; 20 | -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.6); 21 | box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.6); 22 | } 23 | 24 | /* grip hover/sliding state */ 25 | .pathslider-grip.sliding, .pathslider-grip:hover { 26 | border-color: #8ce; 27 | -webkit-box-shadow: 0 0 15px #8ce; 28 | box-shadow: 0 0 15px #8ce; 29 | } 30 | 31 | /* slider extra css - same as #slider above */ 32 | .pathslider { 33 | position: relative; 34 | } 35 | 36 | /************* 37 | grip styles 38 | add these using the gripClass option 39 | *************/ 40 | /* shiny silver - sorta */ 41 | .silver { 42 | border-color: #949494; 43 | background-color: #ebebeb; 44 | background-image: -webkit-linear-gradient(top,#ffffff 0%,#ebebeb 50%,#dbdbdb 50%,#b5b5b5); 45 | background-image: linear-gradient(top,#ffffff 0%,#ebebeb 50%,#dbdbdb 50%,#b5b5b5); 46 | } 47 | 48 | /* shiny black */ 49 | .black { 50 | border-color: #000000 51 | background-color: #3b3b3b; 52 | background-image: -webkit-linear-gradient(top,#a3a3a3 0%,#3b3b3b 50%,#242424 50%,#000000); 53 | background-image: linear-gradient(top,#a3a3a3 0%,#3b3b3b 50%,#242424 50%,#000000); 54 | } 55 | 56 | /* Small circular chrome knob 57 | including ".pathslider-grip" to remove border & box shadow 58 | */ 59 | .pathslider-grip.chrome1 { 60 | width: 18px; 61 | height: 18px; 62 | border: 0; 63 | border-radius: 0; 64 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAABI1BMVEUAAADHx8fNzc/29vbCwsOVlpfFxsbb29vp6enm5eX19vb6+vr///+vr6/Dw8K4uLjGxsbQ0NDR0dHh4eHl5uaqq6qlpaWoqqy+vr+9vb3e4OGTk5TIyMiSkpO9vb2kpabDw8SampzDw8O9vb62tratra7Ly8vW1tbk5OTKysrv8fLt7e3c3Nzi4uLd3d309PTv7+/o6Ojm5ufy8vPu7+/r6+zX2NnT09XS0tTk5Obh4eHP0NLKy83DxMX5+fr19fXz8/Xl5ube3+Dd3d/c3N7Z2tvW19jU1de+vr/3+Pjp6ura29zNztG7vL65uruztLWvsLGtra329vbw8PDh4uPLzc/HyMqysrLs7e3o6Orj4+PV1dfQ0dO4uLmoqKn7+/ujo6WIZbNwAAAAMXRSTlMA/v4F/v79kzcuFgkC9vHmsI9+ZRL99fHw7+7u7Ovj4+Hg19bNzMCooJ+Yi4p5SSYjiIrjEAAAASVJREFUGNMVz0OCxAAUANGfpG2MbSu27aTtmfufYtK1fLuCJgBg72g831bO3hoYQAuKvl+PPGkznWXHL/Um7KvfxVIUoZw9cZGLj0YhX93YZTkx4kJ9rArIZwvqDykTMi4nshZOKr9cZwiDtkQIhs2KrED5ikIlPehuDZzgDUdc4yNfG5NoBzKPx+mAsDhT9SlC4BkEcs80CHzJO0QgMBtxbVcgT6coy7qMaVqcNJuJjgz5LollRHJ0fcVPQsZyEEj/0kSObBxXF2ONpEi2BDe7uTy1ljRBajRN+mqpBNUsl0KexnVKIYOFap9UofZ0mBhBQfRI0ShBrhaTtV7bc80VQY20CVN5/oEibHBaLmceIs/LV8ODApotwGr9x9vL8+v7/h7gH5fyPZ6I/Z3zAAAAAElFTkSuQmCC'); 65 | background-position: center center; 66 | background-repeat: no-repeat; 67 | -webkit-box-shadow: 0 0 0; 68 | box-shadow: 0 0 0; 69 | } 70 | 71 | /* Large curvy chrome slide */ 72 | .pathslider-grip.chrome2 { 73 | width: 57px; 74 | height: 35px; 75 | border: 0; 76 | border-radius: 0; 77 | background: url('data:image/png;base64,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'); 78 | background-position: center center; 79 | background-repeat: no-repeat; 80 | -webkit-box-shadow: 0 0 0; 81 | box-shadow: 0 0 0; 82 | } -------------------------------------------------------------------------------- /dist/pathslider.css: -------------------------------------------------------------------------------- 1 | /* set slider dimension here */ 2 | #slider { 3 | width: 200px; 4 | height: 200px; 5 | } 6 | 7 | /* Basic solid color slider grip */ 8 | .pathslider-grip { 9 | width: 30px; 10 | height: 15px; 11 | position: absolute; 12 | top: 0; 13 | left: 0; 14 | background: #ddd; 15 | font-size: 1px; 16 | z-index: 10; 17 | cursor: move; 18 | border: 1px solid; 19 | border-radius: 7px; 20 | -webkit-box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.6); 21 | box-shadow: 0px 1px 3px rgba(000,000,000,0.5), inset 0px 0px 1px rgba(255,255,255,0.6); 22 | } 23 | 24 | /* grip hover/sliding state */ 25 | .pathslider-grip.sliding, .pathslider-grip:hover { 26 | border-color: #8ce; 27 | -webkit-box-shadow: 0 0 15px #8ce; 28 | box-shadow: 0 0 15px #8ce; 29 | } 30 | 31 | /* slider extra css - same as #slider above */ 32 | .pathslider { 33 | position: relative; 34 | } 35 | 36 | /************* 37 | grip styles 38 | add these using the gripClass option 39 | *************/ 40 | /* shiny silver - sorta */ 41 | .silver { 42 | border-color: #949494; 43 | background-color: #ebebeb; 44 | background-image: -webkit-linear-gradient(top,#ffffff 0%,#ebebeb 50%,#dbdbdb 50%,#b5b5b5); 45 | background-image: linear-gradient(top,#ffffff 0%,#ebebeb 50%,#dbdbdb 50%,#b5b5b5); 46 | } 47 | 48 | /* shiny black */ 49 | .black { 50 | border-color: #000000 51 | background-color: #3b3b3b; 52 | background-image: -webkit-linear-gradient(top,#a3a3a3 0%,#3b3b3b 50%,#242424 50%,#000000); 53 | background-image: linear-gradient(top,#a3a3a3 0%,#3b3b3b 50%,#242424 50%,#000000); 54 | } 55 | 56 | /* Small circular chrome knob 57 | including ".pathslider-grip" to remove border & box shadow 58 | */ 59 | .pathslider-grip.chrome1 { 60 | width: 18px; 61 | height: 18px; 62 | border: 0; 63 | border-radius: 0; 64 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAABI1BMVEUAAADHx8fNzc/29vbCwsOVlpfFxsbb29vp6enm5eX19vb6+vr///+vr6/Dw8K4uLjGxsbQ0NDR0dHh4eHl5uaqq6qlpaWoqqy+vr+9vb3e4OGTk5TIyMiSkpO9vb2kpabDw8SampzDw8O9vb62tratra7Ly8vW1tbk5OTKysrv8fLt7e3c3Nzi4uLd3d309PTv7+/o6Ojm5ufy8vPu7+/r6+zX2NnT09XS0tTk5Obh4eHP0NLKy83DxMX5+fr19fXz8/Xl5ube3+Dd3d/c3N7Z2tvW19jU1de+vr/3+Pjp6ura29zNztG7vL65uruztLWvsLGtra329vbw8PDh4uPLzc/HyMqysrLs7e3o6Orj4+PV1dfQ0dO4uLmoqKn7+/ujo6WIZbNwAAAAMXRSTlMA/v4F/v79kzcuFgkC9vHmsI9+ZRL99fHw7+7u7Ovj4+Hg19bNzMCooJ+Yi4p5SSYjiIrjEAAAASVJREFUGNMVz0OCxAAUANGfpG2MbSu27aTtmfufYtK1fLuCJgBg72g831bO3hoYQAuKvl+PPGkznWXHL/Um7KvfxVIUoZw9cZGLj0YhX93YZTkx4kJ9rArIZwvqDykTMi4nshZOKr9cZwiDtkQIhs2KrED5ikIlPehuDZzgDUdc4yNfG5NoBzKPx+mAsDhT9SlC4BkEcs80CHzJO0QgMBtxbVcgT6coy7qMaVqcNJuJjgz5LollRHJ0fcVPQsZyEEj/0kSObBxXF2ONpEi2BDe7uTy1ljRBajRN+mqpBNUsl0KexnVKIYOFap9UofZ0mBhBQfRI0ShBrhaTtV7bc80VQY20CVN5/oEibHBaLmceIs/LV8ODApotwGr9x9vL8+v7/h7gH5fyPZ6I/Z3zAAAAAElFTkSuQmCC'); 65 | background-position: center center; 66 | background-repeat: no-repeat; 67 | -webkit-box-shadow: 0 0 0; 68 | box-shadow: 0 0 0; 69 | } 70 | 71 | /* Large curvy chrome slide */ 72 | .pathslider-grip.chrome2 { 73 | width: 57px; 74 | height: 35px; 75 | border: 0; 76 | border-radius: 0; 77 | background: url('data:image/png;base64,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'); 78 | background-position: center center; 79 | background-repeat: no-repeat; 80 | -webkit-box-shadow: 0 0 0; 81 | box-shadow: 0 0 0; 82 | } -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | var i, s, t, pathslider = $('#slider'), 4 | points = $('.points'), 5 | position = $('.position'), 6 | angle = $('.angle'), 7 | code = $('textarea.code'), 8 | m = $('.message'), 9 | presets = { 10 | '0' : [ 25,150,50,-50,-50,-50,275,150 ], // arch 11 | '1' : [ 25,275,25,-25,-25,25,275,25 ], // diagonal 12 | '2' : [ 25,275,250,0,-250,0,275,25 ], // s-shape 13 | '3' : [ 100,200,200,25,-100,-275,100,275 ], // p-shape 14 | '4' : [ 75,200,200,-125,-200,-125,225,200 ] // loop 15 | }, 16 | // update code box 17 | updateOptions = function(s){ 18 | var t; 19 | if (s) { 20 | if (s.hasOwnProperty('builderInitialized') && s.builderInitialized) { code.val( s.getCode().replace(/url\(\)/, $('.bkgd').val()) ); } 21 | $('.rotate')[0].checked = s.options.rotateGrip; // I would use prop, but want to make it work with jQuery 1.4.4 22 | $('.tolerance').val(s.options.tolerance).filter('.isRange').next().html( s.options.tolerance ); 23 | $('.range').val( s.options.range ).filter('.isRange').next().html( s.options.range + ' px' ); 24 | $('.edit')[0].checked = s.options.edit; 25 | $('.grid').val( s.options.grid ).filter('.isRange').next().html( s.options.grid === 1 ? 'off' : s.options.grid + ' px' ); 26 | $('.snap')[0].checked = s.options.snap; 27 | $('.thickness').val( s.options.curve.width ).filter('.isRange').next().html( s.options.curve.width + ' px' ); 28 | t = $('.gripColor'); t.val( checkColor(t, t.val()) ); 29 | $('.curveColor').each(function(indx, el){ 30 | var $el = $(el); 31 | $el.val( checkColor($el) ); 32 | }); 33 | } 34 | }, 35 | checkColor = function(t,c){ 36 | var c = c || t.val(), 37 | d = (/^(#[\da-fA-F]{6}$)/.test(c)) ? c : t.attr('data-last'); 38 | t.attr('data-last', d); 39 | return d; 40 | }, 41 | // t = jQuery object, d = default value 42 | checkRange = function(t,d){ 43 | // parseInt($('.tolerance').val(), 10) || 3; 44 | var v = parseInt(t.val(), 10) || d, 45 | min = parseInt(t.attr('min'), 10), 46 | max = parseInt(t.attr('max'), 10); 47 | return (v < min) ? min : (v > max) ? max : v; 48 | }; 49 | 50 | $('#tabs').tabs({ 51 | show: function(e,ui){ 52 | if (ui.tab.innerHTML === "Code") { 53 | var s = pathslider.data('pathslider'); 54 | updateOptions(s); 55 | if (s && s.hasOwnProperty('updateCanvasImage')) { 56 | // update save canvas image 57 | s.updateCanvasImage(); 58 | } 59 | } 60 | } 61 | }); 62 | 63 | // add preset buttons 64 | i = 0; t = ''; 65 | while (presets.hasOwnProperty(i)) { 66 | t += ''; 67 | i++; 68 | } 69 | $('.presets').html(t); 70 | 71 | // check input type support 72 | $('.tolerance, .range, .grid, .thickness').each(function(){ 73 | if (this.type !== 'range') { 74 | $(this) 75 | .addClass('text') 76 | .next() 77 | .html(' range (' + $(this).attr('min') + '-' + $(this).attr('max') + ')'); 78 | } else { 79 | $(this).addClass('isRange'); 80 | } 81 | }); 82 | 83 | // update all pathslider options 84 | $('.set').change(function(){ 85 | var t; 86 | s.options.rotateGrip = $('.rotate')[0].checked; 87 | s.options.tolerance = checkRange( $('.tolerance'), 3); 88 | s.options.range = checkRange( $('.range'), 30); 89 | s.options.edit = $('.edit')[0].checked; 90 | s.options.grid = checkRange( $('.grid'), 25); 91 | s.options.snap = $('.snap')[0].checked; 92 | $('.pathslider-grip').attr('style',''); // clear rotation 93 | s.options.points = points.val().split(','); 94 | s.options.curve.width = checkRange( $('.thickness'), 4); 95 | s.options.curve.cap = $('.capStyle').val().toLowerCase(); 96 | 97 | t = $('.curveColor').map(function(){ 98 | return checkColor($(this)); 99 | }).get(); 100 | if (t[0] === t[1]) { 101 | t = t[0]; 102 | } 103 | // a color array => linear gradient in v1.0.0 104 | s.options.curve.color = t; 105 | 106 | if ($('.gripStyle')[0].selectedIndex === 0) { 107 | $('.gripColor')[0].disabled = false; 108 | t = checkColor( $('.gripColor') ); 109 | s.options.gripClass = ''; // update for code tab 110 | $('.pathslider-grip') 111 | .attr('class', 'pathslider-grip') 112 | .css('background-color', t); 113 | } else { 114 | t = $('.gripStyle').val().toLowerCase().replace(/\s+/g,''); 115 | s.options.gripClass = t; 116 | $('.gripColor')[0].disabled = true; 117 | $('.pathslider-grip') 118 | .attr('class', 'pathslider-grip ' + t) 119 | .css('background-color', ''); 120 | } 121 | 122 | // background - remove ";" because it breaks if you don't 123 | t = $('.bkgd').val().replace(/\;/g,''); 124 | $('.bkgd').val(t); 125 | $('#slider').css('background', t ); 126 | 127 | updateOptions(s); 128 | s.updateBuilder(); 129 | }); 130 | 131 | // set up pathslider 132 | $('#slider') 133 | .pathslider({ 134 | rotateGrip : true, 135 | // points : [ 50,300, 0,250, 180,250, 300,50 ], 136 | points : [ 0,50, 50,-50, -50,-50, 250,50 ], 137 | 138 | create : function(e,slider){ 139 | points.val(slider.points); 140 | position.val(slider.percent); 141 | angle.text( slider.angle );50 142 | }, 143 | update : function(e,slider){ 144 | points.val(slider.points); 145 | updateOptions(slider); 146 | }, 147 | slide : function(e,slider){ 148 | position.val(slider.percent); 149 | angle.text( slider.angle ); 150 | } 151 | }) 152 | .pathbuilder({ 153 | edit : true, 154 | snap : true, 155 | grid : 25, 156 | canvasImage : $('#save') 157 | }); 158 | 159 | // Select all text in the points input 160 | points.mouseup(function(){ 161 | this.select(); 162 | }); 163 | 164 | // shift points 165 | $('.shift').click(function(){ 166 | var x, y, 167 | $el = $(this), 168 | isX = $el.filter('.x').length, 169 | dir = $el.filter('.left, .up').length ? -1 : 1; 170 | amount = parseInt($(isX ? '.movex' : '.movey').val(), 10) || 10; 171 | 172 | x = isX ? dir * amount : 0; 173 | y = isX ? 0 : dir * amount; 174 | s.points = s.options.points = s.shift(x, y); 175 | s.updateBuilder(); 176 | }); 177 | 178 | // "Set" points 179 | $('.setpoints').click(function(){ 180 | var p, pts = points.val().split(','); 181 | if (pts.length === 8) { 182 | s.options.points = pts; 183 | s.updateBuilder(); 184 | } 185 | }); 186 | 187 | // "Set" position 188 | $('.setpos').click(function(){ 189 | s.setSlider( parseFloat(position.val()), function(s){ 190 | // no need to validate, just replace the value 191 | position.val( s.percent ); 192 | } ); 193 | }); 194 | 195 | // presets 196 | $('.presets button').click(function(){ 197 | t = parseInt($(this).text(), 10) - 1; 198 | points.val(s.options.points = presets[t]); 199 | s.updateBuilder(); 200 | }); 201 | 202 | // Add events 203 | pathslider.bind('create.pathslider update.pathslider start.pathslider slide.pathslider change.pathslider stop.pathslider',function(e,s){ 204 | m.append('
  • ' + e.type + ' : ' + s.percent + '
  • '); 205 | var l = m.find('li'); 206 | if (l.length > 10) { l.eq(0).remove(); } 207 | }); 208 | 209 | // Color styles 210 | $('.gripColor, .curveColor').ColorPicker({ 211 | onSubmit: function(hsb, hex, rgb, el) { 212 | $(el).val('#' + hex); 213 | $(el).ColorPickerHide(); 214 | $(el).trigger('change'); 215 | }, 216 | onBeforeShow: function () { 217 | $(this).ColorPickerSetColor(this.value.slice(1)); 218 | } 219 | }) 220 | .bind('keyup', function(){ 221 | $(this).ColorPickerSetColor(this.value.slice(1)); 222 | }); 223 | 224 | // Make info links (tooltips) unclickable 225 | $('a.info').click(function(){ 226 | if (this.target === "") { return false; } 227 | }); 228 | 229 | // set up tooltips 230 | $.jatt({ direction : 'ne' }); 231 | 232 | s = pathslider.data('pathslider'); 233 | 234 | }); -------------------------------------------------------------------------------- /js/jquery.pathslider.builder.js: -------------------------------------------------------------------------------- 1 | /* jQuery Pathslider Builder v0.9.1 alpha 2 | * By Rob Garrison (Mottie) 3 | * MIT License 4 | * 5 | * Code based on the bezier curve demo by Craig Buckler (http://twitter.com/craigbuckler) 6 | * http://blogs.sitepointstatic.com/examples/tech/canvas-curves/bezier-curve.html 7 | */ 8 | (function($){ 9 | 10 | $.pathbuilderdefaults = { 11 | edit : true, // allows user to move bezier curve 12 | snap : true, 13 | grid : 10, 14 | // default canvas styles 15 | // control point line (cpline) & point are used in edit mode 16 | style : { 17 | cpline : { width: 1, color: "#00c" }, 18 | // start point 19 | start : { width: 2, color: "hsl(120,100%,30%)", fill: "hsla(120,100%,30%,0.3)", radius: 10, arc1: 0, arc2: 2 * Math.PI }, 20 | // start control point 21 | cstart : { width: 2, color: "hsl(120,100%,30%)", fill: "hsla(120,100%,30%,0.5)", radius: 10, arc1: 0, arc2: 2 * Math.PI }, 22 | // end control point 23 | cend : { width: 2, color: "hsl(0,100%,30%)", fill: "hsla(0,100%,30%,0.5)", radius: 10, arc1: 0, arc2: 2 * Math.PI }, 24 | // end point 25 | end : { width: 2, color: "hsl(0,100%,30%)", fill: "hsla(0,100%,30%,0.3)", radius: 10, arc1: 0, arc2: 2 * Math.PI }, 26 | grid : { width: 1, color: "hsla(0,0%,60%,0.5)" } 27 | }, 28 | canvasImage : null // jQuery object of img that the canvas is saved into - see base.updateCanvasImage 29 | }; 30 | 31 | $.fn.pathbuilder = function(options) { 32 | return this.each(function(){ 33 | // make sure pathslider is attached 34 | var o, base = $(this).data('pathslider'); 35 | if (!base) { return; } 36 | 37 | base.builderInit = function(){ 38 | base.options = o = $.extend(true, base.options, $.pathbuilderdefaults, options); 39 | base.drag = null; 40 | 41 | base.options.drawCanvas = function(b, ctx, points) { 42 | base.drawControls(ctx, points); 43 | // prevent redrawing the curve 44 | return false; 45 | }; 46 | 47 | if (!base.hasCanvas) { return; } // too bad IE! 48 | 49 | // Add resizable corner to allow resizing of the canvas 50 | $('
    ') 51 | .appendTo(base.$el) 52 | .mousedown(function(e) { 53 | $(document) 54 | .unbind('mousemove.pathbuilder touchmove.pathbuilder') 55 | .bind((base.hasTouch ? 'touchmove' : 'mousemove') + '.pathbuilder', function(e) { 56 | var ww = $(window).width(), 57 | wh = $(window).height(), 58 | l = base.$canvas.offset().left, 59 | t = base.$canvas.offset().top, 60 | w = (e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX) - l, 61 | h = (e.originalEvent.touches ? e.originalEvent.touches[0].pageY : e.pageY) - t; 62 | w = (w < 100) ? 100 : w > ww-l ? ww-l : w; 63 | h = (h < 100) ? 100 : h > wh-t ? wh-t : h; // min w & h 64 | base.$canvas.attr({ width : w, height: h }); 65 | base.$el.css({ width : w, height: h }); 66 | base.sliderDim = [ base.$el.position().left, base.$el.position().top, w, h ]; 67 | // Add bezier curve & controls 68 | base.drawCurve(); 69 | }) 70 | .bind((base.hasTouch ? 'touchend' : 'mouseup') + '.pathbuilder', function(){ 71 | $(document).unbind('mousemove.pathbuilder touchmove.pathbuilder'); 72 | }); 73 | }); 74 | 75 | base.updateBuilder(true); 76 | base.builderInitialized = true; // initialization flag 77 | base.$el.trigger('update.pathslider', [base] ); 78 | 79 | }; 80 | 81 | base.updateBuilder = function(internal) { 82 | // make sure all of the points are numerical 83 | base.points = o.points = $.map(o.points, function(v,i){ 84 | return parseInt(v*100,10)/100; 85 | }); 86 | // convert control point offsets to x/y positions 87 | base.redraw( (base.builderInitialized) ? base.points : base.shift(25,100) ); 88 | if (o.edit) { 89 | base.$canvas 90 | .unbind('.pathbuilder') 91 | .bind((base.hasTouch ? 'touchstart' : 'mousedown') + '.pathbuilder', function(e){ base.dragStart(e); }) 92 | .bind((base.hasTouch ? 'touchmove' : 'mousemove') + '.pathbuilder', function(e){ base.dragging(e); }) 93 | .bind((base.hasTouch ? 'touchend.pathbuilder touchcancel' : 'mouseup.pathbuilder mouseleave') + '.pathbuilder', function(e){ base.dragEnd(e); }); 94 | } 95 | if (!internal) { 96 | base.$el.trigger('update.pathslider', [base]); 97 | } 98 | }; 99 | 100 | // Make purdy stuff 101 | base.drawControls = function(c, b) { 102 | var i, j, x, y, t, p, s = o.style, 103 | // c = base.ctx, b = base.pointsxy, 104 | w = base.sliderDim[2], h = base.sliderDim[3]; 105 | base.points = base.convert(); 106 | clearTimeout(base.timer); 107 | c.clearRect(0, 0, w, h); 108 | 109 | // finish drawing curve - don't let drawCanvas do this because 110 | // we're changing the colors for the control handles 111 | base.finishCurve(c, b); 112 | c.beginPath(); 113 | c.moveTo(b[0], b[1]); 114 | c.bezierCurveTo(b[2], b[3], b[4], b[5], b[6], b[7]); 115 | c.stroke(); 116 | 117 | // Edit mode = add grid, control points/lines 118 | if (o.edit) { 119 | // make grid 120 | if (o.grid >= 2) { 121 | c.prop({ 122 | lineWidth : s.grid.width, 123 | strokeStyle : s.grid.color 124 | }) 125 | .beginPath(); 126 | for (x = o.grid; x < w; x += o.grid) { 127 | c.moveTo(x, 0).lineTo(x, h); 128 | } 129 | for (y = o.grid; y < h; y += o.grid) { 130 | c.moveTo(0, y).lineTo(w, y); 131 | } 132 | c.stroke(); 133 | } 134 | 135 | j = 0; 136 | base.controls = b; 137 | base.controlNames = ['start', 'cstart', 'cend', 'end']; 138 | 139 | // Add control lines 140 | c.prop({ 141 | lineWidth : s.cpline.width, 142 | strokeStyle : s.cpline.color 143 | }) 144 | .beginPath() 145 | .moveTo(b[0], b[1]) 146 | .lineTo(b[2], b[3]) 147 | .moveTo(b[6], b[7]) 148 | .lineTo(b[4], b[5]) 149 | .stroke(); 150 | // Add control points 151 | for (i=0; i < 8; i++) { 152 | c.prop({ 153 | lineWidth : s[base.controlNames[j]].width, 154 | strokeStyle : s[base.controlNames[j]].color, 155 | fillStyle : s[base.controlNames[j]].fill 156 | }) 157 | .beginPath() 158 | .arc( 159 | b[i++], 160 | b[i], 161 | s[base.controlNames[j]].radius, 162 | s[base.controlNames[j]].arc1, 163 | s[base.controlNames[j]].arc2, 164 | true 165 | ) 166 | .fill() 167 | .stroke(); 168 | j += i%2; 169 | } 170 | } 171 | 172 | // thottle resizing window 173 | base.timer = setTimeout(function(){ 174 | base.makeArray(); 175 | base.setSlider(base.percent, null, true); 176 | }, 100); 177 | 178 | }; 179 | 180 | base.updateCanvasImage = function(){ 181 | if (o.canvasImage.length) { 182 | clearTimeout(base.timer); 183 | base.ctx.clearRect(0, 0, base.sliderDim[2], base.sliderDim[3]); 184 | base.drawCurve(); 185 | o.canvasImage.attr('src', base.canvas.toDataURL()); 186 | base.updateBuilder(); 187 | } 188 | }; 189 | 190 | // start dragging points on canvas 191 | base.dragStart = function(event){ 192 | if (!o.edit) { return; } 193 | var e = base.mousePos(event), 194 | l = base.controls.length, 195 | i, j = 0, r, dx, dy; 196 | for (i=0; i < l; i++) { 197 | dx = base.controls[i++] - e[0]; 198 | dy = base.controls[i] - e[1]; 199 | r = o.style[base.controlNames[j]].radius + o.grid/2; 200 | if ((dx * dx) + (dy * dy) < r * r) { 201 | base.drag = i-1; 202 | base.dPoint = e; 203 | base.$canvas.addClass('moving'); 204 | return; 205 | } 206 | j += i%2; 207 | } 208 | }; 209 | 210 | // dragging points on canvas 211 | base.dragging = function(event){ 212 | if (o.edit && base.drag !== null) { 213 | var i, g = o.grid || 1, 214 | e = base.mousePos(event), 215 | c = base.controls, 216 | l = c.length, 217 | x = e[0] - base.dPoint[0], 218 | y = e[1] - base.dPoint[1]; 219 | // Move whole thing 220 | if (event.shiftKey) { 221 | if (x > o.grid || y > o.grid) { base.dPoint = e; } 222 | for (i=0; i < l; i++) { 223 | // *** SHIFT-snap to grid needs improvement *** 224 | c[i] = (o.snap) ? Math.round((c[i] + x)/g)*g : c[i] + x; 225 | c[++i] = (o.snap) ? Math.round((c[i] + y)/g)*g : c[i] + y; 226 | } 227 | base.dPoint = e; 228 | } else { 229 | // only move the one point 230 | c[base.drag] = (o.snap) ? Math.round((base.dPoint[0] + x)/g)*g : base.dPoint[0] + x; 231 | c[base.drag+1] = (o.snap) ? Math.round((base.dPoint[1] + y)/g)*g : base.dPoint[1] + y; 232 | } 233 | // Add bezier curve & controls 234 | base.drawCurve(); 235 | 236 | base.$el.trigger('update.pathslider', [base]); 237 | } 238 | }; 239 | 240 | // end dragging 241 | base.dragEnd = function(e){ 242 | if (o.edit && base.drag !== null) { 243 | base.drag = null; 244 | base.$canvas.removeClass('moving'); 245 | o.points = base.points; 246 | base.updateBuilder(true); 247 | } 248 | }; 249 | 250 | base.crop = function(){ 251 | o.points = base.points; 252 | base.updateBuilder(true); 253 | // include curve width when cropping 254 | var w = o.curve.width * 2; 255 | return [ 256 | Math.min.apply(this,base.arrayX) - w, // min X 257 | Math.min.apply(this,base.arrayY) - w, // min Y 258 | Math.max.apply(this,base.arrayX) + w, // max X 259 | Math.max.apply(this,base.arrayY) + w // max Y 260 | ]; 261 | }; 262 | 263 | // base.pointsxy = [ sx,sy, csx,csy, cex,cey, ex,ey ] to 264 | // base.points = [ sx,sy, csxo,csyo, cexo,ceyo, ex,ey ] 265 | base.convert = function(p){ 266 | p = p || base.pointsxy; 267 | return [ 268 | p[0], p[1], // start x,y 269 | p[2] - p[0], p[3] - p[1], // start control x,y 270 | p[4] - p[6], p[5] - p[7], // end control x,y 271 | p[6], p[7] // end x,y 272 | ]; 273 | }; 274 | 275 | // shift all points so coords based from origin 276 | // example: base.shift(-10, +10); 277 | base.shift = function(x,y){ 278 | // b = [ sx,sy, csx,csy, cex,cey, ex,ey ] 279 | var b = base.points; 280 | return [ 281 | Math.round(b[0]+x), Math.round(b[1]+y), 282 | b[2], b[3], 283 | b[4], b[5], 284 | Math.round(b[6]+x), Math.round(b[7]+y) 285 | ]; 286 | }; 287 | 288 | base.getCode = function(){ 289 | var dim = base.crop(), 290 | points = base.shift(-dim[0],-dim[1]), 291 | txt = '\n' + 298 | ''; 313 | return txt; 314 | }; 315 | 316 | // Run initializer 317 | base.builderInit(); 318 | 319 | }); 320 | }; 321 | 322 | })(jQuery); 323 | 324 | /* jshint ignore:start */ 325 | /** 326 | * Chainvas: Make APIs chainable 327 | * @author Lea Verou 328 | * MIT license http://www.opensource.org/licenses/mit-license.php 329 | */ 330 | (function(){var e=window.Chainvas={chainable:function(a){return function(){var b=a.apply(this,arguments);return b===void 0?this:b}},chainablizeOne:function(a,b){try{e.utils.hasOwnProperty(a,b)&&e.utils.isFunction(a[b])&&(a[b]=e.chainable(a[b]))}catch(c){}return this},chainablize:function(a,b){var c=a.prototype;if(b)for(var d=b.length;d--;)e.chainablizeOne(c,b[d]);else for(d in c)e.chainablizeOne(c,d);return this},helpers:function(a,b){var c=a.prototype,d;for(d in e.methods)c&&!(d in c)&&(c[d]=e.methods[d]); 331 | e.extend(c,b);return this},extend:function(a,b){return Chainvas.methods.prop.call(a,b)},global:function(a,b,c){typeof a==="string"&&(a=[a]);for(var d=a.length;d--;){var f=window[a[d]];f&&e.chainablize(f,c).helpers(f,b)}},methods:{prop:function(){if(arguments.length===1){var a=arguments[0],b;for(b in a)this[b]=a[b]}else arguments.length===2&&(this[arguments[0]]=arguments[1]);return this}},utils:{isFunction:function(a){var b=Object.prototype.toString.call(a);return b==="[object Function]"||b==="[object Object]"&& 332 | "call"in a&&"apply"in a&&/^\s*\bfunction\s+\w+\([\w,]*\) \{/.test(a+"")},hasOwnProperty:function(a,b){try{return a.hasOwnProperty(b)}catch(c){return b in a&&(!a.prototype||!(b in a.prototype)||a.prototype[b]!==a[b])}}}}})(); 333 | 334 | /** 335 | * Chainvas module: Canvas 336 | */ 337 | Chainvas.global("CanvasRenderingContext2D",{circle:function(a,b,d){return this.beginPath().arc(a,b,d,0,2*Math.PI,!1).closePath()},roundRect:function(a,b,d,e,c){return this.beginPath().moveTo(a+c,b).lineTo(a+d-c,b).quadraticCurveTo(a+d,b,a+d,b+c).lineTo(a+d,b+e-c).quadraticCurveTo(a+d,b+e,a+d-c,b+e).lineTo(a+c,b+e).quadraticCurveTo(a,b+e,a,b+e-c).lineTo(a,b+c).quadraticCurveTo(a,b,a+c,b).closePath()}}); 338 | /* jshint ignore:end */ 339 | -------------------------------------------------------------------------------- /js/jquery.pathslider.js: -------------------------------------------------------------------------------- 1 | /* jQuery Pathslider v1.0.0 alpha 2 | * By Rob Garrison (Mottie) 3 | * MIT License 4 | */ 5 | (function($){ 6 | $.pathslider = function(el, options){ 7 | 8 | // To avoid scope issues, use 'base' instead of 'this' 9 | // to reference this class from internal events and functions. 10 | var base = this, o; 11 | 12 | // Access to jQuery and DOM versions of element 13 | base.$el = $(el).addClass('pathslider'); 14 | base.el = el; 15 | 16 | // Add a reverse reference to the DOM object 17 | base.$el.data("pathslider", base); 18 | 19 | base.init = function(){ 20 | var t; 21 | base.options = o = $.extend(true, {}, $.pathslider.defaults, options); 22 | 23 | // Is there a canvas? 24 | t = document.createElement('canvas'); 25 | base.hasCanvas = !!(t.getContext && t.getContext('2d')); 26 | base.hasTouch = document.hasOwnProperty("ontouchend"); 27 | 28 | // add grip 29 | base.$grip = $('
    ').appendTo(base.$el); 30 | 31 | // store array of x & y positions for cross reference 32 | base.points = []; 33 | base.pointsxy = []; 34 | base.arrayX = []; 35 | base.arrayY = []; 36 | base.arrayP = []; 37 | 38 | base.rad2deg = 180 / Math.PI; // convert radians to degrees (multiply radian by this value) 39 | base.sliding = false; // flag for dragging element 40 | base.lastPercent = base.percent = o.value; 41 | 42 | // Callbacks 43 | // slide triggered on EVERY mouse move; change triggered on slide stop 44 | $.each('create update start slide change stop'.split(' '), function(i,f){ 45 | if ($.isFunction(o[f])){ 46 | base.$el.bind(f + '.pathslider', o[f]); 47 | } 48 | }); 49 | 50 | $(document) 51 | .bind( base.hasTouch ? 'touchend.pathslider touchcancel.pathslider' : 'mouseup.pathslider mouseleave.pathslider', function(e){ 52 | if (base.sliding) { // && ($(e.target).closest('.pathslider').length || e.type === 'mouseleave')) { 53 | base.$el.trigger('stop.pathslider', [base]); 54 | if (base.lastPercent !== base.percent) { 55 | base.lastPercent = base.percent; 56 | base.$el.trigger('change.pathslider', [base]); 57 | } 58 | } 59 | base.$grip.removeClass('sliding'); 60 | base.sliding = false; 61 | }) 62 | .bind( (base.hasTouch ? 'touchmove' : 'mousemove') + '.pathslider', function(e){ 63 | if (base.sliding) { 64 | base.setSlider( base.findPos(e), null, true ); 65 | } 66 | }); 67 | 68 | $(window) 69 | .bind('resize.pathslider', function(){ 70 | base.update(); 71 | }) 72 | .bind('load', function(){ 73 | // needed because loading images/fonts will shift the page 74 | base.sliderDim[0] = base.$el.offset().left; 75 | base.sliderDim[1] = base.$el.offset().top; 76 | }); 77 | 78 | base.$grip 79 | .bind( (base.hasTouch ? 'touchstart' : 'mousedown') + '.pathslider', function(e){ 80 | base.sliding = true; 81 | $(this).addClass('sliding'); 82 | base.$el.trigger('start.pathslider', [base]); 83 | return false; 84 | }) 85 | .bind('click', function(){ 86 | return false; 87 | }); 88 | 89 | base.redraw(); 90 | 91 | base.$el.trigger('create.pathslider', [base]); 92 | 93 | }; 94 | 95 | // update dimensions & grip position 96 | base.update = function(){ 97 | 98 | // using attr to remove other css grip classes when updating 99 | base.$grip.attr('class', 'pathslider-grip ' + o.gripClass); 100 | 101 | if (base.ctx) { 102 | // clear canvas *before* setting new dimensions 103 | // just in case the new size is smaller than the previous 104 | base.ctx.clearRect(0, 0, base.sliderDim[2], base.sliderDim[3]); 105 | } 106 | 107 | base.sliderDim = [ 108 | base.$el.offset().left, 109 | base.$el.offset().top, 110 | base.$el.width(), 111 | base.$el.height() 112 | ]; 113 | 114 | // get grip dimensions; jQuery v3+ width() & height() return the rotated dimensions 115 | // which we don't want! 116 | var computedStyle = window.getComputedStyle(base.$grip[0]); 117 | // for centering grip 118 | base.gripCenter = [ parseInt(computedStyle.width, 10)/2, parseInt(computedStyle.height, 10)/2 ]; 119 | 120 | // number of data points to store - increase to smooth the animation (based on slider size) 121 | base.dataPoints = o.dataPoints; 122 | // in next update add min/max/step 123 | // base.range = o.max - o.min; 124 | // base.dataPoints = base.range * o.step; 125 | 126 | base.makeArray(); 127 | // save the position in the array of the starting value (roughly) 128 | var t = $.inArray(base.percent, base.arrayP); 129 | base.position = (t === -1) ? Math.round(base.percent/100 * base.dataPoints) : t; 130 | 131 | base.setSlider(base.percent, null, true); 132 | if (base.hasCanvas && o.useCanvas) { base.drawCurve(); } 133 | 134 | }; 135 | 136 | // set position of slider 137 | base.setSlider = function(percent, callback, internal) { 138 | if (!isNaN(percent)) { 139 | // find position on bezier curve; p = percent (range 0 - 100) 140 | // set position of slider without using the array (more precision) 141 | percent = parseFloat(percent, 10); 142 | percent = (percent > 100) ? 100 : percent < 0 ? 0 : percent; 143 | var css, angle, 144 | // pos = $.inArray(percent, base.arrayP), 145 | p = base.calcBezier(percent/100, base.pointsxy), 146 | pm1 = (percent - 2 > 0) ? base.calcBezier( (percent-2)/100, base.pointsxy ) : p, 147 | pp1 = (percent + 2 < 100) ? base.calcBezier( (percent+2)/100, base.pointsxy ) : p, 148 | // m = slope of tangent - used to change rotation angle of the grip 149 | // yes, I could have used the cubic derivative, but this is less math 150 | m = (pp1[0] - pm1[0] === 0) ? 90 : (pp1[1] - pm1[1])/(pp1[0] - pm1[0]); 151 | base.angle = parseInt(Math.atan(m) * base.rad2deg, 10); 152 | angle = 'rotate(' + base.angle + 'deg)'; 153 | css = (o.rotateGrip) ? { 154 | '-webkit-transform' : angle, 155 | 'transform' : angle 156 | } : {}; 157 | css.left = p[0] - base.gripCenter[0]; 158 | css.top = p[1] - base.gripCenter[1]; 159 | base.$grip 160 | .attr({ 161 | 'data-angle' : base.angle, 162 | 'data-percent' : percent 163 | }) 164 | .css(css); 165 | // find closest percent in the array - this relies on there being a factor 166 | // of 100 datapoints, so it'll need changing when we have a min/max/step 167 | base.percent = percent; // Math.round(percent*r)/r; 168 | if ((percent !== base.lastPercent && !base.sliding) || !internal) { 169 | base.$el.trigger('change.pathslider', [base]); 170 | } 171 | } 172 | if (typeof callback === 'function') { callback(base); } 173 | }; 174 | 175 | // relative mouse position 176 | base.mousePos = function(e) { 177 | return [ 178 | (e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX) - base.sliderDim[0], 179 | (e.originalEvent.touches ? e.originalEvent.touches[0].pageY : e.pageY) - base.sliderDim[1] 180 | ]; 181 | }; 182 | 183 | // find percentage given the x,y coordinates 184 | // searching through a set array of points starting from the last known position 185 | // This allows the curve to loop over itself without mixing up intersecting points 186 | // The biggest issue is a very sharp turn 187 | base.findPos = function(event) { 188 | var i, j, dx, dy, px = [], py = [], 189 | last = base.position, //* base.dataPoints / 100, 190 | // check x & y cross ref based on nearby positions (+/- tolerance) 191 | t = parseInt(o.tolerance + 1, 10) || 2, // tolerance of 1 is too small 192 | r = parseInt(o.range, 10) || base.gripCenter[0], // set to 1/2 width of grip 193 | pos = base.mousePos(event); 194 | // save percent 195 | for ( i=0; i < r; i++ ){ 196 | px = []; py = []; 197 | for ( j=0; j < t + 1; j++ ){ 198 | // check positive direction 199 | dx = Math.abs(base.arrayX[last+j] - pos[0]) <= i; 200 | dy = Math.abs(base.arrayY[last+j] - pos[1]) <= i; 201 | if (dx && dy) { return base.returnPos(last+j); } 202 | if (dx) { px.push(last+j); } 203 | if (dy) { py.push(last+j); } 204 | // check in negative direction 205 | dx = Math.abs(base.arrayX[last-j] - pos[0]) <= i; 206 | dy = Math.abs(base.arrayY[last-j] - pos[1]) <= i; 207 | if (dx && dy) { return base.returnPos(last-j); } 208 | if (dx) { px.push(last-j); } 209 | if (dy) { py.push(last-j); } 210 | } 211 | if (px.length === 1 && py.length > 1) { return base.returnPos(px[0]); } 212 | if (py.length === 1 && px.length > 1) { return base.returnPos(py[0]); } 213 | } 214 | return base.returnPos(last); 215 | }; 216 | 217 | // return found position & trigger slide event 218 | base.returnPos = function(p) { 219 | var t = base.position === p; 220 | base.percent = base.arrayP[p]; 221 | base.position = p; 222 | if (!t) { 223 | if (base.hasCanvas && o.useCanvas) { 224 | base.drawCurve(); 225 | } 226 | base.$el.trigger('slide.pathslider', [base] ); 227 | } 228 | return base.percent; 229 | }; 230 | 231 | // build cross-ref array - find position based on x,y coords 232 | base.makeArray = function(){ 233 | var i, t, b = base.pointsxy, 234 | n = base.dataPoints; 235 | for ( i=0; i < n+1; i++ ){ 236 | t = base.calcBezier(i/n, b); 237 | base.arrayX[i] = t[0]; 238 | base.arrayY[i] = t[1]; 239 | base.arrayP[i] = t[2]; 240 | } 241 | }; 242 | 243 | // Calculate bezier x & y based on percentage (p) 244 | // cubic bezier = start(p^3) + cstart(3*p^2*(1−p)) + cend(3*p*(1−p)^2) + end(1−p)^3 245 | // b = [ startx,starty, cstartx,cstarty, cendx,cendy, endx,endy ] 246 | base.calcBezier = function(p,b){ 247 | var p2 = p*p, 248 | omp = (1-p), // omp = one minus p - smart naming ftw! 249 | omp2 = omp*omp, 250 | f1 = omp*omp2, 251 | f2 = 3*p*omp2, 252 | f3 = 3*p2*omp, 253 | f4 = p*p2; 254 | return [ 255 | Math.round(b[0]*f1 + b[2]*f2 + b[4]*f3 + b[6]*f4), // bezier x 256 | Math.round(b[1]*f1 + b[3]*f2 + b[5]*f3 + b[7]*f4), // bezier y 257 | Math.round(p*1000)/10 // percentage with one decimal place 258 | ]; 259 | }; 260 | 261 | // base.points = [ sx,sy, csxo,csyo, cexo,ceyo, ex,ey ] 262 | // sx,sy = start x & y 263 | // csxo,csyo = control start x & y offset from start point 264 | // cexo,ceyo = control end x & y offset from end point 265 | // ex,ey = end x & y 266 | // convert needed for canvas - using the offset just makes the code easier to read 267 | // base.pointsxy = [ sx, sy, csx, csy, cex, cey, ex, ey ] 268 | base.convert2xy = function(p){ 269 | p = p || base.points; 270 | return [ 271 | p[0], p[1], // start x,y 272 | p[0] + p[2], p[1] + p[3], // start control x,y 273 | p[6] + p[4], p[7] + p[5], // end control x,y 274 | p[6], p[7] // end x,y 275 | ]; 276 | }; 277 | 278 | base.redraw = function(points) { 279 | // update from options 280 | base.points = base.options.points = points || base.options.points; 281 | // store array of x & y positions for cross reference 282 | base.pointsxy = base.convert2xy(); 283 | // update grip 284 | base.update(); 285 | // update curve 286 | base.drawCurve(); 287 | base.setSlider(base.percent, null, true); 288 | }; 289 | 290 | // Make purdy curve 291 | base.drawCurve = function() { 292 | var ctx, grad, tmp, 293 | points = base.pointsxy; 294 | if (!base.$el.find('canvas').length) { 295 | $('').appendTo(base.$el); 296 | // size in attribute needed to keep canvas size in proportion 297 | base.$canvas = base.$el.find('canvas').attr({ width: base.sliderDim[2], height: base.sliderDim[3] }); 298 | base.canvas = base.$canvas[0]; 299 | base.ctx = base.canvas.getContext("2d"); 300 | } 301 | ctx = base.ctx; 302 | ctx.clearRect(0, 0, base.sliderDim[2], base.sliderDim[3]); 303 | ctx.lineCap = o.curve.cap; 304 | ctx.lineJoin = o.curve.cap; 305 | ctx.lineWidth = o.curve.width; 306 | // this can be a gradient or image as well. See 307 | // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors 308 | if ($.isArray(o.curve.color)) { 309 | grad = ctx.createLinearGradient(points[0], points[1], points[6], points[7]); 310 | tmp = base.percent/100; 311 | grad.addColorStop(0, o.curve.color[0]); 312 | grad.addColorStop(tmp, o.curve.color[0]); 313 | if (tmp + 0.01 <= 1) { tmp += 0.01; } 314 | grad.addColorStop(tmp, o.curve.color[1]); 315 | grad.addColorStop(1, o.curve.color[1]); 316 | ctx.strokeStyle = grad; 317 | } else { 318 | ctx.strokeStyle = o.curve.color; 319 | } 320 | tmp = true; 321 | if (typeof o.drawCanvas === 'function') { 322 | // return anything except false to continue drawing the curve 323 | tmp = o.drawCanvas(base, ctx, points) !== false; 324 | ctx = base.ctx; 325 | } 326 | // tmp returned from drawCanvas; if 327 | if (tmp === true) { 328 | base.finishCurve(ctx, points); 329 | } 330 | }; 331 | 332 | base.finishCurve = function(ctx, points) { 333 | ctx = ctx || base.ctx; 334 | points = points || base.pointsxy; 335 | ctx.beginPath(); 336 | ctx.moveTo(points[0], points[1]); 337 | ctx.bezierCurveTo(points[2], points[3], points[4], points[5], points[6], points[7]); 338 | ctx.stroke(); 339 | }; 340 | 341 | // Run initializer 342 | base.init(); 343 | 344 | }; 345 | 346 | $.pathslider.defaults = { 347 | 348 | // Appearance 349 | gripClass : '', // class added to the grip/handle 350 | rotateGrip : true, // when true, the grip will rotate based on the shape of the path 351 | 352 | // canvas curve styling 353 | useCanvas : true, 354 | curve : { width: 4, color: "#333", cap: "round" }, 355 | 356 | // Usability 357 | // sx,sy = start x & y 358 | // csxo,csyo = control start x & y offset from start point 359 | // cexo,ceyo = control end x & y offset from end point 360 | // ex,ey = end x & y 361 | // [ sx,sy, csxo,csyo, cexo,ceyo, ex,ey ] 362 | points : [ 0,50, 50,-50, -50,-50, 250,50 ], 363 | 364 | value : 50, // starting value - range 0 - 100% 365 | // min : 0, // minimum value on the slider 366 | // max : 100, // maximum value on the slider 367 | // step : 1, // step to use between min and max 368 | 369 | // Tweaking 370 | dataPoints: 100, // Total number of points of the curve to save; increase in increments of 100 to smooth out the grip movement, but not more than 500 (it slows everything down) 371 | tolerance : 3, // distance on the curve from the last position to check; increase this to scroll faster 372 | range : 30 // distance, in pixels, from the cursor to a matching x/y on the curve (should be about the same size as the grip) 373 | 374 | }; 375 | 376 | $.fn.pathslider = function(options, callback){ 377 | return this.each(function(){ 378 | var percent, slider = $(this).data('pathslider'); 379 | 380 | // initialize the slider but prevent multiple initializations 381 | if ((typeof(options)).match('object|undefined')){ 382 | if (!slider) { 383 | (new $.pathslider(this, options)); 384 | } else { 385 | return slider.redraw(); 386 | } 387 | // If options is a number, set percentage 388 | } else if (/\d/.test(options) && !isNaN(options) && slider) { 389 | percent = (typeof(options) === "number") ? options : parseInt($.trim(options),10); // accepts " 2 " 390 | // ignore out of bound percentages 391 | if ( percent >= 0 && percent <= 100 ) { 392 | slider.setSlider(percent, callback); // set percent & callback 393 | } 394 | } 395 | }); 396 | }; 397 | 398 | $.fn.getpathslider = function(){ 399 | return this.data('pathslider'); 400 | }; 401 | 402 | })(jQuery); 403 | -------------------------------------------------------------------------------- /dist/jquery.pathslider.js: -------------------------------------------------------------------------------- 1 | /* jQuery Pathslider v1.0.0 alpha 2 | * By Rob Garrison (Mottie) 3 | * MIT License 4 | */ 5 | (function($){ 6 | $.pathslider = function(el, options){ 7 | 8 | // To avoid scope issues, use 'base' instead of 'this' 9 | // to reference this class from internal events and functions. 10 | var base = this, o; 11 | 12 | // Access to jQuery and DOM versions of element 13 | base.$el = $(el).addClass('pathslider'); 14 | base.el = el; 15 | 16 | // Add a reverse reference to the DOM object 17 | base.$el.data("pathslider", base); 18 | 19 | base.init = function(){ 20 | var t; 21 | base.options = o = $.extend(true, {}, $.pathslider.defaults, options); 22 | 23 | // Is there a canvas? 24 | t = document.createElement('canvas'); 25 | base.hasCanvas = !!(t.getContext && t.getContext('2d')); 26 | base.hasTouch = document.hasOwnProperty("ontouchend"); 27 | 28 | // add grip 29 | base.$grip = $('
    ').appendTo(base.$el); 30 | 31 | // store array of x & y positions for cross reference 32 | base.points = []; 33 | base.pointsxy = []; 34 | base.arrayX = []; 35 | base.arrayY = []; 36 | base.arrayP = []; 37 | 38 | base.rad2deg = 180 / Math.PI; // convert radians to degrees (multiply radian by this value) 39 | base.sliding = false; // flag for dragging element 40 | base.lastPercent = base.percent = o.value; 41 | 42 | // Callbacks 43 | // slide triggered on EVERY mouse move; change triggered on slide stop 44 | $.each('create update start slide change stop'.split(' '), function(i,f){ 45 | if ($.isFunction(o[f])){ 46 | base.$el.bind(f + '.pathslider', o[f]); 47 | } 48 | }); 49 | 50 | $(document) 51 | .bind( base.hasTouch ? 'touchend.pathslider touchcancel.pathslider' : 'mouseup.pathslider mouseleave.pathslider', function(e){ 52 | if (base.sliding) { // && ($(e.target).closest('.pathslider').length || e.type === 'mouseleave')) { 53 | base.$el.trigger('stop.pathslider', [base]); 54 | if (base.lastPercent !== base.percent) { 55 | base.lastPercent = base.percent; 56 | base.$el.trigger('change.pathslider', [base]); 57 | } 58 | } 59 | base.$grip.removeClass('sliding'); 60 | base.sliding = false; 61 | }) 62 | .bind( (base.hasTouch ? 'touchmove' : 'mousemove') + '.pathslider', function(e){ 63 | if (base.sliding) { 64 | base.setSlider( base.findPos(e), null, true ); 65 | } 66 | }); 67 | 68 | $(window) 69 | .bind('resize.pathslider', function(){ 70 | base.update(); 71 | }) 72 | .bind('load', function(){ 73 | // needed because loading images/fonts will shift the page 74 | base.sliderDim[0] = base.$el.offset().left; 75 | base.sliderDim[1] = base.$el.offset().top; 76 | }); 77 | 78 | base.$grip 79 | .bind( (base.hasTouch ? 'touchstart' : 'mousedown') + '.pathslider', function(e){ 80 | base.sliding = true; 81 | $(this).addClass('sliding'); 82 | base.$el.trigger('start.pathslider', [base]); 83 | return false; 84 | }) 85 | .bind('click', function(){ 86 | return false; 87 | }); 88 | 89 | base.redraw(); 90 | 91 | base.$el.trigger('create.pathslider', [base]); 92 | 93 | }; 94 | 95 | // update dimensions & grip position 96 | base.update = function(){ 97 | 98 | // using attr to remove other css grip classes when updating 99 | base.$grip.attr('class', 'pathslider-grip ' + o.gripClass); 100 | 101 | if (base.ctx) { 102 | // clear canvas *before* setting new dimensions 103 | // just in case the new size is smaller than the previous 104 | base.ctx.clearRect(0, 0, base.sliderDim[2], base.sliderDim[3]); 105 | } 106 | 107 | base.sliderDim = [ 108 | base.$el.offset().left, 109 | base.$el.offset().top, 110 | base.$el.width(), 111 | base.$el.height() 112 | ]; 113 | 114 | // get grip dimensions; jQuery v3+ width() & height() return the rotated dimensions 115 | // which we don't want! 116 | var computedStyle = window.getComputedStyle(base.$grip[0]); 117 | // for centering grip 118 | base.gripCenter = [ parseInt(computedStyle.width, 10)/2, parseInt(computedStyle.height, 10)/2 ]; 119 | 120 | // number of data points to store - increase to smooth the animation (based on slider size) 121 | base.dataPoints = o.dataPoints; 122 | // in next update add min/max/step 123 | // base.range = o.max - o.min; 124 | // base.dataPoints = base.range * o.step; 125 | 126 | base.makeArray(); 127 | // save the position in the array of the starting value (roughly) 128 | var t = $.inArray(base.percent, base.arrayP); 129 | base.position = (t === -1) ? Math.round(base.percent/100 * base.dataPoints) : t; 130 | 131 | base.setSlider(base.percent, null, true); 132 | if (base.hasCanvas && o.useCanvas) { base.drawCurve(); } 133 | 134 | }; 135 | 136 | // set position of slider 137 | base.setSlider = function(percent, callback, internal) { 138 | if (!isNaN(percent)) { 139 | // find position on bezier curve; p = percent (range 0 - 100) 140 | // set position of slider without using the array (more precision) 141 | percent = parseFloat(percent, 10); 142 | percent = (percent > 100) ? 100 : percent < 0 ? 0 : percent; 143 | var css, angle, 144 | // pos = $.inArray(percent, base.arrayP), 145 | p = base.calcBezier(percent/100, base.pointsxy), 146 | pm1 = (percent - 2 > 0) ? base.calcBezier( (percent-2)/100, base.pointsxy ) : p, 147 | pp1 = (percent + 2 < 100) ? base.calcBezier( (percent+2)/100, base.pointsxy ) : p, 148 | // m = slope of tangent - used to change rotation angle of the grip 149 | // yes, I could have used the cubic derivative, but this is less math 150 | m = (pp1[0] - pm1[0] === 0) ? 90 : (pp1[1] - pm1[1])/(pp1[0] - pm1[0]); 151 | base.angle = parseInt(Math.atan(m) * base.rad2deg, 10); 152 | angle = 'rotate(' + base.angle + 'deg)'; 153 | css = (o.rotateGrip) ? { 154 | '-webkit-transform' : angle, 155 | 'transform' : angle 156 | } : {}; 157 | css.left = p[0] - base.gripCenter[0]; 158 | css.top = p[1] - base.gripCenter[1]; 159 | base.$grip 160 | .attr({ 161 | 'data-angle' : base.angle, 162 | 'data-percent' : percent 163 | }) 164 | .css(css); 165 | // find closest percent in the array - this relies on there being a factor 166 | // of 100 datapoints, so it'll need changing when we have a min/max/step 167 | base.percent = percent; // Math.round(percent*r)/r; 168 | if ((percent !== base.lastPercent && !base.sliding) || !internal) { 169 | base.$el.trigger('change.pathslider', [base]); 170 | } 171 | } 172 | if (typeof callback === 'function') { callback(base); } 173 | }; 174 | 175 | // relative mouse position 176 | base.mousePos = function(e) { 177 | return [ 178 | (e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX) - base.sliderDim[0], 179 | (e.originalEvent.touches ? e.originalEvent.touches[0].pageY : e.pageY) - base.sliderDim[1] 180 | ]; 181 | }; 182 | 183 | // find percentage given the x,y coordinates 184 | // searching through a set array of points starting from the last known position 185 | // This allows the curve to loop over itself without mixing up intersecting points 186 | // The biggest issue is a very sharp turn 187 | base.findPos = function(event) { 188 | var i, j, dx, dy, px = [], py = [], 189 | last = base.position, //* base.dataPoints / 100, 190 | // check x & y cross ref based on nearby positions (+/- tolerance) 191 | t = parseInt(o.tolerance + 1, 10) || 2, // tolerance of 1 is too small 192 | r = parseInt(o.range, 10) || base.gripCenter[0], // set to 1/2 width of grip 193 | pos = base.mousePos(event); 194 | // save percent 195 | for ( i=0; i < r; i++ ){ 196 | px = []; py = []; 197 | for ( j=0; j < t + 1; j++ ){ 198 | // check positive direction 199 | dx = Math.abs(base.arrayX[last+j] - pos[0]) <= i; 200 | dy = Math.abs(base.arrayY[last+j] - pos[1]) <= i; 201 | if (dx && dy) { return base.returnPos(last+j); } 202 | if (dx) { px.push(last+j); } 203 | if (dy) { py.push(last+j); } 204 | // check in negative direction 205 | dx = Math.abs(base.arrayX[last-j] - pos[0]) <= i; 206 | dy = Math.abs(base.arrayY[last-j] - pos[1]) <= i; 207 | if (dx && dy) { return base.returnPos(last-j); } 208 | if (dx) { px.push(last-j); } 209 | if (dy) { py.push(last-j); } 210 | } 211 | if (px.length === 1 && py.length > 1) { return base.returnPos(px[0]); } 212 | if (py.length === 1 && px.length > 1) { return base.returnPos(py[0]); } 213 | } 214 | return base.returnPos(last); 215 | }; 216 | 217 | // return found position & trigger slide event 218 | base.returnPos = function(p) { 219 | var t = base.position === p; 220 | base.percent = base.arrayP[p]; 221 | base.position = p; 222 | if (!t) { 223 | if (base.hasCanvas && o.useCanvas) { 224 | base.drawCurve(); 225 | } 226 | base.$el.trigger('slide.pathslider', [base] ); 227 | } 228 | return base.percent; 229 | }; 230 | 231 | // build cross-ref array - find position based on x,y coords 232 | base.makeArray = function(){ 233 | var i, t, b = base.pointsxy, 234 | n = base.dataPoints; 235 | for ( i=0; i < n+1; i++ ){ 236 | t = base.calcBezier(i/n, b); 237 | base.arrayX[i] = t[0]; 238 | base.arrayY[i] = t[1]; 239 | base.arrayP[i] = t[2]; 240 | } 241 | }; 242 | 243 | // Calculate bezier x & y based on percentage (p) 244 | // cubic bezier = start(p^3) + cstart(3*p^2*(1−p)) + cend(3*p*(1−p)^2) + end(1−p)^3 245 | // b = [ startx,starty, cstartx,cstarty, cendx,cendy, endx,endy ] 246 | base.calcBezier = function(p,b){ 247 | var p2 = p*p, 248 | omp = (1-p), // omp = one minus p - smart naming ftw! 249 | omp2 = omp*omp, 250 | f1 = omp*omp2, 251 | f2 = 3*p*omp2, 252 | f3 = 3*p2*omp, 253 | f4 = p*p2; 254 | return [ 255 | Math.round(b[0]*f1 + b[2]*f2 + b[4]*f3 + b[6]*f4), // bezier x 256 | Math.round(b[1]*f1 + b[3]*f2 + b[5]*f3 + b[7]*f4), // bezier y 257 | Math.round(p*1000)/10 // percentage with one decimal place 258 | ]; 259 | }; 260 | 261 | // base.points = [ sx,sy, csxo,csyo, cexo,ceyo, ex,ey ] 262 | // sx,sy = start x & y 263 | // csxo,csyo = control start x & y offset from start point 264 | // cexo,ceyo = control end x & y offset from end point 265 | // ex,ey = end x & y 266 | // convert needed for canvas - using the offset just makes the code easier to read 267 | // base.pointsxy = [ sx, sy, csx, csy, cex, cey, ex, ey ] 268 | base.convert2xy = function(p){ 269 | p = p || base.points; 270 | return [ 271 | p[0], p[1], // start x,y 272 | p[0] + p[2], p[1] + p[3], // start control x,y 273 | p[6] + p[4], p[7] + p[5], // end control x,y 274 | p[6], p[7] // end x,y 275 | ]; 276 | }; 277 | 278 | base.redraw = function(points) { 279 | // update from options 280 | base.points = base.options.points = points || base.options.points; 281 | // store array of x & y positions for cross reference 282 | base.pointsxy = base.convert2xy(); 283 | // update grip 284 | base.update(); 285 | // update curve 286 | base.drawCurve(); 287 | base.setSlider(base.percent, null, true); 288 | }; 289 | 290 | // Make purdy curve 291 | base.drawCurve = function() { 292 | var ctx, grad, tmp, 293 | points = base.pointsxy; 294 | if (!base.$el.find('canvas').length) { 295 | $('').appendTo(base.$el); 296 | // size in attribute needed to keep canvas size in proportion 297 | base.$canvas = base.$el.find('canvas').attr({ width: base.sliderDim[2], height: base.sliderDim[3] }); 298 | base.canvas = base.$canvas[0]; 299 | base.ctx = base.canvas.getContext("2d"); 300 | } 301 | ctx = base.ctx; 302 | ctx.clearRect(0, 0, base.sliderDim[2], base.sliderDim[3]); 303 | ctx.lineCap = o.curve.cap; 304 | ctx.lineJoin = o.curve.cap; 305 | ctx.lineWidth = o.curve.width; 306 | // this can be a gradient or image as well. See 307 | // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors 308 | if ($.isArray(o.curve.color)) { 309 | grad = ctx.createLinearGradient(points[0], points[1], points[6], points[7]); 310 | tmp = base.percent/100; 311 | grad.addColorStop(0, o.curve.color[0]); 312 | grad.addColorStop(tmp, o.curve.color[0]); 313 | if (tmp + 0.01 <= 1) { tmp += 0.01; } 314 | grad.addColorStop(tmp, o.curve.color[1]); 315 | grad.addColorStop(1, o.curve.color[1]); 316 | ctx.strokeStyle = grad; 317 | } else { 318 | ctx.strokeStyle = o.curve.color; 319 | } 320 | tmp = true; 321 | if (typeof o.drawCanvas === 'function') { 322 | // return anything except false to continue drawing the curve 323 | tmp = o.drawCanvas(base, ctx, points) !== false; 324 | ctx = base.ctx; 325 | } 326 | // tmp returned from drawCanvas; if 327 | if (tmp === true) { 328 | base.finishCurve(ctx, points); 329 | } 330 | }; 331 | 332 | base.finishCurve = function(ctx, points) { 333 | ctx = ctx || base.ctx; 334 | points = points || base.pointsxy; 335 | ctx.beginPath(); 336 | ctx.moveTo(points[0], points[1]); 337 | ctx.bezierCurveTo(points[2], points[3], points[4], points[5], points[6], points[7]); 338 | ctx.stroke(); 339 | }; 340 | 341 | // Run initializer 342 | base.init(); 343 | 344 | }; 345 | 346 | $.pathslider.defaults = { 347 | 348 | // Appearance 349 | gripClass : '', // class added to the grip/handle 350 | rotateGrip : true, // when true, the grip will rotate based on the shape of the path 351 | 352 | // canvas curve styling 353 | useCanvas : true, 354 | curve : { width: 4, color: "#333", cap: "round" }, 355 | 356 | // Usability 357 | // sx,sy = start x & y 358 | // csxo,csyo = control start x & y offset from start point 359 | // cexo,ceyo = control end x & y offset from end point 360 | // ex,ey = end x & y 361 | // [ sx,sy, csxo,csyo, cexo,ceyo, ex,ey ] 362 | points : [ 0,50, 50,-50, -50,-50, 250,50 ], 363 | 364 | value : 50, // starting value - range 0 - 100% 365 | // min : 0, // minimum value on the slider 366 | // max : 100, // maximum value on the slider 367 | // step : 1, // step to use between min and max 368 | 369 | // Tweaking 370 | dataPoints: 100, // Total number of points of the curve to save; increase in increments of 100 to smooth out the grip movement, but not more than 500 (it slows everything down) 371 | tolerance : 3, // distance on the curve from the last position to check; increase this to scroll faster 372 | range : 30 // distance, in pixels, from the cursor to a matching x/y on the curve (should be about the same size as the grip) 373 | 374 | }; 375 | 376 | $.fn.pathslider = function(options, callback){ 377 | return this.each(function(){ 378 | var percent, slider = $(this).data('pathslider'); 379 | 380 | // initialize the slider but prevent multiple initializations 381 | if ((typeof(options)).match('object|undefined')){ 382 | if (!slider) { 383 | (new $.pathslider(this, options)); 384 | } else { 385 | return slider.redraw(); 386 | } 387 | // If options is a number, set percentage 388 | } else if (/\d/.test(options) && !isNaN(options) && slider) { 389 | percent = (typeof(options) === "number") ? options : parseInt($.trim(options),10); // accepts " 2 " 390 | // ignore out of bound percentages 391 | if ( percent >= 0 && percent <= 100 ) { 392 | slider.setSlider(percent, callback); // set percent & callback 393 | } 394 | } 395 | }); 396 | }; 397 | 398 | $.fn.getpathslider = function(){ 399 | return this.data('pathslider'); 400 | }; 401 | 402 | })(jQuery); 403 | -------------------------------------------------------------------------------- /demo/colorpicker/colorpicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Color picker 4 | * Author: Stefan Petre www.eyecon.ro 5 | * 6 | * Dual licensed under the MIT and GPL licenses 7 | * 8 | */ 9 | (function ($) { 10 | var ColorPicker = function () { 11 | var 12 | ids = {}, 13 | inAction, 14 | charMin = 65, 15 | visible, 16 | tpl = '
    ', 17 | defaults = { 18 | eventName: 'click', 19 | onShow: function () {}, 20 | onBeforeShow: function(){}, 21 | onHide: function () {}, 22 | onChange: function () {}, 23 | onSubmit: function () {}, 24 | color: 'ff0000', 25 | livePreview: true, 26 | flat: false 27 | }, 28 | fillRGBFields = function (hsb, cal) { 29 | var rgb = HSBToRGB(hsb); 30 | $(cal).data('colorpicker').fields 31 | .eq(1).val(rgb.r).end() 32 | .eq(2).val(rgb.g).end() 33 | .eq(3).val(rgb.b).end(); 34 | }, 35 | fillHSBFields = function (hsb, cal) { 36 | $(cal).data('colorpicker').fields 37 | .eq(4).val(hsb.h).end() 38 | .eq(5).val(hsb.s).end() 39 | .eq(6).val(hsb.b).end(); 40 | }, 41 | fillHexFields = function (hsb, cal) { 42 | $(cal).data('colorpicker').fields 43 | .eq(0).val(HSBToHex(hsb)).end(); 44 | }, 45 | setSelector = function (hsb, cal) { 46 | $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); 47 | $(cal).data('colorpicker').selectorIndic.css({ 48 | left: parseInt(150 * hsb.s/100, 10), 49 | top: parseInt(150 * (100-hsb.b)/100, 10) 50 | }); 51 | }, 52 | setHue = function (hsb, cal) { 53 | $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); 54 | }, 55 | setCurrentColor = function (hsb, cal) { 56 | $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); 57 | }, 58 | setNewColor = function (hsb, cal) { 59 | $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); 60 | }, 61 | keyDown = function (ev) { 62 | var pressedKey = ev.charCode || ev.keyCode || -1; 63 | if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { 64 | return false; 65 | } 66 | var cal = $(this).parent().parent(); 67 | if (cal.data('colorpicker').livePreview === true) { 68 | change.apply(this); 69 | } 70 | }, 71 | change = function (ev) { 72 | var cal = $(this).parent().parent(), col; 73 | if (this.parentNode.className.indexOf('_hex') > 0) { 74 | cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); 75 | } else if (this.parentNode.className.indexOf('_hsb') > 0) { 76 | cal.data('colorpicker').color = col = fixHSB({ 77 | h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), 78 | s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), 79 | b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) 80 | }); 81 | } else { 82 | cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ 83 | r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), 84 | g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), 85 | b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) 86 | })); 87 | } 88 | if (ev) { 89 | fillRGBFields(col, cal.get(0)); 90 | fillHexFields(col, cal.get(0)); 91 | fillHSBFields(col, cal.get(0)); 92 | } 93 | setSelector(col, cal.get(0)); 94 | setHue(col, cal.get(0)); 95 | setNewColor(col, cal.get(0)); 96 | cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); 97 | }, 98 | blur = function (ev) { 99 | var cal = $(this).parent().parent(); 100 | cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); 101 | }, 102 | focus = function () { 103 | charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; 104 | $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); 105 | $(this).parent().addClass('colorpicker_focus'); 106 | }, 107 | downIncrement = function (ev) { 108 | var field = $(this).parent().find('input').focus(); 109 | var current = { 110 | el: $(this).parent().addClass('colorpicker_slider'), 111 | max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), 112 | y: ev.pageY, 113 | field: field, 114 | val: parseInt(field.val(), 10), 115 | preview: $(this).parent().parent().data('colorpicker').livePreview 116 | }; 117 | $(document).bind('mouseup', current, upIncrement); 118 | $(document).bind('mousemove', current, moveIncrement); 119 | }, 120 | moveIncrement = function (ev) { 121 | ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); 122 | if (ev.data.preview) { 123 | change.apply(ev.data.field.get(0), [true]); 124 | } 125 | return false; 126 | }, 127 | upIncrement = function (ev) { 128 | change.apply(ev.data.field.get(0), [true]); 129 | ev.data.el.removeClass('colorpicker_slider').find('input').focus(); 130 | $(document).unbind('mouseup', upIncrement); 131 | $(document).unbind('mousemove', moveIncrement); 132 | return false; 133 | }, 134 | downHue = function (ev) { 135 | var current = { 136 | cal: $(this).parent(), 137 | y: $(this).offset().top 138 | }; 139 | current.preview = current.cal.data('colorpicker').livePreview; 140 | $(document).bind('mouseup', current, upHue); 141 | $(document).bind('mousemove', current, moveHue); 142 | }, 143 | moveHue = function (ev) { 144 | change.apply( 145 | ev.data.cal.data('colorpicker') 146 | .fields 147 | .eq(4) 148 | .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) 149 | .get(0), 150 | [ev.data.preview] 151 | ); 152 | return false; 153 | }, 154 | upHue = function (ev) { 155 | fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 156 | fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 157 | $(document).unbind('mouseup', upHue); 158 | $(document).unbind('mousemove', moveHue); 159 | return false; 160 | }, 161 | downSelector = function (ev) { 162 | var current = { 163 | cal: $(this).parent(), 164 | pos: $(this).offset() 165 | }; 166 | current.preview = current.cal.data('colorpicker').livePreview; 167 | $(document).bind('mouseup', current, upSelector); 168 | $(document).bind('mousemove', current, moveSelector); 169 | }, 170 | moveSelector = function (ev) { 171 | change.apply( 172 | ev.data.cal.data('colorpicker') 173 | .fields 174 | .eq(6) 175 | .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) 176 | .end() 177 | .eq(5) 178 | .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) 179 | .get(0), 180 | [ev.data.preview] 181 | ); 182 | return false; 183 | }, 184 | upSelector = function (ev) { 185 | fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 186 | fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); 187 | $(document).unbind('mouseup', upSelector); 188 | $(document).unbind('mousemove', moveSelector); 189 | return false; 190 | }, 191 | enterSubmit = function (ev) { 192 | $(this).addClass('colorpicker_focus'); 193 | }, 194 | leaveSubmit = function (ev) { 195 | $(this).removeClass('colorpicker_focus'); 196 | }, 197 | clickSubmit = function (ev) { 198 | var cal = $(this).parent(); 199 | var col = cal.data('colorpicker').color; 200 | cal.data('colorpicker').origColor = col; 201 | setCurrentColor(col, cal.get(0)); 202 | cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); 203 | }, 204 | show = function (ev) { 205 | var cal = $('#' + $(this).data('colorpickerId')); 206 | cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); 207 | var pos = $(this).offset(); 208 | var viewPort = getViewport(); 209 | var top = pos.top + this.offsetHeight; 210 | var left = pos.left; 211 | if (top + 176 > viewPort.t + viewPort.h) { 212 | top -= this.offsetHeight + 176; 213 | } 214 | if (left + 356 > viewPort.l + viewPort.w) { 215 | left -= 356; 216 | } 217 | cal.css({left: left + 'px', top: top + 'px'}); 218 | if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { 219 | cal.show(); 220 | } 221 | $(document).bind('mousedown', {cal: cal}, hide); 222 | return false; 223 | }, 224 | hide = function (ev) { 225 | if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { 226 | if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { 227 | ev.data.cal.hide(); 228 | } 229 | $(document).unbind('mousedown', hide); 230 | } 231 | }, 232 | isChildOf = function(parentEl, el, container) { 233 | if (parentEl == el) { 234 | return true; 235 | } 236 | if (parentEl.contains) { 237 | return parentEl.contains(el); 238 | } 239 | if ( parentEl.compareDocumentPosition ) { 240 | return !!(parentEl.compareDocumentPosition(el) & 16); 241 | } 242 | var prEl = el.parentNode; 243 | while(prEl && prEl != container) { 244 | if (prEl == parentEl) 245 | return true; 246 | prEl = prEl.parentNode; 247 | } 248 | return false; 249 | }, 250 | getViewport = function () { 251 | var m = document.compatMode == 'CSS1Compat'; 252 | return { 253 | l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), 254 | t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), 255 | w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), 256 | h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) 257 | }; 258 | }, 259 | fixHSB = function (hsb) { 260 | return { 261 | h: Math.min(360, Math.max(0, hsb.h)), 262 | s: Math.min(100, Math.max(0, hsb.s)), 263 | b: Math.min(100, Math.max(0, hsb.b)) 264 | }; 265 | }, 266 | fixRGB = function (rgb) { 267 | return { 268 | r: Math.min(255, Math.max(0, rgb.r)), 269 | g: Math.min(255, Math.max(0, rgb.g)), 270 | b: Math.min(255, Math.max(0, rgb.b)) 271 | }; 272 | }, 273 | fixHex = function (hex) { 274 | var len = 6 - hex.length; 275 | if (len > 0) { 276 | var o = []; 277 | for (var i=0; i -1) ? hex.substring(1) : hex), 16); 287 | return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; 288 | }, 289 | HexToHSB = function (hex) { 290 | return RGBToHSB(HexToRGB(hex)); 291 | }, 292 | RGBToHSB = function (rgb) { 293 | var hsb = { 294 | h: 0, 295 | s: 0, 296 | b: 0 297 | }; 298 | var min = Math.min(rgb.r, rgb.g, rgb.b); 299 | var max = Math.max(rgb.r, rgb.g, rgb.b); 300 | var delta = max - min; 301 | hsb.b = max; 302 | if (max != 0) { 303 | 304 | } 305 | hsb.s = max != 0 ? 255 * delta / max : 0; 306 | if (hsb.s != 0) { 307 | if (rgb.r == max) { 308 | hsb.h = (rgb.g - rgb.b) / delta; 309 | } else if (rgb.g == max) { 310 | hsb.h = 2 + (rgb.b - rgb.r) / delta; 311 | } else { 312 | hsb.h = 4 + (rgb.r - rgb.g) / delta; 313 | } 314 | } else { 315 | hsb.h = -1; 316 | } 317 | hsb.h *= 60; 318 | if (hsb.h < 0) { 319 | hsb.h += 360; 320 | } 321 | hsb.s *= 100/255; 322 | hsb.b *= 100/255; 323 | return hsb; 324 | }, 325 | HSBToRGB = function (hsb) { 326 | var rgb = {}; 327 | var h = Math.round(hsb.h); 328 | var s = Math.round(hsb.s*255/100); 329 | var v = Math.round(hsb.b*255/100); 330 | if(s == 0) { 331 | rgb.r = rgb.g = rgb.b = v; 332 | } else { 333 | var t1 = v; 334 | var t2 = (255-s)*v/255; 335 | var t3 = (t1-t2)*(h%60)/60; 336 | if(h==360) h = 0; 337 | if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} 338 | else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} 339 | else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} 340 | else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} 341 | else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} 342 | else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} 343 | else {rgb.r=0; rgb.g=0; rgb.b=0} 344 | } 345 | return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; 346 | }, 347 | RGBToHex = function (rgb) { 348 | var hex = [ 349 | rgb.r.toString(16), 350 | rgb.g.toString(16), 351 | rgb.b.toString(16) 352 | ]; 353 | $.each(hex, function (nr, val) { 354 | if (val.length == 1) { 355 | hex[nr] = '0' + val; 356 | } 357 | }); 358 | return hex.join(''); 359 | }, 360 | HSBToHex = function (hsb) { 361 | return RGBToHex(HSBToRGB(hsb)); 362 | }, 363 | restoreOriginal = function () { 364 | var cal = $(this).parent(); 365 | var col = cal.data('colorpicker').origColor; 366 | cal.data('colorpicker').color = col; 367 | fillRGBFields(col, cal.get(0)); 368 | fillHexFields(col, cal.get(0)); 369 | fillHSBFields(col, cal.get(0)); 370 | setSelector(col, cal.get(0)); 371 | setHue(col, cal.get(0)); 372 | setNewColor(col, cal.get(0)); 373 | }; 374 | return { 375 | init: function (opt) { 376 | opt = $.extend({}, defaults, opt||{}); 377 | if (typeof opt.color == 'string') { 378 | opt.color = HexToHSB(opt.color); 379 | } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { 380 | opt.color = RGBToHSB(opt.color); 381 | } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { 382 | opt.color = fixHSB(opt.color); 383 | } else { 384 | return this; 385 | } 386 | return this.each(function () { 387 | if (!$(this).data('colorpickerId')) { 388 | var options = $.extend({}, opt); 389 | options.origColor = opt.color; 390 | var id = 'collorpicker_' + parseInt(Math.random() * 1000); 391 | $(this).data('colorpickerId', id); 392 | var cal = $(tpl).attr('id', id); 393 | if (options.flat) { 394 | cal.appendTo(this).show(); 395 | } else { 396 | cal.appendTo(document.body); 397 | } 398 | options.fields = cal 399 | .find('input') 400 | .bind('keyup', keyDown) 401 | .bind('change', change) 402 | .bind('blur', blur) 403 | .bind('focus', focus); 404 | cal 405 | .find('span').bind('mousedown', downIncrement).end() 406 | .find('>div.colorpicker_current_color').bind('click', restoreOriginal); 407 | options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); 408 | options.selectorIndic = options.selector.find('div div'); 409 | options.el = this; 410 | options.hue = cal.find('div.colorpicker_hue div'); 411 | cal.find('div.colorpicker_hue').bind('mousedown', downHue); 412 | options.newColor = cal.find('div.colorpicker_new_color'); 413 | options.currentColor = cal.find('div.colorpicker_current_color'); 414 | cal.data('colorpicker', options); 415 | cal.find('div.colorpicker_submit') 416 | .bind('mouseenter', enterSubmit) 417 | .bind('mouseleave', leaveSubmit) 418 | .bind('click', clickSubmit); 419 | fillRGBFields(options.color, cal.get(0)); 420 | fillHSBFields(options.color, cal.get(0)); 421 | fillHexFields(options.color, cal.get(0)); 422 | setHue(options.color, cal.get(0)); 423 | setSelector(options.color, cal.get(0)); 424 | setCurrentColor(options.color, cal.get(0)); 425 | setNewColor(options.color, cal.get(0)); 426 | if (options.flat) { 427 | cal.css({ 428 | position: 'relative', 429 | display: 'block' 430 | }); 431 | } else { 432 | $(this).bind(options.eventName, show); 433 | } 434 | } 435 | }); 436 | }, 437 | showPicker: function() { 438 | return this.each( function () { 439 | if ($(this).data('colorpickerId')) { 440 | show.apply(this); 441 | } 442 | }); 443 | }, 444 | hidePicker: function() { 445 | return this.each( function () { 446 | if ($(this).data('colorpickerId')) { 447 | $('#' + $(this).data('colorpickerId')).hide(); 448 | } 449 | }); 450 | }, 451 | setColor: function(col) { 452 | if (typeof col == 'string') { 453 | col = HexToHSB(col); 454 | } else if (col.r != undefined && col.g != undefined && col.b != undefined) { 455 | col = RGBToHSB(col); 456 | } else if (col.h != undefined && col.s != undefined && col.b != undefined) { 457 | col = fixHSB(col); 458 | } else { 459 | return this; 460 | } 461 | return this.each(function(){ 462 | if ($(this).data('colorpickerId')) { 463 | var cal = $('#' + $(this).data('colorpickerId')); 464 | cal.data('colorpicker').color = col; 465 | cal.data('colorpicker').origColor = col; 466 | fillRGBFields(col, cal.get(0)); 467 | fillHSBFields(col, cal.get(0)); 468 | fillHexFields(col, cal.get(0)); 469 | setHue(col, cal.get(0)); 470 | setSelector(col, cal.get(0)); 471 | setCurrentColor(col, cal.get(0)); 472 | setNewColor(col, cal.get(0)); 473 | } 474 | }); 475 | } 476 | }; 477 | }(); 478 | $.fn.extend({ 479 | ColorPicker: ColorPicker.init, 480 | ColorPickerHide: ColorPicker.hidePicker, 481 | ColorPickerShow: ColorPicker.showPicker, 482 | ColorPickerSetColor: ColorPicker.setColor 483 | }); 484 | })(jQuery) --------------------------------------------------------------------------------