151 |
152 |
153 |
154 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav
2 | and *.ogg) are licensed under the CC-BY-NC license. All other files are
3 | licensed under the Apache 2 license.
4 |
5 | =======================================================================
6 |
7 |
8 | Apache License
9 | --------------
10 |
11 | Apache License
12 | Version 2.0, January 2004
13 | http://www.apache.org/licenses/
14 |
15 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
16 |
17 | 1. Definitions.
18 |
19 | "License" shall mean the terms and conditions for use, reproduction,
20 | and distribution as defined by Sections 1 through 9 of this document.
21 |
22 | "Licensor" shall mean the copyright owner or entity authorized by
23 | the copyright owner that is granting the License.
24 |
25 | "Legal Entity" shall mean the union of the acting entity and all
26 | other entities that control, are controlled by, or are under common
27 | control with that entity. For the purposes of this definition,
28 | "control" means (i) the power, direct or indirect, to cause the
29 | direction or management of such entity, whether by contract or
30 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
31 | outstanding shares, or (iii) beneficial ownership of such entity.
32 |
33 | "You" (or "Your") shall mean an individual or Legal Entity
34 | exercising permissions granted by this License.
35 |
36 | "Source" form shall mean the preferred form for making modifications,
37 | including but not limited to software source code, documentation
38 | source, and configuration files.
39 |
40 | "Object" form shall mean any form resulting from mechanical
41 | transformation or translation of a Source form, including but
42 | not limited to compiled object code, generated documentation,
43 | and conversions to other media types.
44 |
45 | "Work" shall mean the work of authorship, whether in Source or
46 | Object form, made available under the License, as indicated by a
47 | copyright notice that is included in or attached to the work
48 | (an example is provided in the Appendix below).
49 |
50 | "Derivative Works" shall mean any work, whether in Source or Object
51 | form, that is based on (or derived from) the Work and for which the
52 | editorial revisions, annotations, elaborations, or other modifications
53 | represent, as a whole, an original work of authorship. For the purposes
54 | of this License, Derivative Works shall not include works that remain
55 | separable from, or merely link (or bind by name) to the interfaces of,
56 | the Work and Derivative Works thereof.
57 |
58 | "Contribution" shall mean any work of authorship, including
59 | the original version of the Work and any modifications or additions
60 | to that Work or Derivative Works thereof, that is intentionally
61 | submitted to Licensor for inclusion in the Work by the copyright owner
62 | or by an individual or Legal Entity authorized to submit on behalf of
63 | the copyright owner. For the purposes of this definition, "submitted"
64 | means any form of electronic, verbal, or written communication sent
65 | to the Licensor or its representatives, including but not limited to
66 | communication on electronic mailing lists, source code control systems,
67 | and issue tracking systems that are managed by, or on behalf of, the
68 | Licensor for the purpose of discussing and improving the Work, but
69 | excluding communication that is conspicuously marked or otherwise
70 | designated in writing by the copyright owner as "Not a Contribution."
71 |
72 | "Contributor" shall mean Licensor and any individual or Legal Entity
73 | on behalf of whom a Contribution has been received by Licensor and
74 | subsequently incorporated within the Work.
75 |
76 | 2. Grant of Copyright License. Subject to the terms and conditions of
77 | this License, each Contributor hereby grants to You a perpetual,
78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
79 | copyright license to reproduce, prepare Derivative Works of,
80 | publicly display, publicly perform, sublicense, and distribute the
81 | Work and such Derivative Works in Source or Object form.
82 |
83 | 3. Grant of Patent License. Subject to the terms and conditions of
84 | this License, each Contributor hereby grants to You a perpetual,
85 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
86 | (except as stated in this section) patent license to make, have made,
87 | use, offer to sell, sell, import, and otherwise transfer the Work,
88 | where such license applies only to those patent claims licensable
89 | by such Contributor that are necessarily infringed by their
90 | Contribution(s) alone or by combination of their Contribution(s)
91 | with the Work to which such Contribution(s) was submitted. If You
92 | institute patent litigation against any entity (including a
93 | cross-claim or counterclaim in a lawsuit) alleging that the Work
94 | or a Contribution incorporated within the Work constitutes direct
95 | or contributory patent infringement, then any patent licenses
96 | granted to You under this License for that Work shall terminate
97 | as of the date such litigation is filed.
98 |
99 | 4. Redistribution. You may reproduce and distribute copies of the
100 | Work or Derivative Works thereof in any medium, with or without
101 | modifications, and in Source or Object form, provided that You
102 | meet the following conditions:
103 |
104 | (a) You must give any other recipients of the Work or
105 | Derivative Works a copy of this License; and
106 |
107 | (b) You must cause any modified files to carry prominent notices
108 | stating that You changed the files; and
109 |
110 | (c) You must retain, in the Source form of any Derivative Works
111 | that You distribute, all copyright, patent, trademark, and
112 | attribution notices from the Source form of the Work,
113 | excluding those notices that do not pertain to any part of
114 | the Derivative Works; and
115 |
116 | (d) If the Work includes a "NOTICE" text file as part of its
117 | distribution, then any Derivative Works that You distribute must
118 | include a readable copy of the attribution notices contained
119 | within such NOTICE file, excluding those notices that do not
120 | pertain to any part of the Derivative Works, in at least one
121 | of the following places: within a NOTICE text file distributed
122 | as part of the Derivative Works; within the Source form or
123 | documentation, if provided along with the Derivative Works; or,
124 | within a display generated by the Derivative Works, if and
125 | wherever such third-party notices normally appear. The contents
126 | of the NOTICE file are for informational purposes only and
127 | do not modify the License. You may add Your own attribution
128 | notices within Derivative Works that You distribute, alongside
129 | or as an addendum to the NOTICE text from the Work, provided
130 | that such additional attribution notices cannot be construed
131 | as modifying the License.
132 |
133 | You may add Your own copyright statement to Your modifications and
134 | may provide additional or different license terms and conditions
135 | for use, reproduction, or distribution of Your modifications, or
136 | for any such Derivative Works as a whole, provided Your use,
137 | reproduction, and distribution of the Work otherwise complies with
138 | the conditions stated in this License.
139 |
140 | 5. Submission of Contributions. Unless You explicitly state otherwise,
141 | any Contribution intentionally submitted for inclusion in the Work
142 | by You to the Licensor shall be under the terms and conditions of
143 | this License, without any additional terms or conditions.
144 | Notwithstanding the above, nothing herein shall supersede or modify
145 | the terms of any separate license agreement you may have executed
146 | with Licensor regarding such Contributions.
147 |
148 | 6. Trademarks. This License does not grant permission to use the trade
149 | names, trademarks, service marks, or product names of the Licensor,
150 | except as required for reasonable and customary use in describing the
151 | origin of the Work and reproducing the content of the NOTICE file.
152 |
153 | 7. Disclaimer of Warranty. Unless required by applicable law or
154 | agreed to in writing, Licensor provides the Work (and each
155 | Contributor provides its Contributions) on an "AS IS" BASIS,
156 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
157 | implied, including, without limitation, any warranties or conditions
158 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
159 | PARTICULAR PURPOSE. You are solely responsible for determining the
160 | appropriateness of using or redistributing the Work and assume any
161 | risks associated with Your exercise of permissions under this License.
162 |
163 | 8. Limitation of Liability. In no event and under no legal theory,
164 | whether in tort (including negligence), contract, or otherwise,
165 | unless required by applicable law (such as deliberate and grossly
166 | negligent acts) or agreed to in writing, shall any Contributor be
167 | liable to You for damages, including any direct, indirect, special,
168 | incidental, or consequential damages of any character arising as a
169 | result of this License or out of the use or inability to use the
170 | Work (including but not limited to damages for loss of goodwill,
171 | work stoppage, computer failure or malfunction, or any and all
172 | other commercial damages or losses), even if such Contributor
173 | has been advised of the possibility of such damages.
174 |
175 | 9. Accepting Warranty or Additional Liability. While redistributing
176 | the Work or Derivative Works thereof, You may choose to offer,
177 | and charge a fee for, acceptance of support, warranty, indemnity,
178 | or other liability obligations and/or rights consistent with this
179 | License. However, in accepting such obligations, You may act only
180 | on Your own behalf and on Your sole responsibility, not on behalf
181 | of any other Contributor, and only if You agree to indemnify,
182 | defend, and hold each Contributor harmless for any liability
183 | incurred by, or claims asserted against, such Contributor by reason
184 | of your accepting any such warranty or additional liability.
185 |
186 | END OF TERMS AND CONDITIONS
187 |
188 | All image and audio files (including *.png, *.jpg, *.svg, *.mp3, *.wav
189 | and *.ogg) are licensed under the CC-BY-NC license. All other files are
190 | licensed under the Apache 2 license.
191 |
192 | CC-BY-NC License
193 | ----------------
194 |
195 | Attribution-NonCommercial-ShareAlike 4.0 International
196 |
197 | =======================================================================
198 |
199 | Creative Commons Corporation ("Creative Commons") is not a law firm and
200 | does not provide legal services or legal advice. Distribution of
201 | Creative Commons public licenses does not create a lawyer-client or
202 | other relationship. Creative Commons makes its licenses and related
203 | information available on an "as-is" basis. Creative Commons gives no
204 | warranties regarding its licenses, any material licensed under their
205 | terms and conditions, or any related information. Creative Commons
206 | disclaims all liability for damages resulting from their use to the
207 | fullest extent possible.
208 |
209 | Using Creative Commons Public Licenses
210 |
211 | Creative Commons public licenses provide a standard set of terms and
212 | conditions that creators and other rights holders may use to share
213 | original works of authorship and other material subject to copyright
214 | and certain other rights specified in the public license below. The
215 | following considerations are for informational purposes only, are not
216 | exhaustive, and do not form part of our licenses.
217 |
218 | Considerations for licensors: Our public licenses are
219 | intended for use by those authorized to give the public
220 | permission to use material in ways otherwise restricted by
221 | copyright and certain other rights. Our licenses are
222 | irrevocable. Licensors should read and understand the terms
223 | and conditions of the license they choose before applying it.
224 | Licensors should also secure all rights necessary before
225 | applying our licenses so that the public can reuse the
226 | material as expected. Licensors should clearly mark any
227 | material not subject to the license. This includes other CC-
228 | licensed material, or material used under an exception or
229 | limitation to copyright. More considerations for licensors:
230 | wiki.creativecommons.org/Considerations_for_licensors
231 |
232 | Considerations for the public: By using one of our public
233 | licenses, a licensor grants the public permission to use the
234 | licensed material under specified terms and conditions. If
235 | the licensor's permission is not necessary for any reason--for
236 | example, because of any applicable exception or limitation to
237 | copyright--then that use is not regulated by the license. Our
238 | licenses grant only permissions under copyright and certain
239 | other rights that a licensor has authority to grant. Use of
240 | the licensed material may still be restricted for other
241 | reasons, including because others have copyright or other
242 | rights in the material. A licensor may make special requests,
243 | such as asking that all changes be marked or described.
244 | Although not required by our licenses, you are encouraged to
245 | respect those requests where reasonable. More_considerations
246 | for the public:
247 | wiki.creativecommons.org/Considerations_for_licensees
248 |
249 | =======================================================================
250 |
251 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
252 | Public License
253 |
254 | By exercising the Licensed Rights (defined below), You accept and agree
255 | to be bound by the terms and conditions of this Creative Commons
256 | Attribution-NonCommercial-ShareAlike 4.0 International Public License
257 | ("Public License"). To the extent this Public License may be
258 | interpreted as a contract, You are granted the Licensed Rights in
259 | consideration of Your acceptance of these terms and conditions, and the
260 | Licensor grants You such rights in consideration of benefits the
261 | Licensor receives from making the Licensed Material available under
262 | these terms and conditions.
263 |
264 |
265 | Section 1 -- Definitions.
266 |
267 | a. Adapted Material means material subject to Copyright and Similar
268 | Rights that is derived from or based upon the Licensed Material
269 | and in which the Licensed Material is translated, altered,
270 | arranged, transformed, or otherwise modified in a manner requiring
271 | permission under the Copyright and Similar Rights held by the
272 | Licensor. For purposes of this Public License, where the Licensed
273 | Material is a musical work, performance, or sound recording,
274 | Adapted Material is always produced where the Licensed Material is
275 | synched in timed relation with a moving image.
276 |
277 | b. Adapter's License means the license You apply to Your Copyright
278 | and Similar Rights in Your contributions to Adapted Material in
279 | accordance with the terms and conditions of this Public License.
280 |
281 | c. BY-NC-SA Compatible License means a license listed at
282 | creativecommons.org/compatiblelicenses, approved by Creative
283 | Commons as essentially the equivalent of this Public License.
284 |
285 | d. Copyright and Similar Rights means copyright and/or similar rights
286 | closely related to copyright including, without limitation,
287 | performance, broadcast, sound recording, and Sui Generis Database
288 | Rights, without regard to how the rights are labeled or
289 | categorized. For purposes of this Public License, the rights
290 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
291 | Rights.
292 |
293 | e. Effective Technological Measures means those measures that, in the
294 | absence of proper authority, may not be circumvented under laws
295 | fulfilling obligations under Article 11 of the WIPO Copyright
296 | Treaty adopted on December 20, 1996, and/or similar international
297 | agreements.
298 |
299 | f. Exceptions and Limitations means fair use, fair dealing, and/or
300 | any other exception or limitation to Copyright and Similar Rights
301 | that applies to Your use of the Licensed Material.
302 |
303 | g. License Elements means the license attributes listed in the name
304 | of a Creative Commons Public License. The License Elements of this
305 | Public License are Attribution, NonCommercial, and ShareAlike.
306 |
307 | h. Licensed Material means the artistic or literary work, database,
308 | or other material to which the Licensor applied this Public
309 | License.
310 |
311 | i. Licensed Rights means the rights granted to You subject to the
312 | terms and conditions of this Public License, which are limited to
313 | all Copyright and Similar Rights that apply to Your use of the
314 | Licensed Material and that the Licensor has authority to license.
315 |
316 | j. Licensor means the individual(s) or entity(ies) granting rights
317 | under this Public License.
318 |
319 | k. NonCommercial means not primarily intended for or directed towards
320 | commercial advantage or monetary compensation. For purposes of
321 | this Public License, the exchange of the Licensed Material for
322 | other material subject to Copyright and Similar Rights by digital
323 | file-sharing or similar means is NonCommercial provided there is
324 | no payment of monetary compensation in connection with the
325 | exchange.
326 |
327 | l. Share means to provide material to the public by any means or
328 | process that requires permission under the Licensed Rights, such
329 | as reproduction, public display, public performance, distribution,
330 | dissemination, communication, or importation, and to make material
331 | available to the public including in ways that members of the
332 | public may access the material from a place and at a time
333 | individually chosen by them.
334 |
335 | m. Sui Generis Database Rights means rights other than copyright
336 | resulting from Directive 96/9/EC of the European Parliament and of
337 | the Council of 11 March 1996 on the legal protection of databases,
338 | as amended and/or succeeded, as well as other essentially
339 | equivalent rights anywhere in the world.
340 |
341 | n. You means the individual or entity exercising the Licensed Rights
342 | under this Public License. Your has a corresponding meaning.
343 |
344 |
345 | Section 2 -- Scope.
346 |
347 | a. License grant.
348 |
349 | 1. Subject to the terms and conditions of this Public License,
350 | the Licensor hereby grants You a worldwide, royalty-free,
351 | non-sublicensable, non-exclusive, irrevocable license to
352 | exercise the Licensed Rights in the Licensed Material to:
353 |
354 | a. reproduce and Share the Licensed Material, in whole or
355 | in part, for NonCommercial purposes only; and
356 |
357 | b. produce, reproduce, and Share Adapted Material for
358 | NonCommercial purposes only.
359 |
360 | 2. Exceptions and Limitations. For the avoidance of doubt, where
361 | Exceptions and Limitations apply to Your use, this Public
362 | License does not apply, and You do not need to comply with
363 | its terms and conditions.
364 |
365 | 3. Term. The term of this Public License is specified in Section
366 | 6(a).
367 |
368 | 4. Media and formats; technical modifications allowed. The
369 | Licensor authorizes You to exercise the Licensed Rights in
370 | all media and formats whether now known or hereafter created,
371 | and to make technical modifications necessary to do so. The
372 | Licensor waives and/or agrees not to assert any right or
373 | authority to forbid You from making technical modifications
374 | necessary to exercise the Licensed Rights, including
375 | technical modifications necessary to circumvent Effective
376 | Technological Measures. For purposes of this Public License,
377 | simply making modifications authorized by this Section 2(a)
378 | (4) never produces Adapted Material.
379 |
380 | 5. Downstream recipients.
381 |
382 | a. Offer from the Licensor -- Licensed Material. Every
383 | recipient of the Licensed Material automatically
384 | receives an offer from the Licensor to exercise the
385 | Licensed Rights under the terms and conditions of this
386 | Public License.
387 |
388 | b. Additional offer from the Licensor -- Adapted Material.
389 | Every recipient of Adapted Material from You
390 | automatically receives an offer from the Licensor to
391 | exercise the Licensed Rights in the Adapted Material
392 | under the conditions of the Adapter's License You apply.
393 |
394 | c. No downstream restrictions. You may not offer or impose
395 | any additional or different terms or conditions on, or
396 | apply any Effective Technological Measures to, the
397 | Licensed Material if doing so restricts exercise of the
398 | Licensed Rights by any recipient of the Licensed
399 | Material.
400 |
401 | 6. No endorsement. Nothing in this Public License constitutes or
402 | may be construed as permission to assert or imply that You
403 | are, or that Your use of the Licensed Material is, connected
404 | with, or sponsored, endorsed, or granted official status by,
405 | the Licensor or others designated to receive attribution as
406 | provided in Section 3(a)(1)(A)(i).
407 |
408 | b. Other rights.
409 |
410 | 1. Moral rights, such as the right of integrity, are not
411 | licensed under this Public License, nor are publicity,
412 | privacy, and/or other similar personality rights; however, to
413 | the extent possible, the Licensor waives and/or agrees not to
414 | assert any such rights held by the Licensor to the limited
415 | extent necessary to allow You to exercise the Licensed
416 | Rights, but not otherwise.
417 |
418 | 2. Patent and trademark rights are not licensed under this
419 | Public License.
420 |
421 | 3. To the extent possible, the Licensor waives any right to
422 | collect royalties from You for the exercise of the Licensed
423 | Rights, whether directly or through a collecting society
424 | under any voluntary or waivable statutory or compulsory
425 | licensing scheme. In all other cases the Licensor expressly
426 | reserves any right to collect such royalties, including when
427 | the Licensed Material is used other than for NonCommercial
428 | purposes.
429 |
430 |
431 | Section 3 -- License Conditions.
432 |
433 | Your exercise of the Licensed Rights is expressly made subject to the
434 | following conditions.
435 |
436 | a. Attribution.
437 |
438 | 1. If You Share the Licensed Material (including in modified
439 | form), You must:
440 |
441 | a. retain the following if it is supplied by the Licensor
442 | with the Licensed Material:
443 |
444 | i. identification of the creator(s) of the Licensed
445 | Material and any others designated to receive
446 | attribution, in any reasonable manner requested by
447 | the Licensor (including by pseudonym if
448 | designated);
449 |
450 | ii. a copyright notice;
451 |
452 | iii. a notice that refers to this Public License;
453 |
454 | iv. a notice that refers to the disclaimer of
455 | warranties;
456 |
457 | v. a URI or hyperlink to the Licensed Material to the
458 | extent reasonably practicable;
459 |
460 | b. indicate if You modified the Licensed Material and
461 | retain an indication of any previous modifications; and
462 |
463 | c. indicate the Licensed Material is licensed under this
464 | Public License, and include the text of, or the URI or
465 | hyperlink to, this Public License.
466 |
467 | 2. You may satisfy the conditions in Section 3(a)(1) in any
468 | reasonable manner based on the medium, means, and context in
469 | which You Share the Licensed Material. For example, it may be
470 | reasonable to satisfy the conditions by providing a URI or
471 | hyperlink to a resource that includes the required
472 | information.
473 | 3. If requested by the Licensor, You must remove any of the
474 | information required by Section 3(a)(1)(A) to the extent
475 | reasonably practicable.
476 |
477 | b. ShareAlike.
478 |
479 | In addition to the conditions in Section 3(a), if You Share
480 | Adapted Material You produce, the following conditions also apply.
481 |
482 | 1. The Adapter's License You apply must be a Creative Commons
483 | license with the same License Elements, this version or
484 | later, or a BY-NC-SA Compatible License.
485 |
486 | 2. You must include the text of, or the URI or hyperlink to, the
487 | Adapter's License You apply. You may satisfy this condition
488 | in any reasonable manner based on the medium, means, and
489 | context in which You Share Adapted Material.
490 |
491 | 3. You may not offer or impose any additional or different terms
492 | or conditions on, or apply any Effective Technological
493 | Measures to, Adapted Material that restrict exercise of the
494 | rights granted under the Adapter's License You apply.
495 |
496 |
497 | Section 4 -- Sui Generis Database Rights.
498 |
499 | Where the Licensed Rights include Sui Generis Database Rights that
500 | apply to Your use of the Licensed Material:
501 |
502 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
503 | to extract, reuse, reproduce, and Share all or a substantial
504 | portion of the contents of the database for NonCommercial purposes
505 | only;
506 |
507 | b. if You include all or a substantial portion of the database
508 | contents in a database in which You have Sui Generis Database
509 | Rights, then the database in which You have Sui Generis Database
510 | Rights (but not its individual contents) is Adapted Material,
511 | including for purposes of Section 3(b); and
512 |
513 | c. You must comply with the conditions in Section 3(a) if You Share
514 | all or a substantial portion of the contents of the database.
515 |
516 | For the avoidance of doubt, this Section 4 supplements and does not
517 | replace Your obligations under this Public License where the Licensed
518 | Rights include other Copyright and Similar Rights.
519 |
520 |
521 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
522 |
523 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
524 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
525 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
526 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
527 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
528 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
529 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
530 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
531 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
532 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
533 |
534 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
535 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
536 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
537 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
538 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
539 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
540 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
541 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
542 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
543 |
544 | c. The disclaimer of warranties and limitation of liability provided
545 | above shall be interpreted in a manner that, to the extent
546 | possible, most closely approximates an absolute disclaimer and
547 | waiver of all liability.
548 |
549 |
550 | Section 6 -- Term and Termination.
551 |
552 | a. This Public License applies for the term of the Copyright and
553 | Similar Rights licensed here. However, if You fail to comply with
554 | this Public License, then Your rights under this Public License
555 | terminate automatically.
556 |
557 | b. Where Your right to use the Licensed Material has terminated under
558 | Section 6(a), it reinstates:
559 |
560 | 1. automatically as of the date the violation is cured, provided
561 | it is cured within 30 days of Your discovery of the
562 | violation; or
563 |
564 | 2. upon express reinstatement by the Licensor.
565 |
566 | For the avoidance of doubt, this Section 6(b) does not affect any
567 | right the Licensor may have to seek remedies for Your violations
568 | of this Public License.
569 |
570 | c. For the avoidance of doubt, the Licensor may also offer the
571 | Licensed Material under separate terms or conditions or stop
572 | distributing the Licensed Material at any time; however, doing so
573 | will not terminate this Public License.
574 |
575 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
576 | License.
577 |
578 |
579 | Section 7 -- Other Terms and Conditions.
580 |
581 | a. The Licensor shall not be bound by any additional or different
582 | terms or conditions communicated by You unless expressly agreed.
583 |
584 | b. Any arrangements, understandings, or agreements regarding the
585 | Licensed Material not stated herein are separate from and
586 | independent of the terms and conditions of this Public License.
587 |
588 |
589 | Section 8 -- Interpretation.
590 |
591 | a. For the avoidance of doubt, this Public License does not, and
592 | shall not be interpreted to, reduce, limit, restrict, or impose
593 | conditions on any use of the Licensed Material that could lawfully
594 | be made without permission under this Public License.
595 |
596 | b. To the extent possible, if any provision of this Public License is
597 | deemed unenforceable, it shall be automatically reformed to the
598 | minimum extent necessary to make it enforceable. If the provision
599 | cannot be reformed, it shall be severed from this Public License
600 | without affecting the enforceability of the remaining terms and
601 | conditions.
602 |
603 | c. No term or condition of this Public License will be waived and no
604 | failure to comply consented to unless expressly agreed to by the
605 | Licensor.
606 |
607 | d. Nothing in this Public License constitutes or may be interpreted
608 | as a limitation upon, or waiver of, any privileges and immunities
609 | that apply to the Licensor or You, including from the legal
610 | processes of any jurisdiction or authority.
611 |
612 | =======================================================================
613 |
614 | Creative Commons is not a party to its public licenses.
615 | Notwithstanding, Creative Commons may elect to apply one of its public
616 | licenses to material it publishes and in those instances will be
617 | considered the "Licensor." Except for the limited purpose of indicating
618 | that material is shared under a Creative Commons public license or as
619 | otherwise permitted by the Creative Commons policies published at
620 | creativecommons.org/policies, Creative Commons does not authorize the
621 | use of the trademark "Creative Commons" or any other trademark or logo
622 | of Creative Commons without its prior written consent including,
623 | without limitation, in connection with any unauthorized modifications
624 | to any of its public licenses or any other arrangements,
625 | understandings, or agreements concerning use of licensed material. For
626 | the avoidance of doubt, this paragraph does not form part of the public
627 | licenses.
628 |
629 | Creative Commons may be contacted at creativecommons.org.
630 |
631 |
--------------------------------------------------------------------------------
/scripts/peer.js:
--------------------------------------------------------------------------------
1 | /*! peerjs build:0.3.13, development. Copyright(c) 2013 Michelle Bu */(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o util.chunkedMTU) {
186 | this._sendChunks(blob);
187 | return;
188 | }
189 |
190 | // DataChannel currently only supports strings.
191 | if (!util.supports.sctp) {
192 | util.blobToBinaryString(blob, function(str) {
193 | self._bufferedSend(str);
194 | });
195 | } else if (!util.supports.binaryBlob) {
196 | // We only do this if we really need to (e.g. blobs are not supported),
197 | // because this conversion is costly.
198 | util.blobToArrayBuffer(blob, function(ab) {
199 | self._bufferedSend(ab);
200 | });
201 | } else {
202 | this._bufferedSend(blob);
203 | }
204 | } else {
205 | this._bufferedSend(data);
206 | }
207 | }
208 |
209 | DataConnection.prototype._bufferedSend = function(msg) {
210 | if (this._buffering || !this._trySend(msg)) {
211 | this._buffer.push(msg);
212 | this.bufferSize = this._buffer.length;
213 | }
214 | }
215 |
216 | // Returns true if the send succeeds.
217 | DataConnection.prototype._trySend = function(msg) {
218 | try {
219 | this._dc.send(msg);
220 | } catch (e) {
221 | this._buffering = true;
222 |
223 | var self = this;
224 | setTimeout(function() {
225 | // Try again.
226 | self._buffering = false;
227 | self._tryBuffer();
228 | }, 100);
229 | return false;
230 | }
231 | return true;
232 | }
233 |
234 | // Try to send the first message in the buffer.
235 | DataConnection.prototype._tryBuffer = function() {
236 | if (this._buffer.length === 0) {
237 | return;
238 | }
239 |
240 | var msg = this._buffer[0];
241 |
242 | if (this._trySend(msg)) {
243 | this._buffer.shift();
244 | this.bufferSize = this._buffer.length;
245 | this._tryBuffer();
246 | }
247 | }
248 |
249 | DataConnection.prototype._sendChunks = function(blob) {
250 | var blobs = util.chunk(blob);
251 | for (var i = 0, ii = blobs.length; i < ii; i += 1) {
252 | var blob = blobs[i];
253 | this.send(blob, true);
254 | }
255 | }
256 |
257 | DataConnection.prototype.handleMessage = function(message) {
258 | var payload = message.payload;
259 |
260 | switch (message.type) {
261 | case 'ANSWER':
262 | this._peerBrowser = payload.browser;
263 |
264 | // Forward to negotiator
265 | Negotiator.handleSDP(message.type, this, payload.sdp);
266 | break;
267 | case 'CANDIDATE':
268 | Negotiator.handleCandidate(this, payload.candidate);
269 | break;
270 | default:
271 | util.warn('Unrecognized message type:', message.type, 'from peer:', this.peer);
272 | break;
273 | }
274 | }
275 |
276 | module.exports = DataConnection;
277 |
278 | },{"./negotiator":5,"./util":8,"eventemitter3":9,"reliable":12}],3:[function(require,module,exports){
279 | window.Socket = require('./socket');
280 | window.MediaConnection = require('./mediaconnection');
281 | window.DataConnection = require('./dataconnection');
282 | window.Peer = require('./peer');
283 | window.RTCPeerConnection = require('./adapter').RTCPeerConnection;
284 | window.RTCSessionDescription = require('./adapter').RTCSessionDescription;
285 | window.RTCIceCandidate = require('./adapter').RTCIceCandidate;
286 | window.Negotiator = require('./negotiator');
287 | window.util = require('./util');
288 | window.BinaryPack = require('js-binarypack');
289 |
290 | },{"./adapter":1,"./dataconnection":2,"./mediaconnection":4,"./negotiator":5,"./peer":6,"./socket":7,"./util":8,"js-binarypack":10}],4:[function(require,module,exports){
291 | var util = require('./util');
292 | var EventEmitter = require('eventemitter3');
293 | var Negotiator = require('./negotiator');
294 |
295 | /**
296 | * Wraps the streaming interface between two Peers.
297 | */
298 | function MediaConnection(peer, provider, options) {
299 | if (!(this instanceof MediaConnection)) return new MediaConnection(peer, provider, options);
300 | EventEmitter.call(this);
301 |
302 | this.options = util.extend({}, options);
303 |
304 | this.open = false;
305 | this.type = 'media';
306 | this.peer = peer;
307 | this.provider = provider;
308 | this.metadata = this.options.metadata;
309 | this.localStream = this.options._stream;
310 |
311 | this.id = this.options.connectionId || MediaConnection._idPrefix + util.randomToken();
312 | if (this.localStream) {
313 | Negotiator.startConnection(
314 | this,
315 | {_stream: this.localStream, originator: true}
316 | );
317 | }
318 | };
319 |
320 | util.inherits(MediaConnection, EventEmitter);
321 |
322 | MediaConnection._idPrefix = 'mc_';
323 |
324 | MediaConnection.prototype.addStream = function(remoteStream) {
325 | util.log('Receiving stream', remoteStream);
326 |
327 | this.remoteStream = remoteStream;
328 | this.emit('stream', remoteStream); // Should we call this `open`?
329 |
330 | };
331 |
332 | MediaConnection.prototype.handleMessage = function(message) {
333 | var payload = message.payload;
334 |
335 | switch (message.type) {
336 | case 'ANSWER':
337 | // Forward to negotiator
338 | Negotiator.handleSDP(message.type, this, payload.sdp);
339 | this.open = true;
340 | break;
341 | case 'CANDIDATE':
342 | Negotiator.handleCandidate(this, payload.candidate);
343 | break;
344 | default:
345 | util.warn('Unrecognized message type:', message.type, 'from peer:', this.peer);
346 | break;
347 | }
348 | }
349 |
350 | MediaConnection.prototype.answer = function(stream) {
351 | if (this.localStream) {
352 | util.warn('Local stream already exists on this MediaConnection. Are you answering a call twice?');
353 | return;
354 | }
355 |
356 | this.options._payload._stream = stream;
357 |
358 | this.localStream = stream;
359 | Negotiator.startConnection(
360 | this,
361 | this.options._payload
362 | )
363 | // Retrieve lost messages stored because PeerConnection not set up.
364 | var messages = this.provider._getMessages(this.id);
365 | for (var i = 0, ii = messages.length; i < ii; i += 1) {
366 | this.handleMessage(messages[i]);
367 | }
368 | this.open = true;
369 | };
370 |
371 | /**
372 | * Exposed functionality for users.
373 | */
374 |
375 | /** Allows user to close connection. */
376 | MediaConnection.prototype.close = function() {
377 | if (!this.open) {
378 | return;
379 | }
380 | this.open = false;
381 | Negotiator.cleanup(this);
382 | this.emit('close')
383 | };
384 |
385 | module.exports = MediaConnection;
386 |
387 | },{"./negotiator":5,"./util":8,"eventemitter3":9}],5:[function(require,module,exports){
388 | var util = require('./util');
389 | var RTCPeerConnection = require('./adapter').RTCPeerConnection;
390 | var RTCSessionDescription = require('./adapter').RTCSessionDescription;
391 | var RTCIceCandidate = require('./adapter').RTCIceCandidate;
392 |
393 | /**
394 | * Manages all negotiations between Peers.
395 | */
396 | var Negotiator = {
397 | pcs: {
398 | data: {},
399 | media: {}
400 | }, // type => {peerId: {pc_id: pc}}.
401 | //providers: {}, // provider's id => providers (there may be multiple providers/client.
402 | queue: [] // connections that are delayed due to a PC being in use.
403 | }
404 |
405 | Negotiator._idPrefix = 'pc_';
406 |
407 | /** Returns a PeerConnection object set up correctly (for data, media). */
408 | Negotiator.startConnection = function(connection, options) {
409 | var pc = Negotiator._getPeerConnection(connection, options);
410 |
411 | if (connection.type === 'media' && options._stream) {
412 | // Add the stream.
413 | pc.addStream(options._stream);
414 | }
415 |
416 | // Set the connection's PC.
417 | connection.pc = connection.peerConnection = pc;
418 | // What do we need to do now?
419 | if (options.originator) {
420 | if (connection.type === 'data') {
421 | // Create the datachannel.
422 | var config = {};
423 | // Dropping reliable:false support, since it seems to be crashing
424 | // Chrome.
425 | /*if (util.supports.sctp && !options.reliable) {
426 | // If we have canonical reliable support...
427 | config = {maxRetransmits: 0};
428 | }*/
429 | // Fallback to ensure older browsers don't crash.
430 | if (!util.supports.sctp) {
431 | config = {reliable: options.reliable};
432 | }
433 | var dc = pc.createDataChannel(connection.label, config);
434 | connection.initialize(dc);
435 | }
436 |
437 | if (!util.supports.onnegotiationneeded) {
438 | Negotiator._makeOffer(connection);
439 | }
440 | } else {
441 | Negotiator.handleSDP('OFFER', connection, options.sdp);
442 | }
443 | }
444 |
445 | Negotiator._getPeerConnection = function(connection, options) {
446 | if (!Negotiator.pcs[connection.type]) {
447 | util.error(connection.type + ' is not a valid connection type. Maybe you overrode the `type` property somewhere.');
448 | }
449 |
450 | if (!Negotiator.pcs[connection.type][connection.peer]) {
451 | Negotiator.pcs[connection.type][connection.peer] = {};
452 | }
453 | var peerConnections = Negotiator.pcs[connection.type][connection.peer];
454 |
455 | var pc;
456 | // Not multiplexing while FF and Chrome have not-great support for it.
457 | /*if (options.multiplex) {
458 | ids = Object.keys(peerConnections);
459 | for (var i = 0, ii = ids.length; i < ii; i += 1) {
460 | pc = peerConnections[ids[i]];
461 | if (pc.signalingState === 'stable') {
462 | break; // We can go ahead and use this PC.
463 | }
464 | }
465 | } else */
466 | if (options.pc) { // Simplest case: PC id already provided for us.
467 | pc = Negotiator.pcs[connection.type][connection.peer][options.pc];
468 | }
469 |
470 | if (!pc || pc.signalingState !== 'stable') {
471 | pc = Negotiator._startPeerConnection(connection);
472 | }
473 | return pc;
474 | }
475 |
476 | /*
477 | Negotiator._addProvider = function(provider) {
478 | if ((!provider.id && !provider.disconnected) || !provider.socket.open) {
479 | // Wait for provider to obtain an ID.
480 | provider.on('open', function(id) {
481 | Negotiator._addProvider(provider);
482 | });
483 | } else {
484 | Negotiator.providers[provider.id] = provider;
485 | }
486 | }*/
487 |
488 |
489 | /** Start a PC. */
490 | Negotiator._startPeerConnection = function(connection) {
491 | util.log('Creating RTCPeerConnection.');
492 |
493 | var id = Negotiator._idPrefix + util.randomToken();
494 | var optional = {};
495 |
496 | if (connection.type === 'data' && !util.supports.sctp) {
497 | optional = {optional: [{RtpDataChannels: true}]};
498 | } else if (connection.type === 'media') {
499 | // Interop req for chrome.
500 | optional = {optional: [{DtlsSrtpKeyAgreement: true}]};
501 | }
502 |
503 | var pc = new RTCPeerConnection(connection.provider.options.config, optional);
504 | Negotiator.pcs[connection.type][connection.peer][id] = pc;
505 |
506 | Negotiator._setupListeners(connection, pc, id);
507 |
508 | return pc;
509 | }
510 |
511 | /** Set up various WebRTC listeners. */
512 | Negotiator._setupListeners = function(connection, pc, pc_id) {
513 | var peerId = connection.peer;
514 | var connectionId = connection.id;
515 | var provider = connection.provider;
516 |
517 | // ICE CANDIDATES.
518 | util.log('Listening for ICE candidates.');
519 | pc.onicecandidate = function(evt) {
520 | if (evt.candidate) {
521 | util.log('Received ICE candidates for:', connection.peer);
522 | provider.socket.send({
523 | type: 'CANDIDATE',
524 | payload: {
525 | candidate: evt.candidate,
526 | type: connection.type,
527 | connectionId: connection.id
528 | },
529 | dst: peerId
530 | });
531 | }
532 | };
533 |
534 | pc.oniceconnectionstatechange = function() {
535 | switch (pc.iceConnectionState) {
536 | case 'disconnected':
537 | case 'failed':
538 | util.log('iceConnectionState is disconnected, closing connections to ' + peerId);
539 | connection.close();
540 | break;
541 | case 'completed':
542 | pc.onicecandidate = util.noop;
543 | break;
544 | }
545 | };
546 |
547 | // Fallback for older Chrome impls.
548 | pc.onicechange = pc.oniceconnectionstatechange;
549 |
550 | // ONNEGOTIATIONNEEDED (Chrome)
551 | util.log('Listening for `negotiationneeded`');
552 | pc.onnegotiationneeded = function() {
553 | util.log('`negotiationneeded` triggered');
554 | if (pc.signalingState == 'stable') {
555 | Negotiator._makeOffer(connection);
556 | } else {
557 | util.log('onnegotiationneeded triggered when not stable. Is another connection being established?');
558 | }
559 | };
560 |
561 | // DATACONNECTION.
562 | util.log('Listening for data channel');
563 | // Fired between offer and answer, so options should already be saved
564 | // in the options hash.
565 | pc.ondatachannel = function(evt) {
566 | util.log('Received data channel');
567 | var dc = evt.channel;
568 | var connection = provider.getConnection(peerId, connectionId);
569 | connection.initialize(dc);
570 | };
571 |
572 | // MEDIACONNECTION.
573 | util.log('Listening for remote stream');
574 | pc.onaddstream = function(evt) {
575 | util.log('Received remote stream');
576 | var stream = evt.stream;
577 | var connection = provider.getConnection(peerId, connectionId);
578 | // 10/10/2014: looks like in Chrome 38, onaddstream is triggered after
579 | // setting the remote description. Our connection object in these cases
580 | // is actually a DATA connection, so addStream fails.
581 | // TODO: This is hopefully just a temporary fix. We should try to
582 | // understand why this is happening.
583 | if (connection.type === 'media') {
584 | connection.addStream(stream);
585 | }
586 | };
587 | }
588 |
589 | Negotiator.cleanup = function(connection) {
590 | util.log('Cleaning up PeerConnection to ' + connection.peer);
591 |
592 | var pc = connection.pc;
593 |
594 | if (!!pc && (pc.readyState !== 'closed' || pc.signalingState !== 'closed')) {
595 | pc.close();
596 | connection.pc = null;
597 | }
598 | }
599 |
600 | Negotiator._makeOffer = function(connection) {
601 | var pc = connection.pc;
602 | pc.createOffer(function(offer) {
603 | util.log('Created offer.');
604 |
605 | if (!util.supports.sctp && connection.type === 'data' && connection.reliable) {
606 | offer.sdp = Reliable.higherBandwidthSDP(offer.sdp);
607 | }
608 |
609 | pc.setLocalDescription(offer, function() {
610 | util.log('Set localDescription: offer', 'for:', connection.peer);
611 | connection.provider.socket.send({
612 | type: 'OFFER',
613 | payload: {
614 | sdp: offer,
615 | type: connection.type,
616 | label: connection.label,
617 | connectionId: connection.id,
618 | reliable: connection.reliable,
619 | serialization: connection.serialization,
620 | metadata: connection.metadata,
621 | browser: util.browser
622 | },
623 | dst: connection.peer
624 | });
625 | }, function(err) {
626 | connection.provider.emitError('webrtc', err);
627 | util.log('Failed to setLocalDescription, ', err);
628 | });
629 | }, function(err) {
630 | connection.provider.emitError('webrtc', err);
631 | util.log('Failed to createOffer, ', err);
632 | }, connection.options.constraints);
633 | }
634 |
635 | Negotiator._makeAnswer = function(connection) {
636 | var pc = connection.pc;
637 |
638 | pc.createAnswer(function(answer) {
639 | util.log('Created answer.');
640 |
641 | if (!util.supports.sctp && connection.type === 'data' && connection.reliable) {
642 | answer.sdp = Reliable.higherBandwidthSDP(answer.sdp);
643 | }
644 |
645 | pc.setLocalDescription(answer, function() {
646 | util.log('Set localDescription: answer', 'for:', connection.peer);
647 | connection.provider.socket.send({
648 | type: 'ANSWER',
649 | payload: {
650 | sdp: answer,
651 | type: connection.type,
652 | connectionId: connection.id,
653 | browser: util.browser
654 | },
655 | dst: connection.peer
656 | });
657 | }, function(err) {
658 | connection.provider.emitError('webrtc', err);
659 | util.log('Failed to setLocalDescription, ', err);
660 | });
661 | }, function(err) {
662 | connection.provider.emitError('webrtc', err);
663 | util.log('Failed to create answer, ', err);
664 | });
665 | }
666 |
667 | /** Handle an SDP. */
668 | Negotiator.handleSDP = function(type, connection, sdp) {
669 | sdp = new RTCSessionDescription(sdp);
670 | var pc = connection.pc;
671 |
672 | util.log('Setting remote description', sdp);
673 | pc.setRemoteDescription(sdp, function() {
674 | util.log('Set remoteDescription:', type, 'for:', connection.peer);
675 |
676 | if (type === 'OFFER') {
677 | Negotiator._makeAnswer(connection);
678 | }
679 | }, function(err) {
680 | connection.provider.emitError('webrtc', err);
681 | util.log('Failed to setRemoteDescription, ', err);
682 | });
683 | }
684 |
685 | /** Handle a candidate. */
686 | Negotiator.handleCandidate = function(connection, ice) {
687 | var candidate = ice.candidate;
688 | var sdpMLineIndex = ice.sdpMLineIndex;
689 | connection.pc.addIceCandidate(new RTCIceCandidate({
690 | sdpMLineIndex: sdpMLineIndex,
691 | candidate: candidate
692 | }));
693 | util.log('Added ICE candidate for:', connection.peer);
694 | }
695 |
696 | module.exports = Negotiator;
697 |
698 | },{"./adapter":1,"./util":8}],6:[function(require,module,exports){
699 | var util = require('./util');
700 | var EventEmitter = require('eventemitter3');
701 | var Socket = require('./socket');
702 | var MediaConnection = require('./mediaconnection');
703 | var DataConnection = require('./dataconnection');
704 |
705 | /**
706 | * A peer who can initiate connections with other peers.
707 | */
708 | function Peer(id, options) {
709 | if (!(this instanceof Peer)) return new Peer(id, options);
710 | EventEmitter.call(this);
711 |
712 | // Deal with overloading
713 | if (id && id.constructor == Object) {
714 | options = id;
715 | id = undefined;
716 | } else if (id) {
717 | // Ensure id is a string
718 | id = id.toString();
719 | }
720 | //
721 |
722 | // Configurize options
723 | options = util.extend({
724 | debug: 0, // 1: Errors, 2: Warnings, 3: All logs
725 | host: util.CLOUD_HOST,
726 | port: util.CLOUD_PORT,
727 | key: 'peerjs',
728 | path: '/',
729 | token: util.randomToken(),
730 | config: util.defaultConfig
731 | }, options);
732 | this.options = options;
733 | // Detect relative URL host.
734 | if (options.host === '/') {
735 | options.host = window.location.hostname;
736 | }
737 | // Set path correctly.
738 | if (options.path[0] !== '/') {
739 | options.path = '/' + options.path;
740 | }
741 | if (options.path[options.path.length - 1] !== '/') {
742 | options.path += '/';
743 | }
744 |
745 | // Set whether we use SSL to same as current host
746 | if (options.secure === undefined && options.host !== util.CLOUD_HOST) {
747 | options.secure = util.isSecure();
748 | }
749 | // Set a custom log function if present
750 | if (options.logFunction) {
751 | util.setLogFunction(options.logFunction);
752 | }
753 | util.setLogLevel(options.debug);
754 | //
755 |
756 | // Sanity checks
757 | // Ensure WebRTC supported
758 | if (!util.supports.audioVideo && !util.supports.data ) {
759 | this._delayedAbort('browser-incompatible', 'The current browser does not support WebRTC');
760 | return;
761 | }
762 | // Ensure alphanumeric id
763 | if (!util.validateId(id)) {
764 | this._delayedAbort('invalid-id', 'ID "' + id + '" is invalid');
765 | return;
766 | }
767 | // Ensure valid key
768 | if (!util.validateKey(options.key)) {
769 | this._delayedAbort('invalid-key', 'API KEY "' + options.key + '" is invalid');
770 | return;
771 | }
772 | // Ensure not using unsecure cloud server on SSL page
773 | if (options.secure && options.host === '0.peerjs.com') {
774 | this._delayedAbort('ssl-unavailable',
775 | 'The cloud server currently does not support HTTPS. Please run your own PeerServer to use HTTPS.');
776 | return;
777 | }
778 | //
779 |
780 | // States.
781 | this.destroyed = false; // Connections have been killed
782 | this.disconnected = false; // Connection to PeerServer killed but P2P connections still active
783 | this.open = false; // Sockets and such are not yet open.
784 | //
785 |
786 | // References
787 | this.connections = {}; // DataConnections for this peer.
788 | this._lostMessages = {}; // src => [list of messages]
789 | //
790 |
791 | // Start the server connection
792 | this._initializeServerConnection();
793 | if (id) {
794 | this._initialize(id);
795 | } else {
796 | this._retrieveId();
797 | }
798 | //
799 | }
800 |
801 | util.inherits(Peer, EventEmitter);
802 |
803 | // Initialize the 'socket' (which is actually a mix of XHR streaming and
804 | // websockets.)
805 | Peer.prototype._initializeServerConnection = function() {
806 | var self = this;
807 | this.socket = new Socket(this.options.secure, this.options.host, this.options.port, this.options.path, this.options.key);
808 | this.socket.on('message', function(data) {
809 | self._handleMessage(data);
810 | });
811 | this.socket.on('error', function(error) {
812 | self._abort('socket-error', error);
813 | });
814 | this.socket.on('disconnected', function() {
815 | // If we haven't explicitly disconnected, emit error and disconnect.
816 | if (!self.disconnected) {
817 | self.emitError('network', 'Lost connection to server.');
818 | self.disconnect();
819 | }
820 | });
821 | this.socket.on('close', function() {
822 | // If we haven't explicitly disconnected, emit error.
823 | if (!self.disconnected) {
824 | self._abort('socket-closed', 'Underlying socket is already closed.');
825 | }
826 | });
827 | };
828 |
829 | /** Get a unique ID from the server via XHR. */
830 | Peer.prototype._retrieveId = function(cb) {
831 | var self = this;
832 | var http = new XMLHttpRequest();
833 | var protocol = this.options.secure ? 'https://' : 'http://';
834 | var url = protocol + this.options.host + //':' + this.options.port +
835 | this.options.path + this.options.key + '/id';
836 | var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
837 | url += queryString;
838 |
839 | // If there's no ID we need to wait for one before trying to init socket.
840 | http.open('get', url, true);
841 | http.onerror = function(e) {
842 | util.error('Error retrieving ID', e);
843 | var pathError = '';
844 | if (self.options.path === '/' && self.options.host !== util.CLOUD_HOST) {
845 | pathError = ' If you passed in a `path` to your self-hosted PeerServer, ' +
846 | 'you\'ll also need to pass in that same path when creating a new ' +
847 | 'Peer.';
848 | }
849 | self._abort('server-error', 'Could not get an ID from the server.' + pathError);
850 | };
851 | http.onreadystatechange = function() {
852 | if (http.readyState !== 4) {
853 | return;
854 | }
855 | if (http.status !== 200) {
856 | http.onerror();
857 | return;
858 | }
859 | self._initialize(http.responseText);
860 | };
861 | http.send(null);
862 | };
863 |
864 | /** Initialize a connection with the server. */
865 | Peer.prototype._initialize = function(id) {
866 | this.id = id;
867 | this.socket.start(this.id, this.options.token);
868 | };
869 |
870 | /** Handles messages from the server. */
871 | Peer.prototype._handleMessage = function(message) {
872 | var type = message.type;
873 | var payload = message.payload;
874 | var peer = message.src;
875 | var connection;
876 |
877 | switch (type) {
878 | case 'OPEN': // The connection to the server is open.
879 | this.emit('open', this.id);
880 | this.open = true;
881 | break;
882 | case 'ERROR': // Server error.
883 | this._abort('server-error', payload.msg);
884 | break;
885 | case 'ID-TAKEN': // The selected ID is taken.
886 | this._abort('unavailable-id', 'ID `' + this.id + '` is taken');
887 | break;
888 | case 'INVALID-KEY': // The given API key cannot be found.
889 | this._abort('invalid-key', 'API KEY "' + this.options.key + '" is invalid');
890 | break;
891 |
892 | //
893 | case 'LEAVE': // Another peer has closed its connection to this peer.
894 | util.log('Received leave message from', peer);
895 | this._cleanupPeer(peer);
896 | break;
897 |
898 | case 'EXPIRE': // The offer sent to a peer has expired without response.
899 | this.emitError('peer-unavailable', 'Could not connect to peer ' + peer);
900 | break;
901 | case 'OFFER': // we should consider switching this to CALL/CONNECT, but this is the least breaking option.
902 | var connectionId = payload.connectionId;
903 | connection = this.getConnection(peer, connectionId);
904 |
905 | if (connection) {
906 | util.warn('Offer received for existing Connection ID:', connectionId);
907 | //connection.handleMessage(message);
908 | } else {
909 | // Create a new connection.
910 | if (payload.type === 'media') {
911 | connection = new MediaConnection(peer, this, {
912 | connectionId: connectionId,
913 | _payload: payload,
914 | metadata: payload.metadata
915 | });
916 | this._addConnection(peer, connection);
917 | this.emit('call', connection);
918 | } else if (payload.type === 'data') {
919 | connection = new DataConnection(peer, this, {
920 | connectionId: connectionId,
921 | _payload: payload,
922 | metadata: payload.metadata,
923 | label: payload.label,
924 | serialization: payload.serialization,
925 | reliable: payload.reliable
926 | });
927 | this._addConnection(peer, connection);
928 | this.emit('connection', connection);
929 | } else {
930 | util.warn('Received malformed connection type:', payload.type);
931 | return;
932 | }
933 | // Find messages.
934 | var messages = this._getMessages(connectionId);
935 | for (var i = 0, ii = messages.length; i < ii; i += 1) {
936 | connection.handleMessage(messages[i]);
937 | }
938 | }
939 | break;
940 | default:
941 | if (!payload) {
942 | util.warn('You received a malformed message from ' + peer + ' of type ' + type);
943 | return;
944 | }
945 |
946 | var id = payload.connectionId;
947 | connection = this.getConnection(peer, id);
948 |
949 | if (connection && connection.pc) {
950 | // Pass it on.
951 | connection.handleMessage(message);
952 | } else if (id) {
953 | // Store for possible later use
954 | this._storeMessage(id, message);
955 | } else {
956 | util.warn('You received an unrecognized message:', message);
957 | }
958 | break;
959 | }
960 | };
961 |
962 | /** Stores messages without a set up connection, to be claimed later. */
963 | Peer.prototype._storeMessage = function(connectionId, message) {
964 | if (!this._lostMessages[connectionId]) {
965 | this._lostMessages[connectionId] = [];
966 | }
967 | this._lostMessages[connectionId].push(message);
968 | };
969 |
970 | /** Retrieve messages from lost message store */
971 | Peer.prototype._getMessages = function(connectionId) {
972 | var messages = this._lostMessages[connectionId];
973 | if (messages) {
974 | delete this._lostMessages[connectionId];
975 | return messages;
976 | } else {
977 | return [];
978 | }
979 | };
980 |
981 | /**
982 | * Returns a DataConnection to the specified peer. See documentation for a
983 | * complete list of options.
984 | */
985 | Peer.prototype.connect = function(peer, options) {
986 | if (this.disconnected) {
987 | util.warn('You cannot connect to a new Peer because you called ' +
988 | '.disconnect() on this Peer and ended your connection with the ' +
989 | 'server. You can create a new Peer to reconnect, or call reconnect ' +
990 | 'on this peer if you believe its ID to still be available.');
991 | this.emitError('disconnected', 'Cannot connect to new Peer after disconnecting from server.');
992 | return;
993 | }
994 | var connection = new DataConnection(peer, this, options);
995 | this._addConnection(peer, connection);
996 | return connection;
997 | };
998 |
999 | /**
1000 | * Returns a MediaConnection to the specified peer. See documentation for a
1001 | * complete list of options.
1002 | */
1003 | Peer.prototype.call = function(peer, stream, options) {
1004 | if (this.disconnected) {
1005 | util.warn('You cannot connect to a new Peer because you called ' +
1006 | '.disconnect() on this Peer and ended your connection with the ' +
1007 | 'server. You can create a new Peer to reconnect.');
1008 | this.emitError('disconnected', 'Cannot connect to new Peer after disconnecting from server.');
1009 | return;
1010 | }
1011 | if (!stream) {
1012 | util.error('To call a peer, you must provide a stream from your browser\'s `getUserMedia`.');
1013 | return;
1014 | }
1015 | options = options || {};
1016 | options._stream = stream;
1017 | var call = new MediaConnection(peer, this, options);
1018 | this._addConnection(peer, call);
1019 | return call;
1020 | };
1021 |
1022 | /** Add a data/media connection to this peer. */
1023 | Peer.prototype._addConnection = function(peer, connection) {
1024 | if (!this.connections[peer]) {
1025 | this.connections[peer] = [];
1026 | }
1027 | this.connections[peer].push(connection);
1028 | };
1029 |
1030 | /** Retrieve a data/media connection for this peer. */
1031 | Peer.prototype.getConnection = function(peer, id) {
1032 | var connections = this.connections[peer];
1033 | if (!connections) {
1034 | return null;
1035 | }
1036 | for (var i = 0, ii = connections.length; i < ii; i++) {
1037 | if (connections[i].id === id) {
1038 | return connections[i];
1039 | }
1040 | }
1041 | return null;
1042 | };
1043 |
1044 | Peer.prototype._delayedAbort = function(type, message) {
1045 | var self = this;
1046 | util.setZeroTimeout(function(){
1047 | self._abort(type, message);
1048 | });
1049 | };
1050 |
1051 | /**
1052 | * Destroys the Peer and emits an error message.
1053 | * The Peer is not destroyed if it's in a disconnected state, in which case
1054 | * it retains its disconnected state and its existing connections.
1055 | */
1056 | Peer.prototype._abort = function(type, message) {
1057 | util.error('Aborting!');
1058 | if (!this._lastServerId) {
1059 | this.destroy();
1060 | } else {
1061 | this.disconnect();
1062 | }
1063 | this.emitError(type, message);
1064 | };
1065 |
1066 | /** Emits a typed error message. */
1067 | Peer.prototype.emitError = function(type, err) {
1068 | util.error('Error:', err);
1069 | if (typeof err === 'string') {
1070 | err = new Error(err);
1071 | }
1072 | err.type = type;
1073 | this.emit('error', err);
1074 | };
1075 |
1076 | /**
1077 | * Destroys the Peer: closes all active connections as well as the connection
1078 | * to the server.
1079 | * Warning: The peer can no longer create or accept connections after being
1080 | * destroyed.
1081 | */
1082 | Peer.prototype.destroy = function() {
1083 | if (!this.destroyed) {
1084 | this._cleanup();
1085 | this.disconnect();
1086 | this.destroyed = true;
1087 | }
1088 | };
1089 |
1090 |
1091 | /** Disconnects every connection on this peer. */
1092 | Peer.prototype._cleanup = function() {
1093 | if (this.connections) {
1094 | var peers = Object.keys(this.connections);
1095 | for (var i = 0, ii = peers.length; i < ii; i++) {
1096 | this._cleanupPeer(peers[i]);
1097 | }
1098 | }
1099 | this.emit('close');
1100 | };
1101 |
1102 | /** Closes all connections to this peer. */
1103 | Peer.prototype._cleanupPeer = function(peer) {
1104 | var connections = this.connections[peer];
1105 | for (var j = 0, jj = connections.length; j < jj; j += 1) {
1106 | connections[j].close();
1107 | }
1108 | };
1109 |
1110 | /**
1111 | * Disconnects the Peer's connection to the PeerServer. Does not close any
1112 | * active connections.
1113 | * Warning: The peer can no longer create or accept connections after being
1114 | * disconnected. It also cannot reconnect to the server.
1115 | */
1116 | Peer.prototype.disconnect = function() {
1117 | var self = this;
1118 | util.setZeroTimeout(function(){
1119 | if (!self.disconnected) {
1120 | self.disconnected = true;
1121 | self.open = false;
1122 | if (self.socket) {
1123 | self.socket.close();
1124 | }
1125 | self.emit('disconnected', self.id);
1126 | self._lastServerId = self.id;
1127 | self.id = null;
1128 | }
1129 | });
1130 | };
1131 |
1132 | /** Attempts to reconnect with the same ID. */
1133 | Peer.prototype.reconnect = function() {
1134 | if (this.disconnected && !this.destroyed) {
1135 | util.log('Attempting reconnection to server with ID ' + this._lastServerId);
1136 | this.disconnected = false;
1137 | this._initializeServerConnection();
1138 | this._initialize(this._lastServerId);
1139 | } else if (this.destroyed) {
1140 | throw new Error('This peer cannot reconnect to the server. It has already been destroyed.');
1141 | } else if (!this.disconnected && !this.open) {
1142 | // Do nothing. We're still connecting the first time.
1143 | util.error('In a hurry? We\'re still trying to make the initial connection!');
1144 | } else {
1145 | throw new Error('Peer ' + this.id + ' cannot reconnect because it is not disconnected from the server!');
1146 | }
1147 | };
1148 |
1149 | /**
1150 | * Get a list of available peer IDs. If you're running your own server, you'll
1151 | * want to set allow_discovery: true in the PeerServer options. If you're using
1152 | * the cloud server, email team@peerjs.com to get the functionality enabled for
1153 | * your key.
1154 | */
1155 | Peer.prototype.listAllPeers = function(cb) {
1156 | cb = cb || function() {};
1157 | var self = this;
1158 | var http = new XMLHttpRequest();
1159 | var protocol = this.options.secure ? 'https://' : 'http://';
1160 | var url = protocol + this.options.host + //':' + this.options.port +
1161 | this.options.path + this.options.key + '/peers';
1162 | var queryString = '?ts=' + new Date().getTime() + '' + Math.random();
1163 | url += queryString;
1164 |
1165 | // If there's no ID we need to wait for one before trying to init socket.
1166 | http.open('get', url, true);
1167 | http.onerror = function(e) {
1168 | self._abort('server-error', 'Could not get peers from the server.');
1169 | cb([]);
1170 | };
1171 | http.onreadystatechange = function() {
1172 | if (http.readyState !== 4) {
1173 | return;
1174 | }
1175 | if (http.status === 401) {
1176 | var helpfulError = '';
1177 | if (self.options.host !== util.CLOUD_HOST) {
1178 | helpfulError = 'It looks like you\'re using the cloud server. You can email ' +
1179 | 'team@peerjs.com to enable peer listing for your API key.';
1180 | } else {
1181 | helpfulError = 'You need to enable `allow_discovery` on your self-hosted ' +
1182 | 'PeerServer to use this feature.';
1183 | }
1184 | cb([]);
1185 | throw new Error('It doesn\'t look like you have permission to list peers IDs. ' + helpfulError);
1186 | } else if (http.status !== 200) {
1187 | cb([]);
1188 | } else {
1189 | cb(JSON.parse(http.responseText));
1190 | }
1191 | };
1192 | http.send(null);
1193 | };
1194 |
1195 | module.exports = Peer;
1196 |
1197 | },{"./dataconnection":2,"./mediaconnection":4,"./socket":7,"./util":8,"eventemitter3":9}],7:[function(require,module,exports){
1198 | var util = require('./util');
1199 | var EventEmitter = require('eventemitter3');
1200 |
1201 | /**
1202 | * An abstraction on top of WebSockets and XHR streaming to provide fastest
1203 | * possible connection for peers.
1204 | */
1205 | function Socket(secure, host, port, path, key) {
1206 | if (!(this instanceof Socket)) return new Socket(secure, host, port, path, key);
1207 |
1208 | EventEmitter.call(this);
1209 |
1210 | // Disconnected manually.
1211 | this.disconnected = false;
1212 | this._queue = [];
1213 |
1214 | var httpProtocol = secure ? 'https://' : 'http://';
1215 | var wsProtocol = secure ? 'wss://' : 'ws://';
1216 | this._httpUrl = httpProtocol + host + path + key;
1217 | this._wsUrl = wsProtocol + host + path + 'peerjs?key=' + key;
1218 | }
1219 |
1220 | util.inherits(Socket, EventEmitter);
1221 |
1222 |
1223 | /** Check in with ID or get one from server. */
1224 | Socket.prototype.start = function(id, token) {
1225 | this.id = id;
1226 |
1227 | this._httpUrl += '/' + id + '/' + token;
1228 | this._wsUrl += '&id=' + id + '&token=' + token;
1229 |
1230 | this._startXhrStream();
1231 | this._startWebSocket();
1232 | }
1233 |
1234 |
1235 | /** Start up websocket communications. */
1236 | Socket.prototype._startWebSocket = function(id) {
1237 | var self = this;
1238 |
1239 | if (this._socket) {
1240 | return;
1241 | }
1242 |
1243 | this._socket = new WebSocket(this._wsUrl);
1244 |
1245 | this._socket.onmessage = function(event) {
1246 | try {
1247 | var data = JSON.parse(event.data);
1248 | } catch(e) {
1249 | util.log('Invalid server message', event.data);
1250 | return;
1251 | }
1252 | self.emit('message', data);
1253 | };
1254 |
1255 | this._socket.onclose = function(event) {
1256 | util.log('Socket closed.');
1257 | self.disconnected = true;
1258 | self.emit('disconnected');
1259 | };
1260 |
1261 | // Take care of the queue of connections if necessary and make sure Peer knows
1262 | // socket is open.
1263 | this._socket.onopen = function() {
1264 | if (self._timeout) {
1265 | clearTimeout(self._timeout);
1266 | setTimeout(function(){
1267 | self._http.abort();
1268 | self._http = null;
1269 | }, 5000);
1270 | }
1271 | self._sendQueuedMessages();
1272 | util.log('Socket open');
1273 | };
1274 | }
1275 |
1276 | /** Start XHR streaming. */
1277 | Socket.prototype._startXhrStream = function(n) {
1278 | try {
1279 | var self = this;
1280 | this._http = new XMLHttpRequest();
1281 | this._http._index = 1;
1282 | this._http._streamIndex = n || 0;
1283 | this._http.open('post', this._httpUrl + '/id?i=' + this._http._streamIndex, true);
1284 | this._http.onerror = function() {
1285 | // If we get an error, likely something went wrong.
1286 | // Stop streaming.
1287 | clearTimeout(self._timeout);
1288 | self.emit('disconnected');
1289 | }
1290 | this._http.onreadystatechange = function() {
1291 | if (this.readyState == 2 && this.old) {
1292 | this.old.abort();
1293 | delete this.old;
1294 | } else if (this.readyState > 2 && this.status === 200 && this.responseText) {
1295 | self._handleStream(this);
1296 | }
1297 | };
1298 | this._http.send(null);
1299 | this._setHTTPTimeout();
1300 | } catch(e) {
1301 | util.log('XMLHttpRequest not available; defaulting to WebSockets');
1302 | }
1303 | }
1304 |
1305 |
1306 | /** Handles onreadystatechange response as a stream. */
1307 | Socket.prototype._handleStream = function(http) {
1308 | // 3 and 4 are loading/done state. All others are not relevant.
1309 | var messages = http.responseText.split('\n');
1310 |
1311 | // Check to see if anything needs to be processed on buffer.
1312 | if (http._buffer) {
1313 | while (http._buffer.length > 0) {
1314 | var index = http._buffer.shift();
1315 | var bufferedMessage = messages[index];
1316 | try {
1317 | bufferedMessage = JSON.parse(bufferedMessage);
1318 | } catch(e) {
1319 | http._buffer.shift(index);
1320 | break;
1321 | }
1322 | this.emit('message', bufferedMessage);
1323 | }
1324 | }
1325 |
1326 | var message = messages[http._index];
1327 | if (message) {
1328 | http._index += 1;
1329 | // Buffering--this message is incomplete and we'll get to it next time.
1330 | // This checks if the httpResponse ended in a `\n`, in which case the last
1331 | // element of messages should be the empty string.
1332 | if (http._index === messages.length) {
1333 | if (!http._buffer) {
1334 | http._buffer = [];
1335 | }
1336 | http._buffer.push(http._index - 1);
1337 | } else {
1338 | try {
1339 | message = JSON.parse(message);
1340 | } catch(e) {
1341 | util.log('Invalid server message', message);
1342 | return;
1343 | }
1344 | this.emit('message', message);
1345 | }
1346 | }
1347 | }
1348 |
1349 | Socket.prototype._setHTTPTimeout = function() {
1350 | var self = this;
1351 | this._timeout = setTimeout(function() {
1352 | var old = self._http;
1353 | if (!self._wsOpen()) {
1354 | self._startXhrStream(old._streamIndex + 1);
1355 | self._http.old = old;
1356 | } else {
1357 | old.abort();
1358 | }
1359 | }, 25000);
1360 | }
1361 |
1362 | /** Is the websocket currently open? */
1363 | Socket.prototype._wsOpen = function() {
1364 | return this._socket && this._socket.readyState == 1;
1365 | }
1366 |
1367 | /** Send queued messages. */
1368 | Socket.prototype._sendQueuedMessages = function() {
1369 | for (var i = 0, ii = this._queue.length; i < ii; i += 1) {
1370 | this.send(this._queue[i]);
1371 | }
1372 | }
1373 |
1374 | /** Exposed send for DC & Peer. */
1375 | Socket.prototype.send = function(data) {
1376 | if (this.disconnected) {
1377 | return;
1378 | }
1379 |
1380 | // If we didn't get an ID yet, we can't yet send anything so we should queue
1381 | // up these messages.
1382 | if (!this.id) {
1383 | this._queue.push(data);
1384 | return;
1385 | }
1386 |
1387 | if (!data.type) {
1388 | this.emit('error', 'Invalid message');
1389 | return;
1390 | }
1391 |
1392 | var message = JSON.stringify(data);
1393 | if (this._wsOpen()) {
1394 | this._socket.send(message);
1395 | } else {
1396 | var http = new XMLHttpRequest();
1397 | var url = this._httpUrl + '/' + data.type.toLowerCase();
1398 | http.open('post', url, true);
1399 | http.setRequestHeader('Content-Type', 'application/json');
1400 | http.send(message);
1401 | }
1402 | }
1403 |
1404 | Socket.prototype.close = function() {
1405 | if (!this.disconnected && this._wsOpen()) {
1406 | this._socket.close();
1407 | this.disconnected = true;
1408 | }
1409 | }
1410 |
1411 | module.exports = Socket;
1412 |
1413 | },{"./util":8,"eventemitter3":9}],8:[function(require,module,exports){
1414 | var defaultConfig = {'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]};
1415 | var dataCount = 1;
1416 |
1417 | var BinaryPack = require('js-binarypack');
1418 | var RTCPeerConnection = require('./adapter').RTCPeerConnection;
1419 |
1420 | var util = {
1421 | noop: function() {},
1422 |
1423 | CLOUD_HOST: '0.peerjs.com',
1424 | CLOUD_PORT: 9000,
1425 |
1426 | // Browsers that need chunking:
1427 | chunkedBrowsers: {'Chrome': 1},
1428 | chunkedMTU: 16300, // The original 60000 bytes setting does not work when sending data from Firefox to Chrome, which is "cut off" after 16384 bytes and delivered individually.
1429 |
1430 | // Logging logic
1431 | logLevel: 0,
1432 | setLogLevel: function(level) {
1433 | var debugLevel = parseInt(level, 10);
1434 | if (!isNaN(parseInt(level, 10))) {
1435 | util.logLevel = debugLevel;
1436 | } else {
1437 | // If they are using truthy/falsy values for debug
1438 | util.logLevel = level ? 3 : 0;
1439 | }
1440 | util.log = util.warn = util.error = util.noop;
1441 | if (util.logLevel > 0) {
1442 | util.error = util._printWith('ERROR');
1443 | }
1444 | if (util.logLevel > 1) {
1445 | util.warn = util._printWith('WARNING');
1446 | }
1447 | if (util.logLevel > 2) {
1448 | util.log = util._print;
1449 | }
1450 | },
1451 | setLogFunction: function(fn) {
1452 | if (fn.constructor !== Function) {
1453 | util.warn('The log function you passed in is not a function. Defaulting to regular logs.');
1454 | } else {
1455 | util._print = fn;
1456 | }
1457 | },
1458 |
1459 | _printWith: function(prefix) {
1460 | return function() {
1461 | var copy = Array.prototype.slice.call(arguments);
1462 | copy.unshift(prefix);
1463 | util._print.apply(util, copy);
1464 | };
1465 | },
1466 | _print: function () {
1467 | var err = false;
1468 | var copy = Array.prototype.slice.call(arguments);
1469 | copy.unshift('PeerJS: ');
1470 | for (var i = 0, l = copy.length; i < l; i++){
1471 | if (copy[i] instanceof Error) {
1472 | copy[i] = '(' + copy[i].name + ') ' + copy[i].message;
1473 | err = true;
1474 | }
1475 | }
1476 | err ? console.error.apply(console, copy) : console.log.apply(console, copy);
1477 | },
1478 | //
1479 |
1480 | // Returns browser-agnostic default config
1481 | defaultConfig: defaultConfig,
1482 | //
1483 |
1484 | // Returns the current browser.
1485 | browser: (function() {
1486 | if (window.mozRTCPeerConnection) {
1487 | return 'Firefox';
1488 | } else if (window.webkitRTCPeerConnection) {
1489 | return 'Chrome';
1490 | } else if (window.RTCPeerConnection) {
1491 | return 'Supported';
1492 | } else {
1493 | return 'Unsupported';
1494 | }
1495 | })(),
1496 | //
1497 |
1498 | // Lists which features are supported
1499 | supports: (function() {
1500 | if (typeof RTCPeerConnection === 'undefined') {
1501 | return {};
1502 | }
1503 |
1504 | var data = true;
1505 | var audioVideo = true;
1506 |
1507 | var binaryBlob = false;
1508 | var sctp = false;
1509 | var onnegotiationneeded = !!window.webkitRTCPeerConnection;
1510 |
1511 | var pc, dc;
1512 | try {
1513 | pc = new RTCPeerConnection(defaultConfig, {optional: [{RtpDataChannels: true}]});
1514 | } catch (e) {
1515 | data = false;
1516 | audioVideo = false;
1517 | }
1518 |
1519 | if (data) {
1520 | try {
1521 | dc = pc.createDataChannel('_PEERJSTEST');
1522 | } catch (e) {
1523 | data = false;
1524 | }
1525 | }
1526 |
1527 | if (data) {
1528 | // Binary test
1529 | try {
1530 | dc.binaryType = 'blob';
1531 | binaryBlob = true;
1532 | } catch (e) {
1533 | }
1534 |
1535 | // Reliable test.
1536 | // Unfortunately Chrome is a bit unreliable about whether or not they
1537 | // support reliable.
1538 | var reliablePC = new RTCPeerConnection(defaultConfig, {});
1539 | try {
1540 | var reliableDC = reliablePC.createDataChannel('_PEERJSRELIABLETEST', {});
1541 | sctp = reliableDC.reliable;
1542 | } catch (e) {
1543 | }
1544 | reliablePC.close();
1545 | }
1546 |
1547 | // FIXME: not really the best check...
1548 | if (audioVideo) {
1549 | audioVideo = !!pc.addStream;
1550 | }
1551 |
1552 | // FIXME: this is not great because in theory it doesn't work for
1553 | // av-only browsers (?).
1554 | if (!onnegotiationneeded && data) {
1555 | // sync default check.
1556 | var negotiationPC = new RTCPeerConnection(defaultConfig, {optional: [{RtpDataChannels: true}]});
1557 | negotiationPC.onnegotiationneeded = function() {
1558 | onnegotiationneeded = true;
1559 | // async check.
1560 | if (util && util.supports) {
1561 | util.supports.onnegotiationneeded = true;
1562 | }
1563 | };
1564 | negotiationPC.createDataChannel('_PEERJSNEGOTIATIONTEST');
1565 |
1566 | setTimeout(function() {
1567 | negotiationPC.close();
1568 | }, 1000);
1569 | }
1570 |
1571 | if (pc) {
1572 | pc.close();
1573 | }
1574 |
1575 | return {
1576 | audioVideo: audioVideo,
1577 | data: data,
1578 | binaryBlob: binaryBlob,
1579 | binary: sctp, // deprecated; sctp implies binary support.
1580 | reliable: sctp, // deprecated; sctp implies reliable data.
1581 | sctp: sctp,
1582 | onnegotiationneeded: onnegotiationneeded
1583 | };
1584 | }()),
1585 | //
1586 |
1587 | // Ensure alphanumeric ids
1588 | validateId: function(id) {
1589 | // Allow empty ids
1590 | return !id || /^[A-Za-z0-9_-]+(?:[ _-][A-Za-z0-9]+)*$/.exec(id);
1591 | },
1592 |
1593 | validateKey: function(key) {
1594 | // Allow empty keys
1595 | return !key || /^[A-Za-z0-9_-]+(?:[ _-][A-Za-z0-9]+)*$/.exec(key);
1596 | },
1597 |
1598 |
1599 | debug: false,
1600 |
1601 | inherits: function(ctor, superCtor) {
1602 | ctor.super_ = superCtor;
1603 | ctor.prototype = Object.create(superCtor.prototype, {
1604 | constructor: {
1605 | value: ctor,
1606 | enumerable: false,
1607 | writable: true,
1608 | configurable: true
1609 | }
1610 | });
1611 | },
1612 | extend: function(dest, source) {
1613 | for(var key in source) {
1614 | if(source.hasOwnProperty(key)) {
1615 | dest[key] = source[key];
1616 | }
1617 | }
1618 | return dest;
1619 | },
1620 | pack: BinaryPack.pack,
1621 | unpack: BinaryPack.unpack,
1622 |
1623 | log: function () {
1624 | if (util.debug) {
1625 | var err = false;
1626 | var copy = Array.prototype.slice.call(arguments);
1627 | copy.unshift('PeerJS: ');
1628 | for (var i = 0, l = copy.length; i < l; i++){
1629 | if (copy[i] instanceof Error) {
1630 | copy[i] = '(' + copy[i].name + ') ' + copy[i].message;
1631 | err = true;
1632 | }
1633 | }
1634 | err ? console.error.apply(console, copy) : console.log.apply(console, copy);
1635 | }
1636 | },
1637 |
1638 | setZeroTimeout: (function(global) {
1639 | var timeouts = [];
1640 | var messageName = 'zero-timeout-message';
1641 |
1642 | // Like setTimeout, but only takes a function argument. There's
1643 | // no time argument (always zero) and no arguments (you have to
1644 | // use a closure).
1645 | function setZeroTimeoutPostMessage(fn) {
1646 | timeouts.push(fn);
1647 | global.postMessage(messageName, '*');
1648 | }
1649 |
1650 | function handleMessage(event) {
1651 | if (event.source == global && event.data == messageName) {
1652 | if (event.stopPropagation) {
1653 | event.stopPropagation();
1654 | }
1655 | if (timeouts.length) {
1656 | timeouts.shift()();
1657 | }
1658 | }
1659 | }
1660 | if (global.addEventListener) {
1661 | global.addEventListener('message', handleMessage, true);
1662 | } else if (global.attachEvent) {
1663 | global.attachEvent('onmessage', handleMessage);
1664 | }
1665 | return setZeroTimeoutPostMessage;
1666 | }(window)),
1667 |
1668 | // Binary stuff
1669 |
1670 | // chunks a blob.
1671 | chunk: function(bl) {
1672 | var chunks = [];
1673 | var size = bl.size;
1674 | var start = index = 0;
1675 | var total = Math.ceil(size / util.chunkedMTU);
1676 | while (start < size) {
1677 | var end = Math.min(size, start + util.chunkedMTU);
1678 | var b = bl.slice(start, end);
1679 |
1680 | var chunk = {
1681 | __peerData: dataCount,
1682 | n: index,
1683 | data: b,
1684 | total: total
1685 | };
1686 |
1687 | chunks.push(chunk);
1688 |
1689 | start = end;
1690 | index += 1;
1691 | }
1692 | dataCount += 1;
1693 | return chunks;
1694 | },
1695 |
1696 | blobToArrayBuffer: function(blob, cb){
1697 | var fr = new FileReader();
1698 | fr.onload = function(evt) {
1699 | cb(evt.target.result);
1700 | };
1701 | fr.readAsArrayBuffer(blob);
1702 | },
1703 | blobToBinaryString: function(blob, cb){
1704 | var fr = new FileReader();
1705 | fr.onload = function(evt) {
1706 | cb(evt.target.result);
1707 | };
1708 | fr.readAsBinaryString(blob);
1709 | },
1710 | binaryStringToArrayBuffer: function(binary) {
1711 | var byteArray = new Uint8Array(binary.length);
1712 | for (var i = 0; i < binary.length; i++) {
1713 | byteArray[i] = binary.charCodeAt(i) & 0xff;
1714 | }
1715 | return byteArray.buffer;
1716 | },
1717 | randomToken: function () {
1718 | return Math.random().toString(36).substr(2);
1719 | },
1720 | //
1721 |
1722 | isSecure: function() {
1723 | return location.protocol === 'https:';
1724 | }
1725 | };
1726 |
1727 | module.exports = util;
1728 |
1729 | },{"./adapter":1,"js-binarypack":10}],9:[function(require,module,exports){
1730 | 'use strict';
1731 |
1732 | /**
1733 | * Representation of a single EventEmitter function.
1734 | *
1735 | * @param {Function} fn Event handler to be called.
1736 | * @param {Mixed} context Context for function execution.
1737 | * @param {Boolean} once Only emit once
1738 | * @api private
1739 | */
1740 | function EE(fn, context, once) {
1741 | this.fn = fn;
1742 | this.context = context;
1743 | this.once = once || false;
1744 | }
1745 |
1746 | /**
1747 | * Minimal EventEmitter interface that is molded against the Node.js
1748 | * EventEmitter interface.
1749 | *
1750 | * @constructor
1751 | * @api public
1752 | */
1753 | function EventEmitter() { /* Nothing to set */ }
1754 |
1755 | /**
1756 | * Holds the assigned EventEmitters by name.
1757 | *
1758 | * @type {Object}
1759 | * @private
1760 | */
1761 | EventEmitter.prototype._events = undefined;
1762 |
1763 | /**
1764 | * Return a list of assigned event listeners.
1765 | *
1766 | * @param {String} event The events that should be listed.
1767 | * @returns {Array}
1768 | * @api public
1769 | */
1770 | EventEmitter.prototype.listeners = function listeners(event) {
1771 | if (!this._events || !this._events[event]) return [];
1772 |
1773 | for (var i = 0, l = this._events[event].length, ee = []; i < l; i++) {
1774 | ee.push(this._events[event][i].fn);
1775 | }
1776 |
1777 | return ee;
1778 | };
1779 |
1780 | /**
1781 | * Emit an event to all registered event listeners.
1782 | *
1783 | * @param {String} event The name of the event.
1784 | * @returns {Boolean} Indication if we've emitted an event.
1785 | * @api public
1786 | */
1787 | EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
1788 | if (!this._events || !this._events[event]) return false;
1789 |
1790 | var listeners = this._events[event]
1791 | , length = listeners.length
1792 | , len = arguments.length
1793 | , ee = listeners[0]
1794 | , args
1795 | , i, j;
1796 |
1797 | if (1 === length) {
1798 | if (ee.once) this.removeListener(event, ee.fn, true);
1799 |
1800 | switch (len) {
1801 | case 1: return ee.fn.call(ee.context), true;
1802 | case 2: return ee.fn.call(ee.context, a1), true;
1803 | case 3: return ee.fn.call(ee.context, a1, a2), true;
1804 | case 4: return ee.fn.call(ee.context, a1, a2, a3), true;
1805 | case 5: return ee.fn.call(ee.context, a1, a2, a3, a4), true;
1806 | case 6: return ee.fn.call(ee.context, a1, a2, a3, a4, a5), true;
1807 | }
1808 |
1809 | for (i = 1, args = new Array(len -1); i < len; i++) {
1810 | args[i - 1] = arguments[i];
1811 | }
1812 |
1813 | ee.fn.apply(ee.context, args);
1814 | } else {
1815 | for (i = 0; i < length; i++) {
1816 | if (listeners[i].once) this.removeListener(event, listeners[i].fn, true);
1817 |
1818 | switch (len) {
1819 | case 1: listeners[i].fn.call(listeners[i].context); break;
1820 | case 2: listeners[i].fn.call(listeners[i].context, a1); break;
1821 | case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
1822 | default:
1823 | if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
1824 | args[j - 1] = arguments[j];
1825 | }
1826 |
1827 | listeners[i].fn.apply(listeners[i].context, args);
1828 | }
1829 | }
1830 | }
1831 |
1832 | return true;
1833 | };
1834 |
1835 | /**
1836 | * Register a new EventListener for the given event.
1837 | *
1838 | * @param {String} event Name of the event.
1839 | * @param {Functon} fn Callback function.
1840 | * @param {Mixed} context The context of the function.
1841 | * @api public
1842 | */
1843 | EventEmitter.prototype.on = function on(event, fn, context) {
1844 | if (!this._events) this._events = {};
1845 | if (!this._events[event]) this._events[event] = [];
1846 | this._events[event].push(new EE( fn, context || this ));
1847 |
1848 | return this;
1849 | };
1850 |
1851 | /**
1852 | * Add an EventListener that's only called once.
1853 | *
1854 | * @param {String} event Name of the event.
1855 | * @param {Function} fn Callback function.
1856 | * @param {Mixed} context The context of the function.
1857 | * @api public
1858 | */
1859 | EventEmitter.prototype.once = function once(event, fn, context) {
1860 | if (!this._events) this._events = {};
1861 | if (!this._events[event]) this._events[event] = [];
1862 | this._events[event].push(new EE(fn, context || this, true ));
1863 |
1864 | return this;
1865 | };
1866 |
1867 | /**
1868 | * Remove event listeners.
1869 | *
1870 | * @param {String} event The event we want to remove.
1871 | * @param {Function} fn The listener that we need to find.
1872 | * @param {Boolean} once Only remove once listeners.
1873 | * @api public
1874 | */
1875 | EventEmitter.prototype.removeListener = function removeListener(event, fn, once) {
1876 | if (!this._events || !this._events[event]) return this;
1877 |
1878 | var listeners = this._events[event]
1879 | , events = [];
1880 |
1881 | if (fn) for (var i = 0, length = listeners.length; i < length; i++) {
1882 | if (listeners[i].fn !== fn && listeners[i].once !== once) {
1883 | events.push(listeners[i]);
1884 | }
1885 | }
1886 |
1887 | //
1888 | // Reset the array, or remove it completely if we have no more listeners.
1889 | //
1890 | if (events.length) this._events[event] = events;
1891 | else this._events[event] = null;
1892 |
1893 | return this;
1894 | };
1895 |
1896 | /**
1897 | * Remove all listeners or only the listeners for the specified event.
1898 | *
1899 | * @param {String} event The event want to remove all listeners for.
1900 | * @api public
1901 | */
1902 | EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
1903 | if (!this._events) return this;
1904 |
1905 | if (event) this._events[event] = null;
1906 | else this._events = {};
1907 |
1908 | return this;
1909 | };
1910 |
1911 | //
1912 | // Alias methods names because people roll like that.
1913 | //
1914 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
1915 | EventEmitter.prototype.addListener = EventEmitter.prototype.on;
1916 |
1917 | //
1918 | // This function doesn't apply anymore.
1919 | //
1920 | EventEmitter.prototype.setMaxListeners = function setMaxListeners() {
1921 | return this;
1922 | };
1923 |
1924 | //
1925 | // Expose the module.
1926 | //
1927 | EventEmitter.EventEmitter = EventEmitter;
1928 | EventEmitter.EventEmitter2 = EventEmitter;
1929 | EventEmitter.EventEmitter3 = EventEmitter;
1930 |
1931 | if ('object' === typeof module && module.exports) {
1932 | module.exports = EventEmitter;
1933 | }
1934 |
1935 | },{}],10:[function(require,module,exports){
1936 | var BufferBuilder = require('./bufferbuilder').BufferBuilder;
1937 | var binaryFeatures = require('./bufferbuilder').binaryFeatures;
1938 |
1939 | var BinaryPack = {
1940 | unpack: function(data){
1941 | var unpacker = new Unpacker(data);
1942 | return unpacker.unpack();
1943 | },
1944 | pack: function(data){
1945 | var packer = new Packer();
1946 | packer.pack(data);
1947 | var buffer = packer.getBuffer();
1948 | return buffer;
1949 | }
1950 | };
1951 |
1952 | module.exports = BinaryPack;
1953 |
1954 | function Unpacker (data){
1955 | // Data is ArrayBuffer
1956 | this.index = 0;
1957 | this.dataBuffer = data;
1958 | this.dataView = new Uint8Array(this.dataBuffer);
1959 | this.length = this.dataBuffer.byteLength;
1960 | }
1961 |
1962 | Unpacker.prototype.unpack = function(){
1963 | var type = this.unpack_uint8();
1964 | if (type < 0x80){
1965 | var positive_fixnum = type;
1966 | return positive_fixnum;
1967 | } else if ((type ^ 0xe0) < 0x20){
1968 | var negative_fixnum = (type ^ 0xe0) - 0x20;
1969 | return negative_fixnum;
1970 | }
1971 | var size;
1972 | if ((size = type ^ 0xa0) <= 0x0f){
1973 | return this.unpack_raw(size);
1974 | } else if ((size = type ^ 0xb0) <= 0x0f){
1975 | return this.unpack_string(size);
1976 | } else if ((size = type ^ 0x90) <= 0x0f){
1977 | return this.unpack_array(size);
1978 | } else if ((size = type ^ 0x80) <= 0x0f){
1979 | return this.unpack_map(size);
1980 | }
1981 | switch(type){
1982 | case 0xc0:
1983 | return null;
1984 | case 0xc1:
1985 | return undefined;
1986 | case 0xc2:
1987 | return false;
1988 | case 0xc3:
1989 | return true;
1990 | case 0xca:
1991 | return this.unpack_float();
1992 | case 0xcb:
1993 | return this.unpack_double();
1994 | case 0xcc:
1995 | return this.unpack_uint8();
1996 | case 0xcd:
1997 | return this.unpack_uint16();
1998 | case 0xce:
1999 | return this.unpack_uint32();
2000 | case 0xcf:
2001 | return this.unpack_uint64();
2002 | case 0xd0:
2003 | return this.unpack_int8();
2004 | case 0xd1:
2005 | return this.unpack_int16();
2006 | case 0xd2:
2007 | return this.unpack_int32();
2008 | case 0xd3:
2009 | return this.unpack_int64();
2010 | case 0xd4:
2011 | return undefined;
2012 | case 0xd5:
2013 | return undefined;
2014 | case 0xd6:
2015 | return undefined;
2016 | case 0xd7:
2017 | return undefined;
2018 | case 0xd8:
2019 | size = this.unpack_uint16();
2020 | return this.unpack_string(size);
2021 | case 0xd9:
2022 | size = this.unpack_uint32();
2023 | return this.unpack_string(size);
2024 | case 0xda:
2025 | size = this.unpack_uint16();
2026 | return this.unpack_raw(size);
2027 | case 0xdb:
2028 | size = this.unpack_uint32();
2029 | return this.unpack_raw(size);
2030 | case 0xdc:
2031 | size = this.unpack_uint16();
2032 | return this.unpack_array(size);
2033 | case 0xdd:
2034 | size = this.unpack_uint32();
2035 | return this.unpack_array(size);
2036 | case 0xde:
2037 | size = this.unpack_uint16();
2038 | return this.unpack_map(size);
2039 | case 0xdf:
2040 | size = this.unpack_uint32();
2041 | return this.unpack_map(size);
2042 | }
2043 | }
2044 |
2045 | Unpacker.prototype.unpack_uint8 = function(){
2046 | var byte = this.dataView[this.index] & 0xff;
2047 | this.index++;
2048 | return byte;
2049 | };
2050 |
2051 | Unpacker.prototype.unpack_uint16 = function(){
2052 | var bytes = this.read(2);
2053 | var uint16 =
2054 | ((bytes[0] & 0xff) * 256) + (bytes[1] & 0xff);
2055 | this.index += 2;
2056 | return uint16;
2057 | }
2058 |
2059 | Unpacker.prototype.unpack_uint32 = function(){
2060 | var bytes = this.read(4);
2061 | var uint32 =
2062 | ((bytes[0] * 256 +
2063 | bytes[1]) * 256 +
2064 | bytes[2]) * 256 +
2065 | bytes[3];
2066 | this.index += 4;
2067 | return uint32;
2068 | }
2069 |
2070 | Unpacker.prototype.unpack_uint64 = function(){
2071 | var bytes = this.read(8);
2072 | var uint64 =
2073 | ((((((bytes[0] * 256 +
2074 | bytes[1]) * 256 +
2075 | bytes[2]) * 256 +
2076 | bytes[3]) * 256 +
2077 | bytes[4]) * 256 +
2078 | bytes[5]) * 256 +
2079 | bytes[6]) * 256 +
2080 | bytes[7];
2081 | this.index += 8;
2082 | return uint64;
2083 | }
2084 |
2085 |
2086 | Unpacker.prototype.unpack_int8 = function(){
2087 | var uint8 = this.unpack_uint8();
2088 | return (uint8 < 0x80 ) ? uint8 : uint8 - (1 << 8);
2089 | };
2090 |
2091 | Unpacker.prototype.unpack_int16 = function(){
2092 | var uint16 = this.unpack_uint16();
2093 | return (uint16 < 0x8000 ) ? uint16 : uint16 - (1 << 16);
2094 | }
2095 |
2096 | Unpacker.prototype.unpack_int32 = function(){
2097 | var uint32 = this.unpack_uint32();
2098 | return (uint32 < Math.pow(2, 31) ) ? uint32 :
2099 | uint32 - Math.pow(2, 32);
2100 | }
2101 |
2102 | Unpacker.prototype.unpack_int64 = function(){
2103 | var uint64 = this.unpack_uint64();
2104 | return (uint64 < Math.pow(2, 63) ) ? uint64 :
2105 | uint64 - Math.pow(2, 64);
2106 | }
2107 |
2108 | Unpacker.prototype.unpack_raw = function(size){
2109 | if ( this.length < this.index + size){
2110 | throw new Error('BinaryPackFailure: index is out of range'
2111 | + ' ' + this.index + ' ' + size + ' ' + this.length);
2112 | }
2113 | var buf = this.dataBuffer.slice(this.index, this.index + size);
2114 | this.index += size;
2115 |
2116 | //buf = util.bufferToString(buf);
2117 |
2118 | return buf;
2119 | }
2120 |
2121 | Unpacker.prototype.unpack_string = function(size){
2122 | var bytes = this.read(size);
2123 | var i = 0, str = '', c, code;
2124 | while(i < size){
2125 | c = bytes[i];
2126 | if ( c < 128){
2127 | str += String.fromCharCode(c);
2128 | i++;
2129 | } else if ((c ^ 0xc0) < 32){
2130 | code = ((c ^ 0xc0) << 6) | (bytes[i+1] & 63);
2131 | str += String.fromCharCode(code);
2132 | i += 2;
2133 | } else {
2134 | code = ((c & 15) << 12) | ((bytes[i+1] & 63) << 6) |
2135 | (bytes[i+2] & 63);
2136 | str += String.fromCharCode(code);
2137 | i += 3;
2138 | }
2139 | }
2140 | this.index += size;
2141 | return str;
2142 | }
2143 |
2144 | Unpacker.prototype.unpack_array = function(size){
2145 | var objects = new Array(size);
2146 | for(var i = 0; i < size ; i++){
2147 | objects[i] = this.unpack();
2148 | }
2149 | return objects;
2150 | }
2151 |
2152 | Unpacker.prototype.unpack_map = function(size){
2153 | var map = {};
2154 | for(var i = 0; i < size ; i++){
2155 | var key = this.unpack();
2156 | var value = this.unpack();
2157 | map[key] = value;
2158 | }
2159 | return map;
2160 | }
2161 |
2162 | Unpacker.prototype.unpack_float = function(){
2163 | var uint32 = this.unpack_uint32();
2164 | var sign = uint32 >> 31;
2165 | var exp = ((uint32 >> 23) & 0xff) - 127;
2166 | var fraction = ( uint32 & 0x7fffff ) | 0x800000;
2167 | return (sign == 0 ? 1 : -1) *
2168 | fraction * Math.pow(2, exp - 23);
2169 | }
2170 |
2171 | Unpacker.prototype.unpack_double = function(){
2172 | var h32 = this.unpack_uint32();
2173 | var l32 = this.unpack_uint32();
2174 | var sign = h32 >> 31;
2175 | var exp = ((h32 >> 20) & 0x7ff) - 1023;
2176 | var hfrac = ( h32 & 0xfffff ) | 0x100000;
2177 | var frac = hfrac * Math.pow(2, exp - 20) +
2178 | l32 * Math.pow(2, exp - 52);
2179 | return (sign == 0 ? 1 : -1) * frac;
2180 | }
2181 |
2182 | Unpacker.prototype.read = function(length){
2183 | var j = this.index;
2184 | if (j + length <= this.length) {
2185 | return this.dataView.subarray(j, j + length);
2186 | } else {
2187 | throw new Error('BinaryPackFailure: read index out of range');
2188 | }
2189 | }
2190 |
2191 | function Packer(){
2192 | this.bufferBuilder = new BufferBuilder();
2193 | }
2194 |
2195 | Packer.prototype.getBuffer = function(){
2196 | return this.bufferBuilder.getBuffer();
2197 | }
2198 |
2199 | Packer.prototype.pack = function(value){
2200 | var type = typeof(value);
2201 | if (type == 'string'){
2202 | this.pack_string(value);
2203 | } else if (type == 'number'){
2204 | if (Math.floor(value) === value){
2205 | this.pack_integer(value);
2206 | } else{
2207 | this.pack_double(value);
2208 | }
2209 | } else if (type == 'boolean'){
2210 | if (value === true){
2211 | this.bufferBuilder.append(0xc3);
2212 | } else if (value === false){
2213 | this.bufferBuilder.append(0xc2);
2214 | }
2215 | } else if (type == 'undefined'){
2216 | this.bufferBuilder.append(0xc0);
2217 | } else if (type == 'object'){
2218 | if (value === null){
2219 | this.bufferBuilder.append(0xc0);
2220 | } else {
2221 | var constructor = value.constructor;
2222 | if (constructor == Array){
2223 | this.pack_array(value);
2224 | } else if (constructor == Blob || constructor == File) {
2225 | this.pack_bin(value);
2226 | } else if (constructor == ArrayBuffer) {
2227 | if(binaryFeatures.useArrayBufferView) {
2228 | this.pack_bin(new Uint8Array(value));
2229 | } else {
2230 | this.pack_bin(value);
2231 | }
2232 | } else if ('BYTES_PER_ELEMENT' in value){
2233 | if(binaryFeatures.useArrayBufferView) {
2234 | this.pack_bin(new Uint8Array(value.buffer));
2235 | } else {
2236 | this.pack_bin(value.buffer);
2237 | }
2238 | } else if (constructor == Object){
2239 | this.pack_object(value);
2240 | } else if (constructor == Date){
2241 | this.pack_string(value.toString());
2242 | } else if (typeof value.toBinaryPack == 'function'){
2243 | this.bufferBuilder.append(value.toBinaryPack());
2244 | } else {
2245 | throw new Error('Type "' + constructor.toString() + '" not yet supported');
2246 | }
2247 | }
2248 | } else {
2249 | throw new Error('Type "' + type + '" not yet supported');
2250 | }
2251 | this.bufferBuilder.flush();
2252 | }
2253 |
2254 |
2255 | Packer.prototype.pack_bin = function(blob){
2256 | var length = blob.length || blob.byteLength || blob.size;
2257 | if (length <= 0x0f){
2258 | this.pack_uint8(0xa0 + length);
2259 | } else if (length <= 0xffff){
2260 | this.bufferBuilder.append(0xda) ;
2261 | this.pack_uint16(length);
2262 | } else if (length <= 0xffffffff){
2263 | this.bufferBuilder.append(0xdb);
2264 | this.pack_uint32(length);
2265 | } else{
2266 | throw new Error('Invalid length');
2267 | }
2268 | this.bufferBuilder.append(blob);
2269 | }
2270 |
2271 | Packer.prototype.pack_string = function(str){
2272 | var length = utf8Length(str);
2273 |
2274 | if (length <= 0x0f){
2275 | this.pack_uint8(0xb0 + length);
2276 | } else if (length <= 0xffff){
2277 | this.bufferBuilder.append(0xd8) ;
2278 | this.pack_uint16(length);
2279 | } else if (length <= 0xffffffff){
2280 | this.bufferBuilder.append(0xd9);
2281 | this.pack_uint32(length);
2282 | } else{
2283 | throw new Error('Invalid length');
2284 | }
2285 | this.bufferBuilder.append(str);
2286 | }
2287 |
2288 | Packer.prototype.pack_array = function(ary){
2289 | var length = ary.length;
2290 | if (length <= 0x0f){
2291 | this.pack_uint8(0x90 + length);
2292 | } else if (length <= 0xffff){
2293 | this.bufferBuilder.append(0xdc)
2294 | this.pack_uint16(length);
2295 | } else if (length <= 0xffffffff){
2296 | this.bufferBuilder.append(0xdd);
2297 | this.pack_uint32(length);
2298 | } else{
2299 | throw new Error('Invalid length');
2300 | }
2301 | for(var i = 0; i < length ; i++){
2302 | this.pack(ary[i]);
2303 | }
2304 | }
2305 |
2306 | Packer.prototype.pack_integer = function(num){
2307 | if ( -0x20 <= num && num <= 0x7f){
2308 | this.bufferBuilder.append(num & 0xff);
2309 | } else if (0x00 <= num && num <= 0xff){
2310 | this.bufferBuilder.append(0xcc);
2311 | this.pack_uint8(num);
2312 | } else if (-0x80 <= num && num <= 0x7f){
2313 | this.bufferBuilder.append(0xd0);
2314 | this.pack_int8(num);
2315 | } else if ( 0x0000 <= num && num <= 0xffff){
2316 | this.bufferBuilder.append(0xcd);
2317 | this.pack_uint16(num);
2318 | } else if (-0x8000 <= num && num <= 0x7fff){
2319 | this.bufferBuilder.append(0xd1);
2320 | this.pack_int16(num);
2321 | } else if ( 0x00000000 <= num && num <= 0xffffffff){
2322 | this.bufferBuilder.append(0xce);
2323 | this.pack_uint32(num);
2324 | } else if (-0x80000000 <= num && num <= 0x7fffffff){
2325 | this.bufferBuilder.append(0xd2);
2326 | this.pack_int32(num);
2327 | } else if (-0x8000000000000000 <= num && num <= 0x7FFFFFFFFFFFFFFF){
2328 | this.bufferBuilder.append(0xd3);
2329 | this.pack_int64(num);
2330 | } else if (0x0000000000000000 <= num && num <= 0xFFFFFFFFFFFFFFFF){
2331 | this.bufferBuilder.append(0xcf);
2332 | this.pack_uint64(num);
2333 | } else{
2334 | throw new Error('Invalid integer');
2335 | }
2336 | }
2337 |
2338 | Packer.prototype.pack_double = function(num){
2339 | var sign = 0;
2340 | if (num < 0){
2341 | sign = 1;
2342 | num = -num;
2343 | }
2344 | var exp = Math.floor(Math.log(num) / Math.LN2);
2345 | var frac0 = num / Math.pow(2, exp) - 1;
2346 | var frac1 = Math.floor(frac0 * Math.pow(2, 52));
2347 | var b32 = Math.pow(2, 32);
2348 | var h32 = (sign << 31) | ((exp+1023) << 20) |
2349 | (frac1 / b32) & 0x0fffff;
2350 | var l32 = frac1 % b32;
2351 | this.bufferBuilder.append(0xcb);
2352 | this.pack_int32(h32);
2353 | this.pack_int32(l32);
2354 | }
2355 |
2356 | Packer.prototype.pack_object = function(obj){
2357 | var keys = Object.keys(obj);
2358 | var length = keys.length;
2359 | if (length <= 0x0f){
2360 | this.pack_uint8(0x80 + length);
2361 | } else if (length <= 0xffff){
2362 | this.bufferBuilder.append(0xde);
2363 | this.pack_uint16(length);
2364 | } else if (length <= 0xffffffff){
2365 | this.bufferBuilder.append(0xdf);
2366 | this.pack_uint32(length);
2367 | } else{
2368 | throw new Error('Invalid length');
2369 | }
2370 | for(var prop in obj){
2371 | if (obj.hasOwnProperty(prop)){
2372 | this.pack(prop);
2373 | this.pack(obj[prop]);
2374 | }
2375 | }
2376 | }
2377 |
2378 | Packer.prototype.pack_uint8 = function(num){
2379 | this.bufferBuilder.append(num);
2380 | }
2381 |
2382 | Packer.prototype.pack_uint16 = function(num){
2383 | this.bufferBuilder.append(num >> 8);
2384 | this.bufferBuilder.append(num & 0xff);
2385 | }
2386 |
2387 | Packer.prototype.pack_uint32 = function(num){
2388 | var n = num & 0xffffffff;
2389 | this.bufferBuilder.append((n & 0xff000000) >>> 24);
2390 | this.bufferBuilder.append((n & 0x00ff0000) >>> 16);
2391 | this.bufferBuilder.append((n & 0x0000ff00) >>> 8);
2392 | this.bufferBuilder.append((n & 0x000000ff));
2393 | }
2394 |
2395 | Packer.prototype.pack_uint64 = function(num){
2396 | var high = num / Math.pow(2, 32);
2397 | var low = num % Math.pow(2, 32);
2398 | this.bufferBuilder.append((high & 0xff000000) >>> 24);
2399 | this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
2400 | this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
2401 | this.bufferBuilder.append((high & 0x000000ff));
2402 | this.bufferBuilder.append((low & 0xff000000) >>> 24);
2403 | this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
2404 | this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
2405 | this.bufferBuilder.append((low & 0x000000ff));
2406 | }
2407 |
2408 | Packer.prototype.pack_int8 = function(num){
2409 | this.bufferBuilder.append(num & 0xff);
2410 | }
2411 |
2412 | Packer.prototype.pack_int16 = function(num){
2413 | this.bufferBuilder.append((num & 0xff00) >> 8);
2414 | this.bufferBuilder.append(num & 0xff);
2415 | }
2416 |
2417 | Packer.prototype.pack_int32 = function(num){
2418 | this.bufferBuilder.append((num >>> 24) & 0xff);
2419 | this.bufferBuilder.append((num & 0x00ff0000) >>> 16);
2420 | this.bufferBuilder.append((num & 0x0000ff00) >>> 8);
2421 | this.bufferBuilder.append((num & 0x000000ff));
2422 | }
2423 |
2424 | Packer.prototype.pack_int64 = function(num){
2425 | var high = Math.floor(num / Math.pow(2, 32));
2426 | var low = num % Math.pow(2, 32);
2427 | this.bufferBuilder.append((high & 0xff000000) >>> 24);
2428 | this.bufferBuilder.append((high & 0x00ff0000) >>> 16);
2429 | this.bufferBuilder.append((high & 0x0000ff00) >>> 8);
2430 | this.bufferBuilder.append((high & 0x000000ff));
2431 | this.bufferBuilder.append((low & 0xff000000) >>> 24);
2432 | this.bufferBuilder.append((low & 0x00ff0000) >>> 16);
2433 | this.bufferBuilder.append((low & 0x0000ff00) >>> 8);
2434 | this.bufferBuilder.append((low & 0x000000ff));
2435 | }
2436 |
2437 | function _utf8Replace(m){
2438 | var code = m.charCodeAt(0);
2439 |
2440 | if(code <= 0x7ff) return '00';
2441 | if(code <= 0xffff) return '000';
2442 | if(code <= 0x1fffff) return '0000';
2443 | if(code <= 0x3ffffff) return '00000';
2444 | return '000000';
2445 | }
2446 |
2447 | function utf8Length(str){
2448 | if (str.length > 600) {
2449 | // Blob method faster for large strings
2450 | return (new Blob([str])).size;
2451 | } else {
2452 | return str.replace(/[^\u0000-\u007F]/g, _utf8Replace).length;
2453 | }
2454 | }
2455 |
2456 | },{"./bufferbuilder":11}],11:[function(require,module,exports){
2457 | var binaryFeatures = {};
2458 | binaryFeatures.useBlobBuilder = (function(){
2459 | try {
2460 | new Blob([]);
2461 | return false;
2462 | } catch (e) {
2463 | return true;
2464 | }
2465 | })();
2466 |
2467 | binaryFeatures.useArrayBufferView = !binaryFeatures.useBlobBuilder && (function(){
2468 | try {
2469 | return (new Blob([new Uint8Array([])])).size === 0;
2470 | } catch (e) {
2471 | return true;
2472 | }
2473 | })();
2474 |
2475 | module.exports.binaryFeatures = binaryFeatures;
2476 | var BlobBuilder = module.exports.BlobBuilder;
2477 | if (typeof window != 'undefined') {
2478 | BlobBuilder = module.exports.BlobBuilder = window.WebKitBlobBuilder ||
2479 | window.MozBlobBuilder || window.MSBlobBuilder || window.BlobBuilder;
2480 | }
2481 |
2482 | function BufferBuilder(){
2483 | this._pieces = [];
2484 | this._parts = [];
2485 | }
2486 |
2487 | BufferBuilder.prototype.append = function(data) {
2488 | if(typeof data === 'number') {
2489 | this._pieces.push(data);
2490 | } else {
2491 | this.flush();
2492 | this._parts.push(data);
2493 | }
2494 | };
2495 |
2496 | BufferBuilder.prototype.flush = function() {
2497 | if (this._pieces.length > 0) {
2498 | var buf = new Uint8Array(this._pieces);
2499 | if(!binaryFeatures.useArrayBufferView) {
2500 | buf = buf.buffer;
2501 | }
2502 | this._parts.push(buf);
2503 | this._pieces = [];
2504 | }
2505 | };
2506 |
2507 | BufferBuilder.prototype.getBuffer = function() {
2508 | this.flush();
2509 | if(binaryFeatures.useBlobBuilder) {
2510 | var builder = new BlobBuilder();
2511 | for(var i = 0, ii = this._parts.length; i < ii; i++) {
2512 | builder.append(this._parts[i]);
2513 | }
2514 | return builder.getBlob();
2515 | } else {
2516 | return new Blob(this._parts);
2517 | }
2518 | };
2519 |
2520 | module.exports.BufferBuilder = BufferBuilder;
2521 |
2522 | },{}],12:[function(require,module,exports){
2523 | var util = require('./util');
2524 |
2525 | /**
2526 | * Reliable transfer for Chrome Canary DataChannel impl.
2527 | * Author: @michellebu
2528 | */
2529 | function Reliable(dc, debug) {
2530 | if (!(this instanceof Reliable)) return new Reliable(dc);
2531 | this._dc = dc;
2532 |
2533 | util.debug = debug;
2534 |
2535 | // Messages sent/received so far.
2536 | // id: { ack: n, chunks: [...] }
2537 | this._outgoing = {};
2538 | // id: { ack: ['ack', id, n], chunks: [...] }
2539 | this._incoming = {};
2540 | this._received = {};
2541 |
2542 | // Window size.
2543 | this._window = 1000;
2544 | // MTU.
2545 | this._mtu = 500;
2546 | // Interval for setInterval. In ms.
2547 | this._interval = 0;
2548 |
2549 | // Messages sent.
2550 | this._count = 0;
2551 |
2552 | // Outgoing message queue.
2553 | this._queue = [];
2554 |
2555 | this._setupDC();
2556 | };
2557 |
2558 | // Send a message reliably.
2559 | Reliable.prototype.send = function(msg) {
2560 | // Determine if chunking is necessary.
2561 | var bl = util.pack(msg);
2562 | if (bl.size < this._mtu) {
2563 | this._handleSend(['no', bl]);
2564 | return;
2565 | }
2566 |
2567 | this._outgoing[this._count] = {
2568 | ack: 0,
2569 | chunks: this._chunk(bl)
2570 | };
2571 |
2572 | if (util.debug) {
2573 | this._outgoing[this._count].timer = new Date();
2574 | }
2575 |
2576 | // Send prelim window.
2577 | this._sendWindowedChunks(this._count);
2578 | this._count += 1;
2579 | };
2580 |
2581 | // Set up interval for processing queue.
2582 | Reliable.prototype._setupInterval = function() {
2583 | // TODO: fail gracefully.
2584 |
2585 | var self = this;
2586 | this._timeout = setInterval(function() {
2587 | // FIXME: String stuff makes things terribly async.
2588 | var msg = self._queue.shift();
2589 | if (msg._multiple) {
2590 | for (var i = 0, ii = msg.length; i < ii; i += 1) {
2591 | self._intervalSend(msg[i]);
2592 | }
2593 | } else {
2594 | self._intervalSend(msg);
2595 | }
2596 | }, this._interval);
2597 | };
2598 |
2599 | Reliable.prototype._intervalSend = function(msg) {
2600 | var self = this;
2601 | msg = util.pack(msg);
2602 | util.blobToBinaryString(msg, function(str) {
2603 | self._dc.send(str);
2604 | });
2605 | if (self._queue.length === 0) {
2606 | clearTimeout(self._timeout);
2607 | self._timeout = null;
2608 | //self._processAcks();
2609 | }
2610 | };
2611 |
2612 | // Go through ACKs to send missing pieces.
2613 | Reliable.prototype._processAcks = function() {
2614 | for (var id in this._outgoing) {
2615 | if (this._outgoing.hasOwnProperty(id)) {
2616 | this._sendWindowedChunks(id);
2617 | }
2618 | }
2619 | };
2620 |
2621 | // Handle sending a message.
2622 | // FIXME: Don't wait for interval time for all messages...
2623 | Reliable.prototype._handleSend = function(msg) {
2624 | var push = true;
2625 | for (var i = 0, ii = this._queue.length; i < ii; i += 1) {
2626 | var item = this._queue[i];
2627 | if (item === msg) {
2628 | push = false;
2629 | } else if (item._multiple && item.indexOf(msg) !== -1) {
2630 | push = false;
2631 | }
2632 | }
2633 | if (push) {
2634 | this._queue.push(msg);
2635 | if (!this._timeout) {
2636 | this._setupInterval();
2637 | }
2638 | }
2639 | };
2640 |
2641 | // Set up DataChannel handlers.
2642 | Reliable.prototype._setupDC = function() {
2643 | // Handle various message types.
2644 | var self = this;
2645 | this._dc.onmessage = function(e) {
2646 | var msg = e.data;
2647 | var datatype = msg.constructor;
2648 | // FIXME: msg is String until binary is supported.
2649 | // Once that happens, this will have to be smarter.
2650 | if (datatype === String) {
2651 | var ab = util.binaryStringToArrayBuffer(msg);
2652 | msg = util.unpack(ab);
2653 | self._handleMessage(msg);
2654 | }
2655 | };
2656 | };
2657 |
2658 | // Handles an incoming message.
2659 | Reliable.prototype._handleMessage = function(msg) {
2660 | var id = msg[1];
2661 | var idata = this._incoming[id];
2662 | var odata = this._outgoing[id];
2663 | var data;
2664 | switch (msg[0]) {
2665 | // No chunking was done.
2666 | case 'no':
2667 | var message = id;
2668 | if (!!message) {
2669 | this.onmessage(util.unpack(message));
2670 | }
2671 | break;
2672 | // Reached the end of the message.
2673 | case 'end':
2674 | data = idata;
2675 |
2676 | // In case end comes first.
2677 | this._received[id] = msg[2];
2678 |
2679 | if (!data) {
2680 | break;
2681 | }
2682 |
2683 | this._ack(id);
2684 | break;
2685 | case 'ack':
2686 | data = odata;
2687 | if (!!data) {
2688 | var ack = msg[2];
2689 | // Take the larger ACK, for out of order messages.
2690 | data.ack = Math.max(ack, data.ack);
2691 |
2692 | // Clean up when all chunks are ACKed.
2693 | if (data.ack >= data.chunks.length) {
2694 | util.log('Time: ', new Date() - data.timer);
2695 | delete this._outgoing[id];
2696 | } else {
2697 | this._processAcks();
2698 | }
2699 | }
2700 | // If !data, just ignore.
2701 | break;
2702 | // Received a chunk of data.
2703 | case 'chunk':
2704 | // Create a new entry if none exists.
2705 | data = idata;
2706 | if (!data) {
2707 | var end = this._received[id];
2708 | if (end === true) {
2709 | break;
2710 | }
2711 | data = {
2712 | ack: ['ack', id, 0],
2713 | chunks: []
2714 | };
2715 | this._incoming[id] = data;
2716 | }
2717 |
2718 | var n = msg[2];
2719 | var chunk = msg[3];
2720 | data.chunks[n] = new Uint8Array(chunk);
2721 |
2722 | // If we get the chunk we're looking for, ACK for next missing.
2723 | // Otherwise, ACK the same N again.
2724 | if (n === data.ack[2]) {
2725 | this._calculateNextAck(id);
2726 | }
2727 | this._ack(id);
2728 | break;
2729 | default:
2730 | // Shouldn't happen, but would make sense for message to just go
2731 | // through as is.
2732 | this._handleSend(msg);
2733 | break;
2734 | }
2735 | };
2736 |
2737 | // Chunks BL into smaller messages.
2738 | Reliable.prototype._chunk = function(bl) {
2739 | var chunks = [];
2740 | var size = bl.size;
2741 | var start = 0;
2742 | while (start < size) {
2743 | var end = Math.min(size, start + this._mtu);
2744 | var b = bl.slice(start, end);
2745 | var chunk = {
2746 | payload: b
2747 | }
2748 | chunks.push(chunk);
2749 | start = end;
2750 | }
2751 | util.log('Created', chunks.length, 'chunks.');
2752 | return chunks;
2753 | };
2754 |
2755 | // Sends ACK N, expecting Nth blob chunk for message ID.
2756 | Reliable.prototype._ack = function(id) {
2757 | var ack = this._incoming[id].ack;
2758 |
2759 | // if ack is the end value, then call _complete.
2760 | if (this._received[id] === ack[2]) {
2761 | this._complete(id);
2762 | this._received[id] = true;
2763 | }
2764 |
2765 | this._handleSend(ack);
2766 | };
2767 |
2768 | // Calculates the next ACK number, given chunks.
2769 | Reliable.prototype._calculateNextAck = function(id) {
2770 | var data = this._incoming[id];
2771 | var chunks = data.chunks;
2772 | for (var i = 0, ii = chunks.length; i < ii; i += 1) {
2773 | // This chunk is missing!!! Better ACK for it.
2774 | if (chunks[i] === undefined) {
2775 | data.ack[2] = i;
2776 | return;
2777 | }
2778 | }
2779 | data.ack[2] = chunks.length;
2780 | };
2781 |
2782 | // Sends the next window of chunks.
2783 | Reliable.prototype._sendWindowedChunks = function(id) {
2784 | util.log('sendWindowedChunks for: ', id);
2785 | var data = this._outgoing[id];
2786 | var ch = data.chunks;
2787 | var chunks = [];
2788 | var limit = Math.min(data.ack + this._window, ch.length);
2789 | for (var i = data.ack; i < limit; i += 1) {
2790 | if (!ch[i].sent || i === data.ack) {
2791 | ch[i].sent = true;
2792 | chunks.push(['chunk', id, i, ch[i].payload]);
2793 | }
2794 | }
2795 | if (data.ack + this._window >= ch.length) {
2796 | chunks.push(['end', id, ch.length])
2797 | }
2798 | chunks._multiple = true;
2799 | this._handleSend(chunks);
2800 | };
2801 |
2802 | // Puts together a message from chunks.
2803 | Reliable.prototype._complete = function(id) {
2804 | util.log('Completed called for', id);
2805 | var self = this;
2806 | var chunks = this._incoming[id].chunks;
2807 | var bl = new Blob(chunks);
2808 | util.blobToArrayBuffer(bl, function(ab) {
2809 | self.onmessage(util.unpack(ab));
2810 | });
2811 | delete this._incoming[id];
2812 | };
2813 |
2814 | // Ups bandwidth limit on SDP. Meant to be called during offer/answer.
2815 | Reliable.higherBandwidthSDP = function(sdp) {
2816 | // AS stands for Application-Specific Maximum.
2817 | // Bandwidth number is in kilobits / sec.
2818 | // See RFC for more info: http://www.ietf.org/rfc/rfc2327.txt
2819 |
2820 | // Chrome 31+ doesn't want us munging the SDP, so we'll let them have their
2821 | // way.
2822 | var version = navigator.appVersion.match(/Chrome\/(.*?) /);
2823 | if (version) {
2824 | version = parseInt(version[1].split('.').shift());
2825 | if (version < 31) {
2826 | var parts = sdp.split('b=AS:30');
2827 | var replace = 'b=AS:102400'; // 100 Mbps
2828 | if (parts.length > 1) {
2829 | return parts[0] + replace + parts[1];
2830 | }
2831 | }
2832 | }
2833 |
2834 | return sdp;
2835 | };
2836 |
2837 | // Overwritten, typically.
2838 | Reliable.prototype.onmessage = function(msg) {};
2839 |
2840 | module.exports.Reliable = Reliable;
2841 |
2842 | },{"./util":13}],13:[function(require,module,exports){
2843 | var BinaryPack = require('js-binarypack');
2844 |
2845 | var util = {
2846 | debug: false,
2847 |
2848 | inherits: function(ctor, superCtor) {
2849 | ctor.super_ = superCtor;
2850 | ctor.prototype = Object.create(superCtor.prototype, {
2851 | constructor: {
2852 | value: ctor,
2853 | enumerable: false,
2854 | writable: true,
2855 | configurable: true
2856 | }
2857 | });
2858 | },
2859 | extend: function(dest, source) {
2860 | for(var key in source) {
2861 | if(source.hasOwnProperty(key)) {
2862 | dest[key] = source[key];
2863 | }
2864 | }
2865 | return dest;
2866 | },
2867 | pack: BinaryPack.pack,
2868 | unpack: BinaryPack.unpack,
2869 |
2870 | log: function () {
2871 | if (util.debug) {
2872 | var copy = [];
2873 | for (var i = 0; i < arguments.length; i++) {
2874 | copy[i] = arguments[i];
2875 | }
2876 | copy.unshift('Reliable: ');
2877 | console.log.apply(console, copy);
2878 | }
2879 | },
2880 |
2881 | setZeroTimeout: (function(global) {
2882 | var timeouts = [];
2883 | var messageName = 'zero-timeout-message';
2884 |
2885 | // Like setTimeout, but only takes a function argument. There's
2886 | // no time argument (always zero) and no arguments (you have to
2887 | // use a closure).
2888 | function setZeroTimeoutPostMessage(fn) {
2889 | timeouts.push(fn);
2890 | global.postMessage(messageName, '*');
2891 | }
2892 |
2893 | function handleMessage(event) {
2894 | if (event.source == global && event.data == messageName) {
2895 | if (event.stopPropagation) {
2896 | event.stopPropagation();
2897 | }
2898 | if (timeouts.length) {
2899 | timeouts.shift()();
2900 | }
2901 | }
2902 | }
2903 | if (global.addEventListener) {
2904 | global.addEventListener('message', handleMessage, true);
2905 | } else if (global.attachEvent) {
2906 | global.attachEvent('onmessage', handleMessage);
2907 | }
2908 | return setZeroTimeoutPostMessage;
2909 | }(this)),
2910 |
2911 | blobToArrayBuffer: function(blob, cb){
2912 | var fr = new FileReader();
2913 | fr.onload = function(evt) {
2914 | cb(evt.target.result);
2915 | };
2916 | fr.readAsArrayBuffer(blob);
2917 | },
2918 | blobToBinaryString: function(blob, cb){
2919 | var fr = new FileReader();
2920 | fr.onload = function(evt) {
2921 | cb(evt.target.result);
2922 | };
2923 | fr.readAsBinaryString(blob);
2924 | },
2925 | binaryStringToArrayBuffer: function(binary) {
2926 | var byteArray = new Uint8Array(binary.length);
2927 | for (var i = 0; i < binary.length; i++) {
2928 | byteArray[i] = binary.charCodeAt(i) & 0xff;
2929 | }
2930 | return byteArray.buffer;
2931 | },
2932 | randomToken: function () {
2933 | return Math.random().toString(36).substr(2);
2934 | }
2935 | };
2936 |
2937 | module.exports = util;
2938 |
2939 | },{"js-binarypack":10}]},{},[3]);
--------------------------------------------------------------------------------