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 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
+ ![Test Coverage](https://img.shields.io/badge/coverage-99.74%25%20lines%20%7C%2098.32%25%20branches-brightgreen)
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.