mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 12:52:42 +01:00
Rewrite Aphlict to use Websockets
Summary: Fixes T6559. No more flash, use Websockets. This is less aggressive than the earlier version, and retains more server logic. - Support "wss". - Make the client work. - Remove "notification.user" entirely. - Seems ok? Test Plan: In Safari, Firefox and Chrome, saw the browsers connect. Made a bunch of comments/updates and saw notifications. Notable holes in the test plan: - Haven't tested "wss" yet. I'll do this on secure. - Notifications are //too fast// now, locally. I get them after I hit submit but before the page reloads. - There are probably some other rough edges, this is a fairly big patch. Reviewers: joshuaspence, btrahan Reviewed By: joshuaspence, btrahan Subscribers: fabe, btrahan, epriestley Maniphest Tasks: T6713, T6559 Differential Revision: https://secure.phabricator.com/D11143
This commit is contained in:
parent
9a8eb4026e
commit
9e0f70e17d
31 changed files with 394 additions and 2776 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -30,3 +30,6 @@
|
|||
|
||||
# User extensions
|
||||
/src/extensions/*
|
||||
|
||||
# NPM local packages
|
||||
/support/aphlict/server/node_modules/
|
||||
|
|
581
externals/vegas/LICENSE
vendored
581
externals/vegas/LICENSE
vendored
|
@ -1,581 +0,0 @@
|
|||
Mozilla Public License 1.1 (MPL 1.1)
|
||||
|
||||
1. Definitions.
|
||||
|
||||
1.0.1. "Commercial Use" means distribution or
|
||||
otherwise making the Covered Code available to a third
|
||||
party.
|
||||
|
||||
1.1. ''Contributor'' means each entity that creates or
|
||||
contributes to the creation of Modifications.
|
||||
|
||||
1.2. ''Contributor Version'' means the combination of
|
||||
the Original Code, prior Modifications used by
|
||||
a Contributor, and the Modifications made by that
|
||||
particular Contributor.
|
||||
|
||||
1.3. ''Covered Code'' means the Original Code or
|
||||
Modifications or the combination of the Original Code
|
||||
and Modifications, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.4. ''Electronic Distribution Mechanism'' means a
|
||||
mechanism generally accepted in the software
|
||||
development community for the electronic transfer of
|
||||
data.
|
||||
|
||||
1.5. ''Executable'' means Covered Code in any form
|
||||
other than Source Code.
|
||||
|
||||
1.6. ''Initial Developer'' means the individual or
|
||||
entity identified as the Initial Developer in the
|
||||
Source Code notice required by Exhibit A.
|
||||
|
||||
1.7. ''Larger Work'' means a work which combines
|
||||
Covered Code or portions thereof with code not
|
||||
governed by the terms of this License.
|
||||
|
||||
1.8. ''License'' means this document.
|
||||
|
||||
1.8.1. "Licensable" means having the right to grant,
|
||||
to the maximum extent possible, whether at the time of
|
||||
the initial grant or subsequently acquired, any and
|
||||
all of the rights conveyed herein.
|
||||
|
||||
1.9. ''Modifications'' means any addition to or
|
||||
deletion from the substance or structure of either the
|
||||
Original Code or any previous Modifications. When
|
||||
Covered Code is released as a series of files, a
|
||||
Modification is:
|
||||
|
||||
A. Any addition to or deletion from the contents
|
||||
of a file containing Original Code or previous
|
||||
Modifications.
|
||||
|
||||
B. Any new file that contains any part of the
|
||||
Original Code or previous Modifications.
|
||||
|
||||
1.10. ''Original Code'' means Source Code of computer
|
||||
software code which is described in the Source Code
|
||||
notice required by Exhibit A as Original Code, and
|
||||
which, at the time of its release under this License
|
||||
is not already Covered Code governed by this License.
|
||||
|
||||
1.10.1. "Patent Claims" means any patent claim(s), now
|
||||
owned or hereafter acquired, including without
|
||||
limitation, method, process, and apparatus claims, in
|
||||
any patent Licensable by grantor.
|
||||
|
||||
1.11. ''Source Code'' means the preferred form of the
|
||||
Covered Code for making modifications to it, including
|
||||
all modules it contains, plus any associated interface
|
||||
definition files, scripts used to control compilation
|
||||
and installation of an Executable, or source code
|
||||
differential comparisons against either the Original
|
||||
Code or another well known, available Covered Code of
|
||||
the Contributor's choice. The Source Code can be in a
|
||||
compressed or archival form, provided the appropriate
|
||||
decompression or de-archiving software is widely
|
||||
available for no charge.
|
||||
|
||||
1.12. "You'' (or "Your") means an individual or a
|
||||
legal entity exercising rights under, and complying
|
||||
with all of the terms of, this License or a future
|
||||
version of this License issued under Section 6.1. For
|
||||
legal entities, "You'' includes any entity which
|
||||
controls, is controlled by, or is under common control
|
||||
with You. For purposes of this definition, "control''
|
||||
means (a) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by
|
||||
contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or
|
||||
beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. Source Code License.
|
||||
|
||||
2.1. The Initial Developer Grant.
|
||||
The Initial Developer hereby grants You a world-wide,
|
||||
royalty-free, non-exclusive license, subject to third
|
||||
party intellectual property claims:
|
||||
(a) under intellectual property rights (other
|
||||
than patent or trademark) Licensable by Initial
|
||||
Developer to use, reproduce, modify, display,
|
||||
perform, sublicense and distribute the Original
|
||||
Code (or portions thereof) with or without
|
||||
Modifications, and/or as part of a Larger Work;
|
||||
and
|
||||
|
||||
(b) under Patents Claims infringed by the making,
|
||||
using or selling of Original Code, to make, have
|
||||
made, use, practice, sell, and offer for sale,
|
||||
and/or otherwise dispose of the Original Code (or
|
||||
portions thereof).
|
||||
|
||||
(c) the licenses granted in this Section 2.1(a)
|
||||
and (b) are effective on the date Initial
|
||||
Developer first distributes Original Code under
|
||||
the terms of this License.
|
||||
|
||||
(d) Notwithstanding Section 2.1(b) above, no
|
||||
patent license is granted: 1) for code that You
|
||||
delete from the Original Code; 2) separate from
|
||||
the Original Code; or 3) for infringements
|
||||
caused by: i) the modification of the Original
|
||||
Code or ii) the combination of the Original Code
|
||||
with other software or devices.
|
||||
|
||||
2.2. Contributor Grant.
|
||||
Subject to third party intellectual property claims,
|
||||
each Contributor hereby grants You a world-wide,
|
||||
royalty-free, non-exclusive license
|
||||
|
||||
(a) under intellectual property rights (other
|
||||
than patent or trademark) Licensable by
|
||||
Contributor, to use, reproduce, modify, display,
|
||||
perform, sublicense and distribute the
|
||||
Modifications created by such Contributor (or
|
||||
portions thereof) either on an unmodified basis,
|
||||
with other Modifications, as Covered Code and/or
|
||||
as part of a Larger Work;
|
||||
and
|
||||
|
||||
(b) under Patent Claims infringed by the making,
|
||||
using, or selling of Modifications made by that
|
||||
Contributor either alone and/or in combination
|
||||
with its Contributor Version (or portions of such
|
||||
combination), to make, use, sell, offer for sale,
|
||||
have made, and/or otherwise dispose of: 1)
|
||||
Modifications made by that Contributor (or
|
||||
portions thereof); and 2) the combination of
|
||||
Modifications made by that Contributor with its
|
||||
Contributor Version (or portions of such
|
||||
combination).
|
||||
|
||||
(c) the licenses granted in Sections 2.2(a) and
|
||||
2.2(b) are effective on the date Contributor
|
||||
first makes Commercial Use of the Covered Code.
|
||||
|
||||
(d) Notwithstanding Section 2.2(b) above, no
|
||||
patent license is granted: 1) for any code that
|
||||
Contributor has deleted from the Contributor
|
||||
Version; 2) separate from the Contributor
|
||||
Version; 3) for infringements caused by: i)
|
||||
third party modifications of Contributor Version
|
||||
or ii) the combination of Modifications made by
|
||||
that Contributor with other software (except as
|
||||
part of the Contributor Version) or other
|
||||
devices; or 4) under Patent Claims infringed by
|
||||
Covered Code in the absence of Modifications made
|
||||
by that Contributor.
|
||||
|
||||
|
||||
3. Distribution Obligations.
|
||||
|
||||
3.1. Application of License.
|
||||
The Modifications which You create or to which You
|
||||
contribute are governed by the terms of this License,
|
||||
including without limitation Section 2.2. The Source
|
||||
Code version of Covered Code may be distributed only
|
||||
under the terms of this License or a future version of
|
||||
this License released under Section 6.1, and You must
|
||||
include a copy of this License with every copy of the
|
||||
Source Code You distribute. You may not offer or
|
||||
impose any terms on any Source Code version that
|
||||
alters or restricts the applicable version of this
|
||||
License or the recipients' rights hereunder. However,
|
||||
You may include an additional document offering the
|
||||
additional rights described in Section 3.5.
|
||||
|
||||
3.2. Availability of Source Code.
|
||||
Any Modification which You create or to which You
|
||||
contribute must be made available in Source Code form
|
||||
under the terms of this License either on the same
|
||||
media as an Executable version or via an accepted
|
||||
Electronic Distribution Mechanism to anyone to whom
|
||||
you made an Executable version available; and if made
|
||||
available via Electronic Distribution Mechanism, must
|
||||
remain available for at least twelve (12) months after
|
||||
the date it initially became available, or at least
|
||||
six (6) months after a subsequent version of that
|
||||
particular Modification has been made available to
|
||||
such recipients. You are responsible for ensuring that
|
||||
the Source Code version remains available even if the
|
||||
Electronic Distribution Mechanism is maintained by a
|
||||
third party.
|
||||
|
||||
3.3. Description of Modifications.
|
||||
You must cause all Covered Code to which You
|
||||
contribute to contain a file documenting the changes
|
||||
You made to create that Covered Code and the date of
|
||||
any change. You must include a prominent statement
|
||||
that the Modification is derived, directly or
|
||||
indirectly, from Original Code provided by the Initial
|
||||
Developer and including the name of the Initial
|
||||
Developer in (a) the Source Code, and (b) in any
|
||||
notice in an Executable version or related
|
||||
documentation in which You describe the origin or
|
||||
ownership of the Covered Code.
|
||||
|
||||
3.4. Intellectual Property Matters
|
||||
(a) Third Party Claims.
|
||||
If Contributor has knowledge that a license under
|
||||
a third party's intellectual property rights is
|
||||
required to exercise the rights granted by such
|
||||
Contributor under Sections 2.1 or 2.2,
|
||||
Contributor must include a text file with the
|
||||
Source Code distribution titled "LEGAL'' which
|
||||
describes the claim and the party making the
|
||||
claim in sufficient detail that a recipient will
|
||||
know whom to contact. If Contributor obtains such
|
||||
knowledge after the Modification is made
|
||||
available as described in Section 3.2,
|
||||
Contributor shall promptly modify the LEGAL file
|
||||
in all copies Contributor makes available
|
||||
thereafter and shall take other steps (such as
|
||||
notifying appropriate mailing lists or
|
||||
newsgroups) reasonably calculated to inform those
|
||||
who received the Covered Code that new knowledge
|
||||
has been obtained.
|
||||
|
||||
(b) Contributor APIs.
|
||||
If Contributor's Modifications include an
|
||||
application programming interface and Contributor
|
||||
has knowledge of patent licenses which are
|
||||
reasonably necessary to implement that API,
|
||||
Contributor must also include this information in
|
||||
the LEGAL file.
|
||||
|
||||
(c) Representations.
|
||||
Contributor represents that, except as disclosed
|
||||
pursuant to Section 3.4(a) above, Contributor
|
||||
believes that Contributor's Modifications are
|
||||
Contributor's original creation(s) and/or
|
||||
Contributor has sufficient rights to grant the
|
||||
rights conveyed by this License.
|
||||
|
||||
3.5. Required Notices.
|
||||
You must duplicate the notice in Exhibit A in each
|
||||
file of the Source Code. If it is not possible to put
|
||||
such notice in a particular Source Code file due to
|
||||
its structure, then You must include such notice in a
|
||||
location (such as a relevant directory) where a user
|
||||
would be likely to look for such a notice. If You
|
||||
created one or more Modification(s) You may add your
|
||||
name as a Contributor to the notice described in
|
||||
Exhibit A. You must also duplicate this License in
|
||||
any documentation for the Source Code where You
|
||||
describe recipients' rights or ownership rights
|
||||
relating to Covered Code. You may choose to offer,
|
||||
and to charge a fee for, warranty, support, indemnity
|
||||
or liability obligations to one or more recipients of
|
||||
Covered Code. However, You may do so only on Your own
|
||||
behalf, and not on behalf of the Initial Developer or
|
||||
any Contributor. You must make it absolutely clear
|
||||
than any such warranty, support, indemnity or
|
||||
liability obligation is offered by You alone, and You
|
||||
hereby agree to indemnify the Initial Developer and
|
||||
every Contributor for any liability incurred by the
|
||||
Initial Developer or such Contributor as a result of
|
||||
warranty, support, indemnity or liability terms You
|
||||
offer.
|
||||
|
||||
3.6. Distribution of Executable Versions.
|
||||
You may distribute Covered Code in Executable form
|
||||
only if the requirements of Section 3.1-3.5 have been
|
||||
met for that Covered Code, and if You include a notice
|
||||
stating that the Source Code version of the Covered
|
||||
Code is available under the terms of this License,
|
||||
including a description of how and where You have
|
||||
fulfilled the obligations of Section 3.2. The notice
|
||||
must be conspicuously included in any notice in an
|
||||
Executable version, related documentation or
|
||||
collateral in which You describe recipients' rights
|
||||
relating to the Covered Code. You may distribute the
|
||||
Executable version of Covered Code or ownership rights
|
||||
under a license of Your choice, which may contain
|
||||
terms different from this License, provided that You
|
||||
are in compliance with the terms of this License and
|
||||
that the license for the Executable version does not
|
||||
attempt to limit or alter the recipient's rights in
|
||||
the Source Code version from the rights set forth in
|
||||
this License. If You distribute the Executable version
|
||||
under a different license You must make it absolutely
|
||||
clear that any terms which differ from this License
|
||||
are offered by You alone, not by the Initial Developer
|
||||
or any Contributor. You hereby agree to indemnify the
|
||||
Initial Developer and every Contributor for any
|
||||
liability incurred by the Initial Developer or such
|
||||
Contributor as a result of any such terms You offer.
|
||||
|
||||
3.7. Larger Works.
|
||||
You may create a Larger Work by combining Covered Code
|
||||
with other code not governed by the terms of this
|
||||
License and distribute the Larger Work as a single
|
||||
product. In such a case, You must make sure the
|
||||
requirements of this License are fulfilled for the
|
||||
Covered Code.
|
||||
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation.
|
||||
|
||||
If it is impossible for You to comply with any of the
|
||||
terms of this License with respect to some or all of
|
||||
the Covered Code due to statute, judicial order, or
|
||||
regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect.
|
||||
Such description must be included in the LEGAL file
|
||||
described in Section 3.4 and must be included with all
|
||||
distributions of the Source Code. Except to the extent
|
||||
prohibited by statute or regulation, such description
|
||||
must be sufficiently detailed for a recipient of
|
||||
ordinary skill to be able to understand it.
|
||||
|
||||
|
||||
5. Application of this License.
|
||||
|
||||
This License applies to code to which the Initial
|
||||
Developer has attached the notice in Exhibit A and to
|
||||
related Covered Code.
|
||||
|
||||
|
||||
6. Versions of the License.
|
||||
|
||||
6.1. New Versions.
|
||||
Netscape Communications Corporation (''Netscape'') may
|
||||
publish revised and/or new versions of the License
|
||||
from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
6.2. Effect of New Versions.
|
||||
Once Covered Code has been published under a
|
||||
particular version of the License, You may always
|
||||
continue to use it under the terms of that version.
|
||||
You may also choose to use such Covered Code under the
|
||||
terms of any subsequent version of the License
|
||||
published by Netscape. No one other than Netscape has
|
||||
the right to modify the terms applicable to Covered
|
||||
Code created under this License.
|
||||
|
||||
6.3. Derivative Works.
|
||||
If You create or use a modified version of this
|
||||
License (which you may only do in order to apply it to
|
||||
code which is not already Covered Code governed by
|
||||
this License), You must (a) rename Your license so
|
||||
that the phrases ''Mozilla'', ''MOZILLAPL'',
|
||||
''MOZPL'', ''Netscape'', "MPL", ''NPL'' or any
|
||||
confusingly similar phrase do not appear in your
|
||||
license (except to note that your license differs from
|
||||
this License) and (b) otherwise make it clear that
|
||||
Your version of the license contains terms which
|
||||
differ from the Mozilla Public License and Netscape
|
||||
Public License. (Filling in the name of the Initial
|
||||
Developer, Original Code or Contributor in the notice
|
||||
described in Exhibit A shall not of themselves be
|
||||
deemed to be modifications of this License.)
|
||||
|
||||
|
||||
7. DISCLAIMER OF WARRANTY.
|
||||
|
||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS
|
||||
IS'' BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
|
||||
WARRANTIES THAT THE COVERED CODE IS FREE OF DEFECTS,
|
||||
MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-
|
||||
INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
|
||||
PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD
|
||||
ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU
|
||||
(NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR)
|
||||
ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR
|
||||
CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN
|
||||
ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED
|
||||
CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
|
||||
DISCLAIMER.
|
||||
|
||||
|
||||
8. TERMINATION.
|
||||
|
||||
8.1. This License and the rights granted hereunder
|
||||
will terminate automatically if You fail to comply
|
||||
with terms herein and fail to cure such breach within
|
||||
30 days of becoming aware of the breach. All
|
||||
sublicenses to the Covered Code which are properly
|
||||
granted shall survive any termination of this License.
|
||||
Provisions which, by their nature, must remain in
|
||||
effect beyond the termination of this License shall
|
||||
survive.
|
||||
|
||||
8.2. If You initiate litigation by asserting a patent
|
||||
infringement claim (excluding declatory judgment
|
||||
actions) against Initial Developer or a Contributor
|
||||
(the Initial Developer or Contributor against whom You
|
||||
file such action is referred to as "Participant")
|
||||
alleging that:
|
||||
|
||||
(a) such Participant's Contributor Version directly
|
||||
or indirectly infringes any patent, then any and all
|
||||
rights granted by such Participant to You under
|
||||
Sections 2.1 and/or 2.2 of this License shall, upon 60
|
||||
days notice from Participant terminate prospectively,
|
||||
unless if within 60 days after receipt of notice You
|
||||
either: (i) agree in writing to pay Participant a
|
||||
mutually agreeable reasonable royalty for Your past
|
||||
and future use of Modifications made by such
|
||||
Participant, or (ii) withdraw Your litigation claim
|
||||
with respect to the Contributor Version against such
|
||||
Participant. If within 60 days of notice, a
|
||||
reasonable royalty and payment arrangement are not
|
||||
mutually agreed upon in writing by the parties or the
|
||||
litigation claim is not withdrawn, the rights granted
|
||||
by Participant to You under Sections 2.1 and/or 2.2
|
||||
automatically terminate at the expiration of the 60
|
||||
day notice period specified above.
|
||||
|
||||
(b) any software, hardware, or device, other than
|
||||
such Participant's Contributor Version, directly or
|
||||
indirectly infringes any patent, then any rights
|
||||
granted to You by such Participant under Sections 2.1
|
||||
(b) and 2.2(b) are revoked effective as of the date
|
||||
You first made, used, sold, distributed, or had made,
|
||||
Modifications made by that Participant.
|
||||
|
||||
8.3. If You assert a patent infringement claim
|
||||
against Participant alleging that such Participant's
|
||||
Contributor Version directly or indirectly infringes
|
||||
any patent where such claim is resolved (such as by
|
||||
license or settlement) prior to the initiation of
|
||||
patent infringement litigation, then the reasonable
|
||||
value of the licenses granted by such Participant
|
||||
under Sections 2.1 or 2.2 shall be taken into account
|
||||
in determining the amount or value of any payment or
|
||||
license.
|
||||
|
||||
8.4. In the event of termination under Sections 8.1
|
||||
or 8.2 above, all end user license agreements
|
||||
(excluding distributors and resellers) which have been
|
||||
validly granted by You or any distributor hereunder
|
||||
prior to termination shall survive termination.
|
||||
|
||||
|
||||
9. LIMITATION OF LIABILITY.
|
||||
|
||||
UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
|
||||
WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR
|
||||
OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER
|
||||
CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, OR
|
||||
ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY
|
||||
PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING,
|
||||
WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK
|
||||
STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND
|
||||
ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH
|
||||
PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT
|
||||
APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
|
||||
RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
|
||||
APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME
|
||||
JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION
|
||||
OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS
|
||||
EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
|
||||
10. U.S. GOVERNMENT END USERS.
|
||||
|
||||
The Covered Code is a ''commercial item,'' as that
|
||||
term is defined in 48 C.F.R. 2.101 (Oct. 1995),
|
||||
consisting of ''commercial computer software'' and
|
||||
''commercial computer software documentation,'' as
|
||||
such terms are used in 48 C.F.R. 12.212 (Sept. 1995).
|
||||
Consistent with 48 C.F.R. 12.212 and 48 C.F.R.
|
||||
227.7202-1 through 227.7202-4 (June 1995), all U.S.
|
||||
Government End Users acquire Covered Code with only
|
||||
those rights set forth herein.
|
||||
|
||||
|
||||
11. MISCELLANEOUS.
|
||||
|
||||
This License represents the complete agreement
|
||||
concerning subject matter hereof. If any provision of
|
||||
this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. This License shall
|
||||
be governed by California law provisions (except to
|
||||
the extent applicable law, if any, provides
|
||||
otherwise), excluding its conflict-of-law provisions.
|
||||
With respect to disputes in which at least one party
|
||||
is a citizen of, or an entity chartered or registered
|
||||
to do business in the United States of America, any
|
||||
litigation relating to this License shall be subject
|
||||
to the jurisdiction of the Federal Courts of the
|
||||
Northern District of California, with venue lying in
|
||||
Santa Clara County, California, with the losing party
|
||||
responsible for costs, including without limitation,
|
||||
court costs and reasonable attorneys' fees and
|
||||
expenses. The application of the United Nations
|
||||
Convention on Contracts for the International Sale of
|
||||
Goods is expressly excluded. Any law or regulation
|
||||
which provides that the language of a contract shall
|
||||
be construed against the drafter shall not apply to
|
||||
this License.
|
||||
|
||||
|
||||
12. RESPONSIBILITY FOR CLAIMS.
|
||||
|
||||
As between Initial Developer and the Contributors,
|
||||
each party is responsible for claims and damages
|
||||
arising, directly or indirectly, out of its
|
||||
utilization of rights under this License and You agree
|
||||
to work with Initial Developer and Contributors to
|
||||
distribute such responsibility on an equitable basis.
|
||||
Nothing herein is intended or shall be deemed to
|
||||
constitute any admission of liability.
|
||||
|
||||
|
||||
13. MULTIPLE-LICENSED CODE.
|
||||
|
||||
Initial Developer may designate portions of the
|
||||
Covered Code as Multiple-Licensed. Multiple-Licensed
|
||||
means that the Initial Developer permits you to
|
||||
utilize portions of the Covered Code under Your choice
|
||||
of the NPL or the alternative licenses, if any,
|
||||
specified by the Initial Developer in the file
|
||||
described in Exhibit A.
|
||||
|
||||
|
||||
|
||||
EXHIBIT A - Mozilla Public License.
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is ______________________________________.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
________________________.
|
||||
Portions created by ______________________ are Copyright (C) ______
|
||||
_______________________. All Rights Reserved.
|
||||
|
||||
Contributor(s): ______________________________________.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the _____ license (the [___] License), in which case the provisions of
|
||||
[______] License are applicable instead of those above. If you wish to
|
||||
allow use of your version of this file only under the terms of the [____]
|
||||
License and not to allow others to use your version of this file under
|
||||
the MPL, indicate your decision by deleting the provisions above and
|
||||
replace them with the notice and other provisions required by the [___]
|
||||
License. If you do not delete the provisions above, a recipient may use
|
||||
your version of this file under either the MPL or the [___] License.
|
||||
|
||||
[NOTE: The text of this Exhibit A may differ slightly from the text of
|
||||
the notices in the Source Code files of the Original Code. You should use
|
||||
the text of this Exhibit A rather than the text found in the Original Code
|
||||
Source Code for Your Modifications.]
|
||||
|
29
externals/vegas/README
vendored
29
externals/vegas/README
vendored
|
@ -1,29 +0,0 @@
|
|||
VEGAS AS3 - version 1.8.5.2228
|
||||
|
||||
The "vegas.swc" library contains all the AS3 source code of the VEGAS project with all this extensions.
|
||||
|
||||
LICENCE
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
PROJECT PAGES
|
||||
|
||||
* http://code.google.com/p/vegas/
|
||||
|
||||
DOCUMENTATION & CO
|
||||
|
||||
* http://code.google.com/p/vegas/ (tutorials and install)
|
||||
* http://code.google.com/p/vegas/issues/list (issues)
|
||||
* http://www.ekameleon.net/vegas/docs
|
||||
|
||||
* http://www.ekameleon.net/blog/ (french blog)
|
||||
|
||||
ABOUT AUTHOR
|
||||
|
||||
* Author : ALCARAZ Marc (eKameleon)
|
||||
* Link : http://www.ekameleon.net/blog
|
||||
* Mail : ekameleon@gmail.com
|
||||
|
||||
NOTES
|
||||
|
||||
The vegas.swc file target now the FlashPlayer 10.2 and build with the Flex SDK 4.5.0.19786.
|
86
externals/vegas/src/system/Serializer.as
vendored
86
externals/vegas/src/system/Serializer.as
vendored
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the
|
||||
License.
|
||||
|
||||
The Original Code is [maashaack framework].
|
||||
|
||||
The Initial Developers of the Original Code are
|
||||
Zwetan Kjukov <zwetan@gmail.com> and Marc Alcaraz <ekameleon@gmail.com>.
|
||||
Portions created by the Initial Developers are Copyright (C) 2006-2011
|
||||
the Initial Developers. All Rights Reserved.
|
||||
|
||||
Contributor(s):
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*/
|
||||
|
||||
package system
|
||||
{
|
||||
|
||||
/**
|
||||
* Defines what a Serializer have to implements to be integrated in the framework.
|
||||
* <p><b>Note :</b> Every serializers (eden, json, wddx, etc.) should implement it.</p>
|
||||
*/
|
||||
public interface Serializer
|
||||
{
|
||||
/**
|
||||
* The prettyIndent value of the serializer.
|
||||
*/
|
||||
function get prettyIndent():int;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set prettyIndent( value:int ):void;
|
||||
|
||||
/**
|
||||
* The prettyPrinting value of the serializer.
|
||||
*/
|
||||
function get prettyPrinting():Boolean;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set prettyPrinting( value:Boolean ):void;
|
||||
|
||||
/**
|
||||
* The identor String value of the serializer.
|
||||
*/
|
||||
function get indentor():String;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function set indentor( value:String ):void;
|
||||
|
||||
/**
|
||||
* Deserialize the specified String source representation.
|
||||
*/
|
||||
function deserialize( source:String ):*;
|
||||
|
||||
/**
|
||||
* Serialize the specified object.
|
||||
*/
|
||||
function serialize( value:* ):String;
|
||||
}
|
||||
}
|
||||
|
107
externals/vegas/src/vegas/strings/JSON.as
vendored
107
externals/vegas/src/vegas/strings/JSON.as
vendored
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is VEGAS Framework.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s) :
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
|
||||
*/
|
||||
package vegas.strings
|
||||
{
|
||||
import vegas.strings.json.JSONSerializer;
|
||||
|
||||
/**
|
||||
* <code class="prettyprint">JSON</code> (JavaScript object Notation) is a lightweight data-interchange format.
|
||||
* <p>More information in the official site : <a href="http://www.JSON.org/">http://www.JSON.org</a></p>
|
||||
* <p>Add Hexa Digits tool in deserialize method - <a href="http://code.google.com/p/edenrr/">eden inspiration</a></p>
|
||||
*
|
||||
* @example Example
|
||||
* <listing version="3.0">
|
||||
* <code class="prettyprint">
|
||||
* import core.getClassName ;
|
||||
*
|
||||
* import vegas.strings.JSON;
|
||||
* import vegas.strings.errors.JSONError;
|
||||
*
|
||||
* // --- Init
|
||||
*
|
||||
* var a:Array = [2, true, "hello"] ;
|
||||
* var o:Object = { prop1 : 1 , prop2 : 2 } ;
|
||||
* var s:String = "hello world" ;
|
||||
* var n:Number = 4 ;
|
||||
* var b:Boolean = true ;
|
||||
*
|
||||
* trace("# Serialize \r") ;
|
||||
*
|
||||
* trace("- a : " + JSON.serialize( a ) ) ;
|
||||
* trace("- o : " + JSON.serialize( o ) ) ;
|
||||
* trace("- s : " + JSON.serialize( s ) ) ;
|
||||
* trace("- n : " + JSON.serialize( n ) ) ;
|
||||
* trace("- b : " + JSON.serialize( b ) ) ;
|
||||
*
|
||||
* trace ("\r# Deserialize \r") ;
|
||||
*
|
||||
* var source:String = '[ { "prop1" : 0xFF0000 , prop2:2, prop3:"hello", prop4:true} , 2, true, 3, [3, 2] ]' ;
|
||||
*
|
||||
* o = JSON.deserialize(source) ;
|
||||
*
|
||||
* var l:uint = o.length ;
|
||||
* for (var i:uint = 0 ; i < l ; i++)
|
||||
* {
|
||||
* trace("> " + i + " : " + o[i] + " -> typeof :: " + typeof(o[i])) ;
|
||||
* if (typeof(o[i]) == "object")
|
||||
* {
|
||||
* for (var each:String in o[i])
|
||||
* {
|
||||
* trace(" > " + each + " : " + o[i][each] + " :: " + getClassName(o[i][each]) ) ;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* trace ("\r# JSONError \r") ;
|
||||
*
|
||||
* source = "[3, 2," ; // test1
|
||||
*
|
||||
* // var source:String = '{"prop1":coucou"}' ; // test2
|
||||
*
|
||||
* try
|
||||
* {
|
||||
* var errorObj:Object = JSON.deserialize(source) ;
|
||||
* }
|
||||
* catch( e:JSONError )
|
||||
* {
|
||||
* trace( e.toString() ) ;
|
||||
* }
|
||||
* </code>
|
||||
* </listing>
|
||||
*/
|
||||
public var JSON:JSONSerializer = new JSONSerializer() ;
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is VEGAS Framework.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s) :
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
|
||||
*/
|
||||
|
||||
package vegas.strings.json
|
||||
{
|
||||
/**
|
||||
* This JSONError is throw in the JSON static methods.
|
||||
*/
|
||||
public class JSONError extends Error
|
||||
{
|
||||
/**
|
||||
* Creates a new JSONError instance.
|
||||
*/
|
||||
public function JSONError( message:String, at:uint, source:String , id:int=0 )
|
||||
{
|
||||
super( message , id );
|
||||
name = "JSONError" ;
|
||||
this.at = at ;
|
||||
this.source = source ;
|
||||
}
|
||||
|
||||
/**
|
||||
* The position of char with an error parsing in the JSON String representation.
|
||||
*/
|
||||
public var at:uint ;
|
||||
|
||||
/**
|
||||
* The source ot the bad parsing.
|
||||
*/
|
||||
public var source:String ;
|
||||
|
||||
/**
|
||||
* Returns a String representation of the object.
|
||||
* @return a String representation of the object.
|
||||
*/
|
||||
public function toString():String
|
||||
{
|
||||
var msg:String = "## " + name + " : " + message + " ##" ;
|
||||
if (!isNaN(at))
|
||||
{
|
||||
msg += ", at:" + at ;
|
||||
}
|
||||
if ( source != null )
|
||||
{
|
||||
msg += " in \"" + source + "\"";
|
||||
}
|
||||
return msg ;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,757 +0,0 @@
|
|||
/*
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is VEGAS Framework.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s) :
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
|
||||
*/
|
||||
|
||||
package vegas.strings.json
|
||||
{
|
||||
import system.Serializer;
|
||||
|
||||
/**
|
||||
* This class is the concrete class of the JSON singleton.
|
||||
* <code class="prettyprint">JSON</code> (JavaScript object Notation) is a lightweight data-interchange format.
|
||||
* <p>More information in the official site : <a href="http://www.JSON.org/">http://www.JSON.org</a></p>
|
||||
* <p>Add Hexa Digits tool in deserialize method - <a href="http://code.google.com/p/edenrr/">eden inspiration</a></p>
|
||||
* <p><b>Example :</b></p>
|
||||
* <pre class="prettyprint">
|
||||
* import core.getClassName ;
|
||||
*
|
||||
* import vegas.strings.JSON;
|
||||
* import vegas.strings.JSONError;
|
||||
*
|
||||
* // --- Init
|
||||
*
|
||||
* var a:Array = [2, true, "hello"] ;
|
||||
* var o:Object = { prop1 : 1 , prop2 : 2 } ;
|
||||
* var s:String = "hello world" ;
|
||||
* var n:Number = 4 ;
|
||||
* var b:Boolean = true ;
|
||||
*
|
||||
* trace("Serialize") ;
|
||||
*
|
||||
* trace("- a : " + JSON.serialize( a ) ) ;
|
||||
* trace("- o : " + JSON.serialize( o ) ) ;
|
||||
* trace("- s : " + JSON.serialize( s ) ) ;
|
||||
* trace("- n : " + JSON.serialize( n ) ) ;
|
||||
* trace("- b : " + JSON.serialize( b ) ) ;
|
||||
*
|
||||
* trace ("Deserialize") ;
|
||||
*
|
||||
* var source:String = '[ { "prop1" : 0xFF0000 , prop2:2, prop3:"hello", prop4:true} , 2, true, 3, [3, 2] ]' ;
|
||||
*
|
||||
* o = JSON.deserialize(source) ;
|
||||
*
|
||||
* var l:uint = o.length ;
|
||||
* for (var i:uint = 0 ; i < l ; i++)
|
||||
* {
|
||||
* trace("- " + i + " : " + o[i] + " , typeof :: " + typeof(o[i])) ;
|
||||
* if (typeof(o[i]) == "object")
|
||||
* {
|
||||
* for (var each:String in o[i])
|
||||
* {
|
||||
* trace(" + " + each + " : " + o[i][each] + " :: " + getClassName(o[i][each]) ) ;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* trace ("JSONError") ;
|
||||
*
|
||||
* source = "[3, 2," ; // test1
|
||||
*
|
||||
* // var source:String = '{"prop1":coucou"}' ; // test2
|
||||
*
|
||||
* try
|
||||
* {
|
||||
* var errorObj:Object = JSON.deserialize(source) ;
|
||||
* }
|
||||
* catch( e:JSONError )
|
||||
* {
|
||||
* trace( e.toString() ) ;
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class JSONSerializer implements Serializer
|
||||
{
|
||||
/**
|
||||
* Creates a new JSONSerializer instance.
|
||||
*/
|
||||
public function JSONSerializer()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* The source to evaluate.
|
||||
*/
|
||||
public var source:String ;
|
||||
|
||||
/**
|
||||
* Indicates the indentor string representation.
|
||||
*/
|
||||
public function get indentor():String
|
||||
{
|
||||
return _indentor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set indentor(value:String):void
|
||||
{
|
||||
_indentor = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the pretty indent value.
|
||||
*/
|
||||
public function get prettyIndent():int
|
||||
{
|
||||
return _prettyIndent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set prettyIndent(value:int):void
|
||||
{
|
||||
_prettyIndent = value ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the pretty printing flag value.
|
||||
*/
|
||||
public function get prettyPrinting():Boolean
|
||||
{
|
||||
return _prettyPrinting ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set prettyPrinting(value:Boolean):void
|
||||
{
|
||||
_prettyPrinting = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string and interpret the source code to the correct object construct.
|
||||
* <p><b>Example :</b></p>
|
||||
* <pre class="prettyprint">
|
||||
* "hello world" --> "hello world"
|
||||
* "0xFF" --> 255
|
||||
* "{a:1,"b":2}" --> {a:1,b:2}
|
||||
* </pre>
|
||||
* @return a string representing the data.
|
||||
*/
|
||||
public function deserialize( source:String ):*
|
||||
{
|
||||
this.source = source ;
|
||||
at = 0 ;
|
||||
ch = ' ' ;
|
||||
return value() ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the specified value object passed-in argument.
|
||||
*/
|
||||
public function serialize( value:* ):String
|
||||
{
|
||||
var c:String ; // char
|
||||
var i:int ;
|
||||
var l:int ;
|
||||
var s:String = '' ;
|
||||
var v:* ;
|
||||
var tof:String = typeof(value) ;
|
||||
switch (tof)
|
||||
{
|
||||
case 'object' :
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
if (value is Array)
|
||||
{
|
||||
l = (value as Array).length ;
|
||||
for (i = 0 ; i < l ; ++i)
|
||||
{
|
||||
v = serialize(value[i]);
|
||||
if (s) s += ',' ;
|
||||
s += v ;
|
||||
}
|
||||
return '[' + s + ']';
|
||||
}
|
||||
else if ( typeof( value.toString ) != 'undefined')
|
||||
{
|
||||
for (var prop:String in value)
|
||||
{
|
||||
v = value[prop];
|
||||
if ( (typeof(v) != 'undefined') && (typeof(v) != 'function') )
|
||||
{
|
||||
v = serialize(v);
|
||||
if (s)
|
||||
{
|
||||
s += ',' ;
|
||||
}
|
||||
s += serialize(prop) + ':' + v ;
|
||||
}
|
||||
}
|
||||
return "{" + s + "}";
|
||||
}
|
||||
}
|
||||
return 'null';
|
||||
}
|
||||
case 'number':
|
||||
{
|
||||
return isFinite(value) ? String(value) : 'null' ;
|
||||
}
|
||||
case 'string' :
|
||||
{
|
||||
l = (value as String).length ;
|
||||
s = '"' ;
|
||||
for (i = 0 ; i < l ; i += 1)
|
||||
{
|
||||
c = (value as String).charAt(i) ;
|
||||
if (c >= ' ')
|
||||
{
|
||||
if (c == '\\' || c == '"')
|
||||
{
|
||||
s += '\\';
|
||||
}
|
||||
s += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\b':
|
||||
{
|
||||
s += '\\b';
|
||||
break ;
|
||||
}
|
||||
case '\f' :
|
||||
{
|
||||
s += '\\f' ;
|
||||
break ;
|
||||
}
|
||||
case '\n' :
|
||||
{
|
||||
s += '\\n' ;
|
||||
break ;
|
||||
}
|
||||
case '\r':
|
||||
{
|
||||
s += '\\r' ;
|
||||
break ;
|
||||
}
|
||||
case '\t':
|
||||
{
|
||||
s += '\\t' ;
|
||||
break ;
|
||||
}
|
||||
default:
|
||||
{
|
||||
var code:Number = c.charCodeAt() ;
|
||||
s += '\\u00' + String(Math.floor(code / 16).toString(16)) + ((code % 16).toString(16)) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s + '"' ;
|
||||
}
|
||||
case 'boolean' :
|
||||
{
|
||||
return String(value);
|
||||
}
|
||||
default :
|
||||
{
|
||||
return 'null';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current position of the iterator in the source.
|
||||
*/
|
||||
protected var at:Number = 0 ;
|
||||
|
||||
/**
|
||||
* The current character of the iterator in the source.
|
||||
*/
|
||||
protected var ch:String = ' ' ;
|
||||
|
||||
/**
|
||||
* Check the Array objects in the source expression.
|
||||
*/
|
||||
protected function array():Array
|
||||
{
|
||||
var a:Array = [];
|
||||
if ( ch == '[' )
|
||||
{
|
||||
next() ;
|
||||
white() ;
|
||||
if (ch == ']')
|
||||
{
|
||||
next();
|
||||
return a;
|
||||
}
|
||||
while (ch)
|
||||
{
|
||||
a.push( value() ) ;
|
||||
white();
|
||||
if (ch == ']')
|
||||
{
|
||||
next();
|
||||
return a;
|
||||
}
|
||||
else if (ch != ',')
|
||||
{
|
||||
break;
|
||||
}
|
||||
next();
|
||||
white();
|
||||
}
|
||||
}
|
||||
error( JSONStrings.badArray );
|
||||
return null ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a JSONError with the passed-in message.
|
||||
*/
|
||||
protected function error( m:String ):void
|
||||
{
|
||||
throw new JSONError( m, at - 1 , source) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the passed-in character is a digit.
|
||||
*/
|
||||
protected function isDigit( c:String ):Boolean
|
||||
{
|
||||
return( ("0" <= c) && (c <= "9") );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the passed-in character is a hexadecimal digit.
|
||||
*/
|
||||
protected function isHexDigit( c:String ):Boolean
|
||||
{
|
||||
return( isDigit( c ) || (("A" <= c) && (c <= "F")) || (("a" <= c) && (c <= "f")) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the current character is a key.
|
||||
*/
|
||||
protected function key():*
|
||||
{
|
||||
var s:String = ch ;
|
||||
var semiColon:int = source.indexOf( ':' , at ) ;
|
||||
var quoteIndex:int = source.indexOf( '"' , at ) ;
|
||||
var squoteIndex:int = source.indexOf( "'" , at ) ;
|
||||
if( (quoteIndex <= semiColon && quoteIndex > -1) || (squoteIndex <= semiColon && squoteIndex > -1))
|
||||
{
|
||||
s = string() ;
|
||||
white() ;
|
||||
if(ch == ':')
|
||||
{
|
||||
return s;
|
||||
}
|
||||
else
|
||||
{
|
||||
error(JSONStrings.badKey);
|
||||
}
|
||||
}
|
||||
while ( next() ) // Use key handling
|
||||
{
|
||||
if (ch == ':')
|
||||
{
|
||||
return s;
|
||||
}
|
||||
if(ch <= ' ')
|
||||
{
|
||||
//
|
||||
}
|
||||
else
|
||||
{
|
||||
s += ch;
|
||||
}
|
||||
}
|
||||
error( JSONStrings.badKey ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next character in the source String representation.
|
||||
* @return the next character in the source String representation.
|
||||
*/
|
||||
protected function next():String
|
||||
{
|
||||
ch = source.charAt(at);
|
||||
at += 1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the Number values in the source expression.
|
||||
*/
|
||||
protected function number():*
|
||||
{
|
||||
|
||||
var n:* = '' ;
|
||||
var v:* ;
|
||||
var hex:String = '' ;
|
||||
var sign:String = '' ;
|
||||
if (ch == '-')
|
||||
{
|
||||
n = '-';
|
||||
sign = n ;
|
||||
next();
|
||||
}
|
||||
if( ch == "0" )
|
||||
{
|
||||
next() ;
|
||||
if( ( ch == "x") || ( ch == "X") )
|
||||
{
|
||||
next();
|
||||
while( isHexDigit( ch ) )
|
||||
{
|
||||
hex += ch ;
|
||||
next();
|
||||
}
|
||||
if( hex == "" )
|
||||
{
|
||||
error(JSONStrings.malFormedHexadecimal) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Number( sign + "0x" + hex ) ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n += "0" ;
|
||||
}
|
||||
}
|
||||
while ( isDigit(ch) )
|
||||
{
|
||||
n += ch ;
|
||||
next() ;
|
||||
}
|
||||
if (ch == '.')
|
||||
{
|
||||
n += '.';
|
||||
while (next() && ch >= '0' && ch <= '9')
|
||||
{
|
||||
n += ch ;
|
||||
}
|
||||
}
|
||||
v = 1 * n ;
|
||||
if (!isFinite(v))
|
||||
{
|
||||
error( JSONStrings.badNumber );
|
||||
}
|
||||
else
|
||||
{
|
||||
return v ;
|
||||
}
|
||||
return NaN ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the Object values in the source expression.
|
||||
*/
|
||||
protected function object():*
|
||||
{
|
||||
var k:* = {} ;
|
||||
var o:* = {} ;
|
||||
if (ch == '{')
|
||||
{
|
||||
next();
|
||||
white();
|
||||
if (ch == '}')
|
||||
{
|
||||
next() ;
|
||||
return o ;
|
||||
}
|
||||
while (ch)
|
||||
{
|
||||
k = key() ;
|
||||
white();
|
||||
if (ch != ':')
|
||||
{
|
||||
break;
|
||||
}
|
||||
next();
|
||||
o[k] = value() ;
|
||||
white();
|
||||
if (ch == '}')
|
||||
{
|
||||
next();
|
||||
return o;
|
||||
}
|
||||
else if (ch != ',')
|
||||
{
|
||||
break;
|
||||
}
|
||||
next();
|
||||
white();
|
||||
}
|
||||
}
|
||||
error( JSONStrings.badObject ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the string objects in the source expression.
|
||||
*/
|
||||
protected function string():*
|
||||
{
|
||||
var i:* = '' ;
|
||||
var s:* = '' ;
|
||||
var t:* ;
|
||||
var u:* ;
|
||||
var outer:Boolean ;
|
||||
if (ch == '"' || ch == "'" )
|
||||
{
|
||||
var outerChar:String = ch ;
|
||||
while ( next() )
|
||||
{
|
||||
if (ch == outerChar)
|
||||
{
|
||||
next() ;
|
||||
return s ;
|
||||
}
|
||||
else if (ch == '\\')
|
||||
{
|
||||
switch ( next() )
|
||||
{
|
||||
case 'b':
|
||||
{
|
||||
s += '\b' ;
|
||||
break ;
|
||||
}
|
||||
case 'f' :
|
||||
{
|
||||
s += '\f';
|
||||
break ;
|
||||
}
|
||||
case 'n':
|
||||
{
|
||||
s += '\n';
|
||||
break ;
|
||||
}
|
||||
case 'r' :
|
||||
{
|
||||
s += '\r';
|
||||
break ;
|
||||
}
|
||||
case 't' :
|
||||
{
|
||||
s += '\t' ;
|
||||
break ;
|
||||
}
|
||||
case 'u' :
|
||||
{
|
||||
u = 0;
|
||||
for (i = 0; i < 4; i += 1)
|
||||
{
|
||||
t = parseInt( next() , 16 ) ;
|
||||
if (!isFinite(t))
|
||||
{
|
||||
outer = true;
|
||||
break;
|
||||
}
|
||||
u = u * 16 + t;
|
||||
}
|
||||
if(outer)
|
||||
{
|
||||
outer = false;
|
||||
break;
|
||||
}
|
||||
s += String.fromCharCode(u);
|
||||
break;
|
||||
}
|
||||
default :
|
||||
{
|
||||
s += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s += ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
error( JSONStrings.badString );
|
||||
return null ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the values in the source expression.
|
||||
*/
|
||||
protected function value():*
|
||||
{
|
||||
white() ;
|
||||
if (ch == '{' )
|
||||
{
|
||||
return object();
|
||||
}
|
||||
else if ( ch == '[' )
|
||||
{
|
||||
return array();
|
||||
}
|
||||
else if ( ch == '"' || ch == "'" )
|
||||
{
|
||||
return string();
|
||||
}
|
||||
else if ( ch == '-' )
|
||||
{
|
||||
return number();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ( ch >= '0' && ch <= '9' ) ? number() : word() ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all white spaces.
|
||||
*/
|
||||
protected function white():void
|
||||
{
|
||||
while (ch)
|
||||
{
|
||||
if (ch <= ' ')
|
||||
{
|
||||
next();
|
||||
}
|
||||
else if (ch == '/')
|
||||
{
|
||||
switch ( next() )
|
||||
{
|
||||
case '/' :
|
||||
{
|
||||
while ( next() && ch != '\n' && ch != '\r')
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '*' :
|
||||
{
|
||||
next();
|
||||
for (;;)
|
||||
{
|
||||
if (ch)
|
||||
{
|
||||
if (ch == '*')
|
||||
{
|
||||
if ( next() == '/' )
|
||||
{
|
||||
next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
next();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error( JSONStrings.unterminatedComment );
|
||||
}
|
||||
}
|
||||
break ;
|
||||
}
|
||||
default :
|
||||
{
|
||||
error( JSONStrings.syntaxError );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check all special words in the source to evaluate.
|
||||
*/
|
||||
protected function word():*
|
||||
{
|
||||
if (ch == 't')
|
||||
{
|
||||
if (next() == 'r' && next() == 'u' && next() == 'e')
|
||||
{
|
||||
next() ;
|
||||
return true ;
|
||||
}
|
||||
}
|
||||
else if ( ch == 'f' )
|
||||
{
|
||||
if (next() == 'a' && next() == 'l' && next() == 's' && next() == 'e')
|
||||
{
|
||||
next() ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
else if ( ch == 'n' )
|
||||
{
|
||||
if (next() == 'u' && next() == 'l' && next() == 'l')
|
||||
{
|
||||
next() ;
|
||||
return null ;
|
||||
}
|
||||
}
|
||||
error( JSONStrings.syntaxError );
|
||||
return null ;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _prettyIndent:int = 0 ;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _prettyPrinting:Boolean ;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private var _indentor:String = " " ;
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
|
||||
Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
||||
The contents of this file are subject to the Mozilla Public License Version
|
||||
1.1 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS IS" basis,
|
||||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
for the specific language governing rights and limitations under the License.
|
||||
|
||||
The Original Code is VEGAS Framework.
|
||||
|
||||
The Initial Developer of the Original Code is
|
||||
ALCARAZ Marc (aka eKameleon) <ekameleon@gmail.com>.
|
||||
Portions created by the Initial Developer are Copyright (C) 2004-2011
|
||||
the Initial Developer. All Rights Reserved.
|
||||
|
||||
Contributor(s) :
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
use your version of this file under the terms of the MPL, indicate your
|
||||
decision by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the MPL, the GPL or the LGPL.
|
||||
|
||||
*/
|
||||
|
||||
package vegas.strings.json
|
||||
{
|
||||
/**
|
||||
* The string messages used in the JSON class.
|
||||
*/
|
||||
public class JSONStrings
|
||||
{
|
||||
/**
|
||||
* The bad Array error message.
|
||||
*/
|
||||
public static var badArray:String = "Bad Array" ;
|
||||
|
||||
/**
|
||||
* The bad key error message.
|
||||
*/
|
||||
public static var badKey:String = "Bad key" ;
|
||||
|
||||
/**
|
||||
* The bad Number error message.
|
||||
*/
|
||||
public static var badNumber:String = "Bad Number" ;
|
||||
|
||||
/**
|
||||
* The bad Object error message.
|
||||
*/
|
||||
public static var badObject:String = "Bad Object" ;
|
||||
|
||||
/**
|
||||
* The bad String error message.
|
||||
*/
|
||||
public static var badString:String = "Bad String" ;
|
||||
|
||||
/**
|
||||
* The mal formed Hexadecimal error message.
|
||||
*/
|
||||
public static var malFormedHexadecimal:String = "Mal formed Hexadecimal" ;
|
||||
|
||||
/**
|
||||
* The syntax error message.
|
||||
*/
|
||||
public static var syntaxError:String = "Syntax Error" ;
|
||||
|
||||
/**
|
||||
* The unterminated comment error message.
|
||||
*/
|
||||
public static var unterminatedComment:String = "Unterminated Comment" ;
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '63e782fb',
|
||||
'core.pkg.js' => '44aac665',
|
||||
'core.pkg.css' => '8fc8031a',
|
||||
'core.pkg.js' => '21041609',
|
||||
'darkconsole.pkg.js' => '8ab24e01',
|
||||
'differential.pkg.css' => '8af45893',
|
||||
'differential.pkg.js' => 'dad3622f',
|
||||
|
@ -342,9 +342,9 @@ return array(
|
|||
'rsrc/image/texture/table_header.png' => '5c433037',
|
||||
'rsrc/image/texture/table_header_hover.png' => '038ec3b9',
|
||||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||
'rsrc/js/application/aphlict/Aphlict.js' => '4a07e8e3',
|
||||
'rsrc/js/application/aphlict/Aphlict.js' => '464d333a',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'f6bc26f0',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'a826c925',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '1162a152',
|
||||
'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '58f7803f',
|
||||
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
||||
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de',
|
||||
|
@ -489,7 +489,6 @@ return array(
|
|||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||
'rsrc/swf/aphlict.swf' => 'f19daffb',
|
||||
),
|
||||
'symbols' => array(
|
||||
'almanac-css' => 'dbb9b3af',
|
||||
|
@ -536,10 +535,10 @@ return array(
|
|||
'herald-rule-editor' => '335fd41f',
|
||||
'herald-test-css' => '778b008e',
|
||||
'inline-comment-summary-css' => '8cfd34e8',
|
||||
'javelin-aphlict' => '4a07e8e3',
|
||||
'javelin-aphlict' => '464d333a',
|
||||
'javelin-behavior' => '61cbc29a',
|
||||
'javelin-behavior-aphlict-dropdown' => 'f6bc26f0',
|
||||
'javelin-behavior-aphlict-listen' => 'a826c925',
|
||||
'javelin-behavior-aphlict-listen' => '1162a152',
|
||||
'javelin-behavior-aphlict-status' => '58f7803f',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||
'javelin-behavior-aphront-crop' => 'fa0f4fc2',
|
||||
|
@ -909,6 +908,18 @@ return array(
|
|||
'javelin-uri',
|
||||
'javelin-install',
|
||||
),
|
||||
'1162a152' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-aphlict',
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-uri',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-router',
|
||||
'javelin-util',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'13c739ea' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1079,6 +1090,13 @@ return array(
|
|||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'464d333a' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-websocket',
|
||||
'javelin-leader',
|
||||
'javelin-json',
|
||||
),
|
||||
'469c0d9e' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -1100,10 +1118,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-util',
|
||||
),
|
||||
'4a07e8e3' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'4d94d9c3' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@ -1489,18 +1503,6 @@ return array(
|
|||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
),
|
||||
'a826c925' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-aphlict',
|
||||
'javelin-stratcom',
|
||||
'javelin-request',
|
||||
'javelin-uri',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-router',
|
||||
'javelin-util',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'a8d8459d' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
@ -2024,6 +2026,11 @@ return array(
|
|||
'sprite-tokens-css',
|
||||
'tokens-css',
|
||||
'phui-status-list-view-css',
|
||||
'phui-feed-story-css',
|
||||
'phabricator-feed-css',
|
||||
'phabricator-dashboard-css',
|
||||
'aphront-multi-column-view-css',
|
||||
'phui-action-header-view-css',
|
||||
),
|
||||
'core.pkg.js' => array(
|
||||
'javelin-util',
|
||||
|
@ -2093,6 +2100,11 @@ return array(
|
|||
'javelin-behavior-phabricator-show-older-transactions',
|
||||
'javelin-behavior-phui-timeline-dropdown-menu',
|
||||
'javelin-behavior-doorkeeper-tag',
|
||||
'phabricator-title',
|
||||
'javelin-leader',
|
||||
'javelin-websocket',
|
||||
'javelin-behavior-dashboard-async-panel',
|
||||
'javelin-behavior-dashboard-tab-panel',
|
||||
),
|
||||
'darkconsole.pkg.js' => array(
|
||||
'javelin-behavior-dark-console',
|
||||
|
|
|
@ -69,6 +69,11 @@ return array(
|
|||
'javelin-behavior-phabricator-show-older-transactions',
|
||||
'javelin-behavior-phui-timeline-dropdown-menu',
|
||||
'javelin-behavior-doorkeeper-tag',
|
||||
'phabricator-title',
|
||||
'javelin-leader',
|
||||
'javelin-websocket',
|
||||
'javelin-behavior-dashboard-async-panel',
|
||||
'javelin-behavior-dashboard-tab-panel',
|
||||
),
|
||||
'core.pkg.css' => array(
|
||||
'phabricator-core-css',
|
||||
|
@ -126,6 +131,12 @@ return array(
|
|||
'sprite-tokens-css',
|
||||
'tokens-css',
|
||||
'phui-status-list-view-css',
|
||||
|
||||
'phui-feed-story-css',
|
||||
'phabricator-feed-css',
|
||||
'phabricator-dashboard-css',
|
||||
'aphront-multi-column-view-css',
|
||||
'phui-action-header-view-css',
|
||||
),
|
||||
'differential.pkg.css' => array(
|
||||
'differential-core-view-css',
|
||||
|
|
|
@ -1239,7 +1239,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php',
|
||||
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
||||
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
||||
'PhabricatorAphlictManagementBuildWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementBuildWorkflow.php',
|
||||
'PhabricatorAphlictManagementDebugWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php',
|
||||
'PhabricatorAphlictManagementRestartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php',
|
||||
'PhabricatorAphlictManagementStartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php',
|
||||
|
@ -4393,7 +4392,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAlmanacApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||
'PhabricatorAnchorView' => 'AphrontView',
|
||||
'PhabricatorAphlictManagementBuildWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementDebugWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementRestartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementStartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAphlictManagementBuildWorkflow
|
||||
extends PhabricatorAphlictManagementWorkflow {
|
||||
|
||||
public function didConstruct() {
|
||||
$this
|
||||
->setName('build')
|
||||
->setSynopsis(pht('Build the Aphlict client.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'debug',
|
||||
'help' => 'Enable a debug build.',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$root = dirname(__FILE__).'/../../../..';
|
||||
|
||||
if (!Filesystem::binaryExists('mxmlc')) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
"The `mxmlc` binary was not found in PATH. This compiler binary ".
|
||||
"is required to rebuild the Aphlict client.\n\n".
|
||||
"Adjust your PATH, or install the Flex SDK from:\n\n".
|
||||
" http://flex.apache.org\n\n".
|
||||
"You may also be able to install it with `npm`:\n\n".
|
||||
" $ npm install flex-sdk\n\n".
|
||||
"(Note: you should only need to rebuild Aphlict if you are ".
|
||||
"developing Phabricator.)"));
|
||||
}
|
||||
|
||||
$argv = array(
|
||||
"-source-path=$root/externals/vegas/src",
|
||||
'-static-link-runtime-shared-libraries=true',
|
||||
'-warnings=true',
|
||||
'-strict=true',
|
||||
);
|
||||
|
||||
if ($args->getArg('debug')) {
|
||||
$argv[] = '-debug=true';
|
||||
}
|
||||
|
||||
list ($err, $stdout, $stderr) = exec_manual('mxmlc %Ls -output=%s %s',
|
||||
$argv,
|
||||
$root.'/webroot/rsrc/swf/aphlict.swf',
|
||||
$root.'/support/aphlict/client/src/AphlictClient.as');
|
||||
|
||||
if ($err) {
|
||||
$console->writeErr($stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
$console->writeOut("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@ final class PhabricatorAphlictManagementDebugWorkflow
|
|||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$this->willLaunch();
|
||||
$this->willLaunch(true);
|
||||
return $this->launch(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ abstract class PhabricatorAphlictManagementWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
final protected function willLaunch() {
|
||||
final protected function willLaunch($debug = false) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$pid = $this->getPID();
|
||||
|
@ -61,15 +61,66 @@ abstract class PhabricatorAphlictManagementWorkflow
|
|||
'running. Use `aphlict restart` to restart it.'));
|
||||
}
|
||||
|
||||
if (posix_getuid() != 0) {
|
||||
if (posix_getuid() == 0) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'You must run this script as root; the Aphlict server needs to bind '.
|
||||
'to privileged ports.'));
|
||||
// TODO: Update this message after a while.
|
||||
'The notification server should not be run as root. It no '.
|
||||
'longer requires access to privileged ports.'));
|
||||
}
|
||||
|
||||
// This will throw if we can't find an appropriate `node`.
|
||||
$this->getNodeBinary();
|
||||
// Make sure we can write to the PID file.
|
||||
if (!$debug) {
|
||||
Filesystem::writeFile($this->getPIDPath(), '');
|
||||
}
|
||||
|
||||
// First, start the server in configuration test mode with --test. This
|
||||
// will let us error explicitly if there are missing modules, before we
|
||||
// fork and lose access to the console.
|
||||
$test_argv = $this->getServerArgv($debug);
|
||||
$test_argv[] = '--test=true';
|
||||
|
||||
execx(
|
||||
'%s %s %Ls',
|
||||
$this->getNodeBinary(),
|
||||
$this->getAphlictScriptPath(),
|
||||
$test_argv);
|
||||
}
|
||||
|
||||
private function getServerArgv($debug) {
|
||||
$ssl_key = PhabricatorEnv::getEnvConfig('notification.ssl-key');
|
||||
$ssl_cert = PhabricatorEnv::getEnvConfig('notification.ssl-cert');
|
||||
|
||||
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
||||
$server_uri = new PhutilURI($server_uri);
|
||||
|
||||
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
||||
$client_uri = new PhutilURI($client_uri);
|
||||
|
||||
$log = PhabricatorEnv::getEnvConfig('notification.log');
|
||||
|
||||
$server_argv = array();
|
||||
$server_argv[] = '--port='.$client_uri->getPort();
|
||||
$server_argv[] = '--admin='.$server_uri->getPort();
|
||||
|
||||
if ($ssl_key) {
|
||||
$server_argv[] = '--ssl-key='.$ssl_key;
|
||||
}
|
||||
|
||||
if ($ssl_cert) {
|
||||
$server_argv[] = '--ssl-cert='.$ssl_cert;
|
||||
}
|
||||
|
||||
if (!$debug) {
|
||||
$server_argv[] = '--log='.$log;
|
||||
}
|
||||
|
||||
return $server_argv;
|
||||
}
|
||||
|
||||
private function getAphlictScriptPath() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
return $root.'/support/aphlict/server/aphlict_server.js';
|
||||
}
|
||||
|
||||
final protected function launch($debug = false) {
|
||||
|
@ -81,33 +132,11 @@ abstract class PhabricatorAphlictManagementWorkflow
|
|||
Filesystem::writeFile($this->getPIDPath(), getmypid());
|
||||
}
|
||||
|
||||
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
||||
$server_uri = new PhutilURI($server_uri);
|
||||
|
||||
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
||||
$client_uri = new PhutilURI($client_uri);
|
||||
|
||||
$user = PhabricatorEnv::getEnvConfig('notification.user');
|
||||
$log = PhabricatorEnv::getEnvConfig('notification.log');
|
||||
|
||||
$server_argv = array();
|
||||
$server_argv[] = csprintf('--port=%s', $client_uri->getPort());
|
||||
$server_argv[] = csprintf('--admin=%s', $server_uri->getPort());
|
||||
$server_argv[] = csprintf('--host=%s', $server_uri->getDomain());
|
||||
|
||||
if ($user) {
|
||||
$server_argv[] = csprintf('--user=%s', $user);
|
||||
}
|
||||
|
||||
if (!$debug) {
|
||||
$server_argv[] = csprintf('--log=%s', $log);
|
||||
}
|
||||
|
||||
$command = csprintf(
|
||||
'%s %s %C',
|
||||
'%s %s %Ls',
|
||||
$this->getNodeBinary(),
|
||||
dirname(__FILE__).'/../../../../support/aphlict/server/aphlict_server.js',
|
||||
implode(' ', $server_argv));
|
||||
$this->getAphlictScriptPath(),
|
||||
$this->getServerArgv($debug));
|
||||
|
||||
if (!$debug) {
|
||||
declare(ticks = 1);
|
||||
|
@ -159,6 +188,7 @@ abstract class PhabricatorAphlictManagementWorkflow
|
|||
fclose(STDOUT);
|
||||
fclose(STDERR);
|
||||
|
||||
|
||||
$this->launch();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -194,6 +194,11 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
|||
'This option has been renamed to `phabricator.show-prototypes` '.
|
||||
'to emphasize the unfinished nature of many prototype applications. '.
|
||||
'Your existing setting has been migrated.'),
|
||||
'notification.user' => pht(
|
||||
'The notification server no longer requires root permissions. Start '.
|
||||
'the server as the user you want it to run under.'),
|
||||
'notification.debug' => pht(
|
||||
'Notifications no longer have a dedicated debugging mode.'),
|
||||
);
|
||||
|
||||
return $ancient_config;
|
||||
|
|
|
@ -36,23 +36,21 @@ final class PhabricatorNotificationConfigOptions
|
|||
'string',
|
||||
'http://localhost:22281/')
|
||||
->setDescription(pht('Location of the notification receiver server.')),
|
||||
$this->newOption('notification.user', 'string', null)
|
||||
->setSummary(pht('Drop permissions to a less-privileged user.'))
|
||||
->setDescription(
|
||||
pht(
|
||||
'The notifcation server must be started as root so it can bind '.
|
||||
'to privileged ports, but if you specify a system user here it '.
|
||||
'will drop permissions to that user after binding to the ports '.
|
||||
'it needs.')),
|
||||
$this->newOption('notification.log', 'string', '/var/log/aphlict.log')
|
||||
->setDescription(pht('Location of the server log file.')),
|
||||
$this->newOption('notification.ssl-key', 'string', null)
|
||||
->setLocked(true)
|
||||
->setDescription(
|
||||
pht('Path to SSL key to use for secure WebSockets.')),
|
||||
$this->newOption('notification.ssl-cert', 'string', null)
|
||||
->setLocked(true)
|
||||
->setDescription(
|
||||
pht('Path to SSL certificate to use for secure WebSockets.')),
|
||||
$this->newOption(
|
||||
'notification.pidfile',
|
||||
'string',
|
||||
'/var/run/aphlict.pid')
|
||||
'/var/tmp/aphlict/pid/aphlict.pid')
|
||||
->setDescription(pht('Location of the server PID file.')),
|
||||
$this->newOption('notification.debug', 'bool', false)
|
||||
->setDescription(pht('Enable debug output in the browser.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,11 @@ final class PhabricatorNotificationTestController
|
|||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
|
||||
// NOTE: Because we don't currently show you your own notifications, make
|
||||
// sure this comes from a different PHID.
|
||||
$application_phid = id(new PhabricatorNotificationsApplication())
|
||||
->getPHID();
|
||||
|
||||
// TODO: When it's easier to get these buttons to render as forms, this
|
||||
// would be slightly nicer as a more standard isFormPost() check.
|
||||
|
||||
|
@ -24,7 +29,7 @@ final class PhabricatorNotificationTestController
|
|||
->setStoryType($story_type)
|
||||
->setStoryData($story_data)
|
||||
->setStoryTime(time())
|
||||
->setStoryAuthorPHID($viewer_phid)
|
||||
->setStoryAuthorPHID($application_phid)
|
||||
->setRelatedPHIDs(array($viewer_phid))
|
||||
->setPrimaryObjectPHID($viewer_phid)
|
||||
->setSubscribedPHIDs(array($viewer_phid))
|
||||
|
|
|
@ -38,7 +38,7 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck {
|
|||
->addCommand(
|
||||
pht(
|
||||
"(To start the server, run this command.)\n".
|
||||
"phabricator/ $ sudo ./bin/aphlict start"));
|
||||
"phabricator/ $ ./bin/aphlict start"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ final class PhabricatorAphlictSetupCheck extends PhabricatorSetupCheck {
|
|||
->setShortName(pht('Notification Server Version'))
|
||||
->setName(pht('Notification Server Out of Date'))
|
||||
->setMessage($message)
|
||||
->addCommand('phabricator/ $ sudo ./bin/aphlict restart');
|
||||
->addCommand('phabricator/ $ ./bin/aphlict restart');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,18 +13,8 @@ final class PhabricatorNotificationStatusView extends AphrontTagView {
|
|||
'nodeID' => $this->getID(),
|
||||
'pht' => array(
|
||||
'setup' => pht('Setting Up Client'),
|
||||
'start' => pht('Starting Client'),
|
||||
'ready' => pht('Ready to Connect'),
|
||||
'connecting' => pht('Connecting...'),
|
||||
'connected' => pht('Connected'),
|
||||
'error' => pht('Connection Error'),
|
||||
'client' => pht('Connected Locally'),
|
||||
|
||||
'error.flash.xdomain' => pht(
|
||||
'Unable to connect to Flash Policy Server. Check that the '.
|
||||
'notification server is running and port 843 is not firewalled.'),
|
||||
'error.flash.disconnected' => pht(
|
||||
'Disconnected from notification server.'),
|
||||
'open' => pht('Connected'),
|
||||
'closed' => pht('Disconnected'),
|
||||
),
|
||||
));
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
Guide to setting up notifications.
|
||||
|
||||
= Overview =
|
||||
Overview
|
||||
========
|
||||
|
||||
By default, Phabricator delivers information about events (like users creating
|
||||
tasks or commenting on code reviews) through email and in-application
|
||||
|
@ -20,61 +21,95 @@ To enable real-time notifications:
|
|||
|
||||
This document describes the process in detail.
|
||||
|
||||
= Running the Aphlict Server =
|
||||
|
||||
Phabricator implements realtime notifications using a Node.js server called
|
||||
"Aphlict". To run it:
|
||||
Supported Browsers
|
||||
==================
|
||||
|
||||
- Install node.js.
|
||||
- Run `bin/aphlict start` (this script must be run as root).
|
||||
Notifications are supported for browsers which support WebSockets. This covers
|
||||
most modern browsers (like Chrome, Firefox, Safari, and recent versions of
|
||||
Internet Explorer) and many mobile browsers.
|
||||
|
||||
The server must be able to listen on port **843** and port **22280** for Aphlict
|
||||
to work. You can change the latter port in the `notification.client-uri` config,
|
||||
but port 843 is used by Flash and can not be changed. In particular, if you're
|
||||
running in EC2, you need to unblock both of these ports in the server's security
|
||||
group configuration.
|
||||
IE8 and IE9 do not support WebSockets, so real-time notifications won't work in
|
||||
those browsers.
|
||||
|
||||
You may want to adjust these settings:
|
||||
|
||||
Installing Node and Modules
|
||||
===========================
|
||||
|
||||
The notification server uses Node.js, so you'll need to install it first.
|
||||
|
||||
To install Node.js, follow the instructions on
|
||||
[[ http://nodejs.org | nodejs.org ]].
|
||||
|
||||
You will also need to install the `ws` module for Node. After installing
|
||||
Node, run `npm install -g ws` to install it.
|
||||
|
||||
name="(Option 1, Recommended) Install 'ws' Module Globally"
|
||||
$ npm install -g ws # Global Install
|
||||
|
||||
If you prefer, you can also install it locally in the `support/aphlict/server/`
|
||||
directory:
|
||||
|
||||
name="(Option 2) Install 'ws' Module Locally"
|
||||
phabricator/support/aphlict/server/ $ npm install ws
|
||||
|
||||
Once Node.js and the `ws` module are installed, you're ready to start the
|
||||
server.
|
||||
|
||||
|
||||
Running the Aphlict Server
|
||||
==========================
|
||||
|
||||
After installing Node.js, you can control the notification server with the
|
||||
`bin/aphlict` command. To start the server:
|
||||
|
||||
phabricator/ $ bin/aphlict start
|
||||
|
||||
The server must be able to listen on port **22280** for Aphlict to work. In
|
||||
particular, if you're running in EC2, you need to unblock this port in the
|
||||
server's security group configuration. You can change this port in the
|
||||
`notification.client-uri` config.
|
||||
|
||||
You may need to adjust these settings:
|
||||
|
||||
- `notification.ssl-cert` Point this at an SSL certificate for secure
|
||||
WebSockets.
|
||||
- `notification.ssl-key` Point this at an SSL keyfile for secure WebSockets.
|
||||
|
||||
In particular, if your server uses HTTPS, you **must** configure these options.
|
||||
Browsers will not allow you to use non-SSL websockets from an SSL web page.
|
||||
|
||||
You may also want to adjust these settings:
|
||||
|
||||
- `notification.client-uri` Externally-facing host and port that browsers will
|
||||
connect to in order to listen for notifications.
|
||||
- `notification.server-uri` Internally-facing host and port that Phabricator
|
||||
will connect to in order to publish notifications.
|
||||
- `notification.log` Log file location for the server.
|
||||
- `notification.user` Non-root user to drop permissions to after binding to
|
||||
privileged ports.
|
||||
- `notification.pid` Pidfile location used to stop any running server when
|
||||
aphlict is restarted.
|
||||
|
||||
In most cases, the defaults are appropriate, except that you should set
|
||||
`notification.user` to some valid system user so Aphlict isn't running as root.
|
||||
|
||||
== Verifying Server Status ==
|
||||
Verifying Server Status
|
||||
=======================
|
||||
|
||||
Access `/notification/status/` to verify the server is operational. You should
|
||||
see a table showing stats like "uptime" and connection/message counts if the
|
||||
server is working. If it isn't working, you should see an error.
|
||||
|
||||
== Testing the Server ==
|
||||
You can also send a test notification by clicking the button in the upper right
|
||||
corner of this screen.
|
||||
|
||||
The easiest way to test the server is to have two users login and comment on
|
||||
the same Maniphest Task or Differential Revision. They should receive in-browser
|
||||
notifications about the other user's activity.
|
||||
|
||||
NOTE: This is cumbersome. There will be better testing tools at some point.
|
||||
|
||||
== Debugging Server Problems ==
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
You can run `aphlict` in the foreground to get output to your console:
|
||||
|
||||
phabricator/ $ sudo ./bin/aphlict debug
|
||||
phabricator/ $ ./bin/aphlict debug
|
||||
|
||||
You can run `support/aphlict/client/aphlict_test_client.php` to connect to the
|
||||
Aphlict server from the command line. Messages the client receives will be
|
||||
printed to stdout.
|
||||
|
||||
You can set `notification.debug` in your configuration to get additional
|
||||
output in your browser.
|
||||
Because the notification server uses WebSockets, your browser error console
|
||||
may also have information that is useful in figuring out what's wrong.
|
||||
|
||||
The server also generates a log, by default in `/var/log/aphlict.log`. You can
|
||||
change this location by changing `notification.log` in your configuration. The
|
||||
|
|
|
@ -371,9 +371,6 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||
if ($user && $user->isLoggedIn()) {
|
||||
|
||||
$aphlict_object_id = celerity_generate_unique_node_id();
|
||||
$aphlict_container_id = celerity_generate_unique_node_id();
|
||||
|
||||
$client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri');
|
||||
$client_uri = new PhutilURI($client_uri);
|
||||
if ($client_uri->getDomain() == 'localhost') {
|
||||
|
@ -382,37 +379,24 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||
$client_uri->setDomain($this_host->getDomain());
|
||||
}
|
||||
|
||||
$map = CelerityResourceMap::getNamedInstance('phabricator');
|
||||
$swf_uri = $response->getURI($map, 'rsrc/swf/aphlict.swf', true);
|
||||
|
||||
$enable_debug = PhabricatorEnv::getEnvConfig('notification.debug');
|
||||
|
||||
$subscriptions = $this->pageObjects;
|
||||
if ($user) {
|
||||
$subscriptions[] = $user->getPHID();
|
||||
}
|
||||
|
||||
if ($request->isHTTPS()) {
|
||||
$client_uri->setProtocol('wss');
|
||||
} else {
|
||||
$client_uri->setProtocol('ws');
|
||||
}
|
||||
|
||||
Javelin::initBehavior(
|
||||
'aphlict-listen',
|
||||
array(
|
||||
'id' => $aphlict_object_id,
|
||||
'containerID' => $aphlict_container_id,
|
||||
'server' => $client_uri->getDomain(),
|
||||
'port' => $client_uri->getPort(),
|
||||
'debug' => $enable_debug,
|
||||
'swfURI' => $swf_uri,
|
||||
'websocketURI' => (string)$client_uri,
|
||||
'pageObjects' => array_fill_keys($this->pageObjects, true),
|
||||
'subscriptions' => $subscriptions,
|
||||
));
|
||||
|
||||
$tail[] = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => $aphlict_container_id,
|
||||
'style' =>
|
||||
'position: absolute; width: 0; height: 0; overflow: hidden;',
|
||||
),
|
||||
'');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(dirname(__FILE__))));
|
||||
require_once $root.'/scripts/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline('test client for Aphlict server');
|
||||
$args->setSynopsis(<<<EOHELP
|
||||
**aphlict_test_client.php** [__options__]
|
||||
Connect to the Aphlict server configured in the Phabricator config.
|
||||
|
||||
EOHELP
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'server',
|
||||
'param' => 'uri',
|
||||
'default' => PhabricatorEnv::getEnvConfig('notification.client-uri'),
|
||||
'help' => 'Connect to __uri__ instead of the default server.',
|
||||
),
|
||||
));
|
||||
$console = PhutilConsole::getConsole();
|
||||
|
||||
$errno = null;
|
||||
$errstr = null;
|
||||
|
||||
$uri = $args->getArg('server');
|
||||
$uri = new PhutilURI($uri);
|
||||
$uri->setProtocol('tcp');
|
||||
|
||||
$console->writeErr("Connecting...\n");
|
||||
$socket = stream_socket_client(
|
||||
$uri,
|
||||
$errno,
|
||||
$errstr);
|
||||
|
||||
if (!$socket) {
|
||||
$console->writeErr(
|
||||
"Unable to connect to Aphlict (at '$uri'). Error #{$errno}: {$errstr}");
|
||||
exit(1);
|
||||
} else {
|
||||
$console->writeErr("Connected.\n");
|
||||
}
|
||||
|
||||
$io_channel = new PhutilSocketChannel($socket);
|
||||
$proto_channel = new PhutilJSONProtocolChannel($io_channel);
|
||||
|
||||
$json = new PhutilJSON();
|
||||
while (true) {
|
||||
$message = $proto_channel->waitForMessage();
|
||||
$console->writeOut($json->encodeFormatted($message));
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package {
|
||||
|
||||
import flash.display.Sprite;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.net.LocalConnection;
|
||||
|
||||
|
||||
public class Aphlict extends Sprite {
|
||||
|
||||
/**
|
||||
* A transport channel used to receive data.
|
||||
*/
|
||||
protected var recv:LocalConnection;
|
||||
|
||||
/**
|
||||
* A transport channel used to send data.
|
||||
*/
|
||||
protected var send:LocalConnection;
|
||||
|
||||
|
||||
public function Aphlict() {
|
||||
super();
|
||||
|
||||
this.recv = new LocalConnection();
|
||||
this.recv.client = this;
|
||||
|
||||
this.send = new LocalConnection();
|
||||
}
|
||||
|
||||
final protected function externalInvoke(
|
||||
type:String,
|
||||
object:Object = null):void {
|
||||
|
||||
ExternalInterface.call('JX.Aphlict.didReceiveEvent', type, object);
|
||||
}
|
||||
|
||||
final protected function error(error:Object):void {
|
||||
this.externalInvoke('error', error.toString());
|
||||
}
|
||||
|
||||
final protected function log(message:String):void {
|
||||
this.externalInvoke('log', message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
package {
|
||||
|
||||
import flash.events.TimerEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Timer;
|
||||
import flash.events.UncaughtErrorEvent;
|
||||
|
||||
final public class AphlictClient extends Aphlict {
|
||||
|
||||
/**
|
||||
* The connection name for this client. This will be used for the
|
||||
* @{class:LocalConnection} object.
|
||||
*/
|
||||
private var client:String;
|
||||
|
||||
/**
|
||||
* The expiry timestamp for the @{class:AphlictMaster}. If this time is
|
||||
* elapsed then the master will be assumed to be dead and another
|
||||
* @{class:AphlictClient} will create a master.
|
||||
*/
|
||||
private var expiry:Number = 0;
|
||||
|
||||
/**
|
||||
* The interval at which to ping the @{class:AphlictMaster}.
|
||||
*/
|
||||
public static const INTERVAL:Number = 3000;
|
||||
|
||||
private var master:AphlictMaster;
|
||||
private var timer:Timer;
|
||||
|
||||
private var remoteServer:String;
|
||||
private var remotePort:Number;
|
||||
private var subscriptions:Array;
|
||||
|
||||
|
||||
public function AphlictClient() {
|
||||
super();
|
||||
|
||||
loaderInfo.uncaughtErrorEvents.addEventListener(
|
||||
UncaughtErrorEvent.UNCAUGHT_ERROR,
|
||||
this.uncaughtErrorHandler);
|
||||
|
||||
ExternalInterface.marshallExceptions = true;
|
||||
ExternalInterface.addCallback('connect', this.externalConnect);
|
||||
|
||||
this.setStatus('ready');
|
||||
}
|
||||
|
||||
private function uncaughtErrorHandler(event:UncaughtErrorEvent):void {
|
||||
this.error(event.error.toString());
|
||||
}
|
||||
|
||||
public function externalConnect(
|
||||
server:String,
|
||||
port:Number,
|
||||
subscriptions:Array):void {
|
||||
|
||||
this.remoteServer = server;
|
||||
this.remotePort = port;
|
||||
this.subscriptions = subscriptions;
|
||||
|
||||
this.client = AphlictClient.generateClientId();
|
||||
this.recv.connect(this.client);
|
||||
|
||||
this.timer = new Timer(AphlictClient.INTERVAL);
|
||||
this.timer.addEventListener(TimerEvent.TIMER, this.keepalive);
|
||||
|
||||
this.connectToMaster();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique identifier that will be used to communicate with the
|
||||
* @{class:AphlictMaster}.
|
||||
*/
|
||||
private static function generateClientId():String {
|
||||
return 'aphlict_client_' + Math.round(Math.random() * 100000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new connection to the @{class:AphlictMaster}.
|
||||
*
|
||||
* If there is no current @{class:AphlictMaster} instance, then a new master
|
||||
* will be created.
|
||||
*/
|
||||
private function connectToMaster():void {
|
||||
this.timer.stop();
|
||||
|
||||
// Try to become the master.
|
||||
try {
|
||||
this.log('Attempting to become the master...');
|
||||
this.master = new AphlictMaster(this.remoteServer, this.remotePort);
|
||||
this.log('I am the master.');
|
||||
} catch (err:ArgumentError) {
|
||||
this.log('Cannot become the master... probably one already exists');
|
||||
} catch (err:Error) {
|
||||
this.error(err);
|
||||
}
|
||||
|
||||
this.registerWithMaster();
|
||||
this.timer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register our client ID with the @{class:AphlictMaster} and send our
|
||||
* subscriptions.
|
||||
*/
|
||||
private function registerWithMaster():void {
|
||||
this.send.send('aphlict_master', 'register', this.client);
|
||||
this.expiry = new Date().getTime() + (5 * AphlictClient.INTERVAL);
|
||||
this.log('Registered client ' + this.client);
|
||||
|
||||
// Send subscriptions to master.
|
||||
this.log('Sending subscriptions to master.');
|
||||
this.send.send(
|
||||
'aphlict_master',
|
||||
'subscribe',
|
||||
this.client,
|
||||
this.subscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a keepalive signal to the @{class:AphlictMaster}.
|
||||
*
|
||||
* If the connection to the master has expired (because the master has not
|
||||
* sent a heartbeat signal), then a new connection to master will be
|
||||
* created.
|
||||
*/
|
||||
private function keepalive(event:TimerEvent):void {
|
||||
if (new Date().getTime() > this.expiry) {
|
||||
this.connectToMaster();
|
||||
}
|
||||
|
||||
this.send.send('aphlict_master', 'ping', this.client);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to receive the heartbeat signal from the
|
||||
* @{class:AphlictMaster}.
|
||||
*/
|
||||
public function pong():void {
|
||||
this.expiry = new Date().getTime() + (2 * AphlictClient.INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive a message from the Aphlict Server, via the
|
||||
* @{class:AphlictMaster}.
|
||||
*/
|
||||
public function receiveMessage(msg:Object):void {
|
||||
this.log('Received message.');
|
||||
this.externalInvoke('receive', msg);
|
||||
}
|
||||
|
||||
public function setStatus(status:String, code:String = null):void {
|
||||
this.externalInvoke('status', {type: status, code: code});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,312 +0,0 @@
|
|||
package {
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.net.Socket;
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.Timer;
|
||||
import vegas.strings.JSON;
|
||||
|
||||
|
||||
final public class AphlictMaster extends Aphlict {
|
||||
|
||||
/**
|
||||
* The pool of connected clients.
|
||||
*/
|
||||
private var clients:Dictionary;
|
||||
|
||||
/**
|
||||
* A timer used to trigger periodic events.
|
||||
*/
|
||||
private var timer:Timer;
|
||||
|
||||
/**
|
||||
* The interval after which clients will be considered dead and removed
|
||||
* from the pool.
|
||||
*/
|
||||
public static const PURGE_INTERVAL:Number = 3 * AphlictClient.INTERVAL;
|
||||
|
||||
/**
|
||||
* The hostname for the Aphlict Server.
|
||||
*/
|
||||
private var remoteServer:String;
|
||||
|
||||
/**
|
||||
* The port number for the Aphlict Server.
|
||||
*/
|
||||
private var remotePort:Number;
|
||||
|
||||
/**
|
||||
* A dictionary mapping PHID to subscribed clients.
|
||||
*/
|
||||
private var subscriptions:Dictionary;
|
||||
|
||||
private var socket:Socket;
|
||||
private var readBuffer:ByteArray;
|
||||
|
||||
private var status:String;
|
||||
private var statusCode:String;
|
||||
|
||||
|
||||
public function AphlictMaster(server:String, port:Number) {
|
||||
super();
|
||||
|
||||
this.remoteServer = server;
|
||||
this.remotePort = port;
|
||||
|
||||
this.clients = new Dictionary();
|
||||
this.subscriptions = new Dictionary();
|
||||
|
||||
// Connect to the Aphlict Server.
|
||||
this.recv.connect('aphlict_master');
|
||||
this.connectToServer();
|
||||
|
||||
// Start a timer and regularly purge dead clients.
|
||||
this.timer = new Timer(AphlictMaster.PURGE_INTERVAL);
|
||||
this.timer.addEventListener(TimerEvent.TIMER, this.purgeClients);
|
||||
this.timer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a @{class:AphlictClient}.
|
||||
*/
|
||||
public function register(client:String):void {
|
||||
if (!this.clients[client]) {
|
||||
this.log('Registering client: ' + client);
|
||||
this.clients[client] = new Date().getTime();
|
||||
|
||||
this.send.send(client, 'setStatus', this.status, this.statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge stale client connections from the client pool.
|
||||
*/
|
||||
private function purgeClients(event:TimerEvent):void {
|
||||
for (var client:String in this.clients) {
|
||||
var checkin:Number = this.clients[client];
|
||||
|
||||
if (new Date().getTime() - checkin > AphlictMaster.PURGE_INTERVAL) {
|
||||
this.log('Purging client: ' + client);
|
||||
delete this.clients[client];
|
||||
|
||||
this.log('Removing client subscriptions: ' + client);
|
||||
this.unsubscribeAll(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clients will regularly "ping" the master to let us know that they are
|
||||
* still alive. We will "pong" them back to let the client know that the
|
||||
* master is still alive.
|
||||
*/
|
||||
public function ping(client:String):void {
|
||||
this.clients[client] = new Date().getTime();
|
||||
this.send.send(client, 'pong');
|
||||
}
|
||||
|
||||
private function connectToServer():void {
|
||||
this.setStatusOnClients('connecting');
|
||||
|
||||
var socket:Socket = new Socket();
|
||||
|
||||
socket.addEventListener(Event.CONNECT, didConnectSocket);
|
||||
socket.addEventListener(Event.CLOSE, didCloseSocket);
|
||||
socket.addEventListener(ProgressEvent.SOCKET_DATA, didReceiveSocket);
|
||||
|
||||
socket.addEventListener(IOErrorEvent.IO_ERROR, didIOErrorSocket);
|
||||
socket.addEventListener(
|
||||
SecurityErrorEvent.SECURITY_ERROR,
|
||||
didSecurityErrorSocket);
|
||||
|
||||
socket.connect(this.remoteServer, this.remotePort);
|
||||
|
||||
this.readBuffer = new ByteArray();
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
private function didConnectSocket(event:Event):void {
|
||||
this.setStatusOnClients('connected');
|
||||
|
||||
// Send subscriptions
|
||||
var phids = new Array();
|
||||
for (var phid:String in this.subscriptions) {
|
||||
phids.push(phid);
|
||||
}
|
||||
|
||||
if (phids.length) {
|
||||
this.sendSubscribeCommand(phids);
|
||||
}
|
||||
}
|
||||
|
||||
private function didCloseSocket(event:Event):void {
|
||||
this.setStatusOnClients('error', 'error.flash.disconnected');
|
||||
}
|
||||
|
||||
private function didIOErrorSocket(event:IOErrorEvent):void {
|
||||
this.externalInvoke('error', event.text);
|
||||
}
|
||||
|
||||
private function didSecurityErrorSocket(event:SecurityErrorEvent):void {
|
||||
var text = event.text;
|
||||
|
||||
// This is really gross but there doesn't seem to be anything else
|
||||
// on the object which gives us an error code.
|
||||
if (text.match(/^Error #2048/)) {
|
||||
this.setStatusOnClients('error', 'error.flash.xdomain');
|
||||
}
|
||||
|
||||
this.error(text);
|
||||
}
|
||||
|
||||
public function subscribe(client:String, phids:Array):void {
|
||||
var newPHIDs = new Array();
|
||||
|
||||
for (var i:String in phids) {
|
||||
var phid = phids[i];
|
||||
if (!this.subscriptions[phid]) {
|
||||
this.subscriptions[phid] = new Dictionary();
|
||||
newPHIDs.push(phid);
|
||||
}
|
||||
this.subscriptions[phid][client] = true;
|
||||
}
|
||||
|
||||
if (newPHIDs.length) {
|
||||
this.sendSubscribeCommand(newPHIDs);
|
||||
}
|
||||
}
|
||||
|
||||
private function getSubscriptions(client:String):Array {
|
||||
var subscriptions = new Array();
|
||||
|
||||
for (var phid:String in this.subscriptions) {
|
||||
var clients = this.subscriptions[phid];
|
||||
if (clients[client]) {
|
||||
subscriptions.push(phid);
|
||||
}
|
||||
}
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public function unsubscribeAll(client:String):void {
|
||||
this.unsubscribe(client, this.getSubscriptions(client));
|
||||
}
|
||||
|
||||
public function unsubscribe(client:String, phids:Array):void {
|
||||
var oldPHIDs = new Array();
|
||||
|
||||
for (var i:String in phids) {
|
||||
var phid = phids[i];
|
||||
|
||||
if (!this.subscriptions[phid]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
delete this.subscriptions[phid][client];
|
||||
|
||||
var empty = true;
|
||||
for (var key:String in this.subscriptions[phid]) {
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
delete this.subscriptions[phid];
|
||||
oldPHIDs.push(phid);
|
||||
}
|
||||
}
|
||||
|
||||
if (oldPHIDs.length) {
|
||||
this.sendUnsubscribeCommand(oldPHIDs);
|
||||
}
|
||||
}
|
||||
|
||||
private function sendSubscribeCommand(phids:Array):void {
|
||||
var msg:Dictionary = new Dictionary();
|
||||
msg['command'] = 'subscribe';
|
||||
msg['data'] = phids;
|
||||
|
||||
this.log('Sending subscribe command to server.');
|
||||
this.socket.writeUTF(vegas.strings.JSON.serialize(msg));
|
||||
this.socket.flush();
|
||||
}
|
||||
|
||||
private function sendUnsubscribeCommand(phids:Array):void {
|
||||
var msg:Dictionary = new Dictionary();
|
||||
msg['command'] = 'unsubscribe';
|
||||
msg['data'] = phids;
|
||||
|
||||
this.log('Sending subscribe command to server.');
|
||||
this.socket.writeUTF(vegas.strings.JSON.serialize(msg));
|
||||
this.socket.flush();
|
||||
}
|
||||
|
||||
private function didReceiveSocket(event:Event):void {
|
||||
try {
|
||||
var b:ByteArray = this.readBuffer;
|
||||
this.socket.readBytes(b, b.length);
|
||||
|
||||
do {
|
||||
b = this.readBuffer;
|
||||
b.position = 0;
|
||||
|
||||
if (b.length <= 8) {
|
||||
break;
|
||||
}
|
||||
|
||||
var msg_len:Number = parseInt(b.readUTFBytes(8), 10);
|
||||
if (b.length >= msg_len + 8) {
|
||||
var bytes:String = b.readUTFBytes(msg_len);
|
||||
var data:Object = vegas.strings.JSON.deserialize(bytes);
|
||||
var t:ByteArray = new ByteArray();
|
||||
t.writeBytes(b, msg_len + 8);
|
||||
this.readBuffer = t;
|
||||
|
||||
// Send the message to all clients.
|
||||
for (var client:String in this.clients) {
|
||||
var subscribed = false;
|
||||
|
||||
for (var i:String in data.subscribers) {
|
||||
var phid = data.subscribers[i];
|
||||
|
||||
if (this.subscriptions[phid] &&
|
||||
this.subscriptions[phid][client]) {
|
||||
subscribed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (subscribed) {
|
||||
this.log('Sending message to client: ' + client);
|
||||
this.send.send(client, 'receiveMessage', data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
} catch (err:Error) {
|
||||
this.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private function setStatusOnClients(
|
||||
status:String,
|
||||
code:String = null):void {
|
||||
|
||||
this.status = status;
|
||||
this.statusCode = code;
|
||||
|
||||
for (var client:String in this.clients) {
|
||||
this.send.send(client, 'setStatus', status, code);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,9 @@
|
|||
/**
|
||||
* Notification server. Launch with:
|
||||
*
|
||||
* sudo node aphlict_server.js --user=aphlict
|
||||
*
|
||||
* You can also specify `port`, `admin`, `host` and `log`.
|
||||
*/
|
||||
|
||||
var JX = require('./lib/javelin').JX;
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var util = require('util');
|
||||
var fs = require('fs');
|
||||
|
||||
JX.require('lib/AphlictFlashPolicyServer', __dirname);
|
||||
JX.require('lib/AphlictListenerList', __dirname);
|
||||
JX.require('lib/AphlictLog', __dirname);
|
||||
|
||||
|
@ -17,8 +12,10 @@ function parse_command_line_arguments(argv) {
|
|||
port: 22280,
|
||||
admin: 22281,
|
||||
host: '127.0.0.1',
|
||||
user: null,
|
||||
log: '/var/log/aphlict.log'
|
||||
log: '/var/log/aphlict.log',
|
||||
'ssl-key': null,
|
||||
'ssl-certificate': null,
|
||||
test: false
|
||||
};
|
||||
|
||||
for (var ii = 2; ii < argv.length; ii++) {
|
||||
|
@ -42,124 +39,120 @@ function parse_command_line_arguments(argv) {
|
|||
var debug = new JX.AphlictLog()
|
||||
.addConsole(console);
|
||||
|
||||
var clients = new JX.AphlictListenerList();
|
||||
|
||||
var config = parse_command_line_arguments(process.argv);
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
var WebSocket;
|
||||
try {
|
||||
WebSocket = require('ws');
|
||||
} catch (ex) {
|
||||
throw new Error(
|
||||
'You need to install the Node.js "ws" module for websocket support. ' +
|
||||
'Usually, you can do this with `npm install -g ws`. ' + ex.toString());
|
||||
}
|
||||
|
||||
var ssl_config = {
|
||||
enabled: (config['ssl-key'] || config['ssl-cert'])
|
||||
};
|
||||
|
||||
// Load the SSL certificates (if any were provided) now, so that runs with
|
||||
// `--test` will see any errors.
|
||||
if (ssl_config.enabled) {
|
||||
ssl_config.key = fs.readFileSync(config['ssl-key']);
|
||||
ssl_config.cert = fs.readFileSync(config['ssl-cert']);
|
||||
}
|
||||
|
||||
// Add the logfile so we'll fail if we can't write to it.
|
||||
if (config.logfile) {
|
||||
debug.addLogfile(config.logfile);
|
||||
}
|
||||
|
||||
if (process.getuid() !== 0) {
|
||||
console.log(
|
||||
"ERROR: " +
|
||||
"This server must be run as root because it needs to bind to privileged " +
|
||||
"port 843 to start a Flash policy server. It will downgrade to run as a " +
|
||||
"less-privileged user after binding if you pass a user in the command " +
|
||||
"line arguments with '--user=alincoln'.");
|
||||
process.exit(1);
|
||||
// If we're just doing a configuration test, exit here before starting any
|
||||
// servers.
|
||||
if (config.test) {
|
||||
debug.log('Configuration test OK.');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var net = require('net');
|
||||
var http = require('http');
|
||||
var start_time = new Date().getTime();
|
||||
var messages_out = 0;
|
||||
var messages_in = 0;
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
debug.log('\n<<< UNCAUGHT EXCEPTION! >>>\n' + err.stack);
|
||||
var clients = new JX.AphlictListenerList();
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
function https_discard_handler(req, res) {
|
||||
res.writeHead(501);
|
||||
res.end('HTTP/501 Use Websockets\n');
|
||||
}
|
||||
|
||||
new JX.AphlictFlashPolicyServer()
|
||||
.setDebugLog(debug)
|
||||
.setAccessPort(config.port)
|
||||
.start();
|
||||
var ws;
|
||||
if (ssl_config.enabled) {
|
||||
var https_server = https.createServer({
|
||||
key: ssl_config.key,
|
||||
cert: ssl_config.cert
|
||||
}, https_discard_handler).listen(config.port);
|
||||
|
||||
ws = new WebSocket.Server({server: https_server});
|
||||
} else {
|
||||
ws = new WebSocket.Server({port: config.port});
|
||||
}
|
||||
|
||||
net.createServer(function(socket) {
|
||||
var listener = clients.addListener(socket);
|
||||
ws.on('connection', function(ws) {
|
||||
var listener = clients.addListener(ws);
|
||||
|
||||
debug.log('<%s> Connected from %s',
|
||||
listener.getDescription(),
|
||||
socket.remoteAddress);
|
||||
function log() {
|
||||
debug.log(
|
||||
util.format('<%s>', listener.getDescription()) +
|
||||
' ' +
|
||||
util.format.apply(null, arguments));
|
||||
}
|
||||
|
||||
var buffer = new Buffer([]);
|
||||
var length = 0;
|
||||
log('Connected from %s.', ws._socket.remoteAddress);
|
||||
|
||||
socket.on('data', function(data) {
|
||||
buffer = Buffer.concat([buffer, new Buffer(data)]);
|
||||
ws.on('message', function(data) {
|
||||
log('Received message: %s', data);
|
||||
|
||||
while (buffer.length) {
|
||||
if (!length) {
|
||||
length = buffer.readUInt16BE(0);
|
||||
buffer = buffer.slice(2);
|
||||
}
|
||||
var message;
|
||||
try {
|
||||
message = JSON.parse(data);
|
||||
} catch (err) {
|
||||
log('Message is invalid: %s', err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.length < length) {
|
||||
// We need to wait for the rest of the data.
|
||||
return;
|
||||
}
|
||||
switch (message.command) {
|
||||
case 'subscribe':
|
||||
log(
|
||||
'Subscribed to: %s',
|
||||
JSON.stringify(message.data));
|
||||
listener.subscribe(message.data);
|
||||
break;
|
||||
|
||||
var message;
|
||||
try {
|
||||
message = JSON.parse(buffer.toString('utf8', 0, length));
|
||||
} catch (err) {
|
||||
debug.log('<%s> Received invalid data.', listener.getDescription());
|
||||
continue;
|
||||
} finally {
|
||||
buffer = buffer.slice(length);
|
||||
length = 0;
|
||||
}
|
||||
case 'unsubscribe':
|
||||
log(
|
||||
'Unsubscribed from: %s',
|
||||
JSON.stringify(message.data));
|
||||
listener.unsubscribe(message.data);
|
||||
break;
|
||||
|
||||
debug.log('<%s> Received data: %s',
|
||||
listener.getDescription(),
|
||||
JSON.stringify(message));
|
||||
|
||||
switch (message.command) {
|
||||
case 'subscribe':
|
||||
debug.log(
|
||||
'<%s> Subscribed to: %s',
|
||||
listener.getDescription(),
|
||||
JSON.stringify(message.data));
|
||||
listener.subscribe(message.data);
|
||||
break;
|
||||
|
||||
case 'unsubscribe':
|
||||
debug.log(
|
||||
'<%s> Unsubscribed from: %s',
|
||||
listener.getDescription(),
|
||||
JSON.stringify(message.data));
|
||||
listener.unsubscribe(message.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
debug.log('<s> Unrecognized command.', listener.getDescription());
|
||||
}
|
||||
default:
|
||||
log('Unrecognized command "%s".', message.command || '<undefined>');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('close', function() {
|
||||
ws.on('close', function() {
|
||||
clients.removeListener(listener);
|
||||
debug.log('<%s> Disconnected', listener.getDescription());
|
||||
log('Disconnected.');
|
||||
});
|
||||
|
||||
socket.on('timeout', function() {
|
||||
debug.log('<%s> Timed Out', listener.getDescription());
|
||||
ws.on('error', function(err) {
|
||||
log('Error: %s', err.message);
|
||||
});
|
||||
|
||||
socket.on('end', function() {
|
||||
debug.log('<%s> Ended Connection', listener.getDescription());
|
||||
});
|
||||
|
||||
socket.on('error', function(e) {
|
||||
debug.log('<%s> Error: %s', listener.getDescription(), e);
|
||||
});
|
||||
|
||||
}).listen(config.port);
|
||||
|
||||
|
||||
var messages_out = 0;
|
||||
var messages_in = 0;
|
||||
var start_time = new Date().getTime();
|
||||
});
|
||||
|
||||
function transmit(msg) {
|
||||
var listeners = clients.getListeners().filter(function(client) {
|
||||
|
@ -195,7 +188,7 @@ http.createServer(function(request, response) {
|
|||
try {
|
||||
var msg = JSON.parse(body);
|
||||
|
||||
debug.log('notification: ' + JSON.stringify(msg));
|
||||
debug.log('Received notification: ' + JSON.stringify(msg));
|
||||
++messages_in;
|
||||
|
||||
try {
|
||||
|
@ -242,10 +235,4 @@ http.createServer(function(request, response) {
|
|||
}
|
||||
}).listen(config.admin, config.host);
|
||||
|
||||
// If we're configured to drop permissions, get rid of them now that we've
|
||||
// bound to the ports we need and opened logfiles.
|
||||
if (config.user) {
|
||||
process.setuid(config.user);
|
||||
}
|
||||
|
||||
debug.log('Started Server (PID %d)', process.pid);
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
var JX = require('javelin').JX;
|
||||
|
||||
var net = require('net');
|
||||
|
||||
/**
|
||||
* Server which handles cross-domain policy requests for Flash.
|
||||
*
|
||||
* var server = new AphlictFlashPolicyServer()
|
||||
* .setAccessPort(9999)
|
||||
* .start();
|
||||
*/
|
||||
JX.install('AphlictFlashPolicyServer', {
|
||||
|
||||
members: {
|
||||
_server: null,
|
||||
_port: 843,
|
||||
_accessPort: null,
|
||||
_debug: null,
|
||||
|
||||
setDebugLog: function(log) {
|
||||
this._debug = log;
|
||||
return this;
|
||||
},
|
||||
|
||||
setAccessPort: function(port) {
|
||||
this._accessPort = port;
|
||||
return this;
|
||||
},
|
||||
|
||||
start: function() {
|
||||
this._server = net.createServer(JX.bind(this, this._didConnect));
|
||||
this._server.listen(this._port);
|
||||
return this;
|
||||
},
|
||||
|
||||
_didConnect: function(socket) {
|
||||
this._log('<FlashPolicy> Policy Request From %s', socket.remoteAddress);
|
||||
|
||||
socket.on('error', JX.bind(this, this._didSocketError, socket));
|
||||
|
||||
socket.write(this._getFlashPolicyResponse());
|
||||
socket.end();
|
||||
},
|
||||
|
||||
_didSocketError: function(socket, error) {
|
||||
this._log('<FlashPolicy> Socket Error: %s', error);
|
||||
},
|
||||
|
||||
_log: function() {
|
||||
this._debug && this._debug.log.apply(this._debug, arguments);
|
||||
},
|
||||
|
||||
_getFlashPolicyResponse: function() {
|
||||
var policy = [
|
||||
'<?xml version="1.0"?>',
|
||||
'<!DOCTYPE cross-domain-policy SYSTEM ' +
|
||||
'"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">',
|
||||
'<cross-domain-policy>',
|
||||
'<allow-access-from domain="*" to-ports="' + this._accessPort + '"/>',
|
||||
'</cross-domain-policy>'
|
||||
];
|
||||
|
||||
return policy.join('\n') + '\0';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
|
@ -49,15 +49,7 @@ JX.install('AphlictListener', {
|
|||
},
|
||||
|
||||
writeMessage: function(message) {
|
||||
var serial = JSON.stringify(message);
|
||||
|
||||
var length = Buffer.byteLength(serial, 'utf8');
|
||||
length = length.toString();
|
||||
while (length.length < 8) {
|
||||
length = '0' + length;
|
||||
}
|
||||
|
||||
this._socket.write(length + serial);
|
||||
this._socket.send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,39 +2,31 @@
|
|||
* @provides javelin-aphlict
|
||||
* @requires javelin-install
|
||||
* javelin-util
|
||||
* javelin-websocket
|
||||
* javelin-leader
|
||||
* javelin-json
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple JS API for the Flash Aphlict client. Example usage:
|
||||
* Client for the notification server. Example usage:
|
||||
*
|
||||
* var aphlict = new JX.Aphlict('aphlict_swf', '127.0.0.1', 22280)
|
||||
* .setHandler(function(type, message) {
|
||||
* JX.log("Got " + type + " event!")
|
||||
* var aphlict = new JX.Aphlict('ws://localhost:22280', subscriptions)
|
||||
* .setHandler(function(message) {
|
||||
* // ...
|
||||
* })
|
||||
* .start();
|
||||
*
|
||||
* Your handler will receive these events:
|
||||
*
|
||||
* - `connect` The client initiated a connection to the server.
|
||||
* - `connected` The client completed a connection to the server.
|
||||
* - `close` The client disconnected from the server.
|
||||
* - `error` There was an error.
|
||||
* - `receive` Received a message from the server.
|
||||
*
|
||||
* You do not have to handle any of them in any specific way.
|
||||
*/
|
||||
JX.install('Aphlict', {
|
||||
|
||||
construct: function(id, server, port, subscriptions) {
|
||||
construct: function(uri, subscriptions) {
|
||||
if (__DEV__) {
|
||||
if (JX.Aphlict._instance) {
|
||||
JX.$E('Aphlict object is a singleton.');
|
||||
}
|
||||
}
|
||||
|
||||
this._id = id;
|
||||
this._server = server;
|
||||
this._port = port;
|
||||
this._uri = uri;
|
||||
this._subscriptions = subscriptions;
|
||||
this._setStatus('setup');
|
||||
|
||||
|
@ -44,7 +36,6 @@ JX.install('Aphlict', {
|
|||
events: ['didChangeStatus'],
|
||||
|
||||
members: {
|
||||
_id: null,
|
||||
_server: null,
|
||||
_port: null,
|
||||
_subscriptions: null,
|
||||
|
@ -52,47 +43,92 @@ JX.install('Aphlict', {
|
|||
_statusCode: null,
|
||||
|
||||
start: function(node, uri) {
|
||||
this._setStatus('start');
|
||||
JX.Leader.listen('onBecomeLeader', JX.bind(this, this._lead));
|
||||
JX.Leader.listen('onReceiveBroadcast', JX.bind(this, this._receive));
|
||||
JX.Leader.start();
|
||||
|
||||
// NOTE: This is grotesque, but seems to work everywhere.
|
||||
node.innerHTML =
|
||||
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">' +
|
||||
'<param name="movie" value="' + uri + '" />' +
|
||||
'<param name="allowScriptAccess" value="always" />' +
|
||||
'<param name="wmode" value="opaque" />' +
|
||||
'<embed src="' + uri + '" wmode="opaque"' +
|
||||
'width="0" height="0" id="' + this._id + '">' +
|
||||
'</embed>' +
|
||||
'</object>';
|
||||
},
|
||||
|
||||
_didStartFlash: function() {
|
||||
var id = this._id;
|
||||
|
||||
// Flash puts its "objects" into global scope in an inconsistent way,
|
||||
// because it was written in like 1816 when globals were awesome and IE4
|
||||
// didn't support other scopes since global scope is the best anyway.
|
||||
var container = document[id] || window[id];
|
||||
|
||||
this._flashContainer = container;
|
||||
this._flashContainer.connect(
|
||||
this._server,
|
||||
this._port,
|
||||
this._subscriptions);
|
||||
JX.Leader.call(JX.bind(this, this._begin));
|
||||
},
|
||||
|
||||
getStatus: function() {
|
||||
return this._status;
|
||||
},
|
||||
|
||||
getStatusCode: function() {
|
||||
return this._statusCode;
|
||||
_begin: function() {
|
||||
JX.Leader.broadcast(
|
||||
null,
|
||||
{type: 'aphlict.getstatus'});
|
||||
JX.Leader.broadcast(
|
||||
null,
|
||||
{type: 'aphlict.subscribe', data: this._subscriptions});
|
||||
},
|
||||
|
||||
_setStatus: function(status, code) {
|
||||
_lead: function() {
|
||||
var socket = new JX.WebSocket(this._uri);
|
||||
socket.setOpenHandler(JX.bind(this, this._open));
|
||||
socket.setMessageHandler(JX.bind(this, this._message));
|
||||
socket.setCloseHandler(JX.bind(this, this._close));
|
||||
|
||||
this._socket = socket;
|
||||
|
||||
socket.open();
|
||||
},
|
||||
|
||||
_open: function() {
|
||||
this._broadcastStatus('open');
|
||||
JX.Leader.broadcast(null, {type: 'aphlict.getsubscribers'});
|
||||
},
|
||||
|
||||
_close: function() {
|
||||
this._broadcastStatus('closed');
|
||||
},
|
||||
|
||||
_broadcastStatus: function(status) {
|
||||
JX.Leader.broadcast(null, {type: 'aphlict.status', data: status});
|
||||
},
|
||||
|
||||
_message: function(raw) {
|
||||
var message = JX.JSON.parse(raw);
|
||||
JX.Leader.broadcast(null, {type: 'aphlict.server', data: message});
|
||||
},
|
||||
|
||||
_receive: function(message, is_leader) {
|
||||
switch (message.type) {
|
||||
case 'aphlict.status':
|
||||
this._setStatus(message.data);
|
||||
break;
|
||||
case 'aphlict.getstatus':
|
||||
if (is_leader) {
|
||||
this._broadcastStatus(this.getStatus());
|
||||
}
|
||||
break;
|
||||
case 'aphlict.getsubscribers':
|
||||
JX.Leader.broadcast(
|
||||
null,
|
||||
{type: 'aphlict.subscribe', data: this._subscriptions});
|
||||
break;
|
||||
case 'aphlict.subscribe':
|
||||
if (is_leader) {
|
||||
this._write({
|
||||
command: 'subscribe',
|
||||
data: message.data
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'aphlict.server':
|
||||
var handler = this.getHandler();
|
||||
handler && handler(message.data);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setStatus: function(status) {
|
||||
this._status = status;
|
||||
this._statusCode = code || null;
|
||||
this.invoke('didChangeStatus');
|
||||
},
|
||||
|
||||
_write: function(message) {
|
||||
this._socket.send(JX.JSON.stringify(message));
|
||||
}
|
||||
|
||||
},
|
||||
|
@ -110,28 +146,8 @@ JX.install('Aphlict', {
|
|||
return null;
|
||||
}
|
||||
return self._instance;
|
||||
},
|
||||
|
||||
didReceiveEvent: function(type, message) {
|
||||
var client = JX.Aphlict.getInstance();
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == 'status') {
|
||||
client._setStatus(message.type, message.code);
|
||||
switch (message.type) {
|
||||
case 'ready':
|
||||
client._didStartFlash();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var handler = client.getHandler();
|
||||
if (handler) {
|
||||
handler(type, message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -41,22 +41,8 @@ JX.behavior('aphlict-listen', function(config) {
|
|||
|
||||
// Respond to a notification from the Aphlict notification server. We send
|
||||
// a request to Phabricator to get notification details.
|
||||
function onaphlictmessage(type, message) {
|
||||
switch (type) {
|
||||
case 'receive':
|
||||
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'error':
|
||||
case 'log':
|
||||
case 'status':
|
||||
if (config.debug) {
|
||||
var details = message ? JX.JSON.stringify(message) : '';
|
||||
JX.log('(Aphlict) [' + type + '] ' + details);
|
||||
}
|
||||
break;
|
||||
}
|
||||
function onaphlictmessage(message) {
|
||||
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,13 +75,11 @@ JX.behavior('aphlict-listen', function(config) {
|
|||
}
|
||||
|
||||
var client = new JX.Aphlict(
|
||||
config.id,
|
||||
config.server,
|
||||
config.port,
|
||||
config.websocketURI,
|
||||
config.subscriptions);
|
||||
|
||||
client
|
||||
.setHandler(onaphlictmessage)
|
||||
.start(JX.$(config.containerID), config.swfURI);
|
||||
.start();
|
||||
|
||||
});
|
||||
|
|
Binary file not shown.
Loading…
Reference in a new issue