@xpadev-net/niconicomments 0.2.16 → 0.2.17

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/dist/bundle.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- niconicomments.js v0.2.16
2
+ niconicomments.js v0.2.17
3
3
  (c) 2021 xpadev-net https://xpadev.net
4
4
  Released under the MIT License.
5
5
  */
@@ -25,954 +25,954 @@
25
25
  return _assign.apply(this, arguments);
26
26
  };
27
27
 
28
- var isDebug = false;
29
- var NiconiComments = (function () {
30
- function NiconiComments(canvas, data, options) {
31
- if (options === void 0) { options = {
32
- useLegacy: false,
33
- formatted: false,
34
- video: null,
35
- showCollision: false,
36
- showFPS: false,
37
- showCommentCount: false,
38
- drawAllImageOnLoad: false,
39
- debug: false,
40
- enableLegacyPiP: false
41
- }; }
42
- var _this = this;
43
- isDebug = options.debug;
44
- var constructorStart = performance.now();
45
- this.canvas = canvas;
46
- var context = canvas.getContext("2d");
47
- if (!context)
48
- throw new Error("Fail to get CanvasRenderingContext2D");
49
- this.context = context;
50
- this.context.strokeStyle = "rgba(0,0,0,0.7)";
51
- this.context.textAlign = "start";
52
- this.context.textBaseline = "alphabetic";
53
- this.context.lineWidth = 4;
54
- this.commentYPaddingTop = 0.08;
55
- this.commentYMarginBottom = 0.24;
56
- this.fontSize = {
57
- "small": {
58
- "default": 47,
59
- "resized": 26.1
60
- },
61
- "medium": {
62
- "default": 74,
63
- "resized": 38.7
64
- },
65
- "big": {
66
- "default": 110,
67
- "resized": 61
68
- }
69
- };
70
- this.lineHeight = {
71
- "small": {
72
- "default": 1,
73
- "resized": 1
74
- },
75
- "medium": {
76
- "default": 1,
77
- "resized": 1
78
- },
79
- "big": {
80
- "default": 1.03,
81
- "resized": 1.01
82
- }
83
- };
84
- this.doubleResizeMaxWidth = {
85
- full: {
86
- legacy: 3020,
87
- default: 3220
88
- },
89
- normal: {
90
- legacy: 2540,
91
- default: 2740
92
- }
93
- };
94
- var parsedData = options.formatted ? data : this.parseData(data);
95
- this.video = options.video ? options.video : null;
96
- this.showCollision = options.showCollision;
97
- this.showFPS = options.showFPS;
98
- this.showCommentCount = options.showCommentCount;
99
- this.enableLegacyPiP = options.enableLegacyPiP;
100
- this.timeline = {};
101
- this.nicoScripts = { reverse: [], default: [], replace: [], ban: [] };
102
- this.collision_right = {};
103
- this.collision_left = {};
104
- this.collision_ue = {};
105
- this.collision_shita = {};
106
- this.data = [];
107
- this.lastVpos = -1;
108
- this.useLegacy = options.useLegacy;
109
- this.preRendering(parsedData, options.drawAllImageOnLoad);
110
- this.fpsCount = 0;
111
- this.fps = 0;
112
- window.setInterval(function () {
113
- _this.fps = _this.fpsCount * 2;
114
- _this.fpsCount = 0;
115
- }, 500);
116
- logger("constructor complete: ".concat(performance.now() - constructorStart, "ms"));
117
- }
118
- NiconiComments.prototype.parseData = function (data) {
119
- var parseDataStart = performance.now();
120
- var data_ = [];
121
- for (var i = 0; i < data.length; i++) {
122
- var val = data[i];
123
- if (!val)
124
- continue;
125
- for (var key in val) {
126
- var value = val[key];
127
- if (isApiChat(value) && value["deleted"] !== 1) {
128
- var tmpParam = {
129
- "id": value["no"],
130
- "vpos": value["vpos"],
131
- "content": value["content"],
132
- "date": value["date"],
133
- "date_usec": value["date_usec"],
134
- "owner": !value["user_id"],
135
- "premium": value["premium"] === 1,
136
- "mail": []
137
- };
138
- if (value["mail"]) {
139
- tmpParam["mail"] = value["mail"].split(/[\s ]/g);
140
- }
141
- if (value["content"].startsWith("/") && !value["user_id"]) {
142
- tmpParam["mail"].push("invisible");
143
- }
144
- data_.push(tmpParam);
145
- }
146
- }
147
- }
148
- data_.sort(function (a, b) {
149
- if (a.vpos < b.vpos)
150
- return -1;
151
- if (a.vpos > b.vpos)
152
- return 1;
153
- if (a.date < b.date)
154
- return -1;
155
- if (a.date > b.date)
156
- return 1;
157
- if (a.date_usec < b.date_usec)
158
- return -1;
159
- if (a.date_usec > b.date_usec)
160
- return 1;
161
- return 0;
162
- });
163
- logger("parseData complete: ".concat(performance.now() - parseDataStart, "ms"));
164
- return data_;
165
- };
166
- NiconiComments.prototype.preRendering = function (rawData, drawAll) {
167
- var preRenderingStart = performance.now();
168
- var parsedData = this.getCommentPos(this.getCommentSize(this.getFont(rawData)));
169
- this.data = this.sortComment(parsedData);
170
- if (drawAll) {
171
- for (var i in parsedData) {
172
- this.getTextImage(Number(i));
173
- }
174
- }
175
- logger("preRendering complete: ".concat(performance.now() - preRenderingStart, "ms"));
176
- };
177
- NiconiComments.prototype.getFont = function (parsedData) {
178
- var getFontStart = performance.now();
179
- var result = [];
180
- for (var i in parsedData) {
181
- var value = parsedData[i];
182
- if (!value)
183
- continue;
184
- value.content = value.content.replace(/\t/g, "\u2003\u2003");
185
- result[i] = this.parseCommandAndNicoscript(value);
186
- }
187
- logger("getFont complete: ".concat(performance.now() - getFontStart, "ms"));
188
- return result;
189
- };
190
- NiconiComments.prototype.getCommentSize = function (parsedData) {
191
- var getCommentSizeStart = performance.now();
192
- var tmpData = groupBy(parsedData, "font", "fontSize");
193
- var result = [];
194
- for (var i in tmpData) {
195
- for (var j in tmpData[i]) {
196
- this.context.font = parseFont(i, j, this.useLegacy);
197
- for (var k in tmpData[i][j]) {
198
- var comment = tmpData[i][j][k];
199
- if (comment.invisible) {
200
- continue;
201
- }
202
- var measure = this.measureText(comment);
203
- var size = parsedData[comment.index];
204
- size.height = measure.height;
205
- size.width = measure.width;
206
- size.width_max = measure.width_max;
207
- size.width_min = measure.width_min;
208
- size.lineHeight = measure.lineHeight;
209
- if (measure.resized) {
210
- size.fontSize = measure.fontSize;
211
- this.context.font = parseFont(i, j, this.useLegacy);
212
- }
213
- result[comment.index] = size;
214
- }
215
- }
216
- }
217
- logger("getCommentSize complete: ".concat(performance.now() - getCommentSizeStart, "ms"));
218
- return result;
219
- };
220
- NiconiComments.prototype.getCommentPos = function (parsedData) {
221
- var getCommentPosStart = performance.now();
222
- var data = parsedData;
223
- for (var i in data) {
224
- var comment = data[i];
225
- if (!comment || comment.invisible) {
226
- continue;
227
- }
228
- for (var j = 0; j < 500; j++) {
229
- if (!this.timeline[comment.vpos + j]) {
230
- this.timeline[comment.vpos + j] = [];
231
- }
232
- if (!this.collision_right[comment.vpos + j]) {
233
- this.collision_right[comment.vpos + j] = [];
234
- }
235
- if (!this.collision_left[comment.vpos + j]) {
236
- this.collision_left[comment.vpos + j] = [];
237
- }
238
- if (!this.collision_ue[comment.vpos + j]) {
239
- this.collision_ue[comment.vpos + j] = [];
240
- }
241
- if (!this.collision_shita[comment.vpos + j]) {
242
- this.collision_shita[comment.vpos + j] = [];
243
- }
244
- }
245
- if (comment.loc === "naka") {
246
- comment.vpos -= 70;
247
- parsedData[i].vpos -= 70;
248
- var posY = 0, is_break = false, is_change = true, count = 0;
249
- if (1080 < comment.height) {
250
- posY = (comment.height - 1080) / -2;
251
- }
252
- else {
253
- while (is_change && count < 10) {
254
- is_change = false;
255
- count++;
256
- for (var j = 0; j < 500; j++) {
257
- var vpos = comment.vpos + j;
258
- var left_pos = 1920 - ((1920 + comment.width_max) * j / 500);
259
- if (left_pos + comment.width_max >= 1880) {
260
- for (var k in this.collision_right[vpos]) {
261
- var l = this.collision_right[vpos][k];
262
- if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
263
- if (data[l].posY + data[l].height > posY) {
264
- posY = data[l].posY + data[l].height;
265
- is_change = true;
266
- }
267
- if (posY + comment.height > 1080) {
268
- if (1080 < comment.height) {
269
- posY = (comment.height - 1080) / -2;
270
- }
271
- else {
272
- posY = Math.floor(Math.random() * (1080 - comment.height));
273
- }
274
- is_break = true;
275
- break;
276
- }
277
- }
278
- }
279
- if (is_break) {
280
- break;
281
- }
282
- }
283
- if (left_pos <= 40) {
284
- for (var k in this.collision_left[vpos]) {
285
- var l = this.collision_left[vpos][k];
286
- if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
287
- if (data[l].posY + data[l].height > posY) {
288
- posY = data[l].posY + data[l].height;
289
- is_change = true;
290
- }
291
- if (posY + comment.height > 1080) {
292
- if (1080 < comment.height) {
293
- posY = 0;
294
- }
295
- else {
296
- posY = Math.random() * (1080 - comment.height);
297
- }
298
- is_break = true;
299
- break;
300
- }
301
- }
302
- }
303
- if (is_break) {
304
- break;
305
- }
306
- }
307
- }
308
- if (is_break) {
309
- break;
310
- }
311
- }
312
- }
313
- for (var j = 0; j < 500; j++) {
314
- var vpos = comment.vpos + j;
315
- var left_pos = 1920 - ((1920 + comment.width_max) * j / 500);
316
- arrayPush(this.timeline, vpos, i);
317
- if (left_pos + comment.width_max >= 1880) {
318
- arrayPush(this.collision_right, vpos, i);
319
- }
320
- if (left_pos <= 40) {
321
- arrayPush(this.collision_left, vpos, i);
322
- }
323
- }
324
- parsedData[i].posY = posY;
325
- }
326
- else {
327
- var posY = 0, is_break = false, is_change = true, count = 0, collision = void 0;
328
- if (comment.loc === "ue") {
329
- collision = this.collision_ue;
330
- }
331
- else if (comment.loc === "shita") {
332
- collision = this.collision_shita;
333
- }
334
- while (is_change && count < 10) {
335
- is_change = false;
336
- count++;
337
- for (var j = 0; j < 300; j++) {
338
- var vpos = comment.vpos + j;
339
- for (var k in collision[vpos]) {
340
- var l = collision[vpos][k];
341
- if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
342
- if (data[l].posY + data[l].height > posY) {
343
- posY = data[l].posY + data[l].height;
344
- is_change = true;
345
- }
346
- if (posY + comment.height > 1080) {
347
- if (1000 <= comment.height) {
348
- posY = 0;
349
- }
350
- else {
351
- posY = Math.floor(Math.random() * (1080 - comment.height));
352
- }
353
- is_break = true;
354
- break;
355
- }
356
- }
357
- }
358
- if (is_break) {
359
- break;
360
- }
361
- }
362
- }
363
- for (var j = 0; j < comment.long; j++) {
364
- var vpos = comment.vpos + j;
365
- arrayPush(this.timeline, vpos, i);
366
- if (comment.loc === "ue") {
367
- arrayPush(this.collision_ue, vpos, i);
368
- }
369
- else {
370
- arrayPush(this.collision_shita, vpos, i);
371
- }
372
- }
373
- parsedData[i].posY = posY;
374
- }
375
- }
376
- logger("getCommentPos complete: ".concat(performance.now() - getCommentPosStart, "ms"));
377
- return parsedData;
378
- };
379
- NiconiComments.prototype.sortComment = function (parsedData) {
380
- var sortCommentStart = performance.now();
381
- for (var vpos in this.timeline) {
382
- if (!this.timeline[vpos])
383
- continue;
384
- var owner = [], user = [];
385
- for (var _i = 0, _a = this.timeline[vpos]; _i < _a.length; _i++) {
386
- var i = _a[_i];
387
- if (parsedData[i].owner) {
388
- owner.push(i);
389
- }
390
- else {
391
- user.push(i);
392
- }
393
- }
394
- this.timeline[vpos] = owner.concat(user);
395
- }
396
- logger("parseData complete: ".concat(performance.now() - sortCommentStart, "ms"));
397
- return parsedData;
398
- };
399
- NiconiComments.prototype.measureText = function (comment) {
400
- var width, width_max, width_min, height, width_arr = [], lines = comment.content.split("\n");
401
- if (!comment.lineHeight)
402
- comment.lineHeight = this.lineHeight[comment.size].default;
403
- if (!comment.resized && !comment.ender) {
404
- if (comment.size === "big" && lines.length > 2) {
405
- comment.fontSize = this.fontSize.big.resized;
406
- comment.lineHeight = this.lineHeight.big.resized;
407
- comment.resized = true;
408
- comment.tateresized = true;
409
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
410
- }
411
- else if (comment.size === "medium" && lines.length > 4) {
412
- comment.fontSize = this.fontSize.medium.resized;
413
- comment.lineHeight = this.lineHeight.medium.resized;
414
- comment.resized = true;
415
- comment.tateresized = true;
416
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
417
- }
418
- else if (comment.size === "small" && lines.length > 6) {
419
- comment.fontSize = this.fontSize.small.resized;
420
- comment.lineHeight = this.lineHeight.small.resized;
421
- comment.resized = true;
422
- comment.tateresized = true;
423
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
424
- }
425
- }
426
- for (var i = 0; i < lines.length; i++) {
427
- var measure = this.context.measureText(lines[i]);
428
- width_arr.push(measure.width);
429
- }
430
- width = width_arr.reduce(function (p, c) { return p + c; }, 0) / width_arr.length;
431
- width_max = Math.max.apply(Math, width_arr);
432
- width_min = Math.min.apply(Math, width_arr);
433
- height = (comment.fontSize * comment.lineHeight * (1 + this.commentYPaddingTop) * lines.length) + (this.commentYMarginBottom * comment.fontSize);
434
- if (comment.loc !== "naka" && !comment.tateresized) {
435
- if (comment.full && width_max > 1930) {
436
- comment.fontSize -= 2;
437
- comment.resized = true;
438
- comment.yokoResized = true;
439
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
440
- return this.measureText(comment);
441
- }
442
- else if (!comment.full && width_max > 1440) {
443
- comment.fontSize -= 1;
444
- comment.resized = true;
445
- comment.yokoResized = true;
446
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
447
- return this.measureText(comment);
448
- }
449
- }
450
- else if (comment.loc !== "naka" && comment.tateresized && (comment.full && width_max > 2120 || !comment.full && width_max > 1440) && !comment.yokoResized) {
451
- comment.fontSize = this.fontSize[comment.size].default;
452
- comment.resized = true;
453
- comment.yokoResized = true;
454
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
455
- return this.measureText(comment);
456
- }
457
- else if (comment.loc !== "naka" && comment.tateresized && comment.yokoResized) {
458
- if (comment.full && width_max > this.doubleResizeMaxWidth.full[this.useLegacy ? "legacy" : "default"]) {
459
- comment.fontSize -= 1;
460
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
461
- return this.measureText(comment);
462
- }
463
- else if (!comment.full && width_max > this.doubleResizeMaxWidth.normal[this.useLegacy ? "legacy" : "default"]) {
464
- comment.fontSize -= 1;
465
- this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
466
- return this.measureText(comment);
467
- }
468
- }
469
- return {
470
- "width": width,
471
- "width_max": width_max,
472
- "width_min": width_min,
473
- "height": height,
474
- "resized": comment.resized,
475
- "fontSize": comment.fontSize,
476
- "lineHeight": comment.lineHeight
477
- };
478
- };
479
- NiconiComments.prototype.drawText = function (comment, vpos) {
480
- var reverse = false;
481
- for (var i in this.nicoScripts.reverse) {
482
- var range = this.nicoScripts.reverse[i];
483
- if ((range.target === "コメ" && comment.owner) || (range.target === "投コメ" && !comment.owner)) {
484
- break;
485
- }
486
- if (range.start < vpos && vpos < range.end) {
487
- reverse = true;
488
- }
489
- }
490
- for (var i in this.nicoScripts.ban) {
491
- var range = this.nicoScripts.ban[i];
492
- if (range.start < vpos && vpos < range.end) {
493
- return;
494
- }
495
- }
496
- var posX = (1920 - comment.width_max) / 2, posY = comment.posY;
497
- if (comment.loc === "naka") {
498
- if (reverse) {
499
- posX = ((1920 + comment.width_max) * (vpos - comment.vpos) / 500) - comment.width_max;
500
- }
501
- else {
502
- posX = 1920 - ((1920 + comment.width_max) * (vpos - comment.vpos) / 500);
503
- }
504
- }
505
- else if (comment.loc === "shita") {
506
- posY = 1080 - comment.posY - comment.height;
507
- }
508
- if (comment.image && comment.image !== true) {
509
- this.context.drawImage(comment.image, posX, posY);
510
- }
511
- if (this.showCollision) {
512
- this.context.strokeStyle = "rgba(0,255,255,1)";
513
- this.context.strokeRect(posX, posY, comment.width_max, comment.height);
514
- var lines = comment.content.split("\n");
515
- for (var i in lines) {
516
- var linePosY = (Number(i) + 1) * (comment.fontSize * comment.lineHeight) * (1 + this.commentYPaddingTop);
517
- this.context.strokeStyle = "rgba(255,255,0,0.5)";
518
- this.context.strokeRect(posX, posY + linePosY, comment.width_max, comment.fontSize * comment.lineHeight * -1);
519
- }
520
- }
521
- };
522
- NiconiComments.prototype.getTextImage = function (i) {
523
- var _this = this;
524
- var value = this.data[i];
525
- if (!value || value.invisible)
526
- return;
527
- var image = document.createElement("canvas");
528
- image.width = value.width_max;
529
- image.height = value.height;
530
- var context = image.getContext("2d");
531
- if (!context)
532
- throw new Error("Fail to get CanvasRenderingContext2D");
533
- context.strokeStyle = "rgba(0,0,0,0.7)";
534
- context.textAlign = "start";
535
- context.textBaseline = "alphabetic";
536
- context.lineWidth = 4;
537
- context.font = parseFont(value.font, value.fontSize, this.useLegacy);
538
- if (value._live) {
539
- var rgb = hex2rgb(value.color);
540
- context.fillStyle = "rgba(".concat(rgb[0], ",").concat(rgb[1], ",").concat(rgb[2], ",0.5)");
541
- }
542
- else {
543
- context.fillStyle = value.color;
544
- }
545
- if (value.color === "#000000") {
546
- context.strokeStyle = "rgba(255,255,255,0.7)";
547
- }
548
- var lines = value.content.split("\n");
549
- for (var i_1 in lines) {
550
- var line = lines[i_1], posY = void 0;
551
- posY = (Number(i_1) + 1) * (value.fontSize * value.lineHeight) * (1 + this.commentYPaddingTop);
552
- context.strokeText(line, 0, posY);
553
- context.fillText(line, 0, posY);
554
- }
555
- this.data[i].image = image;
556
- setTimeout(function () {
557
- if (_this.data[i].image)
558
- delete _this.data[i].image;
559
- }, 5000);
560
- };
561
- NiconiComments.prototype.parseCommand = function (comment) {
562
- var metadata = comment.mail, loc = null, size = null, fontSize = null, color = null, font = null, full = false, ender = false, _live = false, invisible = false, long = null;
563
- for (var i in metadata) {
564
- var command = metadata[i].toLowerCase();
565
- var match = command.match(/^@([0-9.]+)/);
566
- if (match) {
567
- long = match[1];
568
- }
569
- if (loc === null) {
570
- switch (command) {
571
- case "ue":
572
- loc = "ue";
573
- break;
574
- case "shita":
575
- loc = "shita";
576
- break;
577
- }
578
- }
579
- if (size === null) {
580
- switch (command) {
581
- case "big":
582
- size = "big";
583
- fontSize = this.fontSize.big.default;
584
- break;
585
- case "small":
586
- size = "small";
587
- fontSize = this.fontSize.small.default;
588
- break;
589
- }
590
- }
591
- if (color === null) {
592
- switch (command) {
593
- case "white":
594
- color = "#FFFFFF";
595
- break;
596
- case "red":
597
- color = "#FF0000";
598
- break;
599
- case "pink":
600
- color = "#FF8080";
601
- break;
602
- case "orange":
603
- color = "#FFC000";
604
- break;
605
- case "yellow":
606
- color = "#FFFF00";
607
- break;
608
- case "green":
609
- color = "#00FF00";
610
- break;
611
- case "cyan":
612
- color = "#00FFFF";
613
- break;
614
- case "blue":
615
- color = "#0000FF";
616
- break;
617
- case "purple":
618
- color = "#C000FF";
619
- break;
620
- case "black":
621
- color = "#000000";
622
- break;
623
- case "white2":
624
- case "niconicowhite":
625
- color = "#CCCC99";
626
- break;
627
- case "red2":
628
- case "truered":
629
- color = "#CC0033";
630
- break;
631
- case "pink2":
632
- color = "#FF33CC";
633
- break;
634
- case "orange2":
635
- case "passionorange":
636
- color = "#FF6600";
637
- break;
638
- case "yellow2":
639
- case "madyellow":
640
- color = "#999900";
641
- break;
642
- case "green2":
643
- case "elementalgreen":
644
- color = "#00CC66";
645
- break;
646
- case "cyan2":
647
- color = "#00CCCC";
648
- break;
649
- case "blue2":
650
- case "marineblue":
651
- color = "#3399FF";
652
- break;
653
- case "purple2":
654
- case "nobleviolet":
655
- color = "#6633CC";
656
- break;
657
- case "black2":
658
- color = "#666666";
659
- break;
660
- default:
661
- var match_1 = command.match(/#[0-9a-z]{3,6}/);
662
- if (match_1 && comment.premium) {
663
- color = match_1[0].toUpperCase();
664
- }
665
- break;
666
- }
667
- }
668
- if (font === null) {
669
- switch (command) {
670
- case "gothic":
671
- font = "gothic";
672
- break;
673
- case "mincho":
674
- font = "mincho";
675
- break;
676
- }
677
- }
678
- switch (command) {
679
- case "full":
680
- full = true;
681
- break;
682
- case "ender":
683
- ender = true;
684
- break;
685
- case "_live":
686
- _live = true;
687
- break;
688
- case "invisible":
689
- invisible = true;
690
- break;
691
- }
692
- }
693
- return { loc: loc, size: size, fontSize: fontSize, color: color, font: font, full: full, ender: ender, _live: _live, invisible: invisible, long: long };
694
- };
695
- NiconiComments.prototype.parseCommandAndNicoscript = function (comment) {
696
- var data = this.parseCommand(comment), nicoscript = comment.content.match(/^@(デフォルト|置換|逆|コメント禁止|シーク禁止|ジャンプ)/);
697
- if (nicoscript) {
698
- switch (nicoscript[1]) {
699
- case "デフォルト":
700
- this.nicoScripts.default.push({
701
- start: comment.vpos,
702
- long: data.long === null ? null : Math.floor(data.long * 100),
703
- color: data.color,
704
- size: data.size,
705
- font: data.font,
706
- loc: data.loc
707
- });
708
- break;
709
- case "逆":
710
- var reverse = comment.content.match(/^@逆 ?(全|コメ|投コメ)?/);
711
- if (!reverse)
712
- reverse = [];
713
- if (!reverse[1]) {
714
- reverse[1] = "全";
715
- }
716
- if (data.long === null) {
717
- data.long = 30;
718
- }
719
- this.nicoScripts.reverse.push({
720
- start: comment.vpos,
721
- end: comment.vpos + (data.long * 100),
722
- target: reverse[1]
723
- });
724
- break;
725
- case "コメント禁止":
726
- if (data.long === null) {
727
- data.long = 30;
728
- }
729
- this.nicoScripts.reverse.push({
730
- start: comment.vpos,
731
- end: comment.vpos + (data.long * 100),
732
- });
733
- break;
734
- case "置換":
735
- var content = comment.content.split(""), quote = "", last_i = "", string = "", result = [];
736
- for (var _i = 0, _a = content.slice(4); _i < _a.length; _i++) {
737
- var i = _a[_i];
738
- if (i.match(/["'「]/) && quote === "") {
739
- quote = i;
740
- }
741
- else if (i.match(/["']/) && quote === i && last_i !== "\\") {
742
- result.push(replaceAll(string, "\\n", "\n"));
743
- quote = "";
744
- string = "";
745
- }
746
- else if (i.match(/」/) && quote === "「") {
747
- result.push(string);
748
- quote = "";
749
- string = "";
750
- }
751
- else if (quote === "" && i.match(/[\s ]/)) {
752
- if (string) {
753
- result.push(string);
754
- string = "";
755
- }
756
- }
757
- else {
758
- string += i;
759
- }
760
- last_i = i;
761
- }
762
- result.push(string);
763
- this.nicoScripts.replace.push({
764
- start: comment.vpos,
765
- long: data.long === null ? null : Math.floor(data.long * 100),
766
- keyword: result[0],
767
- replace: result[1] || "",
768
- range: result[2] || "単",
769
- target: result[3] || "コメ",
770
- condition: result[4] || "部分一致",
771
- color: data.color,
772
- size: data.size,
773
- font: data.font,
774
- loc: data.loc
775
- });
776
- break;
777
- }
778
- data.invisible = true;
779
- }
780
- var color = "#FFFFFF", size = "medium", font = "defont", loc = "naka";
781
- for (var i in this.nicoScripts.default) {
782
- if (this.nicoScripts.default[i].long !== null && this.nicoScripts.default[i].start + this.nicoScripts.default[i].long < comment.vpos) {
783
- this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
784
- continue;
785
- }
786
- if (this.nicoScripts.default[i].loc) {
787
- loc = this.nicoScripts.default[i].loc;
788
- }
789
- if (this.nicoScripts.default[i].color) {
790
- color = this.nicoScripts.default[i].color;
791
- }
792
- if (this.nicoScripts.default[i].size) {
793
- size = this.nicoScripts.default[i].size;
794
- }
795
- if (this.nicoScripts.default[i].font) {
796
- font = this.nicoScripts.default[i].font;
797
- }
798
- }
799
- for (var i in this.nicoScripts.replace) {
800
- if (this.nicoScripts.replace[i].long !== null && this.nicoScripts.replace[i].start + this.nicoScripts.replace[i].long < comment.vpos) {
801
- this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
802
- continue;
803
- }
804
- var item = this.nicoScripts.replace[i];
805
- if ((item.target === "コメ" && comment.owner) || (item.target === "投コメ" && !comment.owner) || (item.target === "含まない" && comment.owner))
806
- continue;
807
- if ((item.condition === "完全一致" && comment.content === item.keyword) || (item.condition === "部分一致" && comment.content.indexOf(item.keyword) !== -1)) {
808
- if (item.range === "単") {
809
- comment.content = replaceAll(comment.content, item.keyword, item.replace);
810
- }
811
- else {
812
- comment.content = item.replace;
813
- }
814
- if (item.loc) {
815
- loc = item.loc;
816
- }
817
- if (item.color) {
818
- color = item.color;
819
- }
820
- if (item.size) {
821
- size = item.size;
822
- }
823
- if (item.font) {
824
- font = item.font;
825
- }
826
- }
827
- }
828
- if (!data.loc) {
829
- data.loc = loc;
830
- }
831
- if (!data.color) {
832
- data.color = color;
833
- }
834
- if (!data.size) {
835
- data.size = size;
836
- data.fontSize = this.fontSize[data.size].default;
837
- }
838
- if (!data.font) {
839
- data.font = font;
840
- }
841
- if (data.loc !== "naka") {
842
- if (!data.long) {
843
- data.long = 300;
844
- }
845
- else {
846
- data.long = Math.floor(data.long * 100);
847
- }
848
- }
849
- return _assign(_assign({}, comment), data);
850
- };
851
- NiconiComments.prototype.drawCanvas = function (vpos) {
852
- var drawCanvasStart = performance.now();
853
- if (this.lastVpos === vpos)
854
- return;
855
- this.lastVpos = vpos;
856
- this.fpsCount++;
857
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
858
- if (this.video) {
859
- var offsetX = void 0, offsetY = void 0, scale = void 0, height = this.canvas.height / this.video.videoHeight, width = this.canvas.width / this.video.videoWidth;
860
- if (this.enableLegacyPiP ? height > width : height < width) {
861
- scale = width;
862
- }
863
- else {
864
- scale = height;
865
- }
866
- offsetX = (this.canvas.width - this.video.videoWidth * scale) * 0.5;
867
- offsetY = (this.canvas.height - this.video.videoHeight * scale) * 0.5;
868
- this.context.drawImage(this.video, offsetX, offsetY, this.video.videoWidth * scale, this.video.videoHeight * scale);
869
- }
870
- if (this.timeline[vpos]) {
871
- for (var i in this.timeline[vpos]) {
872
- var index = this.timeline[vpos][Number(i)];
873
- var comment = this.data[index];
874
- if (!comment || comment.invisible) {
875
- continue;
876
- }
877
- if (comment.image === undefined) {
878
- this.getTextImage(index);
879
- }
880
- try {
881
- this.drawText(comment, vpos);
882
- }
883
- catch (e) {
884
- comment.image = false;
885
- }
886
- }
887
- }
888
- if (this.showFPS) {
889
- this.context.font = parseFont("defont", 60, this.useLegacy);
890
- this.context.fillStyle = "#00FF00";
891
- this.context.strokeStyle = "rgba(0,0,0,0.7)";
892
- this.context.strokeText("FPS:" + this.fps, 100, 100);
893
- this.context.fillText("FPS:" + this.fps, 100, 100);
894
- }
895
- if (this.showCommentCount) {
896
- this.context.font = parseFont("defont", 60, this.useLegacy);
897
- this.context.fillStyle = "#00FF00";
898
- this.context.strokeStyle = "rgba(0,0,0,0.7)";
899
- if (this.timeline[vpos]) {
900
- this.context.strokeText("Count:" + this.timeline[vpos].length, 100, 200);
901
- this.context.fillText("Count:" + this.timeline[vpos].length, 100, 200);
902
- }
903
- else {
904
- this.context.strokeText("Count:0", 100, 200);
905
- this.context.fillText("Count:0", 100, 200);
906
- }
907
- }
908
- logger("drawCanvas complete: ".concat(performance.now() - drawCanvasStart, "ms"));
909
- };
910
- NiconiComments.prototype.clear = function () {
911
- this.context.clearRect(0, 0, 1920, 1080);
912
- };
913
- return NiconiComments;
914
- }());
915
- var groupBy = function (array, key, key2) {
916
- var data = {};
917
- for (var i in array) {
918
- if (!data[array[i][key]]) {
919
- data[array[i][key]] = {};
920
- }
921
- if (!data[array[i][key]][array[i][key2]]) {
922
- data[array[i][key]][array[i][key2]] = [];
923
- }
924
- array[i].index = i;
925
- data[array[i][key]][array[i][key2]].push(array[i]);
926
- }
927
- return data;
928
- };
929
- var parseFont = function (font, size, useLegacy) {
930
- switch (font) {
931
- case "gothic":
932
- return "normal 400 ".concat(size, "px \"\u6E38\u30B4\u30B7\u30C3\u30AF\u4F53\", \"\u6E38\u30B4\u30B7\u30C3\u30AF\", \"Yu Gothic\", YuGothic, yugothic, YuGo-Medium");
933
- case "mincho":
934
- return "normal 400 ".concat(size, "px \"\u6E38\u660E\u671D\u4F53\", \"\u6E38\u660E\u671D\", \"Yu Mincho\", YuMincho, yumincho, YuMin-Medium");
935
- default:
936
- if (useLegacy) {
937
- return "normal 600 ".concat(size, "px Arial, \"\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF\", \"MS PGothic\", MSPGothic, MS-PGothic");
938
- }
939
- else {
940
- return "normal 600 ".concat(size, "px sans-serif, Arial, \"\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF\", \"MS PGothic\", MSPGothic, MS-PGothic");
941
- }
942
- }
943
- };
944
- var arrayPush = function (array, key, push) {
945
- if (!array) {
946
- array = {};
947
- }
948
- if (!array[key]) {
949
- array[key] = [];
950
- }
951
- array[key].push(push);
952
- };
953
- var hex2rgb = function (hex) {
954
- if (hex.slice(0, 1) === "#")
955
- hex = hex.slice(1);
956
- if (hex.length === 3)
957
- hex = hex.slice(0, 1) + hex.slice(0, 1) + hex.slice(1, 2) + hex.slice(1, 2) + hex.slice(2, 3) + hex.slice(2, 3);
958
- return [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map(function (str) {
959
- return parseInt(str, 16);
960
- });
961
- };
962
- var replaceAll = function (string, target, replace) {
963
- var count = 0;
964
- while (string.indexOf(target) !== -1 && count < 100) {
965
- string = string.replace(target, replace);
966
- count++;
967
- }
968
- return string;
969
- };
970
- var isApiChat = function (item) {
971
- return item.id && item.vpos && item.vpos;
972
- };
973
- var logger = function (msg) {
974
- if (isDebug)
975
- console.debug(msg);
28
+ var isDebug = false;
29
+ var NiconiComments = (function () {
30
+ function NiconiComments(canvas, data, options) {
31
+ if (options === void 0) { options = {
32
+ useLegacy: false,
33
+ formatted: false,
34
+ video: null,
35
+ showCollision: false,
36
+ showFPS: false,
37
+ showCommentCount: false,
38
+ drawAllImageOnLoad: false,
39
+ debug: false,
40
+ enableLegacyPiP: false
41
+ }; }
42
+ var _this = this;
43
+ isDebug = options.debug;
44
+ var constructorStart = performance.now();
45
+ this.canvas = canvas;
46
+ var context = canvas.getContext("2d");
47
+ if (!context)
48
+ throw new Error("Fail to get CanvasRenderingContext2D");
49
+ this.context = context;
50
+ this.context.strokeStyle = "rgba(0,0,0,0.7)";
51
+ this.context.textAlign = "start";
52
+ this.context.textBaseline = "alphabetic";
53
+ this.context.lineWidth = 4;
54
+ this.commentYPaddingTop = 0.08;
55
+ this.commentYMarginBottom = 0.24;
56
+ this.fontSize = {
57
+ "small": {
58
+ "default": 47,
59
+ "resized": 26.1
60
+ },
61
+ "medium": {
62
+ "default": 74,
63
+ "resized": 38.7
64
+ },
65
+ "big": {
66
+ "default": 110,
67
+ "resized": 61
68
+ }
69
+ };
70
+ this.lineHeight = {
71
+ "small": {
72
+ "default": 1,
73
+ "resized": 1
74
+ },
75
+ "medium": {
76
+ "default": 1,
77
+ "resized": 1
78
+ },
79
+ "big": {
80
+ "default": 1.03,
81
+ "resized": 1.01
82
+ }
83
+ };
84
+ this.doubleResizeMaxWidth = {
85
+ full: {
86
+ legacy: 3020,
87
+ default: 3220
88
+ },
89
+ normal: {
90
+ legacy: 2540,
91
+ default: 2740
92
+ }
93
+ };
94
+ var parsedData = options.formatted ? data : this.parseData(data);
95
+ this.video = options.video ? options.video : null;
96
+ this.showCollision = options.showCollision;
97
+ this.showFPS = options.showFPS;
98
+ this.showCommentCount = options.showCommentCount;
99
+ this.enableLegacyPiP = options.enableLegacyPiP;
100
+ this.timeline = {};
101
+ this.nicoScripts = { reverse: [], default: [], replace: [], ban: [] };
102
+ this.collision_right = {};
103
+ this.collision_left = {};
104
+ this.collision_ue = {};
105
+ this.collision_shita = {};
106
+ this.data = [];
107
+ this.lastVpos = -1;
108
+ this.useLegacy = options.useLegacy;
109
+ this.preRendering(parsedData, options.drawAllImageOnLoad);
110
+ this.fpsCount = 0;
111
+ this.fps = 0;
112
+ window.setInterval(function () {
113
+ _this.fps = _this.fpsCount * 2;
114
+ _this.fpsCount = 0;
115
+ }, 500);
116
+ logger("constructor complete: ".concat(performance.now() - constructorStart, "ms"));
117
+ }
118
+ NiconiComments.prototype.parseData = function (data) {
119
+ var parseDataStart = performance.now();
120
+ var data_ = [];
121
+ for (var i = 0; i < data.length; i++) {
122
+ var val = data[i];
123
+ if (!val)
124
+ continue;
125
+ for (var key in val) {
126
+ var value = val[key];
127
+ if (isApiChat(value) && value["deleted"] !== 1) {
128
+ var tmpParam = {
129
+ "id": value["no"],
130
+ "vpos": value["vpos"],
131
+ "content": value["content"],
132
+ "date": value["date"],
133
+ "date_usec": value["date_usec"],
134
+ "owner": !value["user_id"],
135
+ "premium": value["premium"] === 1,
136
+ "mail": []
137
+ };
138
+ if (value["mail"]) {
139
+ tmpParam["mail"] = value["mail"].split(/[\s ]/g);
140
+ }
141
+ if (value["content"].startsWith("/") && !value["user_id"]) {
142
+ tmpParam["mail"].push("invisible");
143
+ }
144
+ data_.push(tmpParam);
145
+ }
146
+ }
147
+ }
148
+ data_.sort(function (a, b) {
149
+ if (a.vpos < b.vpos)
150
+ return -1;
151
+ if (a.vpos > b.vpos)
152
+ return 1;
153
+ if (a.date < b.date)
154
+ return -1;
155
+ if (a.date > b.date)
156
+ return 1;
157
+ if (a.date_usec < b.date_usec)
158
+ return -1;
159
+ if (a.date_usec > b.date_usec)
160
+ return 1;
161
+ return 0;
162
+ });
163
+ logger("parseData complete: ".concat(performance.now() - parseDataStart, "ms"));
164
+ return data_;
165
+ };
166
+ NiconiComments.prototype.preRendering = function (rawData, drawAll) {
167
+ var preRenderingStart = performance.now();
168
+ var parsedData = this.getCommentPos(this.getCommentSize(this.getFont(rawData)));
169
+ this.data = this.sortComment(parsedData);
170
+ if (drawAll) {
171
+ for (var i in parsedData) {
172
+ this.getTextImage(Number(i));
173
+ }
174
+ }
175
+ logger("preRendering complete: ".concat(performance.now() - preRenderingStart, "ms"));
176
+ };
177
+ NiconiComments.prototype.getFont = function (parsedData) {
178
+ var getFontStart = performance.now();
179
+ var result = [];
180
+ for (var i in parsedData) {
181
+ var value = parsedData[i];
182
+ if (!value)
183
+ continue;
184
+ value.content = value.content.replace(/\t/g, "\u2003\u2003");
185
+ result[i] = this.parseCommandAndNicoscript(value);
186
+ }
187
+ logger("getFont complete: ".concat(performance.now() - getFontStart, "ms"));
188
+ return result;
189
+ };
190
+ NiconiComments.prototype.getCommentSize = function (parsedData) {
191
+ var getCommentSizeStart = performance.now();
192
+ var tmpData = groupBy(parsedData, "font", "fontSize");
193
+ var result = [];
194
+ for (var i in tmpData) {
195
+ for (var j in tmpData[i]) {
196
+ this.context.font = parseFont(i, j, this.useLegacy);
197
+ for (var k in tmpData[i][j]) {
198
+ var comment = tmpData[i][j][k];
199
+ if (comment.invisible) {
200
+ continue;
201
+ }
202
+ var measure = this.measureText(comment);
203
+ var size = parsedData[comment.index];
204
+ size.height = measure.height;
205
+ size.width = measure.width;
206
+ size.width_max = measure.width_max;
207
+ size.width_min = measure.width_min;
208
+ size.lineHeight = measure.lineHeight;
209
+ if (measure.resized) {
210
+ size.fontSize = measure.fontSize;
211
+ this.context.font = parseFont(i, j, this.useLegacy);
212
+ }
213
+ result[comment.index] = size;
214
+ }
215
+ }
216
+ }
217
+ logger("getCommentSize complete: ".concat(performance.now() - getCommentSizeStart, "ms"));
218
+ return result;
219
+ };
220
+ NiconiComments.prototype.getCommentPos = function (parsedData) {
221
+ var getCommentPosStart = performance.now();
222
+ var data = parsedData;
223
+ for (var i in data) {
224
+ var comment = data[i];
225
+ if (!comment || comment.invisible) {
226
+ continue;
227
+ }
228
+ for (var j = 0; j < 500; j++) {
229
+ if (!this.timeline[comment.vpos + j]) {
230
+ this.timeline[comment.vpos + j] = [];
231
+ }
232
+ if (!this.collision_right[comment.vpos + j]) {
233
+ this.collision_right[comment.vpos + j] = [];
234
+ }
235
+ if (!this.collision_left[comment.vpos + j]) {
236
+ this.collision_left[comment.vpos + j] = [];
237
+ }
238
+ if (!this.collision_ue[comment.vpos + j]) {
239
+ this.collision_ue[comment.vpos + j] = [];
240
+ }
241
+ if (!this.collision_shita[comment.vpos + j]) {
242
+ this.collision_shita[comment.vpos + j] = [];
243
+ }
244
+ }
245
+ if (comment.loc === "naka") {
246
+ comment.vpos -= 70;
247
+ parsedData[i].vpos -= 70;
248
+ var posY = 0, is_break = false, is_change = true, count = 0;
249
+ if (1080 < comment.height) {
250
+ posY = (comment.height - 1080) / -2;
251
+ }
252
+ else {
253
+ while (is_change && count < 10) {
254
+ is_change = false;
255
+ count++;
256
+ for (var j = 0; j < 500; j++) {
257
+ var vpos = comment.vpos + j;
258
+ var left_pos = 1920 - ((1920 + comment.width_max) * j / 500);
259
+ if (left_pos + comment.width_max >= 1880) {
260
+ for (var k in this.collision_right[vpos]) {
261
+ var l = this.collision_right[vpos][k];
262
+ if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
263
+ if (data[l].posY + data[l].height > posY) {
264
+ posY = data[l].posY + data[l].height;
265
+ is_change = true;
266
+ }
267
+ if (posY + comment.height > 1080) {
268
+ if (1080 < comment.height) {
269
+ posY = (comment.height - 1080) / -2;
270
+ }
271
+ else {
272
+ posY = Math.floor(Math.random() * (1080 - comment.height));
273
+ }
274
+ is_break = true;
275
+ break;
276
+ }
277
+ }
278
+ }
279
+ if (is_break) {
280
+ break;
281
+ }
282
+ }
283
+ if (left_pos <= 40) {
284
+ for (var k in this.collision_left[vpos]) {
285
+ var l = this.collision_left[vpos][k];
286
+ if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
287
+ if (data[l].posY + data[l].height > posY) {
288
+ posY = data[l].posY + data[l].height;
289
+ is_change = true;
290
+ }
291
+ if (posY + comment.height > 1080) {
292
+ if (1080 < comment.height) {
293
+ posY = 0;
294
+ }
295
+ else {
296
+ posY = Math.random() * (1080 - comment.height);
297
+ }
298
+ is_break = true;
299
+ break;
300
+ }
301
+ }
302
+ }
303
+ if (is_break) {
304
+ break;
305
+ }
306
+ }
307
+ }
308
+ if (is_break) {
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ for (var j = 0; j < 500; j++) {
314
+ var vpos = comment.vpos + j;
315
+ var left_pos = 1920 - ((1920 + comment.width_max) * j / 500);
316
+ arrayPush(this.timeline, vpos, i);
317
+ if (left_pos + comment.width_max >= 1880) {
318
+ arrayPush(this.collision_right, vpos, i);
319
+ }
320
+ if (left_pos <= 40) {
321
+ arrayPush(this.collision_left, vpos, i);
322
+ }
323
+ }
324
+ parsedData[i].posY = posY;
325
+ }
326
+ else {
327
+ var posY = 0, is_break = false, is_change = true, count = 0, collision = void 0;
328
+ if (comment.loc === "ue") {
329
+ collision = this.collision_ue;
330
+ }
331
+ else if (comment.loc === "shita") {
332
+ collision = this.collision_shita;
333
+ }
334
+ while (is_change && count < 10) {
335
+ is_change = false;
336
+ count++;
337
+ for (var j = 0; j < 300; j++) {
338
+ var vpos = comment.vpos + j;
339
+ for (var k in collision[vpos]) {
340
+ var l = collision[vpos][k];
341
+ if ((posY < data[l].posY + data[l].height && posY + comment.height > data[l].posY) && data[l].owner === comment.owner) {
342
+ if (data[l].posY + data[l].height > posY) {
343
+ posY = data[l].posY + data[l].height;
344
+ is_change = true;
345
+ }
346
+ if (posY + comment.height > 1080) {
347
+ if (1000 <= comment.height) {
348
+ posY = 0;
349
+ }
350
+ else {
351
+ posY = Math.floor(Math.random() * (1080 - comment.height));
352
+ }
353
+ is_break = true;
354
+ break;
355
+ }
356
+ }
357
+ }
358
+ if (is_break) {
359
+ break;
360
+ }
361
+ }
362
+ }
363
+ for (var j = 0; j < comment.long; j++) {
364
+ var vpos = comment.vpos + j;
365
+ arrayPush(this.timeline, vpos, i);
366
+ if (comment.loc === "ue") {
367
+ arrayPush(this.collision_ue, vpos, i);
368
+ }
369
+ else {
370
+ arrayPush(this.collision_shita, vpos, i);
371
+ }
372
+ }
373
+ parsedData[i].posY = posY;
374
+ }
375
+ }
376
+ logger("getCommentPos complete: ".concat(performance.now() - getCommentPosStart, "ms"));
377
+ return parsedData;
378
+ };
379
+ NiconiComments.prototype.sortComment = function (parsedData) {
380
+ var sortCommentStart = performance.now();
381
+ for (var vpos in this.timeline) {
382
+ if (!this.timeline[vpos])
383
+ continue;
384
+ var owner = [], user = [];
385
+ for (var _i = 0, _a = this.timeline[vpos]; _i < _a.length; _i++) {
386
+ var i = _a[_i];
387
+ if (parsedData[i].owner) {
388
+ owner.push(i);
389
+ }
390
+ else {
391
+ user.push(i);
392
+ }
393
+ }
394
+ this.timeline[vpos] = owner.concat(user);
395
+ }
396
+ logger("parseData complete: ".concat(performance.now() - sortCommentStart, "ms"));
397
+ return parsedData;
398
+ };
399
+ NiconiComments.prototype.measureText = function (comment) {
400
+ var width, width_max, width_min, height, width_arr = [], lines = comment.content.split("\n");
401
+ if (!comment.lineHeight)
402
+ comment.lineHeight = this.lineHeight[comment.size].default;
403
+ if (!comment.resized && !comment.ender) {
404
+ if (comment.size === "big" && lines.length > 2) {
405
+ comment.fontSize = this.fontSize.big.resized;
406
+ comment.lineHeight = this.lineHeight.big.resized;
407
+ comment.resized = true;
408
+ comment.tateresized = true;
409
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
410
+ }
411
+ else if (comment.size === "medium" && lines.length > 4) {
412
+ comment.fontSize = this.fontSize.medium.resized;
413
+ comment.lineHeight = this.lineHeight.medium.resized;
414
+ comment.resized = true;
415
+ comment.tateresized = true;
416
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
417
+ }
418
+ else if (comment.size === "small" && lines.length > 6) {
419
+ comment.fontSize = this.fontSize.small.resized;
420
+ comment.lineHeight = this.lineHeight.small.resized;
421
+ comment.resized = true;
422
+ comment.tateresized = true;
423
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
424
+ }
425
+ }
426
+ for (var i = 0; i < lines.length; i++) {
427
+ var measure = this.context.measureText(lines[i]);
428
+ width_arr.push(measure.width);
429
+ }
430
+ width = width_arr.reduce(function (p, c) { return p + c; }, 0) / width_arr.length;
431
+ width_max = Math.max.apply(Math, width_arr);
432
+ width_min = Math.min.apply(Math, width_arr);
433
+ height = (comment.fontSize * comment.lineHeight * (1 + this.commentYPaddingTop) * lines.length) + (this.commentYMarginBottom * comment.fontSize);
434
+ if (comment.loc !== "naka" && !comment.tateresized) {
435
+ if (comment.full && width_max > 1930) {
436
+ comment.fontSize -= 2;
437
+ comment.resized = true;
438
+ comment.yokoResized = true;
439
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
440
+ return this.measureText(comment);
441
+ }
442
+ else if (!comment.full && width_max > 1440) {
443
+ comment.fontSize -= 1;
444
+ comment.resized = true;
445
+ comment.yokoResized = true;
446
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
447
+ return this.measureText(comment);
448
+ }
449
+ }
450
+ else if (comment.loc !== "naka" && comment.tateresized && (comment.full && width_max > 2120 || !comment.full && width_max > 1440) && !comment.yokoResized) {
451
+ comment.fontSize = this.fontSize[comment.size].default;
452
+ comment.resized = true;
453
+ comment.yokoResized = true;
454
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
455
+ return this.measureText(comment);
456
+ }
457
+ else if (comment.loc !== "naka" && comment.tateresized && comment.yokoResized) {
458
+ if (comment.full && width_max > this.doubleResizeMaxWidth.full[this.useLegacy ? "legacy" : "default"]) {
459
+ comment.fontSize -= 1;
460
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
461
+ return this.measureText(comment);
462
+ }
463
+ else if (!comment.full && width_max > this.doubleResizeMaxWidth.normal[this.useLegacy ? "legacy" : "default"]) {
464
+ comment.fontSize -= 1;
465
+ this.context.font = parseFont(comment.font, comment.fontSize, this.useLegacy);
466
+ return this.measureText(comment);
467
+ }
468
+ }
469
+ return {
470
+ "width": width,
471
+ "width_max": width_max,
472
+ "width_min": width_min,
473
+ "height": height,
474
+ "resized": comment.resized,
475
+ "fontSize": comment.fontSize,
476
+ "lineHeight": comment.lineHeight
477
+ };
478
+ };
479
+ NiconiComments.prototype.drawText = function (comment, vpos) {
480
+ var reverse = false;
481
+ for (var i in this.nicoScripts.reverse) {
482
+ var range = this.nicoScripts.reverse[i];
483
+ if ((range.target === "コメ" && comment.owner) || (range.target === "投コメ" && !comment.owner)) {
484
+ break;
485
+ }
486
+ if (range.start < vpos && vpos < range.end) {
487
+ reverse = true;
488
+ }
489
+ }
490
+ for (var i in this.nicoScripts.ban) {
491
+ var range = this.nicoScripts.ban[i];
492
+ if (range.start < vpos && vpos < range.end) {
493
+ return;
494
+ }
495
+ }
496
+ var posX = (1920 - comment.width_max) / 2, posY = comment.posY;
497
+ if (comment.loc === "naka") {
498
+ if (reverse) {
499
+ posX = ((1920 + comment.width_max) * (vpos - comment.vpos) / 500) - comment.width_max;
500
+ }
501
+ else {
502
+ posX = 1920 - ((1920 + comment.width_max) * (vpos - comment.vpos) / 500);
503
+ }
504
+ }
505
+ else if (comment.loc === "shita") {
506
+ posY = 1080 - comment.posY - comment.height;
507
+ }
508
+ if (comment.image && comment.image !== true) {
509
+ this.context.drawImage(comment.image, posX, posY);
510
+ }
511
+ if (this.showCollision) {
512
+ this.context.strokeStyle = "rgba(0,255,255,1)";
513
+ this.context.strokeRect(posX, posY, comment.width_max, comment.height);
514
+ var lines = comment.content.split("\n");
515
+ for (var i in lines) {
516
+ var linePosY = (Number(i) + 1) * (comment.fontSize * comment.lineHeight) * (1 + this.commentYPaddingTop);
517
+ this.context.strokeStyle = "rgba(255,255,0,0.5)";
518
+ this.context.strokeRect(posX, posY + linePosY, comment.width_max, comment.fontSize * comment.lineHeight * -1);
519
+ }
520
+ }
521
+ };
522
+ NiconiComments.prototype.getTextImage = function (i) {
523
+ var _this = this;
524
+ var value = this.data[i];
525
+ if (!value || value.invisible)
526
+ return;
527
+ var image = document.createElement("canvas");
528
+ image.width = value.width_max;
529
+ image.height = value.height;
530
+ var context = image.getContext("2d");
531
+ if (!context)
532
+ throw new Error("Fail to get CanvasRenderingContext2D");
533
+ context.strokeStyle = "rgba(0,0,0,0.7)";
534
+ context.textAlign = "start";
535
+ context.textBaseline = "alphabetic";
536
+ context.lineWidth = 4;
537
+ context.font = parseFont(value.font, value.fontSize, this.useLegacy);
538
+ if (value._live) {
539
+ var rgb = hex2rgb(value.color);
540
+ context.fillStyle = "rgba(".concat(rgb[0], ",").concat(rgb[1], ",").concat(rgb[2], ",0.5)");
541
+ }
542
+ else {
543
+ context.fillStyle = value.color;
544
+ }
545
+ if (value.color === "#000000") {
546
+ context.strokeStyle = "rgba(255,255,255,0.7)";
547
+ }
548
+ var lines = value.content.split("\n");
549
+ for (var i_1 in lines) {
550
+ var line = lines[i_1], posY = void 0;
551
+ posY = (Number(i_1) + 1) * (value.fontSize * value.lineHeight) * (1 + this.commentYPaddingTop);
552
+ context.strokeText(line, 0, posY);
553
+ context.fillText(line, 0, posY);
554
+ }
555
+ this.data[i].image = image;
556
+ setTimeout(function () {
557
+ if (_this.data[i].image)
558
+ delete _this.data[i].image;
559
+ }, 5000);
560
+ };
561
+ NiconiComments.prototype.parseCommand = function (comment) {
562
+ var metadata = comment.mail, loc = null, size = null, fontSize = null, color = null, font = null, full = false, ender = false, _live = false, invisible = false, long = null;
563
+ for (var i in metadata) {
564
+ var command = metadata[i].toLowerCase();
565
+ var match = command.match(/^@([0-9.]+)/);
566
+ if (match) {
567
+ long = match[1];
568
+ }
569
+ if (loc === null) {
570
+ switch (command) {
571
+ case "ue":
572
+ loc = "ue";
573
+ break;
574
+ case "shita":
575
+ loc = "shita";
576
+ break;
577
+ }
578
+ }
579
+ if (size === null) {
580
+ switch (command) {
581
+ case "big":
582
+ size = "big";
583
+ fontSize = this.fontSize.big.default;
584
+ break;
585
+ case "small":
586
+ size = "small";
587
+ fontSize = this.fontSize.small.default;
588
+ break;
589
+ }
590
+ }
591
+ if (color === null) {
592
+ switch (command) {
593
+ case "white":
594
+ color = "#FFFFFF";
595
+ break;
596
+ case "red":
597
+ color = "#FF0000";
598
+ break;
599
+ case "pink":
600
+ color = "#FF8080";
601
+ break;
602
+ case "orange":
603
+ color = "#FFC000";
604
+ break;
605
+ case "yellow":
606
+ color = "#FFFF00";
607
+ break;
608
+ case "green":
609
+ color = "#00FF00";
610
+ break;
611
+ case "cyan":
612
+ color = "#00FFFF";
613
+ break;
614
+ case "blue":
615
+ color = "#0000FF";
616
+ break;
617
+ case "purple":
618
+ color = "#C000FF";
619
+ break;
620
+ case "black":
621
+ color = "#000000";
622
+ break;
623
+ case "white2":
624
+ case "niconicowhite":
625
+ color = "#CCCC99";
626
+ break;
627
+ case "red2":
628
+ case "truered":
629
+ color = "#CC0033";
630
+ break;
631
+ case "pink2":
632
+ color = "#FF33CC";
633
+ break;
634
+ case "orange2":
635
+ case "passionorange":
636
+ color = "#FF6600";
637
+ break;
638
+ case "yellow2":
639
+ case "madyellow":
640
+ color = "#999900";
641
+ break;
642
+ case "green2":
643
+ case "elementalgreen":
644
+ color = "#00CC66";
645
+ break;
646
+ case "cyan2":
647
+ color = "#00CCCC";
648
+ break;
649
+ case "blue2":
650
+ case "marineblue":
651
+ color = "#3399FF";
652
+ break;
653
+ case "purple2":
654
+ case "nobleviolet":
655
+ color = "#6633CC";
656
+ break;
657
+ case "black2":
658
+ color = "#666666";
659
+ break;
660
+ default:
661
+ var match_1 = command.match(/#[0-9a-z]{3,6}/);
662
+ if (match_1 && comment.premium) {
663
+ color = match_1[0].toUpperCase();
664
+ }
665
+ break;
666
+ }
667
+ }
668
+ if (font === null) {
669
+ switch (command) {
670
+ case "gothic":
671
+ font = "gothic";
672
+ break;
673
+ case "mincho":
674
+ font = "mincho";
675
+ break;
676
+ }
677
+ }
678
+ switch (command) {
679
+ case "full":
680
+ full = true;
681
+ break;
682
+ case "ender":
683
+ ender = true;
684
+ break;
685
+ case "_live":
686
+ _live = true;
687
+ break;
688
+ case "invisible":
689
+ invisible = true;
690
+ break;
691
+ }
692
+ }
693
+ return { loc: loc, size: size, fontSize: fontSize, color: color, font: font, full: full, ender: ender, _live: _live, invisible: invisible, long: long };
694
+ };
695
+ NiconiComments.prototype.parseCommandAndNicoscript = function (comment) {
696
+ var data = this.parseCommand(comment), nicoscript = comment.content.match(/^@(デフォルト|置換|逆|コメント禁止|シーク禁止|ジャンプ)/);
697
+ if (nicoscript) {
698
+ switch (nicoscript[1]) {
699
+ case "デフォルト":
700
+ this.nicoScripts.default.push({
701
+ start: comment.vpos,
702
+ long: data.long === null ? null : Math.floor(data.long * 100),
703
+ color: data.color,
704
+ size: data.size,
705
+ font: data.font,
706
+ loc: data.loc
707
+ });
708
+ break;
709
+ case "逆":
710
+ var reverse = comment.content.match(/^@逆 ?(全|コメ|投コメ)?/);
711
+ if (!reverse)
712
+ reverse = [];
713
+ if (!reverse[1]) {
714
+ reverse[1] = "全";
715
+ }
716
+ if (data.long === null) {
717
+ data.long = 30;
718
+ }
719
+ this.nicoScripts.reverse.push({
720
+ start: comment.vpos,
721
+ end: comment.vpos + (data.long * 100),
722
+ target: reverse[1]
723
+ });
724
+ break;
725
+ case "コメント禁止":
726
+ if (data.long === null) {
727
+ data.long = 30;
728
+ }
729
+ this.nicoScripts.reverse.push({
730
+ start: comment.vpos,
731
+ end: comment.vpos + (data.long * 100),
732
+ });
733
+ break;
734
+ case "置換":
735
+ var content = comment.content.split(""), quote = "", last_i = "", string = "", result = [];
736
+ for (var _i = 0, _a = content.slice(4); _i < _a.length; _i++) {
737
+ var i = _a[_i];
738
+ if (i.match(/["'「]/) && quote === "") {
739
+ quote = i;
740
+ }
741
+ else if (i.match(/["']/) && quote === i && last_i !== "\\") {
742
+ result.push(replaceAll(string, "\\n", "\n"));
743
+ quote = "";
744
+ string = "";
745
+ }
746
+ else if (i.match(/」/) && quote === "「") {
747
+ result.push(string);
748
+ quote = "";
749
+ string = "";
750
+ }
751
+ else if (quote === "" && i.match(/[\s ]/)) {
752
+ if (string) {
753
+ result.push(string);
754
+ string = "";
755
+ }
756
+ }
757
+ else {
758
+ string += i;
759
+ }
760
+ last_i = i;
761
+ }
762
+ result.push(string);
763
+ this.nicoScripts.replace.push({
764
+ start: comment.vpos,
765
+ long: data.long === null ? null : Math.floor(data.long * 100),
766
+ keyword: result[0],
767
+ replace: result[1] || "",
768
+ range: result[2] || "単",
769
+ target: result[3] || "コメ",
770
+ condition: result[4] || "部分一致",
771
+ color: data.color,
772
+ size: data.size,
773
+ font: data.font,
774
+ loc: data.loc
775
+ });
776
+ break;
777
+ }
778
+ data.invisible = true;
779
+ }
780
+ var color = "#FFFFFF", size = "medium", font = "defont", loc = "naka";
781
+ for (var i in this.nicoScripts.default) {
782
+ if (this.nicoScripts.default[i].long !== null && this.nicoScripts.default[i].start + this.nicoScripts.default[i].long < comment.vpos) {
783
+ this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
784
+ continue;
785
+ }
786
+ if (this.nicoScripts.default[i].loc) {
787
+ loc = this.nicoScripts.default[i].loc;
788
+ }
789
+ if (this.nicoScripts.default[i].color) {
790
+ color = this.nicoScripts.default[i].color;
791
+ }
792
+ if (this.nicoScripts.default[i].size) {
793
+ size = this.nicoScripts.default[i].size;
794
+ }
795
+ if (this.nicoScripts.default[i].font) {
796
+ font = this.nicoScripts.default[i].font;
797
+ }
798
+ }
799
+ for (var i in this.nicoScripts.replace) {
800
+ if (this.nicoScripts.replace[i].long !== null && this.nicoScripts.replace[i].start + this.nicoScripts.replace[i].long < comment.vpos) {
801
+ this.nicoScripts.default = this.nicoScripts.default.splice(Number(i), 1);
802
+ continue;
803
+ }
804
+ var item = this.nicoScripts.replace[i];
805
+ if ((item.target === "コメ" && comment.owner) || (item.target === "投コメ" && !comment.owner) || (item.target === "含まない" && comment.owner))
806
+ continue;
807
+ if ((item.condition === "完全一致" && comment.content === item.keyword) || (item.condition === "部分一致" && comment.content.indexOf(item.keyword) !== -1)) {
808
+ if (item.range === "単") {
809
+ comment.content = replaceAll(comment.content, item.keyword, item.replace);
810
+ }
811
+ else {
812
+ comment.content = item.replace;
813
+ }
814
+ if (item.loc) {
815
+ loc = item.loc;
816
+ }
817
+ if (item.color) {
818
+ color = item.color;
819
+ }
820
+ if (item.size) {
821
+ size = item.size;
822
+ }
823
+ if (item.font) {
824
+ font = item.font;
825
+ }
826
+ }
827
+ }
828
+ if (!data.loc) {
829
+ data.loc = loc;
830
+ }
831
+ if (!data.color) {
832
+ data.color = color;
833
+ }
834
+ if (!data.size) {
835
+ data.size = size;
836
+ data.fontSize = this.fontSize[data.size].default;
837
+ }
838
+ if (!data.font) {
839
+ data.font = font;
840
+ }
841
+ if (data.loc !== "naka") {
842
+ if (!data.long) {
843
+ data.long = 300;
844
+ }
845
+ else {
846
+ data.long = Math.floor(data.long * 100);
847
+ }
848
+ }
849
+ return _assign(_assign({}, comment), data);
850
+ };
851
+ NiconiComments.prototype.drawCanvas = function (vpos) {
852
+ var drawCanvasStart = performance.now();
853
+ if (this.lastVpos === vpos)
854
+ return;
855
+ this.lastVpos = vpos;
856
+ this.fpsCount++;
857
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
858
+ if (this.video) {
859
+ var offsetX = void 0, offsetY = void 0, scale = void 0, height = this.canvas.height / this.video.videoHeight, width = this.canvas.width / this.video.videoWidth;
860
+ if (this.enableLegacyPiP ? height > width : height < width) {
861
+ scale = width;
862
+ }
863
+ else {
864
+ scale = height;
865
+ }
866
+ offsetX = (this.canvas.width - this.video.videoWidth * scale) * 0.5;
867
+ offsetY = (this.canvas.height - this.video.videoHeight * scale) * 0.5;
868
+ this.context.drawImage(this.video, offsetX, offsetY, this.video.videoWidth * scale, this.video.videoHeight * scale);
869
+ }
870
+ if (this.timeline[vpos]) {
871
+ for (var i in this.timeline[vpos]) {
872
+ var index = this.timeline[vpos][Number(i)];
873
+ var comment = this.data[index];
874
+ if (!comment || comment.invisible) {
875
+ continue;
876
+ }
877
+ if (comment.image === undefined) {
878
+ this.getTextImage(index);
879
+ }
880
+ try {
881
+ this.drawText(comment, vpos);
882
+ }
883
+ catch (e) {
884
+ comment.image = false;
885
+ }
886
+ }
887
+ }
888
+ if (this.showFPS) {
889
+ this.context.font = parseFont("defont", 60, this.useLegacy);
890
+ this.context.fillStyle = "#00FF00";
891
+ this.context.strokeStyle = "rgba(0,0,0,0.7)";
892
+ this.context.strokeText("FPS:" + this.fps, 100, 100);
893
+ this.context.fillText("FPS:" + this.fps, 100, 100);
894
+ }
895
+ if (this.showCommentCount) {
896
+ this.context.font = parseFont("defont", 60, this.useLegacy);
897
+ this.context.fillStyle = "#00FF00";
898
+ this.context.strokeStyle = "rgba(0,0,0,0.7)";
899
+ if (this.timeline[vpos]) {
900
+ this.context.strokeText("Count:" + this.timeline[vpos].length, 100, 200);
901
+ this.context.fillText("Count:" + this.timeline[vpos].length, 100, 200);
902
+ }
903
+ else {
904
+ this.context.strokeText("Count:0", 100, 200);
905
+ this.context.fillText("Count:0", 100, 200);
906
+ }
907
+ }
908
+ logger("drawCanvas complete: ".concat(performance.now() - drawCanvasStart, "ms"));
909
+ };
910
+ NiconiComments.prototype.clear = function () {
911
+ this.context.clearRect(0, 0, 1920, 1080);
912
+ };
913
+ return NiconiComments;
914
+ }());
915
+ var groupBy = function (array, key, key2) {
916
+ var data = {};
917
+ for (var i in array) {
918
+ if (!data[array[i][key]]) {
919
+ data[array[i][key]] = {};
920
+ }
921
+ if (!data[array[i][key]][array[i][key2]]) {
922
+ data[array[i][key]][array[i][key2]] = [];
923
+ }
924
+ array[i].index = i;
925
+ data[array[i][key]][array[i][key2]].push(array[i]);
926
+ }
927
+ return data;
928
+ };
929
+ var parseFont = function (font, size, useLegacy) {
930
+ switch (font) {
931
+ case "gothic":
932
+ return "normal 400 ".concat(size, "px \"\u6E38\u30B4\u30B7\u30C3\u30AF\u4F53\", \"\u6E38\u30B4\u30B7\u30C3\u30AF\", \"Yu Gothic\", YuGothic, yugothic, YuGo-Medium");
933
+ case "mincho":
934
+ return "normal 400 ".concat(size, "px \"\u6E38\u660E\u671D\u4F53\", \"\u6E38\u660E\u671D\", \"Yu Mincho\", YuMincho, yumincho, YuMin-Medium");
935
+ default:
936
+ if (useLegacy) {
937
+ return "normal 600 ".concat(size, "px Arial, \"\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF\", \"MS PGothic\", MSPGothic, MS-PGothic");
938
+ }
939
+ else {
940
+ return "normal 600 ".concat(size, "px sans-serif, Arial, \"\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF\", \"MS PGothic\", MSPGothic, MS-PGothic");
941
+ }
942
+ }
943
+ };
944
+ var arrayPush = function (array, key, push) {
945
+ if (!array) {
946
+ array = {};
947
+ }
948
+ if (!array[key]) {
949
+ array[key] = [];
950
+ }
951
+ array[key].push(push);
952
+ };
953
+ var hex2rgb = function (hex) {
954
+ if (hex.slice(0, 1) === "#")
955
+ hex = hex.slice(1);
956
+ if (hex.length === 3)
957
+ hex = hex.slice(0, 1) + hex.slice(0, 1) + hex.slice(1, 2) + hex.slice(1, 2) + hex.slice(2, 3) + hex.slice(2, 3);
958
+ return [hex.slice(0, 2), hex.slice(2, 4), hex.slice(4, 6)].map(function (str) {
959
+ return parseInt(str, 16);
960
+ });
961
+ };
962
+ var replaceAll = function (string, target, replace) {
963
+ var count = 0;
964
+ while (string.indexOf(target) !== -1 && count < 100) {
965
+ string = string.replace(target, replace);
966
+ count++;
967
+ }
968
+ return string;
969
+ };
970
+ var isApiChat = function (item) {
971
+ return item.no && item.vpos && item.content;
972
+ };
973
+ var logger = function (msg) {
974
+ if (isDebug)
975
+ console.debug(msg);
976
976
  };
977
977
 
978
978
  return NiconiComments;