new-js-clock 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +405 -0
- package/dist/index.cjs +684 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +676 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/globalTicker.d.ts +11 -0
- package/dist/internal/globalTicker.d.ts.map +1 -0
- package/dist/internal/globalTicker.js +132 -0
- package/dist/internal/globalTicker.js.map +1 -0
- package/dist/new-js-clock.js +686 -0
- package/dist/new-js-clock.js.map +7 -0
- package/dist/new-js-clock.min.js +10 -0
- package/dist/new-js-clock.min.js.map +7 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thiago Cavalcanti Pimenta
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# New JS Clock
|
|
2
|
+
|
|
3
|
+
A modern TypeScript rewrite of the classic [JS-Clock](https://www.tcpweb.com.br/JS-Clock/) library, featuring full type safety, no dependencies, and proper multi-instance support.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Check out this project's demo [here](https://www.tcpweb.com.br/new-js-clock/).
|
|
8
|
+
|
|
9
|
+
## ๐ What's New in Version 1.0.0
|
|
10
|
+
|
|
11
|
+
- **๐ TypeScript**: Full type safety with comprehensive interfaces
|
|
12
|
+
- **๐ E2E + Unit Test Suite**: Deterministic Jest coverage plus Selenium E2E browser tests (including extended background-tab behavior validation)
|
|
13
|
+
- **๐ฆ Zero Dependencies**: Pure vanilla JavaScript, no jQuery required
|
|
14
|
+
- **๐ Bug Fixes**: Multi-instance support works correctly (main issue in v0.8)
|
|
15
|
+
- **โก Modern API**: Clean, intuitive API with proper instance methods
|
|
16
|
+
- **๐งช Fully Tested**: 162 deterministic Jest tests with 99.74% lines/99.02% statements/100% functions and 98.32% branch coverage
|
|
17
|
+
- **๐ณ Dockerized E2E Grid**: Selenium Grid via `selenium/standalone-all-browsers` on port `4444`, running headless Chrome/Firefox/Edge locally and in CI (`pnpm run e2e:docker`)
|
|
18
|
+
- **๐ฑ ES Modules**: ES Module imports with tree-shaking support
|
|
19
|
+
- **๐ DST-Aware Timezones**: IANA timezone support with automatic daylight saving time handling
|
|
20
|
+
- **โฑ๏ธ Stopwatch Mode**: New stopwatch that counts up from 00:00:00
|
|
21
|
+
- **๐ Lap & Split Times**: Record lap times (delta) and split times (cumulative)
|
|
22
|
+
- **๐ Best/Worst Lap**: Built-in helpers to find best and worst lap times
|
|
23
|
+
- **๐ฏ High-Resolution Timing**: Lap deltas measured via `performance.now()` for sub-millisecond precision
|
|
24
|
+
|
|
25
|
+
## ๐ฆ Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# npm
|
|
29
|
+
npm install new-js-clock
|
|
30
|
+
|
|
31
|
+
# pnpm
|
|
32
|
+
pnpm add new-js-clock
|
|
33
|
+
|
|
34
|
+
# yarn
|
|
35
|
+
yarn add new-js-clock
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### CDN (Script Tag)
|
|
39
|
+
|
|
40
|
+
For direct browser usage without a bundler:
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- Minified (recommended for production) -->
|
|
44
|
+
<script src="https://unpkg.com/new-js-clock/dist/new-js-clock.min.js"></script>
|
|
45
|
+
|
|
46
|
+
<!-- Or via jsDelivr -->
|
|
47
|
+
<script src="https://cdn.jsdelivr.net/npm/new-js-clock/dist/new-js-clock.min.js"></script>
|
|
48
|
+
|
|
49
|
+
<!-- Unminified (for debugging) -->
|
|
50
|
+
<script src="https://unpkg.com/new-js-clock/dist/new-js-clock.js"></script>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
After loading, the library is available as `NewJSClock`:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
var clock = NewJSClock.createClock(document.getElementById('clock'));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## ๐ ๏ธ Build & Development
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Build the project (includes TypeScript type checking)
|
|
63
|
+
pnpm run build
|
|
64
|
+
|
|
65
|
+
# Build ESM output and type declarations
|
|
66
|
+
pnpm run build:esm
|
|
67
|
+
|
|
68
|
+
# Build CommonJS bundle only
|
|
69
|
+
pnpm run build:cjs
|
|
70
|
+
|
|
71
|
+
# Build IIFE bundles only
|
|
72
|
+
pnpm run build:iife
|
|
73
|
+
|
|
74
|
+
# Watch mode for development
|
|
75
|
+
pnpm run dev
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## ๐งช Testing
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Run tests
|
|
82
|
+
pnpm test
|
|
83
|
+
|
|
84
|
+
# Run tests with coverage
|
|
85
|
+
pnpm run test:coverage
|
|
86
|
+
|
|
87
|
+
# Run tests in watch mode
|
|
88
|
+
pnpm run test:watch
|
|
89
|
+
|
|
90
|
+
# Run end to end tests using dockerized Selenium Grid for Chrome, Firefox and Edge testing
|
|
91
|
+
pnpm run e2e:docker
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## ๐ Linting
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Run ESLint
|
|
98
|
+
pnpm lint
|
|
99
|
+
|
|
100
|
+
# Auto-fix linting issues
|
|
101
|
+
pnpm run lint:fix
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Test Quality Snapshot:** 162 passing Jest tests with **99.74% line coverage**, **99.02% statement coverage**, **98.32% branch coverage**, and **100% function coverage**.
|
|
105
|
+
**E2E Snapshot:** Dockerized Selenium Grid using `selenium/standalone-all-browsers` on port `4444`, executing headless browser runs for Chrome, Firefox, and Edge via `pnpm run e2e:docker`.
|
|
106
|
+
|
|
107
|
+
## ๐ Usage
|
|
108
|
+
|
|
109
|
+
### Basic System Clock
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { createClock } from 'new-js-clock';
|
|
113
|
+
|
|
114
|
+
const clock = createClock(document.getElementById('clock'));
|
|
115
|
+
|
|
116
|
+
// Control the clock
|
|
117
|
+
clock.stopClock(); // Pause
|
|
118
|
+
clock.startClock(); // Resume
|
|
119
|
+
clock.toggleClock(); // Toggle
|
|
120
|
+
|
|
121
|
+
// Get current time
|
|
122
|
+
const currentTime = clock.getTime(); // "14:30:25"
|
|
123
|
+
|
|
124
|
+
// Clean up
|
|
125
|
+
clock.destroy();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### CommonJS Usage
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
const { createClock } = require('new-js-clock');
|
|
132
|
+
|
|
133
|
+
const clock = createClock(document.getElementById('clock'));
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Countdown Timer
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const countdown = createClock(
|
|
140
|
+
document.getElementById('timer'),
|
|
141
|
+
'00:05:00', // 5 minutes
|
|
142
|
+
{
|
|
143
|
+
countdown: true,
|
|
144
|
+
callback: () => alert('Time is up!')
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### High Precision (with Centiseconds)
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const precision = createClock(
|
|
153
|
+
document.getElementById('precision'),
|
|
154
|
+
undefined, // Use system time
|
|
155
|
+
{ showCenti: true } // Show centiseconds
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Custom Start Time
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const custom = createClock(
|
|
163
|
+
document.getElementById('custom'),
|
|
164
|
+
'10:30:45' // Start at this time
|
|
165
|
+
);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 12-Hour Format with AM/PM
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
const clock12h = createClock(
|
|
172
|
+
document.getElementById('clock12h'),
|
|
173
|
+
undefined, // Use system time
|
|
174
|
+
{ use12Hour: true }
|
|
175
|
+
);
|
|
176
|
+
// Display: "02:30:45 PM" for 14:30:45
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Multiple Timezones
|
|
180
|
+
|
|
181
|
+
**DST-Aware (Recommended):**
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// Using IANA timezone names - automatically handles DST!
|
|
185
|
+
const nyClock = createClock(
|
|
186
|
+
document.getElementById('ny-time'),
|
|
187
|
+
undefined,
|
|
188
|
+
{ timezone: 'America/New_York' } // Adjusts for EST/EDT
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const londonClock = createClock(
|
|
192
|
+
document.getElementById('london-time'),
|
|
193
|
+
undefined,
|
|
194
|
+
{ timezone: 'Europe/London' } // Adjusts for GMT/BST
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const tokyoClock = createClock(
|
|
198
|
+
document.getElementById('tokyo-time'),
|
|
199
|
+
undefined,
|
|
200
|
+
{ timezone: 'Asia/Tokyo' }
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Static Offset (No DST):**
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Using static offset - does NOT handle DST
|
|
208
|
+
const nyClock = createClock(
|
|
209
|
+
document.getElementById('ny-time'),
|
|
210
|
+
undefined,
|
|
211
|
+
{ timezoneOffset: -5 } // Always UTC-5
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const mumbaiClock = createClock(
|
|
215
|
+
document.getElementById('mumbai-time'),
|
|
216
|
+
undefined,
|
|
217
|
+
{ timezoneOffset: 5.5 } // Supports half-hour offsets
|
|
218
|
+
);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Resetting a Countdown Timer
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const countdown = createClock(
|
|
225
|
+
document.getElementById('timer'),
|
|
226
|
+
'00:05:00',
|
|
227
|
+
{ countdown: true }
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Reset to initial time and restart - no need to destroy/recreate!
|
|
231
|
+
countdown.reset();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Changing Time Dynamically
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
const clock = createClock(
|
|
238
|
+
document.getElementById('clock'),
|
|
239
|
+
'10:00:00'
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Change the time without destroying the instance
|
|
243
|
+
clock.setTime('15:30:00');
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Stopwatch
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const stopwatch = createClock(
|
|
250
|
+
document.getElementById('stopwatch'),
|
|
251
|
+
undefined,
|
|
252
|
+
{ stopwatch: true }
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Control the stopwatch
|
|
256
|
+
stopwatch.stopClock(); // Pause
|
|
257
|
+
stopwatch.startClock(); // Resume
|
|
258
|
+
stopwatch.reset(); // Reset to 00:00:00
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Stopwatch with Lap Times
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
const stopwatch = createClock(
|
|
265
|
+
document.getElementById('stopwatch'),
|
|
266
|
+
undefined,
|
|
267
|
+
{
|
|
268
|
+
stopwatch: true,
|
|
269
|
+
lap: true,
|
|
270
|
+
lapWord: 'Split' // Optional: customize the lap word
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Record a lap
|
|
275
|
+
const lap1 = stopwatch.lap(); // "Split 1: 00:00:15"
|
|
276
|
+
const lap2 = stopwatch.lap(); // "Split 2: 00:00:32"
|
|
277
|
+
|
|
278
|
+
// Get all laps
|
|
279
|
+
const laps = stopwatch.getLaps();
|
|
280
|
+
|
|
281
|
+
// Clear laps
|
|
282
|
+
stopwatch.clearLaps();
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## โ๏ธ Configuration Options
|
|
286
|
+
|
|
287
|
+
| Option | Type | Default | Description |
|
|
288
|
+
|--------|------|---------|-------------|
|
|
289
|
+
| `showCenti` | `boolean` | `false` | Display centiseconds |
|
|
290
|
+
| `countdown` | `boolean` | `false` | Run as countdown timer |
|
|
291
|
+
| `callback` | `function` | `undefined` | Called when countdown reaches zero |
|
|
292
|
+
| `showHour` | `boolean` | `true` | Show hours in display |
|
|
293
|
+
| `showMinute` | `boolean` | `true` | Show minutes in display |
|
|
294
|
+
| `use12Hour` | `boolean` | `false` | Use 12-hour format with AM/PM |
|
|
295
|
+
| `timezone` | `string` | `undefined` | IANA timezone name for DST-aware support (e.g., "America/New_York") |
|
|
296
|
+
| `timezoneOffset` | `number` | `undefined` | Static timezone offset in hours from UTC (no DST handling) |
|
|
297
|
+
| `stopwatch` | `boolean` | `false` | Run as stopwatch (counts up from 00:00:00) |
|
|
298
|
+
| `lap` | `boolean` | `false` | Enable lap/split mode (requires stopwatch: true) |
|
|
299
|
+
| `lapMode` | `"splits" \| "laps" \| "both"` | `"both"` | Lap recording mode: "splits" (cumulative), "laps" (delta), or "both" |
|
|
300
|
+
| `lapWord` | `string` | `"Split"` in `lapMode: "splits"`, otherwise `"Lap"` | Custom word before lap number (set to "" for no word) |
|
|
301
|
+
| `useAnimationFrame` | `boolean` | `false` | Use requestAnimationFrame for smoother updates. Falls back to setTimeout when page is hidden. |
|
|
302
|
+
|
|
303
|
+
## ๐ฏ API Reference
|
|
304
|
+
|
|
305
|
+
### `createClock(element, initialTime?, options?): ClockInstance`
|
|
306
|
+
|
|
307
|
+
Creates a new clock instance.
|
|
308
|
+
|
|
309
|
+
**Parameters:**
|
|
310
|
+
- `element` (HTMLElement): DOM element to render the clock in
|
|
311
|
+
- `initialTime` (string, optional): Time in "HH:MM:SS" or "HH:MM:SS:CC" format
|
|
312
|
+
- `options` (ClockOptions, optional): Configuration options
|
|
313
|
+
|
|
314
|
+
**Returns:** `ClockInstance`
|
|
315
|
+
|
|
316
|
+
### ClockInstance Methods
|
|
317
|
+
|
|
318
|
+
| Method | Returns | Description |
|
|
319
|
+
|--------|---------|-------------|
|
|
320
|
+
| `getTime()` | `string` | Get current time as formatted string |
|
|
321
|
+
| `stopClock()` | `void` | Stop/pause the clock |
|
|
322
|
+
| `startClock()` | `void` | Start/resume the clock |
|
|
323
|
+
| `toggleClock()` | `void` | Toggle between running and stopped |
|
|
324
|
+
| `isRunning()` | `boolean` | Check if clock is currently running |
|
|
325
|
+
| `setTime(timeString)` | `void` | Set a new time without destroying the instance |
|
|
326
|
+
| `reset()` | `void` | Reset to initial time and restart (great for countdowns!) |
|
|
327
|
+
| `lap()` | `string` | Record a lap/split time (only in lap mode) |
|
|
328
|
+
| `getLaps()` | `string[]` | Get all recorded lap/split times (only in lap mode) |
|
|
329
|
+
| `getSplitTimes()` | `string[]` | Get all split times (cumulative; `lapMode: "splits"` or `"both"`) |
|
|
330
|
+
| `getLapTimes()` | `string[]` | Get all lap times (delta; `lapMode: "laps"` or `"both"`) |
|
|
331
|
+
| `getLapRecords()` | `LapRecord[]` | Get all lap records with full details (only in lap mode) |
|
|
332
|
+
| `clearLaps()` | `void` | Clear all recorded lap/split times (only in lap mode) |
|
|
333
|
+
| `bestLap()` | `LapRecord \| null` | Get the fastest lap record (`lapMode: "laps"` or `"both"`) |
|
|
334
|
+
| `worstLap()` | `LapRecord \| null` | Get the slowest lap record (`lapMode: "laps"` or `"both"`) |
|
|
335
|
+
| `destroy()` | `void` | Clean up and remove the clock |
|
|
336
|
+
|
|
337
|
+
## ๐ง Bundler Configuration
|
|
338
|
+
|
|
339
|
+
This library ships ESM and CommonJS builds. For TypeScript ESM projects, `moduleResolution: "node"` provides broad compatibility. If you're using a bundler, you may want to configure it:
|
|
340
|
+
|
|
341
|
+
### Vite
|
|
342
|
+
Works out of the box - no configuration needed.
|
|
343
|
+
|
|
344
|
+
### webpack
|
|
345
|
+
```javascript
|
|
346
|
+
// webpack.config.js
|
|
347
|
+
module.exports = {
|
|
348
|
+
resolve: {
|
|
349
|
+
extensions: ['.js', '.ts'],
|
|
350
|
+
conditionNames: ['import', 'default']
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### esbuild
|
|
356
|
+
```javascript
|
|
357
|
+
// esbuild.config.js
|
|
358
|
+
esbuild.build({
|
|
359
|
+
bundle: true,
|
|
360
|
+
format: 'esm',
|
|
361
|
+
mainFields: ['module', 'main']
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### TypeScript
|
|
366
|
+
The library includes TypeScript declarations. For the best experience, ensure your `tsconfig.json` uses a compatible `moduleResolution`:
|
|
367
|
+
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
370
|
+
"compilerOptions": {
|
|
371
|
+
"moduleResolution": "node" // or "node16", "nodenext", or "bundler"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Note:** If you use `moduleResolution: "bundler"` in your project, the library will still work correctly since it's compiled with `moduleResolution: "node"` for broad compatibility.
|
|
377
|
+
|
|
378
|
+
## ๐ Bug Fixes from v0.8
|
|
379
|
+
|
|
380
|
+
### Multi-Instance Bug (FIXED โ
)
|
|
381
|
+
|
|
382
|
+
In the original jQuery version, calling `stopClock()`, `startClock()`, `toggleClock()`, or `getTime()` only worked on the **last** clock instance created. This was due to closure scope issues.
|
|
383
|
+
|
|
384
|
+
**New version:** Each clock instance has its own independent state and methods that work correctly.
|
|
385
|
+
|
|
386
|
+
```javascript
|
|
387
|
+
// This now works correctly!
|
|
388
|
+
const clock1 = createClock(el1, '10:00:00');
|
|
389
|
+
const clock2 = createClock(el2, '20:00:00');
|
|
390
|
+
|
|
391
|
+
clock1.stopClock(); // Only stops clock1
|
|
392
|
+
clock2.stopClock(); // Only stops clock2
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Variable Scope Bug (FIXED โ
)
|
|
396
|
+
|
|
397
|
+
Fixed inconsistent variable naming (`clockloop` vs `clockLoop`) that could cause timer issues.
|
|
398
|
+
|
|
399
|
+
## ๐ License
|
|
400
|
+
|
|
401
|
+
MIT License - Copyright (c) Thiago Cavalcanti Pimenta
|
|
402
|
+
|
|
403
|
+
## ๐ Credits
|
|
404
|
+
|
|
405
|
+
This is a modern rewrite of the original JS-Clock jQuery plugin.
|