Full Code of jwarby/jquery-awesome-cursor for AI

master b3a4c2f88ff1 cached
21 files
60.2 KB
16.6k tokens
6 symbols
1 requests
Download .txt
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 [![GitHub version](https://badge.fury.io/gh/jwarby%2Fjquery-awesome-cursor.svg)](http://badge.fury.io/gh/jwarby%2Fjquery-awesome-cursor)

[![Build Status](https://secure.travis-ci.org/jwarby/jquery-awesome-cursor.png?branch=master)](https://travis-ci.org/jwarby/jquery-awesome-cursor)
[![Dependency Status](https://david-dm.org/jwarby/jquery-awesome-cursor/peer-status.svg?style=flat)](https://david-dm.org/jwarby/jquery-awesome-cursor#info=peerDependencies)
[![devDependency Status](https://david-dm.org/jwarby/jquery-awesome-cursor/dev-status.svg?style=flat)](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 <https://jwarby.github.io/jquery-awesome-cursor/> 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
<link rel="stylesheet" href="node_modules/font-awesome/css/font-awesome.min.css" type="text/css">
<script src="node_modules/jquery/dist/jquery.min.js" type="text/javascript"></script>
<script src="node_modules/jquery-awesome-cursor/dist/jquery.awesome-cursor.min.js" type="text/javascript"></script>
```

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
<link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.min.css" type="text/css">
<script src="bower_components/jquery/dist/jquery.min.js" type="text/javascript"></script>
<script src="bower_components/jquery-awesome-cursor/dist/jquery.awesome-cursor.min.js" type="text/javascript"></script>
```
##### 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
<link rel="stylesheet" href="/path/to/font-awesome.min.css" type="text/css">
<script src="/path/to/jquery.js"></script>
<script src="path/to/jquery.awesome-cursor.min.js"></script>
```

#### RawGit CDN

You could also use [RawGit](https://rawgit.com)'s CDN:

```html
<link rel="stylesheet" href="/path/to/font-awesome.min.css" type="text/css">
<script src="/path/to/jquery.js"></script>
<script src="https://cdn.rawgit.com/jwarby/jquery-awesome-cursor/master/dist/jquery.awesome-cursor.min.js"></script>
```
## 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('<icon name>');
```

See <http://fontawesome.io/icons/> 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

- <https://github.com/jwarby/jquery-awesome-cursor/issues>

## 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~~ [&#10004; 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~~ [&#10004; v0.0.4](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.0.4)
- ~~Optional outlines for cursors~~ [&#10004; 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~~ [&#10004; v0.1.0](https://github.com/jwarby/jquery-awesome-cursor/releases/tag/v0.1.0)
- IE11 support (if possible)
- ~~Data API (under consideration)~~ [&#10005; 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)~~ [&#10005; 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 <warwood.aj@gmail.com> (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 <warwood.aj@gmail.com> (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 = $('<canvas />')[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 = $('<i />', {
          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 = $('<div />', {
        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 = $('<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 <warwood.aj@gmail.com> (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 = $('<canvas />')[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 = $('<i />', {
          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 = $('<div />', {
        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 = $('<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
================================================
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>jQuery Awesome Cursor plugin Test Suite</title>
  <!-- FontAwesome -->
  <link rel="stylesheet" href="../bower_components/fontawesome/css/font-awesome.min.css" media="screen">
  <!-- Custom font testing -->
  <link rel="stylesheet" href="awesome-cursor-test-font/style.css" media="screen">
  <!-- Load local jQuery. -->
  <script src="../bower_components/jquery/dist/jquery.js"></script>
  <!-- Load local QUnit. -->
  <link rel="stylesheet" href="../bower_components/qunit/qunit/qunit.css" media="screen">

  <script src="../bower_components/qunit/qunit/qunit.js"></script>
  <!-- Load local lib and tests. -->
  <script src="../src/jquery.awesome-cursor.js"></script>
  <script src="jquery-awesome-cursor_test.js"></script>
  <!-- Removing access to jQuery and $. But it'll still be available as _$, if
       you REALLY want to mess around with jQuery in the console. REMEMBER WE
       ARE TESTING A PLUGIN HERE, THIS HELPS ENSURE BEST PRACTICES. REALLY. -->
  <script>window._$ = jQuery.noConflict(true);</script>
</head>
<body>
  <div id="qunit"></div>
  <div id="qunit-fixture">
    <span>lame test markup</span>
    <span>normal test markup</span>
    <span>awesome test markup</span>
  </div>
</body>
</html>


================================================
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('<i class="fa fa-pencil"></i>')
      .append('<i class="act act-pencil"></i>')
    ;

    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($('<img />', {
      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 = $('<img />', {
      src: imgSrc
    });

    $expectedImg.load(function() {
      var expectedCanvas = getCanvasFromImage($expectedImg[0]),
        actualCanvas = getCanvasFromDataURI(actualImgData[1].replace(/^["']|["']$/g, ''));

      if (!canvasCompare(expectedCanvas, actualCanvas)) {
        $('body').append(actualCanvas, expectedCanvas, '<h5>' + imgSrc + '</h5>');

        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 = $('<div />', {
        id: 'qunit'
      });
      var $fixture = $('<div />', {
        id: 'qunit-fixture'
      });

      $fixture
        .append($('<i />', {
          class: 'fa fa-pencil'
        }))
        .append($('<i />', {
          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));
Download .txt
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
Download .txt
SYMBOL INDEX (6 symbols across 3 files)

FILE: dist/jquery.awesome-cursor.js
  function flipCanvas (line 90) | function flipCanvas(canvas, direction) {

FILE: src/jquery.awesome-cursor.js
  function flipCanvas (line 87) | function flipCanvas(canvas, direction) {

FILE: test/jquery-awesome-cursor_test.js
  function extractHotspot (line 30) | function extractHotspot(el) {
  function getCanvasFromImage (line 50) | function getCanvasFromImage(img) {
  function getCanvasFromDataURI (line 72) | function getCanvasFromDataURI(uri) {
  function canvasCompare (line 88) | function canvasCompare(canvas1, canvas2) {
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (66K chars).
[
  {
    "path": ".bowerrc",
    "chars": 40,
    "preview": "{\n    \"directory\": \"bower_components\"\n}\n"
  },
  {
    "path": ".eslintrc",
    "chars": 210,
    "preview": "{\n  \"extends\": \"eslint:recommended\",\n  \"env\": {\n    \"browser\": true\n  },\n  \"globals\": {\n    \"jQuery\": true,\n    \"$\": tru"
  },
  {
    "path": ".gitignore",
    "chars": 34,
    "preview": "/node_modules/\n/bower_components/\n"
  },
  {
    "path": ".jshintrc",
    "chars": 372,
    "preview": "{\n  \"node\": true,\n  \"browser\": true,\n  \"esnext\": true,\n  \"bitwise\": true,\n  \"camelcase\": true,\n  \"curly\": true,\n  \"eqeqe"
  },
  {
    "path": ".npmignore",
    "chars": 87,
    "preview": "test\nsrc\nbower_components\n_unified_manifest.json\nbower.json\nkarma.conf.js\nGruntfile.js\n"
  },
  {
    "path": ".travis.yml",
    "chars": 188,
    "preview": "language: node_js\ninstall:\n  - npm install -g grunt-cli\n  - npm install\n  - bower install\nbefore_script:\n  - export DISP"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2684,
    "preview": "# Contributing\n\n## Getting started\n\n- Install all of the necessary dependencies for development, by running `npm install"
  },
  {
    "path": "Gruntfile.js",
    "chars": 3271,
    "preview": "'use strict';\n\nmodule.exports = function (grunt) {\n  // Load all grunt tasks\n  require('load-grunt-tasks')(grunt);\n  // "
  },
  {
    "path": "LICENSE.txt",
    "chars": 1057,
    "preview": "Copyright (c) 2014 James Warwood\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this s"
  },
  {
    "path": "README.md",
    "chars": 11865,
    "preview": "# jQuery Awesome Cursor plugin [![GitHub version](https://badge.fury.io/gh/jwarby%2Fjquery-awesome-cursor.svg)](http://b"
  },
  {
    "path": "_unified_manifest.json",
    "chars": 2121,
    "preview": "{\n  \"name\": \"jquery-awesome-cursor\",\n  \"version\": \"0.3.1\",\n  \"description\": \"jQuery plugin for using FontAwesome icons a"
  },
  {
    "path": "bower.json",
    "chars": 947,
    "preview": "{\n  \"name\": \"jquery-awesome-cursor\",\n  \"version\": \"0.3.1\",\n  \"description\": \"jQuery plugin for using FontAwesome icons a"
  },
  {
    "path": "dist/jquery.awesome-cursor.js",
    "chars": 8261,
    "preview": "/*! jquery-awesome-cursor - v0.3.1 - 2016-04-23\n* https://jwarby.github.io/jquery-awesome-cursor\n* Copyright (c) 2016 ; "
  },
  {
    "path": "karma.conf.js",
    "chars": 2140,
    "preview": "// Karma configuration\n// Generated on Sat Jun 06 2015 15:48:50 GMT+0800 (MYT)\n\nmodule.exports = function(config) {\n  co"
  },
  {
    "path": "package.json",
    "chars": 1612,
    "preview": "{\n  \"name\": \"jquery-awesome-cursor\",\n  \"version\": \"0.3.1\",\n  \"description\": \"jQuery plugin for using FontAwesome icons a"
  },
  {
    "path": "src/.jshintrc",
    "chars": 257,
    "preview": "{\n  \"curly\": true,\n  \"eqeqeq\": true,\n  \"immed\": true,\n  \"latedef\": true,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"sub\": tru"
  },
  {
    "path": "src/jquery.awesome-cursor.js",
    "chars": 8125,
    "preview": ";(function(global, factory) {\n  if (typeof define === 'function' && define.amd) {\n    define(['jquery'], factory);\n  } e"
  },
  {
    "path": "test/.jshintrc",
    "chars": 485,
    "preview": "{\n  \"curly\": true,\n  \"eqeqeq\": true,\n  \"immed\": true,\n  \"latedef\": true,\n  \"newcap\": true,\n  \"noarg\": true,\n  \"sub\": tru"
  },
  {
    "path": "test/awesome-cursor-test-font/style.css",
    "chars": 750,
    "preview": "@font-face {\n\tfont-family: 'AwesomeCursorTest';\n\tsrc:url('fonts/AwesomeCursorTest.eot');\n\tsrc:url('fonts/AwesomeCursorTe"
  },
  {
    "path": "test/jquery-awesome-cursor.html",
    "chars": 1282,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>jQuery Awesome Cursor plugin Test Suite</title>\n  <!-- F"
  },
  {
    "path": "test/jquery-awesome-cursor_test.js",
    "chars": 15877,
    "preview": "/* global QUnit */\n\n(function(global, $) {\n  'use strict';\n\n  QUnit.test('force fonts to load', function(assert) {\n    a"
  }
]

About this extraction

This page contains the full source code of the jwarby/jquery-awesome-cursor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (60.2 KB), approximately 16.6k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!