Skip to content

Commit 2a29bec

Browse files
committed
#144: added favicon for executing/finished scripts
1 parent 8f1757d commit 2a29bec

2 files changed

Lines changed: 169 additions & 1 deletion

File tree

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import {isNull} from '../../common';
2+
3+
export const defaultFavicon = createDefaultFavicon();
4+
export let executingFavicon;
5+
export let finishedFavicon;
6+
7+
const FILL_COLOR = '#66FF00';
8+
const STROKE_COLOR = '#444444';
9+
10+
let currentIcon = 'defaultFavicon';
11+
12+
const faviconImage = new Image();
13+
faviconImage.src = defaultFavicon.href;
14+
15+
faviconImage.onload = function () {
16+
executingFavicon = createExecutingFavicon(faviconImage);
17+
finishedFavicon = createFinishedFavicon(faviconImage);
18+
19+
setCurrentIcon();
20+
};
21+
22+
export function setDefaultFavicon() {
23+
currentIcon = 'defaultFavicon';
24+
25+
setFavicon(defaultFavicon);
26+
}
27+
28+
export function setExecutingFavicon() {
29+
currentIcon = 'executingFavicon';
30+
31+
if (!isNull(executingFavicon)) {
32+
setFavicon(executingFavicon);
33+
} else {
34+
setFavicon(defaultFavicon);
35+
}
36+
}
37+
38+
export function setFinishedFavicon() {
39+
currentIcon = 'finishedFavicon';
40+
41+
if (!isNull(finishedFavicon)) {
42+
setFavicon(finishedFavicon);
43+
} else {
44+
setFavicon(defaultFavicon);
45+
}
46+
}
47+
48+
function setFavicon(favicon) {
49+
const head = document.getElementsByTagName('head')[0];
50+
51+
for (let i = 0; i < head.childNodes.length; i++) {
52+
const child = head.childNodes[i];
53+
if ((child.tagName === 'LINK') && (child.type === 'image/x-icon')) {
54+
head.replaceChild(favicon, child);
55+
return;
56+
}
57+
}
58+
59+
head.appendChild(favicon);
60+
}
61+
62+
function setCurrentIcon() {
63+
switch (currentIcon) {
64+
case 'executingFavicon': {
65+
setFavicon(executingFavicon);
66+
return;
67+
}
68+
case 'finishedFavicon': {
69+
setFavicon(finishedFavicon);
70+
return;
71+
}
72+
default: {
73+
setFavicon(defaultFavicon);
74+
}
75+
}
76+
}
77+
78+
79+
function createLink(href) {
80+
const link = document.createElement('link');
81+
link.type = 'image/x-icon';
82+
link.rel = 'shortcut icon';
83+
link.href = href;
84+
return link;
85+
}
86+
87+
function createDefaultFavicon() {
88+
return createLink('favicon.ico');
89+
}
90+
91+
function createExecutingFavicon(baseImage) {
92+
const {canvas, context} = prepareCanvas(baseImage);
93+
94+
const radius = baseImage.width / 5;
95+
const offsetX = baseImage.width / 4 * 3;
96+
const offsetY = baseImage.height / 4 * 3;
97+
98+
context.beginPath();
99+
context.arc(offsetX, offsetY, radius, 0, 2 * Math.PI, true);
100+
context.fillStyle = FILL_COLOR;
101+
context.fill();
102+
103+
context.lineWidth = 2;
104+
context.strokeStyle = STROKE_COLOR;
105+
context.stroke();
106+
107+
return createLink(canvas.toDataURL('image/x-icon'));
108+
}
109+
110+
function createFinishedFavicon(baseImage) {
111+
const {canvas, context} = prepareCanvas(baseImage);
112+
113+
const offsetX = baseImage.width / 4 * 3;
114+
const offsetY = baseImage.height / 4 * 3;
115+
116+
function drawTick(lineWidth, color) {
117+
context.beginPath();
118+
context.moveTo(offsetX - 10, offsetY - 8);
119+
context.lineTo(offsetX, offsetY + 8);
120+
context.lineTo(offsetX + 10, offsetY - 8);
121+
context.lineWidth = lineWidth;
122+
context.strokeStyle = color;
123+
context.stroke();
124+
}
125+
126+
drawTick(8, STROKE_COLOR);
127+
drawTick(5, FILL_COLOR);
128+
129+
return createLink(canvas.toDataURL('image/x-icon'));
130+
}
131+
132+
function prepareCanvas(baseImage) {
133+
const canvas = document.createElement('canvas');
134+
canvas.width = baseImage.width;
135+
canvas.height = baseImage.height;
136+
137+
const context = canvas.getContext('2d');
138+
context.drawImage(baseImage, 0, 0);
139+
140+
return {canvas, context};
141+
}

web-src/js/index.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
hasClass,
99
hide,
1010
HttpRequestError,
11-
HttpUnauthorizedError,
11+
HttpUnauthorizedError, isEmptyArray,
1212
isEmptyObject,
1313
isNull,
1414
logError,
@@ -19,6 +19,7 @@ import {
1919
} from './common';
2020

2121
import './connections/rxWebsocket.js';
22+
import {setDefaultFavicon, setExecutingFavicon, setFinishedFavicon} from './components/favicon/favicon_manager';
2223
import {ScriptController} from './script/script-controller';
2324
import {restoreExecutor} from './script/script-execution-model';
2425
import './style_imports.js';
@@ -44,6 +45,8 @@ function onLoad() {
4445
});
4546
scriptSelectionListeners.push(updateTitle);
4647

48+
setDefaultFavicon();
49+
4750
var response = authorizedCallHttp('scripts');
4851

4952
var scripts = JSON.parse(response);
@@ -85,6 +88,7 @@ function onLoad() {
8588
scriptMenuItems.set(script, scriptElement);
8689

8790
updateMenuItemState(script);
91+
updateFavicon();
8892
});
8993

9094
var contentPanel = document.getElementById('content-panel');
@@ -321,10 +325,12 @@ function initWelcomeIcon() {
321325
function addRunningExecutor(scriptExecutor) {
322326
runningScriptExecutors.push(scriptExecutor);
323327
updateMenuItemState(scriptExecutor.scriptName);
328+
updateFavicon();
324329

325330
scriptExecutor.addListener({
326331
'onExecutionStop': function () {
327332
updateMenuItemState(scriptExecutor.scriptName);
333+
updateFavicon();
328334
}
329335
})
330336
}
@@ -340,6 +346,7 @@ function showScript(selectedScript, parameterValues) {
340346
removeElements(runningScriptExecutors, executorsToRemove);
341347

342348
updateMenuItemState(previousScriptName);
349+
updateFavicon();
343350
}
344351

345352
activeScriptController.destroy();
@@ -459,6 +466,26 @@ function updateMenuItemState(scriptName) {
459466
}
460467
}
461468

469+
function updateFavicon() {
470+
if (isEmptyArray(runningScriptExecutors)) {
471+
setDefaultFavicon();
472+
return;
473+
}
474+
475+
let hasExecuting = false;
476+
runningScriptExecutors.forEach(function (executor) {
477+
if (!executor.isFinished()) {
478+
hasExecuting = true;
479+
}
480+
});
481+
482+
if (hasExecuting) {
483+
setExecutingFavicon();
484+
} else {
485+
setFinishedFavicon();
486+
}
487+
}
488+
462489
export function authorizedCallHttp(url, object, method, asyncHandler) {
463490
try {
464491
return callHttp(url, object, method, asyncHandler);

0 commit comments

Comments
 (0)