community-cordova-plugin-nfc 1.3.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.
@@ -0,0 +1,912 @@
1
+ /*jshint bitwise: false, camelcase: false, quotmark: false, unused: vars, esversion: 6, browser: true*/
2
+ /*global cordova, console, require */
3
+
4
+ function handleNfcFromIntentFilter() {
5
+
6
+ // This was historically done in cordova.addConstructor but broke with PhoneGap-2.2.0.
7
+ // We need to handle NFC from an Intent that launched the application, but *after*
8
+ // the code in the application's deviceready has run. After upgrading to 2.2.0,
9
+ // addConstructor was finishing *before* deviceReady was complete and the
10
+ // ndef listeners had not been registered.
11
+ // It seems like there should be a better solution.
12
+ if (cordova.platformId === "android" || cordova.platformId === "windows") {
13
+ setTimeout(
14
+ function () {
15
+ cordova.exec(
16
+ function () {
17
+ console.log("Initialized the NfcPlugin");
18
+ },
19
+ function (reason) {
20
+ console.log("Failed to initialize the NfcPlugin " + reason);
21
+ },
22
+ "NfcPlugin", "init", []
23
+ );
24
+ }, 10
25
+ );
26
+ }
27
+ }
28
+
29
+ document.addEventListener('deviceready', handleNfcFromIntentFilter, false);
30
+
31
+ var ndef = {
32
+
33
+ // see android.nfc.NdefRecord for documentation about constants
34
+ // http://developer.android.com/reference/android/nfc/NdefRecord.html
35
+ TNF_EMPTY: 0x0,
36
+ TNF_WELL_KNOWN: 0x01,
37
+ TNF_MIME_MEDIA: 0x02,
38
+ TNF_ABSOLUTE_URI: 0x03,
39
+ TNF_EXTERNAL_TYPE: 0x04,
40
+ TNF_UNKNOWN: 0x05,
41
+ TNF_UNCHANGED: 0x06,
42
+ TNF_RESERVED: 0x07,
43
+
44
+ RTD_TEXT: [0x54], // "T"
45
+ RTD_URI: [0x55], // "U"
46
+ RTD_SMART_POSTER: [0x53, 0x70], // "Sp"
47
+ RTD_ALTERNATIVE_CARRIER: [0x61, 0x63], // "ac"
48
+ RTD_HANDOVER_CARRIER: [0x48, 0x63], // "Hc"
49
+ RTD_HANDOVER_REQUEST: [0x48, 0x72], // "Hr"
50
+ RTD_HANDOVER_SELECT: [0x48, 0x73], // "Hs"
51
+
52
+ /**
53
+ * Creates a JSON representation of a NDEF Record.
54
+ *
55
+ * @tnf 3-bit TNF (Type Name Format) - use one of the TNF_* constants
56
+ * @type byte array, containing zero to 255 bytes, must not be null
57
+ * @id byte array, containing zero to 255 bytes, must not be null
58
+ * @payload byte array, containing zero to (2 ** 32 - 1) bytes, must not be null
59
+ *
60
+ * @returns JSON representation of a NDEF record
61
+ *
62
+ * @see Ndef.textRecord, Ndef.uriRecord and Ndef.mimeMediaRecord for examples
63
+ */
64
+ record: function (tnf, type, id, payload) {
65
+
66
+ // handle null values
67
+ if (!tnf) { tnf = ndef.TNF_EMPTY; }
68
+ if (!type) { type = []; }
69
+ if (!id) { id = []; }
70
+ if (!payload) { payload = []; }
71
+
72
+ // convert strings to arrays
73
+ if (!(type instanceof Array)) {
74
+ type = nfc.stringToBytes(type);
75
+ }
76
+ if (!(id instanceof Array)) {
77
+ id = nfc.stringToBytes(id);
78
+ }
79
+ if (!(payload instanceof Array)) {
80
+ payload = nfc.stringToBytes(payload);
81
+ }
82
+
83
+ return {
84
+ tnf: tnf,
85
+ type: type,
86
+ id: id,
87
+ payload: payload
88
+ };
89
+ },
90
+
91
+ /**
92
+ * Helper that creates an NDEF record containing plain text.
93
+ *
94
+ * @text String of text to encode
95
+ * @languageCode ISO/IANA language code. Examples: “fi”, “en-US”, “fr- CA”, “jp”. (optional)
96
+ * @id byte[] (optional)
97
+ */
98
+ textRecord: function (text, languageCode, id) {
99
+ var payload = textHelper.encodePayload(text, languageCode);
100
+ if (!id) { id = []; }
101
+ return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_TEXT, id, payload);
102
+ },
103
+
104
+ /**
105
+ * Helper that creates a NDEF record containing a URI.
106
+ *
107
+ * @uri String
108
+ * @id byte[] (optional)
109
+ */
110
+ uriRecord: function (uri, id) {
111
+ var payload = uriHelper.encodePayload(uri);
112
+ if (!id) { id = []; }
113
+ return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_URI, id, payload);
114
+ },
115
+
116
+ /**
117
+ * Helper that creates a NDEF record containing an absolute URI.
118
+ *
119
+ * An Absolute URI record means the URI describes the payload of the record.
120
+ *
121
+ * For example a SOAP message could use "http://schemas.xmlsoap.org/soap/envelope/"
122
+ * as the type and XML content for the payload.
123
+ *
124
+ * Absolute URI can also be used to write LaunchApp records for Windows.
125
+ *
126
+ * See 2.4.2 Payload Type of the NDEF Specification
127
+ * http://www.nfc-forum.org/specs/spec_list#ndefts
128
+ *
129
+ * Note that by default, Android will open the URI defined in the type
130
+ * field of an Absolute URI record (TNF=3) and ignore the payload.
131
+ * BlackBerry and Windows do not open the browser for TNF=3.
132
+ *
133
+ * To write a URI as the payload use ndef.uriRecord(uri)
134
+ *
135
+ * @uri String
136
+ * @payload byte[] or String
137
+ * @id byte[] (optional)
138
+ */
139
+ absoluteUriRecord: function (uri, payload, id) {
140
+ if (!id) { id = []; }
141
+ if (!payload) { payload = []; }
142
+ return ndef.record(ndef.TNF_ABSOLUTE_URI, uri, id, payload);
143
+ },
144
+
145
+ /**
146
+ * Helper that creates a NDEF record containing an mimeMediaRecord.
147
+ *
148
+ * @mimeType String
149
+ * @payload byte[]
150
+ * @id byte[] (optional)
151
+ */
152
+ mimeMediaRecord: function (mimeType, payload, id) {
153
+ if (!id) { id = []; }
154
+ return ndef.record(ndef.TNF_MIME_MEDIA, nfc.stringToBytes(mimeType), id, payload);
155
+ },
156
+
157
+ /**
158
+ * Helper that creates an NDEF record containing an Smart Poster.
159
+ *
160
+ * @ndefRecords array of NDEF Records
161
+ * @id byte[] (optional)
162
+ */
163
+ smartPoster: function (ndefRecords, id) {
164
+ var payload = [];
165
+
166
+ if (!id) { id = []; }
167
+
168
+ if (ndefRecords)
169
+ {
170
+ // make sure we have an array of something like NDEF records before encoding
171
+ if (ndefRecords[0] instanceof Object && ndefRecords[0].hasOwnProperty('tnf')) {
172
+ payload = ndef.encodeMessage(ndefRecords);
173
+ } else {
174
+ // assume the caller has already encoded the NDEF records into a byte array
175
+ payload = ndefRecords;
176
+ }
177
+ } else {
178
+ console.log("WARNING: Expecting an array of NDEF records");
179
+ }
180
+
181
+ return ndef.record(ndef.TNF_WELL_KNOWN, ndef.RTD_SMART_POSTER, id, payload);
182
+ },
183
+
184
+ /**
185
+ * Helper that creates an empty NDEF record.
186
+ *
187
+ */
188
+ emptyRecord: function() {
189
+ return ndef.record(ndef.TNF_EMPTY, [], [], []);
190
+ },
191
+
192
+ /**
193
+ * Helper that creates an Android Application Record (AAR).
194
+ * http://developer.android.com/guide/topics/connectivity/nfc/nfc.html#aar
195
+ *
196
+ */
197
+ androidApplicationRecord: function(packageName) {
198
+ return ndef.record(ndef.TNF_EXTERNAL_TYPE, "android.com:pkg", [], packageName);
199
+ },
200
+
201
+ /**
202
+ * Encodes an NDEF Message into bytes that can be written to a NFC tag.
203
+ *
204
+ * @ndefRecords an Array of NDEF Records
205
+ *
206
+ * @returns byte array
207
+ *
208
+ * @see NFC Data Exchange Format (NDEF) http://www.nfc-forum.org/specs/spec_list/
209
+ */
210
+ encodeMessage: function (ndefRecords) {
211
+
212
+ var encoded = [],
213
+ tnf_byte,
214
+ type_length,
215
+ payload_length,
216
+ id_length,
217
+ i,
218
+ mb, me, // messageBegin, messageEnd
219
+ cf = false, // chunkFlag TODO implement
220
+ sr, // boolean shortRecord
221
+ il; // boolean idLengthFieldIsPresent
222
+
223
+ for(i = 0; i < ndefRecords.length; i++) {
224
+
225
+ mb = (i === 0);
226
+ me = (i === (ndefRecords.length - 1));
227
+ sr = (ndefRecords[i].payload.length < 0xFF);
228
+ il = (ndefRecords[i].id.length > 0);
229
+ tnf_byte = ndef.encodeTnf(mb, me, cf, sr, il, ndefRecords[i].tnf);
230
+ encoded.push(tnf_byte);
231
+
232
+ type_length = ndefRecords[i].type.length;
233
+ encoded.push(type_length);
234
+
235
+ if (sr) {
236
+ payload_length = ndefRecords[i].payload.length;
237
+ encoded.push(payload_length);
238
+ } else {
239
+ payload_length = ndefRecords[i].payload.length;
240
+ // 4 bytes
241
+ encoded.push((payload_length >> 24));
242
+ encoded.push((payload_length >> 16));
243
+ encoded.push((payload_length >> 8));
244
+ encoded.push((payload_length & 0xFF));
245
+ }
246
+
247
+ if (il) {
248
+ id_length = ndefRecords[i].id.length;
249
+ encoded.push(id_length);
250
+ }
251
+
252
+ encoded = encoded.concat(ndefRecords[i].type);
253
+
254
+ if (il) {
255
+ encoded = encoded.concat(ndefRecords[i].id);
256
+ }
257
+
258
+ encoded = encoded.concat(ndefRecords[i].payload);
259
+ }
260
+
261
+ return encoded;
262
+ },
263
+
264
+ /**
265
+ * Decodes an array bytes into an NDEF Message
266
+ *
267
+ * @bytes an array bytes read from a NFC tag
268
+ *
269
+ * @returns array of NDEF Records
270
+ *
271
+ * @see NFC Data Exchange Format (NDEF) http://www.nfc-forum.org/specs/spec_list/
272
+ */
273
+ decodeMessage: function (ndefBytes) {
274
+
275
+ var bytes = ndefBytes.slice(0), // clone since parsing is destructive
276
+ ndef_message = [],
277
+ tnf_byte,
278
+ header,
279
+ type_length = 0,
280
+ payload_length = 0,
281
+ id_length = 0,
282
+ record_type = [],
283
+ id = [],
284
+ payload = [];
285
+
286
+ while(bytes.length) {
287
+ tnf_byte = bytes.shift();
288
+ header = ndef.decodeTnf(tnf_byte);
289
+
290
+ type_length = bytes.shift();
291
+
292
+ if (header.sr) {
293
+ payload_length = bytes.shift();
294
+ } else {
295
+ // next 4 bytes are length
296
+ payload_length = ((0xFF & bytes.shift()) << 24) |
297
+ ((0xFF & bytes.shift()) << 26) |
298
+ ((0xFF & bytes.shift()) << 8) |
299
+ (0xFF & bytes.shift());
300
+ }
301
+
302
+ if (header.il) {
303
+ id_length = bytes.shift();
304
+ }
305
+
306
+ record_type = bytes.splice(0, type_length);
307
+ id = bytes.splice(0, id_length);
308
+ payload = bytes.splice(0, payload_length);
309
+
310
+ ndef_message.push(
311
+ ndef.record(header.tnf, record_type, id, payload)
312
+ );
313
+
314
+ if (header.me) { break; } // last message
315
+ }
316
+
317
+ return ndef_message;
318
+ },
319
+
320
+ /**
321
+ * Decode the bit flags from a TNF Byte.
322
+ *
323
+ * @returns object with decoded data
324
+ *
325
+ * See NFC Data Exchange Format (NDEF) Specification Section 3.2 RecordLayout
326
+ */
327
+ decodeTnf: function (tnf_byte) {
328
+ return {
329
+ mb: (tnf_byte & 0x80) !== 0,
330
+ me: (tnf_byte & 0x40) !== 0,
331
+ cf: (tnf_byte & 0x20) !== 0,
332
+ sr: (tnf_byte & 0x10) !== 0,
333
+ il: (tnf_byte & 0x8) !== 0,
334
+ tnf: (tnf_byte & 0x7)
335
+ };
336
+ },
337
+
338
+ /**
339
+ * Encode NDEF bit flags into a TNF Byte.
340
+ *
341
+ * @returns tnf byte
342
+ *
343
+ * See NFC Data Exchange Format (NDEF) Specification Section 3.2 RecordLayout
344
+ */
345
+ encodeTnf: function (mb, me, cf, sr, il, tnf) {
346
+
347
+ var value = tnf;
348
+
349
+ if (mb) {
350
+ value = value | 0x80;
351
+ }
352
+
353
+ if (me) {
354
+ value = value | 0x40;
355
+ }
356
+
357
+ // note if cf: me, mb, li must be false and tnf must be 0x6
358
+ if (cf) {
359
+ value = value | 0x20;
360
+ }
361
+
362
+ if (sr) {
363
+ value = value | 0x10;
364
+ }
365
+
366
+ if (il) {
367
+ value = value | 0x8;
368
+ }
369
+
370
+ return value;
371
+ },
372
+
373
+ /**
374
+ * Convert TNF to String for user friendly display
375
+ *
376
+ */
377
+ tnfToString: function (tnf) {
378
+ var value = tnf;
379
+
380
+ switch (tnf) {
381
+ case ndef.TNF_EMPTY:
382
+ value = "Empty";
383
+ break;
384
+ case ndef.TNF_WELL_KNOWN:
385
+ value = "Well Known";
386
+ break;
387
+ case ndef.TNF_MIME_MEDIA:
388
+ value = "Mime Media";
389
+ break;
390
+ case ndef.TNF_ABSOLUTE_URI:
391
+ value = "Absolute URI";
392
+ break;
393
+ case ndef.TNF_EXTERNAL_TYPE:
394
+ value = "External";
395
+ break;
396
+ case ndef.TNF_UNKNOWN:
397
+ value = "Unknown";
398
+ break;
399
+ case ndef.TNF_UNCHANGED:
400
+ value = "Unchanged";
401
+ break;
402
+ case ndef.TNF_RESERVED:
403
+ value = "Reserved";
404
+ break;
405
+ }
406
+ return value;
407
+ }
408
+
409
+ };
410
+
411
+ // nfc provides javascript wrappers to the native phonegap implementation
412
+ var nfc = {
413
+
414
+ multiCallbackTest: function(success, failure) {
415
+ cordova.exec(success, failure, "NfcPlugin", "multiCallbackTest", []);
416
+ },
417
+
418
+ // multiCallbackTest: function(success, failure) {
419
+ // //cordova.exec(success, failure, "NfcPlugin", "multiCallbackTest", []);
420
+ // setInterval(failure, 10000, 'Test from JavaScript!');
421
+ // },
422
+
423
+ addTagDiscoveredListener: function (callback, win, fail) {
424
+ document.addEventListener("tag", callback, false);
425
+ cordova.exec(win, fail, "NfcPlugin", "registerTag", []);
426
+ },
427
+
428
+ addMimeTypeListener: function (mimeType, callback, win, fail) {
429
+ document.addEventListener("ndef-mime", callback, false);
430
+ cordova.exec(win, fail, "NfcPlugin", "registerMimeType", [mimeType]);
431
+ },
432
+
433
+ addNdefListener: function (callback, win, fail) {
434
+ document.addEventListener("ndef", callback, false);
435
+ cordova.exec(win, fail, "NfcPlugin", "registerNdef", []);
436
+ },
437
+
438
+ addNdefFormatableListener: function (callback, win, fail) {
439
+ document.addEventListener("ndef-formatable", callback, false);
440
+ cordova.exec(win, fail, "NfcPlugin", "registerNdefFormatable", []);
441
+ },
442
+
443
+ write: function (ndefMessage, win, fail, options) {
444
+
445
+ if (cordova.platformId === "ios") {
446
+ cordova.exec(win, fail, "NfcPlugin", "writeTag", [ndefMessage, options]);
447
+ } else {
448
+ cordova.exec(win, fail, "NfcPlugin", "writeTag", [ndefMessage]);
449
+ }
450
+ },
451
+
452
+ makeReadOnly: function (win, fail) {
453
+ cordova.exec(win, fail, "NfcPlugin", "makeReadOnly", []);
454
+ },
455
+
456
+ share: function (ndefMessage, win, fail) {
457
+ cordova.exec(win, fail, "NfcPlugin", "shareTag", [ndefMessage]);
458
+ },
459
+
460
+ unshare: function (win, fail) {
461
+ cordova.exec(win, fail, "NfcPlugin", "unshareTag", []);
462
+ },
463
+
464
+ handover: function (uris, win, fail) {
465
+ // if we get a single URI, wrap it in an array
466
+ if (!Array.isArray(uris)) {
467
+ uris = [ uris ];
468
+ }
469
+ cordova.exec(win, fail, "NfcPlugin", "handover", uris);
470
+ },
471
+
472
+ stopHandover: function (win, fail) {
473
+ cordova.exec(win, fail, "NfcPlugin", "stopHandover", []);
474
+ },
475
+
476
+ erase: function (win, fail) {
477
+ cordova.exec(win, fail, "NfcPlugin", "eraseTag", [[]]);
478
+ },
479
+
480
+ enabled: function (win, fail) {
481
+ cordova.exec(win, fail, "NfcPlugin", "enabled", [[]]);
482
+ },
483
+
484
+ removeTagDiscoveredListener: function (callback, win, fail) {
485
+ document.removeEventListener("tag", callback, false);
486
+ cordova.exec(win, fail, "NfcPlugin", "removeTag", []);
487
+ },
488
+
489
+ removeMimeTypeListener: function(mimeType, callback, win, fail) {
490
+ document.removeEventListener("ndef-mime", callback, false);
491
+ cordova.exec(win, fail, "NfcPlugin", "removeMimeType", [mimeType]);
492
+ },
493
+
494
+ removeNdefListener: function (callback, win, fail) {
495
+ document.removeEventListener("ndef", callback, false);
496
+ cordova.exec(win, fail, "NfcPlugin", "removeNdef", []);
497
+ },
498
+
499
+ showSettings: function (win, fail) {
500
+ cordova.exec(win, fail, "NfcPlugin", "showSettings", []);
501
+ },
502
+
503
+ // iOS only - scan for NFC NDEF tag using NFCNDEFReaderSession
504
+ scanNdef: function (options) {
505
+ return new Promise(function(resolve, reject) {
506
+ cordova.exec(resolve, reject, "NfcPlugin", "scanNdef", [options]);
507
+ });
508
+ },
509
+
510
+ // iOS only - scan for NFC Tag using NFCTagReaderSession
511
+ scanTag: function (options) {
512
+ return new Promise(function(resolve, reject) {
513
+ cordova.exec(resolve, reject, "NfcPlugin", "scanTag", [options]);
514
+ });
515
+ },
516
+
517
+ // iOS only - cancel NFC scan session
518
+ cancelScan: function () {
519
+ return new Promise(function(resolve, reject) {
520
+ cordova.exec(resolve, reject, "NfcPlugin", "cancelScan", []);
521
+ });
522
+ },
523
+
524
+ // iOS only - deprecated use scanNdef or scanTag
525
+ beginSession: function (win, fail) {
526
+ // cordova.exec(win, fail, "NfcPlugin", "beginSession", []);
527
+ cordova.exec(win, fail, "NfcPlugin", "beginSession", []);
528
+ },
529
+
530
+ // iOS only - deprecated use cancelScan
531
+ invalidateSession: function (win, fail) {
532
+ cordova.exec(win, fail, "NfcPlugin", "invalidateSession", []);
533
+ },
534
+
535
+ // connect to begin transceive
536
+ connect: function(tech, timeout) {
537
+ return new Promise(function(resolve, reject) {
538
+ cordova.exec(resolve, reject, 'NfcPlugin', 'connect', [tech, timeout]);
539
+ });
540
+ },
541
+
542
+ // close transceive connection
543
+ close: function() {
544
+ return new Promise(function(resolve, reject) {
545
+ cordova.exec(resolve, reject, 'NfcPlugin', 'close', []);
546
+ });
547
+ },
548
+
549
+ // data - ArrayBuffer or string of hex data for transcieve
550
+ // the results of transceive are returned in the promise success as an ArrayBuffer
551
+ transceive: function(data) {
552
+ return new Promise(function(resolve, reject) {
553
+
554
+ var buffer;
555
+ if (typeof data === 'string') {
556
+ buffer = util.hexStringToArrayBuffer(data);
557
+ } else if (data instanceof ArrayBuffer) {
558
+ buffer = data;
559
+ } else if (data instanceof Uint8Array) {
560
+ buffer = data.buffer;
561
+ } else {
562
+ reject("Expecting an ArrayBuffer or String");
563
+ }
564
+
565
+ cordova.exec(resolve, reject, 'NfcPlugin', 'transceive', [buffer]);
566
+ });
567
+ },
568
+
569
+ // Android NfcAdapter.enableReaderMode flags
570
+ FLAG_READER_NFC_A: 0x1,
571
+ FLAG_READER_NFC_B: 0x2,
572
+ FLAG_READER_NFC_F: 0x4,
573
+ FLAG_READER_NFC_V: 0x8,
574
+ FLAG_READER_NFC_BARCODE: 0x10,
575
+ FLAG_READER_SKIP_NDEF_CHECK: 0x80,
576
+ FLAG_READER_NO_PLATFORM_SOUNDS: 0x100,
577
+
578
+ // Android NfcAdapter.enabledReaderMode
579
+ readerMode: function(flags, readCallback, errorCallback) {
580
+ cordova.exec(readCallback, errorCallback, 'NfcPlugin', 'readerMode', [flags]);
581
+ },
582
+
583
+ disableReaderMode: function(successCallback, errorCallback) {
584
+ cordova.exec(successCallback, errorCallback, 'NfcPlugin', 'disableReaderMode', []);
585
+ }
586
+
587
+ };
588
+
589
+ var util = {
590
+ // i must be <= 256
591
+ toHex: function (i) {
592
+ var hex;
593
+
594
+ if (i < 0) {
595
+ i += 256;
596
+ }
597
+
598
+ hex = i.toString(16);
599
+
600
+ // zero padding
601
+ if (hex.length === 1) {
602
+ hex = "0" + hex;
603
+ }
604
+
605
+ return hex;
606
+ },
607
+
608
+ toPrintable: function(i) {
609
+
610
+ if (i >= 0x20 & i <= 0x7F) {
611
+ return String.fromCharCode(i);
612
+ } else {
613
+ return '.';
614
+ }
615
+ },
616
+
617
+ bytesToString: function(bytes) {
618
+ // based on http://ciaranj.blogspot.fr/2007/11/utf8-characters-encoding-in-javascript.html
619
+
620
+ var result = "";
621
+ var i, c, c1, c2, c3;
622
+ i = c = c1 = c2 = c3 = 0;
623
+
624
+ // Perform byte-order check.
625
+ if( bytes.length >= 3 ) {
626
+ if( (bytes[0] & 0xef) == 0xef && (bytes[1] & 0xbb) == 0xbb && (bytes[2] & 0xbf) == 0xbf ) {
627
+ // stream has a BOM at the start, skip over
628
+ i = 3;
629
+ }
630
+ }
631
+
632
+ while ( i < bytes.length ) {
633
+ c = bytes[i] & 0xff;
634
+
635
+ if ( c < 128 ) {
636
+
637
+ result += String.fromCharCode(c);
638
+ i++;
639
+
640
+ } else if ( (c > 191) && (c < 224) ) {
641
+
642
+ if ( i + 1 >= bytes.length ) {
643
+ throw "Un-expected encoding error, UTF-8 stream truncated, or incorrect";
644
+ }
645
+ c2 = bytes[i + 1] & 0xff;
646
+ result += String.fromCharCode( ((c & 31) << 6) | (c2 & 63) );
647
+ i += 2;
648
+
649
+ } else {
650
+
651
+ if ( i + 2 >= bytes.length || i + 1 >= bytes.length ) {
652
+ throw "Un-expected encoding error, UTF-8 stream truncated, or incorrect";
653
+ }
654
+ c2 = bytes[i + 1] & 0xff;
655
+ c3 = bytes[i + 2] & 0xff;
656
+ result += String.fromCharCode( ((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63) );
657
+ i += 3;
658
+
659
+ }
660
+ }
661
+ return result;
662
+ },
663
+
664
+ stringToBytes: function(string) {
665
+ // based on http://ciaranj.blogspot.fr/2007/11/utf8-characters-encoding-in-javascript.html
666
+
667
+ var bytes = [];
668
+
669
+ for (var n = 0; n < string.length; n++) {
670
+
671
+ var c = string.charCodeAt(n);
672
+
673
+ if (c < 128) {
674
+
675
+ bytes[bytes.length]= c;
676
+
677
+ } else if((c > 127) && (c < 2048)) {
678
+
679
+ bytes[bytes.length] = (c >> 6) | 192;
680
+ bytes[bytes.length] = (c & 63) | 128;
681
+
682
+ } else {
683
+
684
+ bytes[bytes.length] = (c >> 12) | 224;
685
+ bytes[bytes.length] = ((c >> 6) & 63) | 128;
686
+ bytes[bytes.length] = (c & 63) | 128;
687
+
688
+ }
689
+
690
+ }
691
+
692
+ return bytes;
693
+ },
694
+
695
+ bytesToHexString: function (bytes) {
696
+ var dec, hexstring, bytesAsHexString = "";
697
+ for (var i = 0; i < bytes.length; i++) {
698
+ if (bytes[i] >= 0) {
699
+ dec = bytes[i];
700
+ } else {
701
+ dec = 256 + bytes[i];
702
+ }
703
+ hexstring = dec.toString(16);
704
+ // zero padding
705
+ if (hexstring.length === 1) {
706
+ hexstring = "0" + hexstring;
707
+ }
708
+ bytesAsHexString += hexstring;
709
+ }
710
+ return bytesAsHexString;
711
+ },
712
+
713
+ // This function can be removed if record.type is changed to a String
714
+ /**
715
+ * Returns true if the record's TNF and type matches the supplied TNF and type.
716
+ *
717
+ * @record NDEF record
718
+ * @tnf 3-bit TNF (Type Name Format) - use one of the TNF_* constants
719
+ * @type byte array or String
720
+ */
721
+ isType: function(record, tnf, type) {
722
+ if (record.tnf === tnf) { // TNF is 3-bit
723
+ var recordType;
724
+ if (typeof(type) === 'string') {
725
+ recordType = type;
726
+ } else {
727
+ recordType = nfc.bytesToString(type);
728
+ }
729
+ return (nfc.bytesToString(record.type) === recordType);
730
+ }
731
+ return false;
732
+ },
733
+
734
+ /**
735
+ * Convert an ArrayBuffer to a hex string
736
+ *
737
+ * @param {ArrayBuffer} buffer
738
+ * @returns {srting} - hex representation of bytes e.g. 000407AF
739
+ */
740
+ arrayBufferToHexString: function(buffer) {
741
+ function toHexString(byte) {
742
+ return ('0' + (byte & 0xFF).toString(16)).slice(-2);
743
+ }
744
+ var typedArray = new Uint8Array(buffer);
745
+ var array = Array.from(typedArray); // need to convert to [] so our map result is not typed
746
+ var parts = array.map(function(i) { return toHexString(i) });
747
+
748
+ return parts.join('');
749
+ },
750
+
751
+ /**
752
+ * Convert a hex string to an ArrayBuffer.
753
+ *
754
+ * @param {string} hexString - hex representation of bytes
755
+ * @return {ArrayBuffer} - The bytes in an ArrayBuffer.
756
+ */
757
+ hexStringToArrayBuffer: function(hexString) {
758
+
759
+ // remove any delimiters - space, dash, or colon
760
+ hexString = hexString.replace(/[\s-:]/g, '');
761
+
762
+ // remove the leading 0x
763
+ hexString = hexString.replace(/^0x/, '');
764
+
765
+ // ensure even number of characters
766
+ if (hexString.length % 2 != 0) {
767
+ console.log('WARNING: expecting an even number of characters in the hexString');
768
+ }
769
+
770
+ // check for some non-hex characters
771
+ var bad = hexString.match(/[G-Z\s]/i);
772
+ if (bad) {
773
+ console.log('WARNING: found non-hex characters', bad);
774
+ }
775
+
776
+ // split the string into pairs of octets
777
+ var pairs = hexString.match(/[\dA-F]{2}/gi);
778
+
779
+ // convert the octets to integers
780
+ var ints = pairs.map(function(s) { return parseInt(s, 16) });
781
+
782
+ var array = new Uint8Array(ints);
783
+ return array.buffer;
784
+ }
785
+
786
+ };
787
+
788
+ // this is a module in ndef-js
789
+ var textHelper = {
790
+
791
+ decodePayload: function (data) {
792
+
793
+ var languageCodeLength = (data[0] & 0x3F), // 6 LSBs
794
+ languageCode = data.slice(1, 1 + languageCodeLength),
795
+ utf16 = (data[0] & 0x80) !== 0; // assuming UTF-16BE
796
+
797
+ // TODO need to deal with UTF in the future
798
+ if (utf16) {
799
+ console.log('WARNING: utf-16 data may not be handled properly for', languageCode);
800
+ }
801
+ // Use TextDecoder when we have enough browser support
802
+ // new TextDecoder('utf-8').decode(data.slice(languageCodeLength + 1));
803
+ // new TextDecoder('utf-16').decode(data.slice(languageCodeLength + 1));
804
+
805
+ return util.bytesToString(data.slice(languageCodeLength + 1));
806
+ },
807
+
808
+ // encode text payload
809
+ // @returns an array of bytes
810
+ encodePayload: function(text, lang, encoding) {
811
+
812
+ // ISO/IANA language code, but we're not enforcing
813
+ if (!lang) { lang = 'en'; }
814
+
815
+ var encoded = util.stringToBytes(lang + text);
816
+ encoded.unshift(lang.length);
817
+
818
+ return encoded;
819
+ }
820
+
821
+ };
822
+
823
+ // this is a module in ndef-js
824
+ var uriHelper = {
825
+ // URI identifier codes from URI Record Type Definition NFCForum-TS-RTD_URI_1.0 2006-07-24
826
+ // index in array matches code in the spec
827
+ protocols: [ "", "http://www.", "https://www.", "http://", "https://", "tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.", "ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://", "news:", "telnet://", "imap:", "rtsp://", "urn:", "pop:", "sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://", "tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:" ],
828
+
829
+ // decode a URI payload bytes
830
+ // @returns a string
831
+ decodePayload: function (data) {
832
+ var prefix = uriHelper.protocols[data[0]];
833
+ if (!prefix) { // 36 to 255 should be ""
834
+ prefix = "";
835
+ }
836
+ return prefix + util.bytesToString(data.slice(1));
837
+ },
838
+
839
+ // shorten a URI with standard prefix
840
+ // @returns an array of bytes
841
+ encodePayload: function (uri) {
842
+
843
+ var prefix,
844
+ protocolCode,
845
+ encoded;
846
+
847
+ // check each protocol, unless we've found a match
848
+ // "urn:" is the one exception where we need to keep checking
849
+ // slice so we don't check ""
850
+ uriHelper.protocols.slice(1).forEach(function(protocol) {
851
+ if ((!prefix || prefix === "urn:") && uri.indexOf(protocol) === 0) {
852
+ prefix = protocol;
853
+ }
854
+ });
855
+
856
+ if (!prefix) {
857
+ prefix = "";
858
+ }
859
+
860
+ encoded = util.stringToBytes(uri.slice(prefix.length));
861
+ protocolCode = uriHelper.protocols.indexOf(prefix);
862
+ // prepend protocol code
863
+ encoded.unshift(protocolCode);
864
+
865
+ return encoded;
866
+ }
867
+ };
868
+
869
+ // added since WP8 must call a named function, also used by iOS
870
+ // TODO consider switching NFC events from JS events to using the PG callbacks
871
+ function fireNfcTagEvent(eventType, tagAsJson) {
872
+ setTimeout(function () {
873
+ var e = document.createEvent('Events');
874
+ e.initEvent(eventType, true, false);
875
+ e.tag = JSON.parse(tagAsJson);
876
+ console.log(e.tag);
877
+ document.dispatchEvent(e);
878
+ }, 10);
879
+ }
880
+
881
+ // textHelper and uriHelper aren't exported, add a property
882
+ ndef.uriHelper = uriHelper;
883
+ ndef.textHelper = textHelper;
884
+
885
+ // create aliases
886
+ nfc.bytesToString = util.bytesToString;
887
+ nfc.stringToBytes = util.stringToBytes;
888
+ nfc.bytesToHexString = util.bytesToHexString;
889
+
890
+ // kludge some global variables for plugman js-module support
891
+ // eventually these should be replaced and referenced via the module
892
+ window.nfc = nfc;
893
+ window.ndef = ndef;
894
+ window.util = util;
895
+ window.fireNfcTagEvent = fireNfcTagEvent;
896
+
897
+ // This channel receives nfcEvent data from native code
898
+ // and fires JavaScript events.
899
+ require('cordova/channel').onCordovaReady.subscribe(function() {
900
+ require('cordova/exec')(success, null, 'NfcPlugin', 'channel', []);
901
+ function success(message) {
902
+ if (!message.type) {
903
+ console.log(message);
904
+ } else {
905
+ console.log("Received NFC data, firing '" + message.type + "' event");
906
+ var e = document.createEvent('Events');
907
+ e.initEvent(message.type);
908
+ e.tag = message.tag;
909
+ document.dispatchEvent(e);
910
+ }
911
+ }
912
+ });