<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<!-- Angular Material CSS now available via Google CDN; version 0.10 used here -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.10.1/angular-material.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="mdColorPicker.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body ng-app="plunker" ng-controller="MainCtrl">
<md-content md-theme="docs-dark" layout-padding layout="row">
<div flex>
<h3>Angular md-color-picker</h3>
<h5>*** BETA ***</h5>
<h4><a href="https://github.com/brianpkelley/md-color-picker" target="_blank">View on GitHub</a></h4>
<p style="margin-bottom: 5px;">A pop up color chooser featuring:</p>
<ul>
<li>HSL Specturm</li>
<li>RGBA Sliders</li>
<li>Palette Selection</li>
<li>$cookie based history</li>
<li>Multiple output formats</li>
</ul>
<h5>Spectrum Selector</h5>
<p>Canvas based spectrum selection tool, no images or tricky css.</p>
<h5>RGBA Sliders</h5>
<p>Angular Material sliders and manual input.</p>
<h5>$cookie Based History</h5>
<p>If <code>$cookies</code> is not included in your app, the history function will fall back to an array based history that is reset on each reload.</p>
<h5>Multiple Output Formats</h5>
<p>Using <a href="https://github.com/bgrins/TinyColor">tinycolor.js</a> we can output hexadecimal, rgb(a), or hsl(a) formats.</p>
<h5>Misc.</h5>
<ul>
<li>Random Color on open (optional)</li>
<li>Input focus opens color chooser (optional)</li>
<li>Default color on open (optional)</li>
<li>Specify output type</li>
<li>Accepts label and icon for use in the generated <code>md-input-container</code></li>
</ul>
</div>
<div layout="column" layout-margin flex="40">
<h2>Demo</h2>
<h4>*** Text Color ***</h4>
<p>The text color field is not using the <code>openOnInput</code> attribute and you must click on the preview circle to open the color chooser. This allows you to manually type a color value without opening the color chooser.</p>
<p>This field has a label and icon specified.</p>
<h4>*** Text Background ***</h4>
<p>The text background field is using the <code>openOnInput</code> and the <code>random</code> attributes. This will maket he field open to a random color if one is not already selected when the input field receives focus.</p>
<p>This field only has the label specified.</p>
<h4>Text Style</h4>
<md-input-container>
<label>Font</label>
<md-select ng-model="font" aria-label="Font Family">
<md-option ng-repeat="font in fonts" ng-style="{'font-family':font}">{{font}}</md-option>
</md-select>
</md-input-container>
<div layout="row">
<div flex label="Text Color" md-color-picker value="textColor" icon="format_color_text"></div>
</div>
<div layout="row">
<md-icon style="margin-right: 8px;">font_download</md-icon>
<div flex label="Text Background" md-color-picker random="true" value="textBackground" open-on-input="true"></div>
</div>
<div>
<h5>Text Preview</h5>
<div class="md-color-picker-checkered-bg" layout="row" layout-align="center center">
<div class="text-preview" ng-style="{'background-color': textBackground, 'color': textColor, 'font-family': font}" style="width: 100%; height: 100%;" layout="row" layout-align="center center" layout-padding>
The five boxing wizards jump quickly.
</div>
</div>
</div>
</div>
<span flex="10"></span>
</md-content>
<script src="tinycolor.js"></script>
<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular-cookies.min.js"></script>
<!-- Angular Material Javascript now available via Google CDN; version 0.10 used here -->
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.10.1/angular-material.min.js"></script>
<!-- Custom Scripts -->
<script src="mdColorPicker.js"></script>
<script src="app.js"></script>
</body>
</html>
/* Put your css in here */
* {
box-sizing: border-box;
}
.text-preview {
padding: 20px;
}
h1, h2, h3, h4, h5, h6 {
margin: 7px 0;
color: rgb(75,125,175);
}
p, ul {
margin-top: 0;
margin-bottom: 25px;
font-size: 14px;
}
# md-color-picker
Angular-Material based color picker with no jQuery or other DOM/utility library dependencies.
### Demo
View Code Here: [GitHub Repo](https://github.com/brianpkelley/md-color-picker)
### Requiremnts
The only requirement is [tinycolor.js](https://github.com/bgrins/TinyColor) which is an exceptional color manipulation library.
### Disclaimer
This is still in a very early beta, and is rappidly changing (3 versions before initial commit). I am open to any and all help anyone is willing to put in. Will update as we go.
// md-color-picker 0.1.0
// https://github.com/brianpkelley/md-color-picker
// Brian Kelley
// GNU GENERAL PUBLIC LICENSE
(function() {
'use strict';
var tinycolor = window.tinycolor;
var mdColorPickerTemplate, mdColorPickerDialogTemplate;
angular.module('mdColorPicker', [])
.factory('mdColorPickerHistory', ['$injector', function( $injector ) {
var history = [];
var strHistory = [];
var $cookies = false;
try {
$cookies = $injector.get('$cookies');
} catch(e) {
}
if ( $cookies ) {
var tmpHistory = $cookies.getObject( 'mdColorPickerHistory' ) || [];
for ( var i = 0; i < tmpHistory.length; i++ ) {
history.push( tinycolor( tmpHistory[i] ) );
strHistory.push( tmpHistory[i] );
}
}
var length = 40;
return {
length: function() {
if ( arguments[0] ) {
length = arguments[0];
} else {
return history.length;
}
},
add: function( color ) {
for( var x = 0; x < history.length; x++ ) {
if ( history[x].toRgbString() === color.toRgbString() ) {
history.splice(x, 1);
strHistory.splice(x, 1);
}
}
history.unshift( color );
strHistory.unshift( color.toRgbString() );
if ( history.length > length ) {
history.pop();
strHistory.pop();
}
if ( $cookies ) {
$cookies.putObject('mdColorPickerHistory', strHistory );
}
},
get: function() {
return history;
},
reset: function() {
history = [];
strHistory = [];
if ( $cookies ) {
$cookies.putObject('mdColorPickerHistory', strHistory );
}
}
};
}])
.directive('mdColorPicker', [ '$timeout', 'mdColorPickerHistory', function( $timeout, colorHistory ) {
return {
//templateUrl: "/templates/mdColorPicker/mdColorPicker.tpl.html",
template: mdColorPickerTemplate,
scope: {
value: '=?',
type: '@',
label: '@',
icon: '@',
default: '@',
random: '@',
openOnInput: '@'
},
controller: ['$scope', '$element', '$mdDialog', function( $scope, $element, $mdDialog ) {
var didJustClose = false;
$scope.clearValue = function clearValue() {
$scope.value = '';
};
$scope.showColorPicker = function showColorPicker($event) {
if ( didJustClose ) {
return;
}
$event.preventDefault();
$event.stopImmediatePropagation();
$mdDialog.show({
template: ''+
'<md-dialog class="md-color-picker-dialog">'+
' <div md-color-picker-dialog value="value" default="{{default}}" random="{{random}}" ok="ok"></div>'+
' <md-actions layout="row">'+
' <md-button class="md-mini" flex ng-click="close()">Cancel</md-button>'+
' <md-button class="md-mini" flex ng-click="ok()">Select</md-button>'+
' </md-actions>'+
'</md-dialog>',
hasBackdrop: false,
clickOutsideToClose: false,
controller: ['$scope', 'value', 'defaultValue', 'random', function( $scope, value, defaultValue, random ) {
$scope.close = function close() {
$mdDialog.cancel();
};
$scope.ok = function ok() {
$mdDialog.hide( $scope.value );
};
$scope.value = value;
$scope.default = defaultValue;
$scope.random = random;
$scope.hide = $scope.ok;
}],
locals: {
value: $scope.value,
defaultValue: $scope.default,
random: $scope.random
},
targetEvent: $event,
focusOnOpen: false,
onRemoving: function() {
didJustClose = true;
$timeout(function() {
didJustClose = false;
},500);
console.log( arguments );
}
}).then(function(value) {
$scope.value = value;
colorHistory.add( new tinycolor( value ) );
}, function() { });
};
}],
compile: function( element, attrs ) {
//attrs.value = attrs.value || "#ff0000";
attrs.type = attrs.type !== undefined ? attrs.type : 0;
}
};
}])
.directive( 'mdColorPickerDialog', ['$timeout','mdColorPickerHistory', function( $timeout, colorHistory ) {
return {
//templateUrl: '/templates/mdColorPicker/mdColorPickerDialog.tpl.html',
template: mdColorPickerDialogTemplate,
scope: {
value: '=?',
default: '@',
random: '@',
ok: '=?'
},
controller: function( $scope, $element, $attrs ) {
///////////////////////////////////
// Variables
///////////////////////////////////
var container = angular.element( $element[0].querySelector('.md-color-picker-container') );
var resultSpan = angular.element( container[0].querySelector('.md-color-picker-result') );
var input = angular.element( $element[0].querySelector('.md-color-picker-input') );
var previewInput = angular.element( $element[0].querySelector('.md-color-picker-preview-input') );
var outputFn = [
'toHexString',
'toRgbString',
'toHslString'
];
$scope.default = angular.isDefined($scope.default) && $scope.default ? $scope.default : angular.isDefined($scope.random) && $scope.random ? tinycolor.random() : 'rgb(127, 64, 64)';
$scope.color = new tinycolor($scope.value || $scope.default); // Set initial color
$scope.alpha = $scope.color.getAlpha();
$scope.history = colorHistory;
$scope.whichPane = 0;
$scope.inputFocus = false;
// Colors for the palette screen
///////////////////////////////////
var steps = 9;
var freq = 2*Math.PI/steps;
var basePalette = [
tinycolor('rgb(255, 0, 0)'), // Red
tinycolor('rgb(255, 128, 0)'), // Orange
tinycolor('rgb(255, 255, 0)'), // Yellow
tinycolor('rgb(0, 255, 0)'), // Green
tinycolor('rgb(0, 255, 128)'), //
tinycolor('rgb(0, 255, 255)'), // Teal
tinycolor('rgb(0, 128, 255)'), //
tinycolor('rgb(0, 0, 255)'), // Blue
tinycolor('rgb(128, 0, 255)'), // Purple
tinycolor('rgb(255, 0, 255)') // Fusia
];
var grays = [
'rgb(255, 255, 255)', // White
'rgb(205, 205, 205)', // |
'rgb(178, 178, 178)', // |
'rgb(153, 153, 153)', // |
'rgb(127, 127, 127)', // |
'rgb(102, 102, 102)', // |
'rgb(76, 76, 76)', // |
'rgb(51, 51, 51)', // \ | /
'rgb(25, 25, 25)', // \|/
'rgb(0, 0, 0)' // Black
];
$scope.palette = [
];
var colors = [];
var x, y;
for ( x = -4; x <= 4; x++ ) {
colors = [];
for ( y = 0; y < basePalette.length; y++ ) {
var newColor = new tinycolor( basePalette[y].toRgb() );
if ( x < 0 ) {
colors.push( newColor.lighten( Math.abs( x * 10 ) ).toRgbString() );
}
if ( x === 0 ) {
colors.push( basePalette[y].toRgbString() );
}
if ( x > 0 ) {
colors.push( newColor.darken( (x * ( x / 5 )) * 10 ).toRgbString() );
}
}
$scope.palette.push( colors );
}
$scope.palette.push( grays );
///////////////////////////////////
// Functions
///////////////////////////////////
$scope.previewFocus = function() {
$scope.inputFocus = true;
$timeout( function() {
previewInput[0].setSelectionRange(0, previewInput[0].value.length);
});
};
$scope.previewBlur = function() {
$scope.inputFocus = false;
$scope.setValue();
};
$scope.previewKeyDown = function( $event ) {
console.log( $event, $scope.ok );
if ( $event.keyCode == 13 ) {
$scope.ok && $scope.ok();
}
};
$scope.setPaletteColor = function( event ) {
$scope.color = tinycolor( event.currentTarget.style.background );
};
$scope.setValue = function setValue() {
// Set the value if available
if ( $scope.color && $scope.color && outputFn[$scope.type] && $scope.color.toRgbString() !== 'rgba(0, 0, 0, 0)' ) {
$scope.value = $scope.color[outputFn[$scope.type]]();
}
};
$scope.changeValue = function changeValue() {
$scope.color = tinycolor( $scope.value );
$scope.$broadcast('mdColorPicker:colorSet', { color: $scope.color });
};
///////////////////////////////////
// Watches and Events
///////////////////////////////////
$scope.$watch( 'alpha', function( newValue ) {
$scope.color.setAlpha( newValue );
});
$scope.$watch( 'whichPane', function( newValue ) {
// 0 - spectrum selector
// 1 - sliders
// 2 - palette
$scope.$broadcast('mdColorPicker:colorSet', {color: $scope.color });
});
$scope.$watch( 'type', function() {
resultSpan.removeClass('switch');
$timeout(function() {
resultSpan.addClass('switch');
});
});
$scope.$watchGroup(['color.toRgbString()', 'type'], function( newValue ) {
if ( !$scope.inputFocus ) {
$scope.setValue();
}
});
///////////////////////////////////
// INIT
// Let all the other directives initialize
///////////////////////////////////
$timeout( function() {
$scope.$broadcast('mdColorPicker:colorSet', { color: $scope.color });
//previewInput.focus();
//$scope.previewFocus();
});
}
}
}])
.directive( 'mdColorPickerHue', [function() {
return {
template: '<canvas width="100%" height="100%"></canvas><div class="md-color-picker-marker"></div>',
controller: ['$scope',function($scope) {
}],
link: function( $scope, $element, $attrs ) {
console.log("hue");
////////////////////////////
// Variables
////////////////////////////
var height;
var canvas = $element.children()[0];
var marker = $element.children()[1];
var context = canvas.getContext('2d');
////////////////////////////
// Functions
////////////////////////////
var getColorByMouse = function getColorByMouse( e ) {
var x = e.pageX - offset.x;
var y = e.pageY - offset.y;
return getColorByPoint( x, y );
};
var getColorByPoint = function getColorByPoint( x, y ) {
x = Math.max( 0, Math.min( x, canvas.width-1 ) );
y = Math.max( 0, Math.min( y, canvas.height-1 ) );
var imageData = context.getImageData( x, y, 1, 1 ).data;
setMarkerCenter( y );
var hsl = new tinycolor( {r: imageData[0], g: imageData[1], b: imageData[2] } );
return hsl.toHsl().h;
};
var setMarkerCenter = function setMarkerCenter( y ) {
angular.element(marker).css({'left': '0'});
angular.element(marker).css({'top': y - ( marker.offsetHeight /2 ) + 'px'});
};
var draw = function draw() {
height = 255; //$scope.height || $element[0].getBoundingClientRect().height || $element[0].offsetHeight;
$element.css({'height': height + 'px'});
canvas.height = height;
canvas.width = 50;
// Create gradient
var hueGrd = context.createLinearGradient(90, 0.000, 90, height);
// Add colors
hueGrd.addColorStop(0.01, 'rgba(255, 0, 0, 1.000)');
hueGrd.addColorStop(0.167, 'rgba(255, 0, 255, 1.000)');
hueGrd.addColorStop(0.333, 'rgba(0, 0, 255, 1.000)');
hueGrd.addColorStop(0.500, 'rgba(0, 255, 255, 1.000)');
hueGrd.addColorStop(0.666, 'rgba(0, 255, 0, 1.000)');
hueGrd.addColorStop(0.828, 'rgba(255, 255, 0, 1.000)');
hueGrd.addColorStop(0.999, 'rgba(255, 0, 0, 1.000)');
// Fill with gradient
context.fillStyle = hueGrd;
context.fillRect( 0, 0, canvas.width, height );
};
////////////////////////////
// Watchers, Observes, Events
////////////////////////////
//$scope.$watch( function() { return color.getRgb(); }, hslObserver, true );
var offset = {
x: null,
y: null
};
var $window = angular.element( window );
$element.on( 'mousedown', function( e ) {
// Prevent highlighting
e.preventDefault();
e.stopImmediatePropagation();
$element.css({ 'cursor': 'none' });
offset.x = canvas.getBoundingClientRect().left+1;
offset.y = canvas.getBoundingClientRect().top;
var fn = function( e ) {
var hue = getColorByMouse( e );
$scope.$broadcast( 'mdColorPicker:spectrumHueChange', {hue: hue});
};
$window.on( 'mousemove', fn );
$window.one( 'mouseup', function( e ) {
$window.off( 'mousemove', fn );
$element.css({ 'cursor': 'crosshair' });
});
// Set the color
fn( e );
});
$scope.$on('mdColorPicker:colorSet', function( e, args ) {
var hsv = $scope.color.toHsv();
setMarkerCenter( canvas.height - ( canvas.height * ( hsv.h / 360 ) ) );
});
////////////////////////////
// init
////////////////////////////
draw();
}
};
}])
.directive( 'mdColorPickerAlpha', [function() {
return {
template: '<canvas width="100%" height="100%"></canvas><div class="md-color-picker-marker"></div>',
controller: ['$scope',function($scope) {
}],
link: function( $scope, $element, $attrs ) {
////////////////////////////
// Variables
////////////////////////////
var height;
var canvas = $element.children()[0];
var marker = $element.children()[1];
var context = canvas.getContext('2d');
var currentColor = $scope.color.toRgb();
////////////////////////////
// Functions
////////////////////////////
var getColorByMouse = function getColorByMouse( e ) {
var x = e.pageX - offset.x;
var y = e.pageY - offset.y;
return getColorByPoint( x, y );
};
var getColorByPoint = function getColorByPoint( x, y ) {
x = Math.max( 0, Math.min( x, canvas.width-1 ) );
y = Math.max( 0, Math.min( y, canvas.height-1 ) );
var imageData = context.getImageData( x, y, 1, 1 ).data;
setMarkerCenter( y );
return imageData[3] / 255;
};
var setMarkerCenter = function setMarkerCenter( y ) {
angular.element(marker).css({'left': '0'});
angular.element(marker).css({'top': y - ( marker.offsetHeight /2 ) + 'px'});
};
var alphaObserver = function( alpha ) {
var pos = height - ( alpha * height );
setMarkerCenter( pos );
};
var setAlpha = function setAlpha( alpha ) {
$scope.color.setAlpha( alpha );
$scope.alpha = alpha;
$scope.$apply();
};
// Draw
var draw = function draw() {
height = 255; // $scope.height || $element[0].getBoundingClientRect().height || $element[0].offsetHeight;
$element.css({'height': height + 'px'});
canvas.height = height;
canvas.width = height;
// Create gradient
var hueGrd = context.createLinearGradient(90, 0.000, 90, height);
// Add colors
hueGrd.addColorStop(0.01, 'rgba(' + currentColor.r + ',' + currentColor.g + ',' + currentColor.b + ', 1.000)');
hueGrd.addColorStop(0.999, 'rgba(' + currentColor.r + ',' + currentColor.g + ',' + currentColor.b + ', 0.000)');
// Fill with gradient
context.fillStyle = hueGrd;
context.fillRect( 0, 0, canvas.width, height );
};
////////////////////////////
// Watches, Observers, Events
////////////////////////////
var offset = { x: null, y: null };
var $window = angular.element( window );
$element.on( 'mousedown', function( e ) {
// Prevent highlighting
e.preventDefault();
e.stopImmediatePropagation();
$element.css({ 'cursor': 'none' });
offset.x = canvas.getBoundingClientRect().left+1;
offset.y = canvas.getBoundingClientRect().top;
var fn = function( e ) {
var alpha = getColorByMouse( e );
setAlpha( alpha );
};
$window.on( 'mousemove', fn );
$window.one( 'mouseup', function( e ) {
$window.off( 'mousemove', fn );
$element.css({ 'cursor': 'crosshair' });
});
// Set the color
fn( e );
});
$scope.$on('mdColorPicker:spectrumColorChange', function( e, args ) {
currentColor = args.color;
draw();
});
$scope.$on('mdColorPicker:colorSet', function( e, args ) {
currentColor = args.color.toRgb();
draw();
var alpha = args.color.getAlpha();
var pos = canvas.height - ( canvas.height * alpha );
setMarkerCenter( pos );
});
////////////////////////////
// init
////////////////////////////
draw();
}
};
}])
.directive( 'mdColorPickerSpectrum', [function() {
return {
template: '<canvas width="100%" height="100%"></canvas><div class="md-color-picker-marker"></div>{{hue}}',
controller: ['$scope',function($scope) {
}],
link: function( $scope, $element, $attrs ) {
////////////////////////////
// Variables
////////////////////////////
var height = 255; // Math.ceil( Math.min( $element[0].getBoundingClientRect().width || $element[0].offsetWidth , 255 ) );
$element.css({'height': height + 'px'});
var canvas = $element.children()[0];
canvas.height = height;
canvas.width = height;
var marker = $element.children()[1];
var context = canvas.getContext('2d');
var currentHue = $scope.color.toHsl().h;
////////////////////////////
// Functions
////////////////////////////
var getColorByMouse = function getColorByMouse( e ) {
var x = e.pageX - offset.x;
var y = e.pageY - offset.y;
return getColorByPoint( x, y );
};
var getColorByPoint = function getColorByPoint( x, y, forceApply ) {
if ( forceApply === undefined ) {
forceApply = true;
}
x = Math.max( 0, Math.min( x, canvas.width-1 ) );
y = Math.max( 0, Math.min( y, canvas.height-1 ) );
setMarkerCenter(x,y);
var imageData = context.getImageData( x, y, 1, 1 ).data;
return {
r: imageData[0],
g: imageData[1],
b: imageData[2]
};
};
var setMarkerCenter = function setMarkerCenter( x, y ) {
angular.element(marker).css({'left': x - ( marker.offsetWidth / 2 ) + 'px'});
angular.element(marker).css({'top': y - ( marker.offsetHeight /2 ) + 'px'});
};
var getMarkerCenter = function getMarkerCenter() {
var returnObj = {
x: marker.offsetLeft + ( Math.floor( marker.offsetWidth / 2 ) ),
y: marker.offsetTop + ( Math.floor( marker.offsetHeight / 2 ) )
};
return returnObj;
};
var draw = function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
// White gradient
var whiteGrd = context.createLinearGradient(0, 0, canvas.width, 0);
whiteGrd.addColorStop(0, 'rgba(255, 255, 255, 1.000)');
whiteGrd.addColorStop(1, 'rgba(255, 255, 255, 0.000)');
// Black Gradient
var blackGrd = context.createLinearGradient(0, 0, 0, canvas.height);
blackGrd.addColorStop(0, 'rgba(0, 0, 0, 0.000)');
blackGrd.addColorStop(1, 'rgba(0, 0, 0, 1.000)');
// Fill with solid
context.fillStyle = 'hsl( ' + currentHue + ', 100%, 50%)';
context.fillRect( 0, 0, canvas.width, canvas.height );
// Fill with white
context.fillStyle = whiteGrd;
context.fillRect( 0, 0, canvas.width, canvas.height );
// Fill with black
context.fillStyle = blackGrd;
context.fillRect( 0, 0, canvas.width, canvas.height );
};
var setColor = function setColor( color ) {
$scope.color._r = color.r;
$scope.color._g = color.g;
$scope.color._b = color.b;
$scope.$apply();
$scope.$broadcast('mdColorPicker:spectrumColorChange', { color: color });
};
////////////////////////////
// Watchers, Observers, Events
////////////////////////////
var offset = {
x: null,
y: null
};
var $window = angular.element( window );
$element.on( 'mousedown', function( e ) {
// Prevent highlighting
e.preventDefault();
e.stopImmediatePropagation();
$element.css({ 'cursor': 'none' });
offset.x = canvas.getBoundingClientRect().left+1;
offset.y = canvas.getBoundingClientRect().top;
var fn = function( e ) {
var color = getColorByMouse( e );
setColor( color );
};
$window.on( 'mousemove', fn );
$window.one( 'mouseup', function( e ) {
$window.off( 'mousemove', fn );
$element.css({ 'cursor': 'crosshair' });
});
// Set the color
fn( e );
});
$scope.$on('mdColorPicker:spectrumHueChange', function( e, args ) {
currentHue = args.hue;
draw();
var markerPos = getMarkerCenter();
var color = getColorByPoint( markerPos.x, markerPos.y );
setColor( color );
});
$scope.$on('mdColorPicker:colorSet', function( e, args ) {
var hsv = args.color.toHsv();
currentHue = hsv.h;
draw();
var posX = canvas.width * hsv.s;
var posY = canvas.height - ( canvas.height * hsv.v );
setMarkerCenter( posX, posY );
});
////////////////////////////
// init
////////////////////////////
draw();
}
};
}]);
mdColorPickerTemplate = '' +
'<div class="md-color-picker-input-container" layout="row">' +
' <div class="md-color-picker-preview md-color-picker-checkered-bg" ng-click="showColorPicker($event)">' +
' <div class="md-color-picker-result" ng-style="{background: value}"></div>' +
' </div>' +
' <md-input-container flex>' +
' <label><md-icon ng-if="icon">{{icon}}</md-icon>{{label}}</label>' +
' <input type="input" ng-model="value" class="md-color-picker-input" ng-focus="openOnInput && showColorPicker($event)"/>' +
' </md-input-container>' +
' <md-button class="md-icon-button md-color-picker-clear" ng-if="value" ng-click="clearValue();" aria-label="Clear Color">' +
' <md-icon>clear</md-icon>' +
' </md-button>' +
'</div>';
mdColorPickerDialogTemplate = '' +
'<div class="md-color-picker-container in" layout="column">' +
' <div class="md-color-picker-arrow" ng-style="{\'border-bottom-color\': color.toRgbString() }"></div>' +
'' +
' <div class="md-color-picker-preview md-color-picker-checkered-bg" ng-class="{\'dark\': !color.isDark() || color.getAlpha() < .45}" flex="1" layout="column">' +
'' +
' <div class="md-color-picker-result" ng-style="{\'background\': color.toRgbString()}" flex="100" layout="column" layout-fill layout-align="center center" ng-click="focusPreviewInput( $event )">' +
' <!--<span flex layout="column" layout-align="center center">{{value}}</span>-->' +
' <div flex layout="row" layout-align="center center">' +
' <input class="md-color-picker-preview-input" type="text" ng-model="value" ng-focus="previewFocus($event);" ng-blur="previewBlur()" ng-change="changeValue()" ng-keypress="previewKeyDown($event)" layout-fill />' +
' </div>' +
' <div class="md-color-picker-tabs" style="width: 100%">' +
' <md-tabs md-selected="type" md-stretch-tabs="always" md-no-ink md-no-pagination="true">' +
' <md-tab label="Hex" ng-disabled="color.getAlpha() !== 1"></md-tab>' +
' <md-tab label="RGB"></md-tab>' +
' <md-tab label="HSL"></md-tab>' +
' </md-tabs>' +
' </div>' +
' </div>' +
' </div>' +
'' +
' <div class="md-color-picker-tabs md-color-picker-colors">' +
' <md-tabs md-stretch-tabs="always" md-align-tabs="bottom" md-selected="whichPane">' +
' <md-tab>' +
' <md-tab-label>' +
' <md-icon>gradient</md-icon>' +
' </md-tab-label>' +
' <md-tab-body>' +
' <div layout="row" layout-align="space-between" style="height: 255px">' +
' <div md-color-picker-spectrum></div>' +
' <div md-color-picker-hue></div>' +
' <div md-color-picker-alpha class="md-color-picker-checkered-bg"></div>' +
' </div>' +
' </md-tab-body>' +
' </md-tab>' +
' <md-tab>' +
' <md-tab-label>' +
' <md-icon>tune</md-icon>' +
' </md-tab-label>' +
' <md-tab-body>' +
' <div layout="column" flex="100" layout-fill layout-align="space-between start center" class="md-color-picker-sliders">' +
' <div layout="row" layout-align="start center" layout-wrap flex>' +
' <div flex="10" layout layout-align="center center">' +
' <span class="md-body-1">R</span>' +
' </div>' +
' <md-slider flex="65" min="0" max="255" ng-model="color._r" aria-label="red" class="red-slider"></md-slider>' +
' <span flex></span>' +
' <div flex="20" layout layout-align="center center">' +
' <input style="width: 100%;" type="number" ng-model="color._r" aria-label="red" aria-controls="red-slider">' +
' </div>' +
' </div>' +
' <div layout="row" layout-align="start center" layout-wrap flex>' +
' <div flex="10" layout layout-align="center center">' +
' <span class="md-body-1">G</span>' +
' </div>' +
' <md-slider flex="65" min="0" max="255" ng-model="color._g" aria-label="green" class="green-slider"></md-slider>' +
' <span flex></span>' +
' <div flex="20" layout layout-align="center center">' +
' <input style="width: 100%;" type="number" ng-model="color._g" aria-label="green" aria-controls="green-slider">' +
' </div>' +
' </div>' +
' <div layout="row" layout-align="start center" layout-wrap flex>' +
' <div flex="10" layout layout-align="center center">' +
' <span class="md-body-1">B</span>' +
' </div>' +
' <md-slider flex="65" min="0" max="255" ng-model="color._b" aria-label="blue" class="blue-slider"></md-slider>' +
' <span flex></span>' +
' <div flex="20" layout layout-align="center center" >' +
' <input style="width: 100%;" type="number" ng-model="color._b" aria-label="blue" aria-controls="blue-slider">' +
' </div>' +
' </div>' +
' <div layout="row" layout-align="start center" layout-wrap flex>' +
' <div flex="10" layout layout-align="center center">' +
' <span class="md-body-1">A</span>' +
' </div>' +
' <md-slider flex="65" min="0" max="1" step=".01" ng-model="alpha" aria-label="alpha" class="md-primary"></md-slider>' +
' <span flex></span>' +
' <div flex="20" layout layout-align="center center" >' +
' <input style="width: 100%;" type="number" ng-model="alpha" aria-label="alpha" aria-controls="blue-slider">' +
' </div>' +
' </div>' +
' </div>' +
' </md-tab-body>' +
' </md-tab>' +
' <md-tab>' +
' <md-tab-label>' +
' <md-icon>view_comfy</md-icon>' +
' </md-tab-label>' +
' <md-tab-body>' +
' <div layout="column" layout-align="space-between start center" flex>' +
' <div ng-repeat="row in palette track by $index" flex="15" layout-align="space-between" layout="row">' +
' <div ng-repeat="col in row track by $index" flex="10" style="height: 25.5px;" ng-style="{\'background\': col};" ng-click="setPaletteColor($event)"></div>' +
' </div>' +
' </div>' +
' </md-tab-body>' +
' </md-tab>' +
' <md-tab>' +
' <md-tab-label>' +
' <md-icon>history</md-icon>' +
' </md-tab-label>' +
' <md-tab-body layout="row" layout-fill>' +
' <div layout="column" flex layout-align="space-between start" layout-wrap layout-fill class="md-color-picker-history">' +
' <div layout="row" flex="80" layout-align="space-between start start" layout-wrap layout-fill>' +
' <div flex="10" ng-repeat="historyColor in history.get() track by $index">' +
' <div ng-style="{\'background\': historyColor.toRgbString()}" ng-click="setPaletteColor($event)"></div>' +
' </div>' +
' </div>' +
'' +
'' +
' <md-button flex-end ng-click="history.reset()" class="md-mini">' +
' <md-icon>delete</md-icon>' +
' Clear history' +
' </md-button>' +
' </div>' +
' </md-tab-body>' +
' </md-tab>' +
' </md-tabs>' +
' </div>' +
'</div>';
})();
/*
md-color-picker 0.1.0
https://github.com/brianpkelley/md-color-picker
Brian Kelley
GNU GENERAL PUBLIC LICENSE
*/
[md-color-picker] .md-color-picker-input-container {
position: relative;
}
[md-color-picker] .md-color-picker-input-container .md-color-picker-preview {
content: '';
width: 24px;
height: 24px;
border: 2px solid #fff;
border-radius: 50%;
box-shadow: 0 3px 1px -2px rgba(0,0,0,0.14), 0 2px 2px 0 rgba(0,0,0,0.098), 0 1px 5px 0 rgba(0,0,0,0.084);
margin: auto;
bottom: 26px;
overflow: hidden;
background-color: #fff;
background-image: linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd), linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd);
background-size: 8px 8px;
background-position: 0 0, 4px 4px;
}
[md-color-picker] .md-color-picker-input-container .md-color-picker-preview .md-color-picker-result {
width: 100%;
height: 100%;
}
[md-color-picker] .md-color-picker-input-container .md-color-picker-clear {
position: absolute;
width: 24px;
height: 24px;
bottom: 22px;
right: 0px;
color: rgba(0,0,0,0.26);
line-height: 38px;
}
[md-color-picker] .md-color-picker-input-container .md-color-picker-clear md-icon {
font-size: 18px;
margin: auto;
}
.md-color-picker-container {
padding: 8px;
background: #fff;
outline: none;
height: 397px;
width: 347px;
opacity: 1;
overflow: hidden;
z-index: 1000;
}
.md-color-picker-container .md-color-picker-arrow {
border: 0 solid transparent;
border-right-width: 30px;
border-bottom-width: 0px;
position: absolute;
left: 0;
top: 0px;
transition: border-bottom-width cubic-bezier(.25,.8,.25,1) .25s, top cubic-bezier(.25,.8,.25,1) .25s;
}
.md-color-picker-container .md-color-picker-preview {
flex: 1;
font-weight: bold;
font-size: 18px;
color: #fff;
margin: -8px -8px 0px;
position: relative;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-result {
position: absolute;
height: 100%;
opacity: 1;
background: #ff0000;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-result > span {
position: relative;
top: -15px;
opacity: 0;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-result > div:first-child {
position: relative;
top: -25px;
width: 100%;
opacity: 0;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-result > div:first-child > input {
border-width: 0;
background: transparent;
text-align: center;
position: absolute;
top: -25px;
bottom: -25px;
left: 0;
right: 0;
outline: none;
color: #eee;
font-weight: bold;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-result.switch > span,
.md-color-picker-container .md-color-picker-preview .md-color-picker-result.switch > div:first-child {
transition: top cubic-bezier(.25,.8,.25,1) .25s, .25s opacity ease-out;
top: 0px;
opacity: 1;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs .md-tab,
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs md-tabs-wrapper,
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs md-tabs-canvas,
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs md-pagination-wrapper {
max-height: 28px;
height: 28px;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs md-ink-bar {
background: rgba(255,255,255,0.5);
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs .md-tab {
padding-top: 2px;
background: rgba(255,255,255,0.25);
color: #eee;
max-width: none !important;
}
.md-color-picker-container .md-color-picker-preview .md-color-picker-tabs .md-tab.md-active {
background: transparent;
}
.md-color-picker-container .md-color-picker-preview.dark {
color: #333;
}
.md-color-picker-container .md-color-picker-preview.dark div:first-child > input {
color: #333;
}
.md-color-picker-container .md-color-picker-preview.dark .md-tab {
background: rgba(0,0,0,0.25);
color: #333;
}
.md-color-picker-container .md-color-picker-preview.dark .md-tab.md-active {
background: transparent;
}
.md-color-picker-container .md-color-picker-preview.dark md-ink-bar {
background: rgba(0,0,0,0.5);
}
.md-color-picker-container .md-color-picker-tabs {
margin: 0px -8px 0;
}
.md-color-picker-container .md-color-picker-tabs .md-tab,
.md-color-picker-container .md-color-picker-tabs md-tabs-wrapper,
.md-color-picker-container .md-color-picker-tabs md-tabs-canvas,
.md-color-picker-container .md-color-picker-tabs md-pagination-wrapper {
max-height: 36px;
height: 36px;
}
.md-color-picker-container .md-color-picker-tabs .md-tab {
padding: 7px 24px;
background: transparent;
}
.md-color-picker-container .md-color-picker-tabs .md-tab:last-of-type {
margin-right: -2px;
}
.md-color-picker-container .md-color-picker-tabs md-tabs:not(.md-no-tab-content):not(.md-dynamic-height) {
min-height: 298px;
}
.md-color-picker-container .md-color-picker-tabs md-tabs:not(.md-no-tab-content):not(.md-dynamic-height) md-tabs-content-wrapper {
height: 255px;
margin-bottom: 8px;
}
.md-color-picker-container .md-color-picker-tabs md-tabs:not(.md-no-tab-content):not(.md-dynamic-height) md-tabs-content-wrapper md-tab-content {
height: 255px;
padding: 0px 8px 0;
}
.md-color-picker-container .md-color-picker-tabs md-tabs:not(.md-no-tab-content):not(.md-dynamic-height) md-tabs-content-wrapper md-tab-content [md-template] {
height: 100%;
}
.md-color-picker-container .md-color-picker-tabs.md-color-picker-colors {
margin: 8px -8px -8px;
}
.md-color-picker-container .md-color-picker-tabs.md-color-picker-colors md-ink-bar {
top: auto;
}
.md-color-picker-container .md-color-picker-tabs.md-color-picker-colors .md-tab {
background: rgba(0,0,0,0.075);
}
.md-color-picker-container .md-color-picker-tabs.md-color-picker-colors .md-tab.md-active {
background: #fff;
}
.md-color-picker-container .md-color-picker-colors {
overflow: hidden;
margin-top: 8px;
}
.md-color-picker-container .md-color-picker-colors .md-color-picker-marker {
position: absolute;
border: 2px solid #fff;
box-shadow: 0 0 2px 0 rgba(0,0,0,0.5);
}
.md-color-picker-container .md-color-picker-colors [md-color-picker-spectrum] {
position: relative;
cursor: crosshair;
overflow: hidden;
height: 255px;
width: 255px;
}
.md-color-picker-container .md-color-picker-colors [md-color-picker-spectrum] .md-color-picker-marker {
width: 11px;
height: 11px;
border-radius: 50%;
box-shadow: 0 0 2px 0 rgba(0,0,0,0.5), inset 0 0 2px 0 rgba(0,0,0,0.5);
top: -5px;
left: calc(95%);
}
.md-color-picker-container .md-color-picker-colors [md-color-picker-hue],
.md-color-picker-container .md-color-picker-colors [md-color-picker-alpha] {
position: relative;
cursor: crosshair;
overflow: hidden;
width: 30px;
}
.md-color-picker-container .md-color-picker-colors [md-color-picker-hue] .md-color-picker-marker,
.md-color-picker-container .md-color-picker-colors [md-color-picker-alpha] .md-color-picker-marker {
height: 5px;
width: 100%;
border-left: 0;
border-right: 0;
top: -2px;
left: 0;
}
.md-color-picker-container .md-color-picker-sliders md-slider.red-slider .md-track-fill {
background: #d01515;
}
.md-color-picker-container .md-color-picker-sliders md-slider.red-slider .md-thumb:after {
background-color: #d01515;
border-color: #d01515;
}
.md-color-picker-container .md-color-picker-sliders md-slider.green-slider .md-track-fill {
background: #19d015;
}
.md-color-picker-container .md-color-picker-sliders md-slider.green-slider .md-thumb:after {
background-color: #19d015;
border-color: #19d015;
}
.md-color-picker-container .md-color-picker-sliders md-slider.blue-slider .md-track-fill {
background: #1560d0;
}
.md-color-picker-container .md-color-picker-sliders md-slider.blue-slider .md-thumb:after {
background-color: #1560d0;
border-color: #1560d0;
}
.md-color-picker-container .md-color-picker-history > div > div {
height: 20px;
margin: 4px;
background-color: #fff;
background-image: linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd), linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd);
background-size: 8px 8px;
background-position: 0 0, 4px 4px;
}
.md-color-picker-container .md-color-picker-history > div > div > div {
width: 100%;
height: 100%;
}
.md-color-picker-container .md-color-picker-history button {
margin-top: auto;
margin-left: auto;
}
.md-color-picker-checkered-bg {
background-color: #fff;
background-image: linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd), linear-gradient(45deg,#ddd 25%,transparent 25%,transparent 75%,#ddd 75%,#ddd);
background-size: 8px 8px;
background-position: 0 0, 4px 4px;
}
var app = angular.module('plunker', ['ngMaterial','ngCookies', 'mdColorPicker']);
app.controller('MainCtrl', function($scope) {
$scope.fonts = [
'Arial',
'Arial Black',
'Comic Sans MS',
'Courier New',
'Georgia',
'Impact',
'Times New Roman',
'Trebuchet MS',
'Verdana'
];
$scope.font;
$scope.textColor;
$scope.textBackground;
});
// TinyColor v1.2.1
// https://github.com/bgrins/TinyColor
// Brian Grinstead, MIT License
(function() {
var trimLeft = /^[\s,#]+/,
trimRight = /\s+$/,
tinyCounter = 0,
math = Math,
mathRound = math.round,
mathMin = math.min,
mathMax = math.max,
mathRandom = math.random;
function tinycolor (color, opts) {
color = (color) ? color : '';
opts = opts || { };
// If input is already a tinycolor, return itself
if (color instanceof tinycolor) {
return color;
}
// If we are called as a function, call using new instead
if (!(this instanceof tinycolor)) {
return new tinycolor(color, opts);
}
var rgb = inputToRGB(color);
this._originalInput = color,
this._r = rgb.r,
this._g = rgb.g,
this._b = rgb.b,
this._a = rgb.a,
this._roundA = mathRound(100*this._a) / 100,
this._format = opts.format || rgb.format;
this._gradientType = opts.gradientType;
// Don't let the range of [0,255] come back in [0,1].
// Potentially lose a little bit of precision here, but will fix issues where
// .5 gets interpreted as half of the total, instead of half of 1
// If it was supposed to be 128, this was already taken care of by `inputToRgb`
if (this._r < 1) { this._r = mathRound(this._r); }
if (this._g < 1) { this._g = mathRound(this._g); }
if (this._b < 1) { this._b = mathRound(this._b); }
this._ok = rgb.ok;
this._tc_id = tinyCounter++;
}
tinycolor.prototype = {
isDark: function() {
return this.getBrightness() < 128;
},
isLight: function() {
return !this.isDark();
},
isValid: function() {
return this._ok;
},
getOriginalInput: function() {
return this._originalInput;
},
getFormat: function() {
return this._format;
},
getAlpha: function() {
return this._a;
},
getBrightness: function() {
//http://www.w3.org/TR/AERT#color-contrast
var rgb = this.toRgb();
return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
},
getLuminance: function() {
//http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
var rgb = this.toRgb();
var RsRGB, GsRGB, BsRGB, R, G, B;
RsRGB = rgb.r/255;
GsRGB = rgb.g/255;
BsRGB = rgb.b/255;
if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);}
if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);}
if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);}
return (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
},
setAlpha: function(value) {
this._a = boundAlpha(value);
this._roundA = mathRound(100*this._a) / 100;
return this;
},
toHsv: function() {
var hsv = rgbToHsv(this._r, this._g, this._b);
return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
},
toHsvString: function() {
var hsv = rgbToHsv(this._r, this._g, this._b);
var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
return (this._a == 1) ?
"hsv(" + h + ", " + s + "%, " + v + "%)" :
"hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
},
toHsl: function() {
var hsl = rgbToHsl(this._r, this._g, this._b);
return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
},
toHslString: function() {
var hsl = rgbToHsl(this._r, this._g, this._b);
var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
return (this._a == 1) ?
"hsl(" + h + ", " + s + "%, " + l + "%)" :
"hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
},
toHex: function(allow3Char) {
return rgbToHex(this._r, this._g, this._b, allow3Char);
},
toHexString: function(allow3Char) {
return '#' + this.toHex(allow3Char);
},
toHex8: function() {
return rgbaToHex(this._r, this._g, this._b, this._a);
},
toHex8String: function() {
return '#' + this.toHex8();
},
toRgb: function() {
return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
},
toRgbString: function() {
return (this._a == 1) ?
"rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
"rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
},
toPercentageRgb: function() {
return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
},
toPercentageRgbString: function() {
return (this._a == 1) ?
"rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
"rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
},
toName: function() {
if (this._a === 0) {
return "transparent";
}
if (this._a < 1) {
return false;
}
return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
},
toFilter: function(secondColor) {
var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
var secondHex8String = hex8String;
var gradientType = this._gradientType ? "GradientType = 1, " : "";
if (secondColor) {
var s = tinycolor(secondColor);
secondHex8String = s.toHex8String();
}
return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
},
toString: function(format) {
var formatSet = !!format;
format = format || this._format;
var formattedString = false;
var hasAlpha = this._a < 1 && this._a >= 0;
var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
if (needsAlphaFormat) {
// Special case for "transparent", all other non-alpha formats
// will return rgba when there is transparency.
if (format === "name" && this._a === 0) {
return this.toName();
}
return this.toRgbString();
}
if (format === "rgb") {
formattedString = this.toRgbString();
}
if (format === "prgb") {
formattedString = this.toPercentageRgbString();
}
if (format === "hex" || format === "hex6") {
formattedString = this.toHexString();
}
if (format === "hex3") {
formattedString = this.toHexString(true);
}
if (format === "hex8") {
formattedString = this.toHex8String();
}
if (format === "name") {
formattedString = this.toName();
}
if (format === "hsl") {
formattedString = this.toHslString();
}
if (format === "hsv") {
formattedString = this.toHsvString();
}
return formattedString || this.toHexString();
},
_applyModification: function(fn, args) {
var color = fn.apply(null, [this].concat([].slice.call(args)));
this._r = color._r;
this._g = color._g;
this._b = color._b;
this.setAlpha(color._a);
return this;
},
lighten: function() {
return this._applyModification(lighten, arguments);
},
brighten: function() {
return this._applyModification(brighten, arguments);
},
darken: function() {
return this._applyModification(darken, arguments);
},
desaturate: function() {
return this._applyModification(desaturate, arguments);
},
saturate: function() {
return this._applyModification(saturate, arguments);
},
greyscale: function() {
return this._applyModification(greyscale, arguments);
},
spin: function() {
return this._applyModification(spin, arguments);
},
_applyCombination: function(fn, args) {
return fn.apply(null, [this].concat([].slice.call(args)));
},
analogous: function() {
return this._applyCombination(analogous, arguments);
},
complement: function() {
return this._applyCombination(complement, arguments);
},
monochromatic: function() {
return this._applyCombination(monochromatic, arguments);
},
splitcomplement: function() {
return this._applyCombination(splitcomplement, arguments);
},
triad: function() {
return this._applyCombination(triad, arguments);
},
tetrad: function() {
return this._applyCombination(tetrad, arguments);
}
};
// If input is an object, force 1 into "1.0" to handle ratios properly
// String input requires "1.0" as input, so 1 will be treated as 1
tinycolor.fromRatio = function(color, opts) {
if (typeof color == "object") {
var newColor = {};
for (var i in color) {
if (color.hasOwnProperty(i)) {
if (i === "a") {
newColor[i] = color[i];
}
else {
newColor[i] = convertToPercentage(color[i]);
}
}
}
color = newColor;
}
return tinycolor(color, opts);
};
// Given a string or object, convert that input to RGB
// Possible string inputs:
//
// "red"
// "#f00" or "f00"
// "#ff0000" or "ff0000"
// "#ff000000" or "ff000000"
// "rgb 255 0 0" or "rgb (255, 0, 0)"
// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
//
function inputToRGB(color) {
var rgb = { r: 0, g: 0, b: 0 };
var a = 1;
var ok = false;
var format = false;
if (typeof color == "string") {
color = stringInputToObject(color);
}
if (typeof color == "object") {
if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
rgb = rgbToRgb(color.r, color.g, color.b);
ok = true;
format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
}
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
color.s = convertToPercentage(color.s);
color.v = convertToPercentage(color.v);
rgb = hsvToRgb(color.h, color.s, color.v);
ok = true;
format = "hsv";
}
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
color.s = convertToPercentage(color.s);
color.l = convertToPercentage(color.l);
rgb = hslToRgb(color.h, color.s, color.l);
ok = true;
format = "hsl";
}
if (color.hasOwnProperty("a")) {
a = color.a;
}
}
a = boundAlpha(a);
return {
ok: ok,
format: color.format || format,
r: mathMin(255, mathMax(rgb.r, 0)),
g: mathMin(255, mathMax(rgb.g, 0)),
b: mathMin(255, mathMax(rgb.b, 0)),
a: a
};
}
// Conversion Functions
// --------------------
// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
// `rgbToRgb`
// Handle bounds / percentage checking to conform to CSS color spec
// <http://www.w3.org/TR/css3-color/>
// *Assumes:* r, g, b in [0, 255] or [0, 1]
// *Returns:* { r, g, b } in [0, 255]
function rgbToRgb(r, g, b){
return {
r: bound01(r, 255) * 255,
g: bound01(g, 255) * 255,
b: bound01(b, 255) * 255
};
}
// `rgbToHsl`
// Converts an RGB color value to HSL.
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
// *Returns:* { h, s, l } in [0,1]
function rgbToHsl(r, g, b) {
r = bound01(r, 255);
g = bound01(g, 255);
b = bound01(b, 255);
var max = mathMax(r, g, b), min = mathMin(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min) {
h = s = 0; // achromatic
}
else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h, s: s, l: l };
}
// `hslToRgb`
// Converts an HSL color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
function hslToRgb(h, s, l) {
var r, g, b;
h = bound01(h, 360);
s = bound01(s, 100);
l = bound01(l, 100);
function hue2rgb(p, q, t) {
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
if(s === 0) {
r = g = b = l; // achromatic
}
else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return { r: r * 255, g: g * 255, b: b * 255 };
}
// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
function rgbToHsv(r, g, b) {
r = bound01(r, 255);
g = bound01(g, 255);
b = bound01(b, 255);
var max = mathMax(r, g, b), min = mathMin(r, g, b);
var h, s, v = max;
var d = max - min;
s = max === 0 ? 0 : d / max;
if(max == min) {
h = 0; // achromatic
}
else {
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h, s: s, v: v };
}
// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
function hsvToRgb(h, s, v) {
h = bound01(h, 360) * 6;
s = bound01(s, 100);
v = bound01(v, 100);
var i = math.floor(h),
f = h - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s),
mod = i % 6,
r = [v, q, p, p, t, v][mod],
g = [t, v, v, q, p, p][mod],
b = [p, p, t, v, v, q][mod];
return { r: r * 255, g: g * 255, b: b * 255 };
}
// `rgbToHex`
// Converts an RGB color to hex
// Assumes r, g, and b are contained in the set [0, 255]
// Returns a 3 or 6 character hex
function rgbToHex(r, g, b, allow3Char) {
var hex = [
pad2(mathRound(r).toString(16)),
pad2(mathRound(g).toString(16)),
pad2(mathRound(b).toString(16))
];
// Return a 3 character hex if possible
if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
}
return hex.join("");
}
// `rgbaToHex`
// Converts an RGBA color plus alpha transparency to hex
// Assumes r, g, b and a are contained in the set [0, 255]
// Returns an 8 character hex
function rgbaToHex(r, g, b, a) {
var hex = [
pad2(convertDecimalToHex(a)),
pad2(mathRound(r).toString(16)),
pad2(mathRound(g).toString(16)),
pad2(mathRound(b).toString(16))
];
return hex.join("");
}
// `equals`
// Can be called with any tinycolor input
tinycolor.equals = function (color1, color2) {
if (!color1 || !color2) { return false; }
return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
};
tinycolor.random = function() {
return tinycolor.fromRatio({
r: mathRandom(),
g: mathRandom(),
b: mathRandom()
});
};
// Modification Functions
// ----------------------
// Thanks to less.js for some of the basics here
// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
function desaturate(color, amount) {
amount = (amount === 0) ? 0 : (amount || 10);
var hsl = tinycolor(color).toHsl();
hsl.s -= amount / 100;
hsl.s = clamp01(hsl.s);
return tinycolor(hsl);
}
function saturate(color, amount) {
amount = (amount === 0) ? 0 : (amount || 10);
var hsl = tinycolor(color).toHsl();
hsl.s += amount / 100;
hsl.s = clamp01(hsl.s);
return tinycolor(hsl);
}
function greyscale(color) {
return tinycolor(color).desaturate(100);
}
function lighten (color, amount) {
amount = (amount === 0) ? 0 : (amount || 10);
var hsl = tinycolor(color).toHsl();
hsl.l += amount / 100;
hsl.l = clamp01(hsl.l);
return tinycolor(hsl);
}
function brighten(color, amount) {
amount = (amount === 0) ? 0 : (amount || 10);
var rgb = tinycolor(color).toRgb();
rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
return tinycolor(rgb);
}
function darken (color, amount) {
amount = (amount === 0) ? 0 : (amount || 10);
var hsl = tinycolor(color).toHsl();
hsl.l -= amount / 100;
hsl.l = clamp01(hsl.l);
return tinycolor(hsl);
}
// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
// Values outside of this range will be wrapped into this range.
function spin(color, amount) {
var hsl = tinycolor(color).toHsl();
var hue = (mathRound(hsl.h) + amount) % 360;
hsl.h = hue < 0 ? 360 + hue : hue;
return tinycolor(hsl);
}
// Combination Functions
// ---------------------
// Thanks to jQuery xColor for some of the ideas behind these
// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
function complement(color) {
var hsl = tinycolor(color).toHsl();
hsl.h = (hsl.h + 180) % 360;
return tinycolor(hsl);
}
function triad(color) {
var hsl = tinycolor(color).toHsl();
var h = hsl.h;
return [
tinycolor(color),
tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
];
}
function tetrad(color) {
var hsl = tinycolor(color).toHsl();
var h = hsl.h;
return [
tinycolor(color),
tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
];
}
function splitcomplement(color) {
var hsl = tinycolor(color).toHsl();
var h = hsl.h;
return [
tinycolor(color),
tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
];
}
function analogous(color, results, slices) {
results = results || 6;
slices = slices || 30;
var hsl = tinycolor(color).toHsl();
var part = 360 / slices;
var ret = [tinycolor(color)];
for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
hsl.h = (hsl.h + part) % 360;
ret.push(tinycolor(hsl));
}
return ret;
}
function monochromatic(color, results) {
results = results || 6;
var hsv = tinycolor(color).toHsv();
var h = hsv.h, s = hsv.s, v = hsv.v;
var ret = [];
var modification = 1 / results;
while (results--) {
ret.push(tinycolor({ h: h, s: s, v: v}));
v = (v + modification) % 1;
}
return ret;
}
// Utility Functions
// ---------------------
tinycolor.mix = function(color1, color2, amount) {
amount = (amount === 0) ? 0 : (amount || 50);
var rgb1 = tinycolor(color1).toRgb();
var rgb2 = tinycolor(color2).toRgb();
var p = amount / 100;
var w = p * 2 - 1;
var a = rgb2.a - rgb1.a;
var w1;
if (w * a == -1) {
w1 = w;
} else {
w1 = (w + a) / (1 + w * a);
}
w1 = (w1 + 1) / 2;
var w2 = 1 - w1;
var rgba = {
r: rgb2.r * w1 + rgb1.r * w2,
g: rgb2.g * w1 + rgb1.g * w2,
b: rgb2.b * w1 + rgb1.b * w2,
a: rgb2.a * p + rgb1.a * (1 - p)
};
return tinycolor(rgba);
};
// Readability Functions
// ---------------------
// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)
// `contrast`
// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
tinycolor.readability = function(color1, color2) {
var c1 = tinycolor(color1);
var c2 = tinycolor(color2);
return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
};
// `isReadable`
// Ensure that foreground and background color combinations meet WCAG2 guidelines.
// The third argument is an optional Object.
// the 'level' property states 'AA' or 'AAA' - if missing or invalid, it defaults to 'AA';
// the 'size' property states 'large' or 'small' - if missing or invalid, it defaults to 'small'.
// If the entire object is absent, isReadable defaults to {level:"AA",size:"small"}.
// *Example*
// tinycolor.isReadable("#000", "#111") => false
// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false
tinycolor.isReadable = function(color1, color2, wcag2) {
var readability = tinycolor.readability(color1, color2);
var wcag2Parms, out;
out = false;
wcag2Parms = validateWCAG2Parms(wcag2);
switch (wcag2Parms.level + wcag2Parms.size) {
case "AAsmall":
case "AAAlarge":
out = readability >= 4.5;
break;
case "AAlarge":
out = readability >= 3;
break;
case "AAAsmall":
out = readability >= 7;
break;
}
return out;
};
// `mostReadable`
// Given a base color and a list of possible foreground or background
// colors for that base, returns the most readable color.
// Optionally returns Black or White if the most readable color is unreadable.
// *Example*
// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255"
// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff"
// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3"
// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff"
tinycolor.mostReadable = function(baseColor, colorList, args) {
var bestColor = null;
var bestScore = 0;
var readability;
var includeFallbackColors, level, size ;
args = args || {};
includeFallbackColors = args.includeFallbackColors ;
level = args.level;
size = args.size;
for (var i= 0; i < colorList.length ; i++) {
readability = tinycolor.readability(baseColor, colorList[i]);
if (readability > bestScore) {
bestScore = readability;
bestColor = tinycolor(colorList[i]);
}
}
if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) {
return bestColor;
}
else {
args.includeFallbackColors=false;
return tinycolor.mostReadable(baseColor,["#fff", "#000"],args);
}
};
// Big List of Colors
// ------------------
// <http://www.w3.org/TR/css3-color/#svg-color>
var names = tinycolor.names = {
aliceblue: "f0f8ff",
antiquewhite: "faebd7",
aqua: "0ff",
aquamarine: "7fffd4",
azure: "f0ffff",
beige: "f5f5dc",
bisque: "ffe4c4",
black: "000",
blanchedalmond: "ffebcd",
blue: "00f",
blueviolet: "8a2be2",
brown: "a52a2a",
burlywood: "deb887",
burntsienna: "ea7e5d",
cadetblue: "5f9ea0",
chartreuse: "7fff00",
chocolate: "d2691e",
coral: "ff7f50",
cornflowerblue: "6495ed",
cornsilk: "fff8dc",
crimson: "dc143c",
cyan: "0ff",
darkblue: "00008b",
darkcyan: "008b8b",
darkgoldenrod: "b8860b",
darkgray: "a9a9a9",
darkgreen: "006400",
darkgrey: "a9a9a9",
darkkhaki: "bdb76b",
darkmagenta: "8b008b",
darkolivegreen: "556b2f",
darkorange: "ff8c00",
darkorchid: "9932cc",
darkred: "8b0000",
darksalmon: "e9967a",
darkseagreen: "8fbc8f",
darkslateblue: "483d8b",
darkslategray: "2f4f4f",
darkslategrey: "2f4f4f",
darkturquoise: "00ced1",
darkviolet: "9400d3",
deeppink: "ff1493",
deepskyblue: "00bfff",
dimgray: "696969",
dimgrey: "696969",
dodgerblue: "1e90ff",
firebrick: "b22222",
floralwhite: "fffaf0",
forestgreen: "228b22",
fuchsia: "f0f",
gainsboro: "dcdcdc",
ghostwhite: "f8f8ff",
gold: "ffd700",
goldenrod: "daa520",
gray: "808080",
green: "008000",
greenyellow: "adff2f",
grey: "808080",
honeydew: "f0fff0",
hotpink: "ff69b4",
indianred: "cd5c5c",
indigo: "4b0082",
ivory: "fffff0",
khaki: "f0e68c",
lavender: "e6e6fa",
lavenderblush: "fff0f5",
lawngreen: "7cfc00",
lemonchiffon: "fffacd",
lightblue: "add8e6",
lightcoral: "f08080",
lightcyan: "e0ffff",
lightgoldenrodyellow: "fafad2",
lightgray: "d3d3d3",
lightgreen: "90ee90",
lightgrey: "d3d3d3",
lightpink: "ffb6c1",
lightsalmon: "ffa07a",
lightseagreen: "20b2aa",
lightskyblue: "87cefa",
lightslategray: "789",
lightslategrey: "789",
lightsteelblue: "b0c4de",
lightyellow: "ffffe0",
lime: "0f0",
limegreen: "32cd32",
linen: "faf0e6",
magenta: "f0f",
maroon: "800000",
mediumaquamarine: "66cdaa",
mediumblue: "0000cd",
mediumorchid: "ba55d3",
mediumpurple: "9370db",
mediumseagreen: "3cb371",
mediumslateblue: "7b68ee",
mediumspringgreen: "00fa9a",
mediumturquoise: "48d1cc",
mediumvioletred: "c71585",
midnightblue: "191970",
mintcream: "f5fffa",
mistyrose: "ffe4e1",
moccasin: "ffe4b5",
navajowhite: "ffdead",
navy: "000080",
oldlace: "fdf5e6",
olive: "808000",
olivedrab: "6b8e23",
orange: "ffa500",
orangered: "ff4500",
orchid: "da70d6",
palegoldenrod: "eee8aa",
palegreen: "98fb98",
paleturquoise: "afeeee",
palevioletred: "db7093",
papayawhip: "ffefd5",
peachpuff: "ffdab9",
peru: "cd853f",
pink: "ffc0cb",
plum: "dda0dd",
powderblue: "b0e0e6",
purple: "800080",
rebeccapurple: "663399",
red: "f00",
rosybrown: "bc8f8f",
royalblue: "4169e1",
saddlebrown: "8b4513",
salmon: "fa8072",
sandybrown: "f4a460",
seagreen: "2e8b57",
seashell: "fff5ee",
sienna: "a0522d",
silver: "c0c0c0",
skyblue: "87ceeb",
slateblue: "6a5acd",
slategray: "708090",
slategrey: "708090",
snow: "fffafa",
springgreen: "00ff7f",
steelblue: "4682b4",
tan: "d2b48c",
teal: "008080",
thistle: "d8bfd8",
tomato: "ff6347",
turquoise: "40e0d0",
violet: "ee82ee",
wheat: "f5deb3",
white: "fff",
whitesmoke: "f5f5f5",
yellow: "ff0",
yellowgreen: "9acd32"
};
// Make it easy to access colors via `hexNames[hex]`
var hexNames = tinycolor.hexNames = flip(names);
// Utilities
// ---------
// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
function flip(o) {
var flipped = { };
for (var i in o) {
if (o.hasOwnProperty(i)) {
flipped[o[i]] = i;
}
}
return flipped;
}
// Return a valid alpha value [0,1] with all invalid values being set to 1
function boundAlpha(a) {
a = parseFloat(a);
if (isNaN(a) || a < 0 || a > 1) {
a = 1;
}
return a;
}
// Take input from [0, n] and return it as [0, 1]
function bound01(n, max) {
if (isOnePointZero(n)) { n = "100%"; }
var processPercent = isPercentage(n);
n = mathMin(max, mathMax(0, parseFloat(n)));
// Automatically convert percentage into number
if (processPercent) {
n = parseInt(n * max, 10) / 100;
}
// Handle floating point rounding errors
if ((math.abs(n - max) < 0.000001)) {
return 1;
}
// Convert into [0, 1] range if it isn't already
return (n % max) / parseFloat(max);
}
// Force a number between 0 and 1
function clamp01(val) {
return mathMin(1, mathMax(0, val));
}
// Parse a base-16 hex value into a base-10 integer
function parseIntFromHex(val) {
return parseInt(val, 16);
}
// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
function isOnePointZero(n) {
return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
}
// Check to see if string passed in is a percentage
function isPercentage(n) {
return typeof n === "string" && n.indexOf('%') != -1;
}
// Force a hex value to have 2 characters
function pad2(c) {
return c.length == 1 ? '0' + c : '' + c;
}
// Replace a decimal with it's percentage value
function convertToPercentage(n) {
if (n <= 1) {
n = (n * 100) + "%";
}
return n;
}
// Converts a decimal to a hex value
function convertDecimalToHex(d) {
return Math.round(parseFloat(d) * 255).toString(16);
}
// Converts a hex value to a decimal
function convertHexToDecimal(h) {
return (parseIntFromHex(h) / 255);
}
var matchers = (function() {
// <http://www.w3.org/TR/css3-values/#integers>
var CSS_INTEGER = "[-\\+]?\\d+%?";
// <http://www.w3.org/TR/css3-values/#number-value>
var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
// Actual matching.
// Parentheses and commas are optional, but not required.
// Whitespace can take the place of commas or opening paren
var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
return {
rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
};
})();
// `stringInputToObject`
// Permissive string parsing. Take in a number of formats, and output an object
// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
function stringInputToObject(color) {
color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
var named = false;
if (names[color]) {
color = names[color];
named = true;
}
else if (color == 'transparent') {
return { r: 0, g: 0, b: 0, a: 0, format: "name" };
}
// Try to match string input using regular expressions.
// Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
// Just return an object and let the conversion functions handle that.
// This way the result will be the same whether the tinycolor is initialized with string or object.
var match;
if ((match = matchers.rgb.exec(color))) {
return { r: match[1], g: match[2], b: match[3] };
}
if ((match = matchers.rgba.exec(color))) {
return { r: match[1], g: match[2], b: match[3], a: match[4] };
}
if ((match = matchers.hsl.exec(color))) {
return { h: match[1], s: match[2], l: match[3] };
}
if ((match = matchers.hsla.exec(color))) {
return { h: match[1], s: match[2], l: match[3], a: match[4] };
}
if ((match = matchers.hsv.exec(color))) {
return { h: match[1], s: match[2], v: match[3] };
}
if ((match = matchers.hsva.exec(color))) {
return { h: match[1], s: match[2], v: match[3], a: match[4] };
}
if ((match = matchers.hex8.exec(color))) {
return {
a: convertHexToDecimal(match[1]),
r: parseIntFromHex(match[2]),
g: parseIntFromHex(match[3]),
b: parseIntFromHex(match[4]),
format: named ? "name" : "hex8"
};
}
if ((match = matchers.hex6.exec(color))) {
return {
r: parseIntFromHex(match[1]),
g: parseIntFromHex(match[2]),
b: parseIntFromHex(match[3]),
format: named ? "name" : "hex"
};
}
if ((match = matchers.hex3.exec(color))) {
return {
r: parseIntFromHex(match[1] + '' + match[1]),
g: parseIntFromHex(match[2] + '' + match[2]),
b: parseIntFromHex(match[3] + '' + match[3]),
format: named ? "name" : "hex"
};
}
return false;
}
function validateWCAG2Parms(parms) {
// return valid WCAG2 parms for isReadable.
// If input parms are invalid, return {"level":"AA", "size":"small"}
var level, size;
parms = parms || {"level":"AA", "size":"small"};
level = (parms.level || "AA").toUpperCase();
size = (parms.size || "small").toLowerCase();
if (level !== "AA" && level !== "AAA") {
level = "AA";
}
if (size !== "small" && size !== "large") {
size = "small";
}
return {"level":level, "size":size};
}
// Node: Export function
if (typeof module !== "undefined" && module.exports) {
module.exports = tinycolor;
}
// AMD/requirejs: Define the module
else if (typeof define === 'function' && define.amd) {
define(function () {return tinycolor;});
}
// Browser: Expose to window
else {
window.tinycolor = tinycolor;
}
})();