Repository: jwarby/jquery-awesome-cursor
Branch: master
Commit: b3a4c2f88ff1
Files: 21
Total size: 60.2 KB
Directory structure:
gitextract_1pzgmja8/
├── .bowerrc
├── .eslintrc
├── .gitignore
├── .jshintrc
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE.txt
├── README.md
├── _unified_manifest.json
├── bower.json
├── dist/
│ └── jquery.awesome-cursor.js
├── karma.conf.js
├── package.json
├── src/
│ ├── .jshintrc
│ └── jquery.awesome-cursor.js
└── test/
├── .jshintrc
├── awesome-cursor-test-font/
│ └── style.css
├── jquery-awesome-cursor.html
└── jquery-awesome-cursor_test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .bowerrc
================================================
{
"directory": "bower_components"
}
================================================
FILE: .eslintrc
================================================
{
"extends": "eslint:recommended",
"env": {
"browser": true
},
"globals": {
"jQuery": true,
"$": true,
"require": true,
"define": true
},
"rules": {
"no-extra-semi": 0
}
}
================================================
FILE: .gitignore
================================================
/node_modules/
/bower_components/
================================================
FILE: .jshintrc
================================================
{
"node": true,
"browser": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"regexp": true,
"undef": true,
"unused": "vars",
"strict": true,
"trailing": true,
"smarttabs": true,
"maxlen": 100
}
================================================
FILE: .npmignore
================================================
test
src
bower_components
_unified_manifest.json
bower.json
karma.conf.js
Gruntfile.js
================================================
FILE: .travis.yml
================================================
language: node_js
install:
- npm install -g grunt-cli
- npm install
- bower install
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
node_js:
- '5'
- '4'
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## Getting started
- Install all of the necessary dependencies for development, by running `npm install && bower install`
- Check that the tests pass by running `grunt test`
- You will notice that some of the tests get skipped because they are marked as browser-only. This is because there are some issues with running the tests from PhantomJS (one such problem is that it doesn't report
the cursor hotspot position when getting the value of the `cursor` style). To run these tests, open the test page in a browser (Chrome users: note that you will have to host the test directory locally due to restrictions
on accessing file:// URLs in Chrome);
## Important notes
- Please don't edit files in the `dist` subdirectory as they are generated via Grunt. You'll find source code in the `src` subdirectory!
- Please don't edit `bower.json`, `awesome-cursor.jquery.json` or `package.json` directly, as they are also generated via Grunt. Instead, make your changes in `_unified_manifest.json`.
### Code style
Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.**
### PhantomJS
While Grunt can run the included unit tests via [PhantomJS](http://phantomjs.org/), this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers.
## Modifying the code
First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed.
Test that Grunt's CLI and Bower are installed by running `grunt --version` and `bower --version`. If the commands aren't found, run `npm install -g grunt-cli bower`. For more information about installing the tools, see the [getting started with Grunt guide](http://gruntjs.com/getting-started) or [bower.io](http://bower.io/) respectively.
1. Fork and clone the repo.
1. Run `npm install` to install all build dependencies (including Grunt).
1. Run `bower install` to install the front-end dependencies.
1. Run `grunt` to grunt this project.
Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken.
## Submitting pull requests
1. Create a new branch, please don't work in your `master` branch directly.
1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail.
1. Fix stuff.
1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done.
1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere.
1. Update the documentation to reflect any changes.
1. Push to your fork and submit a pull request.
================================================
FILE: Gruntfile.js
================================================
'use strict';
module.exports = function (grunt) {
// Load all grunt tasks
require('load-grunt-tasks')(grunt);
// Show elapsed time at the end
require('time-grunt')(grunt);
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('_unified_manifest.json'),
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
'<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
'* Copyright (c) <%= grunt.template.today("yyyy") %> <%= ' +
'pkg.author.name %>;' +
' Licensed MIT */\n',
// Task configuration.
clean: {
files: ['dist']
},
concat: {
options: {
banner: '<%= banner %>',
stripBanners: true
},
dist: {
src: ['src/<%= pkg.name.replace(\'-\', \'.\') %>.js'],
dest: 'dist/<%= pkg.name.replace(\'-\', \'.\') %>.js'
}
},
uglify: {
options: {
banner: '<%= banner %>'
},
dist: {
src: '<%= concat.dist.dest %>',
dest: 'dist/<%= pkg.name.replace(\'-\', \'.\') %>.min.js'
}
},
karma: {
options: {
browsers: ['Chrome'], // @todo , 'Firefox'],
configFile: 'karma.conf.js',
singleRun: true
},
src: {
},
dist: {
/* Override files list. Should be able to extend list from Karma config
* but it's not working... @todo
*/
files: [
{
src: ['bower_components/fontawesome/fonts/*.*'],
served: true,
included: false
},
{
src: ['test/awesome-cursor-test-font/fonts/*.*'],
served: true,
included: false
},
{ src: ['bower_components/fontawesome/**/*.css'] },
{ src: ['test/awesome-cursor-test-font/style.css'] },
{ src: ['test/**/*.png'], served: true, included: false },
{ src: ['bower_components/jquery/dist/jquery.js'] },
{ src: ['dist/*.min.js'] },
{ src: ['test/*.js'] }
]
},
watch: {
singleRun: false
}
},
jshint: {
options: {
reporter: require('jshint-stylish')
},
gruntfile: {
options: {
jshintrc: '.jshintrc'
},
src: 'Gruntfile.js'
},
src: {
options: {
jshintrc: 'src/.jshintrc'
},
src: ['src/**/*.js']
},
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/**/*.js']
}
},
watch: {
gruntfile: {
files: '<%= jshint.gruntfile.src %>',
tasks: ['jshint:gruntfile']
},
src: {
files: '<%= jshint.src.src %>',
tasks: ['jshint:src', 'qunit']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
}
},
unifiedmanifest: {
all: {
files: {
'./': '_unified_manifest.json'
}
}
}
});
// Default task.
grunt.registerTask('default', [
'jshint', 'unifiedmanifest', 'clean', 'concat', 'uglify', 'karma:src',
'karma:dist'
]);
grunt.registerTask('test', ['karma:src']);
};
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2014 James Warwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# jQuery Awesome Cursor plugin [](http://badge.fury.io/gh/jwarby%2Fjquery-awesome-cursor)
[](https://travis-ci.org/jwarby/jquery-awesome-cursor)
[](https://david-dm.org/jwarby/jquery-awesome-cursor#info=peerDependencies)
[](https://david-dm.org/jwarby/jquery-awesome-cursor#info=devDependencies)
A jQuery plugin for using FontAwesome icons as custom CSS cursors. Also supports using a custom icon font instead of FontAwesome.
See for the full documentation and demos.
```javascript
$('body').awesomeCursor('pencil');
```
Requires [jQuery](http://jquery.com) and [FontAwesome](http://fontawesome.io).
## Getting started
### Installing the plugin
#### via `npm` (recommended)
```shell
npm install jquery-awesome-cursor
```
```html
```
From `v0.3.0` onwards, FontAwesome is marked as an optional dependency. If you plan on using `jquery-awesome-cursor`
with a different icon font, you can skip installation of any optional dependencies like this:
```shell
npm install --no-optional jquery-awesome-cursor
```
##### IMPORTANT: npm@3 won't install `peerDependencies` anymore, so if you haven't already installed jQuery you will get an `UNMET PEER DEPENDENCY` warning when you install. To fix, just do `npm install jquery`.
#### via `bower`
```shell
bower install jquery-awesome-cursor
```
```html
```
##### IMPORTANT: from `v0.3.0` onwards, you need to bring your own FontAwesome! FontAwesome is considered an optional dependency, but Bower doesn't support optional dependencies - so you must install FontAwesome yourself. See [#21](https://github.com/jwarby/jquery-awesome-cursor/issues/21).
```shell
bower install font-awesome
```
#### Manual installation
Download the [production version][min] or the [development version][max]. You will need to download and install FontAwesome manually as well.
[min]: https://raw.githubusercontent.com/jwarby/jquery-awesome-cursor/master/dist/jquery.awesome-cursor.min.js
[max]: https://raw.githubusercontent.com/jwarby/jquery-awesome-cursor/master/dist/jquery.awesome-cursor.js
In your web page:
```html
```
#### RawGit CDN
You could also use [RawGit](https://rawgit.com)'s CDN:
```html
```
## Documentation
### Setting a cursor
You can set a FontAwesome cursor on any element by calling `awesomeCursor`, and passing the name of the icon you
want to use:
```javascript
$('body').awesomeCursor('');
```
See for a list of available icons.
### Setting cursor options
#### Colour
Cursors can be any color you want, specified as a CSS color:
```javascript
$('body').awesomeCursor('eyedropper', {
color: '#ff0000'
})
```
```javascript
$('body').awesomeCursor('eyedropper', {
color: 'rgba(255, 255, 255, 0.75)'
})
```
```javascript
$('body').awesomeCursor('eyedropper', {
color: 'cyan'
});
```
```javascript
$('body').awesomeCursor('eyedropper', {
color: 'hsl(90, 100%, 50%)'
});
```
#### Size
Cursors can be any size (specified in pixels):
```javascript
$('body').awesomeCursor('pencil', {
size: 32
});
```
Only pixel values are supported, as CSS cursor hotspots can only be specified in pixels.
#### Hotspot
The hotspot for a cursor can be defined, with an array containing the hotspot's x and y offsets:
```javascript
$('body').awesomeCursor('pencil', {
hotspot: [0, 17]
});
```
Or, using a string descriptor:
```javascript
$('body').awesomeCursor('pencil', {
hotspot: 'bottom left'
});
```
##### String descriptors
The following values can be used in the hotspot string descriptor:
- `'center'`: positions the hotspot's x and y offset in the center of the cursor
- `'left'` : positions the hotspot's x offset on the left of the cursor (equivalent to 0)
- `'right'` : positions the hotspot's x offset on the far right of the cursor (equivalent to cursorSize - 1)
- `'top'` : positions the hotspot's y offset at the top of the cursor (equivalent to 0)
- `'bottom'`: positions the hotspot's y offset at the bottom of the cursor (equivalent to cursorSize - 1)
The descriptors can be combined by space-separating them, e.g.:
- `'top left'`
- `'center left'`
- `'bottom right'`
- `'top right'`
- etc.
#### Flip
Cursors can be flipped horizontally, vertically, or in both directions, by setting the `flip` option:
```javascript
// Horizontal flip
$('body').awesomeCursor('pencil', {
flip: 'horizontal'
});
// Vertical flip
$('body').awesomeCursor('pencil', {
flip: 'vertical'
});
// Horizontal and Vertical flip
$('body').awesomeCursor('pencil', {
flip: 'both'
});
```
#### Rotate
A cursor can be rotated any number of degrees using the `rotate` option:
```javascript
// 45 degrees clockwise
$('body').awesomeCursor('pencil', {
rotate: 45
});
// 105 degrees anti-clockwise
$('body').awesomeCursor('pencil', {
rotate: -105
});
```
#### Outline
A cursor can be outlined in any color by setting the `outline` option to any valid CSS color:
```javascript
// Red outline
$('body').awesomeCursor('pencil', {
outline: 'red'
});
// White outline
$('body').awesomeCursor('pencil', {
outline: 'white'
});
```
#### Using a different icon font
As of `v0.1.0`, a different icon font instead of FontAwesome can be used. To use a different font, the `font` option must be set to
an object, specifying the font family and the CSS class format for icons. The example below shows how [typicons](http://typicons.com)
icons can be used instead of FontAwesome icons:
```javascript
// Using 'typicons' icons instead of FontAwesome
$('body').awesomecursor('brush', {
font: {
family: 'typicons',
cssClass: 'typcn typcn-%s' // '%s' is the icon name ('brush')
}
});
```
In the above example, we set the font `family` to 'typicons', and set the `cssClass` to the format that `typicons` uses. `%s` denotes the
icon name that is passed as the first argument to `awesomeCursor`.
The `cssClass` option can either be a string, as shown above, or a function:
```javascript
// Using 'typicons' instead of fontawesome
$('body').awesomecursor('brush', {
font: {
family: 'typicons',
cssClass: function(name) {
// `name` is 'brush'
return 'typcn typcn-' + name;
}
}
});
```
You may want to set your replacement icon font as the default:
```javascript
// Use 'typicons' as default
$.fn.awesomeCursor.defaults.font = {
family: 'typicons',
cssClass: 'typcn typcn-%s'
};
```
**Note: the replacement icon font must use `:before` pseudo elements with unicode content**
## Examples
```javascript
/* Set the body element's cursor to a green pencil, with the hotspot located at the bottom left of the cursor (where the
* pencil tip is):
*/
$('body').awesomeCursor('pencil', {
color: 'green',
hotspot: 'bottom left'
});
// Set the cursor to be a big blue location arrow icon:
$('body').awesomeCursor('location-arrow', {
color: '#0050FF',
hotspot: 'top right',
size: 48
});
// Set the cursor to be a horizontally flipped version of the location arrow
$('body').awesomeCursor('location-arrow', {
color: '#0050FF',
hotspot: 'top right',
size: 48,
flip: 'horizontal'
});
// Set the cursor to be a red rotated left arrow
$('body').awesomeCursor('long-arrow-left', {
color: 'red',
hotspot: 'top left',
rotate: 45,
});
// Set the cursor to a black eraser icon with a red outline
$('body').awesomeCursor('eraser', {
color: 'black',
outline: 'red'
});
```
## Browser Support
- Chrome
- Firefox
## Bugs and Feature Requests
-
## Contributing
See [CONTRIBUTING.md](https://github.com/jwarby/jquery-awesome-cursor/blob/master/CONTRIBUTING.md)
## Roadmap
- ~~Allow cursors to be flipped vertically and/or horizontally~~ [✔ v0.0.2](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.0.2)
- ~~Allow cursors to be rotated by an abitrary number of degrees~~ [✔ v0.0.4](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.0.4)
- ~~Optional outlines for cursors~~ [✔ v0.0.5](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.0.5)
- ~~Allow a different icon font to FontAwesome to be used~~ [✔ v0.1.0](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.1.0)
- IE11 support (if possible)
- ~~Data API (under consideration)~~ [✕ Not implemented](https://github.com/jwarby/jquery-awesome-cursor/issues/6)
- ~~Support for composite cursors made of up of multiple icons, a la FontAwesome stacked icons (under consideration)~~ [✕ Not implemented](https://github.com/jwarby/jquery-awesome-cursor/issues/7)
## Release History
- `v0.3.1` - `23rd Apr 2016` - Hotfix - change TravisCI node test versions to v4 and v5; add dotfiles back into bower ignore field
- `v0.3.0` - `23rd Apr 2016` - Make FontAwesome an optional dependency ([#21](https://github.com/jwarby/jquery-awesome-cursor/issues/21)); add an .npmignore file, update bower ignore field; updated devDependencies
- `v0.2.0` - `28th Mar 2016` - Bug fix for certain icons getting clipped when used as a cursor ([#19](https://github.com/jwarby/jquery-awesome-cursor/issues/19))
- `v0.1.5` - ` 9th Dec 2015` - Add `main` to package.json, allowing plugin to be used properly when installed via npm and used with tools such as Browserify
- `v0.1.4` - `23rd Oct 2015` - Remove minor version constraints on bower/npm dependencies
- `v0.1.3` - `19th Oct 2015` - Bug fix for Chrome not updating cursor if cursor had already been set, run tests using Karma, using SPDX format for "license" field in package.json
- `v0.1.2` - `25th May 2015` - Bug fix for rendering artifacts in Chrome ([#14](https://github.com/jwarby/jquery-awesome-cursor/issues/14))
- `v0.1.1` - ` 5th Mar 2015` - Bug fix for rendering artifacts in Firefox ([#10](https://github.com/jwarby/jquery-awesome-cursor/issues/10))
- `v0.1.0` - `17th Dec 2014` - Add `font` option for specifying an alternative icon font to use instead of FontAwesome
- `v0.0.5` - `12th Dec 2014` - Add `outline` option for specifying an outline color for cursors
- `v0.0.4` - `29th Nov 2014` - Add `rotate` option for rotating cursor by a specified number of degrees. Flip transformation now
applied after other transformations (e.g. rotate)
- `v0.0.3` - `17th Nov 2014` - Fix an error in the README file
- `v0.0.2` - `17th Nov 2014` - Add the `flip` option to allow cursors to be flipped horizontally, vertically, or in both directions
- `v0.0.1` - `10th Nov 2014` - First version
================================================
FILE: _unified_manifest.json
================================================
{
"name": "jquery-awesome-cursor",
"version": "0.3.1",
"description": "jQuery plugin for using FontAwesome icons as custom CSS cursors",
"keywords": [
"jquery-plugin",
"fontawesome",
"cursor",
"custom",
"css"
],
"homepage": "https://jwarby.github.io/jquery-awesome-cursor",
"bugs": {
"url": "http://github.com/jwarby/jquery-awesome-cursor/issues"
},
"author": "James Warwood (https://github.com/jwarby)",
"repository": {
"type": "git",
"url": "git+https://github.com/jwarby/jquery-awesome-cursor.git"
},
"license": "MIT",
"_package.json_": {
"devDependencies": {
"bower": "^1.7.9",
"grunt": "^1.0.1",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.1",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-qunit": "^1.2.0",
"grunt-contrib-uglify": "^1.0.1",
"grunt-contrib-watch": "^1.0.0",
"grunt-karma": "^0.12.2",
"grunt-unified-manifest": "git://github.com/jwarby/grunt-unified-manifest.git",
"jquery": "^2.2.3",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.22",
"karma-chrome-launcher": "^0.2.3",
"karma-firefox-launcher": "^0.1.7",
"karma-qunit": "^0.1.9",
"load-grunt-tasks": "^3.5.0",
"qunitjs": "^2.0.0-rc1",
"time-grunt": "^1.3.0"
},
"optionalDependencies": {
"font-awesome": "4.x"
},
"peerDependencies": {
"jquery": "2.x"
},
"engines": {
"node": ">=0.10.0"
},
"main": "dist/jquery.awesome-cursor",
"scripts": {
"test": "grunt test"
},
"directories": {
"test": "test"
}
},
"_bower.json_": {
"main": "dist/jquery.awesome-cursor.js",
"ignore": [
"node_modules",
"bower_components",
"test",
"src",
"package.json",
"_unified_manifest.json",
"Gruntfile.js",
"karma.conf.js",
"**/.*"
],
"dependencies": {
"jquery": "2.x"
},
"devDependencies": {
"qunit": "1.x",
"jquery": "2.x"
}
}
}
================================================
FILE: bower.json
================================================
{
"name": "jquery-awesome-cursor",
"version": "0.3.1",
"description": "jQuery plugin for using FontAwesome icons as custom CSS cursors",
"keywords": [
"jquery-plugin",
"fontawesome",
"cursor",
"custom",
"css"
],
"homepage": "https://jwarby.github.io/jquery-awesome-cursor",
"bugs": {
"url": "http://github.com/jwarby/jquery-awesome-cursor/issues"
},
"author": "James Warwood (https://github.com/jwarby)",
"repository": {
"type": "git",
"url": "git+https://github.com/jwarby/jquery-awesome-cursor.git"
},
"license": "MIT",
"main": "dist/jquery.awesome-cursor.js",
"ignore": [
"node_modules",
"bower_components",
"test",
"src",
"package.json",
"_unified_manifest.json",
"Gruntfile.js",
"karma.conf.js",
"**/.*"
],
"dependencies": {
"jquery": "2.x"
},
"devDependencies": {
"qunit": "1.x",
"jquery": "2.x"
}
}
================================================
FILE: dist/jquery.awesome-cursor.js
================================================
/*! jquery-awesome-cursor - v0.3.1 - 2016-04-23
* https://jwarby.github.io/jquery-awesome-cursor
* Copyright (c) 2016 ; Licensed MIT */
;(function(global, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(global.jQuery);
}
})(this, function($) {
'use strict';
/**
* Parse the user-supplied hotspot string. Hotspot values as strings are used
* to set the cursor based on a human-readable value.
*
* ## Examples
*
* - `hotspot: 'center'`: the hotspot is in the center of the cursor
* - `hotspot: 'center left'`: the hotspot is centered vertically, and fixed
* to the left of the cursor horizontally
* - `hotspot: 'top right'`: the hotspot is at the top right
* - `hotspot: 'center top'`: the hotspot is centered horizontally, and fixed
* to the top of the cursor vertically
*
* @param {String} hotspot The string descriptor for the hotspot location
* @param {Number} size The size of the cursor
*
* @return {[Number]} an array with two elements, the x and y offsets for the
* hotspot
*
* @throws {Error} if `hotspot` is not a string, or `cursorSize` is not a
* number
*/
var parseHotspotString = function(hotspot, cursorSize) {
var xOffset = 0,
yOffset = 0;
if (typeof hotspot !== 'string') {
$.error('Hotspot value is not a string and could not be parsed');
}
if (typeof cursorSize !== 'number') {
$.error('Cursor size must be a number');
}
hotspot.split(' ').forEach(function(part) {
switch (part) {
case 'center':
xOffset = cursorSize / 2;
yOffset = cursorSize / 2;
break;
case 'top':
yOffset = 0;
break;
case 'bottom':
/* Browsers will default to 0 0 if yOffset is the very last pixel,
* hence - 1
*/
yOffset = cursorSize - 1;
break;
case 'left':
xOffset = 0;
break;
case 'right':
xOffset = cursorSize - 1;
break;
}
});
return [xOffset, yOffset];
};
/**
* Returns a new canvas with the same contents as `canvas`, flipped
* accordingly.
*
* @param {Canvas} canvas The canvas to flip
* @param {String} direction The direction flip the canvas in. Can be one
* of:
* - 'horizontal'
* - 'vertical'
* - 'both'
*
* @return {Canvas} a new canvas with the flipped contents of the input canvas
*/
function flipCanvas(canvas, direction) {
if ($.inArray(direction, ['horizontal', 'vertical', 'both']) === -1) {
$.error('Flip value must be one of horizontal, vertical or both');
}
var flippedCanvas = $('')[0],
flippedContext;
flippedCanvas.width = canvas.width;
flippedCanvas.height = canvas.height;
flippedContext = flippedCanvas.getContext('2d');
if (direction === 'horizontal' || direction === 'both') {
flippedContext.translate(canvas.width, 0);
flippedContext.scale(-1, 1);
}
if (direction === 'vertical' || direction === 'both') {
flippedContext.translate(0, canvas.height);
flippedContext.scale(1, -1);
}
flippedContext.drawImage(canvas, 0, 0, canvas.width, canvas.height);
return flippedCanvas;
}
$.fn.extend({
awesomeCursor: function(iconName, options) {
options = $.extend({}, $.fn.awesomeCursor.defaults, options);
if (typeof iconName !== 'string' || !iconName) {
$.error('First parameter must be the icon name, e.g. \'pencil\'');
}
options.size = typeof options.size === 'string' ?
parseInt(options.size, 10) : options.size;
if (typeof options.hotspot === 'string') {
options.hotspot = parseHotspotString(options.hotspot, options.size);
}
// Clamp hotspot coordinates between 0 and size - 1
options.hotspot = $.map(options.hotspot, function(coordinate) {
return Math.min(options.size - 1, Math.max(0, coordinate));
});
var cssClass = (function(name, template) {
if (typeof template === 'string') {
return template.replace(/%s/g, name);
} else if (typeof template === 'function') {
return template(name);
}
return name;
})(iconName, options.font.cssClass),
srcElement = $('', {
class: cssClass,
style: 'display: inline; font-size: ' + options.size + 'px;'
});
// Wrap the icon inside an absolute element to remove it from doc flow
var wrapper = $('', {
style: 'position: absolute; left: -9999px; top: -9999px;'
}).append(srcElement);
// Render element to the DOM, otherwise `getComputedStyle` will not work
$('body').append(wrapper);
// Get the unicode value and dimensions of the icon
var unicode = window.getComputedStyle(srcElement[0], ':before')
.getPropertyValue('content'),
clientRect = srcElement[0].getBoundingClientRect();
var canvas = $('')[0],
canvasSize = Math.max(clientRect.width, clientRect.height),
hotspotOffset, dataURL, context;
// Remove the source element from the DOM
srcElement.remove();
// Increase the size of the canvas to account for the cursor's outline
if (options.outline) {
canvasSize += 2;
}
if (options.rotate) {
// @TODO: move this into it's own function
canvasSize = Math.ceil(Math.sqrt(
Math.pow(canvasSize, 2) + Math.pow(canvasSize, 2)
));
hotspotOffset = (canvasSize - options.size) / 2;
canvas.width = canvasSize;
canvas.height = canvasSize;
context = canvas.getContext('2d');
context.translate(canvas.width / 2, canvas.height / 2);
// Canvas API works in radians, not degrees, hence `* Math.PI / 180`
context.rotate(options.rotate * Math.PI / 180);
context.translate(-canvas.width / 2, -canvas.height / 2);
// Translate hotspot offset
options.hotspot[0] += options.hotspot[0] !== canvas.width / 2 ?
hotspotOffset : 0;
options.hotspot[1] += options.hotspot[1] !== canvas.height / 2 ?
hotspotOffset : 0;
} else {
canvas.height = canvasSize;
canvas.width = canvasSize;
context = canvas.getContext('2d');
}
/* Firefox wraps the extracted unicode value in double quotes - #10
* Chrome 43+ is wrapping the extracted value in single quotes - #14
*/
unicode = unicode.replace(/['"]/g, '');
// Draw the cursor to the canvas
context.fillStyle = options.color;
context.font = options.size + 'px ' + options.font.family;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(unicode, canvasSize / 2, canvasSize / 2);
// Check for outline option
if (options.outline) {
context.lineWidth = 0.5;
context.strokeStyle = options.outline;
context.strokeText(unicode, canvasSize / 2, canvasSize / 2);
}
// Check flip option
if (options.flip) {
canvas = flipCanvas(canvas, options.flip);
}
dataURL = canvas.toDataURL('image/png');
$(this)
// Fixes issue with Chrome not setting cursor if already set
.css('cursor', '')
.css('cursor', [
'url(' + dataURL + ')',
options.hotspot[0],
options.hotspot[1],
',',
'auto'
].join(' '))
;
// Maintain chaining
return this;
}
});
// Expose the defaults so that users can override them if they want to
$.fn.awesomeCursor.defaults = {
color: '#000000',
size: 18,
hotspot: [0, 0],
flip: '',
rotate: 0,
outline: null,
font: {
family: 'FontAwesome',
cssClass: 'fa fa-%s'
}
};
});
================================================
FILE: karma.conf.js
================================================
// Karma configuration
// Generated on Sat Jun 06 2015 15:48:50 GMT+0800 (MYT)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['qunit'],
// list of files / patterns to load in the browser
files: [
{
pattern: 'bower_components/fontawesome/fonts/*.*',
served: true,
included: false
},
{
pattern: 'test/awesome-cursor-test-font/fonts/*.*',
served: true,
included: false
},
'bower_components/fontawesome/**/*.css',
'test/awesome-cursor-test-font/style.css',
{ pattern: 'test/**/*.png', served: true, included: false },
'bower_components/jquery/dist/jquery.js',
'src/*.js',
'test/*.js'
],
proxies: {
'/expected': 'http://localhost:9876/base/test/expected/'
},
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],//, 'Firefox'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
================================================
FILE: package.json
================================================
{
"name": "jquery-awesome-cursor",
"version": "0.3.1",
"description": "jQuery plugin for using FontAwesome icons as custom CSS cursors",
"keywords": [
"jquery-plugin",
"fontawesome",
"cursor",
"custom",
"css"
],
"homepage": "https://jwarby.github.io/jquery-awesome-cursor",
"bugs": {
"url": "http://github.com/jwarby/jquery-awesome-cursor/issues"
},
"author": "James Warwood (https://github.com/jwarby)",
"repository": {
"type": "git",
"url": "git+https://github.com/jwarby/jquery-awesome-cursor.git"
},
"license": "MIT",
"devDependencies": {
"bower": "^1.7.9",
"grunt": "^1.0.1",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.1",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-qunit": "^1.2.0",
"grunt-contrib-uglify": "^1.0.1",
"grunt-contrib-watch": "^1.0.0",
"grunt-karma": "^0.12.2",
"grunt-unified-manifest": "git://github.com/jwarby/grunt-unified-manifest.git",
"jquery": "^2.2.3",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.22",
"karma-chrome-launcher": "^0.2.3",
"karma-firefox-launcher": "^0.1.7",
"karma-qunit": "^0.1.9",
"load-grunt-tasks": "^3.5.0",
"qunitjs": "^2.0.0-rc1",
"time-grunt": "^1.3.0"
},
"optionalDependencies": {
"font-awesome": "4.x"
},
"peerDependencies": {
"jquery": "2.x"
},
"engines": {
"node": ">=0.10.0"
},
"main": "dist/jquery.awesome-cursor",
"scripts": {
"test": "grunt test"
},
"directories": {
"test": "test"
}
}
================================================
FILE: src/.jshintrc
================================================
{
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"sub": true,
"undef": true,
"unused": true,
"boss": true,
"eqnull": true,
"browser": true,
"predef": ["jQuery", "require", "define"]
}
================================================
FILE: src/jquery.awesome-cursor.js
================================================
;(function(global, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(global.jQuery);
}
})(this, function($) {
'use strict';
/**
* Parse the user-supplied hotspot string. Hotspot values as strings are used
* to set the cursor based on a human-readable value.
*
* ## Examples
*
* - `hotspot: 'center'`: the hotspot is in the center of the cursor
* - `hotspot: 'center left'`: the hotspot is centered vertically, and fixed
* to the left of the cursor horizontally
* - `hotspot: 'top right'`: the hotspot is at the top right
* - `hotspot: 'center top'`: the hotspot is centered horizontally, and fixed
* to the top of the cursor vertically
*
* @param {String} hotspot The string descriptor for the hotspot location
* @param {Number} size The size of the cursor
*
* @return {[Number]} an array with two elements, the x and y offsets for the
* hotspot
*
* @throws {Error} if `hotspot` is not a string, or `cursorSize` is not a
* number
*/
var parseHotspotString = function(hotspot, cursorSize) {
var xOffset = 0,
yOffset = 0;
if (typeof hotspot !== 'string') {
$.error('Hotspot value is not a string and could not be parsed');
}
if (typeof cursorSize !== 'number') {
$.error('Cursor size must be a number');
}
hotspot.split(' ').forEach(function(part) {
switch (part) {
case 'center':
xOffset = cursorSize / 2;
yOffset = cursorSize / 2;
break;
case 'top':
yOffset = 0;
break;
case 'bottom':
/* Browsers will default to 0 0 if yOffset is the very last pixel,
* hence - 1
*/
yOffset = cursorSize - 1;
break;
case 'left':
xOffset = 0;
break;
case 'right':
xOffset = cursorSize - 1;
break;
}
});
return [xOffset, yOffset];
};
/**
* Returns a new canvas with the same contents as `canvas`, flipped
* accordingly.
*
* @param {Canvas} canvas The canvas to flip
* @param {String} direction The direction flip the canvas in. Can be one
* of:
* - 'horizontal'
* - 'vertical'
* - 'both'
*
* @return {Canvas} a new canvas with the flipped contents of the input canvas
*/
function flipCanvas(canvas, direction) {
if ($.inArray(direction, ['horizontal', 'vertical', 'both']) === -1) {
$.error('Flip value must be one of horizontal, vertical or both');
}
var flippedCanvas = $('')[0],
flippedContext;
flippedCanvas.width = canvas.width;
flippedCanvas.height = canvas.height;
flippedContext = flippedCanvas.getContext('2d');
if (direction === 'horizontal' || direction === 'both') {
flippedContext.translate(canvas.width, 0);
flippedContext.scale(-1, 1);
}
if (direction === 'vertical' || direction === 'both') {
flippedContext.translate(0, canvas.height);
flippedContext.scale(1, -1);
}
flippedContext.drawImage(canvas, 0, 0, canvas.width, canvas.height);
return flippedCanvas;
}
$.fn.extend({
awesomeCursor: function(iconName, options) {
options = $.extend({}, $.fn.awesomeCursor.defaults, options);
if (typeof iconName !== 'string' || !iconName) {
$.error('First parameter must be the icon name, e.g. \'pencil\'');
}
options.size = typeof options.size === 'string' ?
parseInt(options.size, 10) : options.size;
if (typeof options.hotspot === 'string') {
options.hotspot = parseHotspotString(options.hotspot, options.size);
}
// Clamp hotspot coordinates between 0 and size - 1
options.hotspot = $.map(options.hotspot, function(coordinate) {
return Math.min(options.size - 1, Math.max(0, coordinate));
});
var cssClass = (function(name, template) {
if (typeof template === 'string') {
return template.replace(/%s/g, name);
} else if (typeof template === 'function') {
return template(name);
}
return name;
})(iconName, options.font.cssClass),
srcElement = $('', {
class: cssClass,
style: 'display: inline; font-size: ' + options.size + 'px;'
});
// Wrap the icon inside an absolute element to remove it from doc flow
var wrapper = $('', {
style: 'position: absolute; left: -9999px; top: -9999px;'
}).append(srcElement);
// Render element to the DOM, otherwise `getComputedStyle` will not work
$('body').append(wrapper);
// Get the unicode value and dimensions of the icon
var unicode = window.getComputedStyle(srcElement[0], ':before')
.getPropertyValue('content'),
clientRect = srcElement[0].getBoundingClientRect();
var canvas = $('')[0],
canvasSize = Math.max(clientRect.width, clientRect.height),
hotspotOffset, dataURL, context;
// Remove the source element from the DOM
srcElement.remove();
// Increase the size of the canvas to account for the cursor's outline
if (options.outline) {
canvasSize += 2;
}
if (options.rotate) {
// @TODO: move this into it's own function
canvasSize = Math.ceil(Math.sqrt(
Math.pow(canvasSize, 2) + Math.pow(canvasSize, 2)
));
hotspotOffset = (canvasSize - options.size) / 2;
canvas.width = canvasSize;
canvas.height = canvasSize;
context = canvas.getContext('2d');
context.translate(canvas.width / 2, canvas.height / 2);
// Canvas API works in radians, not degrees, hence `* Math.PI / 180`
context.rotate(options.rotate * Math.PI / 180);
context.translate(-canvas.width / 2, -canvas.height / 2);
// Translate hotspot offset
options.hotspot[0] += options.hotspot[0] !== canvas.width / 2 ?
hotspotOffset : 0;
options.hotspot[1] += options.hotspot[1] !== canvas.height / 2 ?
hotspotOffset : 0;
} else {
canvas.height = canvasSize;
canvas.width = canvasSize;
context = canvas.getContext('2d');
}
/* Firefox wraps the extracted unicode value in double quotes - #10
* Chrome 43+ is wrapping the extracted value in single quotes - #14
*/
unicode = unicode.replace(/['"]/g, '');
// Draw the cursor to the canvas
context.fillStyle = options.color;
context.font = options.size + 'px ' + options.font.family;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(unicode, canvasSize / 2, canvasSize / 2);
// Check for outline option
if (options.outline) {
context.lineWidth = 0.5;
context.strokeStyle = options.outline;
context.strokeText(unicode, canvasSize / 2, canvasSize / 2);
}
// Check flip option
if (options.flip) {
canvas = flipCanvas(canvas, options.flip);
}
dataURL = canvas.toDataURL('image/png');
$(this)
// Fixes issue with Chrome not setting cursor if already set
.css('cursor', '')
.css('cursor', [
'url(' + dataURL + ')',
options.hotspot[0],
options.hotspot[1],
',',
'auto'
].join(' '))
;
// Maintain chaining
return this;
}
});
// Expose the defaults so that users can override them if they want to
$.fn.awesomeCursor.defaults = {
color: '#000000',
size: 18,
hotspot: [0, 0],
flip: '',
rotate: 0,
outline: null,
font: {
family: 'FontAwesome',
cssClass: 'fa fa-%s'
}
};
});
================================================
FILE: test/.jshintrc
================================================
{
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": true,
"newcap": true,
"noarg": true,
"sub": true,
"undef": true,
"unused": true,
"boss": true,
"eqnull": true,
"browser": true,
"predef": [
"console",
"jQuery",
"QUnit",
"module",
"test",
"asyncTest",
"expect",
"start",
"stop",
"ok",
"equal",
"notEqual",
"deepEqual",
"notDeepEqual",
"strictEqual",
"notStrictEqual",
"throws"
]
}
================================================
FILE: test/awesome-cursor-test-font/style.css
================================================
@font-face {
font-family: 'AwesomeCursorTest';
src:url('fonts/AwesomeCursorTest.eot');
src:url('fonts/AwesomeCursorTest.eot') format('embedded-opentype'),
url('fonts/AwesomeCursorTest.woff') format('woff'),
url('fonts/AwesomeCursorTest.ttf') format('truetype'),
url('fonts/AwesomeCursorTest.svg#AwesomeCursorTest') format('svg');
font-weight: normal;
font-style: normal;
}
.act {
font-family: 'AwesomeCursorTest';
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.act-pencil:before {
content: "\e600";
}
.act-brush:before {
content: "\e601";
}
================================================
FILE: test/jquery-awesome-cursor.html
================================================
jQuery Awesome Cursor plugin Test Suite
lame test markupnormal test markupawesome test markup
================================================
FILE: test/jquery-awesome-cursor_test.js
================================================
/* global QUnit */
(function(global, $) {
'use strict';
QUnit.test('force fonts to load', function(assert) {
assert.expect(0);
$('body')
.append('')
.append('')
;
setTimeout(assert.async(), 500);
});
/* Regular expresssion which defines the expected value of the CSS cursor
* property after the plugin has been called on an element
*/
var CURSOR_REGEX = /^url\((?:")?data:image\/png;base64,.*\)(?: 0 0)?, auto$/;
/**
* Extract the cursor's x, y hotspot from the specified element.
*
* @param {jQuery} el The element to extract the cursor's hotspot from
*
* @return {Array|undefined} an array containing the cursor's x and y values,
* or `undefined` if extraction fails
*/
function extractHotspot(el) {
var match = el.attr('style')
.match(/^cursor: url\(.*\) ([0-9]+) ([0-9]+), auto;$/);
if (match && match.length === 3) {
return match.slice(1).map(function(val) {
return parseFloat(val);
});
}
return;
}
/**
* Get a canvas whose contents represent the supplied image.
*
* @param {Image} img The image to render to the canvas
*
* @return {Canvas} the new canvas element, with `img` rendered to it
*/
function getCanvasFromImage(img) {
// Create an empty canvas element
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
return canvas;
}
/**
* Get a canvas with the supplied data URI rendered to it.
*
* @param {String} uri The data URI
*
* @return {Canvas} a new canvas element, with the supplied data rendered to
* it
*/
function getCanvasFromDataURI(uri) {
return getCanvasFromImage($('', {
src: uri
})[0]);
}
/**
* Compare the content of two canvasses, by checking each pixel in the first
* canvas against the second canvas. Bails early and returns false if:
*
* - the two canvasses have different widths or heights
* - the length of the canvasses data attribute is different
*
* @return {Boolean} true if every single pixel in `canvas1` matches every
* pixel in `canvas2`
*/
function canvasCompare(canvas1, canvas2) {
if (!(canvas1.width === canvas2.width &&
canvas1.height === canvas2.height)) {
return false;
}
var ctx1 = canvas1.getContext('2d'),
ctx2 = canvas2.getContext('2d'),
data1 = ctx1.getImageData(0, 0, canvas1.width, canvas1.height),
data2 = ctx2.getImageData(0, 0, canvas2.width, canvas2.height);
if (data1.data.length !== data2.data.length) {
return false;
}
for (var d = 0; d < data1.data.length; d++) {
if (data1.data[d] !== data2.data[d]) {
return false;
}
}
return true;
}
/**
* Check if the supplied image matches the cursor set on this element.
* If element is not provided, it defaults to the body element.
*
* @param {String} imgSrc The src URL for the image to check
* @param {Function} callback Gets called with true if the cursor matches the
* image, or false if it doesn't. The 2nd and 3rd
* parameters to the callback function are the
* cursor's image data and the image data for
* `imgSrc` respectively
*/
$.fn.cursorMatchesImage = function(imgSrc, callback) {
callback = typeof callback === 'function' ? callback : $.noop;
var actualImgData = this.css('cursor').match(/^url\((.*)\)/),
$expectedImg;
if (!actualImgData || actualImgData.length !== 2) {
return callback(false);
}
$expectedImg = $('', {
src: imgSrc
});
$expectedImg.load(function() {
var expectedCanvas = getCanvasFromImage($expectedImg[0]),
actualCanvas = getCanvasFromDataURI(actualImgData[1].replace(/^["']|["']$/g, ''));
if (!canvasCompare(expectedCanvas, actualCanvas)) {
$('body').append(actualCanvas, expectedCanvas, '
' + imgSrc + '
');
return callback(false);
}
return callback(true);
});
};
QUnit.module('jQuery#awesomeCursor', {
// This will run before each test in this module.
beforeEach: function() {
$('#qunit-fixture').remove();
var $qunit = $('', {
id: 'qunit'
});
var $fixture = $('', {
id: 'qunit-fixture'
});
$fixture
.append($('', {
class: 'fa fa-pencil'
}))
.append($('', {
class: 'act act-pencil'
}))
;
$('body').append($qunit).append($fixture);
this.elems = $('#qunit-fixture').children();
}
});
QUnit.test('is chainable', function(assert) {
assert.expect(1);
assert.strictEqual(
this.elems.awesomeCursor('pencil'), this.elems, 'should be chainable'
);
});
QUnit.test('css is correctly set', function(assert) {
assert.expect(1);
this.elems.awesomeCursor('pencil');
assert.ok(
CURSOR_REGEX.test(this.elems.css('cursor')),
'\'' + this.elems.css('cursor') + '\' does not match expected RegExp'
);
});
QUnit.test('throws an error if name parameter is missing or invalid', function(assert) {
var subjects = ['', 0, -1, false, true, {}, [], null],
that = this;
assert.expect(subjects.length);
subjects.forEach(function(subject) {
assert.throws(
function() {
that.elems.awesomeCursor(subject);
},
'Expected an error to be thrown when using `' + subject +
'` as icon name parameter');
});
});
QUnit.test('`hotspot` value can be a string', function(assert) {
assert.expect(1);
assert.ok(this.elems.awesomeCursor('pencil', {
hotspot: 'bottom left'
}));
});
QUnit.test('`hotspot` string values are correctly parsed', function(assert) {
var size = $.fn.awesomeCursor.defaults.size,
subjects = {
'top left': [0, 0],
'center top': [size / 2, 0],
'top right': [size - 1, 0],
'center left': [0, size / 2],
'center': [size / 2, size / 2],
'center right': [size - 1, size / 2],
'bottom left': [0, size - 1],
'center bottom': [size / 2, size - 1],
'bottom right': [size - 1, size - 1]
},
hotspot;
assert.expect(Object.keys(subjects).length);
for (var s in subjects) {
this.elems.awesomeCursor('pencil', {
hotspot: s
});
hotspot = extractHotspot(this.elems);
assert.deepEqual(hotspot, subjects[s]);
}
});
QUnit.test('`hotspot` values get clamped between 0 and cursor size - 1', function(assert) {
var hotspot;
assert.expect(3);
this.elems.awesomeCursor('pencil', {
size: 32,
hotspot: [-3, 34]
});
hotspot = extractHotspot(this.elems);
assert.ok(hotspot);
assert.equal(hotspot[0], 0);
assert.equal(hotspot[1], 31);
});
QUnit.test('can set the color of a cursor', function(assert) {
var done = assert.async();
assert.expect(2);
var next = function() {
this.elems
.awesomeCursor('flag-checkered', {
color: '#00ff00',
size: 18
})
.cursorMatchesImage(
'expected/lime-flag-18.png', function(matches) {
assert.ok(matches);
done();
});
}.bind(this);
this.elems
.awesomeCursor('pencil', {
color: 'red',
size: 18
})
.cursorMatchesImage(
'expected/red-pencil-18.png', function(matches) {
assert.ok(matches);
next();
});
});
QUnit.test('can set the size of a cursor', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems
.awesomeCursor('globe', {
color: 'black',
size: 32
})
.cursorMatchesImage(
'expected/black-globe-32.png', function(matches) {
assert.ok(matches);
done();
});
});
QUnit.test('can set the size of a cursor using a string value', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems
.awesomeCursor('desktop', {
color: 'black',
size: '22px'
})
.cursorMatchesImage(
'expected/black-desktop-22.png', function(matches) {
assert.ok(matches);
done();
});
});
QUnit.test('can flip a cursor horizontally', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems
.awesomeCursor('pencil', {
color: 'red',
size: 18,
flip: 'horizontal'
})
.cursorMatchesImage(
'expected/red-pencil-flip-h-18.png', function(matches) {
assert.ok(matches);
done();
});
});
QUnit.test('can flip a cursor vertically', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems
.awesomeCursor('flag-checkered', {
color: '#00ff00',
size: 18,
flip: 'vertical'
})
.cursorMatchesImage(
'expected/lime-flag-flip-v-18.png', function(matches) {
assert.ok(matches);
done();
});
});
QUnit.test('can flip a cursor vertically and horizontally', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems
.awesomeCursor('globe', {
color: 'black',
size: 32,
flip: 'both'
})
.cursorMatchesImage(
'expected/black-globe-flip-b-32.png', function(matches) {
assert.ok(matches);
done();
});
});
QUnit.test('can rotate a cursor', function(assert) {
var done = assert.async();
assert.expect(2);
var next = function() {
this.elems.awesomeCursor('wrench', {
color: 'red',
size: '32px',
rotate: -45
}).cursorMatchesImage(
'expected/red-wrench-rotate-45.png', function(matches) {
assert.ok(matches);
done();
}
);
}.bind(this);
this.elems.awesomeCursor('pencil', {
color: 'black',
size: '32px',
rotate: 45
}).cursorMatchesImage(
'expected/black-pencil-rotate45.png', function(matches) {
assert.ok(matches);
next();
}
);
});
QUnit.test('can rotate and flip a cursor', function(assert) {
var done = assert.async();
assert.expect(2);
var next = function() {
this.elems.awesomeCursor('pencil', {
color: 'green',
size: '32px',
rotate: 45,
flip: 'both'
}).cursorMatchesImage(
'expected/green-pencil-rotate45-flip-b.png', function(matches) {
assert.ok(matches);
done();
}
);
}.bind(this);
this.elems.awesomeCursor('pencil', {
color: 'green',
size: '32px',
rotate: 45,
flip: 'horizontal'
}).cursorMatchesImage(
'expected/green-pencil-rotate45-flip-h.png', function(matches) {
assert.ok(matches);
next();
}
);
});
QUnit.test('hotspot gets translated when cursor rotated', function(assert) {
var size = $.fn.awesomeCursor.defaults.size,
newSize = Math.ceil(Math.sqrt(
Math.pow(size, 2) + Math.pow(size, 2)
)),
subjects = {
45: [(newSize - size) / 2, (size / 2) + (newSize - size) / 2]
},
hotspot;
assert.expect(Object.keys(subjects).length);
for (var s in subjects) {
this.elems.awesomeCursor('pencil', {
hotspot: 'center left',
rotate: s
});
hotspot = extractHotspot(this.elems);
assert.deepEqual(hotspot, subjects[s]);
}
});
QUnit.test('can add outline to cursor', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('paint-brush', {
color: 'white',
size: 32,
outline: 'black'
}).cursorMatchesImage(
'expected/black-outline-paint-brush-32.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('can add outlines to flipped cursors', function(assert) {
var done = assert.async();
assert.expect(3);
var runTests = function(tests) {
var current = tests.pop();
if (!current) {
done();
} else {
this.elems.awesomeCursor('paint-brush', {
color: 'white',
size: 32,
outline: 'black',
flip: current
}).cursorMatchesImage(
'expected/black-outline-paint-brush-flip-' + current + '-32.png',
function(matches) {
assert.ok(matches);
runTests(tests);
}
);
}
}.bind(this);
runTests(['horizontal', 'vertical', 'both']);
});
QUnit.test('can add outline to rotated cursor', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('pencil', {
color: 'skyblue',
size: 32,
rotate: 45,
outline: 'blue'
}).cursorMatchesImage(
'expected/blue-outline-paint-brush-rotate45.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('can add outline to rotated and flipped cursor', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('pencil', {
color: 'skyblue',
size: 32,
rotate: 45,
outline: 'blue',
flip: 'horizontal'
}).cursorMatchesImage(
'expected/blue-outline-paint-brush-rotate45-flip-h.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('can use a custom font instead of FontAwesome', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('pencil', {
font: {
family: 'AwesomeCursorTest',
cssClass: 'act act-%s'
},
size: 36,
color: 'black'
}).cursorMatchesImage(
'expected/awesome-cursor-test-font/black-pencil-36.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('can apply effects to custom font cursors', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('brush', {
font: {
family: 'AwesomeCursorTest',
cssClass: 'act act-%s'
},
size: 30,
color: 'limegreen',
outline: 'forestgreen',
rotate: 35,
flip: 'horizontal'
}).cursorMatchesImage(
'expected/awesome-cursor-test-font/green-brush-30-effects.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('can set custom font `cssClass` using a function', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('pencil', {
font: {
family: 'AwesomeCursorTest',
cssClass: function(iconName) {
return 'act act-' + iconName;
}
},
size: 36,
color: 'black'
}).cursorMatchesImage(
'expected/awesome-cursor-test-font/black-pencil-36.png', function(matches) {
assert.ok(matches);
done();
}
);
});
QUnit.test('does not clip large icons', function(assert) {
var done = assert.async();
assert.expect(1);
this.elems.awesomeCursor('usb', {
size: 32,
color: 'pink'
}).cursorMatchesImage(
'expected/pink-usb-32.png', function(matches) {
assert.ok(matches);
done();
}
);
});
}(this, jQuery));