├── LICENSE ├── README.md ├── background.js ├── content.js ├── docs ├── blue_demo.png ├── demo.gif.gif ├── newer-logo.xcf ├── purple_demo.png ├── red_demo.png └── yellow_demo.png ├── icons ├── logo.png ├── logo_128x128.png ├── logo_16x16.png └── logo_48x48.png ├── jquery.js ├── jscolor.js ├── manifest.json ├── plugin.zip ├── plugin ├── background.js ├── content.js ├── icons │ ├── logo.png │ ├── logo_128x128.png │ ├── logo_16x16.png │ └── logo_48x48.png ├── jquery.js ├── jscolor.js ├── manifest.json ├── popup.html └── popup.js ├── popup.html └── popup.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Patrick Scott 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | GitHub Heatmap Colorizer 3 |

4 | 5 |

6 | Colorize Github Contribution Heatmaps 7 |

8 | 9 | :star: Overview 10 | -------- 11 | 12 | A Chrome extension that changes the colors of contribution heatmaps in GitHub. 13 | 14 | Features: 15 | 16 | * Colorize contribution graph and legend 17 | * Colorize activity overview graph 18 | 19 | :wrench: Install 20 | ------- 21 | 1. Download the [plugin files](https://github.com/pkellz/github-heatmap-colorizer/blob/master/plugin.zip?raw=true) 22 | 2. Unzip the folder 23 | 3. Visit `chrome://extensions` 24 | 4. Make sure 'Developer Mode' is enabled 25 | 5. Drag and drop the unzipped folder 26 | 27 | :camera: Screenshots 28 | ----------- 29 | Blue | Red 30 | :----------------------------------:|:--------------------------------------: 31 | ![Blue](https://github.com/pkellz/github-heatmap-colorizer/blob/master/docs/blue_demo.png?raw=true)|![Red](https://github.com/pkellz/github-heatmap-colorizer/blob/master/docs/red_demo.png?raw=true) 32 | 33 | Purple | Yellow 34 | :----------------------------------:|:--------------------------------------: 35 | ![Purple](https://github.com/pkellz/github-heatmap-colorizer/blob/master/docs/purple_demo.png?raw=true)|![Yellow](https://github.com/pkellz/github-heatmap-colorizer/blob/master/docs/yellow_demo.png?raw=true) 36 | 37 | ## :hammer: Usage 38 | ![Purple](https://github.com/pkellz/github-heatmap-colorizer/blob/master/docs/demo.gif.gif?raw=true) 39 | 40 | :family: Contributing 41 | ------------ 42 | 43 | Contributions are welcome! 44 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // Activate plugin on every tab update 2 | chrome.tabs.onUpdated.addListener(() => { 3 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 4 | chrome.tabs.executeScript( 5 | tabs[0].id, 6 | { file: 'content.js' }); 7 | }); 8 | }) -------------------------------------------------------------------------------- /content.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | // If on someone's Github profile 3 | const activityContainer = document.querySelector('.graph-before-activity-overview') 4 | if(activityContainer) 5 | initColorize() 6 | })() 7 | 8 | function initColorize() 9 | { 10 | chrome.storage.local.get('color', function(data) { 11 | if(!data.color) 12 | data.color = '4594A8' 13 | let mainColorHex = `#${data.color}` 14 | 15 | const legendItems = document.querySelectorAll('.contrib-legend ul li') 16 | const originalColors = [] 17 | for(let li of legendItems) 18 | originalColors.push(li.getAttribute('style').split(':')[1].trim()) 19 | 20 | colorizeActivity(mainColorHex) 21 | colorizeLegend(mainColorHex, originalColors, legendItems) 22 | colorizeDays(mainColorHex, originalColors) 23 | }); 24 | } 25 | 26 | function colorizeActivity(color) 27 | { 28 | const activity = document.querySelector('.js-highlight-blob') 29 | if(activity) 30 | { 31 | const axes = document.querySelectorAll('.activity-overview-axis') 32 | const ellipses = document.querySelectorAll('.activity-overview-point') 33 | const blob = document.querySelector('.js-highlight-blob') 34 | activity.setAttribute('stroke', color) 35 | blob.style.fill = color 36 | for(let ellipse of ellipses) 37 | ellipse.style.stroke = color 38 | for(let axis of axes) 39 | axis.style.stroke = color 40 | } 41 | } 42 | 43 | function colorizeLegend(mainColor, originalColors, legendItems) 44 | { 45 | legendItems[0].setAttribute('style', `background-color:${originalColors[0]}`) 46 | legendItems[1].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.9)}`) 47 | legendItems[2].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.75)}`) 48 | legendItems[3].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.5)}`) 49 | legendItems[4].setAttribute('style', `background-color:${mainColor}`) 50 | } 51 | 52 | function colorizeDays(mainColor, originalColors) 53 | { 54 | let days = Array.from(document.getElementsByClassName('day')) 55 | 56 | const dayGroups = { 57 | 0: days.filter( day => day.getAttribute('fill') == originalColors[0]), 58 | 1: days.filter( day => day.getAttribute('fill') == originalColors[1]), 59 | 2: days.filter( day => day.getAttribute('fill') == originalColors[2]), 60 | 3: days.filter( day => day.getAttribute('fill') == originalColors[3]), 61 | 4: days.filter( day => day.getAttribute('fill') == originalColors[4]) 62 | } 63 | 64 | dayGroups[0].forEach(day => day.setAttribute('fill', originalColors[0])) 65 | dayGroups[1].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.9))) 66 | dayGroups[2].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.75))) 67 | dayGroups[3].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.5))) 68 | dayGroups[4].forEach(day => day.setAttribute('fill', mainColor)) 69 | } 70 | 71 | function colorLuminance(hex, lum) { 72 | // validate hex string 73 | hex = String(hex).replace(/[^0-9a-f]/gi, ''); 74 | if (hex.length < 6) { 75 | hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; 76 | } 77 | lum = lum || 0; 78 | 79 | // convert to decimal and change luminosity 80 | let rgb = "#", c, i; 81 | for (i = 0; i < 3; i++) { 82 | c = parseInt(hex.substr(i*2,2), 16); 83 | c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); 84 | rgb += ("00"+c).substr(c.length); 85 | } 86 | return rgb; 87 | } 88 | -------------------------------------------------------------------------------- /docs/blue_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/blue_demo.png -------------------------------------------------------------------------------- /docs/demo.gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/demo.gif.gif -------------------------------------------------------------------------------- /docs/newer-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/newer-logo.xcf -------------------------------------------------------------------------------- /docs/purple_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/purple_demo.png -------------------------------------------------------------------------------- /docs/red_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/red_demo.png -------------------------------------------------------------------------------- /docs/yellow_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/docs/yellow_demo.png -------------------------------------------------------------------------------- /icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/icons/logo.png -------------------------------------------------------------------------------- /icons/logo_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/icons/logo_128x128.png -------------------------------------------------------------------------------- /icons/logo_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/icons/logo_16x16.png -------------------------------------------------------------------------------- /icons/logo_48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/icons/logo_48x48.png -------------------------------------------------------------------------------- /jquery.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0 vs[a] ? 448 | (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : 449 | tp[a], 450 | -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 451 | (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : 452 | (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 453 | ]; 454 | } 455 | 456 | var x = pp[a]; 457 | var y = pp[b]; 458 | var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; 459 | var contractShadow = 460 | (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && 461 | (pp[1] + ps[1] < tp[1] + ts[1]); 462 | 463 | jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); 464 | } 465 | }, 466 | 467 | 468 | _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { 469 | var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px 470 | 471 | jsc.picker.wrap.style.position = positionValue; 472 | jsc.picker.wrap.style.left = x + 'px'; 473 | jsc.picker.wrap.style.top = y + 'px'; 474 | 475 | jsc.setBoxShadow( 476 | jsc.picker.boxS, 477 | thisObj.shadow ? 478 | new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : 479 | null); 480 | }, 481 | 482 | 483 | getPickerDims : function (thisObj) { 484 | var displaySlider = !!jsc.getSliderComponent(thisObj); 485 | var dims = [ 486 | 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width + 487 | (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0), 488 | 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height + 489 | (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0) 490 | ]; 491 | return dims; 492 | }, 493 | 494 | 495 | getPickerOuterDims : function (thisObj) { 496 | var dims = jsc.getPickerDims(thisObj); 497 | return [ 498 | dims[0] + 2 * thisObj.borderWidth, 499 | dims[1] + 2 * thisObj.borderWidth 500 | ]; 501 | }, 502 | 503 | 504 | getPadToSliderPadding : function (thisObj) { 505 | return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness)); 506 | }, 507 | 508 | 509 | getPadYComponent : function (thisObj) { 510 | switch (thisObj.mode.charAt(1).toLowerCase()) { 511 | case 'v': return 'v'; break; 512 | } 513 | return 's'; 514 | }, 515 | 516 | 517 | getSliderComponent : function (thisObj) { 518 | if (thisObj.mode.length > 2) { 519 | switch (thisObj.mode.charAt(2).toLowerCase()) { 520 | case 's': return 's'; break; 521 | case 'v': return 'v'; break; 522 | } 523 | } 524 | return null; 525 | }, 526 | 527 | 528 | onDocumentMouseDown : function (e) { 529 | if (!e) { e = window.event; } 530 | var target = e.target || e.srcElement; 531 | 532 | if (target._jscLinkedInstance) { 533 | if (target._jscLinkedInstance.showOnClick) { 534 | target._jscLinkedInstance.show(); 535 | } 536 | } else if (target._jscControlName) { 537 | jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse'); 538 | } else { 539 | // Mouse is outside the picker controls -> hide the color picker! 540 | if (jsc.picker && jsc.picker.owner) { 541 | jsc.picker.owner.hide(); 542 | } 543 | } 544 | }, 545 | 546 | 547 | onDocumentTouchStart : function (e) { 548 | if (!e) { e = window.event; } 549 | var target = e.target || e.srcElement; 550 | 551 | if (target._jscLinkedInstance) { 552 | if (target._jscLinkedInstance.showOnClick) { 553 | target._jscLinkedInstance.show(); 554 | } 555 | } else if (target._jscControlName) { 556 | jsc.onControlPointerStart(e, target, target._jscControlName, 'touch'); 557 | } else { 558 | if (jsc.picker && jsc.picker.owner) { 559 | jsc.picker.owner.hide(); 560 | } 561 | } 562 | }, 563 | 564 | 565 | onWindowResize : function (e) { 566 | jsc.redrawPosition(); 567 | }, 568 | 569 | 570 | onParentScroll : function (e) { 571 | // hide the picker when one of the parent elements is scrolled 572 | if (jsc.picker && jsc.picker.owner) { 573 | jsc.picker.owner.hide(); 574 | } 575 | }, 576 | 577 | 578 | _pointerMoveEvent : { 579 | mouse: 'mousemove', 580 | touch: 'touchmove' 581 | }, 582 | _pointerEndEvent : { 583 | mouse: 'mouseup', 584 | touch: 'touchend' 585 | }, 586 | 587 | 588 | _pointerOrigin : null, 589 | _capturedTarget : null, 590 | 591 | 592 | onControlPointerStart : function (e, target, controlName, pointerType) { 593 | var thisObj = target._jscInstance; 594 | 595 | jsc.preventDefault(e); 596 | jsc.captureTarget(target); 597 | 598 | var registerDragEvents = function (doc, offset) { 599 | jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], 600 | jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); 601 | jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], 602 | jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); 603 | }; 604 | 605 | registerDragEvents(document, [0, 0]); 606 | 607 | if (window.parent && window.frameElement) { 608 | var rect = window.frameElement.getBoundingClientRect(); 609 | var ofs = [-rect.left, -rect.top]; 610 | registerDragEvents(window.parent.window.document, ofs); 611 | } 612 | 613 | var abs = jsc.getAbsPointerPos(e); 614 | var rel = jsc.getRelPointerPos(e); 615 | jsc._pointerOrigin = { 616 | x: abs.x - rel.x, 617 | y: abs.y - rel.y 618 | }; 619 | 620 | switch (controlName) { 621 | case 'pad': 622 | // if the slider is at the bottom, move it up 623 | switch (jsc.getSliderComponent(thisObj)) { 624 | case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break; 625 | case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break; 626 | } 627 | jsc.setPad(thisObj, e, 0, 0); 628 | break; 629 | 630 | case 'sld': 631 | jsc.setSld(thisObj, e, 0); 632 | break; 633 | } 634 | 635 | jsc.dispatchFineChange(thisObj); 636 | }, 637 | 638 | 639 | onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { 640 | return function (e) { 641 | var thisObj = target._jscInstance; 642 | switch (controlName) { 643 | case 'pad': 644 | if (!e) { e = window.event; } 645 | jsc.setPad(thisObj, e, offset[0], offset[1]); 646 | jsc.dispatchFineChange(thisObj); 647 | break; 648 | 649 | case 'sld': 650 | if (!e) { e = window.event; } 651 | jsc.setSld(thisObj, e, offset[1]); 652 | jsc.dispatchFineChange(thisObj); 653 | break; 654 | } 655 | } 656 | }, 657 | 658 | 659 | onDocumentPointerEnd : function (e, target, controlName, pointerType) { 660 | return function (e) { 661 | var thisObj = target._jscInstance; 662 | jsc.detachGroupEvents('drag'); 663 | jsc.releaseTarget(); 664 | // Always dispatch changes after detaching outstanding mouse handlers, 665 | // in case some user interaction will occur in user's onchange callback 666 | // that would intrude with current mouse events 667 | jsc.dispatchChange(thisObj); 668 | }; 669 | }, 670 | 671 | 672 | dispatchChange : function (thisObj) { 673 | if (thisObj.valueElement) { 674 | if (jsc.isElementType(thisObj.valueElement, 'input')) { 675 | jsc.fireEvent(thisObj.valueElement, 'change'); 676 | } 677 | } 678 | }, 679 | 680 | 681 | dispatchFineChange : function (thisObj) { 682 | if (thisObj.onFineChange) { 683 | var callback; 684 | if (typeof thisObj.onFineChange === 'string') { 685 | callback = new Function (thisObj.onFineChange); 686 | } else { 687 | callback = thisObj.onFineChange; 688 | } 689 | callback.call(thisObj); 690 | } 691 | }, 692 | 693 | 694 | setPad : function (thisObj, e, ofsX, ofsY) { 695 | var pointerAbs = jsc.getAbsPointerPos(e); 696 | var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth; 697 | var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 698 | 699 | var xVal = x * (360 / (thisObj.width - 1)); 700 | var yVal = 100 - (y * (100 / (thisObj.height - 1))); 701 | 702 | switch (jsc.getPadYComponent(thisObj)) { 703 | case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break; 704 | case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break; 705 | } 706 | }, 707 | 708 | 709 | setSld : function (thisObj, e, ofsY) { 710 | var pointerAbs = jsc.getAbsPointerPos(e); 711 | var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 712 | 713 | var yVal = 100 - (y * (100 / (thisObj.height - 1))); 714 | 715 | switch (jsc.getSliderComponent(thisObj)) { 716 | case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break; 717 | case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break; 718 | } 719 | }, 720 | 721 | 722 | _vmlNS : 'jsc_vml_', 723 | _vmlCSS : 'jsc_vml_css_', 724 | _vmlReady : false, 725 | 726 | 727 | initVML : function () { 728 | if (!jsc._vmlReady) { 729 | // init VML namespace 730 | var doc = document; 731 | if (!doc.namespaces[jsc._vmlNS]) { 732 | doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml'); 733 | } 734 | if (!doc.styleSheets[jsc._vmlCSS]) { 735 | var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image']; 736 | var ss = doc.createStyleSheet(); 737 | ss.owningElement.id = jsc._vmlCSS; 738 | for (var i = 0; i < tags.length; i += 1) { 739 | ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);'); 740 | } 741 | } 742 | jsc._vmlReady = true; 743 | } 744 | }, 745 | 746 | 747 | createPalette : function () { 748 | 749 | var paletteObj = { 750 | elm: null, 751 | draw: null 752 | }; 753 | 754 | if (jsc.isCanvasSupported) { 755 | // Canvas implementation for modern browsers 756 | 757 | var canvas = document.createElement('canvas'); 758 | var ctx = canvas.getContext('2d'); 759 | 760 | var drawFunc = function (width, height, type) { 761 | canvas.width = width; 762 | canvas.height = height; 763 | 764 | ctx.clearRect(0, 0, canvas.width, canvas.height); 765 | 766 | var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); 767 | hGrad.addColorStop(0 / 6, '#F00'); 768 | hGrad.addColorStop(1 / 6, '#FF0'); 769 | hGrad.addColorStop(2 / 6, '#0F0'); 770 | hGrad.addColorStop(3 / 6, '#0FF'); 771 | hGrad.addColorStop(4 / 6, '#00F'); 772 | hGrad.addColorStop(5 / 6, '#F0F'); 773 | hGrad.addColorStop(6 / 6, '#F00'); 774 | 775 | ctx.fillStyle = hGrad; 776 | ctx.fillRect(0, 0, canvas.width, canvas.height); 777 | 778 | var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); 779 | switch (type.toLowerCase()) { 780 | case 's': 781 | vGrad.addColorStop(0, 'rgba(255,255,255,0)'); 782 | vGrad.addColorStop(1, 'rgba(255,255,255,1)'); 783 | break; 784 | case 'v': 785 | vGrad.addColorStop(0, 'rgba(0,0,0,0)'); 786 | vGrad.addColorStop(1, 'rgba(0,0,0,1)'); 787 | break; 788 | } 789 | ctx.fillStyle = vGrad; 790 | ctx.fillRect(0, 0, canvas.width, canvas.height); 791 | }; 792 | 793 | paletteObj.elm = canvas; 794 | paletteObj.draw = drawFunc; 795 | 796 | } else { 797 | // VML fallback for IE 7 and 8 798 | 799 | jsc.initVML(); 800 | 801 | var vmlContainer = document.createElement('div'); 802 | vmlContainer.style.position = 'relative'; 803 | vmlContainer.style.overflow = 'hidden'; 804 | 805 | var hGrad = document.createElement(jsc._vmlNS + ':fill'); 806 | hGrad.type = 'gradient'; 807 | hGrad.method = 'linear'; 808 | hGrad.angle = '90'; 809 | hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0' 810 | 811 | var hRect = document.createElement(jsc._vmlNS + ':rect'); 812 | hRect.style.position = 'absolute'; 813 | hRect.style.left = -1 + 'px'; 814 | hRect.style.top = -1 + 'px'; 815 | hRect.stroked = false; 816 | hRect.appendChild(hGrad); 817 | vmlContainer.appendChild(hRect); 818 | 819 | var vGrad = document.createElement(jsc._vmlNS + ':fill'); 820 | vGrad.type = 'gradient'; 821 | vGrad.method = 'linear'; 822 | vGrad.angle = '180'; 823 | vGrad.opacity = '0'; 824 | 825 | var vRect = document.createElement(jsc._vmlNS + ':rect'); 826 | vRect.style.position = 'absolute'; 827 | vRect.style.left = -1 + 'px'; 828 | vRect.style.top = -1 + 'px'; 829 | vRect.stroked = false; 830 | vRect.appendChild(vGrad); 831 | vmlContainer.appendChild(vRect); 832 | 833 | var drawFunc = function (width, height, type) { 834 | vmlContainer.style.width = width + 'px'; 835 | vmlContainer.style.height = height + 'px'; 836 | 837 | hRect.style.width = 838 | vRect.style.width = 839 | (width + 1) + 'px'; 840 | hRect.style.height = 841 | vRect.style.height = 842 | (height + 1) + 'px'; 843 | 844 | // Colors must be specified during every redraw, otherwise IE won't display 845 | // a full gradient during a subsequential redraw 846 | hGrad.color = '#F00'; 847 | hGrad.color2 = '#F00'; 848 | 849 | switch (type.toLowerCase()) { 850 | case 's': 851 | vGrad.color = vGrad.color2 = '#FFF'; 852 | break; 853 | case 'v': 854 | vGrad.color = vGrad.color2 = '#000'; 855 | break; 856 | } 857 | }; 858 | 859 | paletteObj.elm = vmlContainer; 860 | paletteObj.draw = drawFunc; 861 | } 862 | 863 | return paletteObj; 864 | }, 865 | 866 | 867 | createSliderGradient : function () { 868 | 869 | var sliderObj = { 870 | elm: null, 871 | draw: null 872 | }; 873 | 874 | if (jsc.isCanvasSupported) { 875 | // Canvas implementation for modern browsers 876 | 877 | var canvas = document.createElement('canvas'); 878 | var ctx = canvas.getContext('2d'); 879 | 880 | var drawFunc = function (width, height, color1, color2) { 881 | canvas.width = width; 882 | canvas.height = height; 883 | 884 | ctx.clearRect(0, 0, canvas.width, canvas.height); 885 | 886 | var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); 887 | grad.addColorStop(0, color1); 888 | grad.addColorStop(1, color2); 889 | 890 | ctx.fillStyle = grad; 891 | ctx.fillRect(0, 0, canvas.width, canvas.height); 892 | }; 893 | 894 | sliderObj.elm = canvas; 895 | sliderObj.draw = drawFunc; 896 | 897 | } else { 898 | // VML fallback for IE 7 and 8 899 | 900 | jsc.initVML(); 901 | 902 | var vmlContainer = document.createElement('div'); 903 | vmlContainer.style.position = 'relative'; 904 | vmlContainer.style.overflow = 'hidden'; 905 | 906 | var grad = document.createElement(jsc._vmlNS + ':fill'); 907 | grad.type = 'gradient'; 908 | grad.method = 'linear'; 909 | grad.angle = '180'; 910 | 911 | var rect = document.createElement(jsc._vmlNS + ':rect'); 912 | rect.style.position = 'absolute'; 913 | rect.style.left = -1 + 'px'; 914 | rect.style.top = -1 + 'px'; 915 | rect.stroked = false; 916 | rect.appendChild(grad); 917 | vmlContainer.appendChild(rect); 918 | 919 | var drawFunc = function (width, height, color1, color2) { 920 | vmlContainer.style.width = width + 'px'; 921 | vmlContainer.style.height = height + 'px'; 922 | 923 | rect.style.width = (width + 1) + 'px'; 924 | rect.style.height = (height + 1) + 'px'; 925 | 926 | grad.color = color1; 927 | grad.color2 = color2; 928 | }; 929 | 930 | sliderObj.elm = vmlContainer; 931 | sliderObj.draw = drawFunc; 932 | } 933 | 934 | return sliderObj; 935 | }, 936 | 937 | 938 | leaveValue : 1<<0, 939 | leaveStyle : 1<<1, 940 | leavePad : 1<<2, 941 | leaveSld : 1<<3, 942 | 943 | 944 | BoxShadow : (function () { 945 | var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { 946 | this.hShadow = hShadow; 947 | this.vShadow = vShadow; 948 | this.blur = blur; 949 | this.spread = spread; 950 | this.color = color; 951 | this.inset = !!inset; 952 | }; 953 | 954 | BoxShadow.prototype.toString = function () { 955 | var vals = [ 956 | Math.round(this.hShadow) + 'px', 957 | Math.round(this.vShadow) + 'px', 958 | Math.round(this.blur) + 'px', 959 | Math.round(this.spread) + 'px', 960 | this.color 961 | ]; 962 | if (this.inset) { 963 | vals.push('inset'); 964 | } 965 | return vals.join(' '); 966 | }; 967 | 968 | return BoxShadow; 969 | })(), 970 | 971 | 972 | // 973 | // Usage: 974 | // var myColor = new jscolor( [, ]) 975 | // 976 | 977 | jscolor : function (targetElement, options) { 978 | 979 | // General options 980 | // 981 | this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB() 982 | this.valueElement = targetElement; // element that will be used to display and input the color code 983 | this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor 984 | this.required = true; // whether the associated text can be left empty 985 | this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace) 986 | this.hash = false; // whether to prefix the HEX color code with # symbol 987 | this.uppercase = true; // whether to show the color code in upper case 988 | this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code) 989 | this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it 990 | this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important 991 | this.minS = 0; // min allowed saturation (0 - 100) 992 | this.maxS = 100; // max allowed saturation (0 - 100) 993 | this.minV = 1; // min allowed value (brightness) (0 - 100) 994 | this.maxV = 100; // max allowed value (brightness) (0 - 100) 995 | 996 | // Accessing the picked color 997 | // 998 | this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] 999 | this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] 1000 | 1001 | // Color Picker options 1002 | // 1003 | this.width = 181; // width of color palette (in px) 1004 | this.height = 101; // height of color palette (in px) 1005 | this.showOnClick = true; // whether to display the color picker when user clicks on its target element 1006 | this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls 1007 | this.position = 'bottom'; // left | right | top | bottom - position relative to the target element 1008 | this.smartPosition = true; // automatically change picker position when there is not enough space for it 1009 | this.sliderSize = 16; // px 1010 | this.crossSize = 8; // px 1011 | this.closable = false; // whether to display the Close button 1012 | this.closeText = 'Close'; 1013 | this.buttonColor = '#000000'; // CSS color 1014 | this.buttonHeight = 18; // px 1015 | this.padding = 12; // px 1016 | this.backgroundColor = '#FFFFFF'; // CSS color 1017 | this.borderWidth = 1; // px 1018 | this.borderColor = '#BBBBBB'; // CSS color 1019 | this.borderRadius = 8; // px 1020 | this.insetWidth = 1; // px 1021 | this.insetColor = '#BBBBBB'; // CSS color 1022 | this.shadow = true; // whether to display shadow 1023 | this.shadowBlur = 15; // px 1024 | this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color 1025 | this.pointerColor = '#4C4C4C'; // px 1026 | this.pointerBorderColor = '#FFFFFF'; // px 1027 | this.pointerBorderWidth = 1; // px 1028 | this.pointerThickness = 2; // px 1029 | this.zIndex = 1000; 1030 | this.container = null; // where to append the color picker (BODY element by default) 1031 | 1032 | 1033 | for (var opt in options) { 1034 | if (options.hasOwnProperty(opt)) { 1035 | this[opt] = options[opt]; 1036 | } 1037 | } 1038 | 1039 | 1040 | this.hide = function () { 1041 | if (isPickerOwner()) { 1042 | detachPicker(); 1043 | } 1044 | }; 1045 | 1046 | 1047 | this.show = function () { 1048 | drawPicker(); 1049 | }; 1050 | 1051 | 1052 | this.redraw = function () { 1053 | if (isPickerOwner()) { 1054 | drawPicker(); 1055 | } 1056 | }; 1057 | 1058 | 1059 | this.importColor = function () { 1060 | if (!this.valueElement) { 1061 | this.exportColor(); 1062 | } else { 1063 | if (jsc.isElementType(this.valueElement, 'input')) { 1064 | if (!this.refine) { 1065 | if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { 1066 | if (this.styleElement) { 1067 | this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1068 | this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1069 | this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1070 | } 1071 | this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1072 | } 1073 | } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { 1074 | this.valueElement.value = ''; 1075 | if (this.styleElement) { 1076 | this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1077 | this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1078 | this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1079 | } 1080 | this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1081 | 1082 | } else if (this.fromString(this.valueElement.value)) { 1083 | // managed to import color successfully from the value -> OK, don't do anything 1084 | } else { 1085 | this.exportColor(); 1086 | } 1087 | } else { 1088 | // not an input element -> doesn't have any value 1089 | this.exportColor(); 1090 | } 1091 | } 1092 | }; 1093 | 1094 | 1095 | this.exportColor = function (flags) { 1096 | if (!(flags & jsc.leaveValue) && this.valueElement) { 1097 | var value = this.toString(); 1098 | if (this.uppercase) { value = value.toUpperCase(); } 1099 | if (this.hash) { value = '#' + value; } 1100 | 1101 | if (jsc.isElementType(this.valueElement, 'input')) { 1102 | this.valueElement.value = value; 1103 | } else { 1104 | this.valueElement.innerHTML = value; 1105 | } 1106 | } 1107 | if (!(flags & jsc.leaveStyle)) { 1108 | if (this.styleElement) { 1109 | var bgColor = '#' + this.toString(); 1110 | var fgColor = this.isLight() ? '#000' : '#FFF'; 1111 | 1112 | this.styleElement.style.backgroundImage = 'none'; 1113 | this.styleElement.style.backgroundColor = bgColor; 1114 | this.styleElement.style.color = fgColor; 1115 | 1116 | if (this.overwriteImportant) { 1117 | this.styleElement.setAttribute('style', 1118 | 'background: ' + bgColor + ' !important; ' + 1119 | 'color: ' + fgColor + ' !important;' 1120 | ); 1121 | } 1122 | } 1123 | } 1124 | if (!(flags & jsc.leavePad) && isPickerOwner()) { 1125 | redrawPad(); 1126 | } 1127 | if (!(flags & jsc.leaveSld) && isPickerOwner()) { 1128 | redrawSld(); 1129 | } 1130 | }; 1131 | 1132 | 1133 | // h: 0-360 1134 | // s: 0-100 1135 | // v: 0-100 1136 | // 1137 | this.fromHSV = function (h, s, v, flags) { // null = don't change 1138 | if (h !== null) { 1139 | if (isNaN(h)) { return false; } 1140 | h = Math.max(0, Math.min(360, h)); 1141 | } 1142 | if (s !== null) { 1143 | if (isNaN(s)) { return false; } 1144 | s = Math.max(0, Math.min(100, this.maxS, s), this.minS); 1145 | } 1146 | if (v !== null) { 1147 | if (isNaN(v)) { return false; } 1148 | v = Math.max(0, Math.min(100, this.maxV, v), this.minV); 1149 | } 1150 | 1151 | this.rgb = HSV_RGB( 1152 | h===null ? this.hsv[0] : (this.hsv[0]=h), 1153 | s===null ? this.hsv[1] : (this.hsv[1]=s), 1154 | v===null ? this.hsv[2] : (this.hsv[2]=v) 1155 | ); 1156 | 1157 | this.exportColor(flags); 1158 | }; 1159 | 1160 | 1161 | // r: 0-255 1162 | // g: 0-255 1163 | // b: 0-255 1164 | // 1165 | this.fromRGB = function (r, g, b, flags) { // null = don't change 1166 | if (r !== null) { 1167 | if (isNaN(r)) { return false; } 1168 | r = Math.max(0, Math.min(255, r)); 1169 | } 1170 | if (g !== null) { 1171 | if (isNaN(g)) { return false; } 1172 | g = Math.max(0, Math.min(255, g)); 1173 | } 1174 | if (b !== null) { 1175 | if (isNaN(b)) { return false; } 1176 | b = Math.max(0, Math.min(255, b)); 1177 | } 1178 | 1179 | var hsv = RGB_HSV( 1180 | r===null ? this.rgb[0] : r, 1181 | g===null ? this.rgb[1] : g, 1182 | b===null ? this.rgb[2] : b 1183 | ); 1184 | if (hsv[0] !== null) { 1185 | this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); 1186 | } 1187 | if (hsv[2] !== 0) { 1188 | this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); 1189 | } 1190 | this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); 1191 | 1192 | // update RGB according to final HSV, as some values might be trimmed 1193 | var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 1194 | this.rgb[0] = rgb[0]; 1195 | this.rgb[1] = rgb[1]; 1196 | this.rgb[2] = rgb[2]; 1197 | 1198 | this.exportColor(flags); 1199 | }; 1200 | 1201 | 1202 | this.fromString = function (str, flags) { 1203 | var m; 1204 | if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { 1205 | // HEX notation 1206 | // 1207 | 1208 | if (m[1].length === 6) { 1209 | // 6-char notation 1210 | this.fromRGB( 1211 | parseInt(m[1].substr(0,2),16), 1212 | parseInt(m[1].substr(2,2),16), 1213 | parseInt(m[1].substr(4,2),16), 1214 | flags 1215 | ); 1216 | } else { 1217 | // 3-char notation 1218 | this.fromRGB( 1219 | parseInt(m[1].charAt(0) + m[1].charAt(0),16), 1220 | parseInt(m[1].charAt(1) + m[1].charAt(1),16), 1221 | parseInt(m[1].charAt(2) + m[1].charAt(2),16), 1222 | flags 1223 | ); 1224 | } 1225 | return true; 1226 | 1227 | } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { 1228 | var params = m[1].split(','); 1229 | var re = /^\s*(\d*)(\.\d+)?\s*$/; 1230 | var mR, mG, mB; 1231 | if ( 1232 | params.length >= 3 && 1233 | (mR = params[0].match(re)) && 1234 | (mG = params[1].match(re)) && 1235 | (mB = params[2].match(re)) 1236 | ) { 1237 | var r = parseFloat((mR[1] || '0') + (mR[2] || '')); 1238 | var g = parseFloat((mG[1] || '0') + (mG[2] || '')); 1239 | var b = parseFloat((mB[1] || '0') + (mB[2] || '')); 1240 | this.fromRGB(r, g, b, flags); 1241 | return true; 1242 | } 1243 | } 1244 | return false; 1245 | }; 1246 | 1247 | 1248 | this.toString = function () { 1249 | return ( 1250 | (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + 1251 | (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + 1252 | (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) 1253 | ); 1254 | }; 1255 | 1256 | 1257 | this.toHEXString = function () { 1258 | return '#' + this.toString().toUpperCase(); 1259 | }; 1260 | 1261 | 1262 | this.toRGBString = function () { 1263 | return ('rgb(' + 1264 | Math.round(this.rgb[0]) + ',' + 1265 | Math.round(this.rgb[1]) + ',' + 1266 | Math.round(this.rgb[2]) + ')' 1267 | ); 1268 | }; 1269 | 1270 | 1271 | this.isLight = function () { 1272 | return ( 1273 | 0.213 * this.rgb[0] + 1274 | 0.715 * this.rgb[1] + 1275 | 0.072 * this.rgb[2] > 1276 | 255 / 2 1277 | ); 1278 | }; 1279 | 1280 | 1281 | this._processParentElementsInDOM = function () { 1282 | if (this._linkedElementsProcessed) { return; } 1283 | this._linkedElementsProcessed = true; 1284 | 1285 | var elm = this.targetElement; 1286 | do { 1287 | // If the target element or one of its parent nodes has fixed position, 1288 | // then use fixed positioning instead 1289 | // 1290 | // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1291 | // that's why we need to check if the returned style object is non-empty 1292 | var currStyle = jsc.getStyle(elm); 1293 | if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1294 | this.fixed = true; 1295 | } 1296 | 1297 | if (elm !== this.targetElement) { 1298 | // Ensure to attach onParentScroll only once to each parent element 1299 | // (multiple targetElements can share the same parent nodes) 1300 | // 1301 | // Note: It's not just offsetParents that can be scrollable, 1302 | // that's why we loop through all parent nodes 1303 | if (!elm._jscEventsAttached) { 1304 | jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1305 | elm._jscEventsAttached = true; 1306 | } 1307 | } 1308 | } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); 1309 | }; 1310 | 1311 | 1312 | // r: 0-255 1313 | // g: 0-255 1314 | // b: 0-255 1315 | // 1316 | // returns: [ 0-360, 0-100, 0-100 ] 1317 | // 1318 | function RGB_HSV (r, g, b) { 1319 | r /= 255; 1320 | g /= 255; 1321 | b /= 255; 1322 | var n = Math.min(Math.min(r,g),b); 1323 | var v = Math.max(Math.max(r,g),b); 1324 | var m = v - n; 1325 | if (m === 0) { return [ null, 0, 100 * v ]; } 1326 | var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 1327 | return [ 1328 | 60 * (h===6?0:h), 1329 | 100 * (m/v), 1330 | 100 * v 1331 | ]; 1332 | } 1333 | 1334 | 1335 | // h: 0-360 1336 | // s: 0-100 1337 | // v: 0-100 1338 | // 1339 | // returns: [ 0-255, 0-255, 0-255 ] 1340 | // 1341 | function HSV_RGB (h, s, v) { 1342 | var u = 255 * (v / 100); 1343 | 1344 | if (h === null) { 1345 | return [ u, u, u ]; 1346 | } 1347 | 1348 | h /= 60; 1349 | s /= 100; 1350 | 1351 | var i = Math.floor(h); 1352 | var f = i%2 ? h-i : 1-(h-i); 1353 | var m = u * (1 - s); 1354 | var n = u * (1 - s * f); 1355 | switch (i) { 1356 | case 6: 1357 | case 0: return [u,n,m]; 1358 | case 1: return [n,u,m]; 1359 | case 2: return [m,u,n]; 1360 | case 3: return [m,n,u]; 1361 | case 4: return [n,m,u]; 1362 | case 5: return [u,m,n]; 1363 | } 1364 | } 1365 | 1366 | 1367 | function detachPicker () { 1368 | jsc.unsetClass(THIS.targetElement, THIS.activeClass); 1369 | jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); 1370 | delete jsc.picker.owner; 1371 | } 1372 | 1373 | 1374 | function drawPicker () { 1375 | 1376 | // At this point, when drawing the picker, we know what the parent elements are 1377 | // and we can do all related DOM operations, such as registering events on them 1378 | // or checking their positioning 1379 | THIS._processParentElementsInDOM(); 1380 | 1381 | if (!jsc.picker) { 1382 | jsc.picker = { 1383 | owner: null, 1384 | wrap : document.createElement('div'), 1385 | box : document.createElement('div'), 1386 | boxS : document.createElement('div'), // shadow area 1387 | boxB : document.createElement('div'), // border 1388 | pad : document.createElement('div'), 1389 | padB : document.createElement('div'), // border 1390 | padM : document.createElement('div'), // mouse/touch area 1391 | padPal : jsc.createPalette(), 1392 | cross : document.createElement('div'), 1393 | crossBY : document.createElement('div'), // border Y 1394 | crossBX : document.createElement('div'), // border X 1395 | crossLY : document.createElement('div'), // line Y 1396 | crossLX : document.createElement('div'), // line X 1397 | sld : document.createElement('div'), 1398 | sldB : document.createElement('div'), // border 1399 | sldM : document.createElement('div'), // mouse/touch area 1400 | sldGrad : jsc.createSliderGradient(), 1401 | sldPtrS : document.createElement('div'), // slider pointer spacer 1402 | sldPtrIB : document.createElement('div'), // slider pointer inner border 1403 | sldPtrMB : document.createElement('div'), // slider pointer middle border 1404 | sldPtrOB : document.createElement('div'), // slider pointer outer border 1405 | btn : document.createElement('div'), 1406 | btnT : document.createElement('span') // text 1407 | }; 1408 | 1409 | jsc.picker.pad.appendChild(jsc.picker.padPal.elm); 1410 | jsc.picker.padB.appendChild(jsc.picker.pad); 1411 | jsc.picker.cross.appendChild(jsc.picker.crossBY); 1412 | jsc.picker.cross.appendChild(jsc.picker.crossBX); 1413 | jsc.picker.cross.appendChild(jsc.picker.crossLY); 1414 | jsc.picker.cross.appendChild(jsc.picker.crossLX); 1415 | jsc.picker.padB.appendChild(jsc.picker.cross); 1416 | jsc.picker.box.appendChild(jsc.picker.padB); 1417 | jsc.picker.box.appendChild(jsc.picker.padM); 1418 | 1419 | jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); 1420 | jsc.picker.sldB.appendChild(jsc.picker.sld); 1421 | jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); 1422 | jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); 1423 | jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); 1424 | jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); 1425 | jsc.picker.box.appendChild(jsc.picker.sldB); 1426 | jsc.picker.box.appendChild(jsc.picker.sldM); 1427 | 1428 | jsc.picker.btn.appendChild(jsc.picker.btnT); 1429 | jsc.picker.box.appendChild(jsc.picker.btn); 1430 | 1431 | jsc.picker.boxB.appendChild(jsc.picker.box); 1432 | jsc.picker.wrap.appendChild(jsc.picker.boxS); 1433 | jsc.picker.wrap.appendChild(jsc.picker.boxB); 1434 | } 1435 | 1436 | var p = jsc.picker; 1437 | 1438 | var displaySlider = !!jsc.getSliderComponent(THIS); 1439 | var dims = jsc.getPickerDims(THIS); 1440 | var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1441 | var padToSliderPadding = jsc.getPadToSliderPadding(THIS); 1442 | var borderRadius = Math.min( 1443 | THIS.borderRadius, 1444 | Math.round(THIS.padding * Math.PI)); // px 1445 | var padCursor = 'crosshair'; 1446 | 1447 | // wrap 1448 | p.wrap.style.clear = 'both'; 1449 | p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; 1450 | p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; 1451 | p.wrap.style.zIndex = THIS.zIndex; 1452 | 1453 | // picker 1454 | p.box.style.width = dims[0] + 'px'; 1455 | p.box.style.height = dims[1] + 'px'; 1456 | 1457 | p.boxS.style.position = 'absolute'; 1458 | p.boxS.style.left = '0'; 1459 | p.boxS.style.top = '0'; 1460 | p.boxS.style.width = '100%'; 1461 | p.boxS.style.height = '100%'; 1462 | jsc.setBorderRadius(p.boxS, borderRadius + 'px'); 1463 | 1464 | // picker border 1465 | p.boxB.style.position = 'relative'; 1466 | p.boxB.style.border = THIS.borderWidth + 'px solid'; 1467 | p.boxB.style.borderColor = THIS.borderColor; 1468 | p.boxB.style.background = THIS.backgroundColor; 1469 | jsc.setBorderRadius(p.boxB, borderRadius + 'px'); 1470 | 1471 | // IE hack: 1472 | // If the element is transparent, IE will trigger the event on the elements under it, 1473 | // e.g. on Canvas or on elements with border 1474 | p.padM.style.background = 1475 | p.sldM.style.background = 1476 | '#FFF'; 1477 | jsc.setStyle(p.padM, 'opacity', '0'); 1478 | jsc.setStyle(p.sldM, 'opacity', '0'); 1479 | 1480 | // pad 1481 | p.pad.style.position = 'relative'; 1482 | p.pad.style.width = THIS.width + 'px'; 1483 | p.pad.style.height = THIS.height + 'px'; 1484 | 1485 | // pad palettes (HSV and HVS) 1486 | p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); 1487 | 1488 | // pad border 1489 | p.padB.style.position = 'absolute'; 1490 | p.padB.style.left = THIS.padding + 'px'; 1491 | p.padB.style.top = THIS.padding + 'px'; 1492 | p.padB.style.border = THIS.insetWidth + 'px solid'; 1493 | p.padB.style.borderColor = THIS.insetColor; 1494 | 1495 | // pad mouse area 1496 | p.padM._jscInstance = THIS; 1497 | p.padM._jscControlName = 'pad'; 1498 | p.padM.style.position = 'absolute'; 1499 | p.padM.style.left = '0'; 1500 | p.padM.style.top = '0'; 1501 | p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; 1502 | p.padM.style.height = dims[1] + 'px'; 1503 | p.padM.style.cursor = padCursor; 1504 | 1505 | // pad cross 1506 | p.cross.style.position = 'absolute'; 1507 | p.cross.style.left = 1508 | p.cross.style.top = 1509 | '0'; 1510 | p.cross.style.width = 1511 | p.cross.style.height = 1512 | crossOuterSize + 'px'; 1513 | 1514 | // pad cross border Y and X 1515 | p.crossBY.style.position = 1516 | p.crossBX.style.position = 1517 | 'absolute'; 1518 | p.crossBY.style.background = 1519 | p.crossBX.style.background = 1520 | THIS.pointerBorderColor; 1521 | p.crossBY.style.width = 1522 | p.crossBX.style.height = 1523 | (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1524 | p.crossBY.style.height = 1525 | p.crossBX.style.width = 1526 | crossOuterSize + 'px'; 1527 | p.crossBY.style.left = 1528 | p.crossBX.style.top = 1529 | (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; 1530 | p.crossBY.style.top = 1531 | p.crossBX.style.left = 1532 | '0'; 1533 | 1534 | // pad cross line Y and X 1535 | p.crossLY.style.position = 1536 | p.crossLX.style.position = 1537 | 'absolute'; 1538 | p.crossLY.style.background = 1539 | p.crossLX.style.background = 1540 | THIS.pointerColor; 1541 | p.crossLY.style.height = 1542 | p.crossLX.style.width = 1543 | (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; 1544 | p.crossLY.style.width = 1545 | p.crossLX.style.height = 1546 | THIS.pointerThickness + 'px'; 1547 | p.crossLY.style.left = 1548 | p.crossLX.style.top = 1549 | (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; 1550 | p.crossLY.style.top = 1551 | p.crossLX.style.left = 1552 | THIS.pointerBorderWidth + 'px'; 1553 | 1554 | // slider 1555 | p.sld.style.overflow = 'hidden'; 1556 | p.sld.style.width = THIS.sliderSize + 'px'; 1557 | p.sld.style.height = THIS.height + 'px'; 1558 | 1559 | // slider gradient 1560 | p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); 1561 | 1562 | // slider border 1563 | p.sldB.style.display = displaySlider ? 'block' : 'none'; 1564 | p.sldB.style.position = 'absolute'; 1565 | p.sldB.style.right = THIS.padding + 'px'; 1566 | p.sldB.style.top = THIS.padding + 'px'; 1567 | p.sldB.style.border = THIS.insetWidth + 'px solid'; 1568 | p.sldB.style.borderColor = THIS.insetColor; 1569 | 1570 | // slider mouse area 1571 | p.sldM._jscInstance = THIS; 1572 | p.sldM._jscControlName = 'sld'; 1573 | p.sldM.style.display = displaySlider ? 'block' : 'none'; 1574 | p.sldM.style.position = 'absolute'; 1575 | p.sldM.style.right = '0'; 1576 | p.sldM.style.top = '0'; 1577 | p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; 1578 | p.sldM.style.height = dims[1] + 'px'; 1579 | p.sldM.style.cursor = 'default'; 1580 | 1581 | // slider pointer inner and outer border 1582 | p.sldPtrIB.style.border = 1583 | p.sldPtrOB.style.border = 1584 | THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 1585 | 1586 | // slider pointer outer border 1587 | p.sldPtrOB.style.position = 'absolute'; 1588 | p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1589 | p.sldPtrOB.style.top = '0'; 1590 | 1591 | // slider pointer middle border 1592 | p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 1593 | 1594 | // slider pointer spacer 1595 | p.sldPtrS.style.width = THIS.sliderSize + 'px'; 1596 | p.sldPtrS.style.height = sliderPtrSpace + 'px'; 1597 | 1598 | // the Close button 1599 | function setBtnBorder () { 1600 | var insetColors = THIS.insetColor.split(/\s+/); 1601 | var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 1602 | p.btn.style.borderColor = outsetColor; 1603 | } 1604 | p.btn.style.display = THIS.closable ? 'block' : 'none'; 1605 | p.btn.style.position = 'absolute'; 1606 | p.btn.style.left = THIS.padding + 'px'; 1607 | p.btn.style.bottom = THIS.padding + 'px'; 1608 | p.btn.style.padding = '0 15px'; 1609 | p.btn.style.height = THIS.buttonHeight + 'px'; 1610 | p.btn.style.border = THIS.insetWidth + 'px solid'; 1611 | setBtnBorder(); 1612 | p.btn.style.color = THIS.buttonColor; 1613 | p.btn.style.font = '12px sans-serif'; 1614 | p.btn.style.textAlign = 'center'; 1615 | try { 1616 | p.btn.style.cursor = 'pointer'; 1617 | } catch(eOldIE) { 1618 | p.btn.style.cursor = 'hand'; 1619 | } 1620 | p.btn.onmousedown = function () { 1621 | THIS.hide(); 1622 | }; 1623 | p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; 1624 | p.btnT.innerHTML = ''; 1625 | p.btnT.appendChild(document.createTextNode(THIS.closeText)); 1626 | 1627 | // place pointers 1628 | redrawPad(); 1629 | redrawSld(); 1630 | 1631 | // If we are changing the owner without first closing the picker, 1632 | // make sure to first deal with the old owner 1633 | if (jsc.picker.owner && jsc.picker.owner !== THIS) { 1634 | jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); 1635 | } 1636 | 1637 | // Set the new picker owner 1638 | jsc.picker.owner = THIS; 1639 | 1640 | // The redrawPosition() method needs picker.owner to be set, that's why we call it here, 1641 | // after setting the owner 1642 | if (jsc.isElementType(container, 'body')) { 1643 | jsc.redrawPosition(); 1644 | } else { 1645 | jsc._drawPosition(THIS, 0, 0, 'relative', false); 1646 | } 1647 | 1648 | if (p.wrap.parentNode != container) { 1649 | container.appendChild(p.wrap); 1650 | } 1651 | 1652 | jsc.setClass(THIS.targetElement, THIS.activeClass); 1653 | } 1654 | 1655 | 1656 | function redrawPad () { 1657 | // redraw the pad pointer 1658 | switch (jsc.getPadYComponent(THIS)) { 1659 | case 's': var yComponent = 1; break; 1660 | case 'v': var yComponent = 2; break; 1661 | } 1662 | var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); 1663 | var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1664 | var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1665 | var ofs = -Math.floor(crossOuterSize / 2); 1666 | jsc.picker.cross.style.left = (x + ofs) + 'px'; 1667 | jsc.picker.cross.style.top = (y + ofs) + 'px'; 1668 | 1669 | // redraw the slider 1670 | switch (jsc.getSliderComponent(THIS)) { 1671 | case 's': 1672 | var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); 1673 | var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); 1674 | var color1 = 'rgb(' + 1675 | Math.round(rgb1[0]) + ',' + 1676 | Math.round(rgb1[1]) + ',' + 1677 | Math.round(rgb1[2]) + ')'; 1678 | var color2 = 'rgb(' + 1679 | Math.round(rgb2[0]) + ',' + 1680 | Math.round(rgb2[1]) + ',' + 1681 | Math.round(rgb2[2]) + ')'; 1682 | jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1683 | break; 1684 | case 'v': 1685 | var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); 1686 | var color1 = 'rgb(' + 1687 | Math.round(rgb[0]) + ',' + 1688 | Math.round(rgb[1]) + ',' + 1689 | Math.round(rgb[2]) + ')'; 1690 | var color2 = '#000'; 1691 | jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1692 | break; 1693 | } 1694 | } 1695 | 1696 | 1697 | function redrawSld () { 1698 | var sldComponent = jsc.getSliderComponent(THIS); 1699 | if (sldComponent) { 1700 | // redraw the slider pointer 1701 | switch (sldComponent) { 1702 | case 's': var yComponent = 1; break; 1703 | case 'v': var yComponent = 2; break; 1704 | } 1705 | var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1706 | jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; 1707 | } 1708 | } 1709 | 1710 | 1711 | function isPickerOwner () { 1712 | return jsc.picker && jsc.picker.owner === THIS; 1713 | } 1714 | 1715 | 1716 | function blurValue () { 1717 | THIS.importColor(); 1718 | } 1719 | 1720 | 1721 | // Find the target element 1722 | if (typeof targetElement === 'string') { 1723 | var id = targetElement; 1724 | var elm = document.getElementById(id); 1725 | if (elm) { 1726 | this.targetElement = elm; 1727 | } else { 1728 | jsc.warn('Could not find target element with ID \'' + id + '\''); 1729 | } 1730 | } else if (targetElement) { 1731 | this.targetElement = targetElement; 1732 | } else { 1733 | jsc.warn('Invalid target element: \'' + targetElement + '\''); 1734 | } 1735 | 1736 | if (this.targetElement._jscLinkedInstance) { 1737 | jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); 1738 | return; 1739 | } 1740 | this.targetElement._jscLinkedInstance = this; 1741 | 1742 | // Find the value element 1743 | this.valueElement = jsc.fetchElement(this.valueElement); 1744 | // Find the style element 1745 | this.styleElement = jsc.fetchElement(this.styleElement); 1746 | 1747 | var THIS = this; 1748 | var container = 1749 | this.container ? 1750 | jsc.fetchElement(this.container) : 1751 | document.getElementsByTagName('body')[0]; 1752 | var sliderPtrSpace = 3; // px 1753 | 1754 | // For BUTTON elements it's important to stop them from sending the form when clicked 1755 | // (e.g. in Safari) 1756 | if (jsc.isElementType(this.targetElement, 'button')) { 1757 | if (this.targetElement.onclick) { 1758 | var origCallback = this.targetElement.onclick; 1759 | this.targetElement.onclick = function (evt) { 1760 | origCallback.call(this, evt); 1761 | return false; 1762 | }; 1763 | } else { 1764 | this.targetElement.onclick = function () { return false; }; 1765 | } 1766 | } 1767 | 1768 | /* 1769 | var elm = this.targetElement; 1770 | do { 1771 | // If the target element or one of its offsetParents has fixed position, 1772 | // then use fixed positioning instead 1773 | // 1774 | // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1775 | // that's why we need to check if the returned style object is non-empty 1776 | var currStyle = jsc.getStyle(elm); 1777 | if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1778 | this.fixed = true; 1779 | } 1780 | 1781 | if (elm !== this.targetElement) { 1782 | // attach onParentScroll so that we can recompute the picker position 1783 | // when one of the offsetParents is scrolled 1784 | if (!elm._jscEventsAttached) { 1785 | jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1786 | elm._jscEventsAttached = true; 1787 | } 1788 | } 1789 | } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); 1790 | */ 1791 | 1792 | // valueElement 1793 | if (this.valueElement) { 1794 | if (jsc.isElementType(this.valueElement, 'input')) { 1795 | var updateField = function () { 1796 | THIS.fromString(THIS.valueElement.value, jsc.leaveValue); 1797 | jsc.dispatchFineChange(THIS); 1798 | }; 1799 | jsc.attachEvent(this.valueElement, 'keyup', updateField); 1800 | jsc.attachEvent(this.valueElement, 'input', updateField); 1801 | jsc.attachEvent(this.valueElement, 'blur', blurValue); 1802 | this.valueElement.setAttribute('autocomplete', 'off'); 1803 | } 1804 | } 1805 | 1806 | // styleElement 1807 | if (this.styleElement) { 1808 | this.styleElement._jscOrigStyle = { 1809 | backgroundImage : this.styleElement.style.backgroundImage, 1810 | backgroundColor : this.styleElement.style.backgroundColor, 1811 | color : this.styleElement.style.color 1812 | }; 1813 | } 1814 | 1815 | if (this.value) { 1816 | // Try to set the color from the .value option and if unsuccessful, 1817 | // export the current color 1818 | this.fromString(this.value) || this.exportColor(); 1819 | } else { 1820 | this.importColor(); 1821 | } 1822 | } 1823 | 1824 | }; 1825 | 1826 | 1827 | //================================ 1828 | // Public properties and methods 1829 | //================================ 1830 | 1831 | 1832 | // By default, search for all elements with class="jscolor" and install a color picker on them. 1833 | // 1834 | // You can change what class name will be looked for by setting the property jscolor.lookupClass 1835 | // anywhere in your HTML document. To completely disable the automatic lookup, set it to null. 1836 | // 1837 | jsc.jscolor.lookupClass = 'jscolor'; 1838 | 1839 | 1840 | jsc.jscolor.installByClassName = function (className) { 1841 | var inputElms = document.getElementsByTagName('input'); 1842 | var buttonElms = document.getElementsByTagName('button'); 1843 | 1844 | jsc.tryInstallOnElements(inputElms, className); 1845 | jsc.tryInstallOnElements(buttonElms, className); 1846 | }; 1847 | 1848 | 1849 | jsc.register(); 1850 | 1851 | 1852 | return jsc.jscolor; 1853 | 1854 | 1855 | })(); } 1856 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Github Heatmap Customizer", 4 | "description": "Github Heatmap Colorizer", 5 | "version": "1.0.0", 6 | "browser_action": { 7 | "default_popup":"popup.html", 8 | "default_title": "Github Heatmap Colorizer" 9 | }, 10 | "permissions": [ 11 | "activeTab", 12 | "storage", 13 | "http://*.github.com/*", 14 | "https://*.github.com/*" 15 | ], 16 | "background": { 17 | "scripts": ["background.js"], 18 | "persistent": false 19 | }, 20 | "icons": { 21 | "16": "icons/logo_16x16.png", 22 | "48": "icons/logo_48x48.png", 23 | "128": "icons/logo_128x128.png" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/plugin.zip -------------------------------------------------------------------------------- /plugin/background.js: -------------------------------------------------------------------------------- 1 | // Activate plugin on every tab update 2 | chrome.tabs.onUpdated.addListener(() => { 3 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 4 | chrome.tabs.executeScript( 5 | tabs[0].id, 6 | { file: 'content.js' }); 7 | }); 8 | }) -------------------------------------------------------------------------------- /plugin/content.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | // If on someone's Github profile 3 | const activityContainer = document.querySelector('.graph-before-activity-overview') 4 | if(activityContainer) 5 | initColorize() 6 | })() 7 | 8 | function initColorize() 9 | { 10 | chrome.storage.local.get('color', function(data) { 11 | if(!data.color) 12 | data.color = '4594A8' 13 | let mainColorHex = `#${data.color}` 14 | 15 | const legendItems = document.querySelectorAll('.contrib-legend ul li') 16 | const originalColors = [] 17 | for(let li of legendItems) 18 | originalColors.push(li.getAttribute('style').split(':')[1].trim()) 19 | 20 | colorizeActivity(mainColorHex) 21 | colorizeLegend(mainColorHex, originalColors, legendItems) 22 | colorizeDays(mainColorHex, originalColors) 23 | }); 24 | } 25 | 26 | function colorizeActivity(color) 27 | { 28 | const activity = document.querySelector('.js-highlight-blob') 29 | if(activity) 30 | { 31 | const axes = document.querySelectorAll('.activity-overview-axis') 32 | const ellipses = document.querySelectorAll('.activity-overview-point') 33 | const blob = document.querySelector('.js-highlight-blob') 34 | activity.setAttribute('stroke', color) 35 | blob.style.fill = color 36 | for(let ellipse of ellipses) 37 | ellipse.style.stroke = color 38 | for(let axis of axes) 39 | axis.style.stroke = color 40 | } 41 | } 42 | 43 | function colorizeLegend(mainColor, originalColors, legendItems) 44 | { 45 | legendItems[0].setAttribute('style', `background-color:${originalColors[0]}`) 46 | legendItems[1].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.9)}`) 47 | legendItems[2].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.75)}`) 48 | legendItems[3].setAttribute('style', `background-color:${colorLuminance(mainColor, 0.5)}`) 49 | legendItems[4].setAttribute('style', `background-color:${mainColor}`) 50 | } 51 | 52 | function colorizeDays(mainColor, originalColors) 53 | { 54 | let days = Array.from(document.getElementsByClassName('day')) 55 | 56 | const dayGroups = { 57 | 0: days.filter( day => day.getAttribute('fill') == originalColors[0]), 58 | 1: days.filter( day => day.getAttribute('fill') == originalColors[1]), 59 | 2: days.filter( day => day.getAttribute('fill') == originalColors[2]), 60 | 3: days.filter( day => day.getAttribute('fill') == originalColors[3]), 61 | 4: days.filter( day => day.getAttribute('fill') == originalColors[4]) 62 | } 63 | 64 | dayGroups[0].forEach(day => day.setAttribute('fill', originalColors[0])) 65 | dayGroups[1].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.9))) 66 | dayGroups[2].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.75))) 67 | dayGroups[3].forEach(day => day.setAttribute('fill', colorLuminance(mainColor, 0.5))) 68 | dayGroups[4].forEach(day => day.setAttribute('fill', mainColor)) 69 | } 70 | 71 | function colorLuminance(hex, lum) { 72 | // validate hex string 73 | hex = String(hex).replace(/[^0-9a-f]/gi, ''); 74 | if (hex.length < 6) { 75 | hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; 76 | } 77 | lum = lum || 0; 78 | 79 | // convert to decimal and change luminosity 80 | let rgb = "#", c, i; 81 | for (i = 0; i < 3; i++) { 82 | c = parseInt(hex.substr(i*2,2), 16); 83 | c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); 84 | rgb += ("00"+c).substr(c.length); 85 | } 86 | return rgb; 87 | } 88 | -------------------------------------------------------------------------------- /plugin/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/plugin/icons/logo.png -------------------------------------------------------------------------------- /plugin/icons/logo_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/plugin/icons/logo_128x128.png -------------------------------------------------------------------------------- /plugin/icons/logo_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/plugin/icons/logo_16x16.png -------------------------------------------------------------------------------- /plugin/icons/logo_48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/96c9e6f6861a0d4dc33900f3f29601bab4b51956/plugin/icons/logo_48x48.png -------------------------------------------------------------------------------- /plugin/jscolor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jscolor - JavaScript Color Picker 3 | * 4 | * @link http://jscolor.com 5 | * @license For open source use: GPLv3 6 | * For commercial use: JSColor Commercial License 7 | * @author Jan Odvarko 8 | * @version 2.0.5 9 | * 10 | * See usage examples at http://jscolor.com/examples/ 11 | */ 12 | 13 | 14 | "use strict"; 15 | 16 | 17 | if (!window.jscolor) { window.jscolor = (function () { 18 | 19 | 20 | var jsc = { 21 | 22 | 23 | register : function () { 24 | jsc.attachDOMReadyEvent(jsc.init); 25 | jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown); 26 | jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart); 27 | jsc.attachEvent(window, 'resize', jsc.onWindowResize); 28 | }, 29 | 30 | 31 | init : function () { 32 | if (jsc.jscolor.lookupClass) { 33 | jsc.jscolor.installByClassName(jsc.jscolor.lookupClass); 34 | } 35 | }, 36 | 37 | 38 | tryInstallOnElements : function (elms, className) { 39 | var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); 40 | 41 | for (var i = 0; i < elms.length; i += 1) { 42 | if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') { 43 | if (jsc.isColorAttrSupported) { 44 | // skip inputs of type 'color' if supported by the browser 45 | continue; 46 | } 47 | } 48 | var m; 49 | if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) { 50 | var targetElm = elms[i]; 51 | var optsStr = null; 52 | 53 | var dataOptions = jsc.getDataAttr(targetElm, 'jscolor'); 54 | if (dataOptions !== null) { 55 | optsStr = dataOptions; 56 | } else if (m[4]) { 57 | optsStr = m[4]; 58 | } 59 | 60 | var opts = {}; 61 | if (optsStr) { 62 | try { 63 | opts = (new Function ('return (' + optsStr + ')'))(); 64 | } catch(eParseError) { 65 | jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr); 66 | } 67 | } 68 | targetElm.jscolor = new jsc.jscolor(targetElm, opts); 69 | } 70 | } 71 | }, 72 | 73 | 74 | isColorAttrSupported : (function () { 75 | var elm = document.createElement('input'); 76 | if (elm.setAttribute) { 77 | elm.setAttribute('type', 'color'); 78 | if (elm.type.toLowerCase() == 'color') { 79 | return true; 80 | } 81 | } 82 | return false; 83 | })(), 84 | 85 | 86 | isCanvasSupported : (function () { 87 | var elm = document.createElement('canvas'); 88 | return !!(elm.getContext && elm.getContext('2d')); 89 | })(), 90 | 91 | 92 | fetchElement : function (mixed) { 93 | return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; 94 | }, 95 | 96 | 97 | isElementType : function (elm, type) { 98 | return elm.nodeName.toLowerCase() === type.toLowerCase(); 99 | }, 100 | 101 | 102 | getDataAttr : function (el, name) { 103 | var attrName = 'data-' + name; 104 | var attrValue = el.getAttribute(attrName); 105 | if (attrValue !== null) { 106 | return attrValue; 107 | } 108 | return null; 109 | }, 110 | 111 | 112 | attachEvent : function (el, evnt, func) { 113 | if (el.addEventListener) { 114 | el.addEventListener(evnt, func, false); 115 | } else if (el.attachEvent) { 116 | el.attachEvent('on' + evnt, func); 117 | } 118 | }, 119 | 120 | 121 | detachEvent : function (el, evnt, func) { 122 | if (el.removeEventListener) { 123 | el.removeEventListener(evnt, func, false); 124 | } else if (el.detachEvent) { 125 | el.detachEvent('on' + evnt, func); 126 | } 127 | }, 128 | 129 | 130 | _attachedGroupEvents : {}, 131 | 132 | 133 | attachGroupEvent : function (groupName, el, evnt, func) { 134 | if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 135 | jsc._attachedGroupEvents[groupName] = []; 136 | } 137 | jsc._attachedGroupEvents[groupName].push([el, evnt, func]); 138 | jsc.attachEvent(el, evnt, func); 139 | }, 140 | 141 | 142 | detachGroupEvents : function (groupName) { 143 | if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 144 | for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { 145 | var evt = jsc._attachedGroupEvents[groupName][i]; 146 | jsc.detachEvent(evt[0], evt[1], evt[2]); 147 | } 148 | delete jsc._attachedGroupEvents[groupName]; 149 | } 150 | }, 151 | 152 | 153 | attachDOMReadyEvent : function (func) { 154 | var fired = false; 155 | var fireOnce = function () { 156 | if (!fired) { 157 | fired = true; 158 | func(); 159 | } 160 | }; 161 | 162 | if (document.readyState === 'complete') { 163 | setTimeout(fireOnce, 1); // async 164 | return; 165 | } 166 | 167 | if (document.addEventListener) { 168 | document.addEventListener('DOMContentLoaded', fireOnce, false); 169 | 170 | // Fallback 171 | window.addEventListener('load', fireOnce, false); 172 | 173 | } else if (document.attachEvent) { 174 | // IE 175 | document.attachEvent('onreadystatechange', function () { 176 | if (document.readyState === 'complete') { 177 | document.detachEvent('onreadystatechange', arguments.callee); 178 | fireOnce(); 179 | } 180 | }) 181 | 182 | // Fallback 183 | window.attachEvent('onload', fireOnce); 184 | 185 | // IE7/8 186 | if (document.documentElement.doScroll && window == window.top) { 187 | var tryScroll = function () { 188 | if (!document.body) { return; } 189 | try { 190 | document.documentElement.doScroll('left'); 191 | fireOnce(); 192 | } catch (e) { 193 | setTimeout(tryScroll, 1); 194 | } 195 | }; 196 | tryScroll(); 197 | } 198 | } 199 | }, 200 | 201 | 202 | warn : function (msg) { 203 | if (window.console && window.console.warn) { 204 | window.console.warn(msg); 205 | } 206 | }, 207 | 208 | 209 | preventDefault : function (e) { 210 | if (e.preventDefault) { e.preventDefault(); } 211 | e.returnValue = false; 212 | }, 213 | 214 | 215 | captureTarget : function (target) { 216 | // IE 217 | if (target.setCapture) { 218 | jsc._capturedTarget = target; 219 | jsc._capturedTarget.setCapture(); 220 | } 221 | }, 222 | 223 | 224 | releaseTarget : function () { 225 | // IE 226 | if (jsc._capturedTarget) { 227 | jsc._capturedTarget.releaseCapture(); 228 | jsc._capturedTarget = null; 229 | } 230 | }, 231 | 232 | 233 | fireEvent : function (el, evnt) { 234 | if (!el) { 235 | return; 236 | } 237 | if (document.createEvent) { 238 | var ev = document.createEvent('HTMLEvents'); 239 | ev.initEvent(evnt, true, true); 240 | el.dispatchEvent(ev); 241 | } else if (document.createEventObject) { 242 | var ev = document.createEventObject(); 243 | el.fireEvent('on' + evnt, ev); 244 | } else if (el['on' + evnt]) { // alternatively use the traditional event model 245 | el['on' + evnt](); 246 | } 247 | }, 248 | 249 | 250 | classNameToList : function (className) { 251 | return className.replace(/^\s+|\s+$/g, '').split(/\s+/); 252 | }, 253 | 254 | 255 | // The className parameter (str) can only contain a single class name 256 | hasClass : function (elm, className) { 257 | if (!className) { 258 | return false; 259 | } 260 | return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); 261 | }, 262 | 263 | 264 | // The className parameter (str) can contain multiple class names separated by whitespace 265 | setClass : function (elm, className) { 266 | var classList = jsc.classNameToList(className); 267 | for (var i = 0; i < classList.length; i += 1) { 268 | if (!jsc.hasClass(elm, classList[i])) { 269 | elm.className += (elm.className ? ' ' : '') + classList[i]; 270 | } 271 | } 272 | }, 273 | 274 | 275 | // The className parameter (str) can contain multiple class names separated by whitespace 276 | unsetClass : function (elm, className) { 277 | var classList = jsc.classNameToList(className); 278 | for (var i = 0; i < classList.length; i += 1) { 279 | var repl = new RegExp( 280 | '^\\s*' + classList[i] + '\\s*|' + 281 | '\\s*' + classList[i] + '\\s*$|' + 282 | '\\s+' + classList[i] + '(\\s+)', 283 | 'g' 284 | ); 285 | elm.className = elm.className.replace(repl, '$1'); 286 | } 287 | }, 288 | 289 | 290 | getStyle : function (elm) { 291 | return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; 292 | }, 293 | 294 | 295 | setStyle : (function () { 296 | var helper = document.createElement('div'); 297 | var getSupportedProp = function (names) { 298 | for (var i = 0; i < names.length; i += 1) { 299 | if (names[i] in helper.style) { 300 | return names[i]; 301 | } 302 | } 303 | }; 304 | var props = { 305 | borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']), 306 | boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow']) 307 | }; 308 | return function (elm, prop, value) { 309 | switch (prop.toLowerCase()) { 310 | case 'opacity': 311 | var alphaOpacity = Math.round(parseFloat(value) * 100); 312 | elm.style.opacity = value; 313 | elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')'; 314 | break; 315 | default: 316 | elm.style[props[prop]] = value; 317 | break; 318 | } 319 | }; 320 | })(), 321 | 322 | 323 | setBorderRadius : function (elm, value) { 324 | jsc.setStyle(elm, 'borderRadius', value || '0'); 325 | }, 326 | 327 | 328 | setBoxShadow : function (elm, value) { 329 | jsc.setStyle(elm, 'boxShadow', value || 'none'); 330 | }, 331 | 332 | 333 | getElementPos : function (e, relativeToViewport) { 334 | var x=0, y=0; 335 | var rect = e.getBoundingClientRect(); 336 | x = rect.left; 337 | y = rect.top; 338 | if (!relativeToViewport) { 339 | var viewPos = jsc.getViewPos(); 340 | x += viewPos[0]; 341 | y += viewPos[1]; 342 | } 343 | return [x, y]; 344 | }, 345 | 346 | 347 | getElementSize : function (e) { 348 | return [e.offsetWidth, e.offsetHeight]; 349 | }, 350 | 351 | 352 | // get pointer's X/Y coordinates relative to viewport 353 | getAbsPointerPos : function (e) { 354 | if (!e) { e = window.event; } 355 | var x = 0, y = 0; 356 | if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 357 | // touch devices 358 | x = e.changedTouches[0].clientX; 359 | y = e.changedTouches[0].clientY; 360 | } else if (typeof e.clientX === 'number') { 361 | x = e.clientX; 362 | y = e.clientY; 363 | } 364 | return { x: x, y: y }; 365 | }, 366 | 367 | 368 | // get pointer's X/Y coordinates relative to target element 369 | getRelPointerPos : function (e) { 370 | if (!e) { e = window.event; } 371 | var target = e.target || e.srcElement; 372 | var targetRect = target.getBoundingClientRect(); 373 | 374 | var x = 0, y = 0; 375 | 376 | var clientX = 0, clientY = 0; 377 | if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 378 | // touch devices 379 | clientX = e.changedTouches[0].clientX; 380 | clientY = e.changedTouches[0].clientY; 381 | } else if (typeof e.clientX === 'number') { 382 | clientX = e.clientX; 383 | clientY = e.clientY; 384 | } 385 | 386 | x = clientX - targetRect.left; 387 | y = clientY - targetRect.top; 388 | return { x: x, y: y }; 389 | }, 390 | 391 | 392 | getViewPos : function () { 393 | var doc = document.documentElement; 394 | return [ 395 | (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), 396 | (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) 397 | ]; 398 | }, 399 | 400 | 401 | getViewSize : function () { 402 | var doc = document.documentElement; 403 | return [ 404 | (window.innerWidth || doc.clientWidth), 405 | (window.innerHeight || doc.clientHeight), 406 | ]; 407 | }, 408 | 409 | 410 | redrawPosition : function () { 411 | 412 | if (jsc.picker && jsc.picker.owner) { 413 | var thisObj = jsc.picker.owner; 414 | 415 | var tp, vp; 416 | 417 | if (thisObj.fixed) { 418 | // Fixed elements are positioned relative to viewport, 419 | // therefore we can ignore the scroll offset 420 | tp = jsc.getElementPos(thisObj.targetElement, true); // target pos 421 | vp = [0, 0]; // view pos 422 | } else { 423 | tp = jsc.getElementPos(thisObj.targetElement); // target pos 424 | vp = jsc.getViewPos(); // view pos 425 | } 426 | 427 | var ts = jsc.getElementSize(thisObj.targetElement); // target size 428 | var vs = jsc.getViewSize(); // view size 429 | var ps = jsc.getPickerOuterDims(thisObj); // picker size 430 | var a, b, c; 431 | switch (thisObj.position.toLowerCase()) { 432 | case 'left': a=1; b=0; c=-1; break; 433 | case 'right':a=1; b=0; c=1; break; 434 | case 'top': a=0; b=1; c=-1; break; 435 | default: a=0; b=1; c=1; break; 436 | } 437 | var l = (ts[b]+ps[b])/2; 438 | 439 | // compute picker position 440 | if (!thisObj.smartPosition) { 441 | var pp = [ 442 | tp[a], 443 | tp[b]+ts[b]-l+l*c 444 | ]; 445 | } else { 446 | var pp = [ 447 | -vp[a]+tp[a]+ps[a] > vs[a] ? 448 | (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : 449 | tp[a], 450 | -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 451 | (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : 452 | (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 453 | ]; 454 | } 455 | 456 | var x = pp[a]; 457 | var y = pp[b]; 458 | var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; 459 | var contractShadow = 460 | (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && 461 | (pp[1] + ps[1] < tp[1] + ts[1]); 462 | 463 | jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); 464 | } 465 | }, 466 | 467 | 468 | _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { 469 | var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px 470 | 471 | jsc.picker.wrap.style.position = positionValue; 472 | jsc.picker.wrap.style.left = x + 'px'; 473 | jsc.picker.wrap.style.top = y + 'px'; 474 | 475 | jsc.setBoxShadow( 476 | jsc.picker.boxS, 477 | thisObj.shadow ? 478 | new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : 479 | null); 480 | }, 481 | 482 | 483 | getPickerDims : function (thisObj) { 484 | var displaySlider = !!jsc.getSliderComponent(thisObj); 485 | var dims = [ 486 | 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width + 487 | (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0), 488 | 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height + 489 | (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0) 490 | ]; 491 | return dims; 492 | }, 493 | 494 | 495 | getPickerOuterDims : function (thisObj) { 496 | var dims = jsc.getPickerDims(thisObj); 497 | return [ 498 | dims[0] + 2 * thisObj.borderWidth, 499 | dims[1] + 2 * thisObj.borderWidth 500 | ]; 501 | }, 502 | 503 | 504 | getPadToSliderPadding : function (thisObj) { 505 | return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness)); 506 | }, 507 | 508 | 509 | getPadYComponent : function (thisObj) { 510 | switch (thisObj.mode.charAt(1).toLowerCase()) { 511 | case 'v': return 'v'; break; 512 | } 513 | return 's'; 514 | }, 515 | 516 | 517 | getSliderComponent : function (thisObj) { 518 | if (thisObj.mode.length > 2) { 519 | switch (thisObj.mode.charAt(2).toLowerCase()) { 520 | case 's': return 's'; break; 521 | case 'v': return 'v'; break; 522 | } 523 | } 524 | return null; 525 | }, 526 | 527 | 528 | onDocumentMouseDown : function (e) { 529 | if (!e) { e = window.event; } 530 | var target = e.target || e.srcElement; 531 | 532 | if (target._jscLinkedInstance) { 533 | if (target._jscLinkedInstance.showOnClick) { 534 | target._jscLinkedInstance.show(); 535 | } 536 | } else if (target._jscControlName) { 537 | jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse'); 538 | } else { 539 | // Mouse is outside the picker controls -> hide the color picker! 540 | if (jsc.picker && jsc.picker.owner) { 541 | jsc.picker.owner.hide(); 542 | } 543 | } 544 | }, 545 | 546 | 547 | onDocumentTouchStart : function (e) { 548 | if (!e) { e = window.event; } 549 | var target = e.target || e.srcElement; 550 | 551 | if (target._jscLinkedInstance) { 552 | if (target._jscLinkedInstance.showOnClick) { 553 | target._jscLinkedInstance.show(); 554 | } 555 | } else if (target._jscControlName) { 556 | jsc.onControlPointerStart(e, target, target._jscControlName, 'touch'); 557 | } else { 558 | if (jsc.picker && jsc.picker.owner) { 559 | jsc.picker.owner.hide(); 560 | } 561 | } 562 | }, 563 | 564 | 565 | onWindowResize : function (e) { 566 | jsc.redrawPosition(); 567 | }, 568 | 569 | 570 | onParentScroll : function (e) { 571 | // hide the picker when one of the parent elements is scrolled 572 | if (jsc.picker && jsc.picker.owner) { 573 | jsc.picker.owner.hide(); 574 | } 575 | }, 576 | 577 | 578 | _pointerMoveEvent : { 579 | mouse: 'mousemove', 580 | touch: 'touchmove' 581 | }, 582 | _pointerEndEvent : { 583 | mouse: 'mouseup', 584 | touch: 'touchend' 585 | }, 586 | 587 | 588 | _pointerOrigin : null, 589 | _capturedTarget : null, 590 | 591 | 592 | onControlPointerStart : function (e, target, controlName, pointerType) { 593 | var thisObj = target._jscInstance; 594 | 595 | jsc.preventDefault(e); 596 | jsc.captureTarget(target); 597 | 598 | var registerDragEvents = function (doc, offset) { 599 | jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], 600 | jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); 601 | jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], 602 | jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); 603 | }; 604 | 605 | registerDragEvents(document, [0, 0]); 606 | 607 | if (window.parent && window.frameElement) { 608 | var rect = window.frameElement.getBoundingClientRect(); 609 | var ofs = [-rect.left, -rect.top]; 610 | registerDragEvents(window.parent.window.document, ofs); 611 | } 612 | 613 | var abs = jsc.getAbsPointerPos(e); 614 | var rel = jsc.getRelPointerPos(e); 615 | jsc._pointerOrigin = { 616 | x: abs.x - rel.x, 617 | y: abs.y - rel.y 618 | }; 619 | 620 | switch (controlName) { 621 | case 'pad': 622 | // if the slider is at the bottom, move it up 623 | switch (jsc.getSliderComponent(thisObj)) { 624 | case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break; 625 | case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break; 626 | } 627 | jsc.setPad(thisObj, e, 0, 0); 628 | break; 629 | 630 | case 'sld': 631 | jsc.setSld(thisObj, e, 0); 632 | break; 633 | } 634 | 635 | jsc.dispatchFineChange(thisObj); 636 | }, 637 | 638 | 639 | onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { 640 | return function (e) { 641 | var thisObj = target._jscInstance; 642 | switch (controlName) { 643 | case 'pad': 644 | if (!e) { e = window.event; } 645 | jsc.setPad(thisObj, e, offset[0], offset[1]); 646 | jsc.dispatchFineChange(thisObj); 647 | break; 648 | 649 | case 'sld': 650 | if (!e) { e = window.event; } 651 | jsc.setSld(thisObj, e, offset[1]); 652 | jsc.dispatchFineChange(thisObj); 653 | break; 654 | } 655 | } 656 | }, 657 | 658 | 659 | onDocumentPointerEnd : function (e, target, controlName, pointerType) { 660 | return function (e) { 661 | var thisObj = target._jscInstance; 662 | jsc.detachGroupEvents('drag'); 663 | jsc.releaseTarget(); 664 | // Always dispatch changes after detaching outstanding mouse handlers, 665 | // in case some user interaction will occur in user's onchange callback 666 | // that would intrude with current mouse events 667 | jsc.dispatchChange(thisObj); 668 | }; 669 | }, 670 | 671 | 672 | dispatchChange : function (thisObj) { 673 | if (thisObj.valueElement) { 674 | if (jsc.isElementType(thisObj.valueElement, 'input')) { 675 | jsc.fireEvent(thisObj.valueElement, 'change'); 676 | } 677 | } 678 | }, 679 | 680 | 681 | dispatchFineChange : function (thisObj) { 682 | if (thisObj.onFineChange) { 683 | var callback; 684 | if (typeof thisObj.onFineChange === 'string') { 685 | callback = new Function (thisObj.onFineChange); 686 | } else { 687 | callback = thisObj.onFineChange; 688 | } 689 | callback.call(thisObj); 690 | } 691 | }, 692 | 693 | 694 | setPad : function (thisObj, e, ofsX, ofsY) { 695 | var pointerAbs = jsc.getAbsPointerPos(e); 696 | var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth; 697 | var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 698 | 699 | var xVal = x * (360 / (thisObj.width - 1)); 700 | var yVal = 100 - (y * (100 / (thisObj.height - 1))); 701 | 702 | switch (jsc.getPadYComponent(thisObj)) { 703 | case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break; 704 | case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break; 705 | } 706 | }, 707 | 708 | 709 | setSld : function (thisObj, e, ofsY) { 710 | var pointerAbs = jsc.getAbsPointerPos(e); 711 | var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 712 | 713 | var yVal = 100 - (y * (100 / (thisObj.height - 1))); 714 | 715 | switch (jsc.getSliderComponent(thisObj)) { 716 | case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break; 717 | case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break; 718 | } 719 | }, 720 | 721 | 722 | _vmlNS : 'jsc_vml_', 723 | _vmlCSS : 'jsc_vml_css_', 724 | _vmlReady : false, 725 | 726 | 727 | initVML : function () { 728 | if (!jsc._vmlReady) { 729 | // init VML namespace 730 | var doc = document; 731 | if (!doc.namespaces[jsc._vmlNS]) { 732 | doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml'); 733 | } 734 | if (!doc.styleSheets[jsc._vmlCSS]) { 735 | var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image']; 736 | var ss = doc.createStyleSheet(); 737 | ss.owningElement.id = jsc._vmlCSS; 738 | for (var i = 0; i < tags.length; i += 1) { 739 | ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);'); 740 | } 741 | } 742 | jsc._vmlReady = true; 743 | } 744 | }, 745 | 746 | 747 | createPalette : function () { 748 | 749 | var paletteObj = { 750 | elm: null, 751 | draw: null 752 | }; 753 | 754 | if (jsc.isCanvasSupported) { 755 | // Canvas implementation for modern browsers 756 | 757 | var canvas = document.createElement('canvas'); 758 | var ctx = canvas.getContext('2d'); 759 | 760 | var drawFunc = function (width, height, type) { 761 | canvas.width = width; 762 | canvas.height = height; 763 | 764 | ctx.clearRect(0, 0, canvas.width, canvas.height); 765 | 766 | var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); 767 | hGrad.addColorStop(0 / 6, '#F00'); 768 | hGrad.addColorStop(1 / 6, '#FF0'); 769 | hGrad.addColorStop(2 / 6, '#0F0'); 770 | hGrad.addColorStop(3 / 6, '#0FF'); 771 | hGrad.addColorStop(4 / 6, '#00F'); 772 | hGrad.addColorStop(5 / 6, '#F0F'); 773 | hGrad.addColorStop(6 / 6, '#F00'); 774 | 775 | ctx.fillStyle = hGrad; 776 | ctx.fillRect(0, 0, canvas.width, canvas.height); 777 | 778 | var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); 779 | switch (type.toLowerCase()) { 780 | case 's': 781 | vGrad.addColorStop(0, 'rgba(255,255,255,0)'); 782 | vGrad.addColorStop(1, 'rgba(255,255,255,1)'); 783 | break; 784 | case 'v': 785 | vGrad.addColorStop(0, 'rgba(0,0,0,0)'); 786 | vGrad.addColorStop(1, 'rgba(0,0,0,1)'); 787 | break; 788 | } 789 | ctx.fillStyle = vGrad; 790 | ctx.fillRect(0, 0, canvas.width, canvas.height); 791 | }; 792 | 793 | paletteObj.elm = canvas; 794 | paletteObj.draw = drawFunc; 795 | 796 | } else { 797 | // VML fallback for IE 7 and 8 798 | 799 | jsc.initVML(); 800 | 801 | var vmlContainer = document.createElement('div'); 802 | vmlContainer.style.position = 'relative'; 803 | vmlContainer.style.overflow = 'hidden'; 804 | 805 | var hGrad = document.createElement(jsc._vmlNS + ':fill'); 806 | hGrad.type = 'gradient'; 807 | hGrad.method = 'linear'; 808 | hGrad.angle = '90'; 809 | hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0' 810 | 811 | var hRect = document.createElement(jsc._vmlNS + ':rect'); 812 | hRect.style.position = 'absolute'; 813 | hRect.style.left = -1 + 'px'; 814 | hRect.style.top = -1 + 'px'; 815 | hRect.stroked = false; 816 | hRect.appendChild(hGrad); 817 | vmlContainer.appendChild(hRect); 818 | 819 | var vGrad = document.createElement(jsc._vmlNS + ':fill'); 820 | vGrad.type = 'gradient'; 821 | vGrad.method = 'linear'; 822 | vGrad.angle = '180'; 823 | vGrad.opacity = '0'; 824 | 825 | var vRect = document.createElement(jsc._vmlNS + ':rect'); 826 | vRect.style.position = 'absolute'; 827 | vRect.style.left = -1 + 'px'; 828 | vRect.style.top = -1 + 'px'; 829 | vRect.stroked = false; 830 | vRect.appendChild(vGrad); 831 | vmlContainer.appendChild(vRect); 832 | 833 | var drawFunc = function (width, height, type) { 834 | vmlContainer.style.width = width + 'px'; 835 | vmlContainer.style.height = height + 'px'; 836 | 837 | hRect.style.width = 838 | vRect.style.width = 839 | (width + 1) + 'px'; 840 | hRect.style.height = 841 | vRect.style.height = 842 | (height + 1) + 'px'; 843 | 844 | // Colors must be specified during every redraw, otherwise IE won't display 845 | // a full gradient during a subsequential redraw 846 | hGrad.color = '#F00'; 847 | hGrad.color2 = '#F00'; 848 | 849 | switch (type.toLowerCase()) { 850 | case 's': 851 | vGrad.color = vGrad.color2 = '#FFF'; 852 | break; 853 | case 'v': 854 | vGrad.color = vGrad.color2 = '#000'; 855 | break; 856 | } 857 | }; 858 | 859 | paletteObj.elm = vmlContainer; 860 | paletteObj.draw = drawFunc; 861 | } 862 | 863 | return paletteObj; 864 | }, 865 | 866 | 867 | createSliderGradient : function () { 868 | 869 | var sliderObj = { 870 | elm: null, 871 | draw: null 872 | }; 873 | 874 | if (jsc.isCanvasSupported) { 875 | // Canvas implementation for modern browsers 876 | 877 | var canvas = document.createElement('canvas'); 878 | var ctx = canvas.getContext('2d'); 879 | 880 | var drawFunc = function (width, height, color1, color2) { 881 | canvas.width = width; 882 | canvas.height = height; 883 | 884 | ctx.clearRect(0, 0, canvas.width, canvas.height); 885 | 886 | var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); 887 | grad.addColorStop(0, color1); 888 | grad.addColorStop(1, color2); 889 | 890 | ctx.fillStyle = grad; 891 | ctx.fillRect(0, 0, canvas.width, canvas.height); 892 | }; 893 | 894 | sliderObj.elm = canvas; 895 | sliderObj.draw = drawFunc; 896 | 897 | } else { 898 | // VML fallback for IE 7 and 8 899 | 900 | jsc.initVML(); 901 | 902 | var vmlContainer = document.createElement('div'); 903 | vmlContainer.style.position = 'relative'; 904 | vmlContainer.style.overflow = 'hidden'; 905 | 906 | var grad = document.createElement(jsc._vmlNS + ':fill'); 907 | grad.type = 'gradient'; 908 | grad.method = 'linear'; 909 | grad.angle = '180'; 910 | 911 | var rect = document.createElement(jsc._vmlNS + ':rect'); 912 | rect.style.position = 'absolute'; 913 | rect.style.left = -1 + 'px'; 914 | rect.style.top = -1 + 'px'; 915 | rect.stroked = false; 916 | rect.appendChild(grad); 917 | vmlContainer.appendChild(rect); 918 | 919 | var drawFunc = function (width, height, color1, color2) { 920 | vmlContainer.style.width = width + 'px'; 921 | vmlContainer.style.height = height + 'px'; 922 | 923 | rect.style.width = (width + 1) + 'px'; 924 | rect.style.height = (height + 1) + 'px'; 925 | 926 | grad.color = color1; 927 | grad.color2 = color2; 928 | }; 929 | 930 | sliderObj.elm = vmlContainer; 931 | sliderObj.draw = drawFunc; 932 | } 933 | 934 | return sliderObj; 935 | }, 936 | 937 | 938 | leaveValue : 1<<0, 939 | leaveStyle : 1<<1, 940 | leavePad : 1<<2, 941 | leaveSld : 1<<3, 942 | 943 | 944 | BoxShadow : (function () { 945 | var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { 946 | this.hShadow = hShadow; 947 | this.vShadow = vShadow; 948 | this.blur = blur; 949 | this.spread = spread; 950 | this.color = color; 951 | this.inset = !!inset; 952 | }; 953 | 954 | BoxShadow.prototype.toString = function () { 955 | var vals = [ 956 | Math.round(this.hShadow) + 'px', 957 | Math.round(this.vShadow) + 'px', 958 | Math.round(this.blur) + 'px', 959 | Math.round(this.spread) + 'px', 960 | this.color 961 | ]; 962 | if (this.inset) { 963 | vals.push('inset'); 964 | } 965 | return vals.join(' '); 966 | }; 967 | 968 | return BoxShadow; 969 | })(), 970 | 971 | 972 | // 973 | // Usage: 974 | // var myColor = new jscolor( [, ]) 975 | // 976 | 977 | jscolor : function (targetElement, options) { 978 | 979 | // General options 980 | // 981 | this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB() 982 | this.valueElement = targetElement; // element that will be used to display and input the color code 983 | this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor 984 | this.required = true; // whether the associated text can be left empty 985 | this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace) 986 | this.hash = false; // whether to prefix the HEX color code with # symbol 987 | this.uppercase = true; // whether to show the color code in upper case 988 | this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code) 989 | this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it 990 | this.overwriteImportant = false; // whether to overwrite colors of styleElement using !important 991 | this.minS = 0; // min allowed saturation (0 - 100) 992 | this.maxS = 100; // max allowed saturation (0 - 100) 993 | this.minV = 1; // min allowed value (brightness) (0 - 100) 994 | this.maxV = 100; // max allowed value (brightness) (0 - 100) 995 | 996 | // Accessing the picked color 997 | // 998 | this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] 999 | this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] 1000 | 1001 | // Color Picker options 1002 | // 1003 | this.width = 181; // width of color palette (in px) 1004 | this.height = 101; // height of color palette (in px) 1005 | this.showOnClick = true; // whether to display the color picker when user clicks on its target element 1006 | this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls 1007 | this.position = 'bottom'; // left | right | top | bottom - position relative to the target element 1008 | this.smartPosition = true; // automatically change picker position when there is not enough space for it 1009 | this.sliderSize = 16; // px 1010 | this.crossSize = 8; // px 1011 | this.closable = false; // whether to display the Close button 1012 | this.closeText = 'Close'; 1013 | this.buttonColor = '#000000'; // CSS color 1014 | this.buttonHeight = 18; // px 1015 | this.padding = 12; // px 1016 | this.backgroundColor = '#FFFFFF'; // CSS color 1017 | this.borderWidth = 1; // px 1018 | this.borderColor = '#BBBBBB'; // CSS color 1019 | this.borderRadius = 8; // px 1020 | this.insetWidth = 1; // px 1021 | this.insetColor = '#BBBBBB'; // CSS color 1022 | this.shadow = true; // whether to display shadow 1023 | this.shadowBlur = 15; // px 1024 | this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color 1025 | this.pointerColor = '#4C4C4C'; // px 1026 | this.pointerBorderColor = '#FFFFFF'; // px 1027 | this.pointerBorderWidth = 1; // px 1028 | this.pointerThickness = 2; // px 1029 | this.zIndex = 1000; 1030 | this.container = null; // where to append the color picker (BODY element by default) 1031 | 1032 | 1033 | for (var opt in options) { 1034 | if (options.hasOwnProperty(opt)) { 1035 | this[opt] = options[opt]; 1036 | } 1037 | } 1038 | 1039 | 1040 | this.hide = function () { 1041 | if (isPickerOwner()) { 1042 | detachPicker(); 1043 | } 1044 | }; 1045 | 1046 | 1047 | this.show = function () { 1048 | drawPicker(); 1049 | }; 1050 | 1051 | 1052 | this.redraw = function () { 1053 | if (isPickerOwner()) { 1054 | drawPicker(); 1055 | } 1056 | }; 1057 | 1058 | 1059 | this.importColor = function () { 1060 | if (!this.valueElement) { 1061 | this.exportColor(); 1062 | } else { 1063 | if (jsc.isElementType(this.valueElement, 'input')) { 1064 | if (!this.refine) { 1065 | if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { 1066 | if (this.styleElement) { 1067 | this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1068 | this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1069 | this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1070 | } 1071 | this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1072 | } 1073 | } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { 1074 | this.valueElement.value = ''; 1075 | if (this.styleElement) { 1076 | this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1077 | this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1078 | this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1079 | } 1080 | this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1081 | 1082 | } else if (this.fromString(this.valueElement.value)) { 1083 | // managed to import color successfully from the value -> OK, don't do anything 1084 | } else { 1085 | this.exportColor(); 1086 | } 1087 | } else { 1088 | // not an input element -> doesn't have any value 1089 | this.exportColor(); 1090 | } 1091 | } 1092 | }; 1093 | 1094 | 1095 | this.exportColor = function (flags) { 1096 | if (!(flags & jsc.leaveValue) && this.valueElement) { 1097 | var value = this.toString(); 1098 | if (this.uppercase) { value = value.toUpperCase(); } 1099 | if (this.hash) { value = '#' + value; } 1100 | 1101 | if (jsc.isElementType(this.valueElement, 'input')) { 1102 | this.valueElement.value = value; 1103 | } else { 1104 | this.valueElement.innerHTML = value; 1105 | } 1106 | } 1107 | if (!(flags & jsc.leaveStyle)) { 1108 | if (this.styleElement) { 1109 | var bgColor = '#' + this.toString(); 1110 | var fgColor = this.isLight() ? '#000' : '#FFF'; 1111 | 1112 | this.styleElement.style.backgroundImage = 'none'; 1113 | this.styleElement.style.backgroundColor = bgColor; 1114 | this.styleElement.style.color = fgColor; 1115 | 1116 | if (this.overwriteImportant) { 1117 | this.styleElement.setAttribute('style', 1118 | 'background: ' + bgColor + ' !important; ' + 1119 | 'color: ' + fgColor + ' !important;' 1120 | ); 1121 | } 1122 | } 1123 | } 1124 | if (!(flags & jsc.leavePad) && isPickerOwner()) { 1125 | redrawPad(); 1126 | } 1127 | if (!(flags & jsc.leaveSld) && isPickerOwner()) { 1128 | redrawSld(); 1129 | } 1130 | }; 1131 | 1132 | 1133 | // h: 0-360 1134 | // s: 0-100 1135 | // v: 0-100 1136 | // 1137 | this.fromHSV = function (h, s, v, flags) { // null = don't change 1138 | if (h !== null) { 1139 | if (isNaN(h)) { return false; } 1140 | h = Math.max(0, Math.min(360, h)); 1141 | } 1142 | if (s !== null) { 1143 | if (isNaN(s)) { return false; } 1144 | s = Math.max(0, Math.min(100, this.maxS, s), this.minS); 1145 | } 1146 | if (v !== null) { 1147 | if (isNaN(v)) { return false; } 1148 | v = Math.max(0, Math.min(100, this.maxV, v), this.minV); 1149 | } 1150 | 1151 | this.rgb = HSV_RGB( 1152 | h===null ? this.hsv[0] : (this.hsv[0]=h), 1153 | s===null ? this.hsv[1] : (this.hsv[1]=s), 1154 | v===null ? this.hsv[2] : (this.hsv[2]=v) 1155 | ); 1156 | 1157 | this.exportColor(flags); 1158 | }; 1159 | 1160 | 1161 | // r: 0-255 1162 | // g: 0-255 1163 | // b: 0-255 1164 | // 1165 | this.fromRGB = function (r, g, b, flags) { // null = don't change 1166 | if (r !== null) { 1167 | if (isNaN(r)) { return false; } 1168 | r = Math.max(0, Math.min(255, r)); 1169 | } 1170 | if (g !== null) { 1171 | if (isNaN(g)) { return false; } 1172 | g = Math.max(0, Math.min(255, g)); 1173 | } 1174 | if (b !== null) { 1175 | if (isNaN(b)) { return false; } 1176 | b = Math.max(0, Math.min(255, b)); 1177 | } 1178 | 1179 | var hsv = RGB_HSV( 1180 | r===null ? this.rgb[0] : r, 1181 | g===null ? this.rgb[1] : g, 1182 | b===null ? this.rgb[2] : b 1183 | ); 1184 | if (hsv[0] !== null) { 1185 | this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); 1186 | } 1187 | if (hsv[2] !== 0) { 1188 | this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); 1189 | } 1190 | this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); 1191 | 1192 | // update RGB according to final HSV, as some values might be trimmed 1193 | var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 1194 | this.rgb[0] = rgb[0]; 1195 | this.rgb[1] = rgb[1]; 1196 | this.rgb[2] = rgb[2]; 1197 | 1198 | this.exportColor(flags); 1199 | }; 1200 | 1201 | 1202 | this.fromString = function (str, flags) { 1203 | var m; 1204 | if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { 1205 | // HEX notation 1206 | // 1207 | 1208 | if (m[1].length === 6) { 1209 | // 6-char notation 1210 | this.fromRGB( 1211 | parseInt(m[1].substr(0,2),16), 1212 | parseInt(m[1].substr(2,2),16), 1213 | parseInt(m[1].substr(4,2),16), 1214 | flags 1215 | ); 1216 | } else { 1217 | // 3-char notation 1218 | this.fromRGB( 1219 | parseInt(m[1].charAt(0) + m[1].charAt(0),16), 1220 | parseInt(m[1].charAt(1) + m[1].charAt(1),16), 1221 | parseInt(m[1].charAt(2) + m[1].charAt(2),16), 1222 | flags 1223 | ); 1224 | } 1225 | return true; 1226 | 1227 | } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { 1228 | var params = m[1].split(','); 1229 | var re = /^\s*(\d*)(\.\d+)?\s*$/; 1230 | var mR, mG, mB; 1231 | if ( 1232 | params.length >= 3 && 1233 | (mR = params[0].match(re)) && 1234 | (mG = params[1].match(re)) && 1235 | (mB = params[2].match(re)) 1236 | ) { 1237 | var r = parseFloat((mR[1] || '0') + (mR[2] || '')); 1238 | var g = parseFloat((mG[1] || '0') + (mG[2] || '')); 1239 | var b = parseFloat((mB[1] || '0') + (mB[2] || '')); 1240 | this.fromRGB(r, g, b, flags); 1241 | return true; 1242 | } 1243 | } 1244 | return false; 1245 | }; 1246 | 1247 | 1248 | this.toString = function () { 1249 | return ( 1250 | (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + 1251 | (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + 1252 | (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) 1253 | ); 1254 | }; 1255 | 1256 | 1257 | this.toHEXString = function () { 1258 | return '#' + this.toString().toUpperCase(); 1259 | }; 1260 | 1261 | 1262 | this.toRGBString = function () { 1263 | return ('rgb(' + 1264 | Math.round(this.rgb[0]) + ',' + 1265 | Math.round(this.rgb[1]) + ',' + 1266 | Math.round(this.rgb[2]) + ')' 1267 | ); 1268 | }; 1269 | 1270 | 1271 | this.isLight = function () { 1272 | return ( 1273 | 0.213 * this.rgb[0] + 1274 | 0.715 * this.rgb[1] + 1275 | 0.072 * this.rgb[2] > 1276 | 255 / 2 1277 | ); 1278 | }; 1279 | 1280 | 1281 | this._processParentElementsInDOM = function () { 1282 | if (this._linkedElementsProcessed) { return; } 1283 | this._linkedElementsProcessed = true; 1284 | 1285 | var elm = this.targetElement; 1286 | do { 1287 | // If the target element or one of its parent nodes has fixed position, 1288 | // then use fixed positioning instead 1289 | // 1290 | // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1291 | // that's why we need to check if the returned style object is non-empty 1292 | var currStyle = jsc.getStyle(elm); 1293 | if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1294 | this.fixed = true; 1295 | } 1296 | 1297 | if (elm !== this.targetElement) { 1298 | // Ensure to attach onParentScroll only once to each parent element 1299 | // (multiple targetElements can share the same parent nodes) 1300 | // 1301 | // Note: It's not just offsetParents that can be scrollable, 1302 | // that's why we loop through all parent nodes 1303 | if (!elm._jscEventsAttached) { 1304 | jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1305 | elm._jscEventsAttached = true; 1306 | } 1307 | } 1308 | } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); 1309 | }; 1310 | 1311 | 1312 | // r: 0-255 1313 | // g: 0-255 1314 | // b: 0-255 1315 | // 1316 | // returns: [ 0-360, 0-100, 0-100 ] 1317 | // 1318 | function RGB_HSV (r, g, b) { 1319 | r /= 255; 1320 | g /= 255; 1321 | b /= 255; 1322 | var n = Math.min(Math.min(r,g),b); 1323 | var v = Math.max(Math.max(r,g),b); 1324 | var m = v - n; 1325 | if (m === 0) { return [ null, 0, 100 * v ]; } 1326 | var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 1327 | return [ 1328 | 60 * (h===6?0:h), 1329 | 100 * (m/v), 1330 | 100 * v 1331 | ]; 1332 | } 1333 | 1334 | 1335 | // h: 0-360 1336 | // s: 0-100 1337 | // v: 0-100 1338 | // 1339 | // returns: [ 0-255, 0-255, 0-255 ] 1340 | // 1341 | function HSV_RGB (h, s, v) { 1342 | var u = 255 * (v / 100); 1343 | 1344 | if (h === null) { 1345 | return [ u, u, u ]; 1346 | } 1347 | 1348 | h /= 60; 1349 | s /= 100; 1350 | 1351 | var i = Math.floor(h); 1352 | var f = i%2 ? h-i : 1-(h-i); 1353 | var m = u * (1 - s); 1354 | var n = u * (1 - s * f); 1355 | switch (i) { 1356 | case 6: 1357 | case 0: return [u,n,m]; 1358 | case 1: return [n,u,m]; 1359 | case 2: return [m,u,n]; 1360 | case 3: return [m,n,u]; 1361 | case 4: return [n,m,u]; 1362 | case 5: return [u,m,n]; 1363 | } 1364 | } 1365 | 1366 | 1367 | function detachPicker () { 1368 | jsc.unsetClass(THIS.targetElement, THIS.activeClass); 1369 | jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); 1370 | delete jsc.picker.owner; 1371 | } 1372 | 1373 | 1374 | function drawPicker () { 1375 | 1376 | // At this point, when drawing the picker, we know what the parent elements are 1377 | // and we can do all related DOM operations, such as registering events on them 1378 | // or checking their positioning 1379 | THIS._processParentElementsInDOM(); 1380 | 1381 | if (!jsc.picker) { 1382 | jsc.picker = { 1383 | owner: null, 1384 | wrap : document.createElement('div'), 1385 | box : document.createElement('div'), 1386 | boxS : document.createElement('div'), // shadow area 1387 | boxB : document.createElement('div'), // border 1388 | pad : document.createElement('div'), 1389 | padB : document.createElement('div'), // border 1390 | padM : document.createElement('div'), // mouse/touch area 1391 | padPal : jsc.createPalette(), 1392 | cross : document.createElement('div'), 1393 | crossBY : document.createElement('div'), // border Y 1394 | crossBX : document.createElement('div'), // border X 1395 | crossLY : document.createElement('div'), // line Y 1396 | crossLX : document.createElement('div'), // line X 1397 | sld : document.createElement('div'), 1398 | sldB : document.createElement('div'), // border 1399 | sldM : document.createElement('div'), // mouse/touch area 1400 | sldGrad : jsc.createSliderGradient(), 1401 | sldPtrS : document.createElement('div'), // slider pointer spacer 1402 | sldPtrIB : document.createElement('div'), // slider pointer inner border 1403 | sldPtrMB : document.createElement('div'), // slider pointer middle border 1404 | sldPtrOB : document.createElement('div'), // slider pointer outer border 1405 | btn : document.createElement('div'), 1406 | btnT : document.createElement('span') // text 1407 | }; 1408 | 1409 | jsc.picker.pad.appendChild(jsc.picker.padPal.elm); 1410 | jsc.picker.padB.appendChild(jsc.picker.pad); 1411 | jsc.picker.cross.appendChild(jsc.picker.crossBY); 1412 | jsc.picker.cross.appendChild(jsc.picker.crossBX); 1413 | jsc.picker.cross.appendChild(jsc.picker.crossLY); 1414 | jsc.picker.cross.appendChild(jsc.picker.crossLX); 1415 | jsc.picker.padB.appendChild(jsc.picker.cross); 1416 | jsc.picker.box.appendChild(jsc.picker.padB); 1417 | jsc.picker.box.appendChild(jsc.picker.padM); 1418 | 1419 | jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); 1420 | jsc.picker.sldB.appendChild(jsc.picker.sld); 1421 | jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); 1422 | jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); 1423 | jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); 1424 | jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); 1425 | jsc.picker.box.appendChild(jsc.picker.sldB); 1426 | jsc.picker.box.appendChild(jsc.picker.sldM); 1427 | 1428 | jsc.picker.btn.appendChild(jsc.picker.btnT); 1429 | jsc.picker.box.appendChild(jsc.picker.btn); 1430 | 1431 | jsc.picker.boxB.appendChild(jsc.picker.box); 1432 | jsc.picker.wrap.appendChild(jsc.picker.boxS); 1433 | jsc.picker.wrap.appendChild(jsc.picker.boxB); 1434 | } 1435 | 1436 | var p = jsc.picker; 1437 | 1438 | var displaySlider = !!jsc.getSliderComponent(THIS); 1439 | var dims = jsc.getPickerDims(THIS); 1440 | var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1441 | var padToSliderPadding = jsc.getPadToSliderPadding(THIS); 1442 | var borderRadius = Math.min( 1443 | THIS.borderRadius, 1444 | Math.round(THIS.padding * Math.PI)); // px 1445 | var padCursor = 'crosshair'; 1446 | 1447 | // wrap 1448 | p.wrap.style.clear = 'both'; 1449 | p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; 1450 | p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; 1451 | p.wrap.style.zIndex = THIS.zIndex; 1452 | 1453 | // picker 1454 | p.box.style.width = dims[0] + 'px'; 1455 | p.box.style.height = dims[1] + 'px'; 1456 | 1457 | p.boxS.style.position = 'absolute'; 1458 | p.boxS.style.left = '0'; 1459 | p.boxS.style.top = '0'; 1460 | p.boxS.style.width = '100%'; 1461 | p.boxS.style.height = '100%'; 1462 | jsc.setBorderRadius(p.boxS, borderRadius + 'px'); 1463 | 1464 | // picker border 1465 | p.boxB.style.position = 'relative'; 1466 | p.boxB.style.border = THIS.borderWidth + 'px solid'; 1467 | p.boxB.style.borderColor = THIS.borderColor; 1468 | p.boxB.style.background = THIS.backgroundColor; 1469 | jsc.setBorderRadius(p.boxB, borderRadius + 'px'); 1470 | 1471 | // IE hack: 1472 | // If the element is transparent, IE will trigger the event on the elements under it, 1473 | // e.g. on Canvas or on elements with border 1474 | p.padM.style.background = 1475 | p.sldM.style.background = 1476 | '#FFF'; 1477 | jsc.setStyle(p.padM, 'opacity', '0'); 1478 | jsc.setStyle(p.sldM, 'opacity', '0'); 1479 | 1480 | // pad 1481 | p.pad.style.position = 'relative'; 1482 | p.pad.style.width = THIS.width + 'px'; 1483 | p.pad.style.height = THIS.height + 'px'; 1484 | 1485 | // pad palettes (HSV and HVS) 1486 | p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); 1487 | 1488 | // pad border 1489 | p.padB.style.position = 'absolute'; 1490 | p.padB.style.left = THIS.padding + 'px'; 1491 | p.padB.style.top = THIS.padding + 'px'; 1492 | p.padB.style.border = THIS.insetWidth + 'px solid'; 1493 | p.padB.style.borderColor = THIS.insetColor; 1494 | 1495 | // pad mouse area 1496 | p.padM._jscInstance = THIS; 1497 | p.padM._jscControlName = 'pad'; 1498 | p.padM.style.position = 'absolute'; 1499 | p.padM.style.left = '0'; 1500 | p.padM.style.top = '0'; 1501 | p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; 1502 | p.padM.style.height = dims[1] + 'px'; 1503 | p.padM.style.cursor = padCursor; 1504 | 1505 | // pad cross 1506 | p.cross.style.position = 'absolute'; 1507 | p.cross.style.left = 1508 | p.cross.style.top = 1509 | '0'; 1510 | p.cross.style.width = 1511 | p.cross.style.height = 1512 | crossOuterSize + 'px'; 1513 | 1514 | // pad cross border Y and X 1515 | p.crossBY.style.position = 1516 | p.crossBX.style.position = 1517 | 'absolute'; 1518 | p.crossBY.style.background = 1519 | p.crossBX.style.background = 1520 | THIS.pointerBorderColor; 1521 | p.crossBY.style.width = 1522 | p.crossBX.style.height = 1523 | (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1524 | p.crossBY.style.height = 1525 | p.crossBX.style.width = 1526 | crossOuterSize + 'px'; 1527 | p.crossBY.style.left = 1528 | p.crossBX.style.top = 1529 | (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; 1530 | p.crossBY.style.top = 1531 | p.crossBX.style.left = 1532 | '0'; 1533 | 1534 | // pad cross line Y and X 1535 | p.crossLY.style.position = 1536 | p.crossLX.style.position = 1537 | 'absolute'; 1538 | p.crossLY.style.background = 1539 | p.crossLX.style.background = 1540 | THIS.pointerColor; 1541 | p.crossLY.style.height = 1542 | p.crossLX.style.width = 1543 | (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; 1544 | p.crossLY.style.width = 1545 | p.crossLX.style.height = 1546 | THIS.pointerThickness + 'px'; 1547 | p.crossLY.style.left = 1548 | p.crossLX.style.top = 1549 | (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; 1550 | p.crossLY.style.top = 1551 | p.crossLX.style.left = 1552 | THIS.pointerBorderWidth + 'px'; 1553 | 1554 | // slider 1555 | p.sld.style.overflow = 'hidden'; 1556 | p.sld.style.width = THIS.sliderSize + 'px'; 1557 | p.sld.style.height = THIS.height + 'px'; 1558 | 1559 | // slider gradient 1560 | p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); 1561 | 1562 | // slider border 1563 | p.sldB.style.display = displaySlider ? 'block' : 'none'; 1564 | p.sldB.style.position = 'absolute'; 1565 | p.sldB.style.right = THIS.padding + 'px'; 1566 | p.sldB.style.top = THIS.padding + 'px'; 1567 | p.sldB.style.border = THIS.insetWidth + 'px solid'; 1568 | p.sldB.style.borderColor = THIS.insetColor; 1569 | 1570 | // slider mouse area 1571 | p.sldM._jscInstance = THIS; 1572 | p.sldM._jscControlName = 'sld'; 1573 | p.sldM.style.display = displaySlider ? 'block' : 'none'; 1574 | p.sldM.style.position = 'absolute'; 1575 | p.sldM.style.right = '0'; 1576 | p.sldM.style.top = '0'; 1577 | p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; 1578 | p.sldM.style.height = dims[1] + 'px'; 1579 | p.sldM.style.cursor = 'default'; 1580 | 1581 | // slider pointer inner and outer border 1582 | p.sldPtrIB.style.border = 1583 | p.sldPtrOB.style.border = 1584 | THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 1585 | 1586 | // slider pointer outer border 1587 | p.sldPtrOB.style.position = 'absolute'; 1588 | p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1589 | p.sldPtrOB.style.top = '0'; 1590 | 1591 | // slider pointer middle border 1592 | p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 1593 | 1594 | // slider pointer spacer 1595 | p.sldPtrS.style.width = THIS.sliderSize + 'px'; 1596 | p.sldPtrS.style.height = sliderPtrSpace + 'px'; 1597 | 1598 | // the Close button 1599 | function setBtnBorder () { 1600 | var insetColors = THIS.insetColor.split(/\s+/); 1601 | var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 1602 | p.btn.style.borderColor = outsetColor; 1603 | } 1604 | p.btn.style.display = THIS.closable ? 'block' : 'none'; 1605 | p.btn.style.position = 'absolute'; 1606 | p.btn.style.left = THIS.padding + 'px'; 1607 | p.btn.style.bottom = THIS.padding + 'px'; 1608 | p.btn.style.padding = '0 15px'; 1609 | p.btn.style.height = THIS.buttonHeight + 'px'; 1610 | p.btn.style.border = THIS.insetWidth + 'px solid'; 1611 | setBtnBorder(); 1612 | p.btn.style.color = THIS.buttonColor; 1613 | p.btn.style.font = '12px sans-serif'; 1614 | p.btn.style.textAlign = 'center'; 1615 | try { 1616 | p.btn.style.cursor = 'pointer'; 1617 | } catch(eOldIE) { 1618 | p.btn.style.cursor = 'hand'; 1619 | } 1620 | p.btn.onmousedown = function () { 1621 | THIS.hide(); 1622 | }; 1623 | p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; 1624 | p.btnT.innerHTML = ''; 1625 | p.btnT.appendChild(document.createTextNode(THIS.closeText)); 1626 | 1627 | // place pointers 1628 | redrawPad(); 1629 | redrawSld(); 1630 | 1631 | // If we are changing the owner without first closing the picker, 1632 | // make sure to first deal with the old owner 1633 | if (jsc.picker.owner && jsc.picker.owner !== THIS) { 1634 | jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); 1635 | } 1636 | 1637 | // Set the new picker owner 1638 | jsc.picker.owner = THIS; 1639 | 1640 | // The redrawPosition() method needs picker.owner to be set, that's why we call it here, 1641 | // after setting the owner 1642 | if (jsc.isElementType(container, 'body')) { 1643 | jsc.redrawPosition(); 1644 | } else { 1645 | jsc._drawPosition(THIS, 0, 0, 'relative', false); 1646 | } 1647 | 1648 | if (p.wrap.parentNode != container) { 1649 | container.appendChild(p.wrap); 1650 | } 1651 | 1652 | jsc.setClass(THIS.targetElement, THIS.activeClass); 1653 | } 1654 | 1655 | 1656 | function redrawPad () { 1657 | // redraw the pad pointer 1658 | switch (jsc.getPadYComponent(THIS)) { 1659 | case 's': var yComponent = 1; break; 1660 | case 'v': var yComponent = 2; break; 1661 | } 1662 | var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); 1663 | var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1664 | var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1665 | var ofs = -Math.floor(crossOuterSize / 2); 1666 | jsc.picker.cross.style.left = (x + ofs) + 'px'; 1667 | jsc.picker.cross.style.top = (y + ofs) + 'px'; 1668 | 1669 | // redraw the slider 1670 | switch (jsc.getSliderComponent(THIS)) { 1671 | case 's': 1672 | var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); 1673 | var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); 1674 | var color1 = 'rgb(' + 1675 | Math.round(rgb1[0]) + ',' + 1676 | Math.round(rgb1[1]) + ',' + 1677 | Math.round(rgb1[2]) + ')'; 1678 | var color2 = 'rgb(' + 1679 | Math.round(rgb2[0]) + ',' + 1680 | Math.round(rgb2[1]) + ',' + 1681 | Math.round(rgb2[2]) + ')'; 1682 | jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1683 | break; 1684 | case 'v': 1685 | var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); 1686 | var color1 = 'rgb(' + 1687 | Math.round(rgb[0]) + ',' + 1688 | Math.round(rgb[1]) + ',' + 1689 | Math.round(rgb[2]) + ')'; 1690 | var color2 = '#000'; 1691 | jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1692 | break; 1693 | } 1694 | } 1695 | 1696 | 1697 | function redrawSld () { 1698 | var sldComponent = jsc.getSliderComponent(THIS); 1699 | if (sldComponent) { 1700 | // redraw the slider pointer 1701 | switch (sldComponent) { 1702 | case 's': var yComponent = 1; break; 1703 | case 'v': var yComponent = 2; break; 1704 | } 1705 | var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1706 | jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; 1707 | } 1708 | } 1709 | 1710 | 1711 | function isPickerOwner () { 1712 | return jsc.picker && jsc.picker.owner === THIS; 1713 | } 1714 | 1715 | 1716 | function blurValue () { 1717 | THIS.importColor(); 1718 | } 1719 | 1720 | 1721 | // Find the target element 1722 | if (typeof targetElement === 'string') { 1723 | var id = targetElement; 1724 | var elm = document.getElementById(id); 1725 | if (elm) { 1726 | this.targetElement = elm; 1727 | } else { 1728 | jsc.warn('Could not find target element with ID \'' + id + '\''); 1729 | } 1730 | } else if (targetElement) { 1731 | this.targetElement = targetElement; 1732 | } else { 1733 | jsc.warn('Invalid target element: \'' + targetElement + '\''); 1734 | } 1735 | 1736 | if (this.targetElement._jscLinkedInstance) { 1737 | jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); 1738 | return; 1739 | } 1740 | this.targetElement._jscLinkedInstance = this; 1741 | 1742 | // Find the value element 1743 | this.valueElement = jsc.fetchElement(this.valueElement); 1744 | // Find the style element 1745 | this.styleElement = jsc.fetchElement(this.styleElement); 1746 | 1747 | var THIS = this; 1748 | var container = 1749 | this.container ? 1750 | jsc.fetchElement(this.container) : 1751 | document.getElementsByTagName('body')[0]; 1752 | var sliderPtrSpace = 3; // px 1753 | 1754 | // For BUTTON elements it's important to stop them from sending the form when clicked 1755 | // (e.g. in Safari) 1756 | if (jsc.isElementType(this.targetElement, 'button')) { 1757 | if (this.targetElement.onclick) { 1758 | var origCallback = this.targetElement.onclick; 1759 | this.targetElement.onclick = function (evt) { 1760 | origCallback.call(this, evt); 1761 | return false; 1762 | }; 1763 | } else { 1764 | this.targetElement.onclick = function () { return false; }; 1765 | } 1766 | } 1767 | 1768 | /* 1769 | var elm = this.targetElement; 1770 | do { 1771 | // If the target element or one of its offsetParents has fixed position, 1772 | // then use fixed positioning instead 1773 | // 1774 | // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1775 | // that's why we need to check if the returned style object is non-empty 1776 | var currStyle = jsc.getStyle(elm); 1777 | if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1778 | this.fixed = true; 1779 | } 1780 | 1781 | if (elm !== this.targetElement) { 1782 | // attach onParentScroll so that we can recompute the picker position 1783 | // when one of the offsetParents is scrolled 1784 | if (!elm._jscEventsAttached) { 1785 | jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1786 | elm._jscEventsAttached = true; 1787 | } 1788 | } 1789 | } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); 1790 | */ 1791 | 1792 | // valueElement 1793 | if (this.valueElement) { 1794 | if (jsc.isElementType(this.valueElement, 'input')) { 1795 | var updateField = function () { 1796 | THIS.fromString(THIS.valueElement.value, jsc.leaveValue); 1797 | jsc.dispatchFineChange(THIS); 1798 | }; 1799 | jsc.attachEvent(this.valueElement, 'keyup', updateField); 1800 | jsc.attachEvent(this.valueElement, 'input', updateField); 1801 | jsc.attachEvent(this.valueElement, 'blur', blurValue); 1802 | this.valueElement.setAttribute('autocomplete', 'off'); 1803 | } 1804 | } 1805 | 1806 | // styleElement 1807 | if (this.styleElement) { 1808 | this.styleElement._jscOrigStyle = { 1809 | backgroundImage : this.styleElement.style.backgroundImage, 1810 | backgroundColor : this.styleElement.style.backgroundColor, 1811 | color : this.styleElement.style.color 1812 | }; 1813 | } 1814 | 1815 | if (this.value) { 1816 | // Try to set the color from the .value option and if unsuccessful, 1817 | // export the current color 1818 | this.fromString(this.value) || this.exportColor(); 1819 | } else { 1820 | this.importColor(); 1821 | } 1822 | } 1823 | 1824 | }; 1825 | 1826 | 1827 | //================================ 1828 | // Public properties and methods 1829 | //================================ 1830 | 1831 | 1832 | // By default, search for all elements with class="jscolor" and install a color picker on them. 1833 | // 1834 | // You can change what class name will be looked for by setting the property jscolor.lookupClass 1835 | // anywhere in your HTML document. To completely disable the automatic lookup, set it to null. 1836 | // 1837 | jsc.jscolor.lookupClass = 'jscolor'; 1838 | 1839 | 1840 | jsc.jscolor.installByClassName = function (className) { 1841 | var inputElms = document.getElementsByTagName('input'); 1842 | var buttonElms = document.getElementsByTagName('button'); 1843 | 1844 | jsc.tryInstallOnElements(inputElms, className); 1845 | jsc.tryInstallOnElements(buttonElms, className); 1846 | }; 1847 | 1848 | 1849 | jsc.register(); 1850 | 1851 | 1852 | return jsc.jscolor; 1853 | 1854 | 1855 | })(); } 1856 | -------------------------------------------------------------------------------- /plugin/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Github Heatmap Customizer", 4 | "description": "Github Heatmap Colorizer", 5 | "version": "1.0.0", 6 | "browser_action": { 7 | "default_popup":"popup.html", 8 | "default_title": "Github Heatmap Colorizer" 9 | }, 10 | "permissions": [ 11 | "activeTab", 12 | "storage", 13 | "http://*.github.com/*", 14 | "https://*.github.com/*" 15 | ], 16 | "background": { 17 | "scripts": ["background.js"], 18 | "persistent": false 19 | }, 20 | "icons": { 21 | "16": "icons/logo_16x16.png", 22 | "48": "icons/logo_48x48.png", 23 | "128": "icons/logo_128x128.png" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /plugin/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Heatmap Customizer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 41 | 42 |

Choose Main Color

43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /plugin/popup.js: -------------------------------------------------------------------------------- 1 | 2 | $(function(){ 3 | let color 4 | $('#btnChange').click(function(){ 5 | color = $('#fontColor').val(); 6 | // Save color to chrome storage 7 | chrome.storage.local.set({color}, function() { 8 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 9 | chrome.tabs.executeScript( 10 | tabs[0].id, 11 | { file: 'content.js' }); 12 | }); 13 | }) 14 | }) 15 | 16 | $("#fontColor").on("change paste keyup", function() { 17 | color = $(this).val(); 18 | }); 19 | 20 | chrome.storage.local.get('color', function(data) { 21 | if(data.color) 22 | { 23 | $('#fontColor').val(data.color).css('background-color', `#${data.color}`).click() 24 | // Activate script 25 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 26 | chrome.tabs.executeScript( 27 | tabs[0].id, 28 | { file: 'content.js' }); 29 | }); 30 | } 31 | }) 32 | }); 33 | -------------------------------------------------------------------------------- /popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Heatmap Customizer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 41 | 42 |

Choose Main Color

43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /popup.js: -------------------------------------------------------------------------------- 1 | 2 | $(function(){ 3 | let color 4 | $('#btnChange').click(function(){ 5 | color = $('#fontColor').val(); 6 | // Save color to chrome storage 7 | chrome.storage.local.set({color}, function() { 8 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 9 | chrome.tabs.executeScript( 10 | tabs[0].id, 11 | { file: 'content.js' }); 12 | }); 13 | }) 14 | }) 15 | 16 | $("#fontColor").on("change paste keyup", function() { 17 | color = $(this).val(); 18 | }); 19 | 20 | chrome.storage.local.get('color', function(data) { 21 | if(data.color) 22 | { 23 | $('#fontColor').val(data.color).css('background-color', `#${data.color}`).click() 24 | // Activate script 25 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 26 | chrome.tabs.executeScript( 27 | tabs[0].id, 28 | { file: 'content.js' }); 29 | }); 30 | } 31 | }) 32 | }); 33 | --------------------------------------------------------------------------------