├── plugin.zip
├── icons
├── logo.png
├── logo_16x16.png
├── logo_48x48.png
└── logo_128x128.png
├── docs
├── blue_demo.png
├── demo.gif.gif
├── red_demo.png
├── newer-logo.xcf
├── purple_demo.png
└── yellow_demo.png
├── plugin
├── icons
│ ├── logo.png
│ ├── logo_16x16.png
│ ├── logo_48x48.png
│ └── logo_128x128.png
├── background.js
├── manifest.json
├── popup.js
├── popup.html
├── content.js
└── jscolor.js
├── background.js
├── manifest.json
├── popup.js
├── LICENSE
├── popup.html
├── README.md
├── content.js
├── jscolor.js
└── jquery.js
/plugin.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/plugin.zip
--------------------------------------------------------------------------------
/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/icons/logo.png
--------------------------------------------------------------------------------
/docs/blue_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/blue_demo.png
--------------------------------------------------------------------------------
/docs/demo.gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/demo.gif.gif
--------------------------------------------------------------------------------
/docs/red_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/red_demo.png
--------------------------------------------------------------------------------
/docs/newer-logo.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/newer-logo.xcf
--------------------------------------------------------------------------------
/docs/purple_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/purple_demo.png
--------------------------------------------------------------------------------
/docs/yellow_demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/docs/yellow_demo.png
--------------------------------------------------------------------------------
/icons/logo_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/icons/logo_16x16.png
--------------------------------------------------------------------------------
/icons/logo_48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/icons/logo_48x48.png
--------------------------------------------------------------------------------
/icons/logo_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/icons/logo_128x128.png
--------------------------------------------------------------------------------
/plugin/icons/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/plugin/icons/logo.png
--------------------------------------------------------------------------------
/plugin/icons/logo_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/plugin/icons/logo_16x16.png
--------------------------------------------------------------------------------
/plugin/icons/logo_48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/plugin/icons/logo_48x48.png
--------------------------------------------------------------------------------
/plugin/icons/logo_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pkellz/github-heatmap-colorizer/HEAD/plugin/icons/logo_128x128.png
--------------------------------------------------------------------------------
/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/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 | })
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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.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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
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 | |
32 |
33 | Purple | Yellow
34 | :----------------------------------:|:--------------------------------------:
35 | |
36 |
37 | ## :hammer: Usage
38 | 
39 |
40 | :family: Contributing
41 | ------------
42 |
43 | Contributions are welcome!
44 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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=/