').appendTo("body");f=o(h,{width:1,height:1,wmode:"transparent",swf:e,allowScriptAccess:"always",flashvars:{allowedDomain:a.location.hostname}}),n=function(a,b){m(k).done(function(c){c.getColor(a,b)})},n(c,d)};g.setSwf=function(a){e=a},g.getSwf=function(){return e},g.trigger=function(a){switch(a.type){case h:m(k).resolve(f);break;case i:m(a.data.url).resolve(a.data);break;case j:m(a.data.url).reject(a.data)}},g.color=function(a){var b=m(a.url);"function"==typeof a.success&&b.done(a.success),"function"==typeof a.error&&b.fail(a.error),"pending"===b.state()&&n(a.url,"string"==typeof a.ignore?a.ignore:"")};var o=function(){function c(a){if(!b.isPlainObject(a))return a;var d,e,f=[],g="";for(d in a)e=a[d],g=b.isPlainObject(e)?c(e):[d,encodeURI(e)].join("="),f.push(g);return f.join("&")}function d(a){var b,c,d=[];for(b in a)c=a[b],c&&d.push([b,'="',c,'"'].join(""));return d.join(" ")}function e(a){var b=[];for(var d in a)b.push(['
'].join(""));return b.join("")}var f,g,h,i=!!a.attachEvent&&"[object Opera]"!==Object.prototype.toString.call(a.opera),j=navigator.plugins["Shockwave Flash"]||a.ActiveXObject;try{f=j.description?j.description:new j("ShockwaveFlash.ShockwaveFlash").GetVariable("$version")}catch(k){f="Unavailable"}return g=f.match(/\d+/g),h=!!g&&g[0]>0,function(a,c){var f;if(!c.swf||!h)return!1;f={id:"swf-"+b.guid++,width:c.width||1,height:c.height||1,style:c.style||""},i?(f.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",c.movie=c.swf):(f.data=c.swf,f.type="application/x-shockwave-flash"),c.wmode=c.wmode||"opaque";var g=["
"].join("");if(i){var j=document.createElement("div");a.html(j),j.outerHTML=g}else a.html(g);return a.children().get(0)}}();a.Imgcolr=g;var p="imgcolr";b.fn[p]=function(a,b){return this.each(function(){new d(this,a,b)})},g.imgcolr=function(a,c,d){b(a).imgcolr(c,d)},"function"==typeof define&&define.amd&&define("imgcolr",[],function(){return g})}(window,jQuery);
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | //Project config
4 | grunt.initConfig({
5 | pkg: grunt.file.readJSON('package.json'),
6 | banner: {
7 | compact: '/*! <%= pkg.name %> v<%= pkg.version %> | <%= pkg.author %> | <%= pkg.license %> */\n',
8 | full: '/*!\n' +
9 | ' * <%= pkg.name %> v<%= pkg.version %>\n' +
10 | ' * Author: <%= pkg.author %>\n' +
11 | ' * Released under the <%= pkg.license %> license\n' +
12 | ' */'
13 | },
14 | clean: {
15 | build: [
16 | 'dist',
17 | '<%= pkg.name %>.html5.min.js'
18 | ]
19 | },
20 | concat: {
21 | dist: {
22 | options: {
23 | banner: '<%= banner.full %>\n(function (window, $, undefined) {',
24 | footer: '})(window, jQuery);',
25 | process: function (src, filepath) {
26 | if ((/define\(.*?\{/).test(src)) {
27 | src = src.replace(/define\(.*?\{/, '');
28 | src = src.replace(/\}\);\s*?$/,'');
29 | }
30 | return src;
31 | }
32 | },
33 | src: [
34 | 'src/shortcut.js',
35 | 'src/color.js',
36 | 'src/appendFlash.js',
37 | 'src/outer.js',
38 | 'src/plugin.js',
39 | 'src/amd-support.js'
40 | ],
41 | dest: 'dist/<%= pkg.name %>.js',
42 | nonull: true
43 | }
44 | },
45 | jshint: {
46 | options: {
47 | jshintrc: '.jshintrc'
48 | },
49 | files: [
50 | 'dist/<%= pkg.name %>.js',
51 | '<%= pkg.name %>.html5.js'
52 | ]
53 | },
54 | uglify: {
55 | options: {
56 | banner: '<%= banner.compact %>'
57 | },
58 | build: {
59 | files: {
60 | 'dist/<%= pkg.name %>.min.js': 'dist/<%= pkg.name %>.js',
61 | '<%= pkg.name %>.html5.min.js': '<%= pkg.name %>.html5.js'
62 | }
63 | }
64 | },
65 | qunit: {
66 | options: {
67 | urls: ['./test/qunit/index.html']
68 | }
69 | }
70 | });
71 |
72 | grunt.loadNpmTasks('grunt-contrib-clean');
73 | grunt.loadNpmTasks('grunt-contrib-concat');
74 | grunt.loadNpmTasks('grunt-contrib-jshint');
75 | grunt.loadNpmTasks('grunt-contrib-uglify');
76 | grunt.loadNpmTasks('grunt-contrib-qunit');
77 |
78 | // Default task(s).
79 | grunt.registerTask('build', ['clean', 'concat', 'jshint', 'uglify']);
80 | grunt.registerTask('test', ['build', 'qunit']);
81 | grunt.registerTask('default', ['build']);
82 |
83 | };
--------------------------------------------------------------------------------
/test/test.vhtml5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
59 |
87 |
88 |
--------------------------------------------------------------------------------
/src/color.js:
--------------------------------------------------------------------------------
1 | define(['jQuery', 'appendFlash', 'Imgcolr'], function ($, appendFlash, Imgcolr) {
2 |
3 | /****** some constants ******/
4 | // event name
5 | var EVT_SWFREADY = 'swfReady';
6 | var EVT_SUCCESS = 'success';
7 | var EVT_ERROR = 'error';
8 | // swf dfd keyname
9 | var DFD_SWF = 'dfd-swf';
10 |
11 | // swf file url, swf object
12 | var swfUrl, swfObj;
13 | // all deferred objects cache
14 | var dfdCache = {};
15 | // get or cache Deferred objects
16 | var getDfd = function (key) {
17 | var dfd = dfdCache[key];
18 | if (!dfd) {
19 | dfd = $.Deferred();
20 | dfdCache[key] = dfd;
21 | }
22 | return dfd;
23 | };
24 |
25 | // swf method
26 | var compute = function (url, ignore) {
27 | var style = 'position:absolute; left:0; top:0; width:1px; height:1px;';
28 | var swfNode = $('
').appendTo('body');
29 |
30 | swfObj = appendFlash(swfNode, {
31 | width: 1,
32 | height: 1,
33 | wmode: 'transparent',
34 | swf: swfUrl,
35 | allowScriptAccess: 'always',
36 | flashvars: {
37 | allowedDomain: window.location.hostname
38 | }
39 | });
40 |
41 | compute = function (url, ignore) {
42 | getDfd(DFD_SWF).done(function (obj) {
43 | obj.getColor(url, ignore);
44 | });
45 | };
46 |
47 | compute(url, ignore);
48 | };
49 | // You must specify the swf url according to your scenario before using this.
50 | Imgcolr.setSwf = function (url) {
51 | swfUrl = url;
52 | };
53 |
54 | Imgcolr.getSwf = function () {
55 | return swfUrl;
56 | };
57 |
58 | // @private - very important, this method is called from swf internally
59 | Imgcolr.trigger = function (evtObj) {
60 | switch (evtObj.type) {
61 | case EVT_SWFREADY:
62 | getDfd(DFD_SWF).resolve(swfObj);
63 | break;
64 | case EVT_SUCCESS:
65 | getDfd(evtObj.data.url).resolve(evtObj.data);
66 | break;
67 | case EVT_ERROR:
68 | getDfd(evtObj.data.url).reject(evtObj.data);
69 | break;
70 | }
71 | };
72 | // Imgcolr.color
73 | // ---------------- core method ---------------
74 | // @param {string} options.url - The url of the image
75 | // @param {string} options.ignore - Which border should be ignored,
76 | // there are 4 kinds of values: 't', 'r', 'b', 'l', you can ignore multiple borders like this: 'tb', it's optional
77 | // @param {function} options.success - The callback for success
78 | // @param {function} options.error - The callback for error, it's optional
79 | Imgcolr.color = function (options) {
80 | var dfd = getDfd(options.url);
81 |
82 | if (typeof options.success === 'function') {
83 | dfd.done(options.success);
84 | }
85 |
86 | if (typeof options.error === 'function') {
87 | dfd.fail(options.error);
88 | }
89 |
90 | if ('pending' === dfd.state()) {
91 | compute(options.url, typeof options.ignore === 'string' ? options.ignore : '');
92 | }
93 | };
94 |
95 | });
--------------------------------------------------------------------------------
/src/appendFlash.js:
--------------------------------------------------------------------------------
1 | define(['jQuery'], function ($) {
2 |
3 | var appendFlash = (function () {
4 | var version, versionNumbers, flashAvailable;
5 | // Prototype style
6 | // https://github.com/sstephenson/prototype/blob/1fb9728/src/prototype.js#L81
7 | var isIE = !!window.attachEvent && Object.prototype.toString.call(window.opera) !== '[object Opera]';
8 |
9 | var Plugin = navigator.plugins['Shockwave Flash'] || window.ActiveXObject;
10 |
11 | try {
12 | if (Plugin.description) {
13 | version = Plugin.description;
14 | } else {
15 | version = (new Plugin('ShockwaveFlash.ShockwaveFlash')).GetVariable('$version');
16 | }
17 | } catch (e) {
18 | version = 'Unavailable';
19 | }
20 | versionNumbers = version.match(/\d+/g);
21 | flashAvailable = !!versionNumbers && versionNumbers[0] > 0;
22 |
23 | function buildQueryString (obj) {
24 | if (!$.isPlainObject(obj)) {
25 | return obj;
26 | }
27 |
28 | var k, v;
29 | var arr = [];
30 | var str = '';
31 |
32 | for (k in obj) {
33 | v = obj[k];
34 | if ($.isPlainObject(v)) {
35 | str = buildQueryString(v);
36 | } else {
37 | str = [k, encodeURI(v)].join('=');
38 | }
39 | arr.push(str);
40 | }
41 |
42 | return arr.join('&');
43 | }
44 |
45 | // build attributes based on an object
46 | function buildAttr (obj) {
47 | var k, v;
48 | var arr = [];
49 |
50 | for (k in obj) {
51 | v = obj[k];
52 | if (v) {
53 | arr.push([k, '="', v, '"'].join(''));
54 | }
55 | }
56 |
57 | return arr.join(' ');
58 | }
59 |
60 | function buildParamTag (obj) {
61 | var arr = [];
62 |
63 | for (var k in obj) {
64 | arr.push(['
'].join(''));
65 | }
66 |
67 | return arr.join('');
68 | }
69 |
70 | // return the real method to append a flash object
71 | return function (elem, options) {
72 | var attrs;
73 |
74 | if (!options.swf || !flashAvailable) {
75 | return false;
76 | }
77 |
78 | attrs = {
79 | id: 'swf-' + ($.guid++),
80 | width: options.width || 1,
81 | height: options.height || 1,
82 | style: options.style || ''
83 | };
84 |
85 | if (isIE) {
86 | attrs.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
87 | options.movie = options.swf;
88 | } else {
89 | attrs.data = options.swf;
90 | attrs.type = 'application/x-shockwave-flash';
91 | }
92 |
93 | options.wmode = options.wmode || 'opaque';
94 |
95 | var html = ['
'].join('');
96 | if (isIE) {
97 | var flashContainer = document.createElement('div');
98 | elem.html(flashContainer);
99 | flashContainer.outerHTML = html;
100 | } else {
101 | elem.html(html);
102 | }
103 |
104 | return elem.children().get(0);
105 | };
106 | })();
107 |
108 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | imgcolr
2 | =======
3 |
4 | imgcolr is a jQuery plugin for grabbing the dominant color of a given image's borders. You can programmably adapt the elements' color on the webpage for the image after getting the color. Based on the idea, we can make the web more beautiful and interesting. Note that imgcolr can not handle images with colorful borders properly.
5 |
6 | :point_right: [Check out the demo page](http://swaydeng.github.io/imgcolr/).
7 |
8 | ## How-To
9 |
10 | imgcolr accesses image binary data by using flash, so make sure Adobe Flash Player is installed properly. (You can also use the [HTML5 version](#vhtml5))
11 |
12 | #### Upload SWF file
13 |
14 | First you should upload `media/imgcolr.swf` and hosts it on a server, suppose that your swf file url is `http://static.bar.com/dir/imgcolr.swf`, and all your images are hosted on the server with domain `http://img.foo.com`.
15 |
16 | #### crossdomain.xml
17 |
18 | If your swf file and images are hosted on the same domain, skip this step, if not, keep following.
19 | Make sure a `crossdomain.xml` file is in the root directory of the image server, like this: `http://img.foo.com/crossdomain.xml`, A crossdomain.xml file is an XML document that grants Adobe Flash Player(swf) permission to handle data across multiple domains ([learn more](http://www.adobe.com/cn/devnet/articles/crossdomain_policy_file_spec.html)). In this scenario, we should grants the swf on `static.bar.com` permission to access image data, so your crossdomain.xml may be like this:
20 |
21 | ```xml
22 |
23 |
24 |
25 |
26 | ```
27 |
28 | #### JavaScript
29 |
30 | imgcolr is a jQuery plugin, so make sure jQuery is included in your web page before including imgcolr code:
31 |
32 | ```html
33 |
34 |
35 | ```
36 |
37 | imgcolr handles `
![]()
` tag, or `HTMLImageElement`, consider a page with a simple list on it:
38 |
39 | ```html
40 |
41 | -
42 |
43 |

44 |
45 | Hello, World!
46 |
47 | -
48 |
49 |

50 |
51 | Hello, Human!
52 |
53 |
54 | ```
55 |
56 | Now i will explain the method in action:
57 |
58 | After including **imgcolr.min.js**, a global object `Imgcolr` will be created. Before
59 | you use the plugin, you need specify the swf file's url:
60 |
61 | ```javascript
62 | Imgcolr.setSwf('http://static.bar.com/dir/imgcolr.swf');
63 | ```
64 |
65 | then feel free to use:
66 |
67 | ```javascript
68 | var imgs = $('img');
69 |
70 | // get the image borders' color
71 | // and the style property background-color of each parent will be set to this color,
72 | // The result of this call is a background-color change for all div element with class "box".
73 | imgs.imgcolr();
74 |
75 | // get the image borders' color
76 | // and the style property background-color of the ancestors with class "item" will
77 | // be set to this color.
78 | imgs.imgcolr('.item');
79 |
80 | // get the image borders' color
81 | // you can specify which elements' background-color to set by returning them
82 | imgs.imgcolr(function () {
83 | // `this` refers to the current img element
84 | return $(this).parent().next('.label');
85 | });
86 |
87 | // if you don't want any element's background-color change,
88 | // or maybe you just want to know the image borders' color,
89 | // return nothing is fine.
90 | imgs.imgcolr(function (img, color) {
91 | // `img` refers to the current img element
92 | console.log(img);
93 | // `color` is the grabbed color, a string like "#ededed"
94 | console.log(color);
95 | });
96 |
97 | // Suppose that you just adapt background color only for a given image's left and
98 | // right borders, you can ignore the others, here is the rule:
99 | // "t" represents top border
100 | // "r" represents right border
101 | // "b" represents bottom border
102 | // "l" represents left border
103 | imgs.imgcolr({
104 | ignore: 'tb' // ignores top border and bottom border
105 | });
106 |
107 | // Of course you can use the filter and ignore option at the same time
108 | imgs.imgcolr('.item', {
109 | ignore: 'tb'
110 | });
111 | ```
112 |
113 | #### AMD module support
114 |
115 | imgcolr supports AMD, the arguments are identical except that the first one is the element(s) to handle:
116 |
117 | ```javascript
118 | define(['jquery', 'imgcolr'], function ( $, Imgcolr ) {
119 |
120 | Imgcolr.setSwf('http://static.bar.com/dir/imgcolr.swf');
121 |
122 | var imgs = $('img');
123 |
124 | // identical to `imgs.imgcolr();`
125 | Imgcolr.imgcolr(imgs);
126 |
127 | // identical to `imgs.imgcolr('.item');`
128 | Imgcolr.imgcolr(imgs, '.item');
129 |
130 | // identical to `imgs.imgcolr('.item', { ignore: 'tb' });`
131 | Imgcolr.imgcolr(imgs, '.item', { ignore: 'tb' });
132 |
133 | });
134 | ```
135 |
136 | ## Build your own imgcolr
137 |
138 | Make sure `node` and node package `grunt-cli` are installed globally on your computer, and cd into the project directory, install necessary packages by running `npm install`.
139 |
140 | You should not modify **imgcolr.js** in root directory, for modular reason, please modify files in `src/`, and then run `grunt build` , the latest **imgcolr.js** and **imgcolr.min.js** will be created in `dist/`.
141 |
142 | ##
HTML5 version
143 |
144 | I am a big fan of HTML5, however HTML5 image [CORS](https://developer.mozilla.org/en/docs/HTTP/Access_control_CORS) specification is supported terribly by browser vendors. So imgcolr based on HTML5 just supports latest modern browsers like Google Chrome and Firefox , and it is experimental. Anyway, it's really faster than the Flash version.
145 | First of all , make sure jQuery and `imgcolr.html5.min.js` are included in your web page:
146 |
147 | ```html
148 |
149 |
150 | ```
151 |
152 | If your web page and images on the page are hosted on the same domain, skip this step, if not, you should enable image CORS by adding the appropriate Access-Control-Allow-Origin header info ([more details](https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image) you must read).
153 |
154 | There is no different on calling method, and `Imgcolr.setSwf` is unnecessary:
155 |
156 | ```javascript
157 |
158 | var imgs = $('img');
159 |
160 | imgs.imgcolr();
161 |
162 | ```
163 |
164 | :point_right: [Check out the demo page](http://swaydeng.github.io/imgcolr/h5.html).
--------------------------------------------------------------------------------
/imgcolr.html5.js:
--------------------------------------------------------------------------------
1 | (function (root, $) {
2 |
3 | var previousImgcolr = root.Imgcolr;
4 |
5 | var Imgcolr = {};
6 | // all deferred objects cache
7 | var dfdCache = {};
8 | var canvas = document.createElement('canvas');
9 | // TODO find a better way to determine image cors support precisely.
10 | //var imgCors = !!(canvas.getContext && canvas.getContext('2d')) && $.support.cors && ('crossOrigin' in new Image());
11 |
12 | var SIDE_TOP = 't';
13 | var SIDE_RIGHT = 'r';
14 | var SIDE_BOTTOM = 'b';
15 | var SIDE_LEFT = 'l';
16 |
17 | // get or cache Deferred objects
18 | var getDfd = function (key) {
19 | var dfd = dfdCache[key];
20 | if (!dfd) {
21 | dfd = $.Deferred();
22 | dfdCache[key] = dfd;
23 | }
24 | return dfd;
25 | };
26 |
27 | var decToHex = function (num) {
28 | var hex = num.toString(16);
29 | return hex.length === 1 ? '0' + hex : hex;
30 | };
31 |
32 | var rgbToHex = function (r, g, b) {
33 | return ['#', decToHex(r), decToHex(g), decToHex(b)].join('');
34 | };
35 |
36 | var count = function (idx, imageData, colorsInfo) {
37 | var name;
38 | var alpha = imageData[idx + 3];
39 |
40 | // if true, means alpha value is less than 127, ignore this point
41 | if (alpha < 127) {
42 | return;
43 | }
44 |
45 | name = rgbToHex(imageData[idx], imageData[idx + 1], imageData[idx + 2]);
46 | if (colorsInfo[name]) {
47 | colorsInfo[name] ++;
48 | } else {
49 | colorsInfo[name] = 1;
50 | }
51 | };
52 |
53 | var traverse = function (side, colorsInfo, width, height, imageData) {
54 | var x, y;
55 | if (side === SIDE_TOP || side === SIDE_BOTTOM) {
56 | y = (side === SIDE_TOP) ? 0 : (height - 1);
57 | for (x = 0; x < width; x ++) {
58 | count((y * width + x) * 4, imageData, colorsInfo);
59 | }
60 | } else { // side is right or left
61 | height = height - 1;
62 | x = (side === SIDE_RIGHT) ? (width - 1) : 0;
63 | for (y = 1; y < height; y ++) {
64 | count((y * width + x) * 4, imageData, colorsInfo);
65 | }
66 | }
67 | };
68 |
69 | var computeByImage = function (img, ignore) {
70 | var data, k, v;
71 | var color = '#ffffff';
72 | var colorAmount = 0;
73 | var colorsInfo = {};
74 | var width = img.width;
75 | var height = img.height;
76 | var ctx = canvas.getContext('2d');
77 |
78 | canvas.width = width;
79 | canvas.height = height;
80 | ctx.drawImage(img, 0, 0);
81 | data = ctx.getImageData(0, 0, width, height).data;
82 |
83 | if (ignore.indexOf(SIDE_TOP) < 0) { // don't ignore top border
84 | traverse(SIDE_TOP, colorsInfo, width, height, data);
85 | }
86 | if (ignore.indexOf(SIDE_RIGHT) < 0) { // don't ignore right border
87 | traverse(SIDE_RIGHT, colorsInfo, width, height, data);
88 | }
89 | if (ignore.indexOf(SIDE_BOTTOM) < 0) { // don't ignore bottom border
90 | traverse(SIDE_BOTTOM, colorsInfo, width, height, data);
91 | }
92 | if (ignore.indexOf(SIDE_LEFT) < 0) { // don't ignore left border
93 | traverse(SIDE_LEFT, colorsInfo, width, height, data);
94 | }
95 |
96 | for (k in colorsInfo) {
97 | v = colorsInfo[k];
98 | if (v > colorAmount) {
99 | color = k;
100 | colorAmount = v;
101 | }
102 | }
103 |
104 | return color;
105 | };
106 |
107 | var compute = function (url, ignore) {
108 | var img = new Image();
109 | var data = { url: url, ignore: ignore };
110 |
111 | img.onload = function () {
112 | try {
113 | data.color = computeByImage(this, ignore);
114 | getDfd(url).resolve(data);
115 | } catch (e) { // Error - the canvas has been tainted by cross-origin data.
116 | getDfd(url).reject(data);
117 | }
118 | img = null;
119 | };
120 | img.onerror = function () { // Error - Cross-origin image load denied
121 | img.onerror = null;
122 | img = null;
123 | getDfd(url).reject(data);
124 | };
125 |
126 | img.crossOrigin = ''; // '' is same as 'anonymous'
127 | img.src = url;
128 | };
129 |
130 | // Imgcolr.color
131 | // ---------------- core method ---------------
132 | // @param {string} options.url - The url of the image
133 | // @param {string} options.ignore - Which border should be ignored,
134 | // there are 4 kinds of values: 't', 'r', 'b', 'l', you can ignore multiple borders like this: 'tb', it's optional
135 | // @param {function} options.success - The callback for success
136 | // @param {function} options.error - The callback for error, it's optional
137 | Imgcolr.color = function (options) {
138 | var dfd = getDfd(options.url);
139 |
140 | if (typeof options.success === 'function') {
141 | dfd.done(options.success);
142 | }
143 |
144 | if (typeof options.error === 'function') {
145 | dfd.fail(options.error);
146 | }
147 |
148 | if ('pending' === dfd.state()) {
149 | compute(options.url, typeof options.ignore === 'string' ? options.ignore : '');
150 | }
151 | };
152 |
153 | Imgcolr.noConflict = function () {
154 | root.Imgcolr = previousImgcolr;
155 | return Imgcolr;
156 | };
157 |
158 | root.Imgcolr = Imgcolr;
159 |
160 | // jQuery Plugin extend - for example: $(elem).imgcolr()
161 | var pluginName = 'imgcolr';
162 |
163 | function Plugin (element, selector, options) {
164 | var elem = $(element);
165 | var ignore = elem.data('imgcolrIgnore');
166 | var defOpt = {
167 | url: element.src
168 | };
169 |
170 | if (typeof selector === 'object') {
171 | options = selector;
172 | selector = undefined;
173 | }
174 |
175 | options = $.extend(defOpt, options);
176 | // if data-imgcolr-ignore is specified on the img node, then rewrite the options
177 | if (typeof ignore === 'string') {
178 | options.ignore = ignore;
179 | }
180 |
181 | options.success = function (data) {
182 | var matches = typeof selector === 'function' ? selector.call(element, element, data.color) :
183 | typeof selector === 'string' ? elem.parents(selector) : elem.parent();
184 | // for `selector.call(element, element, data.color)` may not return a jQuery object
185 | if (matches && matches.jquery) {
186 | matches.css('backgroundColor', data.color);
187 | }
188 | };
189 |
190 | Imgcolr.color(options);
191 | }
192 |
193 | // @param selector {Selector | Function}[optional]
194 | // @param {string} options.ignore - Which border should be ignored,
195 | // there are 4 kinds of values: 't', 'r', 'b', 'l', you can ignore multiple borders like this: 'tb', it's optional
196 | $.fn[pluginName] = function (selector, options) {
197 | return this.each(function () {
198 | new Plugin(this, selector, options);
199 | });
200 | };
201 |
202 | })(this, jQuery);
--------------------------------------------------------------------------------
/as_src/Imgcolr.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 |
4 |
5 | import flash.display.Bitmap;
6 | import flash.display.BitmapData;
7 | import flash.display.Sprite;
8 | import flash.events.Event;
9 | import flash.events.HTTPStatusEvent;
10 | import flash.external.ExternalInterface;
11 | import flash.net.URLRequest;
12 | import flash.system.LoaderContext;
13 | import flash.system.Security;
14 | import flash.utils.setTimeout;
15 |
16 |
17 | /**
18 | * A tool for getting the border color of an image.
19 | * @author Sway Deng
20 | * Document Class
21 | */
22 | public class Imgcolr extends Sprite {
23 |
24 | //--------------------------------------------------------------------------
25 | // Private global constants which represent border side
26 | //--------------------------------------------------------------------------
27 | private const SIDE_TOP:String = 't'; // top
28 | private const SIDE_RIGHT:String = 'r'; // right
29 | private const SIDE_BOTTOM:String = 'b'; // bottom
30 | private const SIDE_LEFT:String = 'l'; // left
31 | private const JS_EVENT_TRIGGER:String = 'Imgcolr.trigger';
32 | private const JS_ACCESSOR:String = 'getColor'; // js can use this as the accessor
33 |
34 | //--------------------------------------------------------------------------
35 | // Private global variables
36 | //--------------------------------------------------------------------------
37 | private var _loaderCtx:LoaderContext;
38 | private var _eventTrigger:String = JS_EVENT_TRIGGER;
39 |
40 | public function Imgcolr () {
41 | setTimeout(function ():void {
42 |
43 | var paramObj:Object = root.loaderInfo.parameters;
44 | var allowedDomain:String = paramObj.allowedDomain || null;
45 | var eventHandler:String = paramObj.eventHandler || null;
46 |
47 | _loaderCtx = new LoaderContext(true);
48 |
49 | if (allowedDomain) {
50 | Security.allowDomain(allowedDomain);
51 | }
52 | if (eventHandler) {
53 | _eventTrigger = eventHandler;
54 | }
55 | // register the external interface
56 | ExternalInterface.addCallback(JS_ACCESSOR, getColor);
57 | // tell JS that swf is ready
58 | dispatchEventToJavascript( { type:'swfReady' } );
59 |
60 | }, 500);
61 | }
62 |
63 | private function getColor (url:String, ignore:String=''):void {
64 | var ldr:ImageLoader = new ImageLoader(ignore);
65 | var fileRequest:URLRequest = new URLRequest(url);
66 |
67 | ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
68 | ldr.load(fileRequest, _loaderCtx);
69 | }
70 |
71 | private function onLoadComplete (evt:Event):void {
72 | var targetLdr:ImageLoader = evt.currentTarget.loader;
73 | var ignore:String = targetLdr.ignore;
74 | var url:String = targetLdr.contentLoaderInfo.url;
75 | var data:Object = { };
76 | data.url = url;
77 | data.ignore = ignore;
78 | try {
79 | var bm:Bitmap = Bitmap(targetLdr.content);
80 | var bmd:BitmapData = bm.bitmapData;
81 | var color:String = getBorderColor(bmd, ignore);
82 | data.color = color;
83 | // Got the dominant color of the loaded image's border
84 | dispatchEventToJavascript({ type:'success', data: data });
85 | } catch (error:Error) {
86 | data.errorMsg = error.name;
87 | dispatchEventToJavascript({ type:'error', data: data });
88 | }
89 | }
90 |
91 | /**
92 | * Broadcast an event to JS
93 | * @param evtObj { type: 'eventType', data: {} }
94 | */
95 | private function dispatchEventToJavascript (evtObj:Object):void {
96 | if (ExternalInterface.available) {
97 | ExternalInterface.call(_eventTrigger, evtObj);
98 | }
99 | }
100 |
101 | ///==========================================================================================================================
102 |
103 | /**
104 | * Get the dominant color of an image's border
105 | *
106 | * @param bmd The image's bitmapData
107 | * @param ignore Which border should be ignored, there are 4 kinds of values: 't', 'r', 'b', 'l'
108 | * @return The dominant color of an image's border
109 | *
110 | */
111 | public function getBorderColor (bmd:BitmapData, ignore:String = ''):String {
112 | var mainColorNum:uint = 0;
113 | var mainColor:String = '#ffffff';
114 |
115 | var colors:Object = {};
116 |
117 | bmd.lock();
118 |
119 | if (ignore.indexOf(SIDE_TOP) < 0) { // don't ignore top border
120 | traverse(bmd, SIDE_TOP, colors);
121 | }
122 | if (ignore.indexOf(SIDE_RIGHT) < 0) { // don't ignore right border
123 | traverse(bmd, SIDE_RIGHT, colors);
124 | }
125 | if (ignore.indexOf(SIDE_BOTTOM) < 0) { // don't ignore bottom border
126 | traverse(bmd, SIDE_BOTTOM, colors);
127 | }
128 | if (ignore.indexOf(SIDE_LEFT) < 0) { // don't ignore left border
129 | traverse(bmd, SIDE_LEFT, colors);
130 | }
131 |
132 | bmd.unlock();
133 |
134 | for (var k:String in colors) {
135 | var v:uint = colors[k];
136 | if (v > mainColorNum) {
137 | mainColor = k;
138 | mainColorNum = v;
139 | }
140 | }
141 |
142 | return mainColor;
143 | }
144 | /**
145 | * Traverse the specified border's color
146 | *
147 | * @param bmd The image's bitmapData
148 | * @param side The specified border to traverse
149 | * @param colors the JSON Object that is used for storing colors' amount
150 | *
151 | */
152 | private function traverse (bmd:BitmapData, side:String, colors:Object):void {
153 | var x:uint;
154 | var y:uint;
155 | var len:uint;
156 | var width:uint = bmd.width;
157 | var height:uint = bmd.height;
158 |
159 | if (side === SIDE_TOP || side === SIDE_BOTTOM) { // side top or bottom
160 | y = (side == SIDE_TOP) ? 0 : (height - 1);
161 | for (x = 0; x < width; x++) {
162 | countColor(bmd, x, y, colors);
163 | }
164 | } else { // side right or left
165 | x = (side == SIDE_RIGHT) ? (width - 1) : 0;
166 | len = height - 1;
167 | for (y = 1; y < len; y++) {
168 | countColor(bmd, x, y, colors);
169 | }
170 | }
171 | }
172 |
173 | // Count the specified point's color
174 | private function countColor (bmd:BitmapData, x:uint, y:uint, colors:Object):void {
175 | var argb:uint = bmd.getPixel32(x, y);
176 | var alpha:uint = (argb >> 24 & 0xFF);
177 |
178 | if (alpha < 0x7f) return; // if true, means alpha value is less than 127, ignore this point
179 |
180 | var hexColor:String = '#' + argb.toString(16).slice(2); // e.g. 0xff33eedd -> 33eedd
181 |
182 | if (colors[hexColor]) {
183 | colors[hexColor] ++;
184 | } else {
185 | colors[hexColor] = 1;
186 | }
187 | }
188 | }
189 | }
--------------------------------------------------------------------------------
/imgcolr.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * imgcolr v1.0.1
3 | * Author: Sway Deng
4 | * Released under the MIT license
5 | */
6 | (function (window, $, undefined) {
7 | // shortcut for Imgcolr
8 | var Imgcolr = {};
9 |
10 |
11 |
12 | /****** some constants ******/
13 | // event name
14 | var EVT_SWFREADY = 'swfReady';
15 | var EVT_SUCCESS = 'success';
16 | var EVT_ERROR = 'error';
17 | // swf dfd keyname
18 | var DFD_SWF = 'dfd-swf';
19 |
20 | // swf file url, swf object
21 | var swfUrl, swfObj;
22 | // all deferred objects cache
23 | var dfdCache = {};
24 | // get or cache Deferred objects
25 | var getDfd = function (key) {
26 | var dfd = dfdCache[key];
27 | if (!dfd) {
28 | dfd = $.Deferred();
29 | dfdCache[key] = dfd;
30 | }
31 | return dfd;
32 | };
33 |
34 | // swf method
35 | var compute = function (url, ignore) {
36 | var style = 'position:absolute; left:0; top:0; width:1px; height:1px;';
37 | var swfNode = $('
').appendTo('body');
38 |
39 | swfObj = appendFlash(swfNode, {
40 | width: 1,
41 | height: 1,
42 | wmode: 'transparent',
43 | swf: swfUrl,
44 | allowScriptAccess: 'always',
45 | flashvars: {
46 | allowedDomain: window.location.hostname
47 | }
48 | });
49 |
50 | compute = function (url, ignore) {
51 | getDfd(DFD_SWF).done(function (obj) {
52 | obj.getColor(url, ignore);
53 | });
54 | };
55 |
56 | compute(url, ignore);
57 | };
58 | // You must specify the swf url according to your scenario before using this.
59 | Imgcolr.setSwf = function (url) {
60 | swfUrl = url;
61 | };
62 |
63 | Imgcolr.getSwf = function () {
64 | return swfUrl;
65 | };
66 |
67 | // @private - very important, this method is called from swf internally
68 | Imgcolr.trigger = function (evtObj) {
69 | switch (evtObj.type) {
70 | case EVT_SWFREADY:
71 | getDfd(DFD_SWF).resolve(swfObj);
72 | break;
73 | case EVT_SUCCESS:
74 | getDfd(evtObj.data.url).resolve(evtObj.data);
75 | break;
76 | case EVT_ERROR:
77 | getDfd(evtObj.data.url).reject(evtObj.data);
78 | break;
79 | }
80 | };
81 | // Imgcolr.color
82 | // ---------------- core method ---------------
83 | // @param {string} options.url - The url of the image
84 | // @param {string} options.ignore - Which border should be ignored,
85 | // there are 4 kinds of values: 't', 'r', 'b', 'l', you can ignore multiple borders like this: 'tb', it's optional
86 | // @param {function} options.success - The callback for success
87 | // @param {function} options.error - The callback for error, it's optional
88 | Imgcolr.color = function (options) {
89 | var dfd = getDfd(options.url);
90 |
91 | if (typeof options.success === 'function') {
92 | dfd.done(options.success);
93 | }
94 |
95 | if (typeof options.error === 'function') {
96 | dfd.fail(options.error);
97 | }
98 |
99 | if ('pending' === dfd.state()) {
100 | compute(options.url, typeof options.ignore === 'string' ? options.ignore : '');
101 | }
102 | };
103 |
104 |
105 |
106 |
107 | var appendFlash = (function () {
108 | var version, versionNumbers, flashAvailable;
109 | // Prototype style
110 | // https://github.com/sstephenson/prototype/blob/1fb9728/src/prototype.js#L81
111 | var isIE = !!window.attachEvent && Object.prototype.toString.call(window.opera) !== '[object Opera]';
112 |
113 | var Plugin = navigator.plugins['Shockwave Flash'] || window.ActiveXObject;
114 |
115 | try {
116 | if (Plugin.description) {
117 | version = Plugin.description;
118 | } else {
119 | version = (new Plugin('ShockwaveFlash.ShockwaveFlash')).GetVariable('$version');
120 | }
121 | } catch (e) {
122 | version = 'Unavailable';
123 | }
124 | versionNumbers = version.match(/\d+/g);
125 | flashAvailable = !!versionNumbers && versionNumbers[0] > 0;
126 |
127 | function buildQueryString (obj) {
128 | if (!$.isPlainObject(obj)) {
129 | return obj;
130 | }
131 |
132 | var k, v;
133 | var arr = [];
134 | var str = '';
135 |
136 | for (k in obj) {
137 | v = obj[k];
138 | if ($.isPlainObject(v)) {
139 | str = buildQueryString(v);
140 | } else {
141 | str = [k, encodeURI(v)].join('=');
142 | }
143 | arr.push(str);
144 | }
145 |
146 | return arr.join('&');
147 | }
148 |
149 | // build attributes based on an object
150 | function buildAttr (obj) {
151 | var k, v;
152 | var arr = [];
153 |
154 | for (k in obj) {
155 | v = obj[k];
156 | if (v) {
157 | arr.push([k, '="', v, '"'].join(''));
158 | }
159 | }
160 |
161 | return arr.join(' ');
162 | }
163 |
164 | function buildParamTag (obj) {
165 | var arr = [];
166 |
167 | for (var k in obj) {
168 | arr.push(['
'].join(''));
169 | }
170 |
171 | return arr.join('');
172 | }
173 |
174 | // return the real method to append a flash object
175 | return function (elem, options) {
176 | var attrs;
177 |
178 | if (!options.swf || !flashAvailable) {
179 | return false;
180 | }
181 |
182 | attrs = {
183 | id: 'swf-' + ($.guid++),
184 | width: options.width || 1,
185 | height: options.height || 1,
186 | style: options.style || ''
187 | };
188 |
189 | if (isIE) {
190 | attrs.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
191 | options.movie = options.swf;
192 | } else {
193 | attrs.data = options.swf;
194 | attrs.type = 'application/x-shockwave-flash';
195 | }
196 |
197 | options.wmode = options.wmode || 'opaque';
198 |
199 | var html = ['
'].join('');
200 | if (isIE) {
201 | var flashContainer = document.createElement('div');
202 | elem.html(flashContainer);
203 | flashContainer.outerHTML = html;
204 | } else {
205 | elem.html(html);
206 | }
207 |
208 | return elem.children().get(0);
209 | };
210 | })();
211 |
212 |
213 |
214 |
215 | // The top-level namespace, so that so that the method Imgcolr.color can be invoked from swf
216 | window.Imgcolr = Imgcolr;
217 |
218 |
219 |
220 |
221 | // jQuery Plugin - for example: $(elem).imgcolr()
222 | var pluginName = 'imgcolr';
223 |
224 | function Plugin (element, selector, options) {
225 | var elem = $(element);
226 | var ignore = elem.data('imgcolrIgnore');
227 | var defOpt = {
228 | url: element.src
229 | };
230 |
231 | if (typeof selector === 'object') {
232 | options = selector;
233 | selector = undefined;
234 | }
235 |
236 | options = $.extend(defOpt, options);
237 | // if data-imgcolr-ignore is specified on the img node, then rewrite the options
238 | if (typeof ignore === 'string') {
239 | options.ignore = ignore;
240 | }
241 |
242 | options.success = function (data) {
243 | var matches = typeof selector === 'function' ? selector.call(element, element, data.color) :
244 | typeof selector === 'string' ? elem.parents(selector) : elem.parent();
245 | // for `selector.call(element, element, data.color)` may not return a jQuery object
246 | if (matches && matches.jquery) {
247 | matches.css('backgroundColor', data.color);
248 | }
249 | };
250 |
251 | Imgcolr.color(options);
252 | }
253 |
254 | // @param selector {Selector | Function}[optional]
255 | // @param {string} options.ignore - Which border should be ignored,
256 | // there are 4 kinds of values: 't', 'r', 'b', 'l', you can ignore multiple borders like this: 'tb', it's optional
257 | $.fn[pluginName] = function (selector, options) {
258 | return this.each(function () {
259 | new Plugin(this, selector, options);
260 | });
261 | };
262 |
263 |
264 |
265 |
266 | // AMD module support
267 | Imgcolr.imgcolr = function (elem, selector, options) {
268 | $(elem).imgcolr(selector, options);
269 | };
270 |
271 | if (typeof define === 'function' && define.amd) {
272 | define('imgcolr', [], function () { return Imgcolr; });
273 | }
274 |
275 | })(window, jQuery);
--------------------------------------------------------------------------------